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.
In [1]:
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
In [2]:
# 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)
In [3]:
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
In [4]:
# str and repr produce different results
print(myFirstNeuron)
In [5]:
print(repr(myFirstNeuron)) # NOTE: this is equivalent to typing `myFirstNeuron` and running the cell
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.
In [6]:
# view the turtle (ttl) serialization of all neurons
turtle = config.ttl()
print(turtle)
In [7]:
# view the python serialization of all neurons for the current config
python = config.python()
print(python)
In [8]:
# write the turtle file defined in cell 1
config.write()
In [9]:
# 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()
In [10]:
# view a list of all neurons for the current config
config.neurons()
Out[10]:
In [11]:
import neurondm.lang
%scig --help
In [12]:
# use -t to limit the number of results
%scig -t 1 t hippocampus -v
In [13]:
# you can escape spaces with \
%scig t macaca\ mulatta
In [14]:
# quotes also allow search with spaces
%scig -t 1 s 'nucleus basalis of meynert'
In [15]:
# without quotes scig will search multiple terms at once
%scig -t 1 t cat mouse
In [16]:
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']))
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
In [17]:
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.'))
In [18]:
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()
In [19]:
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()
In [20]:
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()
In [21]:
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()
In [22]:
cellguard(True)
try: Neuron(Mouse, Rat)
except TypeError as e: print(tc.blue('Neuron(Mouse, Rat) fails as expected'), e, sep='\n')
cellguard()