bluecellulab 2.3.7__py3-none-any.whl → 2.4.1__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.

@@ -20,7 +20,7 @@ import os
20
20
  from pathlib import Path
21
21
  import re
22
22
  import string
23
- from typing import Optional
23
+ from typing import NamedTuple, Optional
24
24
 
25
25
  import neuron
26
26
 
@@ -44,11 +44,19 @@ def public_hoc_cell(cell: HocObjectType) -> HocObjectType:
44
44
  from the hoc model. Either getCell() or CellRef needs to be provided""")
45
45
 
46
46
 
47
+ class TemplateParams(NamedTuple):
48
+ template_filepath: str | Path
49
+ morph_filepath: str | Path
50
+ template_format: str
51
+ emodel_properties: Optional[EmodelProperties]
52
+
53
+
47
54
  class NeuronTemplate:
48
55
  """NeuronTemplate representation."""
49
56
 
50
57
  def __init__(
51
- self, template_filepath: str | Path, morph_filepath: str | Path
58
+ self, template_filepath: str | Path, morph_filepath: str | Path,
59
+ template_format: str, emodel_properties: Optional[EmodelProperties]
52
60
  ) -> None:
53
61
  """Load the hoc template and init object."""
54
62
  if isinstance(template_filepath, Path):
@@ -63,18 +71,18 @@ class NeuronTemplate:
63
71
 
64
72
  self.template_name = self.load(template_filepath)
65
73
  self.morph_filepath = morph_filepath
74
+ self.template_format = template_format
75
+ self.emodel_properties = emodel_properties
66
76
 
67
- def get_cell(
68
- self, template_format: str, gid: Optional[int], emodel_properties: Optional[EmodelProperties]
69
- ) -> HocObjectType:
77
+ def get_cell(self, gid: Optional[int]) -> HocObjectType:
70
78
  """Returns the hoc object matching the template format."""
71
79
  morph_dir, morph_fname = os.path.split(self.morph_filepath)
72
- if template_format == "v6":
80
+ if self.template_format == "v6":
73
81
  attr_names = getattr(
74
82
  neuron.h, self.template_name.split('_bluecellulab')[0] + "_NeededAttributes", None
75
83
  )
76
84
  if attr_names is not None:
77
- if emodel_properties is None:
85
+ if self.emodel_properties is None:
78
86
  raise BluecellulabError(
79
87
  "EmodelProperties must be provided for template "
80
88
  "format v6 that specifies _NeededAttributes"
@@ -83,7 +91,7 @@ class NeuronTemplate:
83
91
  gid,
84
92
  morph_dir,
85
93
  morph_fname,
86
- *[emodel_properties.__getattribute__(name) for name in attr_names.split(";")]
94
+ *[self.emodel_properties.__getattribute__(name) for name in attr_names.split(";")]
87
95
  )
88
96
  else:
89
97
  cell = getattr(neuron.h, self.template_name)(
@@ -91,7 +99,7 @@ class NeuronTemplate:
91
99
  morph_dir,
92
100
  morph_fname,
93
101
  )
94
- elif template_format == "bluepyopt":
102
+ elif self.template_format == "bluepyopt":
95
103
  cell = getattr(neuron.h, self.template_name)(morph_dir, morph_fname)
96
104
  else:
97
105
  cell = getattr(neuron.h, self.template_name)(gid, self.morph_filepath)
@@ -137,31 +145,21 @@ class NeuronTemplate:
137
145
  return template_name
138
146
 
139
147
 
140
- def shorten_and_hash_string(label, keep_length=40, hash_length=9):
141
- """Convert string to a shorter string if required.
142
-
143
- Parameters
144
- ----------
145
- label : string
146
- a string to be converted
147
- keep_length : int
148
- length of the original string to keep. Default is 40
149
- characters.
150
- hash_length : int
151
- length of the hash to generate, should not be more then
152
- 20. Default is 9 characters.
153
-
154
- Returns
155
- -------
156
- new_label : string
157
- If the length of the original label is shorter than the sum of
158
- 'keep_length' and 'hash_length' plus one the original string is
159
- returned. Otherwise, a string with structure <partial>_<hash> is
160
- returned, where <partial> is the first part of the original string
161
- with length equal to <keep_length> and the last part is a hash of
162
- 'hash_length' characters, based on the original string.
163
- """
148
+ def shorten_and_hash_string(label: str, keep_length=40, hash_length=9) -> str:
149
+ """Converts a string to a shorter string if required.
150
+
151
+ Args:
152
+ label: A string to be converted.
153
+ keep_length: Length of the original string to keep.
154
+ hash_length: Length of the hash to generate, should not be more than 20.
164
155
 
156
+ Returns:
157
+ If the length of the original label is shorter than the sum of 'keep_length'
158
+ and 'hash_length' plus one, the original string is returned. Otherwise, a
159
+ string with structure <partial>_<hash> is returned, where <partial> is the
160
+ first part of the original string with length equal to <keep_length> and the
161
+ last part is a hash of 'hash_length' characters, based on the original string.
162
+ """
165
163
  if hash_length > 20:
166
164
  raise ValueError(
167
165
  "Parameter hash_length should not exceed 20, "
@@ -175,46 +173,24 @@ def shorten_and_hash_string(label, keep_length=40, hash_length=9):
175
173
  return "{}_{}".format(label[0:keep_length], hash_string[0:hash_length])
176
174
 
177
175
 
178
- def check_compliance_with_neuron(template_name):
176
+ def check_compliance_with_neuron(template_name: str) -> bool:
179
177
  """Verify that a given name is compliant with the rules for a NEURON.
180
178
 
181
- Parameters
182
- ----------
183
- template name : string
184
- a name should be a non-empty alphanumeric string,
185
- and start with a letter. Underscores are allowed.
186
- The length should not exceed 50 characters.
187
-
188
- Returns
189
- -------
190
- compliant : boolean
191
- True if compliant, false otherwise.
179
+ A name should be a non-empty alphanumeric string, and start with a
180
+ letter. Underscores are allowed. The length should not exceed 50
181
+ characters.
192
182
  """
193
183
  max_len = 50
194
184
  return (
195
- template_name
185
+ bool(template_name)
196
186
  and template_name[0].isalpha()
197
187
  and template_name.replace("_", "").isalnum()
198
188
  and len(template_name) <= max_len
199
189
  )
200
190
 
201
191
 
202
- def get_neuron_compliant_template_name(name):
203
- """Get template name that is compliant with NEURON based on given name.
204
-
205
- Parameters
206
- ----------
207
- name : string
208
- template_name to transform
209
-
210
- Returns
211
- -------
212
- new_name : string
213
- If `name` is NEURON-compliant, the same string is return.
214
- Otherwise, hyphens are replaced by underscores and if
215
- appropriate, the string is shortened.
216
- Leading numbers are removed.
217
- """
192
+ def get_neuron_compliant_template_name(name: str) -> str:
193
+ """Get template name that is compliant with NEURON based on given name."""
218
194
  template_name = name
219
195
  if not check_compliance_with_neuron(template_name):
220
196
  template_name = template_name.lstrip(string.digits).replace("-", "_")
@@ -21,18 +21,14 @@
21
21
  {load_file("fileUtils.hoc")}
22
22
 
23
23
  rngMode = 0 // corresponds to COMPATIBILITY defined below
24
- ionchannelSeed = 0
25
- synapseSeed = 0
26
- stimulusSeed = 0
27
- minisSeed = 0
28
24
  globalSeed = 0
29
25
 
30
26
  begintemplate RNGSettings
31
27
 
32
- public init, interpret, getRNGMode, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed
28
+ public init, interpret, getRNGMode, getGlobalSeed
33
29
  public COMPATIBILITY, RANDOM123, UPMCELLRAN4
34
30
 
35
- external rngMode, ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate
31
+ external rngMode, globalSeed, terminate
36
32
 
37
33
  proc init() {
38
34
  // consts for random number handling
@@ -18,35 +18,59 @@ import logging
18
18
  from typing import Optional
19
19
 
20
20
  import neuron
21
- from bluecellulab import Singleton
22
- from bluecellulab.circuit.circuit_access import CircuitAccess
21
+ from bluecellulab.circuit.config.definition import SimulationConfig
23
22
  from bluecellulab.exceptions import UndefinedRNGException
24
23
  from bluecellulab.importer import load_hoc_and_mod_files
25
24
 
26
25
  logger = logging.getLogger(__name__)
27
26
 
28
27
 
29
- class RNGSettings(metaclass=Singleton):
30
- """Class that represents RNG settings in bluecellulab."""
28
+ class RNGSettings:
29
+ """Singleton object that represents RNG settings in bluecellulab."""
30
+
31
+ _instance = None
32
+
33
+ @classmethod
34
+ def get_instance(cls):
35
+ """Return the instance of the class."""
36
+ if cls._instance is None:
37
+ cls._instance = cls()
38
+ return cls._instance
31
39
 
32
40
  @load_hoc_and_mod_files
33
41
  def __init__(
42
+ self,
43
+ mode="Random123",
44
+ base_seed=0,
45
+ synapse_seed=0,
46
+ ionchannel_seed=0,
47
+ stimulus_seed=0,
48
+ minis_seed=0,
49
+ ) -> None:
50
+ self.mode = mode
51
+ self.base_seed = base_seed
52
+ self.synapse_seed = synapse_seed
53
+ self.ionchannel_seed = ionchannel_seed
54
+ self.stimulus_seed = stimulus_seed
55
+ self.minis_seed = minis_seed
56
+
57
+ def set_seeds(
34
58
  self,
35
59
  mode: Optional[str] = None,
36
- circuit_access: Optional[CircuitAccess] = None,
60
+ sim_config: Optional[SimulationConfig] = None,
37
61
  base_seed: Optional[int] = None):
38
62
  """Constructor.
39
63
 
40
64
  Parameters
41
65
  ----------
42
66
  mode : rng mode, if not specified mode is taken from circuit_access
43
- circuit: circuit access object, if present seeds are read from simulation
67
+ sim_config: simulation config object, if present seeds are read from simulation
44
68
  base_seed: base seed for entire sim, overrides config value
45
69
  """
46
70
  self._mode = ""
47
71
  if mode is None:
48
- if circuit_access is not None:
49
- self.mode = circuit_access.config.rng_mode if circuit_access else "Compatibility"
72
+ if sim_config is not None:
73
+ self.mode = sim_config.rng_mode if sim_config else "Compatibility"
50
74
  else:
51
75
  self.mode = "Random123"
52
76
  else:
@@ -55,7 +79,7 @@ class RNGSettings(metaclass=Singleton):
55
79
  logger.debug("Setting rng mode to: %s", self._mode)
56
80
 
57
81
  if base_seed is None:
58
- self.base_seed = circuit_access.config.base_seed if circuit_access else 0
82
+ self.base_seed = sim_config.base_seed if sim_config else 0
59
83
  else:
60
84
  self.base_seed = base_seed
61
85
  neuron.h.globalSeed = self.base_seed
@@ -64,17 +88,11 @@ class RNGSettings(metaclass=Singleton):
64
88
  rng = neuron.h.Random()
65
89
  rng.Random123_globalindex(self.base_seed)
66
90
 
67
- self.synapse_seed = circuit_access.config.synapse_seed if circuit_access else 0
68
- neuron.h.synapseSeed = self.synapse_seed
69
-
70
- self.ionchannel_seed = circuit_access.config.ionchannel_seed if circuit_access else 0
71
- neuron.h.ionchannelSeed = self.ionchannel_seed
72
-
73
- self.stimulus_seed = circuit_access.config.stimulus_seed if circuit_access else 0
74
- neuron.h.stimulusSeed = self.stimulus_seed
75
-
76
- self.minis_seed = circuit_access.config.minis_seed if circuit_access else 0
77
- neuron.h.minisSeed = self.minis_seed
91
+ if sim_config:
92
+ self.synapse_seed = sim_config.synapse_seed
93
+ self.ionchannel_seed = sim_config.ionchannel_seed
94
+ self.stimulus_seed = sim_config.stimulus_seed
95
+ self.minis_seed = sim_config.minis_seed
78
96
 
79
97
  @property
80
98
  def mode(self):
bluecellulab/ssim.py CHANGED
@@ -40,6 +40,7 @@ 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.rngsettings import RNGSettings
43
44
  from bluecellulab.stimulus.circuit_stimulus_definitions import Noise, OrnsteinUhlenbeck, RelativeOrnsteinUhlenbeck, RelativeShotNoise, ShotNoise
44
45
  import bluecellulab.stimulus.circuit_stimulus_definitions as circuit_stimulus_definitions
45
46
  from bluecellulab.exceptions import BluecellulabError
@@ -94,10 +95,12 @@ class SSim:
94
95
 
95
96
  self.pc = neuron.h.ParallelContext() if print_cellstate else None
96
97
 
97
- self.rng_settings = bluecellulab.RNGSettings(
98
+ self.rng_settings = RNGSettings.get_instance()
99
+ self.rng_settings.set_seeds(
98
100
  rng_mode,
99
- self.circuit_access,
100
- base_seed=base_seed)
101
+ self.circuit_access.config,
102
+ base_seed=base_seed
103
+ )
101
104
 
102
105
  self.cells: CellDict = CellDict()
103
106
 
@@ -733,7 +736,6 @@ class SSim:
733
736
  'morphology_path': self.circuit_access.morph_filepath(cell_id),
734
737
  'cell_id': cell_id,
735
738
  'record_dt': self.record_dt,
736
- 'rng_settings': self.rng_settings,
737
739
  'template_format': self.circuit_access.get_template_format(),
738
740
  'emodel_properties': emodel_properties,
739
741
  }
@@ -747,6 +749,5 @@ class SSim:
747
749
  morphology_path=cell_kwargs['morphology_path'],
748
750
  cell_id=cell_kwargs['cell_id'],
749
751
  record_dt=cell_kwargs['record_dt'],
750
- rng_settings=cell_kwargs['rng_settings'],
751
752
  template_format=cell_kwargs['template_format'],
752
753
  emodel_properties=cell_kwargs['emodel_properties'])
@@ -60,13 +60,13 @@ class SynapseFactory:
60
60
  randomize_gaba_risetime = condition_parameters.randomize_gaba_rise_time
61
61
  else:
62
62
  randomize_gaba_risetime = True
63
- synapse = GabaabSynapse(cell.rng_settings, cell.cell_id, syn_hoc_args, syn_id, syn_description,
63
+ synapse = GabaabSynapse(cell.cell_id, syn_hoc_args, syn_id, syn_description,
64
64
  popids, extracellular_calcium, randomize_gaba_risetime)
65
65
  elif syn_type == SynapseType.AMPANMDA:
66
- synapse = AmpanmdaSynapse(cell.rng_settings, cell.cell_id, syn_hoc_args, syn_id, syn_description,
66
+ synapse = AmpanmdaSynapse(cell.cell_id, syn_hoc_args, syn_id, syn_description,
67
67
  popids, extracellular_calcium)
68
68
  else:
69
- synapse = GluSynapse(cell.rng_settings, cell.cell_id, syn_hoc_args, syn_id, syn_description,
69
+ synapse = GluSynapse(cell.cell_id, syn_hoc_args, syn_id, syn_description,
70
70
  popids, extracellular_calcium)
71
71
 
72
72
  synapse = cls.apply_connection_modifiers(connection_modifiers, synapse)
@@ -47,7 +47,6 @@ class Synapse:
47
47
 
48
48
  def __init__(
49
49
  self,
50
- rng_settings: RNGSettings,
51
50
  cell_id: CellId,
52
51
  hoc_args: SynapseHocArgs,
53
52
  syn_id: tuple[str, int],
@@ -57,7 +56,6 @@ class Synapse:
57
56
  """Constructor.
58
57
 
59
58
  Args:
60
- rng_settings: Random number generator settings.
61
59
  gid: The post-synaptic cell gid.
62
60
  hoc_args: The synapse location and section in hoc.
63
61
  syn_id: A tuple containing a synapse identifier, where the string is
@@ -88,8 +86,6 @@ class Synapse:
88
86
  self.mech_name: str = "not-yet-defined"
89
87
  self.randseed3: Optional[int] = None
90
88
 
91
- self.rng_settings = rng_settings
92
-
93
89
  @property
94
90
  def delay_weights(self) -> list[tuple[float, float]]:
95
91
  """Adjustments to synapse delay and weight."""
@@ -147,31 +143,32 @@ class Synapse:
147
143
  Raises:
148
144
  ValueError: when rng mode is not recognised.
149
145
  """
150
- if self.rng_settings.mode == "Random123":
146
+ rng_settings = RNGSettings.get_instance()
147
+ if rng_settings.mode == "Random123":
151
148
  self.randseed1 = self.post_cell_id.id + 250
152
149
  self.randseed2 = self.syn_id.sid + 100
153
150
  self.randseed3 = self.source_popid * 65536 + self.target_popid + \
154
- self.rng_settings.synapse_seed + 300
151
+ rng_settings.synapse_seed + 300
155
152
  self.hsynapse.setRNG( # type: ignore
156
153
  self.randseed1,
157
154
  self.randseed2,
158
155
  self.randseed3)
159
156
  else:
160
157
  rndd = neuron.h.Random()
161
- if self.rng_settings.mode == "Compatibility":
158
+ if rng_settings.mode == "Compatibility":
162
159
  self.randseed1 = self.syn_id.sid * 100000 + 100
163
160
  self.randseed2 = self.post_cell_id.id + \
164
- 250 + self.rng_settings.base_seed
165
- elif self.rng_settings.mode == "UpdatedMCell":
161
+ 250 + rng_settings.base_seed
162
+ elif rng_settings.mode == "UpdatedMCell":
166
163
  self.randseed1 = self.syn_id.sid * 1000 + 100
167
164
  self.randseed2 = self.source_popid * 16777216 + \
168
165
  self.post_cell_id.id + \
169
- 250 + self.rng_settings.base_seed + \
170
- self.rng_settings.synapse_seed
166
+ 250 + rng_settings.base_seed + \
167
+ rng_settings.synapse_seed
171
168
  else:
172
169
  raise ValueError(
173
170
  "Synapse: unknown RNG mode: %s" %
174
- self.rng_settings.mode)
171
+ rng_settings.mode)
175
172
  self.randseed3 = None # Not used in this case
176
173
  rndd.MCellRan4(self.randseed1, self.randseed2)
177
174
  rndd.uniform(0, 1)
@@ -241,8 +238,8 @@ class Synapse:
241
238
 
242
239
  class GluSynapse(Synapse):
243
240
 
244
- def __init__(self, rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium):
245
- super().__init__(rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium)
241
+ def __init__(self, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium):
242
+ super().__init__(gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium)
246
243
  self.use_glusynapse_helper()
247
244
 
248
245
  def use_glusynapse_helper(self) -> None:
@@ -290,7 +287,8 @@ class GluSynapse(Synapse):
290
287
 
291
288
  self.randseed1 = self.post_cell_id.id
292
289
  self.randseed2 = 100000 + self.syn_id.sid
293
- self.randseed3 = self.rng_settings.synapse_seed + 200
290
+ rng_settings = RNGSettings.get_instance()
291
+ self.randseed3 = rng_settings.synapse_seed + 200
294
292
  self.hsynapse.setRNG(self.randseed1, self.randseed2, self.randseed3)
295
293
  self.hsynapse.synapseID = self.syn_id.sid
296
294
 
@@ -303,8 +301,8 @@ class GluSynapse(Synapse):
303
301
 
304
302
  class GabaabSynapse(Synapse):
305
303
 
306
- def __init__(self, rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium, randomize_risetime=True):
307
- super().__init__(rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium)
304
+ def __init__(self, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium, randomize_risetime=True):
305
+ super().__init__(gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium)
308
306
  self.use_gabaab_helper(randomize_risetime)
309
307
 
310
308
  def use_gabaab_helper(self, randomize_gaba_risetime: bool) -> None:
@@ -329,20 +327,21 @@ class GabaabSynapse(Synapse):
329
327
 
330
328
  if randomize_gaba_risetime is True:
331
329
  rng = neuron.h.Random()
332
- if self.rng_settings.mode == "Compatibility":
330
+ rng_settings = RNGSettings.get_instance()
331
+ if rng_settings.mode == "Compatibility":
333
332
  rng.MCellRan4(
334
333
  self.syn_id.sid * 100000 + 100,
335
- self.post_cell_id.id + 250 + self.rng_settings.base_seed)
336
- elif self.rng_settings.mode == "UpdatedMCell":
334
+ self.post_cell_id.id + 250 + rng_settings.base_seed)
335
+ elif rng_settings.mode == "UpdatedMCell":
337
336
  rng.MCellRan4(
338
337
  self.syn_id.sid * 1000 + 100,
339
338
  self.source_popid *
340
339
  16777216 +
341
340
  self.post_cell_id.id +
342
341
  250 +
343
- self.rng_settings.base_seed +
344
- self.rng_settings.synapse_seed)
345
- elif self.rng_settings.mode == "Random123":
342
+ rng_settings.base_seed +
343
+ rng_settings.synapse_seed)
344
+ elif rng_settings.mode == "Random123":
346
345
  rng.Random123(
347
346
  self.post_cell_id.id +
348
347
  250,
@@ -351,12 +350,12 @@ class GabaabSynapse(Synapse):
351
350
  self.source_popid *
352
351
  65536 +
353
352
  self.target_popid +
354
- self.rng_settings.synapse_seed +
353
+ rng_settings.synapse_seed +
355
354
  450)
356
355
  else:
357
356
  raise ValueError(
358
357
  "Synapse: unknown RNG mode: %s" %
359
- self.rng_settings.mode)
358
+ rng_settings.mode)
360
359
 
361
360
  rng.lognormal(0.2, 0.1)
362
361
  self.hsynapse.tau_r_GABAA = rng.repick()
@@ -386,8 +385,8 @@ class GabaabSynapse(Synapse):
386
385
 
387
386
  class AmpanmdaSynapse(Synapse):
388
387
 
389
- def __init__(self, rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium):
390
- super().__init__(rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium)
388
+ def __init__(self, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium):
389
+ super().__init__(gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium)
391
390
  self.use_ampanmda_helper()
392
391
 
393
392
  def use_ampanmda_helper(self) -> None: