Neuron Lang is a python based DSL for naming neurons. Neurons are modelled as collections of phenotypes with semantics backed by Web Ontology Language (OWL2) classes. Neuron Lang provides tools for mapping to and from collections of local names for phenotypes by using ontology identifiers as the common language underlying all local naming. These tools also let us automatically generate names for neurons in a regular and consistent way using a set of rules operating on the neurons' constitutent phenotypes. Neuron Lang can export to python or to any serialziation supported by rdflib, however deterministic turtle (ttl) is prefered. Neuron Lang depends on files from the NIF-Ontology.
This notebook has examples of how to use Neuron Lang to:
%scig
magic to search for existing ontology identifiersLocalNameManager
to create abbreviations for phenotypes.with
or setLocalNames
.with
or setLocalContext
.Please see the documentation in order to set up a working environment for this notebook.
from neurondm import *
# set predicates in the event that the default config options do not work
# if you cloned the NIF-Ontology into a nonstandard location change ontology_local_repo in devconfig.yaml
from pyontutils.namespaces import ilxtr as pred
from neurondm import phenotype_namespaces as phns
config = Config('neuron-lang-notebook')
# By default Config saves ontology files in NIF-Ontology/ttl/generated/neurons/
# and python files in pyontutils/neurondm/neurondm/compiled/
# NOTE: if you call config multiple times any call to Neuron
# will be associate with the most recent instance of Config
# you can ignore this cell
# some utility functions needed for this tutorial
# due to the potential for notebooks to run cells out of order
def cellguard(addns=False):
# unfortunately ipy.hooks['pre_run_code_hook'].add(__cellguard)
# causes this to be called too frequently :/
setLocalNames()
setLocalContext()
if addns:
setLocalNames(phns.BBP)
Neuron instances are build out of Phenotype instances. Phenotypes are object-predicate pairs that take curied string representations (or uris) as arguments.
myFirstNeuron = Neuron(Phenotype('NCBITaxon:10090'),
Phenotype('UBERON:0000955'))
# NOTE: label is cosmetic and will be overwritten by rdfs:label
# unless you set override=True
myPhenotype = Phenotype('NCBITaxon:9685', # object
pred.hasInstanceInSpecies, # predicate (optional)
label='Cat') # label for human readability
# str and repr produce different results
print(myFirstNeuron)
Neuron(Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus'), Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain'))
print(repr(myFirstNeuron)) # NOTE: this is equivalent to typing `myFirstNeuron` and running the cell
Neuron(Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus'), Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain'))
Neuron Lang can only be used to add new neurons to a graph. Therefore if you need to remove neruons you need to reset the whole program. For this reason I do not suggest using ipython notebooks since they persist state in ways that can be very confusing when working with a persistent datastore.
# view the turtle (ttl) serialization of all neurons
turtle = config.ttl()
print(turtle)
@prefix : <file:///ERROR/EMPTY/PREFIX/BANNED/> . @prefix ilxtr: <http://uri.interlex.org/tgbugs/uris/readable/> . @prefix NCBITaxon: <http://purl.obolibrary.org/obo/NCBITaxon_> . @prefix owl: <http://www.w3.org/2002/07/owl#> . @prefix prov: <http://www.w3.org/ns/prov#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix SAO: <http://uri.neuinfo.org/nif/nifstd/sao> . @prefix TEMP: <http://uri.interlex.org/temp/uris/> . @prefix UBERON: <http://purl.obolibrary.org/obo/UBERON_> . @prefix xml: <http://www.w3.org/XML/1998/namespace> . @prefix xsd: <http://www.w3.org/2001/XMLSchema#> . <https://raw.githubusercontent.com/SciCrunch/NIF-Ontology/neurons/ttl/generated/neurons/neuron-lang-notebook.ttl> a owl:Ontology ; owl:imports <https://raw.githubusercontent.com/SciCrunch/NIF-Ontology/neurons/ttl/phenotype-core.ttl>, <https://raw.githubusercontent.com/SciCrunch/NIF-Ontology/neurons/ttl/phenotypes.ttl> ; prov:wasGeneratedBy <https://github.com/tgbugs/pyontutils/blob/f8c3c5c5a3ac8b259009d30153214fb056a13274/neurondm/neurondm/core.py#L555> . ### Classes TEMP:0-p0-NCBITaxon-10090-0-p3-UBERON-0000955 a owl:Class ; owl:equivalentClass [ a owl:Class ; owl:intersectionOf ( SAO:1417703748 [ a owl:Restriction ; owl:onProperty ilxtr:hasInstanceInSpecies ; owl:someValuesFrom NCBITaxon:10090 ] [ a owl:Restriction ; owl:onProperty ilxtr:hasSomaLocatedIn ; owl:someValuesFrom UBERON:0000955 ] ) ] ; rdfs:label "Mus musculus brain neuron" . ### Serialized using the pyontutils deterministic serializer v1.2.0
# view the python serialization of all neurons for the current config
python = config.python()
print(python)
#!/usr/bin/env python3.7 from neurondm.core import * config = Config('neuron-lang-notebook') # Mus musculus brain neuron Neuron(Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus'), Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain'))
# write the turtle file defined in cell 1
config.write()
# write a python file that has the same name as the file in cell 1
# but with python safe separators and a .py extension
config.write_python()
# view a list of all neurons for the current config
config.neurons()
[Neuron(Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus'), Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain'))]
When creating neurons we want to be able to find relevant identifiers
quickly while working. There is a cli utility called scig that can be
used as a cell magic %scig
to search a SciGraph instance for terms.
import neurondm.lang
%scig --help
Look look up ontology terms on the command line. Usage: scig v [--local --verbose --key=KEY] <id>... scig i [--local --verbose --key=KEY] <id>... scig t [--local --verbose --limit=LIMIT --key=KEY --prefix=P...] <term>... scig s [--local --verbose --limit=LIMIT --key=KEY --prefix=P...] <term>... scig g [--local --verbose --rt=RELTYPE --edges --key=KEY] <id>... scig e [--local --verbose --key=KEY] <p> <s> <o> scig c [--local --verbose --key=KEY] scig cy [--verbose --limit=LIMIT] <query> scig onts [options] Options: -e --edges print edges only -l --local hit the local scigraph server -v --verbose print the full uri -t --limit=LIMIT limit number of results [default: 10] -k --key=KEY api key -w --warn warn on errors -p --prefix=P... filter by prefix
# use -t to limit the number of results
%scig -t 1 t hippocampus -v
hippocampus http://localhost:9000/scigraph/vocabulary/term/hippocampus?limit=1 UBERON:0001954 abbreviations: [] acronyms: [] categories: ['anatomical entity'] definitions: ['A part of the brain consisting of a three layered cortex located in the forebrain bordering the medial surface of the lateral ventricle. The term hippocampus is often used synonymously with hippocampal formation which consists of the hippocampus proper or Cornu Ammonis, the dentate gyrus and the subiculum.'] deprecated: False iri: http://purl.obolibrary.org/obo/UBERON_0001954 labels: ["Ammon's horn"] synonyms: ['ammon horn', 'ammon gyrus', 'cornu ammonis', 'hippocampus proprius', 'hippocampus', "Ammon's horn", 'hippocampus proper', 'hippocampus major', 'Ammons horn', 'Ammon horn fields']
# you can escape spaces with \
%scig t macaca\ mulatta
macaca mulatta NCBITaxon:9544 abbreviations: [] acronyms: [] categories: ['organism'] definitions: [] deprecated: False iri: http://purl.obolibrary.org/obo/NCBITaxon_9544 labels: ['Macaca mulatta'] synonyms: ['Rhesus monkey', 'rhesus macaque', 'rhesus monkeys', 'rhesus macaques']
# quotes also allow search with spaces
%scig -t 1 s 'nucleus basalis of meynert'
nucleus basalis of meynert SAO:1702920020 abbreviations: [] acronyms: [] categories: [] definitions: ['A membrane-bounded organelle of eukaryotic cells that contains the chromosomes. It is the primary site of DNA replication and RNA synthesis in the cell (Gene Ontology).'] deprecated: False iri: http://uri.neuinfo.org/nif/nifstd/sao1702920020 labels: ['Nucleus'] synonyms: []
# without quotes scig will search multiple terms at once
%scig -t 1 t cat mouse
cat GO:0004096 abbreviations: [] acronyms: [] categories: [] definitions: ['Catalysis of the reaction: 2 hydrogen peroxide = O2 + 2 H2O.'] deprecated: False iri: http://purl.obolibrary.org/obo/GO_0004096 labels: ['catalase activity'] synonyms: ['catalase-peroxidase activity', 'optidase activity', 'hydrogen-peroxide:hydrogen-peroxide oxidoreductase activity', 'equilase activity', 'haem catalase activity', 'caperase activity', 'catalase reaction', 'CAT', 'manganese catalase activity', 'heme catalase activity', 'bacterial catalase-peroxidase activity'] mouse BIRNLEX:167 abbreviations: [] acronyms: [] categories: ['organism'] definitions: [] deprecated: False iri: http://uri.neuinfo.org/nif/nifstd/birnlex_167 labels: ['Mouse'] synonyms: ['mouse', 'Mus musculus', 'house mouse']
from pyontutils.scigraph_client import Graph, Vocabulary
sgg = Graph()
sgv = Vocabulary()
terms = sgv.findByTerm('neocortex')
nodes_edges = sgg.getNeighbors('UBERON:0000955',
relationshipType='BFO:0000050', # part of
direction='INCOMING')
print('synonyms:', terms[0]['synonyms'])
print('subjects:', *(e['sub'] for e in nodes_edges['edges']))
synonyms: ['homotypical cortex', 'isocortex', 'neopallium'] subjects: UBERON:0017631 UBERON:0003547 UBERON:0022776 UBERON:0001058 UBERON:0010403 UBERON:0003544 UBERON:0002616 UBERON:0005838 UBERON:0013694 UBERON:0013146 UBERON:0003947 UBERON:6003626 UBERON:0010009 UBERON:0006795 UBERON:0005075 UBERON:0013118 UBERON:0001059 UBERON:0007702 UBERON:0008998 UBERON:0000454 UBERON:0003053 UBERON:0006796 UBERON:0003052 UBERON:0005282 UBERON:0003528 UBERON:0000315
We can be more concise by creating a namespace for our phenotype names.
Normally these are defined in another file (e.g. phenotype_namespaces.py) so that they can be shared and reused.
NOTE: for a full explication of phenotype namespaces see neurondm/example.py
from neurondm import LocalNameManager
from pyontutils.utils import TermColors as tc # pretty printing that is not part of this tutorial
class myPhenotypeNames(LocalNameManager): # see neurons.LocalNameManager
Mouse = Phenotype('NCBITaxon:10090', pred.hasInstanceInSpecies)
Rat = Phenotype('NCBITaxon:10116', pred.hasInstanceInSpecies)
brain = Phenotype('UBERON:0000955', pred.hasSomaLocatedIn)
PV = Phenotype('PR:000013502', pred.hasExpressionPhenotype)
# you can see all the mappings in a local name manager by printing it or repring it
print(myPhenotypeNames)
# with a context manager we can use a namespace to create neurons
# more concisely and more importantly to repr them more concisely
with myPhenotypeNames:
n = Neuron(Rat, brain, PV)
# printing is unaffected so the fully expanded form is always
# accessible (__str__ vs __repr__)
print(tc.red('print inside unchanged:'), n, sep='\n')
print(tc.red('repr inside inside:'), repr(n))
# we can also repr a neuron defined elsewhere using our own names
print(tc.red('repr outside inside:'), repr(myFirstNeuron))
# outside the context manager our concise repr is gone
print(tc.red('repr inside outside:'), repr(n))
# in addition we will now get a NameError of we try to use bare words
try: Neuron(Rat)
except NameError: print(tc.blue('Rat fails as expected.'))
class myPhenotypeNames(LocalNameManager): Mouse = Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus') PV = Phenotype('PR:000013502', 'ilxtr:hasExpressionPhenotype', label='parvalbumin alpha') Rat = Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus') brain = Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain') print inside unchanged: Neuron(Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus'), Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain'), Phenotype('PR:000013502', 'ilxtr:hasExpressionPhenotype', label='parvalbumin alpha')) repr inside inside: Neuron(Rat, brain, PV) repr outside inside: Neuron(Mouse, brain) repr inside outside: Neuron(Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus'), Phenotype('UBERON:0000955', 'ilxtr:hasSomaLocatedIn', label='brain'), Phenotype('PR:000013502', 'ilxtr:hasExpressionPhenotype', label='parvalbumin alpha')) Rat fails as expected.
cellguard()
# there are already many namespaces defined in phenotype_namespaces.py
print(tc.red('Namespaces:'), phns.__all__)
# setLocalNames adds any names from a namespace to the current namespace
setLocalNames(phns.Species)
# we can load additional names
setLocalNames(phns.Regions, phns.Layers)
# however we will get a ValueError on a conflict
try:
setLocalNames(phns.Test)
except ValueError as e:
print(tc.red('The error:'), e)
# we can extend namespaces as well (again, best in a separate file)
# as long as the local names match we can combine entries
class MoreSpecies(phns.Species, myPhenotypeNames):
Cat = myPhenotype
ACh = Phenotype('CHEBI:15355', pred.hasExpressionPhenotype)
AChMinus = NegPhenotype(ACh)
with MoreSpecies:
can = Neuron(Cat, ACh, L2)
cant = Neuron(Cat, AChMinus, L3)
print(tc.red('More species:'), can, cant, sep='\n')
# we can also refer to phenotypes in a namespace directly
n = Neuron(Mouse, MoreSpecies.ACh)
print(tc.red('Direct usage:'), n, sep='\n')
# getLocalNames can be used to inspect the current set of defined names
print(tc.red('getLocalNames:'), sorted(getLocalNames().keys()))
# clear the local names by calling setLocalNames with no arguments
setLocalNames()
# no more short names ;_;
try: Neuron(Mouse, PV)
except NameError: print(tc.blue('Neuron(Mouse, PV) fails as expected'))
# for the rest of these examples we will use the BBP namespace
setLocalNames(phns.BBP)
# define neurons using our local names
Neuron(Mouse, L23, CCK, NPY)
Neuron(Mouse, brain, L3, PV)
Neuron(PV, DA)
cellguard()
Namespaces: ['Test', 'Layers', 'PaxRatLayers', 'Regions', 'PaxRatRegions', 'Species', 'BBP', 'CUT'] The error: Mapping between LocalNames and phenotypes must be injective. Cannot cannot bind 'LOOK_AT_THE_CUTE_LITTLE_GUY' to Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus'). It is already bound to 'Rat' More species: Neuron(Phenotype('NCBITaxon:9685', 'ilxtr:hasInstanceInSpecies', label='Felis catus'), Phenotype('UBERON:0005391', 'ilxtr:hasLayerLocationPhenotype', label='cortical layer II'), Phenotype('CHEBI:15355', 'ilxtr:hasExpressionPhenotype', label='acetylcholine')) Neuron(Phenotype('NCBITaxon:9685', 'ilxtr:hasInstanceInSpecies', label='Felis catus'), Phenotype('UBERON:0005392', 'ilxtr:hasLayerLocationPhenotype', label='cortical layer III'), NegPhenotype('CHEBI:15355', 'ilxtr:hasExpressionPhenotype', label='acetylcholine')) Direct usage: Neuron(Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus'), Phenotype('CHEBI:15355', 'ilxtr:hasExpressionPhenotype', label='acetylcholine')) getLocalNames: ['CA1', 'CA2', 'CA3', 'CTX', 'L1', 'L2', 'L23', 'L3', 'L4', 'L5', 'L56', 'L6', 'Mouse', 'Rat', 'S1', 'SLA', 'SLM', 'SLU', 'SO', 'SPy', 'SR', 'V1', 'brain', 'setBy_MoreSpecies'] Neuron(Mouse, PV) fails as expected
cellguard(True)
# we often want to create many neurons in the same contex
# the easiest way to do this is to use a instance of a neuron
# as the input to a context manager
with Neuron(Rat, CA1):
n1 = Neuron(CCK)
n2 = Neuron(NPY)
n3 = Neuron(PC)
# neurons always retain the context they were created in
print(tc.red('example 1:'), *map(repr, (n1, n2, n3)), '', sep='\n')
# you cannot change a neuron's context but you can see its original context
print(tc.red('example 2:'), n3.context, '', sep='\n')
try:
n3.context = Neuron(Mouse, CA2)
except TypeError as e:
print(tc.red('error when setting context:'), e, '\n')
# you can also use with as syntax when creating a context
with Neuron(Mouse) as n4:
n5 = Neuron(CCK)
print(tc.red('example 3:'), *map(repr, (n4, n5)), '', sep='\n')
# contexts cannot violate disjointness axioms
try:
with Neuron(Rat):
print(tc.red('neuron ok:'), Neuron(), '', sep='\n')
with Neuron(Mouse):
print('This will not print')
except TypeError: print(tc.blue('Neuron(Rat, Mouse) fails as expected\n'))
# if you define a new neuron inside a context it will carry
# that context with it if used to define a new context
# context does not nest for neurons defined outside a with
with n3:
n6 = Neuron(VIP)
with n5: # defined outside does not nest
n7 = Neuron(SOM)
with Neuron(SLM) as n8: # defined inside nests
n9 = Neuron(SOM)
n10 = Neuron(SOM)
print(tc.red('example 4:'), *map(repr, (n3, n6, n5, n7, n8, n9, n10)), sep='\n')
#
with Neuron(Rat), Neuron(CTX) as context:
print(context)
n11 = Neuron(L1)
print(n11)
cellguard()
example 1: Neuron(Rat, CA1, CCK) Neuron(Rat, CA1, NPY) Neuron(Rat, CA1, PC) example 2: (Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus'), Phenotype('PAXRAT:322', 'ilxtr:hasSomaLocatedIn', label='field CA1 of the hippocampus (paxrat)')) error when setting context: Cannot change the context of an instantiated neuron. example 3: Neuron(Mouse) Neuron(Mouse, CCK) neuron ok: Neuron(Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus')) Neuron(Rat, Mouse) fails as expected example 4: Neuron(Rat, CA1, PC) Neuron(Rat, CA1, PC, VIP) Neuron(Mouse, CCK) Neuron(Mouse, CCK, SOM) Neuron(Rat, CA1, SLM, PC) Neuron(Rat, CA1, SLM, PC, SOM) Neuron(Rat, CA1, PC, SOM) Neuron(Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus'), Phenotype('UBERON:0001950', 'ilxtr:hasSomaLocatedIn', label='neocortex')) Neuron(Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus'), Phenotype('UBERON:0001950', 'ilxtr:hasSomaLocatedIn', label='neocortex'), Phenotype('PAXRAT:509', 'ilxtr:hasLayerLocationPhenotype', label='layer 1 (paxrat)'))
cellguard(True)
# like namespaces you can also set a persistent local context
context0 = Neuron(CCK, NPY, SOM, DA, CA1, SPy)
context1 = Neuron(Rat, S1, L4)
setLocalContext(context0)
print(tc.red('created with context:'), repr(Neuron(TPC)))
# contexts are addative
# to change context using a Neuron you need to setLocalContext() first
# without resetting we get a disjointness error
try: setLocalContext(Neuron(Rat, S1, L4))
except TypeError as e: print(tc.blue('Neuron(S1, CA1) fails as expected'), e)
# reset
setLocalContext()
# now we will not get an error
setLocalContext(Neuron(Rat, S1, L4))
print(tc.red('Success:'), repr(Neuron(PC)))
# a neuron declared in a different context can be used to change the context withour resetting
# if you know in advance that you will be dealing with multiple contexts, I suggest you
# create all of those context neurons first so that they are available when needed
setLocalContext(context0)
# like namespaces call getLocalContext to see the current context
print(tc.red('getLocalContext:'), *(p.pShortName for p in getLocalContext()))
# like namespaces calling setLocalContext without arguments clears context
setLocalContext()
print(tc.red('no context:'), repr(Neuron(brain)))
cellguard()
created with context: Neuron(CA1, SPy, TPC, DA, CCK, NPY, SOM) Neuron(S1, CA1) fails as expected Disjointness violated for http://uri.interlex.org/tgbugs/uris/readable/hasSomaLocatedIn due to [Phenotype('PAXRAT:322', 'ilxtr:hasSomaLocatedIn', label='field CA1 of the hippocampus (paxrat)'), Phenotype('PAXRAT:794', 'ilxtr:hasSomaLocatedIn', label='primary somatosensory cortex (paxrat)')] Success: Neuron(Rat, S1, L4, PC) getLocalContext: CA1 SPy DA CCK NPY SOM no context: Neuron(brain)
cellguard(True)
context = (Rat, S1)
ca1_context = (Rat, CA1)
def NeuronC(*args, **kwargs):
return Neuron(*args, *context, **kwargs)
def NeuronH(*args, **kwargs):
return Neuron(*args, *ca1_context, **kwargs)
neurons = {
'HBP_CELL:0000013': NeuronC(CCK),
'HBP_CELL:0000016': NeuronC(PV),
'HBP_CELL:0000018': NeuronC(PC),
'HBP_CELL:0000135': NeuronH(SLM, PPA),
'HBP_CELL:0000136': NeuronH(SO, BP),
'HBP_CELL:0000137': NeuronH(SPy, BS),
'HBP_CELL:0000148': Neuron(Rat, STRI, MSN, D1),
'HBP_CELL:0000149': Neuron(Rat, CA3, PC),
}
neurons['HBP_CELL:0000013']
cellguard()
Neuron Lang enforces basic disjointness on phenotypes of 'data' level neurons
cellguard(True)
try: Neuron(Mouse, Rat)
except TypeError as e: print(tc.blue('Neuron(Mouse, Rat) fails as expected'), e, sep='\n')
cellguard()
Neuron(Mouse, Rat) fails as expected
Disjointness violated for http://uri.interlex.org/tgbugs/uris/readable/hasInstanceInSpecies due to [Phenotype('NCBITaxon:10090', 'ilxtr:hasInstanceInSpecies', label='Mus musculus'), Phenotype('NCBITaxon:10116', 'ilxtr:hasInstanceInSpecies', label='Rattus norvegicus')]