bluecellulab 2.6.49__tar.gz → 2.6.51__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bluecellulab might be problematic. Click here for more details.

Files changed (109) hide show
  1. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/PKG-INFO +2 -1
  2. bluecellulab-2.6.51/bluecellulab/analysis/analysis.py +502 -0
  3. bluecellulab-2.6.51/bluecellulab/analysis/utils.py +7 -0
  4. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/bluepy_circuit_access.py +1 -1
  5. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/utils.py +26 -0
  6. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/validation/validation.py +169 -55
  7. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/PKG-INFO +2 -1
  8. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/SOURCES.txt +1 -0
  9. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/requires.txt +1 -0
  10. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/pyproject.toml +1 -0
  11. bluecellulab-2.6.49/bluecellulab/analysis/analysis.py +0 -213
  12. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.compile_mod.sh +0 -0
  13. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.gitattributes +0 -0
  14. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.github/dependabot.yml +0 -0
  15. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.github/workflows/release.yml +0 -0
  16. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.github/workflows/test.yml +0 -0
  17. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.gitignore +0 -0
  18. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.gitlab-ci.yml +0 -0
  19. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.readthedocs.yml +0 -0
  20. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.zenodo.json +0 -0
  21. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/AUTHORS.txt +0 -0
  22. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/CHANGELOG.rst +0 -0
  23. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/CITATION.cff +0 -0
  24. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/CONTRIBUTING.rst +0 -0
  25. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/LICENSE +0 -0
  26. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/MANIFEST.in +0 -0
  27. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/Makefile +0 -0
  28. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/README.rst +0 -0
  29. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/__init__.py +0 -0
  30. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/analysis/__init__.py +0 -0
  31. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/analysis/inject_sequence.py +0 -0
  32. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/analysis/plotting.py +0 -0
  33. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/__init__.py +0 -0
  34. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/ballstick/__init__.py +0 -0
  35. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/ballstick/emodel.hoc +0 -0
  36. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/ballstick/morphology.asc +0 -0
  37. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/cell_dict.py +0 -0
  38. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/core.py +0 -0
  39. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/injector.py +0 -0
  40. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/plotting.py +0 -0
  41. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/random.py +0 -0
  42. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/recording.py +0 -0
  43. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/section_distance.py +0 -0
  44. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/serialized_sections.py +0 -0
  45. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/sonata_proxy.py +0 -0
  46. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/stimuli_generator.py +0 -0
  47. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/template.py +0 -0
  48. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/__init__.py +0 -0
  49. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/__init__.py +0 -0
  50. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/definition.py +0 -0
  51. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/sonata_circuit_access.py +0 -0
  52. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/__init__.py +0 -0
  53. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/bluepy_simulation_config.py +0 -0
  54. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/definition.py +0 -0
  55. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/sections.py +0 -0
  56. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/sonata_simulation_config.py +0 -0
  57. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/format.py +0 -0
  58. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/iotools.py +0 -0
  59. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/node_id.py +0 -0
  60. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/simulation_access.py +0 -0
  61. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/synapse_properties.py +0 -0
  62. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/validate.py +0 -0
  63. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit_simulation.py +0 -0
  64. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/connection.py +0 -0
  65. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/dendrogram.py +0 -0
  66. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/exceptions.py +0 -0
  67. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/graph.py +0 -0
  68. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/Cell.hoc +0 -0
  69. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/RNGSettings.hoc +0 -0
  70. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/TDistFunc.hoc +0 -0
  71. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/TStim.hoc +0 -0
  72. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/fileUtils.hoc +0 -0
  73. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/importer.py +0 -0
  74. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/neuron_interpreter.py +0 -0
  75. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/plotwindow.py +0 -0
  76. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/psection.py +0 -0
  77. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/psegment.py +0 -0
  78. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/rngsettings.py +0 -0
  79. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/__init__.py +0 -0
  80. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/neuron_globals.py +0 -0
  81. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/parallel.py +0 -0
  82. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/simulation.py +0 -0
  83. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/__init__.py +0 -0
  84. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/circuit_stimulus_definitions.py +0 -0
  85. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/factory.py +0 -0
  86. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/stimulus.py +0 -0
  87. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/synapse/__init__.py +0 -0
  88. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/synapse/synapse_factory.py +0 -0
  89. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/synapse/synapse_types.py +0 -0
  90. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/tools.py +0 -0
  91. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/type_aliases.py +0 -0
  92. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/verbosity.py +0 -0
  93. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/dependency_links.txt +0 -0
  94. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/top_level.txt +0 -0
  95. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/Makefile +0 -0
  96. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/images/voltage-readme.png +0 -0
  97. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/make.bat +0 -0
  98. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/requirements_docs.txt +0 -0
  99. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/_static/.gitkeep +0 -0
  100. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/api.rst +0 -0
  101. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/changelog.rst +0 -0
  102. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/compiling-mechanisms.rst +0 -0
  103. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/conf.py +0 -0
  104. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/contributing.rst +0 -0
  105. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/index.rst +0 -0
  106. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/list_of_stim.rst +0 -0
  107. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/logo/BlueCelluLabBanner.jpg +0 -0
  108. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/setup.cfg +0 -0
  109. {bluecellulab-2.6.49 → bluecellulab-2.6.51}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bluecellulab
3
- Version: 2.6.49
3
+ Version: 2.6.51
4
4
  Summary: Biologically detailed neural network simulations and analysis.
5
5
  Author: Blue Brain Project, EPFL
6
6
  License: Apache2.0
@@ -27,6 +27,7 @@ Requires-Dist: pydantic<3.0.0,>=2.5.2
27
27
  Requires-Dist: typing-extensions>=4.8.0
28
28
  Requires-Dist: networkx>=3.1
29
29
  Requires-Dist: h5py>=3.8.0
30
+ Requires-Dist: seaborn
30
31
  Dynamic: license-file
31
32
 
32
33
  |banner|
@@ -0,0 +1,502 @@
1
+ """Module for analyzing cell simulation results."""
2
+ try:
3
+ import efel
4
+ except ImportError:
5
+ efel = None
6
+ from itertools import islice
7
+ from itertools import repeat
8
+ import logging
9
+ from matplotlib.collections import LineCollection
10
+ import matplotlib.pyplot as plt
11
+ from multiprocessing import Pool
12
+ import neuron
13
+ import numpy as np
14
+ import pathlib
15
+ import seaborn as sns
16
+
17
+
18
+ from bluecellulab import Cell
19
+ from bluecellulab.analysis.inject_sequence import run_stimulus
20
+ from bluecellulab.analysis.plotting import plot_iv_curve, plot_fi_curve
21
+ from bluecellulab.analysis.utils import exp_decay
22
+ from bluecellulab.simulation import Simulation
23
+ from bluecellulab.stimulus import StimulusFactory
24
+ from bluecellulab.stimulus.circuit_stimulus_definitions import Hyperpolarizing
25
+ from bluecellulab.tools import calculate_rheobase
26
+
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ def compute_plot_iv_curve(cell,
32
+ injecting_section="soma[0]",
33
+ injecting_segment=0.5,
34
+ recording_section="soma[0]",
35
+ recording_segment=0.5,
36
+ stim_start=100.0,
37
+ duration=500.0,
38
+ post_delay=100.0,
39
+ threshold_voltage=-20,
40
+ nb_bins=11,
41
+ rheobase=None,
42
+ show_figure=True,
43
+ save_figure=False,
44
+ output_dir="./",
45
+ output_fname="iv_curve.pdf"):
46
+ """Compute and plot the Current-Voltage (I-V) curve for a given cell by
47
+ injecting a range of currents.
48
+
49
+ This function evaluates the relationship between the injected current amplitude and the resulting
50
+ steady-state membrane potential of a neuronal cell model. Currents are injected at a specified section
51
+ and segment, and the steady-state voltage at the recording location is used to construct the I-V curve.
52
+
53
+ Args:
54
+ cell (bluecellulab.cell.Cell): The initialized BlueCelluLab cell model.
55
+ injecting_section (str, optional): The name of the section where the stimulus is injected.
56
+ Default is "soma[0]".
57
+ injecting_segment (float, optional): The position along the injecting section (0.0 to 1.0)
58
+ where the stimulus is applied. Default is 0.5.
59
+ recording_section (str, optional): The name of the section where the voltage is recorded.
60
+ Default is "soma[0]".
61
+ recording_segment (float, optional): The position along the recording section (0.0 to 1.0)
62
+ where the voltage is recorded. Default is 0.5.
63
+ stim_start (float, optional): The start time of the current injection (in ms). Default is 100.0 ms.
64
+ duration (float, optional): The duration of the current injection (in ms). Default is 500.0 ms.
65
+ post_delay (float, optional): The delay after the stimulation ends before the simulation stops
66
+ (in ms). Default is 100.0 ms.
67
+ threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
68
+ response. Default is -20 mV.
69
+ nb_bins (int, optional): The number of discrete current levels between 0 and the maximum current.
70
+ Default is 11.
71
+ rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
72
+ be calculated using the `calculate_rheobase` function.
73
+ show_figure (bool): Whether to display the figure. Default is True.
74
+ save_figure (bool): Whether to save the figure. Default is False.
75
+ output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
76
+ output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.png".
77
+
78
+ Returns:
79
+ tuple: A tuple containing:
80
+ - list_amp (np.ndarray): The injected current amplitudes (nA).
81
+ - steady_states (np.ndarray): The corresponding steady-state voltages (mV) recorded at the
82
+ specified location.
83
+
84
+ Raises:
85
+ ValueError: If the cell object is invalid, the specified sections/segments are not found, or if
86
+ the simulation results are inconsistent.
87
+ """
88
+ if rheobase is None:
89
+ rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
90
+
91
+ list_amp = np.linspace(rheobase - 2, rheobase - 0.1, nb_bins) # [nA]
92
+
93
+ # inject step current and record voltage response
94
+ stim_factory = StimulusFactory(dt=0.1)
95
+ steps = [
96
+ stim_factory.step(pre_delay=stim_start, duration=duration, post_delay=post_delay, amplitude=amp)
97
+ for amp in list_amp
98
+ ]
99
+
100
+ with Pool(len(steps)) as p:
101
+ recordings = p.starmap(
102
+ run_stimulus,
103
+ zip(
104
+ repeat(cell.template_params),
105
+ steps,
106
+ repeat(injecting_section),
107
+ repeat(injecting_segment),
108
+ repeat(True), # cvode
109
+ repeat(True), # add_hypamp
110
+ repeat(recording_section),
111
+ repeat(recording_segment),
112
+ )
113
+ )
114
+
115
+ steady_states = []
116
+ # compute steady state response
117
+ efel.set_setting('Threshold', threshold_voltage)
118
+ for recording in recordings:
119
+ trace = {
120
+ 'T': recording.time,
121
+ 'V': recording.voltage,
122
+ 'stim_start': [stim_start],
123
+ 'stim_end': [stim_start + duration]
124
+ }
125
+ features_results = efel.get_feature_values([trace], ['steady_state_voltage_stimend'])
126
+ steady_state = features_results[0]['steady_state_voltage_stimend'][0]
127
+ steady_states.append(steady_state)
128
+
129
+ plot_iv_curve(list_amp,
130
+ steady_states,
131
+ injecting_section=injecting_section,
132
+ injecting_segment=injecting_segment,
133
+ recording_section=recording_section,
134
+ recording_segment=recording_segment,
135
+ show_figure=show_figure,
136
+ save_figure=save_figure,
137
+ output_dir=output_dir,
138
+ output_fname=output_fname)
139
+
140
+ return np.array(list_amp), np.array(steady_states)
141
+
142
+
143
+ def compute_plot_fi_curve(cell,
144
+ injecting_section="soma[0]",
145
+ injecting_segment=0.5,
146
+ recording_section="soma[0]",
147
+ recording_segment=0.5,
148
+ stim_start=100.0,
149
+ duration=500.0,
150
+ post_delay=100.0,
151
+ max_current=0.8,
152
+ threshold_voltage=-20,
153
+ nb_bins=11,
154
+ rheobase=None,
155
+ show_figure=True,
156
+ save_figure=False,
157
+ output_dir="./",
158
+ output_fname="fi_curve.pdf"):
159
+ """Compute and plot the Frequency-Current (F-I) curve for a given cell by
160
+ injecting a range of currents.
161
+
162
+ This function evaluates the relationship between injected current amplitude and the firing rate
163
+ of a neuronal cell model. Currents are injected at a specified section and segment, and the number
164
+ of spikes recorded in the specified recording location is used to construct the F-I curve.
165
+
166
+ Args:
167
+ cell (bluecellulab.cell.Cell): The initialized BlueCelluLab cell model.
168
+ injecting_section (str, optional): The name of the section where the stimulus is injected.
169
+ Default is "soma[0]".
170
+ injecting_segment (float, optional): The position along the injecting section (0.0 to 1.0)
171
+ where the stimulus is applied. Default is 0.5.
172
+ recording_section (str, optional): The name of the section where spikes are recorded.
173
+ Default is "soma[0]".
174
+ recording_segment (float, optional): The position along the recording section (0.0 to 1.0)
175
+ where spikes are recorded. Default is 0.5.
176
+ stim_start (float, optional): The start time of the current injection (in ms). Default is 100.0 ms.
177
+ duration (float, optional): The duration of the current injection (in ms). Default is 500.0 ms.
178
+ post_delay (float, optional): The delay after the stimulation ends before the simulation stops
179
+ (in ms). Default is 100.0 ms.
180
+ max_current (float, optional): The maximum amplitude of the injected current (in nA).
181
+ Default is 0.8 nA.
182
+ threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
183
+ response. Default is -20 mV.
184
+ nb_bins (int, optional): The number of discrete current levels between 0 and `max_current`.
185
+ Default is 11.
186
+ rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
187
+ be calculated using the `calculate_rheobase` function.
188
+ show_figure (bool): Whether to display the figure. Default is True.
189
+ save_figure (bool): Whether to save the figure. Default is False.
190
+ output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
191
+ output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.png".
192
+
193
+ Returns:
194
+ tuple: A tuple containing:
195
+ - list_amp (np.ndarray): The injected current amplitudes (nA).
196
+ - spike_count (np.ndarray): The corresponding spike counts for each current amplitude.
197
+
198
+ Raises:
199
+ ValueError: If the cell object is invalid or the specified sections/segments are not found.
200
+ """
201
+ if rheobase is None:
202
+ rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
203
+
204
+ list_amp = np.linspace(rheobase, max_current, nb_bins) # [nA]
205
+ stim_factory = StimulusFactory(dt=0.1)
206
+ steps = [
207
+ stim_factory.step(pre_delay=stim_start, duration=duration, post_delay=post_delay, amplitude=amp)
208
+ for amp in list_amp
209
+ ]
210
+
211
+ with Pool(len(steps)) as p:
212
+ recordings = p.starmap(
213
+ run_stimulus,
214
+ zip(
215
+ repeat(cell.template_params),
216
+ steps,
217
+ repeat(injecting_section),
218
+ repeat(injecting_segment),
219
+ repeat(True), # cvode
220
+ repeat(True), # add_hypamp
221
+ repeat(recording_section),
222
+ repeat(recording_segment),
223
+ repeat(True), # enable_spike_detection
224
+ repeat(threshold_voltage), # threshold_spike_detection
225
+ )
226
+ )
227
+
228
+ spike_count = [len(recording.spike) for recording in recordings]
229
+
230
+ plot_fi_curve(list_amp,
231
+ spike_count,
232
+ injecting_section=injecting_section,
233
+ injecting_segment=injecting_segment,
234
+ recording_section=recording_section,
235
+ recording_segment=recording_segment,
236
+ show_figure=show_figure,
237
+ save_figure=save_figure,
238
+ output_dir=output_dir,
239
+ output_fname=output_fname)
240
+
241
+ return np.array(list_amp), np.array(spike_count)
242
+
243
+
244
+ class BPAP:
245
+ # taken from the examples
246
+
247
+ def __init__(self, cell: Cell) -> None:
248
+ self.cell = cell
249
+ self.dt = 0.025
250
+ self.stim_start = 1000
251
+ self.stim_duration = 3
252
+ self.basal_cmap = sns.color_palette("crest", as_cmap=True)
253
+ self.apical_cmap = sns.color_palette("YlOrBr_r", as_cmap=True)
254
+
255
+ @property
256
+ def start_index(self) -> int:
257
+ """Get the index of the start of the stimulus."""
258
+ return int(self.stim_start / self.dt)
259
+
260
+ @property
261
+ def end_index(self) -> int:
262
+ """Get the index of the end of the stimulus."""
263
+ return int((self.stim_start + self.stim_duration) / self.dt)
264
+
265
+ def get_recordings(self):
266
+ """Get the soma, basal and apical recordings."""
267
+ all_recordings = self.cell.get_allsections_voltagerecordings()
268
+ soma_rec = None
269
+ dend_rec = {}
270
+ apic_rec = {}
271
+ for key, value in all_recordings.items():
272
+ if "soma" in key:
273
+ soma_rec = value
274
+ elif "dend" in key:
275
+ dend_rec[key] = value
276
+ elif "apic" in key:
277
+ apic_rec[key] = value
278
+
279
+ return soma_rec, dend_rec, apic_rec
280
+
281
+ def run(self, duration: float, amplitude: float) -> None:
282
+ """Apply depolarization and hyperpolarization at the same time."""
283
+ sim = Simulation()
284
+ sim.add_cell(self.cell)
285
+ self.cell.add_allsections_voltagerecordings()
286
+ self.cell.add_step(start_time=self.stim_start, stop_time=self.stim_start + self.stim_duration, level=amplitude)
287
+ hyperpolarizing = Hyperpolarizing("single-cell", delay=0, duration=duration)
288
+ self.cell.add_replay_hypamp(hyperpolarizing)
289
+ sim.run(duration, dt=self.dt, cvode=False)
290
+
291
+ def amplitudes(self, recs) -> list[float]:
292
+ """Return amplitude across given sections."""
293
+ efel_feature_name = "maximum_voltage_from_voltagebase"
294
+ traces = [
295
+ {
296
+ 'T': self.cell.get_time(),
297
+ 'V': rec,
298
+ 'stim_start': [self.stim_start],
299
+ 'stim_end': [self.stim_start + self.stim_duration]
300
+ }
301
+ for rec in recs.values()
302
+ ]
303
+ features_results = efel.get_feature_values(traces, [efel_feature_name])
304
+ amps = [feat_res[efel_feature_name][0] for feat_res in features_results]
305
+
306
+ return amps
307
+
308
+ def distances_to_soma(self, recs) -> list[float]:
309
+ """Return the distance to the soma for each section."""
310
+ res = []
311
+ soma = self.cell.soma
312
+ for key in recs.keys():
313
+ section_name = key.rsplit(".")[-1].split("[")[0] # e.g. "dend"
314
+ section_idx = int(key.rsplit(".")[-1].split("[")[1].split("]")[0]) # e.g. 0
315
+ attribute_value = getattr(self.cell.cell.getCell(), section_name)
316
+ section = next(islice(attribute_value, section_idx, None))
317
+ # section e.g. cADpyr_L2TPC_bluecellulab_x[0].dend[0]
318
+ res.append(neuron.h.distance(soma(0.5), section(0.5)))
319
+ return res
320
+
321
+ def get_amplitudes_and_distances(self):
322
+ soma_rec, dend_rec, apic_rec = self.get_recordings()
323
+ soma_amp = self.amplitudes({"soma": soma_rec})[0]
324
+ dend_amps = None
325
+ dend_dist = None
326
+ apic_amps = None
327
+ apic_dist = None
328
+ if dend_rec:
329
+ dend_amps = self.amplitudes(dend_rec)
330
+ dend_dist = self.distances_to_soma(dend_rec)
331
+ if apic_rec:
332
+ apic_amps = self.amplitudes(apic_rec)
333
+ apic_dist = self.distances_to_soma(apic_rec)
334
+
335
+ return soma_amp, dend_amps, dend_dist, apic_amps, apic_dist
336
+
337
+ def fit(self, soma_amp, dend_amps, dend_dist, apic_amps, apic_dist):
338
+ """Fit the amplitudes vs distances to an exponential decay function."""
339
+ from scipy.optimize import curve_fit
340
+
341
+ popt_dend = None
342
+ if dend_amps and dend_dist:
343
+ dist = [0] + dend_dist # add soma distance
344
+ amps = [soma_amp] + dend_amps # add soma amplitude
345
+ popt_dend, _ = curve_fit(exp_decay, dist, amps)
346
+
347
+ popt_apic = None
348
+ if apic_amps and apic_dist:
349
+ dist = [0] + apic_dist # add soma distance
350
+ amps = [soma_amp] + apic_amps # add soma amplitude
351
+ popt_apic, _ = curve_fit(exp_decay, dist, amps)
352
+
353
+ return popt_dend, popt_apic
354
+
355
+ def validate(self, soma_amp, dend_amps, dend_dist, apic_amps, apic_dist):
356
+ """Check that the exponential fit is decaying."""
357
+ validated = True
358
+ notes = ""
359
+ popt_dend, popt_apic = self.fit(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
360
+ if popt_dend is None:
361
+ logger.debug("No dendritic recordings found.")
362
+ notes += "No dendritic recordings found.\n"
363
+ elif popt_dend[1] <= 0:
364
+ logger.debug("Dendritic fit is not decaying.")
365
+ validated = False
366
+ notes += "Dendritic fit is not decaying.\n"
367
+ else:
368
+ notes += "Dendritic validation passed: dendritic amplitude is decaying with distance relative to soma.\n"
369
+ if popt_apic is None:
370
+ logger.debug("No apical recordings found.")
371
+ notes += "No apical recordings found.\n"
372
+ elif popt_apic[1] <= 0:
373
+ logger.debug("Apical fit is not decaying.")
374
+ validated = False
375
+ notes += "Apical fit is not decaying.\n"
376
+ else:
377
+ notes += "Apical validation passed: apical amplitude is decaying with distance relative to soma.\n"
378
+
379
+ return validated, notes
380
+
381
+ def plot_amp_vs_dist(
382
+ self,
383
+ soma_amp,
384
+ dend_amps,
385
+ dend_dist,
386
+ apic_amps,
387
+ apic_dist,
388
+ show_figure=True,
389
+ save_figure=False,
390
+ output_dir="./",
391
+ output_fname="bpap.pdf",
392
+ ):
393
+ """Plot the results of the BPAP analysis."""
394
+ popt_dend, popt_apic = self.fit(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
395
+
396
+ outpath = pathlib.Path(output_dir) / output_fname
397
+ fig, ax1 = plt.subplots(figsize=(10, 6))
398
+ ax1.scatter([0], [soma_amp], marker="^", color='black', label='Soma')
399
+ if dend_amps and dend_dist:
400
+ ax1.scatter(
401
+ dend_dist,
402
+ dend_amps,
403
+ c=dend_dist,
404
+ cmap=self.basal_cmap,
405
+ label='Basal Dendrites',
406
+ )
407
+ if popt_dend is not None:
408
+ x = np.linspace(0, max(dend_dist), 100)
409
+ y = exp_decay(x, *popt_dend)
410
+ ax1.plot(x, y, color='darkgreen', linestyle='--', label='Basal Dendritic Fit')
411
+ if apic_amps and apic_dist:
412
+ ax1.scatter(
413
+ apic_dist,
414
+ apic_amps,
415
+ c=apic_dist,
416
+ cmap=self.apical_cmap,
417
+ label='Apical Dendrites'
418
+ )
419
+ if popt_apic is not None:
420
+ x = np.linspace(0, max(apic_dist), 100)
421
+ y = exp_decay(x, *popt_apic)
422
+ ax1.plot(x, y, color='goldenrod', linestyle='--', label='Apical Fit')
423
+ ax1.set_xlabel('Distance to Soma (um)')
424
+ ax1.set_ylabel('Amplitude (mV)')
425
+ ax1.legend()
426
+ fig.suptitle('Back-propagating Action Potential Analysis')
427
+ fig.tight_layout()
428
+ if save_figure:
429
+ fig.savefig(outpath)
430
+ if show_figure:
431
+ plt.show()
432
+
433
+ return outpath
434
+
435
+ def plot_one_axis_recordings(self, fig, ax, rec_list, dist, cmap):
436
+ """Plot the soma and dendritic recordings on one axis.
437
+
438
+ Args:
439
+ fig (matplotlib.figure.Figure): The figure to plot on.
440
+ ax (matplotlib.axes.Axes): The axis to plot on.
441
+ rec_list (list): List of recordings to plot.
442
+ dist (list): List of distances from the soma for each recording.
443
+ cmap (matplotlib.colors.Colormap): Colormap to use for the recordings.
444
+ """
445
+ time = self.cell.get_time()
446
+ line_collection = LineCollection(
447
+ [np.column_stack([time, rec]) for rec in rec_list],
448
+ array=dist,
449
+ cmap=cmap,
450
+ )
451
+ ax.set_xlim(
452
+ self.stim_start - 0.1,
453
+ self.stim_start + 30
454
+ )
455
+ ax.set_ylim(
456
+ min([min(rec[self.start_index:]) for rec in rec_list]) - 2,
457
+ max([max(rec[self.start_index:]) for rec in rec_list]) + 2
458
+ )
459
+ ax.add_collection(line_collection)
460
+ fig.colorbar(line_collection, label="soma distance (um)", ax=ax)
461
+
462
+ def plot_recordings(
463
+ self,
464
+ show_figure=True,
465
+ save_figure=False,
466
+ output_dir="./",
467
+ output_fname="bpap_recordings.pdf",
468
+ ):
469
+ """Plot the recordings from all dendrites."""
470
+ soma_rec, dend_rec, apic_rec = self.get_recordings()
471
+ dend_dist = []
472
+ apic_dist = []
473
+ if dend_rec:
474
+ dend_dist = self.distances_to_soma(dend_rec)
475
+ if apic_rec:
476
+ apic_dist = self.distances_to_soma(apic_rec)
477
+ # add soma_rec to the lists
478
+ dend_rec_list = [soma_rec] + list(dend_rec.values())
479
+ dend_dist = [0] + dend_dist
480
+ apic_rec_list = [soma_rec] + list(apic_rec.values())
481
+ apic_dist = [0] + apic_dist
482
+
483
+ outpath = pathlib.Path(output_dir) / output_fname
484
+ fig, (ax1, ax2) = plt.subplots(figsize=(10, 12), nrows=2, sharex=True)
485
+
486
+ self.plot_one_axis_recordings(fig, ax1, dend_rec_list, dend_dist, self.basal_cmap)
487
+ self.plot_one_axis_recordings(fig, ax2, apic_rec_list, apic_dist, self.apical_cmap)
488
+
489
+ # plt.setp(ax1.get_xticklabels(), visible=False)
490
+ ax1.set_title('Basal Dendritic Recordings')
491
+ ax2.set_title('Apical Dendritic Recordings')
492
+ ax1.set_ylabel('Voltage (mV)')
493
+ ax2.set_ylabel('Voltage (mV)')
494
+ ax2.set_xlabel('Time (ms)')
495
+ fig.suptitle('Back-propagating Action Potential Recordings')
496
+ fig.tight_layout()
497
+ if save_figure:
498
+ fig.savefig(outpath)
499
+ if show_figure:
500
+ plt.show()
501
+
502
+ return outpath
@@ -0,0 +1,7 @@
1
+ """Utility functions for analysis."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def exp_decay(x, a, b, c):
7
+ return a * np.exp(-b * x) + c
@@ -233,7 +233,7 @@ class BluepyCircuitAccess:
233
233
  source_popid, target_popid = zip(*pop_ids)
234
234
 
235
235
  result = result.assign(
236
- source_popid=source_popid, target_popid=target_popid
236
+ source_popid=pd.Series(source_popid), target_popid=pd.Series(target_popid)
237
237
  )
238
238
 
239
239
  if result.empty:
@@ -4,6 +4,8 @@ from __future__ import annotations
4
4
  import contextlib
5
5
  import io
6
6
  import json
7
+ import multiprocessing
8
+ from multiprocessing import pool
7
9
 
8
10
  import numpy as np
9
11
 
@@ -56,3 +58,27 @@ class NumpyEncoder(json.JSONEncoder):
56
58
  elif isinstance(obj, np.ndarray):
57
59
  return obj.tolist()
58
60
  return json.JSONEncoder.default(self, obj)
61
+
62
+
63
+ class NoDaemonProcess(multiprocessing.Process):
64
+ """Class that represents a non-daemon process."""
65
+
66
+ # pylint: disable=dangerous-default-value
67
+
68
+ def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
69
+ """Ensures group=None, for macosx."""
70
+ super().__init__(group=None, target=target, name=name, args=args, kwargs=kwargs)
71
+
72
+ @property
73
+ def daemon(self):
74
+ return False
75
+
76
+ @daemon.setter
77
+ def daemon(self, val):
78
+ pass
79
+
80
+
81
+ class NestedPool(pool.Pool): # pylint: disable=abstract-method
82
+ """Class that represents a MultiProcessing nested pool."""
83
+
84
+ Process = NoDaemonProcess