Source code for emodelrunner.protocols.sscx_protocols

"""Ephys protocols for the SSCX packages."""

# Copyright 2020-2022 Blue Brain Project / EPFL

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=too-many-lines

import collections
import copy
import logging
import numpy as np
from bluepyopt import ephys

from emodelrunner.protocols.protocols_func import CurrentOutputKeyMixin

logger = logging.getLogger(__name__)


[docs] class RatSSCxMainProtocol(ephys.protocols.Protocol): """Main protocol to fit RatSSCx neuron ephys parameters. Pseudo code: - Find resting membrane potential - Find input resistance - Run other protocols: - Find holding current - Find rheobase - Run IDRest - Possibly run other protocols (based on constructor arguments) - Return all the responses Attributes: name (str): name of the protocol rmp_protocol (StepProtocol): resting membrane potential protocol rmp_efeature (bluepyopt.ephys.efeatures.eFELFeature): voltage base efeature rinhold_protocol (RatSSCxRinHoldcurrentProtocol): protocol to get the holding current rin_efeature (bluepyopt.ephys.efeatures.eFELFeature): ohmic input resistance vb ssse thdetect_protocol (RatSSCxThresholdDetectionProtocol): protocol to detect threshold current other_protocols (bluepyopt.ephys.protocols.Protocol): other protocols to run pre_protocols (bluepyopt.ephys.protocols.Protocol): protocols to run before the 'other protocols' """ def __init__( self, name, rmp_protocol=None, rmp_efeature=None, rinhold_protocol=None, rin_efeature=None, thdetect_protocol=None, other_protocols=None, pre_protocols=None, ): """Constructor. Args: name (str): name of the protocol rmp_protocol (StepProtocol): resting membrane potential protocol rmp_efeature (bluepyopt.ephys.efeatures.eFELFeature): voltage base efeature rinhold_protocol (RatSSCxRinHoldcurrentProtocol): protocol to get the holding current rin_efeature (bluepyopt.ephys.efeatures.eFELFeature): ohmic input resistance vb ssse thdetect_protocol (RatSSCxThresholdDetectionProtocol): protocol to detect threshold current other_protocols (bluepyopt.ephys.protocols.Protocol): other protocols to run pre_protocols (bluepyopt.ephys.protocols.Protocol): protocols to run before the 'other protocols' """ # pylint: disable=too-many-arguments super().__init__(name=name) self.rmp_protocol = rmp_protocol self.rmp_efeature = rmp_efeature self.rinhold_protocol = rinhold_protocol self.rin_efeature = rin_efeature self.thdetect_protocol = thdetect_protocol self.other_protocols = other_protocols self.pre_protocols = pre_protocols
[docs] def subprotocols(self): """Return all the subprotocols contained in this protocol, is recursive. Returns: dict containing all the subprotocols """ subprotocols = collections.OrderedDict({self.name: self}) subprotocols.update(self.rmp_protocol.subprotocols()) subprotocols.update(self.rinhold_protocol.subprotocols()) subprotocols.update(self.thdetect_protocol.subprotocols()) for other_protocol in self.other_protocols: subprotocols.update(other_protocol.subprotocols()) for pre_protocol in self.pre_protocols: subprotocols.update(pre_protocol.subprotocols()) return subprotocols
@property def rin_efeature(self): """Get rin_efeature. Returns: rin_efeature (bluepyopt.ephys.efeatures.eFELFeature): ohmic input resistance vb ssse """ return self.rinhold_protocol.rin_efeature @rin_efeature.setter def rin_efeature(self, value): """Set rin_efeature.""" self.rinhold_protocol.rin_efeature = value
[docs] def run_pre_protocols(self, responses, cell_model, sim): """Run the pre-protocols. Args: responses (dict): responses to be updated cell_model (bluepyopt.ephys.models.CellModel): the cell on which to apply the pre protocols sim (bluepyopt.ephys.NrnSimulator): neuron simulator """ for pre_protocol in self.pre_protocols: response = pre_protocol.run(cell_model, {}, sim=sim) responses.update(response)
[docs] def run_other_protocols(self, responses, cell_model, sim): """Run other protocols. Args: responses (dict): responses to be updated cell_model (bluepyopt.ephys.models.CellModel): the cell on which to apply the other protocols sim (bluepyopt.ephys.NrnSimulator): neuron simulator """ for other_protocol in self.other_protocols: response = other_protocol.run(cell_model, {}, sim=sim) responses.update(response)
[docs] def run(self, cell_model, param_values, sim=None, isolate=None): """Run protocol. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator isolate (bool): whether to isolate the run in a process with a timeout to avoid bad cells running for too long Returns: dict containing the responses for all the protocols """ # pylint: disable=unused-argument responses = collections.OrderedDict() cell_model.freeze(param_values) # Find resting membrane potential rmp_response = self.rmp_protocol.run(cell_model, {}, sim=sim) responses.update(rmp_response) rmp = self.rmp_efeature.calculate_feature(rmp_response) # Find Rin and holding current rinhold_response = self.rinhold_protocol.run(cell_model, {}, sim=sim, rmp=rmp) holding_current = cell_model.holding_current if rinhold_response is not None: rin = self.rin_efeature.calculate_feature(rinhold_response) responses.update(rinhold_response) responses.update( self.thdetect_protocol.run( cell_model, sim=sim, holdi=holding_current, rin=rin, ) ) if cell_model.threshold_current is not None: self.run_pre_protocols(responses, cell_model, sim) self.run_other_protocols(responses, cell_model, sim) cell_model.unfreeze(param_values.keys()) return responses
[docs] def generate_current(self, threshold_current=None, holding_current=None, dt=0.1): """Generate current for all protocols except rin and threshold detection. Args: threshold_current (float): the threshold current (nA) holding_current (float): the holding current (nA) dt (float): timestep of the generated currents (ms) Returns: dict containing the generated currents """ currents = {} # rmp is step protocol currents.update( self.rmp_protocol.generate_current( threshold_current=threshold_current, holding_current=holding_current, dt=dt, ) ) for pre_protocol in self.pre_protocols: currents.update( pre_protocol.generate_current( threshold_current=threshold_current, holding_current=holding_current, dt=dt, ) ) for other_protocol in self.other_protocols: currents.update( other_protocol.generate_current( threshold_current=threshold_current, holding_current=holding_current, dt=dt, ) ) return currents
[docs] class RatSSCxRinHoldcurrentProtocol(ephys.protocols.Protocol): """IDRest protocol to fit RatSSCx neuron ephys parameters. Attributes: name (str): name of the protocol rin_protocol_template (StepProtocol): template for Rin protocol with amplitude for the holding stimulus to be filled voltagebase_efeature (bluepyopt.ephys.efeatures.eFELFeature): voltage base efeature holdi_estimate_multiplier (float): when searching for holding current amplitude, multiply holding current estimate by this value to get lower bound holdi_precision (float): precision with which to reach holding voltage when searching for holding current holdi_max_depth (int): maximum number of times to compute the holding voltage when searching for holding current if holdi_precision is not reached prefix (str): prefix used in naming responses, features, recordings, etc. rin_efeature (bluepyopt.ephys.efeatures.eFELFeature): ohmic input resistance vb ssse rin_protocol (StepProtocol): Rin protocol with holding current amplitude """ def __init__( self, name, rin_protocol_template=None, voltagebase_efeature=None, holdi_estimate_multiplier=2, holdi_precision=0.1, holdi_max_depth=5, prefix=None, ): """Constructor. Args: name (str): name of the protocol rin_protocol_template (StepProtocol): template for Rin protocol with amplitude for the holding stimulus to be filled voltagebase_efeature (bluepyopt.ephys.efeatures.eFELFeature): voltage base efeature holdi_estimate_multiplier (float): when searching for holding current amplitude, multiply holding current estimate by this value to get lower bound holdi_precision (float): precision with which to reach holding voltage when searching for holding current holdi_max_depth (int): maximum number of times to compute the holding voltage when searching for holding current if holdi_precision is not reached prefix (str): prefix used in naming responses, features, recordings, etc. """ # pylint: disable=too-many-arguments super().__init__(name=name) self.rin_protocol_template = rin_protocol_template self.voltagebase_efeature = voltagebase_efeature self.holdi_estimate_multiplier = holdi_estimate_multiplier self.holdi_precision = holdi_precision self.holdi_max_depth = holdi_max_depth if prefix is None: self.prefix = "" else: self.prefix = prefix + "." # this will be set before the run through main protocol setter self.rin_efeature = None # This will be set after the run() self.rin_protocol = None
[docs] def run(self, cell_model, param_values, sim, rmp=None): """Run protocol. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator rmp (float): resting membrane potential (mV) Returns: dict containing the responses for the Rin protocol using holding current """ # Calculate Rin without holding current rin_noholding_protocol = self.create_rin_protocol(holdi=0) rin_noholding_response = rin_noholding_protocol.run( cell_model, param_values, sim=sim ) rin_noholding = self.rin_efeature.calculate_feature(rin_noholding_response) # Search holding current holdi = self.search_holdi( cell_model, param_values, sim, self.voltagebase_efeature.exp_mean, rin_noholding, rmp, ) if holdi is None: return None # Set up Rin protocol self.rin_protocol = self.create_rin_protocol(holdi=holdi) # Return response responses = self.rin_protocol.run(cell_model, param_values, sim) responses[self.prefix + "bpo_holding_current"] = holdi cell_model.holding_current = holdi return responses
[docs] def subprotocols(self): """Return subprotocols. Returns: dict containing the Rin protocol and the Rin protocol template """ subprotocols = collections.OrderedDict({self.name: self}) subprotocols.update( {self.rin_protocol_template.name: self.rin_protocol_template} ) return subprotocols
[docs] def create_rin_protocol(self, holdi=None): """Create threshold protocol. Args: holdi (float): amplitude of holding current (nA) Returns: StepProtocol: the Rin protocol with holding current """ rin_protocol = copy.deepcopy(self.rin_protocol_template) rin_protocol.name = "Rin" for recording in rin_protocol.recordings: recording.name = recording.name.replace( self.rin_protocol_template.name, rin_protocol.name ) rin_protocol.holding_stimulus.step_amplitude = holdi return rin_protocol
[docs] def search_holdi( self, cell_model, param_values, sim, holding_voltage, rin_noholding, rmp ): """Find the holding current to hold cell at holding_voltage. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator holding_voltage (float): experimental mean of the voltage base efeature (mV) rin_noholding (float): Rin efeature (ohmic input resistance vb ssse) value when no holding current is applied (mV/nA) rmp (float): resting membrane potential (mV) Returns: float: the holding current (nA) that reproduces the experimental holding voltage """ holdi_estimate = float(holding_voltage - rmp) / rin_noholding upper_bound = 0.0 lower_bound = self.holdi_estimate_multiplier * holdi_estimate middle_bound = upper_bound - abs(upper_bound - lower_bound) / 2 middle_voltage = self.voltage_base( middle_bound, cell_model, param_values, sim=sim ) n = 0 while ( abs(middle_voltage - holding_voltage) > self.holdi_precision and n < self.holdi_max_depth ): if middle_voltage > holding_voltage: upper_bound = middle_bound else: lower_bound = middle_bound middle_bound = upper_bound - abs(upper_bound - lower_bound) / 2 middle_voltage = self.voltage_base( middle_bound, cell_model, param_values, sim=sim ) n += 1 return middle_bound
[docs] def voltage_base(self, current, cell_model, param_values, sim=None): """Calculate voltage base for certain stimulus current. Args: current (float): amplitude of the holding current to inject (nA) cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator Returns: float: voltage base (mV) of response """ protocol = self.create_rin_protocol(holdi=current) response = protocol.run(cell_model, param_values, sim=sim) feature = ephys.efeatures.eFELFeature( name="Holding.voltage_base", efel_feature_name="voltage_base", recording_names={"": self.prefix + "Rin.soma.v"}, stim_start=protocol.stim_start, stim_end=protocol.stim_end, exp_mean=0, exp_std=0.1, ) voltage_base = feature.calculate_feature(response) return voltage_base
[docs] class RatSSCxThresholdDetectionProtocol(ephys.protocols.Protocol): """IDRest protocol to fit RatSSCx neuron ephys parameters. Attributes: name (str): name of the protocol step_protocol_template (StepProtocol): template for threshold protocol with amplitude for the holding and steps stimuli to be filled max_threshold_voltage (float): Use this (mV) to get max threshold current upper bound when searching for threshold current short_perc (float): multiply step duration by this value when detecting spikes with short stimulus. Should be < 1. Not actually a percentage (0.1 -> 10%) short_steps (int): the number of short steps to perform to determine the upper bound of the threshold search with long steps holding_voltage (float): experimental mean of the voltage base efeature (mV) of the Rin protocol prefix (str): prefix used in naming responses, features, recordings, etc. """ def __init__( self, name, step_protocol_template=None, max_threshold_voltage=-40, holding_voltage=None, prefix=None, ): """Constructor. Args: name (str): name of the protocol step_protocol_template (StepProtocol): template for threshold protocol with amplitude for the holding and steps stimuli to be filled max_threshold_voltage (float): Use this (mV) to get max threshold current upper bound when searching for threshold current holding_voltage (float): experimental mean of the voltage base efeature (mV) of the Rin protocol prefix (str): prefix used in naming responses, features, recordings, etc. """ super().__init__(name=name) self.step_protocol_template = step_protocol_template self.max_threshold_voltage = max_threshold_voltage self.short_perc = 0.1 self.short_steps = 20 self.holding_voltage = holding_voltage if prefix is None: self.prefix = "" else: self.prefix = prefix + "."
[docs] def subprotocols(self): """Return subprotocols. Returns: dict: the threshold detection protocol and the threshold detection protocol template """ subprotocols = collections.OrderedDict({self.name: self}) subprotocols.update(self.step_protocol_template.subprotocols()) return subprotocols
[docs] def run(self, cell_model, sim, holdi, rin): """Run protocol. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model sim (bluepyopt.ephys.NrnSimulator): neuron simulator holdi (float): holding current amplitude (nA) rin (float): Rin efeature (ohmic input resistance vb ssse) value (mV/nA) Returns: dict containing threshold current """ responses = collections.OrderedDict() # Calculate max threshold current max_threshold_current = self.max_threshold_current(rin=rin) # Calculate spike threshold threshold_current = self.search_spike_threshold( cell_model, {}, holdi=holdi, lower_bound=-holdi, upper_bound=max_threshold_current, sim=sim, ) cell_model.threshold_current = threshold_current responses[self.prefix + "bpo_threshold_current"] = threshold_current return responses
[docs] def max_threshold_current(self, rin=None): """Find the current necessary to get to max_threshold_voltage. Args: rin (float): Rin efeature (ohmic input resistance vb ssse) value (mV/nA) Returns: float: maximum threshold current (nA) """ max_threshold_current = ( float(self.max_threshold_voltage - self.holding_voltage) / rin ) logger.info("Max threshold current: %.6g", max_threshold_current) return max_threshold_current
[docs] def create_step_protocol(self, holdi=0.0, step_current=0.0): """Create threshold protocol. Args: holdi (float): holding current amplitude (nA) step_current (float): step current amplitude (nA) Returns: StepProtocol: the threshold protocol """ threshold_protocol = copy.deepcopy(self.step_protocol_template) threshold_protocol.name = "Threshold" for recording in threshold_protocol.recordings: recording.name = recording.name.replace( self.step_protocol_template.name, threshold_protocol.name ) if threshold_protocol.holding_stimulus is not None: threshold_protocol.holding_stimulus.step_amplitude = holdi for step_stim in threshold_protocol.step_stimuli: step_stim.step_amplitude = step_current return threshold_protocol
[docs] def create_short_threshold_protocol(self, holdi=None, step_current=None): """Create short threshold protocol. Args: holdi (float): holding current amplitude (nA) step_current (float): step current amplitude (nA) Returns: StepProtocol: the threshold protocol with shorter step duration and total duration """ short_protocol = self.create_step_protocol( holdi=holdi, step_current=step_current ) origin_step_duration = short_protocol.stim_duration origin_step_delay = short_protocol.stim_start short_step_duration = origin_step_duration * self.short_perc short_total_duration = origin_step_delay + short_step_duration short_protocol.step_stimuli[0].step_duration = short_step_duration short_protocol.step_stimuli[0].total_duration = short_total_duration if short_protocol.holding_stimulus is not None: short_protocol.holding_stimulus.step_duration = short_total_duration short_protocol.holding_stimulus.total_duration = short_total_duration return short_protocol
[docs] def detect_spike( self, cell_model, param_values, sim=None, step_current=None, holdi=None, short=False, ): """Detect if spike is present at current level. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator step_current (float): step current amplitude (nA) holdi (float): holding current amplitude (nA) short (bool): whether the protocol step duration should be shorten Returns: bool: True if at least one spike was detected, False otherwise """ # Only run short pulse if percentage set smaller than 100% if short and self.short_perc < 1.0: protocol = self.create_short_threshold_protocol( holdi=holdi, step_current=step_current ) else: protocol = self.create_step_protocol(holdi=holdi, step_current=step_current) response = protocol.run(cell_model, param_values, sim=sim) feature = ephys.efeatures.eFELFeature( name="ThresholdDetection.spike_count", efel_feature_name="spike_count", recording_names={"": self.prefix + "ThresholdDetection.soma.v"}, stim_start=protocol.stim_start, stim_end=protocol.stim_end, exp_mean=1, exp_std=0.1, ) spike_count = feature.calculate_feature(response) return spike_count >= 1
[docs] def search_spike_threshold( self, cell_model, param_values, sim=None, holdi=None, lower_bound=None, upper_bound=None, precision=0.01, max_depth=5, ): """Find the current step spiking threshold. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator holdi (float): holding current amplitude (nA) lower_bound (float): lower bound in which to search for threshold current (nA) upper_bound (float): upper bound in which to search for threshold current (nA) precision (float): precision with which to compute threshold current (nA) max_depth (int): maximum number of times to run the cell when searching for threshold current when precision is not reached Returns: float: threshold current amplitude (nA) """ # pylint: disable=undefined-loop-variable, too-many-arguments step_currents = np.linspace(lower_bound, upper_bound, num=self.short_steps) if len(step_currents) == 0: return None for step_current in step_currents: spike_detected = self.detect_spike( cell_model, param_values, sim=sim, holdi=holdi, step_current=step_current, short=True, ) if spike_detected: upper_bound = step_current break # if upper bound didn't have spike with short stimulus # check if there is one with longer stimulus if not spike_detected: spike_detected = self.detect_spike( cell_model, param_values, sim=sim, holdi=holdi, step_current=step_current, short=False, ) if spike_detected: upper_bound = step_current else: return None depth = 0 while depth < max_depth and abs(upper_bound - lower_bound) >= precision: middle_bound = upper_bound - abs(upper_bound - lower_bound) / 2 spike_detected = self.detect_spike( cell_model, param_values, sim=sim, holdi=holdi, step_current=middle_bound, short=False, ) if spike_detected: upper_bound = middle_bound else: lower_bound = middle_bound depth += 1 threshold_current = upper_bound return threshold_current
[docs] class StepProtocol(ephys.protocols.SweepProtocol, CurrentOutputKeyMixin): """Protocol consisting of step and holding current. Attributes: name (str): name of this object stimuli (list of Stimuli): List of all Stimulus objects used in protocol recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step step_stimuli (list of Stimuli): List of step Stimulus objects used in protocol holding_stimulus (Stimulus): Holding Stimulus stochkv_det (bool): set if stochastic or deterministic """ def __init__( self, name=None, step_stimuli=None, holding_stimulus=None, recordings=None, cvode_active=None, stochkv_det=None, ): """Constructor. Args: name (str): name of this object step_stimuli (list of Stimuli): List of Stimulus objects used in protocol holding_stimulus (Stimulus): Holding Stimulus recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step stochkv_det (bool): set if stochastic or deterministic """ super().__init__( name, stimuli=( step_stimuli + [holding_stimulus] if holding_stimulus is not None else step_stimuli ), recordings=recordings, cvode_active=cvode_active, ) self.step_stimuli = step_stimuli # this can be used through inheritance self.holding_stimulus = holding_stimulus self.stochkv_det = stochkv_det
[docs] def instantiate(self, sim=None, cell_model=None): """Instantiate. Args: sim (bluepyopt.ephys.NrnSimulator): neuron simulator cell_model (bluepyopt.ephys.CellModel): cell model """ for stimulus in self.stimuli: stimulus.instantiate(sim=sim, icell=cell_model.icell) for recording in self.recordings: try: recording.instantiate(sim=sim, icell=cell_model.icell) except ephys.locations.EPhysLocInstantiateException as e: logger.debug( "SweepProtocol: Instantiating recording generated location " "exception: %s, will return empty response for this recording", e, )
[docs] def run(self, cell_model, param_values, sim=None, isolate=None, timeout=None): """Run protocol. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator isolate (bool): whether to isolate the run in a process with a timeout to avoid bad cells running for too long timeout (float): maximum real time (s) the cell is allowed to run when isolated Returns: dict containing the responses for the step protocol """ responses = {} cvode_active_copy = self.cvode_active if self.stochkv_det is not None and not self.stochkv_det: for mechanism in cell_model.mechanisms: if "Stoch" in mechanism.prefix: mechanism.deterministic = False self.cvode_active = False responses.update( super().run( cell_model, param_values, sim=sim, isolate=isolate, timeout=timeout ) ) if self.stochkv_det is not None and not self.stochkv_det: for mechanism in cell_model.mechanisms: if "Stoch" in mechanism.prefix: mechanism.deterministic = True self.cvode_active = cvode_active_copy return responses
[docs] def generate_current(self, threshold_current=None, holding_current=None, dt=0.1): """Return current time series. Args: threshold_current (float): the threshold current (nA) holding_current (float): the holding current (nA) dt (float): timestep of the generated currents (ms) Returns: dict containing the generated current """ # pylint: disable=unused-argument holding_current = 0.0 if self.holding_stimulus is not None: holding_current = self.holding_stimulus.step_amplitude total_duration = self.step_stimuli[-1].total_duration t = np.arange(0.0, total_duration, dt) current = np.full(t.shape, holding_current, dtype="float64") for stimuli in self.step_stimuli: ton = stimuli.step_delay ton_idx = int(ton / dt) toff = stimuli.step_delay + stimuli.step_duration toff_idx = int(toff / dt) current[ton_idx:toff_idx] += stimuli.step_amplitude return {self.curr_output_key(): {"time": t, "current": current}}
@property def stim_start(self): """Time stimulus starts. Returns: the time at which the stimulus starts (ms) """ return self.step_stimuli[0].step_delay @property def stim_duration(self): """Time stimulus duration. Returns: the duration of the stimulus (ms) """ return ( self.step_stimuli[-1].step_delay + self.step_stimuli[-1].step_duration - self.step_stimuli[0].step_delay ) @property def stim_end(self): """Time stimulus ends. Returns: the time at which the stimulus ends (ms) """ return self.step_stimuli[-1].step_delay + self.step_stimuli[-1].step_duration @property def stim_last_start(self): """Time stimulus last start. Returns: the time at which the last step stimulus in the list starts (ms) """ return self.step_stimuli[-1].step_delay @property def step_amplitude(self): """Stimuli mean amplitude. Returns: the mean amplitude of the step stimuli (nA) """ amplitudes = [step_stim.step_amplitude for step_stim in self.step_stimuli] if None in amplitudes: return None else: return np.mean(amplitudes)
[docs] class StepThresholdProtocol(StepProtocol): """Step protocol based on threshold. Attributes: name (str): name of this object stimuli (list of Stimuli): List of all Stimulus objects used in protocol recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step step_stimuli (list of Stimuli): List of step Stimulus objects used in protocol holding_stimulus (Stimulus): Holding Stimulus stochkv_det (bool): set if stochastic or deterministic thresh_perc (float): percentage of the threshold current at which to set the step amplitudes """ def __init__( self, name, thresh_perc=None, step_stimuli=None, holding_stimulus=None, recordings=None, cvode_active=None, stochkv_det=None, ): """Constructor. Args: name (str): name of this object thresh_perc (float): percentage of the threshold current at which to set the step amplitudes step_stimuli (list of Stimuli): List of Stimulus objects used in protocol holding_stimulus (Stimulus): Holding Stimulus recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step stochkv_det (bool): set if stochastic or deterministic """ super().__init__( name, step_stimuli=step_stimuli, holding_stimulus=holding_stimulus, recordings=recordings, cvode_active=cvode_active, stochkv_det=stochkv_det, ) self.thresh_perc = thresh_perc
[docs] def run(self, cell_model, param_values, sim=None, isolate=None, timeout=None): """Run protocol. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator isolate (bool): whether to isolate the run in a process with a timeout to avoid bad cells running for too long timeout (float): maximum real time (s) the cell is allowed to run when isolated Raises: AttributeError: if the threshold_current is not set to the cell model Returns: dict containing the responses for the step protocol """ # pylint: disable=unused-argument responses = {} if not hasattr(cell_model, "threshold_current"): raise AttributeError( "StepThresholdProtocol: running on cell_model " f"that doesnt have threshold current value set: {cell_model}" ) for step_stim in self.step_stimuli: step_stim.step_amplitude = cell_model.threshold_current * ( float(self.thresh_perc) / 100.0 ) self.holding_stimulus.step_amplitude = cell_model.holding_current responses.update( super().run( cell_model, param_values, sim=sim, isolate=isolate, timeout=timeout ) ) return responses
[docs] def generate_current(self, threshold_current, holding_current, dt=0.1): """Return current time series. Args: threshold_current (float): the threshold current (nA) holding_current (float): the holding current (nA) dt (float): timestep of the generated currents (ms) Returns: dict containing the generated current """ # pylint: disable=signature-differs total_duration = self.step_stimuli[-1].total_duration t = np.arange(0.0, total_duration, dt) current = np.full(t.shape, holding_current, dtype="float64") for stimuli in self.step_stimuli: ton = stimuli.step_delay ton_idx = int(ton / dt) toff = stimuli.step_delay + stimuli.step_duration toff_idx = int(toff / dt) current[ton_idx:toff_idx] += threshold_current * ( float(self.thresh_perc) / 100.0 ) return {self.curr_output_key(): {"time": t, "current": current}}
[docs] class RampProtocol(ephys.protocols.SweepProtocol, CurrentOutputKeyMixin): """Protocol consisting of ramp and holding current. Attributes: name (str): name of this object stimuli (list of Stimuli): List of all Stimulus objects used in protocol recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step ramp_stimulus (Stimulus): ramp Stimulus holding_stimulus (Stimulus): Holding Stimulus """ def __init__( self, name=None, ramp_stimulus=None, holding_stimulus=None, recordings=None, cvode_active=None, ): """Constructor. Args: name (str): name of this object ramp_stimulus (Stimulus): Stimulus objects used in protocol holding_stimulus (Stimulus): Holding Stimulus recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step """ super().__init__( name, stimuli=( [ramp_stimulus, holding_stimulus] if holding_stimulus is not None else [ramp_stimulus] ), recordings=recordings, cvode_active=cvode_active, ) self.ramp_stimulus = ramp_stimulus self.holding_stimulus = holding_stimulus
[docs] def generate_current(self, threshold_current=None, holding_current=None, dt=0.1): """Return current time series. Args: threshold_current (float): the threshold current (nA) holding_current (float): the holding current (nA) dt (float): timestep of the generated currents (ms) Returns: dict containing the generated current """ # pylint: disable=unused-argument holding_current = 0.0 if self.holding_stimulus is not None: holding_current = self.holding_stimulus.step_amplitude t = np.arange(0.0, self.ramp_stimulus.total_duration, dt) current = np.full(t.shape, holding_current, dtype="float64") ton_idx = int(self.step_delay / dt) toff_idx = int((self.step_delay + self.step_duration) / dt) current[ton_idx:toff_idx] += np.linspace( self.ramp_stimulus.ramp_amplitude_start, self.ramp_stimulus.ramp_amplitude_end, toff_idx - ton_idx + 1, )[:-1] return {self.curr_output_key(): {"time": t, "current": current}}
@property def step_delay(self): """Time stimulus delay. Returns: time at which the ramp starts (ms) """ return self.ramp_stimulus.ramp_delay @property def step_duration(self): """Time stimulus duration. Returns: duration of the ramp stimulus (ms) """ return self.ramp_stimulus.ramp_duration
[docs] class RampThresholdProtocol(RampProtocol): """Step protocol based on threshold. Attributes: name (str): name of this object stimuli (list of Stimuli): List of all Stimulus objects used in protocol recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step ramp_stimulus (Stimulus): ramp Stimulus holding_stimulus (Stimulus): Holding Stimulus thresh_perc_start (float): percentage of the threshold current at which to set the start of the ramp amplitude thresh_perc_end (float): percentage of the threshold current at which to set the end of the ramp amplitude """ def __init__( self, name, thresh_perc_start=None, thresh_perc_end=None, ramp_stimulus=None, holding_stimulus=None, recordings=None, cvode_active=None, ): """Constructor. Args: name (str): name of this object thresh_perc_start (float): percentage of the threshold current at which to set the start of the ramp amplitude thresh_perc_end (float): percentage of the threshold current at which to set the end of the ramp amplitude ramp_stimulus (Stimulus): Stimulus objects used in protocol holding_stimulus (Stimulus): Holding Stimulus recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step """ super().__init__( name, ramp_stimulus=ramp_stimulus, holding_stimulus=holding_stimulus, recordings=recordings, cvode_active=cvode_active, ) self.thresh_perc_start = thresh_perc_start self.thresh_perc_end = thresh_perc_end
[docs] def run(self, cell_model, param_values, sim=None, isolate=None, timeout=None): """Run protocol. Args: cell_model (bluepyopt.ephys.models.CellModel): the cell model param_values (dict): optimized parameters sim (bluepyopt.ephys.NrnSimulator): neuron simulator isolate (bool): whether to isolate the run in a process with a timeout to avoid bad cells running for too long timeout (float): maximum real time (s) the cell is allowed to run when isolated Raises: AttributeError: if the threshold_current is not set to the cell model Returns: dict containing the responses for the ramp protocol """ # pylint: disable=unused-argument responses = {} if not hasattr(cell_model, "threshold_current"): raise AttributeError( "RampThresholdProtocol: running on cell_model " f"that doesnt have threshold current value set: {cell_model}" ) self.ramp_stimulus.ramp_amplitude_start = cell_model.threshold_current * ( float(self.thresh_perc_start) / 100 ) self.ramp_stimulus.ramp_amplitude_end = cell_model.threshold_current * ( float(self.thresh_perc_end) / 100 ) self.holding_stimulus.step_amplitude = cell_model.holding_current responses.update( super().run( cell_model, param_values, sim=sim, isolate=isolate, timeout=timeout ) ) return responses
[docs] def generate_current(self, threshold_current, holding_current, dt=0.1): """Return current time series. Args: threshold_current (float): the threshold current (nA) holding_current (float): the holding current (nA) dt (float): timestep of the generated currents (ms) Returns: dict containing the generated current """ # pylint: disable=signature-differs t = np.arange(0.0, self.ramp_stimulus.total_duration, dt) current = np.full(t.shape, holding_current, dtype="float64") ton_idx = int(self.step_delay / dt) toff_idx = int((self.step_delay + self.step_duration) / dt) amp_start = threshold_current * (float(self.thresh_perc_start) / 100) amp_end = threshold_current * (float(self.thresh_perc_end) / 100) current[ton_idx:toff_idx] += np.linspace( amp_start, amp_end, toff_idx - ton_idx + 1, )[:-1] return {self.curr_output_key(): {"time": t, "current": current}}
[docs] class SweepProtocolCustom(ephys.protocols.SweepProtocol): """SweepProtocol with generate_current method. Attributes: name (str): name of this object stimuli (list of Stimuli): List of all Stimulus objects used in protocol recordings (list of Recordings): Recording objects used in the protocol cvode_active (bool): whether to use variable time step Args of the parent constructor: - name (str): name of this object - stimuli (list of Stimuli): Stimulus objects used in the protocol - recordings (list of Recordings): Recording objects used in the protocol - cvode_active (bool): whether to use variable time step """
[docs] @staticmethod def generate_current(threshold_current, holding_current, dt=0.1): """Return an empty dictionary. Args: threshold_current (float): the threshold current (nA) holding_current (float): the holding current (nA) dt (float): timestep of the generated currents (ms) Returns: empty dict """ # pylint: disable=unused-argument return {}