braintools 0.0.2__py2.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.
Files changed (38) hide show
  1. braintools/__init__.py +27 -0
  2. braintools/functional/__init__.py +25 -0
  3. braintools/functional/_activations.py +733 -0
  4. braintools/functional/_normalization.py +62 -0
  5. braintools/functional/_spikes.py +90 -0
  6. braintools/init/__init__.py +26 -0
  7. braintools/init/_base.py +36 -0
  8. braintools/init/_generic.py +205 -0
  9. braintools/init/_random_inits.py +489 -0
  10. braintools/init/_regular_inits.py +109 -0
  11. braintools/input/__init__.py +27 -0
  12. braintools/input/currents.py +380 -0
  13. braintools/input/currents_test.py +111 -0
  14. braintools/metric/__init__.py +46 -0
  15. braintools/metric/_classification.py +569 -0
  16. braintools/metric/_classification_test.py +625 -0
  17. braintools/metric/_correlation.py +302 -0
  18. braintools/metric/_correlation_test.py +124 -0
  19. braintools/metric/_fenchel_young.py +67 -0
  20. braintools/metric/_fenchel_young_test.py +55 -0
  21. braintools/metric/_firings.py +85 -0
  22. braintools/metric/_firings_test.py +41 -0
  23. braintools/metric/_lfp.py +139 -0
  24. braintools/metric/_ranking.py +163 -0
  25. braintools/metric/_ranking_test.py +242 -0
  26. braintools/metric/_regression.py +425 -0
  27. braintools/metric/_regression_test.py +133 -0
  28. braintools/metric/_smoothing.py +46 -0
  29. braintools/metric/_smoothing_test.py +43 -0
  30. braintools/metric/_util.py +25 -0
  31. braintools/optim/__init__.py +23 -0
  32. braintools/optim/_lr_scheduler.py +482 -0
  33. braintools/optim/_sgd_optimizer.py +1130 -0
  34. braintools-0.0.2.dist-info/LICENSE +202 -0
  35. braintools-0.0.2.dist-info/METADATA +93 -0
  36. braintools-0.0.2.dist-info/RECORD +38 -0
  37. braintools-0.0.2.dist-info/WHEEL +6 -0
  38. braintools-0.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,380 @@
1
+ # Copyright 2024- BrainPy Ecosystem Limited. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ # -*- coding: utf-8 -*-
17
+
18
+
19
+ import braincore as bc
20
+ import jax.lax
21
+ import jax.numpy as jnp
22
+ import numpy as np
23
+
24
+ __all__ = [
25
+ 'section_input',
26
+ 'constant_input',
27
+ 'spike_input',
28
+ 'ramp_input',
29
+ 'wiener_process',
30
+ 'ou_process',
31
+ 'sinusoidal_input',
32
+ 'square_input',
33
+ ]
34
+
35
+
36
+ def section_input(values, durations, dt=None, return_length=False):
37
+ """Format an input current with different sections.
38
+
39
+ For example:
40
+
41
+ If you want to get an input where the size is 0 bwteen 0-100 ms,
42
+ and the size is 1. between 100-200 ms.
43
+
44
+ >>> section_input(values=[0, 1],
45
+ >>> durations=[100, 100])
46
+
47
+ Parameters
48
+ ----------
49
+ values : list, np.ndarray
50
+ The current values for each period duration.
51
+ durations : list, np.ndarray
52
+ The duration for each period.
53
+ dt : float
54
+ Default is None.
55
+ return_length : bool
56
+ Return the final duration length.
57
+
58
+ Returns
59
+ -------
60
+ current_and_duration
61
+ """
62
+ if len(durations) != len(values):
63
+ raise ValueError(f'"values" and "durations" must be the same length, while '
64
+ f'we got {len(values)} != {len(durations)}.')
65
+
66
+ dt = bc.environ.get_dt() if dt is None else dt
67
+
68
+ # get input current shape, and duration
69
+ I_duration = sum(durations)
70
+ I_shape = ()
71
+ for val in values:
72
+ shape = jnp.shape(val)
73
+ if len(shape) > len(I_shape):
74
+ I_shape = shape
75
+
76
+ # get the current
77
+ start = 0
78
+ I_current = jnp.zeros((int(np.ceil(I_duration / dt)),) + I_shape, dtype=bc.environ.dftype())
79
+ for c_size, duration in zip(values, durations):
80
+ length = int(duration / dt)
81
+ I_current = I_current.at[start: start + length].set(c_size)
82
+ start += length
83
+
84
+ if return_length:
85
+ return I_current, I_duration
86
+ else:
87
+ return I_current
88
+
89
+
90
+ def constant_input(I_and_duration, dt=None):
91
+ """Format constant input in durations.
92
+
93
+ For example:
94
+
95
+ If you want to get an input where the size is 0 bwteen 0-100 ms,
96
+ and the size is 1. between 100-200 ms.
97
+
98
+ >>> import brainpy.math as bm
99
+ >>> constant_input([(0, 100), (1, 100)])
100
+ >>> constant_input([(bm.zeros(100), 100), (bm.random.rand(100), 100)])
101
+
102
+ Parameters
103
+ ----------
104
+ I_and_duration : list
105
+ This parameter receives the current size and the current
106
+ duration pairs, like `[(Isize1, duration1), (Isize2, duration2)]`.
107
+ dt : float
108
+ Default is None.
109
+
110
+ Returns
111
+ -------
112
+ current_and_duration : tuple
113
+ (The formatted current, total duration)
114
+ """
115
+ dt = bc.environ.get_dt() if dt is None else dt
116
+
117
+ # get input current dimension, shape, and duration
118
+ I_duration = 0.
119
+ I_shape = ()
120
+ for I in I_and_duration:
121
+ I_duration += I[1]
122
+ shape = jnp.shape(I[0])
123
+ if len(shape) > len(I_shape):
124
+ I_shape = shape
125
+
126
+ # get the current
127
+ start = 0
128
+ I_current = jnp.zeros((int(np.ceil(I_duration / dt)),) + I_shape, dtype=bc.environ.dftype())
129
+ for c_size, duration in I_and_duration:
130
+ length = int(duration / dt)
131
+ I_current = I_current.at[start: start + length].set(c_size)
132
+ start += length
133
+ return I_current, I_duration
134
+
135
+
136
+
137
+ def spike_input(sp_times, sp_lens, sp_sizes, duration, dt=None):
138
+ """Format current input like a series of short-time spikes.
139
+
140
+ For example:
141
+
142
+ If you want to generate a spike train at 10 ms, 20 ms, 30 ms, 200 ms, 300 ms,
143
+ and each spike lasts 1 ms and the spike current is 0.5, then you can use the
144
+ following funtions:
145
+
146
+ >>> spike_input(sp_times=[10, 20, 30, 200, 300],
147
+ >>> sp_lens=1., # can be a list to specify the spike length at each point
148
+ >>> sp_sizes=0.5, # can be a list to specify the current size at each point
149
+ >>> duration=400.)
150
+
151
+ Parameters
152
+ ----------
153
+ sp_times : list, tuple
154
+ The spike time-points. Must be an iterable object.
155
+ sp_lens : int, float, list, tuple
156
+ The length of each point-current, mimicking the spike durations.
157
+ sp_sizes : int, float, list, tuple
158
+ The current sizes.
159
+ duration : int, float
160
+ The total current duration.
161
+ dt : float
162
+ The default is None.
163
+
164
+ Returns
165
+ -------
166
+ current : bm.ndarray
167
+ The formatted input current.
168
+ """
169
+ dt = bc.environ.get_dt() if dt is None else dt
170
+ assert isinstance(sp_times, (list, tuple))
171
+ if isinstance(sp_lens, (float, int)):
172
+ sp_lens = [sp_lens] * len(sp_times)
173
+ if isinstance(sp_sizes, (float, int)):
174
+ sp_sizes = [sp_sizes] * len(sp_times)
175
+
176
+ current = jnp.zeros(int(np.ceil(duration / dt)), dtype=bc.environ.dftype())
177
+ for time, dur, size in zip(sp_times, sp_lens, sp_sizes):
178
+ pp = int(time / dt)
179
+ p_len = int(dur / dt)
180
+ current = current.at[pp: pp + p_len].set(size)
181
+ return current
182
+
183
+
184
+ def ramp_input(c_start, c_end, duration, t_start=0, t_end=None, dt=None):
185
+ """Get the gradually changed input current.
186
+
187
+ Parameters
188
+ ----------
189
+ c_start : float
190
+ The minimum (or maximum) current size.
191
+ c_end : float
192
+ The maximum (or minimum) current size.
193
+ duration : int, float
194
+ The total duration.
195
+ t_start : float
196
+ The ramped current start time-point.
197
+ t_end : float
198
+ The ramped current end time-point. Default is the None.
199
+ dt : float, int, optional
200
+ The numerical precision.
201
+
202
+ Returns
203
+ -------
204
+ current : bm.ndarray
205
+ The formatted current
206
+ """
207
+ dt = bc.environ.get_dt() if dt is None else dt
208
+ t_end = duration if t_end is None else t_end
209
+
210
+ current = jnp.zeros(int(np.ceil(duration / dt)), dtype=bc.environ.dftype())
211
+ p1 = int(np.ceil(t_start / dt))
212
+ p2 = int(np.ceil(t_end / dt))
213
+ cc = jnp.array(jnp.linspace(c_start, c_end, p2 - p1))
214
+ current = current.at[p1: p2].set(cc)
215
+ return current
216
+
217
+
218
+
219
+ def wiener_process(duration, dt=None, n=1, t_start=0., t_end=None, seed=None):
220
+ """Stimulus sampled from a Wiener process, i.e.
221
+ drawn from standard normal distribution N(0, sqrt(dt)).
222
+
223
+ Parameters
224
+ ----------
225
+ duration: float
226
+ The input duration.
227
+ dt: float
228
+ The numerical precision.
229
+ n: int
230
+ The variable number.
231
+ t_start: float
232
+ The start time.
233
+ t_end: float
234
+ The end time.
235
+ seed: int
236
+ The noise seed.
237
+ """
238
+ dt = bc.environ.get_dt() if dt is None else dt
239
+ t_end = duration if t_end is None else t_end
240
+ i_start = int(t_start / dt)
241
+ i_end = int(t_end / dt)
242
+ noises = bc.random.standard_normal((i_end - i_start, n)) * jnp.sqrt(dt)
243
+ currents = jnp.zeros((int(duration / dt), n), dtype=bc.environ.dftype())
244
+ currents = currents.at[i_start: i_end].set(noises)
245
+ return currents
246
+
247
+
248
+ def ou_process(mean, sigma, tau, duration, dt=None, n=1, t_start=0., t_end=None, seed=None):
249
+ r"""Ornstein–Uhlenbeck input.
250
+
251
+ .. math::
252
+
253
+ dX = (mu - X)/\tau * dt + \sigma*dW
254
+
255
+ Parameters
256
+ ----------
257
+ mean: float
258
+ Drift of the OU process.
259
+ sigma: float
260
+ Standard deviation of the Wiener process, i.e. strength of the noise.
261
+ tau: float
262
+ Timescale of the OU process, in ms.
263
+ duration: float
264
+ The input duration.
265
+ dt: float
266
+ The numerical precision.
267
+ n: int
268
+ The variable number.
269
+ t_start: float
270
+ The start time.
271
+ t_end: float
272
+ The end time.
273
+ seed: optional, int
274
+ The random seed.
275
+ """
276
+ dt = bc.environ.get_dt() if dt is None else dt
277
+ dt_sqrt = jnp.sqrt(dt)
278
+ t_end = duration if t_end is None else t_end
279
+ i_start = int(t_start / dt)
280
+ i_end = int(t_end / dt)
281
+
282
+ def _f(x, key):
283
+ x = x + dt * ((mean - x) / tau) + sigma * dt_sqrt * bc.random.rand(n, key=key)
284
+ return x, x
285
+
286
+ _, noises = jax.lax.scan(_f, jnp.full(n, mean, dtype=bc.environ.dftype()), bc.random.split_keys(i_end - i_start))
287
+ currents = jnp.zeros((int(duration / dt), n), dtype=bc.environ.dftype())
288
+ return currents.at[i_start: i_end].set(noises)
289
+
290
+
291
+ def sinusoidal_input(amplitude, frequency, duration, dt=None, t_start=0., t_end=None, bias=False):
292
+ """Sinusoidal input.
293
+
294
+ Parameters
295
+ ----------
296
+ amplitude: float
297
+ Amplitude of the sinusoid.
298
+ frequency: float
299
+ Frequency of the sinus oscillation, in Hz
300
+ duration: float
301
+ The input duration.
302
+ t_start: float
303
+ The start time.
304
+ t_end: float
305
+ The end time.
306
+ dt: float
307
+ The numerical precision.
308
+ bias: bool
309
+ Whether the sinusoid oscillates around 0 (False), or
310
+ has a positive DC bias, thus non-negative (True).
311
+ """
312
+ dt = bc.environ.get_dt() if dt is None else dt
313
+ if t_end is None:
314
+ t_end = duration
315
+ times = jnp.arange(0, t_end - t_start, dt)
316
+ start_i = int(t_start / dt)
317
+ end_i = int(t_end / dt)
318
+ sin_inputs = amplitude * jnp.sin(2 * jnp.pi * times * (frequency / 1000.0))
319
+ if bias: sin_inputs += amplitude
320
+ currents = jnp.zeros(int(duration / dt), dtype=bc.environ.dftype())
321
+ return currents.at[start_i:end_i].set(sin_inputs)
322
+
323
+
324
+ def _square(t, duty=0.5):
325
+ t, w = np.asarray(t), np.asarray(duty)
326
+ w = np.asarray(w + (t - t))
327
+ t = np.asarray(t + (w - w))
328
+ if t.dtype.char in 'fFdD':
329
+ ytype = t.dtype.char
330
+ else:
331
+ ytype = 'd'
332
+
333
+ y = np.zeros(t.shape, ytype)
334
+
335
+ # width must be between 0 and 1 inclusive
336
+ mask1 = (w > 1) | (w < 0)
337
+ np.place(y, mask1, np.nan)
338
+
339
+ # on the interval 0 to duty*2*pi function is 1
340
+ tmod = np.mod(t, 2 * np.pi)
341
+ mask2 = (1 - mask1) & (tmod < w * 2 * np.pi)
342
+ np.place(y, mask2, 1)
343
+
344
+ # on the interval duty*2*pi to 2*pi function is
345
+ # (pi*(w+1)-tmod) / (pi*(1-w))
346
+ mask3 = (1 - mask1) & (1 - mask2)
347
+ np.place(y, mask3, -1)
348
+ return y
349
+
350
+
351
+ def square_input(amplitude, frequency, duration, dt=None, bias=False, t_start=0., t_end=None):
352
+ """Oscillatory square input.
353
+
354
+ Parameters
355
+ ----------
356
+ amplitude: float
357
+ Amplitude of the square oscillation.
358
+ frequency: float
359
+ Frequency of the square oscillation, in Hz.
360
+ duration: float
361
+ The input duration.
362
+ t_start: float
363
+ The start time.
364
+ t_end: float
365
+ The end time.
366
+ dt: float
367
+ The numerical precision.
368
+ bias: bool
369
+ Whether the sinusoid oscillates around 0 (False), or
370
+ has a positive DC bias, thus non-negative (True).
371
+ """
372
+ dt = bc.environ.get_dt() if dt is None else dt
373
+ if t_end is None: t_end = duration
374
+ times = np.arange(0, t_end - t_start, dt)
375
+ sin_inputs = amplitude * _square(2 * np.pi * times * (frequency / 1000.0))
376
+ if bias: sin_inputs += amplitude
377
+ currents = jnp.zeros(int(duration / dt), dtype=bc.environ.dftype())
378
+ start_i = int(t_start / dt)
379
+ end_i = int(t_end / dt)
380
+ return currents.at[start_i:end_i].set(sin_inputs)
@@ -0,0 +1,111 @@
1
+ # Copyright 2024- BrainPy Ecosystem Limited. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ # -*- coding: utf-8 -*-
17
+
18
+
19
+ from unittest import TestCase
20
+
21
+ import braincore as bc
22
+ import jax.numpy as jnp
23
+ import numpy as np
24
+
25
+ import braintools as bt
26
+ try:
27
+ import matplotlib.pyplot as plt
28
+ except (ImportError, ModuleNotFoundError):
29
+ plt = None
30
+
31
+ block = False
32
+
33
+
34
+ def show(current, duration, title=''):
35
+ if plt is not None:
36
+ ts = np.arange(0, duration, bc.environ.get_dt())
37
+ plt.plot(ts, current)
38
+ plt.title(title)
39
+ plt.xlabel('Time [ms]')
40
+ plt.ylabel('Current Value')
41
+ plt.show(block=block)
42
+
43
+
44
+ class TestCurrents(TestCase):
45
+ def test_section_input(self):
46
+ current1, duration = bt.input.section_input(values=[0, 1., 0.],
47
+ durations=[100, 300, 100],
48
+ return_length=True,
49
+ dt=0.1)
50
+ show(current1, duration, 'values=[0, 1, 0], durations=[100, 300, 100]')
51
+
52
+ def test_constant_input(self):
53
+ current2, duration = bt.input.constant_input([(0, 100), (1, 300), (0, 100)])
54
+ show(current2, duration, '[(0, 100), (1, 300), (0, 100)]')
55
+
56
+ def test_spike_input(self):
57
+ current3 = bt.input.spike_input(
58
+ sp_times=[10, 20, 30, 200, 300],
59
+ sp_lens=1., # can be a list to specify the spike length at each point
60
+ sp_sizes=0.5, # can be a list to specify the spike current size at each point
61
+ duration=400.)
62
+
63
+ show(current3, 400, 'Spike Input Example')
64
+
65
+ def test_ramp_input(self):
66
+ duration = 500
67
+ current4 = bt.input.ramp_input(0, 1, duration)
68
+
69
+ show(current4, duration, r'$c_{start}$=0, $c_{end}$=%d, duration, '
70
+ r'$t_{start}$=0, $t_{end}$=None' % (duration))
71
+
72
+ def test_ramp_input2(self):
73
+ duration, t_start, t_end = 500, 100, 400
74
+ current5 = bt.input.ramp_input(0, 1, duration, t_start, t_end)
75
+
76
+ show(current5, duration, r'$c_{start}$=0, $c_{end}$=1, duration=%d, '
77
+ r'$t_{start}$=%d, $t_{end}$=%d' % (duration, t_start, t_end))
78
+
79
+ def test_wiener_process(self):
80
+ duration = 200
81
+ current6 = bt.input.wiener_process(duration, n=2, t_start=10., t_end=180.)
82
+ show(current6, duration, 'Wiener Process')
83
+
84
+ def test_ou_process(self):
85
+ duration = 200
86
+ current7 = bt.input.ou_process(mean=1., sigma=0.1, tau=10., duration=duration, n=2, t_start=10., t_end=180.)
87
+ show(current7, duration, 'Ornstein-Uhlenbeck Process')
88
+
89
+ def test_sinusoidal_input(self):
90
+ duration = 2000
91
+ current8 = bt.input.sinusoidal_input(amplitude=1., frequency=2.0, duration=duration, t_start=100., )
92
+ show(current8, duration, 'Sinusoidal Input')
93
+
94
+ def test_square_input(self):
95
+ duration = 2000
96
+ current9 = bt.input.square_input(amplitude=1., frequency=2.0,
97
+ duration=duration, t_start=100)
98
+ show(current9, duration, 'Square Input')
99
+
100
+ def test_general1(self):
101
+ I1 = bt.input.section_input(values=[0, 1, 2], durations=[10, 20, 30], dt=0.1)
102
+ I2 = bt.input.section_input(values=[0, 1, 2], durations=[10, 20, 30], dt=0.01)
103
+ self.assertTrue(I1.shape[0] == 600)
104
+ self.assertTrue(I2.shape[0] == 6000)
105
+
106
+ def test_general2(self):
107
+ bc.random.seed(123)
108
+ current = bt.input.section_input(values=[0, jnp.ones(10), bc.random.random((3, 10))],
109
+ durations=[100, 300, 100])
110
+ self.assertTrue(current.shape == (5000, 3, 10))
111
+
@@ -0,0 +1,46 @@
1
+ # Copyright 2024- BrainPy Ecosystem Limited. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ from ._classification import *
17
+ from ._classification import __all__ as _classification_all
18
+ from ._correlation import *
19
+ from ._correlation import __all__ as _correlation_all
20
+ from ._fenchel_young import *
21
+ from ._fenchel_young import __all__ as _fenchel_young_all
22
+ from ._firings import *
23
+ from ._firings import __all__ as _firings_all
24
+ from ._lfp import *
25
+ from ._lfp import __all__ as _lfp_all
26
+ from ._ranking import *
27
+ from ._ranking import __all__ as _ranking_all
28
+ from ._regression import *
29
+ from ._regression import __all__ as _regression_all
30
+ from ._smoothing import *
31
+ from ._smoothing import __all__ as _smoothing_all
32
+
33
+ __all__ = (
34
+ _classification_all
35
+ + _correlation_all
36
+ + _fenchel_young_all
37
+ + _firings_all
38
+ + _lfp_all
39
+ + _ranking_all
40
+ + _regression_all
41
+ + _smoothing_all
42
+ )
43
+ del (_classification_all, _correlation_all,
44
+ _fenchel_young_all, _firings_all,
45
+ _lfp_all, _ranking_all,
46
+ _regression_all, _smoothing_all)