Skip to content

Commit

Permalink
Refactor functions that include running isolated simulations [vcs: #m…
Browse files Browse the repository at this point in the history
…inor] (#149)

* move NumpyEncoder to utils

* move Singleton to utils

* update docstrings of tools and utils modules

* move verbosity setting to verbosity module

* update search_threshold_current

* delete detect_threshold_current

* add future annotations

* remove calculate_SS_voltage_replay

* remove search_hyp_current_replay functions

* use context manager for pools

* control all multiprocessing through IsolatedProcess class

* add checks for object id in test_setting_rngmodel

* reduce RNGSettings' dependency on CircuitAccess to SimulationConfig

* use get_instance in RNGSettings Singleton

* update example notebook

* uv==0.1.16

* tox-uv 1.4.0

* decouple Synapse from cell.rng_settings

* decouple injector from cell.rng_settings

* delete cell.rng_settings in test_add_replay_relative_shotnoise

* delete cell.rng_settings in test_inject_current_clamp_via_shotnoise_signal

* delete cell.rng_settings in test_get_noise_step_rand

* decouple add_replay_minis from cell.rng_settings

* avoid syncing neuron params for synapse ionchannel stimulus minis seeds

* delete cell.rng_settings in remaining test_injector

* update example notebook not to access rng_settings

* move seed initiations to init from set_seeds

* decouple Cell from RNGSettings

* remove tox-uv

* delete Cell.morphology_path attribute

* update Cell's init docstring

* update docstrings in template.py

* require 4 template dependent arguments in NeuronTemplate

* remove emodel_properties attribute from Cell

* gather template loading code together

* update CHANGELOG

* add back images on singlecell.ipynb
  • Loading branch information
anilbey authored Mar 14, 2024
1 parent eb63577 commit 8bfa9b5
Show file tree
Hide file tree
Showing 23 changed files with 446 additions and 717 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ jobs:
- name: Install dependencies
run: |
pip install tox-gh-actions
pip install tox-uv
- name: Run tox
run: |
tox
Expand All @@ -48,7 +47,6 @@ jobs:
run: |
python -m pip install --upgrade pip setuptools
pip install tox-gh-actions
pip install tox-uv
- name: Run tox
run: |
tox -e lint
Expand All @@ -65,7 +63,6 @@ jobs:
run: |
python -m pip install --upgrade pip setuptools
pip install tox-gh-actions
pip install tox-uv
- name: Run tox
run: |
tox -e examples
Expand All @@ -82,7 +79,6 @@ jobs:
run: |
python -m pip install --upgrade pip setuptools
pip install tox-gh-actions
pip install tox-uv
- name: Run tox
run: |
tox -e docs
9 changes: 9 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Changelog
2.4.0
------

* Decouple Cell and Synapse from RNGSettings
* Have a single way of isolating processes via IsolatedProcess
* Implement: Add missing unit tests for tools functions #76
* Remove redundancies in tools.py
* RNGSettings.get_instance() replaces constructor

2.3.2
------

* Add StimulusFactory enabling Python generated Stimulus creation
* Stimulus creation through StimulusFactory is decoupled from the Cell object
* Cell.add_step and Cell.add_ramp use StimulusFactory
Expand Down
1 change: 1 addition & 0 deletions bluecellulab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from .importer import * # NOQA
from .tools import * # NOQA
from .verbosity import *
from .cell import Cell, create_ball_stick # NOQA
from .connection import Connection # NOQA
from .plotwindow import PlotWindow # NOQA
Expand Down
87 changes: 37 additions & 50 deletions bluecellulab/cell/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,48 +61,47 @@ def __init__(self,
cell_id: Optional[CellId] = None,
record_dt: Optional[float] = None,
template_format: str = "v5",
emodel_properties: Optional[EmodelProperties] = None,
rng_settings: Optional[RNGSettings] = None) -> None:
emodel_properties: Optional[EmodelProperties] = None) -> None:
"""Initializes a Cell object.
Args:
template_path: Full path to hoc template file.
template_path: Path to hoc template file.
morphology_path: Path to morphology file.
gid: ID of the cell, used in RNG seeds. Defaults to 0.
cell_id: ID of the cell, used in RNG seeds.
record_dt: Timestep for the recordings.
If not provided, a default is used. Defaults to None.
template_format: Cell template format such as 'v5'
or 'v6_air_scaler'. Defaults to "v5".
emodel_properties: Properties such as
threshold_current, holding_current. Defaults to None.
rng_settings: Random number generation setting
object used by the Cell. Defaults to None.
template_format: Cell template format such as 'v5' or 'v6_air_scaler'.
emodel_properties: Template specific emodel properties.
"""
super().__init__()
if cell_id is None:
cell_id = CellId("", Cell.last_id)
Cell.last_id += 1
self.cell_id = cell_id
# Persistent objects, like clamps, that exist as long
# as the object exists
self.persistent: list[HocObjectType] = []

self.morphology_path = morphology_path

# Load the template
neuron_template = NeuronTemplate(template_path, morphology_path)
neuron_template = NeuronTemplate(template_path, morphology_path, template_format, emodel_properties)
self.template_id = neuron_template.template_name # useful to map NEURON and python objects
self.cell = neuron_template.get_cell(template_format, self.cell_id.id, emodel_properties)
self.cell = neuron_template.get_cell(self.cell_id.id)
if template_format == 'v6':
if emodel_properties is None:
raise BluecellulabError('EmodelProperties must be provided for v6 template')
self.hypamp: float | None = emodel_properties.holding_current
self.threshold: float | None = emodel_properties.threshold_current
else:
try:
self.hypamp = self.cell.getHypAmp()
except AttributeError:
self.hypamp = None

try:
self.threshold = self.cell.getThreshold()
except AttributeError:
self.threshold = None
self.soma = public_hoc_cell(self.cell).soma[0]
# WARNING: this finitialize 'must' be here, otherwhise the
# diameters of the loaded morph are wrong
neuron.h.finitialize()

if rng_settings is None:
self.rng_settings = RNGSettings("Random123") # SONATA value
else:
self.rng_settings = rng_settings

self.recordings: dict[str, HocObjectType] = {}
self.synapses: dict[SynapseID, Synapse] = {}
self.connections: dict[SynapseID, bluecellulab.Connection] = {}
Expand All @@ -121,30 +120,17 @@ def __init__(self,
self.delayed_weights = queue.PriorityQueue() # type: ignore
self.psections, self.secname_to_psection = init_psections(public_hoc_cell(self.cell))

self.emodel_properties = emodel_properties
if template_format == 'v6':
if self.emodel_properties is None:
raise BluecellulabError('EmodelProperties must be provided for v6 template')
self.hypamp: float | None = self.emodel_properties.holding_current
self.threshold: float | None = self.emodel_properties.threshold_current
else:
try:
self.hypamp = self.cell.getHypAmp()
except AttributeError:
self.hypamp = None

try:
self.threshold = self.cell.getThreshold()
except AttributeError:
self.threshold = None

# Keep track of when a cell is made passive by make_passive()
# Used to know when re_init_rng() can be executed
self.is_made_passive = False

neuron.h.pop_section() # Undoing soma push
self.sonata_proxy: Optional[SonataProxy] = None

# Persistent objects, like clamps, that exist as long
# as the object exists
self.persistent: list[HocObjectType] = []

@property
def somatic(self) -> list[NeuronSection]:
return list(public_hoc_cell(self.cell).somatic)
Expand Down Expand Up @@ -486,7 +472,6 @@ def add_replay_minis(self,

sid = synapse_id[1]

base_seed = self.rng_settings.base_seed
weight = syn_description[SynapseProperty.G_SYNX]
# numpy int to int
post_sec_id = int(syn_description[SynapseProperty.POST_SECTION_ID])
Expand Down Expand Up @@ -526,9 +511,10 @@ def add_replay_minis(self,
# NC_SPONTMINI
self.syn_mini_netcons[synapse_id].weight[nc_type_param] = 1

if self.rng_settings.mode == 'Random123':
rng_settings = RNGSettings.get_instance()
if rng_settings.mode == 'Random123':
seed2 = source_popid * 65536 + target_popid \
+ self.rng_settings.minis_seed
+ rng_settings.minis_seed
self.ips[synapse_id].setRNGs(
sid + 200,
self.cell_id.id + 250,
Expand All @@ -543,25 +529,26 @@ def add_replay_minis(self,
uniformrng = neuron.h.Random()
self.persistent.append(uniformrng)

if self.rng_settings.mode == 'Compatibility':
base_seed = rng_settings.base_seed
if rng_settings.mode == 'Compatibility':
exp_seed1 = sid * 100000 + 200
exp_seed2 = self.cell_id.id + 250 + base_seed + \
self.rng_settings.minis_seed
rng_settings.minis_seed
uniform_seed1 = sid * 100000 + 300
uniform_seed2 = self.cell_id.id + 250 + base_seed + \
self.rng_settings.minis_seed
elif self.rng_settings.mode == "UpdatedMCell":
rng_settings.minis_seed
elif rng_settings.mode == "UpdatedMCell":
exp_seed1 = sid * 1000 + 200
exp_seed2 = source_popid * 16777216 + self.cell_id.id + 250 + \
base_seed + \
self.rng_settings.minis_seed
rng_settings.minis_seed
uniform_seed1 = sid * 1000 + 300
uniform_seed2 = source_popid * 16777216 + self.cell_id.id + 250 \
+ base_seed + \
self.rng_settings.minis_seed
rng_settings.minis_seed
else:
raise ValueError(
f"Cell: Unknown rng mode: {self.rng_settings.mode}")
f"Cell: Unknown rng mode: {rng_settings.mode}")

exprng.MCellRan4(exp_seed1, exp_seed2)
exprng.negexp(1.0)
Expand Down
24 changes: 14 additions & 10 deletions bluecellulab/cell/injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
get_relative_shotnoise_params,
)
from bluecellulab.exceptions import BluecellulabError
from bluecellulab.rngsettings import RNGSettings
from bluecellulab.stimulus.circuit_stimulus_definitions import (
ClampMode,
Hyperpolarizing,
Expand Down Expand Up @@ -191,20 +192,21 @@ def add_voltage_clamp(

def _get_noise_step_rand(self, noisestim_count):
"""Return rng for noise step stimulus."""
if self.rng_settings.mode == "Compatibility":
rng_settings = RNGSettings.get_instance()
if rng_settings.mode == "Compatibility":
rng = neuron.h.Random(self.cell_id.id + noisestim_count)
elif self.rng_settings.mode == "UpdatedMCell":
elif rng_settings.mode == "UpdatedMCell":
rng = neuron.h.Random()
rng.MCellRan4(
noisestim_count * 10000 + 100,
self.rng_settings.base_seed +
self.rng_settings.stimulus_seed +
rng_settings.base_seed +
rng_settings.stimulus_seed +
self.cell_id.id * 1000)
elif self.rng_settings.mode == "Random123":
elif rng_settings.mode == "Random123":
rng = neuron.h.Random()
rng.Random123(
noisestim_count + 100,
self.rng_settings.stimulus_seed + 500,
rng_settings.stimulus_seed + 500,
self.cell_id.id + 300)

self.persistent.append(rng)
Expand Down Expand Up @@ -268,9 +270,10 @@ def add_replay_relativelinear(self, stimulus):

def _get_ornstein_uhlenbeck_rand(self, stim_count, seed):
"""Return rng for ornstein_uhlenbeck simulation."""
if self.rng_settings.mode == "Random123":
rng_settings = RNGSettings.get_instance()
if rng_settings.mode == "Random123":
seed1 = stim_count + 2997 # stimulus block
seed2 = self.rng_settings.stimulus_seed + 291204 # stimulus type
seed2 = rng_settings.stimulus_seed + 291204 # stimulus type
seed3 = self.cell_id.id + 123 if seed is None else seed # GID
logger.debug("Using ornstein_uhlenbeck process seeds %d %d %d" %
(seed1, seed2, seed3))
Expand All @@ -284,9 +287,10 @@ def _get_ornstein_uhlenbeck_rand(self, stim_count, seed):

def _get_shotnoise_step_rand(self, shotnoise_stim_count, seed=None):
"""Return rng for shot noise step stimulus."""
if self.rng_settings.mode == "Random123":
rng_settings = RNGSettings.get_instance()
if rng_settings.mode == "Random123":
seed1 = shotnoise_stim_count + 2997
seed2 = self.rng_settings.stimulus_seed + 19216
seed2 = rng_settings.stimulus_seed + 19216
seed3 = self.cell_id.id + 123 if seed is None else seed
logger.debug("Using shot noise seeds %d %d %d" %
(seed1, seed2, seed3))
Expand Down
Loading

0 comments on commit 8bfa9b5

Please sign in to comment.