diff --git a/bluecellulab/synapse/synapse_factory.py b/bluecellulab/synapse/synapse_factory.py index 97d4cc6a..02f65472 100644 --- a/bluecellulab/synapse/synapse_factory.py +++ b/bluecellulab/synapse/synapse_factory.py @@ -25,6 +25,7 @@ from bluecellulab.synapse import Synapse, GabaabSynapse, AmpanmdaSynapse, GluSynapse from bluecellulab.circuit.config.sections import Conditions from bluecellulab.circuit.synapse_properties import SynapseProperties, SynapseProperty +from bluecellulab.synapse.synapse_types import SynapseHocArgs from bluecellulab.type_aliases import NeuronSection @@ -54,7 +55,7 @@ def create_synapse( ) syn_type = cls.determine_synapse_type(is_inhibitory, plasticity_available) - syn_location = cls.determine_synapse_location(syn_description, cell) + syn_hoc_args = cls.determine_synapse_location(syn_description, cell) synapse: Synapse if syn_type == SynapseType.GABAAB: @@ -62,13 +63,13 @@ def create_synapse( randomize_gaba_risetime = condition_parameters.randomize_gaba_rise_time else: randomize_gaba_risetime = True - synapse = GabaabSynapse(cell, syn_location, syn_id, syn_description, + synapse = GabaabSynapse(cell.rng_settings, cell.gid, syn_hoc_args, syn_id, syn_description, popids, extracellular_calcium, randomize_gaba_risetime) elif syn_type == SynapseType.AMPANMDA: - synapse = AmpanmdaSynapse(cell, syn_location, syn_id, syn_description, + synapse = AmpanmdaSynapse(cell.rng_settings, cell.gid, syn_hoc_args, syn_id, syn_description, popids, extracellular_calcium) else: - synapse = GluSynapse(cell, syn_location, syn_id, syn_description, + synapse = GluSynapse(cell.rng_settings, cell.gid, syn_hoc_args, syn_id, syn_description, popids, extracellular_calcium) synapse = cls.apply_connection_modifiers(connection_modifiers, synapse) @@ -99,9 +100,10 @@ def determine_synapse_type( return SynapseType.AMPANMDA @classmethod - def determine_synapse_location(cls, syn_description: pd.Series, cell: bluecellulab.Cell) -> float: + def determine_synapse_location(cls, syn_description: pd.Series, cell: bluecellulab.Cell) -> SynapseHocArgs: """Returns the location of the synapse.""" isec = syn_description[SynapseProperty.POST_SECTION_ID] + section = cell.get_hsection(isec) # old circuits don't have it, it needs to be computed via synlocation_to_segx if ("afferent_section_pos" in syn_description and @@ -111,10 +113,9 @@ def determine_synapse_location(cls, syn_description: pd.Series, cell: bluecellul else: ipt = syn_description[SynapseProperty.POST_SEGMENT_ID] syn_offset = syn_description[SynapseProperty.POST_SEGMENT_OFFSET] - section = cell.get_hsection(isec) location = cls.synlocation_to_segx(section, ipt, syn_offset) - return location + return SynapseHocArgs(location, section) @staticmethod def synlocation_to_segx( diff --git a/bluecellulab/synapse/synapse_types.py b/bluecellulab/synapse/synapse_types.py index 2dd4fe95..9d1bac78 100644 --- a/bluecellulab/synapse/synapse_types.py +++ b/bluecellulab/synapse/synapse_types.py @@ -20,7 +20,8 @@ import bluecellulab from bluecellulab.circuit import SynapseProperty -from bluecellulab.type_aliases import HocObjectType +from bluecellulab.rngsettings import RNGSettings +from bluecellulab.type_aliases import HocObjectType, NeuronSection logger = logging.getLogger(__name__) @@ -33,36 +34,45 @@ class SynapseID(NamedTuple): sid: int +class SynapseHocArgs(NamedTuple): + """Parameters required by synapse hoc constructor.""" + location: float + section: NeuronSection + + class Synapse: """Class that represents a synapse in bluecellulab.""" def __init__( self, - cell: bluecellulab.Cell, - location: float, + rng_settings: RNGSettings, + gid: int, + hoc_args: SynapseHocArgs, syn_id: tuple[str, int], syn_description: pd.Series, popids: tuple[int, int], extracellular_calcium: float | None = None): """Constructor. - Parameters - ---------- - cell : Cell that contains the synapse - location : Location on the section this synapse is placed between [0.0,1.0] - syn_id : Synapse identifier, string being the projection name and - int the synapse id. Empty string refers to a local connection - syn_description : Parameters of the synapse - popids : Source and target popids used by the random number generation - extracellular_calcium: the extracellular calcium concentration + Args: + rng_settings: Random number generator settings. + gid: The post-synaptic cell gid. + hoc_args: The synapse location and section in hoc. + syn_id: A tuple containing a synapse identifier, where the string is + the projection name and int is the synapse id. An empty string + refers to a local connection. + syn_description: Parameters of the synapse. + popids: A tuple containing source and target popids used by the random + number generation. + extracellular_calcium: The extracellular calcium concentration. Optional + and defaults to None. """ self.persistent: list[HocObjectType] = [] self.synapseconfigure_cmds: list[str] = [] self._delay_weights: list[tuple[float, float]] = [] self._weight: Optional[float] = None - self.cell = cell - self.post_gid = cell.gid + self.post_gid = gid self.syn_id = SynapseID(*syn_id) self.extracellular_calcium = extracellular_calcium self.syn_description: pd.Series = self.update_syn_description(syn_description) @@ -72,11 +82,11 @@ def __init__( self.pre_gid = int(self.syn_description[SynapseProperty.PRE_GID]) - self.post_segx = location + self.hoc_args = hoc_args self.mech_name: str = "not-yet-defined" self.randseed3: Optional[int] = None - self.rng_settings = cell.rng_settings + self.rng_settings = rng_settings @property def delay_weights(self) -> list[tuple[float, float]]: @@ -136,7 +146,7 @@ def _set_gabaab_ampanmda_rng(self) -> None: ValueError: when rng mode is not recognised. """ if self.rng_settings.mode == "Random123": - self.randseed1 = self.cell.gid + 250 + self.randseed1 = self.post_gid + 250 self.randseed2 = self.syn_id.sid + 100 self.randseed3 = self.source_popid * 65536 + self.target_popid + \ self.rng_settings.synapse_seed + 300 @@ -148,12 +158,12 @@ def _set_gabaab_ampanmda_rng(self) -> None: rndd = bluecellulab.neuron.h.Random() if self.rng_settings.mode == "Compatibility": self.randseed1 = self.syn_id.sid * 100000 + 100 - self.randseed2 = self.cell.gid + \ + self.randseed2 = self.post_gid + \ 250 + self.rng_settings.base_seed elif self.rng_settings.mode == "UpdatedMCell": self.randseed1 = self.syn_id.sid * 1000 + 100 self.randseed2 = self.source_popid * 16777216 + \ - self.cell.gid + \ + self.post_gid + \ 250 + self.rng_settings.base_seed + \ self.rng_settings.synapse_seed else: @@ -205,7 +215,7 @@ def info_dict(self) -> dict[str, Any]: synapse_dict['syn_description'] = { str(k): v for k, v in synapse_dict['syn_description'].items()} - synapse_dict['post_segx'] = self.post_segx + synapse_dict['post_segx'] = self.hoc_args.location synapse_dict['mech_name'] = self.mech_name synapse_dict['randseed1'] = self.randseed1 synapse_dict['randseed2'] = self.randseed2 @@ -229,8 +239,8 @@ def __del__(self) -> None: class GluSynapse(Synapse): - def __init__(self, cell, location, syn_id, syn_description, popids, extracellular_calcium): - super().__init__(cell, location, syn_id, syn_description, popids, extracellular_calcium) + def __init__(self, rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium): + super().__init__(rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium) self.use_glusynapse_helper() def use_glusynapse_helper(self) -> None: @@ -242,10 +252,8 @@ def use_glusynapse_helper(self) -> None: self.mech_name = 'GluSynapse' self.hsynapse = bluecellulab.neuron.h.GluSynapse( - self.post_segx, - sec=self.cell.get_hsection( - self.syn_description[SynapseProperty.POST_SECTION_ID] - ), + self.hoc_args.location, + sec=self.hoc_args.section ) self.hsynapse.Use_d = self.syn_description["Use_d_TM"] * \ @@ -278,7 +286,7 @@ def use_glusynapse_helper(self) -> None: if self.syn_description[SynapseProperty.NRRP] >= 0: self.hsynapse.Nrrp = self.syn_description[SynapseProperty.NRRP] - self.randseed1 = self.cell.gid + self.randseed1 = self.post_gid self.randseed2 = 100000 + self.syn_id.sid self.randseed3 = self.rng_settings.synapse_seed + 200 self.hsynapse.setRNG(self.randseed1, self.randseed2, self.randseed3) @@ -293,8 +301,8 @@ def info_dict(self): class GabaabSynapse(Synapse): - def __init__(self, cell, location, syn_id, syn_description, popids, extracellular_calcium, randomize_risetime=True): - super().__init__(cell, location, syn_id, syn_description, popids, extracellular_calcium) + def __init__(self, rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium, randomize_risetime=True): + super().__init__(rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium) self.use_gabaab_helper(randomize_risetime) def use_gabaab_helper(self, randomize_gaba_risetime: bool) -> None: @@ -313,10 +321,8 @@ def use_gabaab_helper(self, randomize_gaba_risetime: bool) -> None: self.mech_name = 'ProbGABAAB_EMS' self.hsynapse = bluecellulab.neuron.h.ProbGABAAB_EMS( - self.post_segx, - sec=self.cell.get_hsection( - self.syn_description[SynapseProperty.POST_SECTION_ID] - ) + self.hoc_args.location, + sec=self.hoc_args.section ) if randomize_gaba_risetime is True: @@ -324,19 +330,19 @@ def use_gabaab_helper(self, randomize_gaba_risetime: bool) -> None: if self.rng_settings.mode == "Compatibility": rng.MCellRan4( self.syn_id.sid * 100000 + 100, - self.cell.gid + 250 + self.rng_settings.base_seed) + self.post_gid + 250 + self.rng_settings.base_seed) elif self.rng_settings.mode == "UpdatedMCell": rng.MCellRan4( self.syn_id.sid * 1000 + 100, self.source_popid * 16777216 + - self.cell.gid + + self.post_gid + 250 + self.rng_settings.base_seed + self.rng_settings.synapse_seed) elif self.rng_settings.mode == "Random123": rng.Random123( - self.cell.gid + + self.post_gid + 250, self.syn_id.sid + 100, @@ -378,8 +384,8 @@ def info_dict(self): class AmpanmdaSynapse(Synapse): - def __init__(self, cell, location, syn_id, syn_description, popids, extracellular_calcium): - super().__init__(cell, location, syn_id, syn_description, popids, extracellular_calcium) + def __init__(self, rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium): + super().__init__(rng_settings, gid, hoc_args, syn_id, syn_description, popids, extracellular_calcium) self.use_ampanmda_helper() def use_ampanmda_helper(self) -> None: @@ -391,10 +397,8 @@ def use_ampanmda_helper(self) -> None: self.mech_name = 'ProbAMPANMDA_EMS' self.hsynapse = bluecellulab.neuron.h.ProbAMPANMDA_EMS( - self.post_segx, - sec=self.cell.get_hsection( - self.syn_description[SynapseProperty.POST_SECTION_ID] - ), + self.hoc_args.location, + sec=self.hoc_args.section, ) self.hsynapse.tau_d_AMPA = self.syn_description[SynapseProperty.DTC] if SynapseProperty.CONDUCTANCE_RATIO in self.syn_description: diff --git a/tests/test_synapse/test_synapse_factory.py b/tests/test_synapse/test_synapse_factory.py index 8744bfcd..46866729 100644 --- a/tests/test_synapse/test_synapse_factory.py +++ b/tests/test_synapse/test_synapse_factory.py @@ -54,12 +54,13 @@ def test_create_synapse(self): assert synapse.weight == connection_modifiers["Weight"] def test_determine_synapse_location(self): - location = SynapseFactory.determine_synapse_location(self.syn_description, self.cell) - assert location == 0.9999999 + res = SynapseFactory.determine_synapse_location(self.syn_description, self.cell) + assert res.location == 0.9999999 # set afferent_section_pos self.syn_description["afferent_section_pos"] = 1.2 - location = SynapseFactory.determine_synapse_location(self.syn_description, self.cell) - assert location == 1.2 + res = SynapseFactory.determine_synapse_location(self.syn_description, self.cell) + assert res.location == 1.2 + assert res.section.L == pytest.approx(9.530376893488256) def test_determine_synapse_type():