Source code for golding_mso.syns

"""
This module provides classes and functions for creating and managing synaptic units (Exp2Syn, Netcon & Netstim)
in NEURON simulations, including random and path-based innervation of cell sections.
"""

import numpy as np
import random

from neuron import h
from .nrn_types import Section, Segment, Exp2Syn, NetCon, NetStim
from .cell import Cell
from .cell_calc import get_terminal_sections, get_parent_sections, section_list_length


[docs] class SynapseUnit: """ Represents a synaptic unit consisting of a Exp2Syn, NetStim, and NetCon on a given segment. Handles creation and management of synaptic parameters and firing probability. """ def __init__(self, segment: Segment, **kwargs) -> None: r""" Initialize a SynapseUnit on a given segment. A Synapse consists of an Exp2Syn synapse, a NetStim for stimulation, and a NetCon to connect them. Parameters ---------- segment : Segment The NEURON segment to place the synapse on. \**kwargs: Optional parameters for synapse and stimulation properties. firing_probability (float): Probability the synapse will fire (default 1.0). start (float): Start time for NetStim (default 0). tau1, tau2, e, number, interval, noise, gmax, delay: Passed to respective NEURON objects. """ self.section = segment.sec self.segment = segment self.firing_probability = kwargs.pop("firing_probability", 1.0) self.start = kwargs.pop("start", 0) # Create NEURON objects for this synapse unit self.syn = self.create_syn(**kwargs) self.netstim = self.create_netstim(**kwargs) self.netcon = self.create_netcon(**kwargs)
[docs] def create_syn(self, **kwargs) -> Exp2Syn: r""" Create and configure an Exp2Syn synapse on the segment. Parameters ---------- \**kwargs: tau1, tau2, e (optional synaptic parameters). Returns ------- Exp2Syn: The created synapse object. """ syn = h.Exp2Syn(self.segment) syn.tau1 = kwargs.pop("tau1", 0.29) syn.tau2 = kwargs.pop("tau2", 0.29) syn.e = kwargs.pop("e", 15) return syn
[docs] def create_netstim(self, **kwargs) -> NetStim: r""" Create and configure a NetStim for this synapse. Parameters ---------- \**kwargs: number, interval, noise (optional stimulation parameters). Returns ------- NetStim: The created NetStim object. """ netstim = h.NetStim() netstim.number = kwargs.pop("number", 1) netstim.interval = kwargs.pop("interval", 1) netstim.noise = kwargs.pop("noise", 0) # Determine if this synapse will fire based on probability self.firing = random.random() < self.firing_probability netstim.start = self.start if self.firing else -1 return netstim
[docs] def create_netcon(self, **kwargs) -> NetCon: r""" Create and configure a NetCon connecting NetStim to the synapse. Parameters ---------- \**kwargs: gmax (weight), delay (optional connection parameters). Returns ------- NetCon: The created NetCon object. """ netcon = h.NetCon(self.netstim, self.syn) netcon.weight[0] = kwargs.pop("gmax", 0.037) netcon.delay = kwargs.pop("delay", 0) return netcon
[docs] def reroll_firing(self, **kwargs) -> None: r""" Reroll the firing probability of the synapse unit and update NetStim start time. Parameters ---------- \**kwargs: firing_probability (float, optional): New probability to use. """ self.firing_probability = kwargs.pop( "firing_probability", self.firing_probability ) self.firing = random.random() < self.firing_probability self.netstim.start = self.start if self.firing else -1
def __repr__(self) -> str: """ Return a string representation of the SynapseUnit. """ return f"Synapse @ {self.segment} on section {str(self.section)}"
[docs] def destroy(self) -> None: """ Destroy the synapse unit by deleting the synapse, netstim, and netcon objects. """ del self.syn del self.netstim del self.netcon
[docs] def innervate_total(section_list: list[Section], **kwargs) -> list[SynapseUnit]: r""" Create SynapseUnits for every segment in the provided list of sections. Parameters ---------- section_list : list[Section] List of NEURON Section objects. \**kwargs: Passed to SynapseUnit constructor. Returns ------- list[SynapseUnit]: List of created SynapseUnit objects. """ syn_units = innervate_points( *[seg for sec in section_list for seg in sec], **kwargs ) return syn_units
[docs] def innervate_points(*segments: list[Segment], **kwargs) -> list[SynapseUnit]: r""" Create SynapseUnits for each provided segment. Parameters ---------- \*segments : list[Segment] Segments to innervate. \**kwargs: Passed to SynapseUnit constructor. Returns ------- list[SynapseUnit]: List of created SynapseUnit objects. """ units = [] for segment in segments: synapse_unit = create_synunit(segment, **kwargs) units.append(synapse_unit) return units
[docs] def innervate_random( cell: Cell, section_list: list[Section], numgroups: int = 1, numsyns: int = 4, synspace: float = 7, **kwargs, ) -> list[list[SynapseUnit]]: r""" Randomly innervate a cell with groups of SynapseUnits along random paths. Parameters ---------- cell : Cell The cell to innervate. section_list : list[Section] List of NEURON Section objects. numgroups : int Number of synapse groups to create. numsyns : int Number of synapses per group. synspace : float Minimum spacing between synapses. \**kwargs: Passed to SynapseUnit constructor. Returns ------- list[list[SynapseUnit]]: List of groups, each a list of SynapseUnit objects. """ synapse_groups = [] for group_num in range(numgroups): ends = list(get_terminal_sections(section_list)) random_end = random.choice(ends) # Ensure the chosen path is long enough for the desired number of synapses while ( section_list_length(cell, get_parent_sections(random_end, starts_from_soma=False))[0] < numsyns * synspace ): random_end = random.choice(ends) chosen_path_sections = get_parent_sections(random_end, starts_from_soma=False) # Pick a random length along the path random_length_on_path = random.uniform( 0, section_list_length(cell, chosen_path_sections)[0] ) while random_length_on_path < numsyns * synspace: random_length_on_path = random.uniform( 0, section_list_length(cell, chosen_path_sections)[0] ) chosen_length_on_path = random_length_on_path chosen_path_lengths = section_list_length( cell, chosen_path_sections, return_array=True )[1] # Determine the locations for synapse placement lengths_to_place = np.linspace( chosen_length_on_path - (synspace * numsyns), chosen_length_on_path, numsyns, endpoint=True, ) segments_for_placement = [] for length in lengths_to_place: length_total = 0 for section, section_length in zip( chosen_path_sections, chosen_path_lengths ): length_total += section_length if length_total > length: # Find the segment at the correct location along the section segment = section((length_total - length) / section.L) segments_for_placement.append(segment) break synapse_groups.append(innervate_points(*segments_for_placement, **kwargs)) return synapse_groups
[docs] def syn_path_place( cell: Cell, section_list: list[Section], locations: list[float], tau1=0.270, tau2=0.271, e=15, ): lengtharray = [] synlist = [] for sec in section_list: # record each length of each section if sec in cell.somatic: lengtharray.append(sec.L / 2) # cutting soma in half, to keep on one side else: lengtharray.append(sec.L) for loc in locations: sectioncount = 0 lengthcount = 0 sectionindex = 0 loconsec = 0 # Move along the length of the path until section and relative location are found for length in lengtharray: if length + lengthcount > loc: sectionindex = sectioncount loconsec = loc - lengthcount break else: sectioncount += 1 lengthcount += length syn = SynapseUnit( list(section_list)[sectionindex]( loconsec / list(section_list)[sectionindex].L ), tau1=tau1, tau2=tau2, e=e, ) # Old implementation. New is less tested # syn = h.Exp2Syn( # list(section_list)[sectionindex]( # loconsec / list(section_list)[sectionindex].L # ) # ) # place syn # syn.tau1 = tau1 # syn.tau2 = tau2 # syn.e = e synlist.append(syn) syn = None return synlist
[docs] def create_synunit(segment: Segment, **kwargs) -> SynapseUnit: r""" Helper function to create a SynapseUnit on a given segment. Parameters ---------- segment : Segment The NEURON segment to place the synapse on. \**kwargs: Passed to SynapseUnit constructor. Returns ------- SynapseUnit: The created SynapseUnit object. """ return SynapseUnit(segment, **kwargs)