Evaluating biophysically detailed multi-compartmental models

Tutorial 1.1: Simulating biophysically detailed multi-compartmental models outlined how to run 5 stimulus protocols (\(bAP\), \(BAC\) and \(3\) step currents) in silico.

This tutorial will show how to use ISF in order to compare the cell’s in silico response to an empirical distribution of electrophysiological parameters, i.e. the simulation objectives. Quantifying how far off the responses are compared to this distribution will be done with the Evaluator object.

In addition to quantifying the response and comparing it to empirical values, this tutorial will also show how to visually inspect simulations using the visualize package.

To get started, adapt the path below to your desired output directory:

[1]:
from pathlib import Path
tutorial_output_dir = f"{Path.home()}/isf_tutorial_output"  # <-- Change this to your desired output directory
[2]:
import Interface as I
%matplotlib inline

db = I.DataBase(tutorial_output_dir).create_sub_db("neuron_modeling")
[INFO] ISF: Current version: heads/data+0.g1077bcf8.dirty
[INFO] ISF: Current pid: 227965
[INFO] ISF: Loading mechanisms:
Warning: no DISPLAY environment variable.
--No graphics will be displayed.
[ATTENTION] ISF: The source folder has uncommited changes!



[INFO] ISF: Loaded modules with __version__ attribute are:
IPython: 8.12.2, Interface: heads/data+0.g1077bcf8.dirty, PIL: 10.4.0, _brotli: 1.0.9, _csv: 1.0, _ctypes: 1.1.0, _curses: b'2.2', _decimal: 1.70, argparse: 1.1, backcall: 0.2.0, blosc: 1.11.1, bluepyopt: 1.9.126, brotli: 1.0.9, certifi: 2024.08.30, cffi: 1.17.0, charset_normalizer: 3.4.0, click: 7.1.2, cloudpickle: 3.1.0, colorama: 0.4.6, comm: 0.2.2, csv: 1.0, ctypes: 1.1.0, cycler: 0.12.1, cytoolz: 0.12.3, dash: 2.18.2, dask: 2.30.0, dateutil: 2.9.0, deap: 1.4, debugpy: 1.8.5, decimal: 1.70, decorator: 5.1.1, defusedxml: 0.7.1, distributed: 2.30.0, distutils: 3.8.20, django: 1.8.19, entrypoints: 0.4, executing: 2.1.0, fasteners: 0.17.3, flask: 1.1.4, fsspec: 2024.10.0, future: 1.0.0, greenlet: 3.1.1, idna: 3.10, ipaddress: 1.0, ipykernel: 6.29.5, ipywidgets: 8.1.5, isf_pandas_msgpack: 0.2.3, itsdangerous: 1.1.0, jedi: 0.19.1, jinja2: 2.11.3, joblib: 1.4.2, json: 2.0.9, jupyter_client: 7.3.4, jupyter_core: 5.7.2, kiwisolver: 1.4.5, logging: 0.5.1.2, markupsafe: 2.0.1, matplotlib: 3.5.1, msgpack: 1.0.8, neuron: 7.8.2+, numcodecs: 0.12.1, numexpr: 2.8.6, numpy: 1.19.2, packaging: 25.0, pandas: 1.1.3, parameters: 0.2.1, parso: 0.8.4, past: 1.0.0, pexpect: 4.9.0, pickleshare: 0.7.5, platform: 1.0.8, platformdirs: 4.3.6, plotly: 5.24.1, prompt_toolkit: 3.0.48, psutil: 6.0.0, ptyprocess: 0.7.0, pure_eval: 0.2.3, pydevd: 2.9.5, pygments: 2.18.0, pyparsing: 3.1.4, pytz: 2024.2, re: 2.2.1, requests: 2.32.3, scandir: 1.10.0, scipy: 1.5.2, seaborn: 0.12.2, six: 1.16.0, sklearn: 0.23.2, socketserver: 0.4, socks: 1.7.1, sortedcontainers: 2.4.0, stack_data: 0.6.2, statsmodels: 0.13.2, sumatra: 0.7.4, tables: 3.8.0, tblib: 3.0.0, tlz: 0.12.3, toolz: 1.0.0, tqdm: 4.67.1, traitlets: 5.14.3, urllib3: 2.2.3, wcwidth: 0.2.13, werkzeug: 1.0.1, yaml: 5.3.1, zarr: 2.15.0, zlib: 1.0, zmq: 26.2.0, zstandard: 0.19.0

The previous tutorial showed how to set up a Simulator object, which we saved to re-use here. Let’s load it back in to evaluate how well these models have performed

[3]:
simulator = db['simulator']
voltage_traces = db['voltage_traces']

The ISF Evaluator

ISF implements an Evaluator object to evaluate voltage traces. It can be configured once with empirical values on voltage traces and re-used afterwards.

[4]:
from biophysics_fitting.hay.default_setup import get_Evaluator

evaluator = get_Evaluator(step=True, interpolate_voltage_trace=False)
[5]:
evaluation = evaluator.evaluate(voltage_traces)
evaluation
[5]:
{'BAC_APheight.check_1AP': 'True',
 'BAC_APheight.raw': array([33.66002227, 29.8688259 , 23.41371875]),
 'BAC_APheight.normalized': 1.0076752949319177,
 'BAC_APheight': 1.0076752949319177,
 'BAC_ISI.check_2_or_3_APs': 'True',
 'BAC_ISI.check_repolarization': 'True',
 'BAC_ISI.raw': 9.936833063034726,
 'BAC_ISI.normalized': 0.04207239994684291,
 'BAC_ISI': 0.04207239994684291,
 'BAC_caSpike_height.check_1_Ca_AP': 'True',
 'BAC_caSpike_height.check_>=2_Na_AP': 'True',
 'BAC_caSpike_height.check_ca_max_after_nth_somatic_spike': 'True',
 'BAC_caSpike_height.raw': 8.413835125674638,
 'BAC_caSpike_height.normalized': 0.6629272148325343,
 'BAC_caSpike_height': 0.6629272148325343,
 'BAC_caSpike_width.check_1_Ca_AP': 'True',
 'BAC_caSpike_width.raw': 38.31055111593054,
 'BAC_caSpike_width.normalized': 0.6933473353783755,
 'BAC_caSpike_width': 0.6933473353783755,
 'BAC_spikecount.raw': 3,
 'BAC_spikecount.normalized': 0.0,
 'BAC_spikecount': 0.0,
 'BAC_ahpdepth.check_2AP': 'True',
 'BAC_ahpdepth.raw': array([-65.68694881, -65.42729689]),
 'BAC_ahpdepth.normalized': 0.1392807122615114,
 'BAC_ahpdepth': 0.1392807122615114,
 'BAC.err': 143.74912826015054,
 'BAC.check_minspikenum': 'True',
 'BAC.check_returning_to_rest': 'True',
 'BAC.check_no_spike_before_stimulus': 'True',
 'BAC.check_last_spike_before_deadline': 'True',
 'BAC.check_max_prestim_dendrite_depo': nan,
 'bAP_spikecount.raw': 1,
 'bAP_spikecount.normalized': 0.0,
 'bAP_spikecount': 0.0,
 'bAP_APheight.check_1AP': 'True',
 'bAP_APheight.raw': array([33.65989277]),
 'bAP_APheight.normalized': 1.7319785547438031,
 'bAP_APheight': 1.7319785547438031,
 'bAP_APwidth.check_1AP': 'True',
 'bAP_APwidth.raw': array([0.98336221]),
 'bAP_APwidth.normalized': 2.0332755883802065,
 'bAP_APwidth': 2.0332755883802065,
 'bAP_att2.raw': 32.61344140808186,
 'bAP_att2.check_1_AP': 'True',
 'bAP_att2.check_relative_height': 'True',
 'bAP_att2.normalized': 1.2386558591918138,
 'bAP_att2': 1.2386558591918138,
 'bAP_att3.raw': 21.018319507819953,
 'bAP_att3.check_1_AP': 'True',
 'bAP_att3.check_relative_height': 'True',
 'bAP_att3.normalized': 1.6057535361393405,
 'bAP_att3': 1.6057535361393405,
 'bAP.err': 248.30532972531503,
 'bAP.check_minspikenum': 'True',
 'bAP.check_returning_to_rest': 'True',
 'bAP.check_no_spike_before_stimulus': 'True',
 'bAP.check_last_spike_before_deadline': 'True',
 'bAP.check_max_prestim_dendrite_depo': nan,
 'mf1.check_1AP': 21,
 'mf1.raw': 10.5,
 'mf1.normalized': 1.7045454545454546,
 'mf1': 1.7045454545454546,
 'AI1.check_2ISI': 21,
 'AI1.raw': 0.008210116836947173,
 'AI1.normalized': 0.5066062458183707,
 'AI1': 0.5066062458183707,
 'ISIcv1.check_2ISI': 21,
 'ISIcv1.raw': 0.07166545818156783,
 'ISIcv1.normalized': 1.518210025496329,
 'ISIcv1': 1.518210025496329,
 'DI1.check_2ISI': 21,
 'DI1.raw': 43.55860401502662,
 'DI1.normalized': 0.42387682153444994,
 'DI1': 0.42387682153444994,
 'TTFS1.check_1AP': 21,
 'TTFS1.raw': 31.83428349013741,
 'TTFS1.normalized': 1.5595241133692064,
 'TTFS1': 1.5595241133692064,
 'APh1.check_1AP': 'True',
 'APh1.raw': array([24.09137071, 22.99574218, 22.78835239, 22.69107345, 22.68429012,
        22.67564812, 22.6771194 , 22.52439294, 22.60587203, 22.56109392,
        22.51164211, 22.4663929 , 22.52556049, 22.46631672, 22.40467648,
        22.40949821, 22.42663761, 22.33953598, 22.30147383, 22.33121202,
        22.36159536]),
 'APh1.normalized': 0.7275205487209074,
 'APh1': 0.7275205487209074,
 'fAHPd1.check_2AP': 'True',
 'fAHPd1.raw': array([-63.07277651, -62.78005906, -62.6583248 , -62.53651584,
        -62.52194432, -62.43449412, -62.32089897, -62.39006   ,
        -62.31048539, -62.33128433, -62.28978495, -62.31658177,
        -62.28410763, -62.30394597, -62.3336867 , -62.30956485,
        -62.31995899, -62.31292736, -62.3652368 ]),
 'fAHPd1.normalized': 1.8003008765264834,
 'fAHPd1': 1.8003008765264834,
 'sAHPd1.check_2AP': 'True',
 'sAHPd1.raw': array([-62.67978109, -63.10984807, -63.25590053, -63.21579511,
        -63.11367589, -63.00336828, -62.86072748, -62.80277767,
        -62.68235573, -62.62529579, -62.56086612, -62.52874268,
        -62.48671966, -62.43637328, -62.41316377, -62.38445205,
        -62.35582907, -62.33716901, -62.35128974]),
 'sAHPd1.normalized': 1.0151163975923705,
 'sAHPd1': 1.0151163975923705,
 'sAHPt1.check_2AP': 'True',
 'sAHPt1.raw': array([0.21629595, 0.19349339, 0.21138262, 0.21059077, 0.21985869,
        0.22330926, 0.226997  , 0.2386829 , 0.24629334, 0.23097281,
        0.24412891, 0.2647404 , 0.24537822, 0.25003761, 0.24583298,
        0.24744496, 0.25608634, 0.26335499, 0.25594333]),
 'sAHPt1.normalized': 0.5495925224841391,
 'sAHPt1': 0.5495925224841391,
 'APw1.check_1AP': 'True',
 'APw1.raw': array([0.96198111, 0.93800774, 0.97709169, 0.94631752, 0.97689972,
        0.95077889, 0.97062854, 0.9449781 , 0.94071347, 0.95432122,
        0.95311969, 0.932242  , 0.98678948, 0.93568953, 0.95748403,
        0.96239958, 0.95148441, 0.96229513, 0.95876679, 0.95203888,
        0.94387863]),
 'APw1.normalized': 2.117487156301,
 'APw1': 2.117487156301,
 'StepOne.err': 186.063249205872,
 'StepOne.check_minspikenum': 'True',
 'StepOne.check_returning_to_rest': 'True',
 'StepOne.check_no_spike_before_stimulus': 'True',
 'StepOne.check_last_spike_before_deadline': 'True',
 'StepOne.check_max_prestim_dendrite_depo': nan,
 'mf2.check_1AP': 29,
 'mf2.raw': 14.5,
 'mf2.normalized': 0.0,
 'mf2': 0.0,
 'AI2.check_2ISI': 29,
 'AI2.raw': 0.01302293276553171,
 'AI2.normalized': 1.9148094224163767,
 'AI2': 1.9148094224163767,
 'ISIcv2.check_2ISI': 29,
 'ISIcv2.raw': 0.1270919522564917,
 'ISIcv2.normalized': 0.5106508765351007,
 'ISIcv2': 0.5106508765351007,
 'DI2.check_2ISI': 29,
 'DI2.raw': 19.335769758475635,
 'DI2.normalized': 1.4694531512688596,
 'DI2': 1.4694531512688596,
 'TTFS2.check_1AP': 29,
 'TTFS2.raw': 20.04291489923446,
 'TTFS2.normalized': 0.125569753657245,
 'TTFS2': 0.125569753657245,
 'APh2.check_1AP': 'True',
 'APh2.raw': array([25.289338  , 22.91487818, 23.00459579, 22.49166693, 22.29761099,
        22.21175384, 22.25860675, 22.2450473 , 22.26937709, 22.14045844,
        22.22940218, 22.18828798, 22.08730248, 22.09746412, 22.02329864,
        22.00543709, 22.02109181, 21.95402584, 22.0333001 , 22.00324597,
        21.94894466, 21.94920213, 21.92281833, 21.88771034, 21.88948634,
        21.84765357, 21.89441045, 21.77926653, 21.82714999]),
 'APh2.normalized': 0.9342151521837061,
 'APh2': 0.9342151521837061,
 'fAHPd2.check_2AP': 'True',
 'fAHPd2.raw': array([-63.31954044, -62.99594263, -62.62724758, -62.40615908,
        -62.20911732, -62.17715377, -62.09167612, -62.07826386,
        -61.96563229, -62.0238239 , -62.00215742, -61.93515078,
        -61.90298672, -61.90306708, -61.89143113, -61.89131561,
        -61.89810665, -61.95373015, -61.93932434, -61.9037699 ,
        -61.95375415, -61.95347455, -61.93760715, -61.94996851,
        -61.95934734, -61.99514349, -61.95610612]),
 'fAHPd2.normalized': 1.4198796199388009,
 'fAHPd2': 1.4198796199388009,
 'sAHPd2.check_2AP': 'True',
 'sAHPd2.raw': array([-62.69571127, -62.50916915, -62.79705684, -62.91073606,
        -62.7546071 , -62.61809871, -62.47858224, -62.35608982,
        -62.23057261, -62.10762341, -62.06436578, -61.98290703,
        -61.93966353, -61.85511224, -61.81083838, -61.76599192,
        -61.73416086, -61.72949083, -61.69477853, -61.65643276,
        -61.64104844, -61.61903164, -61.58479313, -61.58183362,
        -61.55295009, -61.55902855, -61.52938553]),
 'sAHPd2.normalized': 0.3243466774696222,
 'sAHPd2': 0.3243466774696222,
 'sAHPt2.check_2AP': 'True',
 'sAHPt2.raw': array([0.14440786, 0.10149109, 0.23006425, 0.23913882, 0.23972689,
        0.22124587, 0.26057754, 0.26478589, 0.26298941, 0.27062517,
        0.2596264 , 0.26580278, 0.2734945 , 0.28501231, 0.26285161,
        0.28442103, 0.27858236, 0.26828522, 0.29121099, 0.28293423,
        0.27761429, 0.29550939, 0.26738561, 0.28966649, 0.25861875,
        0.27133896, 0.29678496]),
 'sAHPt2.normalized': 1.01641064338122,
 'sAHPt2': 1.01641064338122,
 'APw2.check_1AP': 'True',
 'APw2.raw': array([0.94586939, 0.91959924, 0.94497682, 0.94000711, 0.97506431,
        0.96358534, 0.94363674, 0.93621152, 0.95582546, 0.95260643,
        0.97758583, 0.94832052, 0.93123934, 0.9398469 , 0.93592603,
        0.93713813, 0.94962583, 0.96446361, 0.93927337, 0.9493291 ,
        0.9349232 , 0.96418846, 0.94105855, 0.9350136 , 0.95549361,
        0.96590542, 0.94363678, 0.93346691, 0.95732679]),
 'APw2.normalized': 1.5324457777590226,
 'APw2': 1.5324457777590226,
 'StepTwo.err': 183.82534769658955,
 'StepTwo.check_minspikenum': 'True',
 'StepTwo.check_returning_to_rest': 'True',
 'StepTwo.check_no_spike_before_stimulus': 'True',
 'StepTwo.check_last_spike_before_deadline': 'True',
 'StepTwo.check_max_prestim_dendrite_depo': nan,
 'mf3.check_1AP': 55,
 'mf3.raw': 27.5,
 'mf3.normalized': 2.2500225002250023,
 'mf3': 2.2500225002250023,
 'AI3.check_2ISI': 55,
 'AI3.raw': 0.009023622914987494,
 'AI3.normalized': 1.701393428841344,
 'AI3': 1.701393428841344,
 'ISIcv3.check_2ISI': 55,
 'ISIcv3.raw': 0.15152925677739668,
 'ISIcv3.normalized': 4.009232626956906,
 'ISIcv3': 4.009232626956906,
 'DI3.check_2ISI': 55,
 'DI3.raw': 7.828677080833472,
 'DI3.normalized': 2.950213350401774,
 'DI3': 2.950213350401774,
 'TTFS3.check_1AP': 55,
 'TTFS3.raw': 7.333930069455391,
 'TTFS3.normalized': 0.08393006945539128,
 'TTFS3': 0.08393006945539128,
 'APh3.check_1AP': 'True',
 'APh3.raw': array([31.06548527, 25.12701956, 23.61311489, 22.73688262, 22.21078341,
        21.57937786, 21.04672383, 20.76563028, 20.64813921, 20.55610157,
        20.42377868, 20.4599674 , 20.45969288, 20.43528102, 20.174338  ,
        20.30478441, 20.35465197, 20.28209844, 20.22536986, 20.27072808,
        20.15914034, 20.19224602, 20.15232081, 20.14372265, 20.13439816,
        20.07233316, 20.11473716, 20.03663349, 20.0170812 , 20.05731578,
        20.03805308, 20.00040917, 19.95123863, 19.94288751, 19.95577528,
        19.95158293, 19.93562061, 19.92758261, 19.78689311, 19.87964998,
        19.87197628, 19.83337694, 19.81677821, 19.84509897, 19.72888495,
        19.79162615, 19.76673602, 19.77400936, 19.76127948, 19.80455543,
        19.74585616, 19.75381166, 19.72279777, 19.77130918, 19.69740275]),
 'APh3.normalized': 0.592363621161479,
 'APh3': 0.592363621161479,
 'fAHPd3.check_2AP': 'True',
 'fAHPd3.raw': array([-64.49147803, -63.19511496, -62.72814944, -62.28446636,
        -61.75978386, -61.31762381, -61.01314808, -60.81151239,
        -60.67912156, -60.52746746, -60.53372268, -60.48151412,
        -60.44317895, -60.18392669, -60.31955776, -60.34059709,
        -60.298159  , -60.263136  , -60.28501073, -60.1924222 ,
        -60.25948116, -60.24707285, -60.21552421, -60.22883443,
        -60.2118771 , -60.244356  , -60.18499324, -60.2022267 ,
        -60.23005033, -60.25431815, -60.26227308, -60.23695933,
        -60.22131564, -60.26720735, -60.29679708, -60.27592727,
        -60.29552467, -60.22672025, -60.29066048, -60.313126  ,
        -60.31248887, -60.2925893 , -60.3463166 , -60.25238435,
        -60.3302365 , -60.35249597, -60.37554333, -60.36903818,
        -60.38429871, -60.39394615, -60.37696266, -60.39843727,
        -60.45272127]),
 'fAHPd3.normalized': 1.1277531070124256,
 'fAHPd3': 1.1277531070124256,
 'sAHPd3.check_2AP': 'True',
 'sAHPd3.raw': array([-62.3132864 , -61.88864277, -61.77676612, -61.69415586,
        -61.30304238, -60.98434918, -60.72257492, -60.51480139,
        -60.46581423, -60.27360188, -60.30502217, -60.21105122,
        -60.18140053, -59.9297075 , -60.08423894, -60.03726704,
        -60.01453674, -59.98993216, -59.93277795, -59.89552556,
        -59.91701656, -59.92733511, -59.86207856, -59.79412642,
        -59.77317544, -59.86225267, -59.76805902, -59.80739407,
        -59.8257073 , -59.77714064, -59.74146828, -59.73339037,
        -59.83316817, -59.84775185, -59.85289352, -59.8342664 ,
        -59.86712761, -59.73352912, -59.73979828, -59.84408651,
        -59.77256854, -59.81154729, -59.80507675, -59.78089181,
        -59.76471253, -59.79733875, -59.74039751, -59.80519001,
        -59.83571012, -59.7251643 , -59.81061982, -59.81074666,
        -59.82644007]),
 'sAHPd3.normalized': 0.09246104391805444,
 'sAHPd3': 0.09246104391805444,
 'sAHPt3.check_2AP': 'True',
 'sAHPt3.raw': array([0.36279071, 0.30161064, 0.2484799 , 0.19095867, 0.16237276,
        0.13911125, 0.12780075, 0.12544222, 0.11718686, 0.12136835,
        0.11837248, 0.12196111, 0.12127243, 0.12714399, 0.121469  ,
        0.12636774, 0.12568368, 0.12529812, 0.13047645, 0.12941498,
        0.13028479, 0.12950152, 0.13214559, 0.13703346, 0.13802471,
        0.13345267, 0.13726805, 0.13487522, 0.13450501, 0.13951205,
        0.14203114, 0.14143575, 0.13531704, 0.13557297, 0.13556914,
        0.13666068, 0.13540744, 0.14145944, 0.14279928, 0.13822604,
        0.14193758, 0.13887647, 0.14073416, 0.13959887, 0.14259464,
        0.14216232, 0.14713721, 0.14205005, 0.14046243, 0.14771324,
        0.14297933, 0.14214369, 0.14378104]),
 'sAHPt3.normalized': 2.109119864685762,
 'sAHPt3': 2.109119864685762,
 'APw3.check_1AP': 'True',
 'APw3.raw': array([0.98531213, 0.91123546, 0.94754453, 0.92717859, 0.92242316,
        0.95287909, 0.92477266, 0.92511115, 0.95809013, 0.93476667,
        0.94955365, 0.94920487, 0.93034679, 0.95285811, 0.93205066,
        0.94868883, 0.93788963, 0.94364911, 0.93710963, 0.94210618,
        0.96209178, 0.93310596, 0.96895262, 0.94518157, 0.95295191,
        0.93313142, 0.93405116, 0.95356331, 0.94557525, 0.95123666,
        0.9397376 , 0.94528431, 0.94698763, 0.96069676, 0.95914107,
        0.95262626, 0.95051096, 0.929552  , 0.93727957, 0.92652508,
        0.96162485, 0.94524348, 0.92878712, 0.92922577, 0.95214297,
        0.95807564, 0.95623037, 0.95059179, 0.94546294, 0.94579291,
        0.94372633, 0.9617165 , 0.9415482 , 0.94118213, 0.933252  ]),
 'APw3.normalized': 2.234564571206278,
 'APw3': 2.234564571206278,
 'StepThree.err': 179.370143914727,
 'StepThree.check_minspikenum': 'True',
 'StepThree.check_returning_to_rest': 'True',
 'StepThree.check_no_spike_before_stimulus': 'True',
 'StepThree.check_last_spike_before_deadline': 'True',
 'StepThree.check_max_prestim_dendrite_depo': nan}

evaluation contains a lot of information. Most are self-explanatory, but not all, so here is a small overview.

Suffix

Meaning

.check

Check if certain conditions are met before moving on to evaluation. E.g. it does not make sense to measure the AP height of the second AP during the BAC stimulus, if there is no burst firing at all.

.raw

The measured values for all objectives are saved under .raw variables. If multiple values exists for some objective, then this .raw value will be an array, but the mean is taken for further evaluation.

.normalized

Scales objective between \(0\) and \(1\).

No suffix

The objective in units of \(\sigma\) relative to the empirically observed mean \(\mu\).

The step current objectives have some cryptic acronyms as well:

Acronym

Meaning

mf

Spike frequency

AI

Adaptation Index

ISIcv

Interspike Interval Coefficient of Variation

DI

Initial burst ISI

TTFS

First spike latency

APh

AP peak

fAHPd

Fast AHP Depth

sAHPd

Slow AHP Depth

sAHPt

Slow AHP Time

APw

AP half-width

Exactly how much each objective is allowed to deviate from its empirical mean is defined in each evaluator class, associated with a stimulu protocol. See for example the BAC class in biophysics_fitting.hay.evaluation

Let’s clean up this data a bit and plot out their deviation from the mean.

[6]:
# only plot out sigma from mean, not raw or normalized values - also no checks.
sigma_mean = {k: v for k, v in evaluation.items() if \
    not k.endswith('.raw') and \
    not k.endswith('.normalized') and \
    not 'check' in k and \
    not k.endswith('err') and\
    not "1BAC" in k}
sigma_mean
[6]:
{'BAC_APheight': 1.0076752949319177,
 'BAC_ISI': 0.04207239994684291,
 'BAC_caSpike_height': 0.6629272148325343,
 'BAC_caSpike_width': 0.6933473353783755,
 'BAC_spikecount': 0.0,
 'BAC_ahpdepth': 0.1392807122615114,
 'bAP_spikecount': 0.0,
 'bAP_APheight': 1.7319785547438031,
 'bAP_APwidth': 2.0332755883802065,
 'bAP_att2': 1.2386558591918138,
 'bAP_att3': 1.6057535361393405,
 'mf1': 1.7045454545454546,
 'AI1': 0.5066062458183707,
 'ISIcv1': 1.518210025496329,
 'DI1': 0.42387682153444994,
 'TTFS1': 1.5595241133692064,
 'APh1': 0.7275205487209074,
 'fAHPd1': 1.8003008765264834,
 'sAHPd1': 1.0151163975923705,
 'sAHPt1': 0.5495925224841391,
 'APw1': 2.117487156301,
 'mf2': 0.0,
 'AI2': 1.9148094224163767,
 'ISIcv2': 0.5106508765351007,
 'DI2': 1.4694531512688596,
 'TTFS2': 0.125569753657245,
 'APh2': 0.9342151521837061,
 'fAHPd2': 1.4198796199388009,
 'sAHPd2': 0.3243466774696222,
 'sAHPt2': 1.01641064338122,
 'APw2': 1.5324457777590226,
 'mf3': 2.2500225002250023,
 'AI3': 1.701393428841344,
 'ISIcv3': 4.009232626956906,
 'DI3': 2.950213350401774,
 'TTFS3': 0.08393006945539128,
 'APh3': 0.592363621161479,
 'fAHPd3': 1.1277531070124256,
 'sAHPd3': 0.09246104391805444,
 'sAHPt3': 2.109119864685762,
 'APw3': 2.234564571206278}
[7]:
def translate_objective(objective):
    """
    A simple map between objective acronyms and their full name
    """
    step_map = {
        'mf': "Spike frequency",
        'AI': 'Adaptation Index',
        'ISIcv': "Interspike Interval \n Coefficient of Variation",
        'DI': "Initial burst ISI",
        'TTFS': "First spike latency",
        'APh': "AP peak",
        'fAHPd': "Fast AHP Depth",
        'sAHPd': "Slow AHP Depth",
        'sAHPt': "Slow AHP Time",
        'APw': "AP half-width",
    }
    if not "bAP" in objective and not "BAC" in objective:
        # must be step current objective
        t = step_map[objective[:-1]]
    else:
        t = objective
    return t
[8]:
from collections import OrderedDict
I.plt.style.use("fivethirtyeight")
colors = I.plt.rcParams['axes.prop_cycle'].by_key()['color']
fig = I.plt.figure(figsize=(10, 10))
gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, :])

for k, v in sigma_mean.items():
    if "BAC" in k:
        c = colors[0]
        ax = ax1
        label="BAC"
    elif 'bAP' in k:
        c = colors[1]
        ax = ax2
        label="bAP"
    else:
        label = "Step current {}".format(k[-1])
        c = colors[int(k[-1])+1]
        ax = ax3
    ax.barh(y=translate_objective(k), width=v, zorder=5-v, label=label, color=c)

ax1.vlines(3.2, ymin=-.5, ymax=5.5, color='k')
ax2.vlines(3.2, ymin=-.5, ymax=4.5, color='k')
ax3.vlines(4.5, ymin=-.5, ymax=9.5, color='k')

ax1.set_title("bAP stimulus")
ax2.set_title("BAC stimulus")
ax3.set_title("Step current stimuli")
ax3.set_xlabel("Sigma from mean")

handles, labels = I.plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
I.plt.legend(by_label.values(), by_label.keys(), loc="best")
fig.tight_layout()

I.plt.show()
../../_images/tutorials_1._neuron_models_1.2_Evaluation_12_0.png

All objectives are well within the pre-defined bounds. most are even under \(2 \sigma\) from the mean!

Visualization

How much did current ended up flowing through the ion channels? Let’s plot out a spatial integration of all the current types in the apical dendrite. Let’s take a \(BAC\) current injection as an example simulation.

[9]:
from getting_started import example_data_dir
model_db = I.DataBase(I.os.path.join(example_data_dir, "simulation_data", "biophysics"))
example_models = model_db['example_models']
biophysical_parameter_names = [e for e in example_models.columns if "ephys" in e or e == "scale_apical.scale"]
params = example_models[biophysical_parameter_names].iloc[0]

In order to know which currents flow through the membrane, we need to record this. We can configure the simulator to record range variables with a cell_modify_function:

[10]:
cell, biophysical_parameters = simulator.setup.get(params)
record_vars = [
    'NaTa_t.ina',
    'Ca_HVA.ica',
    'Ca_LVAst.ica',
    'SKv3_1.ik',
    'SK_E2.ik',
    'Ih.ihcn',
    'Im.ik'
]

def record_rangevars(cell, params):
    for rv in record_vars:
        cell.record_range_var(rv)
    return cell

simulator.setup.cell_modify_funs.append(('BAC.record_range_vars', record_rangevars))
cell, _ = simulator.get_simulated_cell(biophysical_parameters, stim="BAC")
[11]:
from visualize.current_visualizer import CurrentAnalysis
ca = CurrentAnalysis(cell, rangeVars = record_vars)
fig = ca.plot_areas(plot_voltage = True, t_stim=295)
fig.set_size_inches(8,6)
for ax in fig.axes: ax.grid(False)
../../_images/tutorials_1._neuron_models_1.2_Evaluation_18_0.png

The black and orange line denote membrane voltage at the soma and dendritic recording location respectively. The filled in areas are a spatial sum of ionic current across time: \(I_{ion}(t) = \sum_{dendrites} \int_x I_{ion} dx\)

Animating the membrane voltage

So far, we have only visualized the somatic membrane potential. Now, we create a short animation, where the whole dendrite is visualized.

[12]:
if not 'burst_trail_video' in db.keys():
    I.cell_to_animation(
        cell,
        xlim = [0,1500],
        ylim = [-90, 50],
        tstart = 295,
        tend = 350,
        tstep = 0.2,
        outdir = db.create_managed_folder('burst_trail_video'))
I.display_animation(db['burst_trail_video'].join('*', '*.png'), embedded=True);


Once Loop Reflect

If you want to visualize the entire dendritic tree, that’s also possible, although a bit slower. Here, we use Dask to parallellize frame generation of the membrane voltage across the entire dendritic tree. You can use the +/- buttons to cycle through the frames faster or slower. Alternatively, you play around with the t_step parameter to create mre or less frames per time interval.

[13]:
client = I.get_client()
outdir = I.os.path.join(db.basedir, 'burst_trail_video_3d')

cmv = I.CellMorphologyVisualizer(
    cell,
    t_start=295,    # Don't load all simulation data, only the interesting part
    t_stop=350,     # these are in [ms]
    t_step=1)
cmv.animation(
    images_path=outdir,
    show_legend=True,
    color="voltage",
    client=client,
    overwrite_frames=True);


Once Loop Reflect

These visualization aren’t limited to just the membrane voltage either! Let’s visualize some current flowing through one specific ion channel.

Animating ion currents

We can also visualize current through ion channels. Let’s have a look at the calcium current through low voltage activated calcium channels:

[14]:
# # to re-create this video, comment out below
# del db['burst_trail_ca_current_video']

range_var = 'Ca_LVAst.ica'

if not 'burst_trail_ca_current_video' in db.keys():
    I.cell_to_animation(
        cell,
        xlim = [0,1500],
        ylim = [-0.3, 0.1],
        tstart = 295,
        tend = 350,
        tstep = 0.2,
        outdir = db.create_managed_folder('burst_trail_ca_current_video'),
        range_vars = range_var)

print("{} membrane current".format(range_var))
I.display_animation(db['burst_trail_ca_current_video'].join('*', '*png'), embedded = True);
Ca_LVAst.ica membrane current


Once Loop Reflect
[15]:
outdir = I.os.path.join(db.basedir, 'Ca_LVAst_video_3d')

cmv.set_cmap("RdBu", vmin=-2e-1, vmax=2e-1)  # more visible
cmv.animation(
    images_path=outdir,
    color="Ca_LVAst.ica",
    show_legend=True,
    client=client,
    overwrite_frames=True);


Once Loop Reflect

In the visualization above, not all dendrites are shown, because not all dendrites contain this ion channel.

Recap

We have seen how to run simulations in Tutorial 1.3 and how to evaluate the results of these simulation here. Now, we have all the necessary tools to start building neuron models from scratch. This will be shown in the next tutorial.