Neuron++ is a library which wraps NEURON (http://www.neuron.yale.edu) with easy to use Python objects.
Neuron++ is a wrapper for NEURON (http://www.neuron.yale.edu) with easy to use Python objects.
The key intention behind this framework was to perform tedious tasks in few lines of code with
the object-oriented paradigm.
NEURON allows to create simulations of Biological Neural Networks. The Neuron++ framework was
designed to match the simplicity of libraries such as Keras library for Artificial Neural Networks.
With Neuron++ you can easily create group of cells, stack them together as populations,
then stimulate them with external input and collect readouts to perform any task.
Use for fast prototyping of neural models in NEURON simulator, using Python interface with
the object-oriented paradigm (OOP)
Precisely define single cell models and connect them together to create a network
Auto-compilation of all MOD files on the fly
Auto-load SWC/ASC or HOC morphology for each cell
Upload HOC defined models and adapt them to your needs
Define in vitro experimental protocols (eg. STDP paradigms)
Manage synaptic signaling
Debug synapses and point processes RANGE values in real time with interactive
stimulation from the keyboard
Create predefined dendritic spines and synapses with ease
Define populations of neurons and connect them together
Provide helpful exception messages and guidelines of how to use NEURON functions with Neuron++
wrapper without errors
This is the Alpha version.
pip install -r requirements.txt
https://github.com/ziemowit-s/neuronpp
Locally:
python setup.py bdist_wheel
Through pip and GitHub:
pip install git+https://github.com/ziemowit-s//neuronpp
So if you want to work with those predefined models it is recommended to clone the repository from
GitHub rather than install through pip.
This repository contains the basic cell objects:
Cell
classHocCell
- the experimental `HOC class which loads HOC based cell model. The repository also contains some predefined cell models from ModelDB (https://senselab.med.yale.edu/modeldb)
Cell
HocCell
- if you want to reuse existing HOC modelThe list of predefined cell models:
MOD files of all of those models are located in the commons/mods/ folder.
Combe 2018 model and Graham 2014 model additionally have HOC files located in the
commons/hocmodels/ folder.
Cell()
object has a compile_path
param which allows to specify paths which contain MOD files.
You don’t need to compile files externally, if you provided appropriate pathways it
will be done automatically before each run.
All examples are located in the examples/ folder
Please bear in mind that due to substantial updates some examples may not work as described here in
the README, also some additional features have been added which are not listed here. We will update
readme soon.
create a cell:
cell = Cell(name="mycell")
load SWCor ASC morphology:
cell.load_morpho(filepath='commons/morphologies/asc/cell2.asc')
load HOC-based cell (and HOC-based morphology) model to the Cell
object:
This is an experimental feature, so currently works ONLY with HOC models which define a
single cell
cell = HocCell(name="mycell")
cell.load_hoc("your_cell_model.hoc")
if the HOC cell model is defined as a Template - just specify the cell_template_name
param:
cell = HocCell(name="mycell")
cell.load_hoc("Ebner2019_minimum_load/load_model.hoc", hoc_template_name="L5PCtemplate")
create and connect sections:
cell.add_seg("soma", diam=20, l=20, nseg=10)
cell.add_seg("dend", diam=2, l=100, nseg=10)
cell.connect_secs(child="dend", parent="soma")
add NEURON mechanisms (default or MOD-based):
cell.insert("pas")
cell.insert("hh")
define simulation and run:
sim = Simulation(init_v=-65, warmup=20)
sim.run(runtime=500)
add IClamp:
sections = cell.filter_secs("soma")
soma = sections[0]
ic = IClamp(segment=soma(0.5))
ic.debug(delay=100, dur=10, amp=0.1)
You can obtain any part of the cell by string or regular expression filtering
filter section of the cell by string:
# Assuming you have sections dend[0]...dend[100] it will return all of them
sections = cell.filter_secs(name="dend")
filter by string separated by coma:
# Each coma function as OR between strings
sections = cell.filter_secs(name="apic[1],apic[50]")
filter section of the cell by regular expression:
# Assuming you have sections dend[0]...dend[100] and apic[0]...apic[100] it will return all of them
sections = cell.filter_secs(name="regex:(apic)|(basal)")
return synapses of type ‘ExpSyn’ located in all heads sections (of the dendritic spines)
cell.filter_synapses(mod_name="ExpSyn", name="head")
custom function-based filtering:
obj_filter
param.
soma = cell.filter_secs("soma")
cell.filter_secs(obj_filter=lambda o: 'apic' in o.name or h.distance(soma(0.5), o(0.5)) > 1000)
field-based filtering with custom function.
eg. (lambda expression) returns sections which parent’s name contains less than 10 characters
cell.filter_secs(parent=lambda o: len(o.parent.name) < 10)
All filter functions available in default cell object Cell
:
If you define source
param - it will stimulate synapse based on the stimulation from the source.
If source
is None - there will be no source and no netcon, however you can add those later or
use such synapse for the Experiment
.
add single synapse:
cell = Cell(name="cell")
soma = cell.filter_secs(name="soma")
cell.add_sypanse(source=None, mod_name="Syn4P", seg=soma(0.5), netcon_weight=0.01, delay=1)
add many spines to the provided sections:
cell = Cell(name="cell")
dendrites = cell.filter_secs(name="dend")
cell.add_randuniform_spines(spine_number=10, head_nseg=10, neck_nseg=10, secs=dendrites)
add many synapses with spines (1 synapse/spine) in a single function to the provided sections:
cell = Cell(name="cell")
dendrites = cell.filter_secs(name="dend")
syns = cell.add_random_synapses_with_spine(source=None, secs=dendrites, mod="ExpSyn",
netcon_weight=0.01, delay=1, number=10)
define NetStim (or VecStim) and pass it to synapses as a source while creating:
netstim = NetStimCell(name="netst")
stim = netstim.add_netstim(start=300, number=5, interval=10)
cell = Cell(name="cell")
soma = cell.filter_secs(name="soma")
cell.add_sypanse(source=stim, seg=soma(0.5), mod_name="ExpSyn", netcon_weight=0.01, delay=1)
netcon_weight=0.01, delay=1)
sim = Simulation(init_v=-55, warmup=20)
syns[0].make_event(10)
syns[0].make_event(20)
sim.run(runtime=100)
```
Add another source to the previously created synapse:
synapse.add_netcon(source=None, weight=0.035, threshold=15, delay=2)
make spike detector for the cell:
cell.make_spike_detector()
sim = RunSim(init_v=-65)
sim.run(runtime=500)
cell.plot_spikes()
record variables from sections and point_processes:
# record section's voltage
soma = cell.filter_secs(name="soma")
rec_v = Record(soma(0.5), variables="v")
# record synaptic (point_process) wariables (weight 'w')
point_processes = cell.filter_point_processes(mod_name="Syn4P", name="dend")
rec_syn = Record(point_processes, variables="w")
sim = Simulation(init_v=-65, warmup=20, with_neuron_gui=True)
sim.run(runtime=500)
make plots and export recorded variables to CSV:
rec_v.plot()
rec_syn.plot()
plt.show()
rec_v.to_csv("vrec.csv")
make shape plot of the cell in NEURON GUI:
# show 'cai' propagation in range 0-0.01 uM
make_shape_plot(variable="cai", min_val=0, max_val=0.01)
# show 'v' propagation in range -70-40 mV
make_shape_plot(variable="v", min_val=-70, max_val=40)
define experimetal protocols, eg. STDP protocol:
soma = cell.filter_secs("soma")
syn = cell.filter_synapses(tag="my_synapse")
experiment = Experiment(iti=40)
experiment.add_epsp(num=3, synapse=syn, init=20, interval=20, weight=0.02)
experiment.add_iclamp(num=3, segment=soma(0.5), init=60, interval=20, dur=3, amp=1.6)
experiment.build()
Create a population of many neurons of the same type and connect them between populations:
Create a template cell function:
def cell_function():
cell = Cell(name="cell")
morpho_path = os.path.join(path, "..", "commons/morphologies/swc/my.swc")
cell.load_morpho(filepath=morpho_path)
cell.insert("pas")
cell.insert("hh")
cell.make_spike_detector(seg=cell.filter_secs("soma")(0.5))
return cell
Create stimulation:
# Create NetStim
netstim = NetStimCell("stim").add_netstim(start=21, number=100, interval=2)
# Define weight distribution for both: NetStim->population1 and population1->population2
weight_dist = NormalTruncatedDist(mean=0.1, std=0.2)
Define population 1:
pop1 = Population("pop_1")
pop1.add_cells(num=3, cell_function=cell_function)
# create 10 synapses on population 2 per NetStim object (single NetStim here)
connector = pop1.connect(syn_num_per_cell_source=10)
connector.set_source(netstim)
# choose all dendrites as potential targets for synaptic choice
targets = [d(0.5) for c in pop1.cells for d in c.filter_secs("dend")]
connector.set_target(targets)
# Make synapse
syn_adder = connector.add_synapse("Exp2Syn")
syn_adder.add_netcon(weight=weight_dist)
# change tau1 and tau2 for Exp2Syn synapses
syn_adder.add_point_process_params(tau1=0.1, tau2=2)
Build connections and define that you want to record from the population
record()
method make records of: voltage variable in soma(0.5)
connector.build()
pop1.record()
Create population 2:
pop2 = Population("pop_2")
pop2.add_cells(num=3, cell_function=cell_function)
Define connections between pop1 and pop2 where weights will be chosen
from the Normal Truncated Distribution:
# create 5 synapses per single cell in population 1
connector = pop2.connect(syn_num_per_cell_source=5)
source = [c.filter_secs("soma")(0.5) for c in pop1.cells]
connector.set_source(source)
# choose all dendrites as potential targets for synaptic choice
targets = [d(0.5) for c in pop2.cells for d in c.filter_secs("dend")]
connector.set_target(targets)
# Make synapse
syn_adder = connector.add_synapse("Exp2Syn")
syn_adder.add_netcon(weight=weight_dist)
# change tau1 and tau2 for Exp2Syn synapses
syn_adder.add_point_process_params(tau1=0.1, tau2=2)
Build connections and define that you want to record from the population
By default record()
method make records of: voltage variable in soma(0.5)
connector.build()
pop2.record()
Ryn simulation and plot activities:
sim = Simulation(init_v=-70, warmup=20)
for i in range(1000):
sim.run(runtime=1)
pop1.plot(animate=True)
pop2.plot(animate=True)
Create (experimental) interactive graph of connected populations which allows you to see and
move nodes in the web browser:
show_connectivity_graph(pop1.cells + pop2.cells)
Debug any cell and synapse on interactive plot.
stim_key
param (default is w) you can stimulate synapsesIt allows to easily plot synaptic weight (defined as MOD’s RANGE variable) to see how
the plasticity behaves in real time
from neuronpp.cells.cell import Cell
from neuronpp.utils.synaptic_debugger import SynapticDebugger
# Prepare cell
cell = Cell("cell")
soma = cell.add_sec("soma", diam=20, l=20, nseg=100)
cell.insert("pas")
cell.insert("hh")
syn1 = cell.add_synapse(source=None, netcon_weight=0.002, seg=soma(0.1), mod_name="Exp2Syn")
syn2 = cell.add_synapse(source=None, netcon_weight=0.002, seg=soma(0.9), mod_name="Exp2Syn")
syn3 = cell.add_synapse(source=None, netcon_weight=0.002, seg=soma(0.5), mod_name="Exp2Syn")
# Debug
debug = SynapticDebugger(init_v=-70, warmup=10, delay_between_steps=15)
debug.add_syn(syn1, key_press='1', plot=True, syn_variables='i')
debug.add_syn(syn2, key_press='2')
debug.add_syn(syn3, key_press='3')
debug.add_seg(soma(0.5))
debug.debug_interactive()
Example of Ebner et al. 2019 model of synaptic weight (variable w) changing based on synaptic stimulation on demand
by pressing key “w” on the keyboard: