bluecellulab 2.2.7__py3-none-any.whl → 2.3.2__py3-none-any.whl
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.
- bluecellulab/cell/core.py +34 -96
- bluecellulab/cell/injector.py +72 -55
- bluecellulab/cell/plotting.py +0 -1
- bluecellulab/cell/serialized_sections.py +3 -3
- bluecellulab/circuit/config/bluepy_simulation_config.py +1 -1
- bluecellulab/circuit/config/definition.py +1 -1
- bluecellulab/circuit/config/sonata_simulation_config.py +1 -1
- bluecellulab/dendrogram.py +61 -17
- bluecellulab/psection.py +71 -70
- bluecellulab/psegment.py +2 -2
- bluecellulab/ssim.py +11 -11
- bluecellulab/stimulus/__init__.py +1 -0
- bluecellulab/{stimuli.py → stimulus/circuit_stimulus_definitions.py} +4 -0
- bluecellulab/stimulus/factory.py +254 -0
- bluecellulab/synapse/synapse_factory.py +2 -2
- {bluecellulab-2.2.7.dist-info → bluecellulab-2.3.2.dist-info}/METADATA +1 -1
- {bluecellulab-2.2.7.dist-info → bluecellulab-2.3.2.dist-info}/RECORD +21 -19
- {bluecellulab-2.2.7.dist-info → bluecellulab-2.3.2.dist-info}/AUTHORS.txt +0 -0
- {bluecellulab-2.2.7.dist-info → bluecellulab-2.3.2.dist-info}/LICENSE +0 -0
- {bluecellulab-2.2.7.dist-info → bluecellulab-2.3.2.dist-info}/WHEEL +0 -0
- {bluecellulab-2.2.7.dist-info → bluecellulab-2.3.2.dist-info}/top_level.txt +0 -0
bluecellulab/cell/core.py
CHANGED
|
@@ -27,12 +27,11 @@ import numpy as np
|
|
|
27
27
|
import pandas as pd
|
|
28
28
|
|
|
29
29
|
import bluecellulab
|
|
30
|
-
from bluecellulab import
|
|
30
|
+
from bluecellulab.psection import PSection, init_psections
|
|
31
31
|
from bluecellulab.cell.injector import InjectableMixin
|
|
32
32
|
from bluecellulab.cell.plotting import PlottableMixin
|
|
33
33
|
from bluecellulab.cell.section_distance import EuclideanSectionDistance
|
|
34
34
|
from bluecellulab.cell.sonata_proxy import SonataProxy
|
|
35
|
-
from bluecellulab.cell.serialized_sections import SerializedSections
|
|
36
35
|
from bluecellulab.cell.template import NeuronTemplate, public_hoc_cell
|
|
37
36
|
from bluecellulab.circuit.config.sections import Conditions
|
|
38
37
|
from bluecellulab.circuit import EmodelProperties, SynapseProperty
|
|
@@ -42,7 +41,7 @@ from bluecellulab.exceptions import BluecellulabError
|
|
|
42
41
|
from bluecellulab.importer import load_hoc_and_mod_files
|
|
43
42
|
from bluecellulab.neuron_interpreter import eval_neuron
|
|
44
43
|
from bluecellulab.rngsettings import RNGSettings
|
|
45
|
-
from bluecellulab.
|
|
44
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import SynapseReplay
|
|
46
45
|
from bluecellulab.synapse import SynapseFactory, Synapse
|
|
47
46
|
from bluecellulab.synapse.synapse_types import SynapseID
|
|
48
47
|
from bluecellulab.type_aliases import HocObjectType, NeuronSection
|
|
@@ -99,8 +98,6 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
99
98
|
# diameters of the loaded morph are wrong
|
|
100
99
|
neuron.h.finitialize()
|
|
101
100
|
|
|
102
|
-
self.cellname = neuron.h.secname(sec=self.soma).split(".")[0]
|
|
103
|
-
|
|
104
101
|
if rng_settings is None:
|
|
105
102
|
self.rng_settings = RNGSettings("Random123") # SONATA value
|
|
106
103
|
else:
|
|
@@ -112,25 +109,17 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
112
109
|
|
|
113
110
|
self.ips: dict[SynapseID, HocObjectType] = {}
|
|
114
111
|
self.syn_mini_netcons: dict[SynapseID, HocObjectType] = {}
|
|
115
|
-
self.serialized: Optional[SerializedSections] = None
|
|
116
112
|
|
|
117
113
|
# Be careful when removing this,
|
|
118
114
|
# time recording needs this push
|
|
119
115
|
self.soma.push()
|
|
120
116
|
self.hocname = neuron.h.secname(sec=self.soma).split(".")[0]
|
|
121
|
-
self.somatic = list(public_hoc_cell(self.cell).somatic)
|
|
122
|
-
self.basal = list(public_hoc_cell(self.cell).basal) # dend is same as basal
|
|
123
|
-
self.apical = list(public_hoc_cell(self.cell).apical)
|
|
124
|
-
self.axonal = list(public_hoc_cell(self.cell).axonal)
|
|
125
|
-
self.all = list(public_hoc_cell(self.cell).all)
|
|
126
117
|
self.record_dt = record_dt
|
|
127
118
|
self.add_recordings(['self.soma(0.5)._ref_v', 'neuron.h._ref_t'],
|
|
128
119
|
dt=self.record_dt)
|
|
129
120
|
|
|
130
121
|
self.delayed_weights = queue.PriorityQueue() # type: ignore
|
|
131
|
-
self.
|
|
132
|
-
self.secname_to_hsection: dict[str, HocObjectType] = {}
|
|
133
|
-
self.secname_to_psection: dict[str, psection.PSection] = {}
|
|
122
|
+
self.psections, self.secname_to_psection = init_psections(public_hoc_cell(self.cell))
|
|
134
123
|
|
|
135
124
|
self.emodel_properties = emodel_properties
|
|
136
125
|
if template_format == 'v6':
|
|
@@ -153,12 +142,29 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
153
142
|
# Used to know when re_init_rng() can be executed
|
|
154
143
|
self.is_made_passive = False
|
|
155
144
|
|
|
156
|
-
self.psections: dict[int, psection.PSection] = {}
|
|
157
|
-
|
|
158
145
|
neuron.h.pop_section() # Undoing soma push
|
|
159
|
-
# self.init_psections()
|
|
160
146
|
self.sonata_proxy: Optional[SonataProxy] = None
|
|
161
147
|
|
|
148
|
+
@property
|
|
149
|
+
def somatic(self) -> list[NeuronSection]:
|
|
150
|
+
return list(public_hoc_cell(self.cell).somatic)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def basal(self) -> list[NeuronSection]:
|
|
154
|
+
return list(public_hoc_cell(self.cell).basal)
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def apical(self) -> list[NeuronSection]:
|
|
158
|
+
return list(public_hoc_cell(self.cell).apical)
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def axonal(self) -> list[NeuronSection]:
|
|
162
|
+
return list(public_hoc_cell(self.cell).axonal)
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def all(self) -> list[NeuronSection]:
|
|
166
|
+
return list(public_hoc_cell(self.cell).all)
|
|
167
|
+
|
|
162
168
|
def __repr__(self) -> str:
|
|
163
169
|
base_info = f"Cell Object: {super().__repr__()}"
|
|
164
170
|
hoc_info = f"NEURON ID: {self.template_id}"
|
|
@@ -168,47 +174,6 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
168
174
|
"""Connect this cell to a circuit via sonata proxy."""
|
|
169
175
|
self.sonata_proxy = sonata_proxy
|
|
170
176
|
|
|
171
|
-
def init_psections(self) -> None:
|
|
172
|
-
"""Initialize the psections list.
|
|
173
|
-
|
|
174
|
-
This list contains the Python representation of the psections of
|
|
175
|
-
this morphology.
|
|
176
|
-
"""
|
|
177
|
-
for hsection in self.all:
|
|
178
|
-
secname = neuron.h.secname(sec=hsection)
|
|
179
|
-
self.secname_to_hsection[secname] = hsection
|
|
180
|
-
self.secname_to_psection[secname] = psection.PSection(hsection)
|
|
181
|
-
|
|
182
|
-
# section are not serialized yet, do it now
|
|
183
|
-
if self.serialized is None:
|
|
184
|
-
self.serialized = SerializedSections(public_hoc_cell(self.cell))
|
|
185
|
-
|
|
186
|
-
for isec in self.serialized.isec2sec:
|
|
187
|
-
hsection = self.get_hsection(isec)
|
|
188
|
-
if hsection:
|
|
189
|
-
secname = neuron.h.secname(sec=hsection)
|
|
190
|
-
self.psections[isec] = self.secname_to_psection[secname]
|
|
191
|
-
self.psections[isec].isec = isec
|
|
192
|
-
self.secname_to_isec[secname] = isec
|
|
193
|
-
|
|
194
|
-
# Set the parents and children of all the psections
|
|
195
|
-
for psec in self.psections.values():
|
|
196
|
-
hparent = psec.hparent
|
|
197
|
-
if hparent:
|
|
198
|
-
parentname = neuron.h.secname(sec=hparent)
|
|
199
|
-
psec.pparent = self.get_psection(secname=parentname)
|
|
200
|
-
else:
|
|
201
|
-
psec.pparent = None
|
|
202
|
-
|
|
203
|
-
for hchild in psec.hchildren:
|
|
204
|
-
childname = neuron.h.secname(sec=hchild)
|
|
205
|
-
pchild = self.get_psection(secname=childname)
|
|
206
|
-
psec.add_pchild(pchild)
|
|
207
|
-
|
|
208
|
-
def get_section_id(self, secname: str) -> int:
|
|
209
|
-
"""Returns the id of the section with name secname."""
|
|
210
|
-
return self.secname_to_psection[secname].isec
|
|
211
|
-
|
|
212
177
|
def re_init_rng(self, use_random123_stochkv: bool = False) -> None:
|
|
213
178
|
"""Reinitialize the random number generator for stochastic channels."""
|
|
214
179
|
|
|
@@ -233,44 +198,16 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
233
198
|
else:
|
|
234
199
|
self.cell.re_init_rng()
|
|
235
200
|
|
|
236
|
-
def get_psection(self, section_id
|
|
237
|
-
"""Return a python section with the specified section id
|
|
238
|
-
|
|
239
|
-
Parameters
|
|
240
|
-
----------
|
|
241
|
-
section_id: int
|
|
242
|
-
Return the PSection object based on section id
|
|
243
|
-
secname: string
|
|
244
|
-
Return the PSection object based on section name
|
|
245
|
-
|
|
246
|
-
Returns
|
|
247
|
-
-------
|
|
248
|
-
psection: PSection
|
|
249
|
-
PSection object of the specified section id or name
|
|
250
|
-
"""
|
|
251
|
-
if section_id is not None:
|
|
201
|
+
def get_psection(self, section_id: int | str) -> PSection:
|
|
202
|
+
"""Return a python section with the specified section id."""
|
|
203
|
+
if isinstance(section_id, int):
|
|
252
204
|
return self.psections[section_id]
|
|
253
|
-
elif
|
|
254
|
-
return self.secname_to_psection[
|
|
205
|
+
elif isinstance(section_id, str):
|
|
206
|
+
return self.secname_to_psection[section_id]
|
|
255
207
|
else:
|
|
256
|
-
raise
|
|
257
|
-
"
|
|
258
|
-
|
|
259
|
-
def get_hsection(self, section_id: int | float) -> NeuronSection:
|
|
260
|
-
"""Use the serialized object to find a hoc section from a section
|
|
261
|
-
id."""
|
|
262
|
-
section_id = int(section_id)
|
|
263
|
-
# section are not serialized yet, do it now
|
|
264
|
-
if self.serialized is None:
|
|
265
|
-
self.serialized = SerializedSections(public_hoc_cell(self.cell))
|
|
266
|
-
|
|
267
|
-
try:
|
|
268
|
-
sec_ref = self.serialized.isec2sec[section_id]
|
|
269
|
-
except IndexError as e:
|
|
270
|
-
raise IndexError(
|
|
271
|
-
f"bluecellulab get_hsection: section-id {section_id} not found in {self.morphology_path}"
|
|
272
|
-
) from e
|
|
273
|
-
return sec_ref.sec
|
|
208
|
+
raise BluecellulabError(
|
|
209
|
+
f"Section id must be an int or a str, not {type(section_id)}"
|
|
210
|
+
)
|
|
274
211
|
|
|
275
212
|
def make_passive(self) -> None:
|
|
276
213
|
"""Make the cell passive by deactivating all the active channels."""
|
|
@@ -551,7 +488,8 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
551
488
|
|
|
552
489
|
base_seed = self.rng_settings.base_seed
|
|
553
490
|
weight = syn_description[SynapseProperty.G_SYNX]
|
|
554
|
-
|
|
491
|
+
# numpy int to int
|
|
492
|
+
post_sec_id = int(syn_description[SynapseProperty.POST_SECTION_ID])
|
|
555
493
|
|
|
556
494
|
location = SynapseFactory.determine_synapse_location(
|
|
557
495
|
syn_description, self
|
|
@@ -572,7 +510,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
572
510
|
spont_minis_rate = inh_mini_frequency
|
|
573
511
|
|
|
574
512
|
if spont_minis_rate is not None and spont_minis_rate > 0:
|
|
575
|
-
sec = self.
|
|
513
|
+
sec = self.get_psection(post_sec_id).hsection
|
|
576
514
|
# add the *minis*: spontaneous synaptic events
|
|
577
515
|
self.ips[synapse_id] = neuron.h.\
|
|
578
516
|
InhPoissonStim(location, sec=sec)
|
bluecellulab/cell/injector.py
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
"""Contains injection functionality for the cell."""
|
|
15
|
-
|
|
15
|
+
from __future__ import annotations
|
|
16
16
|
import math
|
|
17
17
|
import warnings
|
|
18
18
|
import logging
|
|
@@ -27,7 +27,7 @@ from bluecellulab.cell.stimuli_generator import (
|
|
|
27
27
|
get_relative_shotnoise_params,
|
|
28
28
|
)
|
|
29
29
|
from bluecellulab.exceptions import BluecellulabError
|
|
30
|
-
from bluecellulab.
|
|
30
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import (
|
|
31
31
|
ClampMode,
|
|
32
32
|
Hyperpolarizing,
|
|
33
33
|
Noise,
|
|
@@ -36,7 +36,8 @@ from bluecellulab.stimuli import (
|
|
|
36
36
|
RelativeOrnsteinUhlenbeck,
|
|
37
37
|
RelativeShotNoise,
|
|
38
38
|
)
|
|
39
|
-
from bluecellulab.
|
|
39
|
+
from bluecellulab.stimulus.factory import StimulusFactory
|
|
40
|
+
from bluecellulab.type_aliases import NeuronSection
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
logger = logging.getLogger(__name__)
|
|
@@ -69,36 +70,62 @@ class InjectableMixin:
|
|
|
69
70
|
self.persistent.append(tstim)
|
|
70
71
|
return tstim
|
|
71
72
|
|
|
72
|
-
def add_step(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
def add_step(
|
|
74
|
+
self,
|
|
75
|
+
start_time: float,
|
|
76
|
+
stop_time: float,
|
|
77
|
+
level: float,
|
|
78
|
+
section: NeuronSection | None = None,
|
|
79
|
+
segx: float = 0.5,
|
|
80
|
+
dt: float = 0.025
|
|
81
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
82
|
+
"""Add a step current injection.
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
section
|
|
84
|
+
Args:
|
|
85
|
+
start_time: Start time of the step injection in seconds.
|
|
86
|
+
stop_time: Stop time of the step injection in seconds.
|
|
87
|
+
level: Current level to inject in nanoamperes (nA).
|
|
88
|
+
section: The section to inject current into.
|
|
89
|
+
Defaults to the soma section.
|
|
90
|
+
segx: The fractional location within the section to inject.
|
|
91
|
+
Defaults to 0.5 (center of the section).
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Tuple of time and current data.
|
|
95
|
+
"""
|
|
96
|
+
stim = StimulusFactory(dt=dt).step(start_time, stop_time, level)
|
|
97
|
+
t_content, i_content = stim.time, stim.current
|
|
98
|
+
self.inject_current_waveform(t_content, i_content, section, segx)
|
|
99
|
+
return (t_content, i_content)
|
|
88
100
|
|
|
89
|
-
|
|
101
|
+
def add_ramp(
|
|
102
|
+
self,
|
|
103
|
+
start_time: float,
|
|
104
|
+
stop_time: float,
|
|
105
|
+
start_level: float,
|
|
106
|
+
stop_level: float,
|
|
107
|
+
section: NeuronSection | None = None,
|
|
108
|
+
segx: float = 0.5,
|
|
109
|
+
dt: float = 0.025
|
|
110
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
111
|
+
"""Add a ramp current injection.
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
start_level
|
|
95
|
-
stop_level
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
Args:
|
|
114
|
+
start_time: Start time of the ramp injection in seconds.
|
|
115
|
+
stop_time: Stop time of the ramp injection in seconds.
|
|
116
|
+
start_level: Current level at the start of the ramp in nanoamperes (nA).
|
|
117
|
+
stop_level: Current level at the end of the ramp in nanoamperes (nA).
|
|
118
|
+
section: The section to inject current into (optional). Defaults to soma.
|
|
119
|
+
segx: The fractional location within the section to inject (optional).
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
A tuple of numpy arrays containing time and current data.
|
|
123
|
+
"""
|
|
124
|
+
stim = StimulusFactory(dt=dt).ramp(start_time, stop_time, start_level, stop_level)
|
|
125
|
+
t_content, i_content = stim.time, stim.current
|
|
126
|
+
self.inject_current_waveform(t_content, i_content, section, segx)
|
|
99
127
|
|
|
100
|
-
|
|
101
|
-
return tstim
|
|
128
|
+
return t_content, i_content
|
|
102
129
|
|
|
103
130
|
def add_voltage_clamp(
|
|
104
131
|
self, stop_time, level, rs=None, section=None, segx=0.5,
|
|
@@ -395,32 +422,22 @@ class InjectableMixin:
|
|
|
395
422
|
else:
|
|
396
423
|
return self.inject_current_clamp_signal(section, segx, tvec, svec)
|
|
397
424
|
|
|
398
|
-
def inject_current_waveform(self, t_content, i_content, section=None,
|
|
399
|
-
|
|
400
|
-
"""Inject a custom current to the cell."""
|
|
401
|
-
start_time = t_content[0]
|
|
402
|
-
stop_time = t_content[-1]
|
|
403
|
-
time = neuron.h.Vector()
|
|
404
|
-
currents = neuron.h.Vector()
|
|
405
|
-
time = time.from_python(t_content)
|
|
406
|
-
currents = currents.from_python(i_content)
|
|
407
|
-
|
|
425
|
+
def inject_current_waveform(self, t_content, i_content, section=None, segx=0.5):
|
|
426
|
+
"""Inject a custom current waveform into the cell."""
|
|
408
427
|
if section is None:
|
|
409
428
|
section = self.soma
|
|
429
|
+
|
|
430
|
+
time_vector = neuron.h.Vector().from_python(t_content)
|
|
431
|
+
current_vector = neuron.h.Vector().from_python(i_content)
|
|
432
|
+
|
|
410
433
|
pulse = neuron.h.IClamp(segx, sec=section)
|
|
411
|
-
self.persistent.
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
pulse.
|
|
416
|
-
|
|
417
|
-
return
|
|
418
|
-
|
|
419
|
-
@deprecated("Use inject_current_waveform instead.")
|
|
420
|
-
def injectCurrentWaveform(self, t_content, i_content, section=None,
|
|
421
|
-
segx=0.5):
|
|
422
|
-
"""Inject a current in the cell."""
|
|
423
|
-
return self.inject_current_waveform(t_content, i_content, section, segx)
|
|
434
|
+
self.persistent.extend([pulse, time_vector, current_vector])
|
|
435
|
+
|
|
436
|
+
pulse.delay = t_content[0]
|
|
437
|
+
pulse.dur = t_content[-1] - t_content[0]
|
|
438
|
+
current_vector.play(pulse._ref_amp, time_vector)
|
|
439
|
+
|
|
440
|
+
return current_vector
|
|
424
441
|
|
|
425
442
|
@deprecated("Use add_sin_current instead.")
|
|
426
443
|
def addSineCurrentInject(self, start_time, stop_time, freq,
|
|
@@ -435,7 +452,7 @@ class InjectableMixin:
|
|
|
435
452
|
t_content = np.arange(start_time, stop_time, dt)
|
|
436
453
|
i_content = [amplitude * math.sin(freq * (x - start_time) * (
|
|
437
454
|
2 * math.pi)) + mid_level for x in t_content]
|
|
438
|
-
self.
|
|
455
|
+
self.inject_current_waveform(t_content, i_content)
|
|
439
456
|
return (t_content, i_content)
|
|
440
457
|
|
|
441
458
|
def add_sin_current(self, amp, start_time, duration, frequency,
|
|
@@ -454,9 +471,9 @@ class InjectableMixin:
|
|
|
454
471
|
tau: float,
|
|
455
472
|
gmax: float,
|
|
456
473
|
e: float,
|
|
457
|
-
section:
|
|
474
|
+
section: NeuronSection,
|
|
458
475
|
segx=0.5,
|
|
459
|
-
) ->
|
|
476
|
+
) -> NeuronSection:
|
|
460
477
|
"""Add an AlphaSynapse NEURON point process stimulus to the cell."""
|
|
461
478
|
syn = neuron.h.AlphaSynapse(segx, sec=section)
|
|
462
479
|
syn.onset = onset
|
bluecellulab/cell/plotting.py
CHANGED
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
"""Module that allows morphology sections to be accessed from an array by
|
|
15
15
|
index."""
|
|
16
|
-
|
|
16
|
+
from __future__ import annotations
|
|
17
17
|
import logging
|
|
18
18
|
import warnings
|
|
19
19
|
import neuron
|
|
20
|
-
from bluecellulab.type_aliases import HocObjectType
|
|
20
|
+
from bluecellulab.type_aliases import HocObjectType, NeuronSection
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
@@ -27,7 +27,7 @@ warnings.filterwarnings("once", category=UserWarning, module=__name__)
|
|
|
27
27
|
class SerializedSections:
|
|
28
28
|
|
|
29
29
|
def __init__(self, cell: HocObjectType) -> None:
|
|
30
|
-
self.isec2sec = {}
|
|
30
|
+
self.isec2sec: dict[int, NeuronSection] = {}
|
|
31
31
|
n = cell.nSecAll
|
|
32
32
|
|
|
33
33
|
for index, sec in enumerate(cell.all, start=1):
|
|
@@ -25,7 +25,7 @@ if BLUEPY_AVAILABLE:
|
|
|
25
25
|
from bluepy.utils import open_utf8
|
|
26
26
|
|
|
27
27
|
from bluecellulab.circuit.config.sections import Conditions, ConnectionOverrides
|
|
28
|
-
from bluecellulab.
|
|
28
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Stimulus
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class BluepySimulationConfig:
|
|
@@ -18,7 +18,7 @@ from typing import Optional, Protocol
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
from bluecellulab.circuit.config.sections import Conditions, ConnectionOverrides
|
|
21
|
-
from bluecellulab.
|
|
21
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Stimulus
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class SimulationConfig(Protocol):
|
|
@@ -17,7 +17,7 @@ from pathlib import Path
|
|
|
17
17
|
from typing import Optional
|
|
18
18
|
|
|
19
19
|
from bluecellulab.circuit.config.sections import Conditions, ConnectionOverrides
|
|
20
|
-
from bluecellulab.
|
|
20
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Stimulus
|
|
21
21
|
|
|
22
22
|
from bluepysnap import Simulation as SnapSimulation
|
|
23
23
|
|
bluecellulab/dendrogram.py
CHANGED
|
@@ -12,8 +12,54 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
"""Class that represents a dendrogram window."""
|
|
15
|
-
|
|
15
|
+
from __future__ import annotations
|
|
16
16
|
import numpy as np
|
|
17
|
+
import pylab
|
|
18
|
+
|
|
19
|
+
from bluecellulab.psection import PSection
|
|
20
|
+
from bluecellulab.psegment import PSegment
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def setup_draw(psegments: list[PSegment], maxsegdiam: float, figure, x, y, variable=None, varbounds=None) -> None:
|
|
24
|
+
"""Setup draw of psection."""
|
|
25
|
+
y_accum = 0.0
|
|
26
|
+
for psegment in psegments:
|
|
27
|
+
psegment.setupDraw(figure,
|
|
28
|
+
x + (maxsegdiam - psegment.diam) / 2,
|
|
29
|
+
y + y_accum,
|
|
30
|
+
variable=variable,
|
|
31
|
+
varbounds=varbounds)
|
|
32
|
+
y_accum += psegment.L
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def draw_tree(psection: PSection, figure, x, y, variable=None, varbounds=None) -> None:
|
|
36
|
+
"""Draw a dendritic tree."""
|
|
37
|
+
# Draw myself
|
|
38
|
+
setup_draw(
|
|
39
|
+
psection.psegments, psection.maxsegdiam, figure, x, y, variable=variable, varbounds=varbounds
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Draw children
|
|
43
|
+
|
|
44
|
+
# First child is a same x coordinate
|
|
45
|
+
new_x = x # + self.L + self.xSpacing
|
|
46
|
+
|
|
47
|
+
# Children drawn L + ySpacing heigher
|
|
48
|
+
new_y = y + psection.L + psection.ySpacing
|
|
49
|
+
|
|
50
|
+
for child in psection.pchildren:
|
|
51
|
+
draw_tree(child, figure, new_x, new_y, variable=variable, varbounds=varbounds)
|
|
52
|
+
pylab.plot(
|
|
53
|
+
[x + psection.diam / 2, new_x + child.diam / 2],
|
|
54
|
+
[y + psection.L, new_y], 'k')
|
|
55
|
+
# Prepare new_x for next child
|
|
56
|
+
new_x = new_x + child.tree_width()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def redraw_psection(psection: PSection) -> None:
|
|
60
|
+
"""Redraw psection."""
|
|
61
|
+
for psegment in psection.psegments:
|
|
62
|
+
psegment.redraw()
|
|
17
63
|
|
|
18
64
|
|
|
19
65
|
class Dendrogram:
|
|
@@ -21,7 +67,7 @@ class Dendrogram:
|
|
|
21
67
|
|
|
22
68
|
def __init__(
|
|
23
69
|
self,
|
|
24
|
-
psections,
|
|
70
|
+
psections: list[PSection],
|
|
25
71
|
variable=None,
|
|
26
72
|
active=False,
|
|
27
73
|
save_fig_path=None,
|
|
@@ -30,7 +76,6 @@ class Dendrogram:
|
|
|
30
76
|
scale_bar_size=10.0,
|
|
31
77
|
fig_title=None,
|
|
32
78
|
fig_show=True):
|
|
33
|
-
import pylab
|
|
34
79
|
|
|
35
80
|
if interactive:
|
|
36
81
|
pylab.ion()
|
|
@@ -48,15 +93,14 @@ class Dendrogram:
|
|
|
48
93
|
self.psections = psections
|
|
49
94
|
# neuron.h.finitialize()
|
|
50
95
|
|
|
51
|
-
|
|
52
|
-
self.
|
|
53
|
-
# self.psections = [self.proot] + self.proot.getAllPDescendants()
|
|
96
|
+
self.proot: PSection = psections[0]
|
|
97
|
+
self.psections = [self.proot] + self.proot.all_descendants()
|
|
54
98
|
|
|
55
99
|
xSpacing = self.proot.xSpacing
|
|
56
100
|
ySpacing = self.proot.ySpacing
|
|
57
101
|
|
|
58
|
-
max_y = self.proot.
|
|
59
|
-
max_x = self.proot.
|
|
102
|
+
max_y = self.proot.tree_height() + self.proot.ySpacing + title_space
|
|
103
|
+
max_x = self.proot.tree_width() + self.proot.xSpacing + scale_bar_size
|
|
60
104
|
pylab.xlim([0, max_x])
|
|
61
105
|
pylab.ylim([0, max_y])
|
|
62
106
|
pylab.gca().set_xticks([])
|
|
@@ -79,9 +123,9 @@ class Dendrogram:
|
|
|
79
123
|
cbar.ax.set_yticklabels(["%.2e" % (
|
|
80
124
|
varbounds[0]), "%.2e" % (varbounds[1])])
|
|
81
125
|
|
|
82
|
-
self.proot
|
|
83
|
-
|
|
84
|
-
|
|
126
|
+
draw_tree(self.proot, self.dend_figure, self.proot.xSpacing,
|
|
127
|
+
self.proot.ySpacing, variable=variable,
|
|
128
|
+
varbounds=varbounds)
|
|
85
129
|
|
|
86
130
|
if scale_bar:
|
|
87
131
|
pylab.plot(
|
|
@@ -119,8 +163,10 @@ class Dendrogram:
|
|
|
119
163
|
|
|
120
164
|
self.dend_figure.canvas.draw()
|
|
121
165
|
|
|
122
|
-
for
|
|
123
|
-
|
|
166
|
+
for section in self.psections:
|
|
167
|
+
section_id = section.isec
|
|
168
|
+
if section_id is not None:
|
|
169
|
+
redraw_psection(psections[section_id])
|
|
124
170
|
|
|
125
171
|
self.canvas = self.dend_figure.gca().figure.canvas
|
|
126
172
|
self.ax = self.dend_figure.gca()
|
|
@@ -138,15 +184,13 @@ class Dendrogram:
|
|
|
138
184
|
if not interactive and fig_show:
|
|
139
185
|
pylab.show()
|
|
140
186
|
|
|
141
|
-
def redraw(self):
|
|
187
|
+
def redraw(self) -> None:
|
|
142
188
|
"""Redraw the dendrogram."""
|
|
143
189
|
if self.active:
|
|
144
190
|
if not self.drawCount:
|
|
145
191
|
for psection in self.psections:
|
|
146
|
-
psection
|
|
192
|
+
redraw_psection(psection)
|
|
147
193
|
self.canvas.blit(self.ax.bbox)
|
|
148
194
|
self.drawCount = 1
|
|
149
195
|
else:
|
|
150
196
|
self.drawCount = self.drawCount - 1
|
|
151
|
-
|
|
152
|
-
return True
|
bluecellulab/psection.py
CHANGED
|
@@ -12,23 +12,67 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
"""Represents a python version of NEURON Section (for drawing)."""
|
|
15
|
-
|
|
15
|
+
from __future__ import annotations
|
|
16
16
|
import neuron
|
|
17
17
|
|
|
18
18
|
import bluecellulab
|
|
19
|
+
from bluecellulab.cell.serialized_sections import SerializedSections
|
|
20
|
+
from bluecellulab.psegment import PSegment
|
|
21
|
+
from bluecellulab.type_aliases import HocObjectType, NeuronSection
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def init_psections(
|
|
25
|
+
hoc_cell: HocObjectType,
|
|
26
|
+
) -> tuple[dict[int, PSection], dict[str, PSection]]:
|
|
27
|
+
"""Initialize the psections list.
|
|
28
|
+
|
|
29
|
+
This list contains the Python representation of the psections of
|
|
30
|
+
this morphology.
|
|
31
|
+
"""
|
|
32
|
+
psections: dict[int, PSection] = {}
|
|
33
|
+
secname_to_psection: dict[str, PSection] = {}
|
|
34
|
+
for sec in hoc_cell.all:
|
|
35
|
+
secname = neuron.h.secname(sec=sec)
|
|
36
|
+
secname_to_psection[secname] = PSection(sec)
|
|
37
|
+
|
|
38
|
+
serial_sections = SerializedSections(hoc_cell)
|
|
39
|
+
for isec, val in serial_sections.isec2sec.items():
|
|
40
|
+
hsection: NeuronSection = val.sec
|
|
41
|
+
if hsection:
|
|
42
|
+
secname = neuron.h.secname(sec=hsection)
|
|
43
|
+
psections[isec] = secname_to_psection[secname]
|
|
44
|
+
psections[isec].isec = isec
|
|
45
|
+
|
|
46
|
+
# Set the parents and children of all the psections
|
|
47
|
+
for psec in psections.values():
|
|
48
|
+
hparent = psec.hparent
|
|
49
|
+
if hparent:
|
|
50
|
+
parentname = hparent.name()
|
|
51
|
+
psec.pparent = secname_to_psection[parentname]
|
|
52
|
+
else:
|
|
53
|
+
psec.pparent = None
|
|
54
|
+
|
|
55
|
+
for hchild in psec.hchildren:
|
|
56
|
+
childname = hchild.name()
|
|
57
|
+
if "myelin" in childname:
|
|
58
|
+
continue
|
|
59
|
+
pchild = secname_to_psection[childname]
|
|
60
|
+
psec.add_pchild(pchild)
|
|
61
|
+
|
|
62
|
+
return psections, secname_to_psection
|
|
19
63
|
|
|
20
64
|
|
|
21
65
|
class PSection:
|
|
22
66
|
"""Class that represents a cell section."""
|
|
23
67
|
|
|
24
|
-
def __init__(self, hsection, isec=None):
|
|
25
|
-
self.L = hsection.L
|
|
26
|
-
self.diam = hsection.diam
|
|
68
|
+
def __init__(self, hsection: NeuronSection, isec: int | None = None):
|
|
69
|
+
self.L: float = hsection.L
|
|
70
|
+
self.diam: float = hsection.diam
|
|
27
71
|
self.hsection = hsection
|
|
28
|
-
self.name = neuron.h.secname(sec=hsection)
|
|
29
|
-
self.href = neuron.h.SectionRef(sec=self.hsection)
|
|
30
|
-
self.pparent = None
|
|
31
|
-
self.pchildren = []
|
|
72
|
+
self.name: str = neuron.h.secname(sec=hsection)
|
|
73
|
+
self.href: NeuronSection = neuron.h.SectionRef(sec=self.hsection)
|
|
74
|
+
self.pparent: PSection | None = None
|
|
75
|
+
self.pchildren: list[PSection] = []
|
|
32
76
|
self.isec = isec
|
|
33
77
|
|
|
34
78
|
if 'apic' in self.name:
|
|
@@ -46,24 +90,24 @@ class PSection:
|
|
|
46
90
|
"PSection: Section of unknown type: %s" %
|
|
47
91
|
self.name)
|
|
48
92
|
|
|
49
|
-
self.psegments = []
|
|
50
|
-
self.maxsegdiam = 0
|
|
93
|
+
self.psegments: list[PSegment] = []
|
|
94
|
+
self.maxsegdiam = 0.0
|
|
51
95
|
for hsegment in hsection:
|
|
52
96
|
# psegment = bluecellulab.PSegment(hsection(hsegment.x), self)
|
|
53
97
|
psegment = bluecellulab.PSegment(hsegment, self)
|
|
54
98
|
self.psegments.append(psegment)
|
|
55
99
|
self.maxsegdiam = max(self.maxsegdiam, psegment.diam)
|
|
56
100
|
|
|
57
|
-
self.xSpacing = 1
|
|
58
|
-
self.ySpacing = 5
|
|
101
|
+
self.xSpacing = 1.0
|
|
102
|
+
self.ySpacing = 5.0
|
|
59
103
|
|
|
60
104
|
@property
|
|
61
|
-
def
|
|
105
|
+
def is_leaf(self) -> bool:
|
|
62
106
|
"""Return true if section is a leaf in the morphological structure."""
|
|
63
107
|
return not self.hchildren
|
|
64
108
|
|
|
65
109
|
@property
|
|
66
|
-
def hparent(self):
|
|
110
|
+
def hparent(self) -> NeuronSection | None:
|
|
67
111
|
"""Return the hoc section of the parent."""
|
|
68
112
|
if self.href.has_parent():
|
|
69
113
|
return self.href.parent
|
|
@@ -71,37 +115,22 @@ class PSection:
|
|
|
71
115
|
return None
|
|
72
116
|
|
|
73
117
|
@property
|
|
74
|
-
def hchildren(self):
|
|
118
|
+
def hchildren(self) -> list[NeuronSection]:
|
|
75
119
|
"""Return a list with the hoc sections of the children."""
|
|
76
120
|
return [self.href.child[index] for index in
|
|
77
121
|
range(0, int(self.href.nchild()))]
|
|
78
122
|
|
|
79
|
-
def add_pchild(self, pchild):
|
|
123
|
+
def add_pchild(self, pchild: PSection) -> None:
|
|
80
124
|
"""Add a python represent of a child section."""
|
|
81
125
|
self.pchildren.append(pchild)
|
|
82
|
-
|
|
83
|
-
def setupDraw(self, figure, x, y, variable=None, varbounds=None):
|
|
84
|
-
"""Setup draw of psection."""
|
|
85
|
-
y_accum = 0
|
|
86
|
-
for psegment in self.psegments:
|
|
87
|
-
psegment.setupDraw(figure,
|
|
88
|
-
x + (self.maxsegdiam - psegment.diam) / 2,
|
|
89
|
-
y + y_accum,
|
|
90
|
-
variable=variable,
|
|
91
|
-
varbounds=varbounds)
|
|
92
|
-
y_accum += psegment.L
|
|
93
|
-
|
|
94
|
-
def redraw(self):
|
|
95
|
-
"""Redraw psection."""
|
|
96
|
-
for psegment in self.psegments:
|
|
97
|
-
psegment.redraw()
|
|
126
|
+
pchild.pparent = self
|
|
98
127
|
|
|
99
128
|
def getSectionVarBounds(self, variable):
|
|
100
129
|
"""Get bounds a variable in a section."""
|
|
101
130
|
varmin = None
|
|
102
131
|
varmax = None
|
|
103
132
|
for psegment in self.psegments:
|
|
104
|
-
value = psegment.
|
|
133
|
+
value = psegment.get_variable_value(variable)
|
|
105
134
|
if value:
|
|
106
135
|
varmin = min(value, varmin) if varmin else value
|
|
107
136
|
varmax = max(value, varmax) if varmax else value
|
|
@@ -122,51 +151,23 @@ class PSection:
|
|
|
122
151
|
|
|
123
152
|
return varbounds
|
|
124
153
|
|
|
125
|
-
def
|
|
154
|
+
def all_descendants(self) -> list[PSection]:
|
|
126
155
|
"""Return all the psection that are descendants of this psection."""
|
|
127
156
|
pdescendants = list(self.pchildren)
|
|
128
157
|
for child in self.pchildren:
|
|
129
|
-
pdescendants += child.
|
|
158
|
+
pdescendants += child.all_descendants()
|
|
130
159
|
return pdescendants
|
|
131
160
|
|
|
132
|
-
def
|
|
133
|
-
"""Draw a dendritic tree."""
|
|
134
|
-
import pylab
|
|
135
|
-
|
|
136
|
-
# Draw myself
|
|
137
|
-
self.setupDraw(figure, x, y, variable=variable, varbounds=varbounds)
|
|
138
|
-
|
|
139
|
-
# Draw children
|
|
140
|
-
|
|
141
|
-
# First child is a same x coordinate
|
|
142
|
-
new_x = x # + self.L + self.xSpacing
|
|
143
|
-
|
|
144
|
-
# Children drawn L + ySpacing heigher
|
|
145
|
-
new_y = y + self.L + self.ySpacing
|
|
146
|
-
|
|
147
|
-
for child in self.pchildren:
|
|
148
|
-
child.drawTree(
|
|
149
|
-
figure, new_x, new_y, variable=variable, varbounds=varbounds)
|
|
150
|
-
pylab.plot(
|
|
151
|
-
[x + self.diam / 2, new_x + child.diam / 2],
|
|
152
|
-
[y + self.L, new_y], 'k')
|
|
153
|
-
# Prepare new_x for next child
|
|
154
|
-
new_x = new_x + child.treeWidth()
|
|
155
|
-
|
|
156
|
-
def treeWidth(self):
|
|
161
|
+
def tree_width(self) -> float:
|
|
157
162
|
"""Width of a dendritic tree."""
|
|
158
|
-
if self.
|
|
159
|
-
|
|
163
|
+
if self.is_leaf:
|
|
164
|
+
width = self.maxsegdiam + self.xSpacing
|
|
160
165
|
else:
|
|
161
|
-
|
|
162
|
-
return max(self.diam + self.xSpacing,
|
|
166
|
+
width = sum(child.tree_width() for child in self.pchildren)
|
|
167
|
+
return max(self.diam + self.xSpacing, width)
|
|
163
168
|
|
|
164
|
-
def
|
|
169
|
+
def tree_height(self) -> float:
|
|
165
170
|
"""Height of dendritic tree."""
|
|
166
171
|
return self.L + self.ySpacing + \
|
|
167
|
-
(max([child.
|
|
172
|
+
(max([child.tree_height() for child in self.pchildren])
|
|
168
173
|
if self.pchildren else 0)
|
|
169
|
-
|
|
170
|
-
def getHChildren(self):
|
|
171
|
-
"""All hoc children of a section."""
|
|
172
|
-
return self.hchildren
|
bluecellulab/psegment.py
CHANGED
|
@@ -61,7 +61,7 @@ class PSegment:
|
|
|
61
61
|
def redraw(self):
|
|
62
62
|
"""Redraw a segment."""
|
|
63
63
|
if self.plotvariable:
|
|
64
|
-
plotvariable_value = self.
|
|
64
|
+
plotvariable_value = self.get_variable_value(self.plotvariable)
|
|
65
65
|
if plotvariable_value is not None:
|
|
66
66
|
self.patch.set_facecolor(self.color_map(
|
|
67
67
|
(plotvariable_value - self.varbounds[0]) /
|
|
@@ -71,7 +71,7 @@ class PSegment:
|
|
|
71
71
|
self.patch.set_hatch("/")
|
|
72
72
|
self.ax.draw_artist(self.patch)
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def get_variable_value(self, variable):
|
|
75
75
|
"""Get a variable value in a segment."""
|
|
76
76
|
if variable == "v" or neuron.h.execute1(
|
|
77
77
|
"{%s.%s(%f)}"
|
bluecellulab/ssim.py
CHANGED
|
@@ -40,8 +40,8 @@ from bluecellulab.circuit.format import determine_circuit_format, CircuitFormat
|
|
|
40
40
|
from bluecellulab.circuit.node_id import create_cell_id, create_cell_ids
|
|
41
41
|
from bluecellulab.circuit.simulation_access import BluepySimulationAccess, SimulationAccess, SonataSimulationAccess, _sample_array
|
|
42
42
|
from bluecellulab.importer import load_hoc_and_mod_files
|
|
43
|
-
from bluecellulab.
|
|
44
|
-
import bluecellulab.
|
|
43
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Noise, OrnsteinUhlenbeck, RelativeOrnsteinUhlenbeck, RelativeShotNoise, ShotNoise
|
|
44
|
+
import bluecellulab.stimulus.circuit_stimulus_definitions as circuit_stimulus_definitions
|
|
45
45
|
from bluecellulab.exceptions import BluecellulabError
|
|
46
46
|
from bluecellulab.simulation import (
|
|
47
47
|
set_global_condition_parameters,
|
|
@@ -298,40 +298,40 @@ class SSim:
|
|
|
298
298
|
for cell_id in self.cells:
|
|
299
299
|
if cell_id not in gids_of_target:
|
|
300
300
|
continue
|
|
301
|
-
if isinstance(stimulus,
|
|
301
|
+
if isinstance(stimulus, circuit_stimulus_definitions.Noise):
|
|
302
302
|
if add_noise_stimuli:
|
|
303
303
|
self.cells[cell_id].add_replay_noise(
|
|
304
304
|
stimulus, noisestim_count=noisestim_count)
|
|
305
|
-
elif isinstance(stimulus,
|
|
305
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.Hyperpolarizing):
|
|
306
306
|
if add_hyperpolarizing_stimuli:
|
|
307
307
|
self.cells[cell_id].add_replay_hypamp(stimulus)
|
|
308
|
-
elif isinstance(stimulus,
|
|
308
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.Pulse):
|
|
309
309
|
if add_pulse_stimuli:
|
|
310
310
|
self.cells[cell_id].add_pulse(stimulus)
|
|
311
|
-
elif isinstance(stimulus,
|
|
311
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.RelativeLinear):
|
|
312
312
|
if add_relativelinear_stimuli:
|
|
313
313
|
self.cells[cell_id].add_replay_relativelinear(stimulus)
|
|
314
|
-
elif isinstance(stimulus,
|
|
314
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.ShotNoise):
|
|
315
315
|
if add_shotnoise_stimuli:
|
|
316
316
|
self.cells[cell_id].add_replay_shotnoise(
|
|
317
317
|
self.cells[cell_id].soma, 0.5, stimulus,
|
|
318
318
|
shotnoise_stim_count=shotnoise_stim_count)
|
|
319
|
-
elif isinstance(stimulus,
|
|
319
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.RelativeShotNoise):
|
|
320
320
|
if add_shotnoise_stimuli:
|
|
321
321
|
self.cells[cell_id].add_replay_relative_shotnoise(
|
|
322
322
|
self.cells[cell_id].soma, 0.5, stimulus,
|
|
323
323
|
shotnoise_stim_count=shotnoise_stim_count)
|
|
324
|
-
elif isinstance(stimulus,
|
|
324
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.OrnsteinUhlenbeck):
|
|
325
325
|
if add_ornstein_uhlenbeck_stimuli:
|
|
326
326
|
self.cells[cell_id].add_ornstein_uhlenbeck(
|
|
327
327
|
self.cells[cell_id].soma, 0.5, stimulus,
|
|
328
328
|
stim_count=ornstein_uhlenbeck_stim_count)
|
|
329
|
-
elif isinstance(stimulus,
|
|
329
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.RelativeOrnsteinUhlenbeck):
|
|
330
330
|
if add_ornstein_uhlenbeck_stimuli:
|
|
331
331
|
self.cells[cell_id].add_relative_ornstein_uhlenbeck(
|
|
332
332
|
self.cells[cell_id].soma, 0.5, stimulus,
|
|
333
333
|
stim_count=ornstein_uhlenbeck_stim_count)
|
|
334
|
-
elif isinstance(stimulus,
|
|
334
|
+
elif isinstance(stimulus, circuit_stimulus_definitions.SynapseReplay): # sonata only
|
|
335
335
|
if self.circuit_access.target_contains_cell(
|
|
336
336
|
stimulus.target, cell_id
|
|
337
337
|
):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .factory import StimulusFactory
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
"""Defines the expected data structures associated with the stimulus defined in
|
|
15
|
+
simulation configs.
|
|
14
16
|
|
|
17
|
+
Run-time validates the data via Pydantic.
|
|
18
|
+
"""
|
|
15
19
|
from __future__ import annotations
|
|
16
20
|
|
|
17
21
|
from enum import Enum
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Stimulus(ABC):
|
|
8
|
+
def __init__(self, dt: float) -> None:
|
|
9
|
+
self.dt = dt
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def time(self) -> np.ndarray:
|
|
14
|
+
"""Time values of the stimulus."""
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def current(self) -> np.ndarray:
|
|
20
|
+
"""Current values of the stimulus."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
def __len__(self) -> int:
|
|
24
|
+
return len(self.time)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def stimulus_time(self) -> float:
|
|
28
|
+
return len(self) * self.dt
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f"{self.__class__.__name__}(dt={self.dt})"
|
|
32
|
+
|
|
33
|
+
def plot(self, ax=None, **kwargs):
|
|
34
|
+
if ax is None:
|
|
35
|
+
ax = plt.gca()
|
|
36
|
+
ax.plot(self.time, self.current, **kwargs)
|
|
37
|
+
ax.set_xlabel("Time (ms)")
|
|
38
|
+
ax.set_ylabel("Current (nA)")
|
|
39
|
+
ax.set_title(self.__class__.__name__)
|
|
40
|
+
return ax
|
|
41
|
+
|
|
42
|
+
def plot_during_simulation(self, duration: float, ax=None, **kwargs):
|
|
43
|
+
if ax is None:
|
|
44
|
+
ax = plt.gca()
|
|
45
|
+
# Create an array for the entire duration
|
|
46
|
+
full_time = np.arange(0, duration, self.dt)
|
|
47
|
+
full_current = np.zeros_like(full_time)
|
|
48
|
+
|
|
49
|
+
# Replace the corresponding values with self.time and self.current
|
|
50
|
+
indices = (self.time / self.dt).astype(int)
|
|
51
|
+
full_current[indices] = self.current
|
|
52
|
+
|
|
53
|
+
ax.plot(full_time, full_current, **kwargs)
|
|
54
|
+
ax.set_xlabel("Time (ms)")
|
|
55
|
+
ax.set_ylabel("Current (nA)")
|
|
56
|
+
ax.set_title(self.__class__.__name__)
|
|
57
|
+
ax.set_xlim(0, duration)
|
|
58
|
+
return ax
|
|
59
|
+
|
|
60
|
+
def __add__(self, other: Stimulus) -> CombinedStimulus:
|
|
61
|
+
"""Override + operator to concatenate Stimulus objects."""
|
|
62
|
+
if self.dt != other.dt:
|
|
63
|
+
raise ValueError("Stimulus objects must have the same dt to be concatenated")
|
|
64
|
+
# shift other time
|
|
65
|
+
other_time = other.time + self.time[-1] + self.dt
|
|
66
|
+
combined_time = np.concatenate([self.time, other_time])
|
|
67
|
+
# Concatenate the current arrays
|
|
68
|
+
combined_current = np.concatenate([self.current, other.current])
|
|
69
|
+
return CombinedStimulus(self.dt, combined_time, combined_current)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class CombinedStimulus(Stimulus):
|
|
73
|
+
"""Represents the Stimulus created by combining multiple stimuli."""
|
|
74
|
+
def __init__(self, dt: float, time: np.ndarray, current: np.ndarray) -> None:
|
|
75
|
+
super().__init__(dt)
|
|
76
|
+
self._time = time
|
|
77
|
+
self._current = current
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def time(self) -> np.ndarray:
|
|
81
|
+
return self._time
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def current(self) -> np.ndarray:
|
|
85
|
+
return self._current
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class EmptyStimulus(Stimulus):
|
|
89
|
+
"""Represents empty stimulus (all zeros) that has no impact on the cell.
|
|
90
|
+
|
|
91
|
+
This is required by some Stimuli that expect the cell to rest.
|
|
92
|
+
"""
|
|
93
|
+
def __init__(self, dt: float, duration: float) -> None:
|
|
94
|
+
super().__init__(dt)
|
|
95
|
+
self.duration = duration
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def time(self) -> np.ndarray:
|
|
99
|
+
return np.arange(0.0, self.duration, self.dt)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def current(self) -> np.ndarray:
|
|
103
|
+
return np.zeros_like(self.time)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class Step(Stimulus):
|
|
107
|
+
def __init__(self, dt: float, start: float, end: float, amplitude: float) -> None:
|
|
108
|
+
super().__init__(dt)
|
|
109
|
+
self.start = start
|
|
110
|
+
self.end = end
|
|
111
|
+
self.amplitude = amplitude
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def threshold_based(
|
|
115
|
+
cls,
|
|
116
|
+
dt: float,
|
|
117
|
+
threshold_current: float,
|
|
118
|
+
threshold_percentage: float,
|
|
119
|
+
start: float,
|
|
120
|
+
end: float,
|
|
121
|
+
post_wait: float,
|
|
122
|
+
) -> CombinedStimulus:
|
|
123
|
+
amplitude = threshold_current * threshold_percentage / 100
|
|
124
|
+
res = cls(dt, start, end, amplitude) + EmptyStimulus(dt, duration=post_wait)
|
|
125
|
+
return res
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def time(self) -> np.ndarray:
|
|
129
|
+
return np.arange(self.start, self.end, self.dt)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def current(self) -> np.ndarray:
|
|
133
|
+
return np.full_like(self.time, self.amplitude)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Ramp(Stimulus):
|
|
137
|
+
def __init__(
|
|
138
|
+
self,
|
|
139
|
+
dt: float,
|
|
140
|
+
start: float,
|
|
141
|
+
end: float,
|
|
142
|
+
amplitude_start: float,
|
|
143
|
+
amplitude_end: float,
|
|
144
|
+
) -> None:
|
|
145
|
+
super().__init__(dt)
|
|
146
|
+
self.start = start
|
|
147
|
+
self.end = end
|
|
148
|
+
self.amplitude_start = amplitude_start
|
|
149
|
+
self.amplitude_end = amplitude_end
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def time(self) -> np.ndarray:
|
|
153
|
+
return np.arange(self.start, self.end, self.dt)
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def current(self) -> np.ndarray:
|
|
157
|
+
return np.linspace(self.amplitude_start, self.amplitude_end, len(self.time))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class StimulusFactory:
|
|
161
|
+
def __init__(self, dt: float):
|
|
162
|
+
self.dt = dt
|
|
163
|
+
|
|
164
|
+
def step(self, start: float, end: float, amplitude: float) -> Stimulus:
|
|
165
|
+
return Step(self.dt, start, end, amplitude)
|
|
166
|
+
|
|
167
|
+
def ramp(
|
|
168
|
+
self, start: float, end: float, amplitude_start: float, amplitude_end: float
|
|
169
|
+
) -> Stimulus:
|
|
170
|
+
return Ramp(self.dt, start, end, amplitude_start, amplitude_end)
|
|
171
|
+
|
|
172
|
+
def ap_waveform(
|
|
173
|
+
self,
|
|
174
|
+
threshold_current: float,
|
|
175
|
+
threshold_percentage: float = 220.0,
|
|
176
|
+
start: float = 250.0,
|
|
177
|
+
end: float = 300.0,
|
|
178
|
+
post_wait: float = 250.0,
|
|
179
|
+
) -> Stimulus:
|
|
180
|
+
"""Returns the APWaveform Stimulus object, a type of Step stimulus.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
threshold_current: The threshold current of the Cell.
|
|
184
|
+
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
185
|
+
start: The start time of the step.
|
|
186
|
+
end: The end time of the step.
|
|
187
|
+
post_wait: The time to wait after the end of the step.
|
|
188
|
+
"""
|
|
189
|
+
return Step.threshold_based(
|
|
190
|
+
self.dt, threshold_current, threshold_percentage, start, end, post_wait
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def idrest(
|
|
194
|
+
self,
|
|
195
|
+
threshold_current: float,
|
|
196
|
+
threshold_percentage: float = 200.0,
|
|
197
|
+
start: float = 250.0,
|
|
198
|
+
end: float = 1600.0,
|
|
199
|
+
post_wait: float = 250.0,
|
|
200
|
+
) -> Stimulus:
|
|
201
|
+
"""Returns the IDRest Stimulus object, a type of Step stimulus.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
threshold_current: The threshold current of the Cell.
|
|
205
|
+
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
206
|
+
start: The start time of the step.
|
|
207
|
+
end: The end time of the step.
|
|
208
|
+
post_wait: The time to wait after the end of the step.
|
|
209
|
+
"""
|
|
210
|
+
return Step.threshold_based(
|
|
211
|
+
self.dt, threshold_current, threshold_percentage, start, end, post_wait
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def iv(
|
|
215
|
+
self,
|
|
216
|
+
threshold_current: float,
|
|
217
|
+
threshold_percentage: float = -40.0,
|
|
218
|
+
start: float = 250.0,
|
|
219
|
+
end: float = 3250.0,
|
|
220
|
+
post_wait: float = 250.0,
|
|
221
|
+
) -> Stimulus:
|
|
222
|
+
"""Returns the IV Stimulus object, a type of Step stimulus.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
threshold_current: The threshold current of the Cell.
|
|
226
|
+
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
227
|
+
start: The start time of the step.
|
|
228
|
+
end: The end time of the step.
|
|
229
|
+
post_wait: The time to wait after the end of the step.
|
|
230
|
+
"""
|
|
231
|
+
return Step.threshold_based(
|
|
232
|
+
self.dt, threshold_current, threshold_percentage, start, end, post_wait
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def fire_pattern(
|
|
236
|
+
self,
|
|
237
|
+
threshold_current: float,
|
|
238
|
+
threshold_percentage: float = 200.0,
|
|
239
|
+
start: float = 250.0,
|
|
240
|
+
end: float = 3850.0,
|
|
241
|
+
post_wait: float = 250.0,
|
|
242
|
+
) -> Stimulus:
|
|
243
|
+
"""Returns the FirePattern Stimulus object, a type of Step stimulus.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
threshold_current: The threshold current of the Cell.
|
|
247
|
+
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
248
|
+
start: The start time of the step.
|
|
249
|
+
end: The end time of the step.
|
|
250
|
+
post_wait: The time to wait after the end of the step.
|
|
251
|
+
"""
|
|
252
|
+
return Step.threshold_based(
|
|
253
|
+
self.dt, threshold_current, threshold_percentage, start, end, post_wait
|
|
254
|
+
)
|
|
@@ -110,8 +110,8 @@ class SynapseFactory:
|
|
|
110
110
|
@classmethod
|
|
111
111
|
def determine_synapse_location(cls, syn_description: pd.Series, cell: bluecellulab.Cell) -> SynapseHocArgs:
|
|
112
112
|
"""Returns the location of the synapse."""
|
|
113
|
-
isec = syn_description[SynapseProperty.POST_SECTION_ID]
|
|
114
|
-
section = cell.
|
|
113
|
+
isec = int(syn_description[SynapseProperty.POST_SECTION_ID]) # numpy int to int
|
|
114
|
+
section: NeuronSection = cell.get_psection(section_id=isec).hsection
|
|
115
115
|
|
|
116
116
|
# old circuits don't have it, it needs to be computed via synlocation_to_segx
|
|
117
117
|
if ("afferent_section_pos" in syn_description and
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
bluecellulab/__init__.py,sha256=tbFwA146uSwNPO1HeVhqHNfxUBPM0VsuNk3NeMCq-Wc,620
|
|
2
2
|
bluecellulab/connection.py,sha256=volV2YKtmqAF7MOEJar5ldF1ARAo7k2vF9MB1NXybUY,4640
|
|
3
|
-
bluecellulab/dendrogram.py,sha256=
|
|
3
|
+
bluecellulab/dendrogram.py,sha256=w0vvv0Q169DolTX1j9dAZIvHIl4H258gAjQ1xQaNiGk,6427
|
|
4
4
|
bluecellulab/exceptions.py,sha256=KIxF7s_7gPVJ07TiQ-Z1D8de7ylV74gNMhzl0339CVM,2379
|
|
5
5
|
bluecellulab/graph.py,sha256=ODiQy4xjRVxtNITXeXpYnqHas0wR7gZ_aXuDAF7jMq4,3419
|
|
6
6
|
bluecellulab/importer.py,sha256=Sf8_aWqBCp98WfNkLMhNSp4UdurEneFF5leNYRlXjAw,2852
|
|
7
7
|
bluecellulab/neuron_interpreter.py,sha256=hXig_u3T6JmEHbkV8ZblEZtX0kY80ate4VpRtANNFrM,2185
|
|
8
8
|
bluecellulab/plotwindow.py,sha256=UVHzml-BB83m5Qr-YGkjR9kB-vSW8mM0Owh2j95yIaU,2721
|
|
9
|
-
bluecellulab/psection.py,sha256=
|
|
10
|
-
bluecellulab/psegment.py,sha256=
|
|
9
|
+
bluecellulab/psection.py,sha256=EgAS8IS9DcYv2xOkNISgfg_CfRc0nDfRFjvgQhyi9eY,6328
|
|
10
|
+
bluecellulab/psegment.py,sha256=rBryDYHC_uDK9itfXXrFZ0DL9F6WgRICL0j5EHN56QM,3125
|
|
11
11
|
bluecellulab/rngsettings.py,sha256=SLvkgM8K26Z-zTTN__CnFl_KjIJNuF4n_jzviyei5P4,3986
|
|
12
|
-
bluecellulab/ssim.py,sha256=
|
|
13
|
-
bluecellulab/stimuli.py,sha256=u3AG3u0yYIvZzvFAwl1-8UPvEbq50mERVLVajX8JXdo,15540
|
|
12
|
+
bluecellulab/ssim.py,sha256=_MyUObeKIHhuT4cbB1uSD7izpZZHNRACiPlO9R1TNa8,33552
|
|
14
13
|
bluecellulab/tools.py,sha256=s4NTE0DzK8z8oAy1BF1C2f19nBx-qBPuArz7e7hUTnw,24231
|
|
15
14
|
bluecellulab/type_aliases.py,sha256=EMrunY-pIgZrsmetpAM8lA7tr0TFEzFoU5SX9sBuiOk,312
|
|
16
15
|
bluecellulab/utils.py,sha256=GKZkRYI2LY08tsIkG95MGn7EfmV4pBrIAqfRQCW14fs,300
|
|
17
16
|
bluecellulab/cell/__init__.py,sha256=Sbc0QOsJ8E7tSwf3q7fsXuE_SevBN6ZmoCVyyU5zfII,208
|
|
18
17
|
bluecellulab/cell/cell_dict.py,sha256=PVmZsjhZ9jp3HC-8QmdFqp-crAcVMSVeLWujcOPLlpo,1346
|
|
19
|
-
bluecellulab/cell/core.py,sha256=
|
|
20
|
-
bluecellulab/cell/injector.py,sha256=
|
|
21
|
-
bluecellulab/cell/plotting.py,sha256=
|
|
18
|
+
bluecellulab/cell/core.py,sha256=UtPjq_SYX8wOehQe7GqkbPxDWG1vIIJQ5-e3T_g29YU,31480
|
|
19
|
+
bluecellulab/cell/injector.py,sha256=4h11fObfbKhuvexE_AawCWiAalZIUjDsbVjqOeJ34Tw,17842
|
|
20
|
+
bluecellulab/cell/plotting.py,sha256=_hZs5oYG4vmJBVf05cJ2O_cVazi5Eap8OkL9BtIwjW8,4001
|
|
22
21
|
bluecellulab/cell/random.py,sha256=FDby9BN4eJT27COwHP59bhDE2v-c6rdOKNFj3cYZTVY,1773
|
|
23
22
|
bluecellulab/cell/section_distance.py,sha256=J8-oqgCHzRaJkpfjPUR6NFtXDhwbrXad9nDaTCKNkTU,3908
|
|
24
|
-
bluecellulab/cell/serialized_sections.py,sha256=
|
|
23
|
+
bluecellulab/cell/serialized_sections.py,sha256=UQUecO07ChRZ7xHuDp-QAQRUpUiIwROzHxjZliP9gfQ,1614
|
|
25
24
|
bluecellulab/cell/sonata_proxy.py,sha256=dLT9mLlGVpXxj2O2lXN0g7Sq4BwroPLVdPikR2yNMv4,1529
|
|
26
25
|
bluecellulab/cell/stimuli_generator.py,sha256=cJwjNwsQeEBHLjuJIFv6VBSqd9IpmbR7CuSMyotCiWc,6320
|
|
27
26
|
bluecellulab/cell/template.py,sha256=EGZDS7Lb7kScwh0LqKl7BcvA6r5UdNx1_HiYo7ALbPs,8015
|
|
@@ -40,10 +39,10 @@ bluecellulab/circuit/circuit_access/bluepy_circuit_access.py,sha256=j-DYHMBTbq1T
|
|
|
40
39
|
bluecellulab/circuit/circuit_access/definition.py,sha256=SnKBFEgdXFG-QexYRrGSzVAd7bSj7NwN0IuTsjDOrDY,4435
|
|
41
40
|
bluecellulab/circuit/circuit_access/sonata_circuit_access.py,sha256=P1ElK_VA10_JyyKbAnqzuAlduEYE2c_NSec2TZuix1U,10345
|
|
42
41
|
bluecellulab/circuit/config/__init__.py,sha256=aaoJXRKBJzpxxREo9NxKc-_CCPmVeuR1mcViRXcLrC4,215
|
|
43
|
-
bluecellulab/circuit/config/bluepy_simulation_config.py,sha256=
|
|
44
|
-
bluecellulab/circuit/config/definition.py,sha256=
|
|
42
|
+
bluecellulab/circuit/config/bluepy_simulation_config.py,sha256=tUyHvzlxFWRxh8rBNvU0FdUqGqJR2G8OXifATQ9W7yw,6974
|
|
43
|
+
bluecellulab/circuit/config/definition.py,sha256=o0751Dd83f8TWGw95EAQ8coJrYGasdshrIrXViF5lzg,2774
|
|
45
44
|
bluecellulab/circuit/config/sections.py,sha256=O1198JvusdOQBWgUeOZ5OhOupbHvvt3NtP9JfBWNbn0,7151
|
|
46
|
-
bluecellulab/circuit/config/sonata_simulation_config.py,sha256=
|
|
45
|
+
bluecellulab/circuit/config/sonata_simulation_config.py,sha256=P0IKazCuNAG97xq2xLwHLbmr_lrNAqPsa6rXi-uDqb0,4829
|
|
47
46
|
bluecellulab/hoc/Cell.hoc,sha256=z77qRQG_-afj-RLX0xN6V-K6Duq3bR7vmlDrGWPdh4E,16435
|
|
48
47
|
bluecellulab/hoc/RNGSettings.hoc,sha256=wOtPxVMkCBTHsFLs5jPMKyverm1llXqaX8VKsAX43r8,1413
|
|
49
48
|
bluecellulab/hoc/TDistFunc.hoc,sha256=WKX-anvL83xGuGPH9g1oIORB17UM4Pi3-iIXzKO-pUQ,2663
|
|
@@ -52,12 +51,15 @@ bluecellulab/hoc/fileUtils.hoc,sha256=LSM7BgyjYVqo2DGSOKvg4W8IIusbsL45JVYK0vgwit
|
|
|
52
51
|
bluecellulab/simulation/__init__.py,sha256=G1md-6mqaYQkfZT8pmzqRi3WW1fMQCgkeaela2kX3OM,258
|
|
53
52
|
bluecellulab/simulation/neuron_globals.py,sha256=PuAxrXbr734RP-5q6YkHpnOgzTVyo_8SYZux9HABlaI,3348
|
|
54
53
|
bluecellulab/simulation/simulation.py,sha256=tM5Psl-w25CHA4hp2fqs3GcZ2HSjIbu_6iyOcs-1jd8,6439
|
|
54
|
+
bluecellulab/stimulus/__init__.py,sha256=DgIgVaSyR-URf3JZzvO6j-tjCerzvktuK-ep8pjMRPQ,37
|
|
55
|
+
bluecellulab/stimulus/circuit_stimulus_definitions.py,sha256=uij_s44uNdmMwMLGmTHSRgmp9K9B_vvHHshX6YPJNJU,15686
|
|
56
|
+
bluecellulab/stimulus/factory.py,sha256=AOby3Sp2g8BJsRccz3afazfCyysyWIgLtcliWlyeiu0,8097
|
|
55
57
|
bluecellulab/synapse/__init__.py,sha256=RW8XoAMXOvK7OG1nHl_q8jSEKLj9ZN4oWf2nY9HAwuk,192
|
|
56
|
-
bluecellulab/synapse/synapse_factory.py,sha256=
|
|
58
|
+
bluecellulab/synapse/synapse_factory.py,sha256=ZBRpjvIRyHc4hBfBvtLukLf7E2PjHhzwqGt9O4MBOTk,6862
|
|
57
59
|
bluecellulab/synapse/synapse_types.py,sha256=wWVBfvPYF6g3dSoaXsjJ6QI929yWYrTh_OmauS5Y-Uk,17027
|
|
58
|
-
bluecellulab-2.2.
|
|
59
|
-
bluecellulab-2.2.
|
|
60
|
-
bluecellulab-2.2.
|
|
61
|
-
bluecellulab-2.2.
|
|
62
|
-
bluecellulab-2.2.
|
|
63
|
-
bluecellulab-2.2.
|
|
60
|
+
bluecellulab-2.3.2.dist-info/AUTHORS.txt,sha256=EDs3H-2HXBojbma10psixk3C2rFiOCTIREi2ZAbXYNQ,179
|
|
61
|
+
bluecellulab-2.3.2.dist-info/LICENSE,sha256=xOouu1gC1GGklDxkITlaVl60I9Ab860O-nZsFbWydvU,11749
|
|
62
|
+
bluecellulab-2.3.2.dist-info/METADATA,sha256=1uZpRUUBcPBBCbAiudS3Qg3anmrygJBuNtQ_kTEC6O8,6907
|
|
63
|
+
bluecellulab-2.3.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
64
|
+
bluecellulab-2.3.2.dist-info/top_level.txt,sha256=VSyEP8w9l3pXdRkyP_goeMwiNA8KWwitfAqUkveJkdQ,13
|
|
65
|
+
bluecellulab-2.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|