vbi 0.1.3__cp310-cp310-manylinux2014_x86_64.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.
- vbi/__init__.py +37 -0
- vbi/_version.py +17 -0
- vbi/dataset/__init__.py +0 -0
- vbi/dataset/connectivity_84/centers.txt +84 -0
- vbi/dataset/connectivity_84/centres.txt +84 -0
- vbi/dataset/connectivity_84/cortical.txt +84 -0
- vbi/dataset/connectivity_84/tract_lengths.txt +84 -0
- vbi/dataset/connectivity_84/weights.txt +84 -0
- vbi/dataset/connectivity_88/Aud_88.txt +88 -0
- vbi/dataset/connectivity_88/Bold.npz +0 -0
- vbi/dataset/connectivity_88/Labels.txt +17 -0
- vbi/dataset/connectivity_88/Region_labels.txt +88 -0
- vbi/dataset/connectivity_88/tract_lengths.txt +88 -0
- vbi/dataset/connectivity_88/weights.txt +88 -0
- vbi/feature_extraction/__init__.py +1 -0
- vbi/feature_extraction/calc_features.py +293 -0
- vbi/feature_extraction/features.json +535 -0
- vbi/feature_extraction/features.py +2124 -0
- vbi/feature_extraction/features_settings.py +374 -0
- vbi/feature_extraction/features_utils.py +1357 -0
- vbi/feature_extraction/infodynamics.jar +0 -0
- vbi/feature_extraction/utility.py +507 -0
- vbi/inference.py +98 -0
- vbi/models/__init__.py +0 -0
- vbi/models/cpp/__init__.py +0 -0
- vbi/models/cpp/_src/__init__.py +0 -0
- vbi/models/cpp/_src/__pycache__/mpr_sde.cpython-310.pyc +0 -0
- vbi/models/cpp/_src/_do.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/_jr_sdde.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/_jr_sde.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/_km_sde.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/_mpr_sde.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/_vep.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/_wc_ode.cpython-310-x86_64-linux-gnu.so +0 -0
- vbi/models/cpp/_src/bold.hpp +303 -0
- vbi/models/cpp/_src/do.hpp +167 -0
- vbi/models/cpp/_src/do.i +17 -0
- vbi/models/cpp/_src/do.py +467 -0
- vbi/models/cpp/_src/do_wrap.cxx +12811 -0
- vbi/models/cpp/_src/jr_sdde.hpp +352 -0
- vbi/models/cpp/_src/jr_sdde.i +19 -0
- vbi/models/cpp/_src/jr_sdde.py +688 -0
- vbi/models/cpp/_src/jr_sdde_wrap.cxx +18718 -0
- vbi/models/cpp/_src/jr_sde.hpp +264 -0
- vbi/models/cpp/_src/jr_sde.i +17 -0
- vbi/models/cpp/_src/jr_sde.py +470 -0
- vbi/models/cpp/_src/jr_sde_wrap.cxx +13406 -0
- vbi/models/cpp/_src/km_sde.hpp +158 -0
- vbi/models/cpp/_src/km_sde.i +19 -0
- vbi/models/cpp/_src/km_sde.py +671 -0
- vbi/models/cpp/_src/km_sde_wrap.cxx +17367 -0
- vbi/models/cpp/_src/makefile +52 -0
- vbi/models/cpp/_src/mpr_sde.hpp +327 -0
- vbi/models/cpp/_src/mpr_sde.i +19 -0
- vbi/models/cpp/_src/mpr_sde.py +711 -0
- vbi/models/cpp/_src/mpr_sde_wrap.cxx +18618 -0
- vbi/models/cpp/_src/utility.hpp +307 -0
- vbi/models/cpp/_src/vep.hpp +171 -0
- vbi/models/cpp/_src/vep.i +16 -0
- vbi/models/cpp/_src/vep.py +464 -0
- vbi/models/cpp/_src/vep_wrap.cxx +12968 -0
- vbi/models/cpp/_src/wc_ode.hpp +294 -0
- vbi/models/cpp/_src/wc_ode.i +19 -0
- vbi/models/cpp/_src/wc_ode.py +686 -0
- vbi/models/cpp/_src/wc_ode_wrap.cxx +24263 -0
- vbi/models/cpp/damp_oscillator.py +143 -0
- vbi/models/cpp/jansen_rit.py +543 -0
- vbi/models/cpp/km.py +187 -0
- vbi/models/cpp/mpr.py +289 -0
- vbi/models/cpp/vep.py +150 -0
- vbi/models/cpp/wc.py +216 -0
- vbi/models/cupy/__init__.py +0 -0
- vbi/models/cupy/bold.py +111 -0
- vbi/models/cupy/ghb.py +284 -0
- vbi/models/cupy/jansen_rit.py +473 -0
- vbi/models/cupy/km.py +224 -0
- vbi/models/cupy/mpr.py +475 -0
- vbi/models/cupy/mpr_modified_bold.py +12 -0
- vbi/models/cupy/utils.py +184 -0
- vbi/models/numba/__init__.py +0 -0
- vbi/models/numba/_ww_EI.py +444 -0
- vbi/models/numba/damp_oscillator.py +162 -0
- vbi/models/numba/ghb.py +208 -0
- vbi/models/numba/mpr.py +383 -0
- vbi/models/pytorch/__init__.py +0 -0
- vbi/models/pytorch/data/default_parameters.npz +0 -0
- vbi/models/pytorch/data/input/ROI_sim.mat +0 -0
- vbi/models/pytorch/data/input/fc_test.csv +68 -0
- vbi/models/pytorch/data/input/fc_train.csv +68 -0
- vbi/models/pytorch/data/input/fc_vali.csv +68 -0
- vbi/models/pytorch/data/input/fcd_test.mat +0 -0
- vbi/models/pytorch/data/input/fcd_test_high_window.mat +0 -0
- vbi/models/pytorch/data/input/fcd_test_low_window.mat +0 -0
- vbi/models/pytorch/data/input/fcd_train.mat +0 -0
- vbi/models/pytorch/data/input/fcd_vali.mat +0 -0
- vbi/models/pytorch/data/input/myelin.csv +68 -0
- vbi/models/pytorch/data/input/rsfc_gradient.csv +68 -0
- vbi/models/pytorch/data/input/run_label_testset.mat +0 -0
- vbi/models/pytorch/data/input/sc_test.csv +68 -0
- vbi/models/pytorch/data/input/sc_train.csv +68 -0
- vbi/models/pytorch/data/input/sc_vali.csv +68 -0
- vbi/models/pytorch/data/obs_kong0.npz +0 -0
- vbi/models/pytorch/ww_sde_kong.py +570 -0
- vbi/models/tvbk/__init__.py +9 -0
- vbi/models/tvbk/tvbk_wrapper.py +166 -0
- vbi/models/tvbk/utils.py +72 -0
- vbi/papers/__init__.py +0 -0
- vbi/papers/pavlides_pcb_2015/pavlides.py +211 -0
- vbi/tests/__init__.py +0 -0
- vbi/tests/_test_mpr_nb.py +36 -0
- vbi/tests/test_features.py +355 -0
- vbi/tests/test_ghb_cupy.py +90 -0
- vbi/tests/test_mpr_cupy.py +49 -0
- vbi/tests/test_mpr_numba.py +84 -0
- vbi/tests/test_suite.py +19 -0
- vbi/utils.py +402 -0
- vbi-0.1.3.dist-info/METADATA +166 -0
- vbi-0.1.3.dist-info/RECORD +121 -0
- vbi-0.1.3.dist-info/WHEEL +5 -0
- vbi-0.1.3.dist-info/licenses/LICENSE +201 -0
- vbi-0.1.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,473 @@
|
|
1
|
+
import tqdm
|
2
|
+
import numpy as np
|
3
|
+
from vbi.models.cupy.utils import *
|
4
|
+
|
5
|
+
|
6
|
+
class JR_sde:
|
7
|
+
"""
|
8
|
+
Jansen-Rit model cupy implementation.
|
9
|
+
|
10
|
+
.. list-table:: Parameters
|
11
|
+
:widths: 25 50 25
|
12
|
+
:header-rows: 1
|
13
|
+
|
14
|
+
* - Name
|
15
|
+
- Explanation
|
16
|
+
- Default Value
|
17
|
+
* - `A`
|
18
|
+
- Excitatory post synaptic potential amplitude.
|
19
|
+
- 3.25
|
20
|
+
* - `B`
|
21
|
+
- Inhibitory post synaptic potential amplitude.
|
22
|
+
- 22.0
|
23
|
+
* - `1/a`
|
24
|
+
- Time constant of the excitatory postsynaptic potential.
|
25
|
+
- a: 0.1 (1/a: 10.0)
|
26
|
+
* - `1/b`
|
27
|
+
- Time constant of the inhibitory postsynaptic potential.
|
28
|
+
- b: 0.05 (1/b: 20.0)
|
29
|
+
* - `C0`, `C1`
|
30
|
+
- Average numbers of synapses between excitatory populations. If array-like, it should be the shape of (`num_nodes`, `num_sim`).
|
31
|
+
- C0: 1.0 * 135.0, C1: 0.8 * 135.0
|
32
|
+
* - `C2`, `C3`
|
33
|
+
- Average numbers of synapses between inhibitory populations. If array-like, it should be the shape of (`num_nodes`, `num_sim`).
|
34
|
+
- C2: 0.25 * 135.0, C3: 0.25 * 135.0
|
35
|
+
* - `vmax`
|
36
|
+
- Maximum firing rate
|
37
|
+
- 0.005
|
38
|
+
* - `v`
|
39
|
+
- Potential at half of maximum firing rate
|
40
|
+
- 6.0
|
41
|
+
* - `r`
|
42
|
+
- Slope of sigmoid function at `v_0`
|
43
|
+
- 0.56
|
44
|
+
* - `G`
|
45
|
+
- Scaling the strength of network connections. If array-like, it should of length `num_sim`.
|
46
|
+
- 1.0
|
47
|
+
* - `mu`
|
48
|
+
- Mean of the noise
|
49
|
+
- 0.24
|
50
|
+
* - `noise_amp`
|
51
|
+
- Amplitude of the noise
|
52
|
+
- 0.01
|
53
|
+
* - `weights`
|
54
|
+
- Weight matrix of shape (`num_nodes`, `num_nodes`)
|
55
|
+
- None
|
56
|
+
* - `num_sim`
|
57
|
+
- Number of simulations
|
58
|
+
- 1
|
59
|
+
* - `dt`
|
60
|
+
- Time step
|
61
|
+
- 0.01
|
62
|
+
* - `t_end`
|
63
|
+
- End time of simulation
|
64
|
+
- 1000.0
|
65
|
+
* - `t_cut`
|
66
|
+
- Cut time
|
67
|
+
- 500.0
|
68
|
+
* - `engine`
|
69
|
+
- "cpu" or "gpu"
|
70
|
+
- "cpu"
|
71
|
+
* - `method`
|
72
|
+
- "heun" or "euler" method for integration
|
73
|
+
- "heun"
|
74
|
+
* - `seed`
|
75
|
+
- Random seed
|
76
|
+
- None
|
77
|
+
* - `initial_state`
|
78
|
+
- Initial state of the system of shape (`num_nodes`, `num_sim`)
|
79
|
+
- None
|
80
|
+
* - `same_initial_state`
|
81
|
+
- If True, all simulations have the same initial state
|
82
|
+
- False
|
83
|
+
* - `same_noise_per_sim`
|
84
|
+
- If True, all simulations have the same noise
|
85
|
+
- False
|
86
|
+
* - `decimate`
|
87
|
+
- Decimation factor for the output time series
|
88
|
+
- 1
|
89
|
+
* - `dtype`
|
90
|
+
- Data type to use for the simulation, `float` for `float64` or `f` for `float32`.
|
91
|
+
- "float"
|
92
|
+
"""
|
93
|
+
|
94
|
+
def __init__(self, par: dict = {}):
|
95
|
+
|
96
|
+
self.valid_parameters = list(self.get_default_parameters().keys())
|
97
|
+
self.check_parameters(par)
|
98
|
+
self._par = self.get_default_parameters()
|
99
|
+
self._par.update(par)
|
100
|
+
|
101
|
+
for item in self._par.items():
|
102
|
+
name = item[0]
|
103
|
+
value = item[1]
|
104
|
+
setattr(self, name, value)
|
105
|
+
|
106
|
+
self.xp = get_module(self.engine)
|
107
|
+
if self.seed is not None:
|
108
|
+
self.xp.random.seed(self.seed)
|
109
|
+
|
110
|
+
def __str__(self) -> str:
|
111
|
+
print("Jansen-Rit Model")
|
112
|
+
print("----------------")
|
113
|
+
for item in self._par.items():
|
114
|
+
name = item[0]
|
115
|
+
value = item[1]
|
116
|
+
print(f"{name} = {value}")
|
117
|
+
return ""
|
118
|
+
|
119
|
+
def __call__(self):
|
120
|
+
print("Jansen-Rit Model")
|
121
|
+
return self._par
|
122
|
+
|
123
|
+
def check_parameters(self, par):
|
124
|
+
"""
|
125
|
+
Check if the provided parameters are valid.
|
126
|
+
|
127
|
+
Parameters
|
128
|
+
----------
|
129
|
+
par : dict
|
130
|
+
Dictionary of parameters to check.
|
131
|
+
|
132
|
+
Raises
|
133
|
+
------
|
134
|
+
ValueError
|
135
|
+
If any parameter in `par` is not valid.
|
136
|
+
"""
|
137
|
+
for key in par.keys():
|
138
|
+
if key not in self.valid_parameters:
|
139
|
+
raise ValueError("Invalid parameter: " + key)
|
140
|
+
|
141
|
+
def set_initial_state(self):
|
142
|
+
self.initial_state = set_initial_state(
|
143
|
+
self.nn,
|
144
|
+
self.num_sim,
|
145
|
+
self.engine,
|
146
|
+
self.seed,
|
147
|
+
self.same_initial_state,
|
148
|
+
self.dtype,
|
149
|
+
)
|
150
|
+
|
151
|
+
def get_default_parameters(self) -> dict:
|
152
|
+
"""
|
153
|
+
Default parameters for the Jansen-Rit model
|
154
|
+
|
155
|
+
Parameters
|
156
|
+
----------
|
157
|
+
nn : int
|
158
|
+
number of nodes
|
159
|
+
|
160
|
+
Returns
|
161
|
+
-------
|
162
|
+
params : dict
|
163
|
+
default parameters
|
164
|
+
"""
|
165
|
+
params = {
|
166
|
+
"G": 1.0,
|
167
|
+
"A": 3.25,
|
168
|
+
"B": 22.0,
|
169
|
+
"v": 6.0,
|
170
|
+
"r": 0.56,
|
171
|
+
"v0": 6.0,
|
172
|
+
"vmax": 0.005,
|
173
|
+
"C0": 1.0 * 135.0,
|
174
|
+
"C1": 0.8 * 135.0,
|
175
|
+
"C2": 0.25 * 135.0,
|
176
|
+
"C3": 0.25 * 135.0,
|
177
|
+
"a": 0.1,
|
178
|
+
"b": 0.05,
|
179
|
+
"mu": 0.24,
|
180
|
+
"noise_amp": 0.01,
|
181
|
+
"decimate": 1,
|
182
|
+
"dt": 0.01,
|
183
|
+
"t_end": 1000.0,
|
184
|
+
"t_cut": 500.0,
|
185
|
+
"engine": "cpu",
|
186
|
+
"method": "heun",
|
187
|
+
"num_sim": 1,
|
188
|
+
"weights": None,
|
189
|
+
"dtype": "float",
|
190
|
+
"seed": None,
|
191
|
+
"initial_state": None,
|
192
|
+
"same_initial_state": False,
|
193
|
+
"same_noise_per_sim": False,
|
194
|
+
}
|
195
|
+
return params
|
196
|
+
|
197
|
+
def prepare_input(self):
|
198
|
+
"""Prepare input parameters for the Jansen-Rit model."""
|
199
|
+
|
200
|
+
self.G = self.xp.array(self.G)
|
201
|
+
assert self.weights is not None, "weights must be provided"
|
202
|
+
self.weights = self.xp.array(self.weights).T # ! check this
|
203
|
+
self.weights = move_data(self.weights, self.engine)
|
204
|
+
self.nn = self.num_nodes = self.weights.shape[0]
|
205
|
+
|
206
|
+
if self.initial_state is None:
|
207
|
+
self.set_initial_state()
|
208
|
+
|
209
|
+
self.C0 = prepare_vec(self.C0, self.num_sim, self.engine, self.dtype)
|
210
|
+
self.C1 = prepare_vec(self.C1, self.num_sim, self.engine, self.dtype)
|
211
|
+
self.C2 = prepare_vec(self.C2, self.num_sim, self.engine, self.dtype)
|
212
|
+
self.C3 = prepare_vec(self.C3, self.num_sim, self.engine, self.dtype)
|
213
|
+
assert self.t_cut < self.t_end, "t_cut must be smaller than t_end"
|
214
|
+
|
215
|
+
def S_(self, x):
|
216
|
+
"""
|
217
|
+
Compute the sigmoid function.
|
218
|
+
|
219
|
+
This function calculates the sigmoid of the input `x` using the parameters
|
220
|
+
`vmax`, `r`, and `v0`.
|
221
|
+
|
222
|
+
Parameters
|
223
|
+
----------
|
224
|
+
x : float or array-like
|
225
|
+
The input value(s) for which to compute the sigmoid function.
|
226
|
+
|
227
|
+
Returns
|
228
|
+
-------
|
229
|
+
float or array-like
|
230
|
+
The computed sigmoid value(s).
|
231
|
+
|
232
|
+
"""
|
233
|
+
return self.vmax / (1.0 + self.xp.exp(self.r * (self.v0 - x)))
|
234
|
+
|
235
|
+
def f_sys(self, x0, t):
|
236
|
+
"""
|
237
|
+
Compute the derivatives of the Jansen-Rit neural mass model.
|
238
|
+
|
239
|
+
Parameters
|
240
|
+
----------
|
241
|
+
x0 : array_like
|
242
|
+
Initial state vector of the system. It should have a shape of (6*nn, ns), where nn is the number of neurons and ns is the number of simulations.
|
243
|
+
t : float
|
244
|
+
Current time point (not used in the computation but required for compatibility with ODE solvers).
|
245
|
+
|
246
|
+
Returns
|
247
|
+
-------
|
248
|
+
dx : array_like
|
249
|
+
Derivatives of the state vector. It has the same shape as `x0`.
|
250
|
+
|
251
|
+
The function computes the derivatives of the state vector based on the Jansen-Rit model equations.
|
252
|
+
"""
|
253
|
+
|
254
|
+
nn = self.nn
|
255
|
+
ns = self.num_sim
|
256
|
+
mu = self.mu
|
257
|
+
G = self.G
|
258
|
+
C0 = self.C0
|
259
|
+
C1 = self.C1
|
260
|
+
C2 = self.C2
|
261
|
+
C3 = self.C3
|
262
|
+
A = self.A
|
263
|
+
B = self.B
|
264
|
+
a = self.a
|
265
|
+
b = self.b
|
266
|
+
Aa = A * a
|
267
|
+
Bb = B * b
|
268
|
+
bb = b * b
|
269
|
+
aa = a * a
|
270
|
+
SC = self.weights
|
271
|
+
_xp = self.xp
|
272
|
+
S = self.S_
|
273
|
+
|
274
|
+
x = x0[:nn, :]
|
275
|
+
y = x0[nn : 2 * nn, :]
|
276
|
+
z = x0[2 * nn : 3 * nn, :]
|
277
|
+
xp = x0[3 * nn : 4 * nn, :]
|
278
|
+
yp = x0[4 * nn : 5 * nn, :]
|
279
|
+
zp = x0[5 * nn : 6 * nn, :]
|
280
|
+
|
281
|
+
dx = _xp.zeros((6 * nn, ns))
|
282
|
+
couplings = S(SC.dot(y - z))
|
283
|
+
|
284
|
+
dx[0:nn, :] = xp
|
285
|
+
dx[nn : 2 * nn, :] = yp
|
286
|
+
dx[2 * nn : 3 * nn, :] = zp
|
287
|
+
dx[3 * nn : 4 * nn, :] = Aa * S(y - z) - 2 * a * xp - aa * x
|
288
|
+
dx[4 * nn : 5 * nn, :] = (
|
289
|
+
Aa * (mu + C1 * S(C0 * x) + G * couplings) - 2 * a * yp - aa * y
|
290
|
+
)
|
291
|
+
dx[5 * nn : 6 * nn, :] = Bb * C3 * S(C2 * x) - 2 * b * zp - bb * z
|
292
|
+
|
293
|
+
return dx
|
294
|
+
|
295
|
+
def euler(self, x0, t):
|
296
|
+
"""
|
297
|
+
Perform one step of the Euler-Maruyama method for stochastic differential equations.
|
298
|
+
|
299
|
+
Parameters
|
300
|
+
----------
|
301
|
+
x0 : array_like
|
302
|
+
The initial state of the system.
|
303
|
+
t : float
|
304
|
+
The current time.
|
305
|
+
|
306
|
+
Returns
|
307
|
+
-------
|
308
|
+
array_like
|
309
|
+
The updated state of the system after one Euler step.
|
310
|
+
"""
|
311
|
+
|
312
|
+
_xp = self.xp
|
313
|
+
nn = self.nn
|
314
|
+
ns = self.num_sim
|
315
|
+
dt = self.dt
|
316
|
+
sqrt_dt = np.sqrt(dt)
|
317
|
+
noise_amp = self.noise_amp
|
318
|
+
randn = _xp.random.randn
|
319
|
+
snps = self.same_noise_per_sim
|
320
|
+
|
321
|
+
dW = randn(nn, 1) if snps else randn(nn, ns)
|
322
|
+
dW = sqrt_dt * noise_amp * dW
|
323
|
+
|
324
|
+
x0 = x0 + dt * self.f_sys(x0, t)
|
325
|
+
x0[4 * nn : 5 * nn, :] += dW
|
326
|
+
|
327
|
+
return x0
|
328
|
+
|
329
|
+
def heun(self, x0, t):
|
330
|
+
"""
|
331
|
+
Perform a single step of the Heun's method for stochastic differential equations.
|
332
|
+
|
333
|
+
Parameters
|
334
|
+
----------
|
335
|
+
x0 : ndarray
|
336
|
+
The initial state of the system.
|
337
|
+
t : float
|
338
|
+
The current time.
|
339
|
+
|
340
|
+
Returns
|
341
|
+
-------
|
342
|
+
ndarray
|
343
|
+
The updated state of the system after one Heun step.
|
344
|
+
"""
|
345
|
+
|
346
|
+
nn = self.nn
|
347
|
+
ns = self.num_sim
|
348
|
+
dt = self.dt
|
349
|
+
_xp = self.xp
|
350
|
+
sqrt_dt = np.sqrt(dt)
|
351
|
+
noise_amp = self.noise_amp
|
352
|
+
randn = _xp.random.randn
|
353
|
+
snps = self.same_noise_per_sim
|
354
|
+
|
355
|
+
dW = randn(nn, 1) if snps else randn(nn, ns)
|
356
|
+
if snps:
|
357
|
+
dW = np.tile(dW, (1, ns))
|
358
|
+
dW = sqrt_dt * noise_amp * dW
|
359
|
+
|
360
|
+
k1 = self.f_sys(x0, t) * dt
|
361
|
+
x1 = x0 + k1
|
362
|
+
x1[4 * nn : 5 * nn, :] += dW
|
363
|
+
k2 = self.f_sys(x1, t + dt) * dt
|
364
|
+
x0 = x0 + (k1 + k2) / 2.0
|
365
|
+
x0[4 * nn : 5 * nn, :] += dW
|
366
|
+
|
367
|
+
return x0
|
368
|
+
|
369
|
+
def run(self, x0=None):
|
370
|
+
"""
|
371
|
+
Simulate the Jansen-Rit model.
|
372
|
+
|
373
|
+
Parameters
|
374
|
+
----------
|
375
|
+
|
376
|
+
x0: array [num_nodes, num_sim]
|
377
|
+
initial state
|
378
|
+
|
379
|
+
Returns
|
380
|
+
-------
|
381
|
+
|
382
|
+
dict: simulation results
|
383
|
+
t: array [n_step]
|
384
|
+
time
|
385
|
+
x: array [n_step, num_nodes, num_sim]
|
386
|
+
y1-y2 time series
|
387
|
+
|
388
|
+
"""
|
389
|
+
|
390
|
+
self.prepare_input()
|
391
|
+
x = self.initial_state if x0 is None else x0
|
392
|
+
self.integrator = self.euler if self.method == "euler" else self.heun
|
393
|
+
dt = self.dt
|
394
|
+
_xp = self.xp
|
395
|
+
nn = self.nn
|
396
|
+
ns = self.num_sim
|
397
|
+
decimate = self.decimate
|
398
|
+
t_end = self.t_end
|
399
|
+
t_cut = self.t_cut
|
400
|
+
|
401
|
+
tspan = _xp.arange(0, t_end, dt)
|
402
|
+
i_cut = int(_xp.where(tspan >= t_cut)[0][0])
|
403
|
+
|
404
|
+
n_step = int((len(tspan) - i_cut) / decimate)
|
405
|
+
y = np.zeros((n_step, nn, ns), dtype="f") # store in host
|
406
|
+
ii = 0
|
407
|
+
|
408
|
+
for i in tqdm.trange(len(tspan)):
|
409
|
+
|
410
|
+
x = self.integrator(x, tspan[i])
|
411
|
+
x_ = get_(x, self.engine, "f")
|
412
|
+
|
413
|
+
if (i >= i_cut) and (i % decimate == 0):
|
414
|
+
y[ii, :, :] = x_[nn : 2 * nn, :] - x_[2 * nn : 3 * nn, :]
|
415
|
+
ii += 1
|
416
|
+
|
417
|
+
t = get_(tspan[tspan >= t_cut][::decimate], self.engine, "f")
|
418
|
+
|
419
|
+
return {"t": t, "x": y}
|
420
|
+
|
421
|
+
|
422
|
+
def set_initial_state(
|
423
|
+
nn, ns, engine, seed=None, same_initial_state=False, dtype="float"
|
424
|
+
):
|
425
|
+
"""
|
426
|
+
set initial state for the Jansen-Rit model
|
427
|
+
|
428
|
+
Parameters
|
429
|
+
----------
|
430
|
+
nn: int
|
431
|
+
number of nodes
|
432
|
+
ns: int
|
433
|
+
number of simulations
|
434
|
+
engine: str
|
435
|
+
cpu or gpu
|
436
|
+
seed: int
|
437
|
+
random seed
|
438
|
+
same_initial_state: bool
|
439
|
+
if True, all simulations have the same initial state
|
440
|
+
dtype: str
|
441
|
+
data type
|
442
|
+
|
443
|
+
Returns
|
444
|
+
-------
|
445
|
+
y0: array [nn, ns]
|
446
|
+
initial state
|
447
|
+
|
448
|
+
"""
|
449
|
+
|
450
|
+
if seed is not None:
|
451
|
+
np.random.seed(seed)
|
452
|
+
|
453
|
+
if same_initial_state:
|
454
|
+
y0 = np.random.uniform(-1, 1, (nn, 1))
|
455
|
+
y1 = np.random.uniform(-500, 500, (nn, 1))
|
456
|
+
y2 = np.random.uniform(-50, 50, (nn, 1))
|
457
|
+
y3 = np.random.uniform(-6, 6, (nn, 1))
|
458
|
+
y4 = np.random.uniform(-20, 20, (nn, 1))
|
459
|
+
y5 = np.random.uniform(-500, 500, (nn, 1))
|
460
|
+
y = np.vstack((y0, y1, y2, y3, y4, y5))
|
461
|
+
y = np.tile(y, (1, ns))
|
462
|
+
y = move_data(y, engine)
|
463
|
+
else:
|
464
|
+
y0 = np.random.uniform(-1, 1, (nn, ns))
|
465
|
+
y1 = np.random.uniform(-500, 500, (nn, ns))
|
466
|
+
y2 = np.random.uniform(-50, 50, (nn, ns))
|
467
|
+
y3 = np.random.uniform(-6, 6, (nn, ns))
|
468
|
+
y4 = np.random.uniform(-20, 20, (nn, ns))
|
469
|
+
y5 = np.random.uniform(-500, 500, (nn, ns))
|
470
|
+
y = np.vstack((y0, y1, y2, y3, y4, y5))
|
471
|
+
y = move_data(y, engine)
|
472
|
+
|
473
|
+
return y.astype(dtype)
|
vbi/models/cupy/km.py
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
import tqdm
|
2
|
+
import numpy as np
|
3
|
+
from vbi.models.cupy.utils import *
|
4
|
+
|
5
|
+
|
6
|
+
class KM_sde:
|
7
|
+
|
8
|
+
valid_parameters = [
|
9
|
+
"num_sim", # number of simulations
|
10
|
+
"G", # global coupling strength
|
11
|
+
"dt", # time step
|
12
|
+
"noise_amp", # noise amplitude
|
13
|
+
"omega", # natural angular frequency
|
14
|
+
"weights", # weighted connection matrix
|
15
|
+
"seed",
|
16
|
+
"alpha", # frustration matrix
|
17
|
+
"t_initial", # initial time
|
18
|
+
"t_transition", # transition time
|
19
|
+
"t_end", # end time
|
20
|
+
"output", # output directory
|
21
|
+
"num_threads", # number of threads using openmp
|
22
|
+
"initial_state",
|
23
|
+
"type", # output times series data type
|
24
|
+
"engine", # cpu or gpu
|
25
|
+
]
|
26
|
+
|
27
|
+
def __init__(self, par={}) -> None:
|
28
|
+
|
29
|
+
self.check_parameters(par)
|
30
|
+
self._par = self.get_default_parameters()
|
31
|
+
self._par.update(par)
|
32
|
+
|
33
|
+
for item in self._par.items():
|
34
|
+
name = item[0]
|
35
|
+
value = item[1]
|
36
|
+
setattr(self, name, value)
|
37
|
+
|
38
|
+
self.xp = get_module(self.engine)
|
39
|
+
self.ns = self.num_sim
|
40
|
+
|
41
|
+
self.nn = self.num_nodes = self.weights.shape[0]
|
42
|
+
|
43
|
+
if self.seed is not None:
|
44
|
+
self.xp.random.seed(self.seed)
|
45
|
+
|
46
|
+
if self.initial_state is None:
|
47
|
+
self.INITIAL_STATE_SET = False
|
48
|
+
|
49
|
+
def set_initial_state(self):
|
50
|
+
self.INITIAL_STATE_SET = True
|
51
|
+
self.initial_state = set_initial_state(
|
52
|
+
self.nn, self.ns, self.xp, self.seed, self.same_initial_state)
|
53
|
+
|
54
|
+
def __str__(self) -> str:
|
55
|
+
print (f"Kuramoto model with noise (sde), {self.engine} implementation.")
|
56
|
+
print ("----------------")
|
57
|
+
for item in self._par.items():
|
58
|
+
name = item[0]
|
59
|
+
value = item[1]
|
60
|
+
print (f"{name} = {value}")
|
61
|
+
return ""
|
62
|
+
|
63
|
+
def __call__(self):
|
64
|
+
print(
|
65
|
+
f"Kuramoto model with noise (sde), {self.engine} implementation.")
|
66
|
+
return self._par
|
67
|
+
|
68
|
+
def get_default_parameters(self):
|
69
|
+
|
70
|
+
return {
|
71
|
+
"G": 1.0, # global coupling strength
|
72
|
+
"dt": 0.01, # time step
|
73
|
+
"noise_amp": 0.1, # noise amplitude
|
74
|
+
"weights": None, # weighted connection matrix
|
75
|
+
"omega": None, # natural angular frequency
|
76
|
+
"seed": None, # fix random seed for initial state
|
77
|
+
"t_initial": 0.0, # initial time
|
78
|
+
"t_transition": 0.0, # transition time
|
79
|
+
"t_end": 100.0, # end time
|
80
|
+
"num_threads": 1, # number of threads using openmp
|
81
|
+
"output": "output", # output directory
|
82
|
+
"initial_state": None, # initial state
|
83
|
+
"engine": "cpu", # cpu or gpu
|
84
|
+
"type": np.float32, # output times series data type
|
85
|
+
"alpha": None, # frustration matrix
|
86
|
+
"num_sim": 1, # number of simulations
|
87
|
+
"method": "heun", # integration method
|
88
|
+
"same_initial_state": False, # use the same initial state for all simulations
|
89
|
+
|
90
|
+
}
|
91
|
+
|
92
|
+
def check_parameters(self, par):
|
93
|
+
for key in par.keys():
|
94
|
+
if key not in self.valid_parameters:
|
95
|
+
raise ValueError(f"Invalid parameter: {key}")
|
96
|
+
|
97
|
+
def prepare_input(self):
|
98
|
+
|
99
|
+
assert(self.weights is not None), "weights must be provided"
|
100
|
+
assert (self.omega is not None), "omega must be provided"
|
101
|
+
|
102
|
+
self.G = self.xp.array(self.G)
|
103
|
+
self.weights = self.xp.array(self.weights).T #! Directed network
|
104
|
+
self.weights = self.weights.reshape(self.weights.shape+(1,))
|
105
|
+
self.weights = move_data(self.weights, self.engine)
|
106
|
+
|
107
|
+
if self.omega.ndim == 1:
|
108
|
+
self.omega = repmat_vec(self.omega, self.ns, self.engine)
|
109
|
+
|
110
|
+
def f_sys(self, x, t):
|
111
|
+
return self.omega + self.G * self.xp.sum(self.weights * self.xp.sin(x - x[:, None]), axis=1)
|
112
|
+
|
113
|
+
def euler(self, x, t):
|
114
|
+
''' Euler's method integration'''
|
115
|
+
coef = self.xp.sqrt(self.dt)
|
116
|
+
dW = self.xp.random.normal(0, self.noise_amp, size=x.shape)
|
117
|
+
return x + self.dt * self.f_sys(x, t) + coef * dW
|
118
|
+
|
119
|
+
def heun(self, x, t):
|
120
|
+
''' Heun's method integration'''
|
121
|
+
coef = self.xp.sqrt(self.dt)
|
122
|
+
dW = self.xp.random.normal(0, self.noise_amp, size=x.shape)
|
123
|
+
k1 = self.f_sys(x, t) * self.dt
|
124
|
+
tmp = x + k1 + coef * dW
|
125
|
+
k2 = self.f_sys(tmp, t + self.dt) * self.dt
|
126
|
+
return x + 0.5 * (k1 + k2) + coef * dW
|
127
|
+
|
128
|
+
def integrate(self, t, verbose=True):
|
129
|
+
''' Integrate the model'''
|
130
|
+
x = self.initial_state
|
131
|
+
xs = []
|
132
|
+
integrator = self.euler if self.method == "euler" else self.heun
|
133
|
+
n_transition = int(self.t_transition /
|
134
|
+
self.dt) if self.t_transition > 0 else 1
|
135
|
+
|
136
|
+
for it in tqdm.tqdm(range(1, len(t)), disable=not verbose, desc="Integrating"):
|
137
|
+
x = integrator(x, t[it])
|
138
|
+
if it >= n_transition:
|
139
|
+
if self.engine == "gpu":
|
140
|
+
xs.append(x.get())
|
141
|
+
else:
|
142
|
+
xs.append(x)
|
143
|
+
xs = np.asarray(xs).astype(self.type)
|
144
|
+
t = t[n_transition:]
|
145
|
+
|
146
|
+
return {"t": t, "x": xs}
|
147
|
+
|
148
|
+
def run(self, x0=None, verbose=True):
|
149
|
+
'''
|
150
|
+
run the model
|
151
|
+
|
152
|
+
Parameters
|
153
|
+
----------
|
154
|
+
par: dict
|
155
|
+
parameters
|
156
|
+
x0: array
|
157
|
+
initial state
|
158
|
+
verbose: bool
|
159
|
+
print progress bar
|
160
|
+
|
161
|
+
Returns
|
162
|
+
-------
|
163
|
+
dict
|
164
|
+
x: array [n_timesteps, n_regions, n_sim]
|
165
|
+
time series data
|
166
|
+
t: array
|
167
|
+
time points [n_timepoints]
|
168
|
+
|
169
|
+
'''
|
170
|
+
|
171
|
+
if x0 is not None:
|
172
|
+
self.initial_state = x0
|
173
|
+
self.INITIAL_STATE_SET = True
|
174
|
+
else:
|
175
|
+
self.set_initial_state()
|
176
|
+
if verbose:
|
177
|
+
print("Initial state set randomly.")
|
178
|
+
# self.check_parameters(par)
|
179
|
+
# for key in par.keys():
|
180
|
+
# setattr(self, key, par[key]['value'])
|
181
|
+
self.prepare_input()
|
182
|
+
t = self.xp.arange(self.t_initial, self.t_end, self.dt)
|
183
|
+
data = self.integrate(t, verbose=verbose)
|
184
|
+
|
185
|
+
data['t'] = data['t'].get() if self.engine == "gpu" else data['t']
|
186
|
+
return data
|
187
|
+
|
188
|
+
|
189
|
+
def set_initial_state(nn, ns=1, engine="cpu", seed=None, same_initial_state=False, dtype=float):
|
190
|
+
'''
|
191
|
+
set initial state
|
192
|
+
|
193
|
+
Parameters
|
194
|
+
----------
|
195
|
+
|
196
|
+
nn: int
|
197
|
+
number of nodes
|
198
|
+
ns: int
|
199
|
+
number of states
|
200
|
+
engine: str
|
201
|
+
cpu or gpu
|
202
|
+
seed: int
|
203
|
+
set random seed if not None
|
204
|
+
same_initial_state: bool
|
205
|
+
use the same initial state for all simulations
|
206
|
+
|
207
|
+
Returns
|
208
|
+
-------
|
209
|
+
x: array [nn, ns]
|
210
|
+
initial state
|
211
|
+
|
212
|
+
'''
|
213
|
+
|
214
|
+
|
215
|
+
if seed is not None:
|
216
|
+
np.random.seed(seed)
|
217
|
+
if same_initial_state:
|
218
|
+
x0 = np.random.uniform(0, 2*np.pi, size=nn)
|
219
|
+
x0 = repmat_vec(x0, ns, engine)
|
220
|
+
else:
|
221
|
+
x0 = np.random.uniform(0, 2*np.pi, size=(nn, ns))
|
222
|
+
x0 = move_data(x0, engine)
|
223
|
+
|
224
|
+
return x0.astype(dtype)
|