bluecellulab 2.6.42__py3-none-any.whl → 2.6.43__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bluecellulab might be problematic. Click here for more details.
- bluecellulab/analysis/inject_sequence.py +4 -4
- bluecellulab/cell/injector.py +1 -1
- bluecellulab/stimulus/factory.py +289 -361
- bluecellulab/stimulus/stimulus.py +782 -0
- {bluecellulab-2.6.42.dist-info → bluecellulab-2.6.43.dist-info}/METADATA +1 -1
- {bluecellulab-2.6.42.dist-info → bluecellulab-2.6.43.dist-info}/RECORD +10 -9
- {bluecellulab-2.6.42.dist-info → bluecellulab-2.6.43.dist-info}/WHEEL +1 -1
- {bluecellulab-2.6.42.dist-info → bluecellulab-2.6.43.dist-info}/AUTHORS.txt +0 -0
- {bluecellulab-2.6.42.dist-info → bluecellulab-2.6.43.dist-info}/LICENSE +0 -0
- {bluecellulab-2.6.42.dist-info → bluecellulab-2.6.43.dist-info}/top_level.txt +0 -0
bluecellulab/stimulus/factory.py
CHANGED
|
@@ -1,361 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from typing import Optional
|
|
4
|
-
import logging
|
|
5
|
-
import matplotlib.pyplot as plt
|
|
6
|
-
import numpy as np
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger(__name__)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Stimulus(ABC):
|
|
12
|
-
def __init__(self, dt: float) -> None:
|
|
13
|
-
self.dt = dt
|
|
14
|
-
|
|
15
|
-
@property
|
|
16
|
-
@abstractmethod
|
|
17
|
-
def time(self) -> np.ndarray:
|
|
18
|
-
"""Time values of the stimulus."""
|
|
19
|
-
...
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
@abstractmethod
|
|
23
|
-
def current(self) -> np.ndarray:
|
|
24
|
-
"""Current values of the stimulus."""
|
|
25
|
-
...
|
|
26
|
-
|
|
27
|
-
def __len__(self) -> int:
|
|
28
|
-
return len(self.time)
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def stimulus_time(self) -> float:
|
|
32
|
-
return len(self) * self.dt
|
|
33
|
-
|
|
34
|
-
def __repr__(self) -> str:
|
|
35
|
-
return f"{self.__class__.__name__}(dt={self.dt})"
|
|
36
|
-
|
|
37
|
-
def plot(self, ax=None, **kwargs):
|
|
38
|
-
if ax is None:
|
|
39
|
-
ax = plt.gca()
|
|
40
|
-
ax.plot(self.time, self.current, **kwargs)
|
|
41
|
-
ax.set_xlabel("Time (ms)")
|
|
42
|
-
ax.set_ylabel("Current (nA)")
|
|
43
|
-
ax.set_title(self.__class__.__name__)
|
|
44
|
-
return ax
|
|
45
|
-
|
|
46
|
-
def __add__(self, other: Stimulus) -> CombinedStimulus:
|
|
47
|
-
"""Override + operator to concatenate Stimulus objects."""
|
|
48
|
-
if self.dt != other.dt:
|
|
49
|
-
raise ValueError(
|
|
50
|
-
"Stimulus objects must have the same dt to be concatenated"
|
|
51
|
-
)
|
|
52
|
-
if len(self.time) == 0:
|
|
53
|
-
return CombinedStimulus(other.dt, other.time, other.current)
|
|
54
|
-
elif len(other.time) == 0:
|
|
55
|
-
return CombinedStimulus(self.dt, self.time, self.current)
|
|
56
|
-
else:
|
|
57
|
-
# shift other time
|
|
58
|
-
other_time = other.time + self.time[-1] + self.dt
|
|
59
|
-
combined_time = np.concatenate([self.time, other_time])
|
|
60
|
-
# Concatenate the current arrays
|
|
61
|
-
combined_current = np.concatenate([self.current, other.current])
|
|
62
|
-
return CombinedStimulus(self.dt, combined_time, combined_current)
|
|
63
|
-
|
|
64
|
-
def __eq__(self, other: object) -> bool:
|
|
65
|
-
if not isinstance(other, Stimulus):
|
|
66
|
-
return NotImplemented
|
|
67
|
-
else:
|
|
68
|
-
return (
|
|
69
|
-
np.allclose(self.time, other.time)
|
|
70
|
-
and np.allclose(self.current, other.current)
|
|
71
|
-
and self.dt == other.dt
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class CombinedStimulus(Stimulus):
|
|
76
|
-
"""Represents the Stimulus created by combining multiple stimuli."""
|
|
77
|
-
|
|
78
|
-
def __init__(self, dt: float, time: np.ndarray, current: np.ndarray) -> None:
|
|
79
|
-
super().__init__(dt)
|
|
80
|
-
self._time = time
|
|
81
|
-
self._current = current
|
|
82
|
-
|
|
83
|
-
@property
|
|
84
|
-
def time(self) -> np.ndarray:
|
|
85
|
-
return self._time
|
|
86
|
-
|
|
87
|
-
@property
|
|
88
|
-
def current(self) -> np.ndarray:
|
|
89
|
-
return self._current
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
class Empty(Stimulus):
|
|
93
|
-
"""Represents empty stimulus (all zeros) that has no impact on the cell.
|
|
94
|
-
|
|
95
|
-
This is required by some Stimuli that expect the cell to rest.
|
|
96
|
-
"""
|
|
97
|
-
|
|
98
|
-
def __init__(self, dt: float, duration: float) -> None:
|
|
99
|
-
super().__init__(dt)
|
|
100
|
-
self.duration = duration
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
def time(self) -> np.ndarray:
|
|
104
|
-
return np.arange(0.0, self.duration, self.dt)
|
|
105
|
-
|
|
106
|
-
@property
|
|
107
|
-
def current(self) -> np.ndarray:
|
|
108
|
-
return np.zeros_like(self.time)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
class Flat(Stimulus):
|
|
112
|
-
def __init__(self, dt: float, duration: float, amplitude: float) -> None:
|
|
113
|
-
super().__init__(dt)
|
|
114
|
-
self.duration = duration
|
|
115
|
-
self.amplitude = amplitude
|
|
116
|
-
|
|
117
|
-
@property
|
|
118
|
-
def time(self) -> np.ndarray:
|
|
119
|
-
return np.arange(0.0, self.duration, self.dt)
|
|
120
|
-
|
|
121
|
-
@property
|
|
122
|
-
def current(self) -> np.ndarray:
|
|
123
|
-
return np.full_like(self.time, self.amplitude)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class Slope(Stimulus):
|
|
127
|
-
def __init__(
|
|
128
|
-
self, dt: float, duration: float, amplitude_start: float, amplitude_end: float
|
|
129
|
-
) -> None:
|
|
130
|
-
super().__init__(dt)
|
|
131
|
-
self.duration = duration
|
|
132
|
-
self.amplitude_start = amplitude_start
|
|
133
|
-
self.amplitude_end = amplitude_end
|
|
134
|
-
|
|
135
|
-
@property
|
|
136
|
-
def time(self) -> np.ndarray:
|
|
137
|
-
return np.arange(0.0, self.duration, self.dt)
|
|
138
|
-
|
|
139
|
-
@property
|
|
140
|
-
def current(self) -> np.ndarray:
|
|
141
|
-
return np.linspace(self.amplitude_start, self.amplitude_end, len(self.time))
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class Zap(Stimulus):
|
|
145
|
-
def __init__(self, dt: float, duration: float, amplitude: float) -> None:
|
|
146
|
-
super().__init__(dt)
|
|
147
|
-
self.duration = duration
|
|
148
|
-
self.amplitude = amplitude
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def time(self) -> np.ndarray:
|
|
152
|
-
return np.arange(0.0, self.duration, self.dt)
|
|
153
|
-
|
|
154
|
-
@property
|
|
155
|
-
def current(self) -> np.ndarray:
|
|
156
|
-
return self.amplitude * np.sin(
|
|
157
|
-
2.0 * np.pi * (1.0 + (1.0 / (5.15 - (self.time - 0.1)))) * (self.time - 0.1)
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
class Step(Stimulus):
|
|
162
|
-
|
|
163
|
-
def __init__(self):
|
|
164
|
-
raise NotImplementedError(
|
|
165
|
-
"This class cannot be instantiated directly. "
|
|
166
|
-
"Please use the class methods 'amplitude_based' "
|
|
167
|
-
"or 'threshold_based' to create objects."
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
@classmethod
|
|
171
|
-
def amplitude_based(
|
|
172
|
-
cls,
|
|
173
|
-
dt: float,
|
|
174
|
-
pre_delay: float,
|
|
175
|
-
duration: float,
|
|
176
|
-
post_delay: float,
|
|
177
|
-
amplitude: float,
|
|
178
|
-
) -> CombinedStimulus:
|
|
179
|
-
"""Create a Step stimulus from given time events and amplitude.
|
|
180
|
-
|
|
181
|
-
Args:
|
|
182
|
-
dt: The time step of the stimulus.
|
|
183
|
-
pre_delay: The delay before the start of the step.
|
|
184
|
-
duration: The duration of the step.
|
|
185
|
-
post_delay: The time to wait after the end of the step.
|
|
186
|
-
amplitude: The amplitude of the step.
|
|
187
|
-
"""
|
|
188
|
-
return (
|
|
189
|
-
Empty(dt, duration=pre_delay)
|
|
190
|
-
+ Flat(dt, duration=duration, amplitude=amplitude)
|
|
191
|
-
+ Empty(dt, duration=post_delay)
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
@classmethod
|
|
195
|
-
def threshold_based(
|
|
196
|
-
cls,
|
|
197
|
-
dt: float,
|
|
198
|
-
pre_delay: float,
|
|
199
|
-
duration: float,
|
|
200
|
-
post_delay: float,
|
|
201
|
-
threshold_current: float,
|
|
202
|
-
threshold_percentage: float,
|
|
203
|
-
) -> CombinedStimulus:
|
|
204
|
-
"""Creates a Step stimulus with respect to the threshold current.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
|
|
208
|
-
dt: The time step of the stimulus.
|
|
209
|
-
pre_delay: The delay before the start of the step.
|
|
210
|
-
duration: The duration of the step.
|
|
211
|
-
post_delay: The time to wait after the end of the step.
|
|
212
|
-
threshold_current: The threshold current of the Cell.
|
|
213
|
-
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
214
|
-
"""
|
|
215
|
-
amplitude = threshold_current * threshold_percentage / 100
|
|
216
|
-
res = cls.amplitude_based(
|
|
217
|
-
dt,
|
|
218
|
-
pre_delay=pre_delay,
|
|
219
|
-
duration=duration,
|
|
220
|
-
post_delay=post_delay,
|
|
221
|
-
amplitude=amplitude,
|
|
222
|
-
)
|
|
223
|
-
return res
|
|
1
|
+
# Copyright 2023-2024 Blue Brain Project / EPFL
|
|
2
|
+
# Copyright 2025 Open Brain Institute
|
|
224
3
|
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
225
7
|
|
|
226
|
-
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
227
9
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
)
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
234
15
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
duration: float,
|
|
241
|
-
post_delay: float,
|
|
242
|
-
amplitude: float,
|
|
243
|
-
) -> CombinedStimulus:
|
|
244
|
-
"""Create a Ramp stimulus from given time events and amplitudes.
|
|
245
|
-
|
|
246
|
-
Args:
|
|
247
|
-
dt: The time step of the stimulus.
|
|
248
|
-
pre_delay: The delay before the start of the ramp.
|
|
249
|
-
duration: The duration of the ramp.
|
|
250
|
-
post_delay: The time to wait after the end of the ramp.
|
|
251
|
-
amplitude: The final amplitude of the ramp.
|
|
252
|
-
"""
|
|
253
|
-
return (
|
|
254
|
-
Empty(dt, duration=pre_delay)
|
|
255
|
-
+ Slope(
|
|
256
|
-
dt,
|
|
257
|
-
duration=duration,
|
|
258
|
-
amplitude_start=0.0,
|
|
259
|
-
amplitude_end=amplitude,
|
|
260
|
-
)
|
|
261
|
-
+ Empty(dt, duration=post_delay)
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
@classmethod
|
|
265
|
-
def threshold_based(
|
|
266
|
-
cls,
|
|
267
|
-
dt: float,
|
|
268
|
-
pre_delay: float,
|
|
269
|
-
duration: float,
|
|
270
|
-
post_delay: float,
|
|
271
|
-
threshold_current: float,
|
|
272
|
-
threshold_percentage: float,
|
|
273
|
-
) -> CombinedStimulus:
|
|
274
|
-
"""Creates a Ramp stimulus with respect to the threshold current.
|
|
275
|
-
|
|
276
|
-
Args:
|
|
277
|
-
|
|
278
|
-
dt: The time step of the stimulus.
|
|
279
|
-
pre_delay: The delay before the start of the ramp.
|
|
280
|
-
duration: The duration of the ramp.
|
|
281
|
-
post_delay: The time to wait after the end of the ramp.
|
|
282
|
-
threshold_current: The threshold current of the Cell.
|
|
283
|
-
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
284
|
-
"""
|
|
285
|
-
amplitude = threshold_current * threshold_percentage / 100
|
|
286
|
-
res = cls.amplitude_based(
|
|
287
|
-
dt,
|
|
288
|
-
pre_delay=pre_delay,
|
|
289
|
-
duration=duration,
|
|
290
|
-
post_delay=post_delay,
|
|
291
|
-
amplitude=amplitude,
|
|
292
|
-
)
|
|
293
|
-
return res
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
class DelayedZap(Stimulus):
|
|
297
|
-
|
|
298
|
-
def __init__(self):
|
|
299
|
-
raise NotImplementedError(
|
|
300
|
-
"This class cannot be instantiated directly. "
|
|
301
|
-
"Please use the class methods 'amplitude_based' "
|
|
302
|
-
"or 'threshold_based' to create objects."
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
@classmethod
|
|
306
|
-
def amplitude_based(
|
|
307
|
-
cls,
|
|
308
|
-
dt: float,
|
|
309
|
-
pre_delay: float,
|
|
310
|
-
duration: float,
|
|
311
|
-
post_delay: float,
|
|
312
|
-
amplitude: float,
|
|
313
|
-
) -> CombinedStimulus:
|
|
314
|
-
"""Create a DelayedZap stimulus from given time events and amplitude.
|
|
315
|
-
|
|
316
|
-
Args:
|
|
317
|
-
dt: The time step of the stimulus.
|
|
318
|
-
pre_delay: The delay before the start of the step.
|
|
319
|
-
duration: The duration of the step.
|
|
320
|
-
post_delay: The time to wait after the end of the step.
|
|
321
|
-
amplitude: The amplitude of the step.
|
|
322
|
-
"""
|
|
323
|
-
return (
|
|
324
|
-
Empty(dt, duration=pre_delay)
|
|
325
|
-
+ Zap(dt, duration=duration, amplitude=amplitude)
|
|
326
|
-
+ Empty(dt, duration=post_delay)
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
@classmethod
|
|
330
|
-
def threshold_based(
|
|
331
|
-
cls,
|
|
332
|
-
dt: float,
|
|
333
|
-
pre_delay: float,
|
|
334
|
-
duration: float,
|
|
335
|
-
post_delay: float,
|
|
336
|
-
threshold_current: float,
|
|
337
|
-
threshold_percentage: float,
|
|
338
|
-
) -> CombinedStimulus:
|
|
339
|
-
"""Creates a SineSpec stimulus with respect to the threshold current.
|
|
340
|
-
|
|
341
|
-
Args:
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
from typing import Optional
|
|
18
|
+
import logging
|
|
19
|
+
from bluecellulab.stimulus.stimulus import DelayedZap, Empty, Ramp, Slope, Step, StepNoise, Stimulus, OrnsteinUhlenbeck, ShotNoise
|
|
20
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Stimulus as CircuitStimulus
|
|
342
21
|
|
|
343
|
-
|
|
344
|
-
pre_delay: The delay before the start of the step.
|
|
345
|
-
duration: The duration of the step.
|
|
346
|
-
post_delay: The time to wait after the end of the step.
|
|
347
|
-
threshold_current: The threshold current of the Cell.
|
|
348
|
-
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
349
|
-
"""
|
|
350
|
-
amplitude = threshold_current * threshold_percentage / 100
|
|
351
|
-
res = cls.amplitude_based(
|
|
352
|
-
dt,
|
|
353
|
-
pre_delay=pre_delay,
|
|
354
|
-
duration=duration,
|
|
355
|
-
post_delay=post_delay,
|
|
356
|
-
amplitude=amplitude,
|
|
357
|
-
)
|
|
358
|
-
return res
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
359
23
|
|
|
360
24
|
|
|
361
25
|
class StimulusFactory:
|
|
@@ -374,15 +38,39 @@ class StimulusFactory:
|
|
|
374
38
|
)
|
|
375
39
|
|
|
376
40
|
def ramp(
|
|
377
|
-
self,
|
|
41
|
+
self,
|
|
42
|
+
pre_delay: float,
|
|
43
|
+
duration: float,
|
|
44
|
+
post_delay: float,
|
|
45
|
+
amplitude: Optional[float] = None,
|
|
46
|
+
threshold_current: Optional[float] = None,
|
|
47
|
+
threshold_percentage: Optional[float] = 220.0,
|
|
378
48
|
) -> Stimulus:
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
49
|
+
if amplitude is not None:
|
|
50
|
+
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
51
|
+
logger.info(
|
|
52
|
+
"amplitude, threshold_current and threshold_percentage are all set in ramp."
|
|
53
|
+
" Will only keep amplitude value."
|
|
54
|
+
)
|
|
55
|
+
return Ramp.amplitude_based(
|
|
56
|
+
self.dt,
|
|
57
|
+
pre_delay=pre_delay,
|
|
58
|
+
duration=duration,
|
|
59
|
+
post_delay=post_delay,
|
|
60
|
+
amplitude=amplitude,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
64
|
+
return Ramp.threshold_based(
|
|
65
|
+
self.dt,
|
|
66
|
+
pre_delay=pre_delay,
|
|
67
|
+
duration=duration,
|
|
68
|
+
post_delay=post_delay,
|
|
69
|
+
threshold_current=threshold_current,
|
|
70
|
+
threshold_percentage=threshold_percentage,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
raise TypeError("You have to give either threshold_current or amplitude")
|
|
386
74
|
|
|
387
75
|
def ap_waveform(
|
|
388
76
|
self,
|
|
@@ -687,3 +375,243 @@ class StimulusFactory:
|
|
|
687
375
|
)
|
|
688
376
|
|
|
689
377
|
raise TypeError("You have to give either threshold_current or amplitude")
|
|
378
|
+
|
|
379
|
+
def ornstein_uhlenbeck(
|
|
380
|
+
self,
|
|
381
|
+
pre_delay: float,
|
|
382
|
+
post_delay: float,
|
|
383
|
+
duration: float,
|
|
384
|
+
tau: float,
|
|
385
|
+
sigma: Optional[float] = None,
|
|
386
|
+
mean: Optional[float] = None,
|
|
387
|
+
mean_percent: Optional[float] = None,
|
|
388
|
+
sigma_percent: Optional[float] = None,
|
|
389
|
+
threshold_current: Optional[float] = None,
|
|
390
|
+
seed: Optional[int] = None
|
|
391
|
+
) -> Stimulus:
|
|
392
|
+
"""Creates an Ornstein-Uhlenbeck process stimulus (factory-compatible).
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
pre_delay: Delay before the noise starts (ms).
|
|
396
|
+
post_delay: Delay after the noise ends (ms).
|
|
397
|
+
duration: Duration of the stimulus (ms).
|
|
398
|
+
tau: Time constant of the noise process.
|
|
399
|
+
sigma: Standard deviation of the noise (used when mean is provided).
|
|
400
|
+
mean: Absolute mean current value (used if provided).
|
|
401
|
+
mean_percent: Mean current as a percentage of threshold current (used if mean is None).
|
|
402
|
+
sigma_percent: Standard deviation as a percentage of threshold current (used if sigma is None).
|
|
403
|
+
threshold_current: Reference threshold current for percentage-based calculation.
|
|
404
|
+
seed: Optional random seed for reproducibility.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
A `Stimulus` object (OrnsteinUhlenbeck) that can be plotted and injected.
|
|
408
|
+
|
|
409
|
+
Notes:
|
|
410
|
+
- If `mean` is provided, `mean_percent` is ignored.
|
|
411
|
+
- If `threshold_current` is not provided, threshold-based parameters cannot be used.
|
|
412
|
+
"""
|
|
413
|
+
is_amplitude_based = mean is not None and sigma is not None
|
|
414
|
+
is_threshold_based = (
|
|
415
|
+
threshold_current is not None
|
|
416
|
+
and threshold_current != 0
|
|
417
|
+
and mean_percent is not None
|
|
418
|
+
and sigma_percent is not None
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
if is_amplitude_based:
|
|
422
|
+
if is_threshold_based:
|
|
423
|
+
logger.info(
|
|
424
|
+
"mean, threshold_current, and mean_percent are all set in Ornstein-Uhlenbeck."
|
|
425
|
+
" Using mean and ignoring threshold-based parameters."
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
return OrnsteinUhlenbeck.amplitude_based(
|
|
429
|
+
dt=self.dt,
|
|
430
|
+
pre_delay=pre_delay,
|
|
431
|
+
post_delay=post_delay,
|
|
432
|
+
duration=duration,
|
|
433
|
+
tau=tau,
|
|
434
|
+
sigma=sigma, # type: ignore[arg-type]
|
|
435
|
+
mean=mean, # type: ignore[arg-type]
|
|
436
|
+
seed=seed
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
if is_threshold_based:
|
|
440
|
+
return OrnsteinUhlenbeck.threshold_based(
|
|
441
|
+
dt=self.dt,
|
|
442
|
+
pre_delay=pre_delay,
|
|
443
|
+
post_delay=post_delay,
|
|
444
|
+
duration=duration,
|
|
445
|
+
mean_percent=mean_percent, # type: ignore[arg-type]
|
|
446
|
+
sigma_percent=sigma_percent, # type: ignore[arg-type]
|
|
447
|
+
threshold_current=threshold_current, # type: ignore[arg-type]
|
|
448
|
+
tau=tau,
|
|
449
|
+
seed=seed
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
raise TypeError("You have to give either `mean` and `sigma` or `threshold_current` and `mean_percent` and `sigma_percent`.")
|
|
453
|
+
|
|
454
|
+
def shot_noise(
|
|
455
|
+
self,
|
|
456
|
+
pre_delay: float,
|
|
457
|
+
post_delay: float,
|
|
458
|
+
duration: float,
|
|
459
|
+
rate: float,
|
|
460
|
+
rise_time: float,
|
|
461
|
+
decay_time: float,
|
|
462
|
+
mean: Optional[float] = None,
|
|
463
|
+
sigma: Optional[float] = None,
|
|
464
|
+
mean_percent: Optional[float] = None,
|
|
465
|
+
sigma_percent: Optional[float] = None,
|
|
466
|
+
relative_skew: float = 0.5,
|
|
467
|
+
threshold_current: Optional[float] = None,
|
|
468
|
+
seed: Optional[int] = None
|
|
469
|
+
) -> Stimulus:
|
|
470
|
+
"""Creates a ShotNoise instance, either with an absolute amplitude or
|
|
471
|
+
relative to a threshold current.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
pre_delay: Delay before the noise starts (ms).
|
|
475
|
+
post_delay: Delay after the noise ends (ms).
|
|
476
|
+
duration: Duration of the stimulus (ms).
|
|
477
|
+
rate: Mean rate of synaptic events (Hz).
|
|
478
|
+
mean: Mean amplitude of events (nA), used if provided.
|
|
479
|
+
sigma: Standard deviation of event amplitudes.
|
|
480
|
+
rise_time: Rise time of synaptic events (ms).
|
|
481
|
+
decay_time: Decay time of synaptic events (ms).
|
|
482
|
+
mean_percent: Mean current as a percentage of threshold current (used if mean is None).
|
|
483
|
+
sigma_percent: Standard deviation as a percentage of threshold current (used if sigma is None).
|
|
484
|
+
relative_skew: Skew factor for the shot noise process (default: 0.5).
|
|
485
|
+
threshold_current: Reference threshold current for percentage-based calculation.
|
|
486
|
+
seed: Optional random seed for reproducibility.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
A `Stimulus` object that can be plotted and injected.
|
|
490
|
+
|
|
491
|
+
Notes:
|
|
492
|
+
- If `mean` is provided, `mean_percent` is ignored.
|
|
493
|
+
- If `threshold_current` is not provided, threshold-based parameters cannot be used.
|
|
494
|
+
"""
|
|
495
|
+
is_amplitude_based = mean is not None and sigma is not None
|
|
496
|
+
is_threshold_based = (
|
|
497
|
+
threshold_current is not None
|
|
498
|
+
and threshold_current != 0
|
|
499
|
+
and mean_percent is not None
|
|
500
|
+
and sigma_percent is not None
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
if is_amplitude_based:
|
|
504
|
+
if is_threshold_based:
|
|
505
|
+
logger.info(
|
|
506
|
+
"mean, threshold_current, and mean_percent are all set in ShotNoise."
|
|
507
|
+
" Using mean and ignoring threshold-based parameters."
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
return ShotNoise.amplitude_based(
|
|
511
|
+
dt=self.dt,
|
|
512
|
+
pre_delay=pre_delay,
|
|
513
|
+
post_delay=post_delay,
|
|
514
|
+
duration=duration,
|
|
515
|
+
rate=rate,
|
|
516
|
+
mean=mean, # type: ignore[arg-type]
|
|
517
|
+
sigma=sigma, # type: ignore[arg-type]
|
|
518
|
+
rise_time=rise_time,
|
|
519
|
+
decay_time=decay_time,
|
|
520
|
+
seed=seed
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
if is_threshold_based:
|
|
524
|
+
return ShotNoise.threshold_based(
|
|
525
|
+
dt=self.dt,
|
|
526
|
+
pre_delay=pre_delay,
|
|
527
|
+
post_delay=post_delay,
|
|
528
|
+
duration=duration,
|
|
529
|
+
rise_time=rise_time,
|
|
530
|
+
decay_time=decay_time,
|
|
531
|
+
mean_percent=mean_percent, # type: ignore[arg-type]
|
|
532
|
+
sigma_percent=sigma_percent, # type: ignore[arg-type]
|
|
533
|
+
threshold_current=threshold_current, # type: ignore[arg-type]
|
|
534
|
+
relative_skew=relative_skew,
|
|
535
|
+
seed=seed
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
raise TypeError("You must provide either `mean` and `sigma`, or `threshold_current` and `mean_percent` and `sigma_percent` with percentage values.")
|
|
539
|
+
|
|
540
|
+
def step_noise(
|
|
541
|
+
self,
|
|
542
|
+
pre_delay: float,
|
|
543
|
+
post_delay: float,
|
|
544
|
+
duration: float,
|
|
545
|
+
step_interval: float,
|
|
546
|
+
mean: Optional[float] = None,
|
|
547
|
+
sigma: Optional[float] = None,
|
|
548
|
+
mean_percent: Optional[float] = None,
|
|
549
|
+
sigma_percent: Optional[float] = None,
|
|
550
|
+
threshold_current: Optional[float] = None,
|
|
551
|
+
seed: Optional[int] = None,
|
|
552
|
+
) -> Stimulus:
|
|
553
|
+
"""Creates a StepNoise instance, either with an absolute amplitude or
|
|
554
|
+
relative to a threshold current.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
pre_delay: Delay before the step noise starts (ms).
|
|
558
|
+
post_delay: Delay after the step noise ends (ms).
|
|
559
|
+
duration: Duration of the stimulus (ms).
|
|
560
|
+
step_interval: Interval at which noise amplitude changes.
|
|
561
|
+
mean: Mean amplitude of step noise (nA), used if provided.
|
|
562
|
+
sigma: Standard deviation of step noise.
|
|
563
|
+
mean_percent: Mean current as a percentage of threshold current (used if mean is None).
|
|
564
|
+
sigma_percent: Standard deviation as a percentage of threshold current (used if sigma is None).
|
|
565
|
+
threshold_current: Reference threshold current for percentage-based calculation.
|
|
566
|
+
seed: Optional random seed for reproducibility.
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
A `Stimulus` object that can be plotted and injected.
|
|
570
|
+
|
|
571
|
+
Notes:
|
|
572
|
+
- If `mean` is provided, `mean_percent` is ignored.
|
|
573
|
+
- If `threshold_current` is not provided, threshold-based parameters cannot be used.
|
|
574
|
+
"""
|
|
575
|
+
is_amplitude_based = mean is not None and sigma is not None
|
|
576
|
+
is_threshold_based = (
|
|
577
|
+
threshold_current is not None
|
|
578
|
+
and threshold_current != 0
|
|
579
|
+
and mean_percent is not None
|
|
580
|
+
and sigma_percent is not None
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
if is_amplitude_based:
|
|
584
|
+
if is_threshold_based:
|
|
585
|
+
logger.info(
|
|
586
|
+
"mean, threshold_current, and mean_percent are all set in StepNoise."
|
|
587
|
+
" Using mean and ignoring threshold-based parameters."
|
|
588
|
+
)
|
|
589
|
+
return StepNoise.amplitude_based(
|
|
590
|
+
dt=self.dt,
|
|
591
|
+
pre_delay=pre_delay,
|
|
592
|
+
post_delay=post_delay,
|
|
593
|
+
duration=duration,
|
|
594
|
+
step_interval=step_interval,
|
|
595
|
+
mean=mean, # type: ignore[arg-type]
|
|
596
|
+
sigma=sigma, # type: ignore[arg-type]
|
|
597
|
+
seed=seed,
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
if is_threshold_based:
|
|
601
|
+
return StepNoise.threshold_based(
|
|
602
|
+
dt=self.dt,
|
|
603
|
+
pre_delay=pre_delay,
|
|
604
|
+
post_delay=post_delay,
|
|
605
|
+
duration=duration,
|
|
606
|
+
step_interval=step_interval,
|
|
607
|
+
mean_percent=mean_percent, # type: ignore[arg-type]
|
|
608
|
+
sigma_percent=sigma_percent, # type: ignore[arg-type]
|
|
609
|
+
threshold_current=threshold_current, # type: ignore[arg-type]
|
|
610
|
+
seed=seed,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
raise TypeError("You must provide either `mean` and `sigma`, or `threshold_current` and `mean_percent` and `sigma_percent` with percentage values.")
|
|
614
|
+
|
|
615
|
+
def from_sonata(cls, circuit_stimulus: CircuitStimulus):
|
|
616
|
+
"""Convert a SONATA stimulus into a factory-based stimulus."""
|
|
617
|
+
raise ValueError(f"Unsupported circuit stimulus type: {type(circuit_stimulus)}")
|