Source code for dendrotweaks.stimuli.synapses

# SPDX-FileCopyrightText: 2025 Poirazi Lab <dendrotweaks@dendrites.gr>
# SPDX-License-Identifier: MPL-2.0

from typing import List
from neuron import h
import numpy as np
from dendrotweaks.morphology.seg_trees import Segment

[docs] class Synapse(): """ A synapse object that can be placed on a section of a neuron. Contains references to the NEURON synapse object, the stimulus object (NetStim), and the connection object (NetCon). Parameters ---------- syn_type : str The type of synapse to create e.g. 'AMPA', 'NMDA', 'AMPA_NMDA', 'GABA'. sec : Section The section on which the synapse is placed. loc : float The location on the section where the synapse is placed, between 0 and 1. Attributes ---------- sec : Section The section on which the synapse is placed. loc : float The location on the section where the synapse is placed, between 0 and 1. """ def __init__(self, syn_type: str, sec, loc=0.5) -> None: """ Creates a new synapse object. """ self._Model = getattr(h, syn_type) self.sec = sec self.loc = loc self._ref_syn = self._Model(self.seg._ref) self._ref_stim = None self._ref_con = None @property def seg(self): """ The segment on which the synapse is placed. """ return self.sec(self.loc) def __repr__(self): return f"<Synapse({self.sec}({self.loc:.3f}))>" @property def spike_times(self): """ The spike times of the stimulus from the NetStim object. """ if self._ref_stim is not None: return self._ref_stim[1].to_python() return [] def _clear_syn(self): """ Clears the synapse (Model) object. """ del self._ref_syn self._ref_syn = None def _clear_stim(self): """ Clears the stimulus (NetStim) object. """ self._ref_stim[0] = None self._ref_stim[1] = None self._ref_stim.pop(0) self._ref_stim.pop(0) self._ref_stim = None
[docs] def create_stim(self, **kwargs): """ Creates a stimulus (NetStim) for the synapse. Parameters ---------- **kwargs : dict Keyword arguments for the create_spike_times function. """ if self._ref_stim is not None: self._clear_stim() spike_times = create_spike_times(**kwargs) spike_vec = h.Vector(spike_times) stim = h.VecStim() stim.play(spike_vec) self._ref_stim = [stim, spike_vec]
def _clear_con(self): """ Clears the connection (NetCon) object. """ self._ref_con = None
[docs] def create_con(self, delay, weight): """ Create a connection (NetCon) between the stimulus and the synapse. Parameters ---------- delay : int The delay of the connection, in ms. weight : float The weight of the connection. """ if self._ref_con is not None: self._clear_con() self._ref_con = h.NetCon(self._ref_stim[0], self._ref_syn, 0, delay, weight)
[docs] def xyz(self): """ Returns the 3D coordinates of the synapse location. Returns ------- tuple The (x, y, z) coordinates of the synapse location. """ return self.sec.get_location_coordinates(self.loc)
def create_spike_times(rate=1, noise=1, duration=300, delay=0, seed=None): """ Create a spike train with a given regularity. Parameters ---------- rate : float The rate of the spike train, in Hz. noise : float A parameter between 0 and 1 that controls the regularity of the spike train. 0 corresponds to a regular spike train. 1 corresponds to a Poisson process. duration : int The total time to run the simulation for, in ms. delay : int The delay of the spike train, in ms. Returns ------- np.array The spike times as a vector, in ms. """ if noise == 1: return delay + generate_poisson_process(rate, duration, seed) else: return delay + generate_jittered_spikes(rate, duration, noise, seed) def generate_poisson_process(lam, dur, seed=None): """ Generate a Poisson process. Parameters ---------- lam : float The rate parameter (lambda) of the Poisson process, in Hz. dur : int The total time to run the simulation for, in ms. Returns ------- np.array The spike times as a vector, in ms. """ rng = np.random.default_rng(seed) dur_s = dur / 1000 intervals = rng.exponential(1/lam, int(lam*dur_s)) spike_times = np.cumsum(intervals) spike_times = spike_times[spike_times <= dur_s] spike_times_ms = spike_times * 1000 return spike_times_ms def generate_jittered_spikes(rate, dur, noise, seed=None): """ Generate a jittered spike train. Parameters ---------- rate : float The rate of the spike train, in Hz. dur : int The total time to run the simulation for, in ms. noise : float A parameter between 0 and 1 that controls the regularity of the spike train. 0 corresponds to a regular spike train. 1 corresponds to a Poisson process. Returns ------- np.array The spike times as a vector, in ms. """ dur_s = dur / 1000 spike_times = np.arange(0, dur_s, 1/rate) # Add noise rng = np.random.default_rng(seed) noise_values = rng.normal(0, noise/rate, len(spike_times)) spike_times += noise_values # Ensure spike times are within the duration and sort them spike_times = spike_times[(spike_times >= 0) & (spike_times <= dur_s)] spike_times.sort() spike_times_ms = spike_times * 1000 return spike_times_ms