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.
Files changed (121) hide show
  1. vbi/__init__.py +37 -0
  2. vbi/_version.py +17 -0
  3. vbi/dataset/__init__.py +0 -0
  4. vbi/dataset/connectivity_84/centers.txt +84 -0
  5. vbi/dataset/connectivity_84/centres.txt +84 -0
  6. vbi/dataset/connectivity_84/cortical.txt +84 -0
  7. vbi/dataset/connectivity_84/tract_lengths.txt +84 -0
  8. vbi/dataset/connectivity_84/weights.txt +84 -0
  9. vbi/dataset/connectivity_88/Aud_88.txt +88 -0
  10. vbi/dataset/connectivity_88/Bold.npz +0 -0
  11. vbi/dataset/connectivity_88/Labels.txt +17 -0
  12. vbi/dataset/connectivity_88/Region_labels.txt +88 -0
  13. vbi/dataset/connectivity_88/tract_lengths.txt +88 -0
  14. vbi/dataset/connectivity_88/weights.txt +88 -0
  15. vbi/feature_extraction/__init__.py +1 -0
  16. vbi/feature_extraction/calc_features.py +293 -0
  17. vbi/feature_extraction/features.json +535 -0
  18. vbi/feature_extraction/features.py +2124 -0
  19. vbi/feature_extraction/features_settings.py +374 -0
  20. vbi/feature_extraction/features_utils.py +1357 -0
  21. vbi/feature_extraction/infodynamics.jar +0 -0
  22. vbi/feature_extraction/utility.py +507 -0
  23. vbi/inference.py +98 -0
  24. vbi/models/__init__.py +0 -0
  25. vbi/models/cpp/__init__.py +0 -0
  26. vbi/models/cpp/_src/__init__.py +0 -0
  27. vbi/models/cpp/_src/__pycache__/mpr_sde.cpython-310.pyc +0 -0
  28. vbi/models/cpp/_src/_do.cpython-310-x86_64-linux-gnu.so +0 -0
  29. vbi/models/cpp/_src/_jr_sdde.cpython-310-x86_64-linux-gnu.so +0 -0
  30. vbi/models/cpp/_src/_jr_sde.cpython-310-x86_64-linux-gnu.so +0 -0
  31. vbi/models/cpp/_src/_km_sde.cpython-310-x86_64-linux-gnu.so +0 -0
  32. vbi/models/cpp/_src/_mpr_sde.cpython-310-x86_64-linux-gnu.so +0 -0
  33. vbi/models/cpp/_src/_vep.cpython-310-x86_64-linux-gnu.so +0 -0
  34. vbi/models/cpp/_src/_wc_ode.cpython-310-x86_64-linux-gnu.so +0 -0
  35. vbi/models/cpp/_src/bold.hpp +303 -0
  36. vbi/models/cpp/_src/do.hpp +167 -0
  37. vbi/models/cpp/_src/do.i +17 -0
  38. vbi/models/cpp/_src/do.py +467 -0
  39. vbi/models/cpp/_src/do_wrap.cxx +12811 -0
  40. vbi/models/cpp/_src/jr_sdde.hpp +352 -0
  41. vbi/models/cpp/_src/jr_sdde.i +19 -0
  42. vbi/models/cpp/_src/jr_sdde.py +688 -0
  43. vbi/models/cpp/_src/jr_sdde_wrap.cxx +18718 -0
  44. vbi/models/cpp/_src/jr_sde.hpp +264 -0
  45. vbi/models/cpp/_src/jr_sde.i +17 -0
  46. vbi/models/cpp/_src/jr_sde.py +470 -0
  47. vbi/models/cpp/_src/jr_sde_wrap.cxx +13406 -0
  48. vbi/models/cpp/_src/km_sde.hpp +158 -0
  49. vbi/models/cpp/_src/km_sde.i +19 -0
  50. vbi/models/cpp/_src/km_sde.py +671 -0
  51. vbi/models/cpp/_src/km_sde_wrap.cxx +17367 -0
  52. vbi/models/cpp/_src/makefile +52 -0
  53. vbi/models/cpp/_src/mpr_sde.hpp +327 -0
  54. vbi/models/cpp/_src/mpr_sde.i +19 -0
  55. vbi/models/cpp/_src/mpr_sde.py +711 -0
  56. vbi/models/cpp/_src/mpr_sde_wrap.cxx +18618 -0
  57. vbi/models/cpp/_src/utility.hpp +307 -0
  58. vbi/models/cpp/_src/vep.hpp +171 -0
  59. vbi/models/cpp/_src/vep.i +16 -0
  60. vbi/models/cpp/_src/vep.py +464 -0
  61. vbi/models/cpp/_src/vep_wrap.cxx +12968 -0
  62. vbi/models/cpp/_src/wc_ode.hpp +294 -0
  63. vbi/models/cpp/_src/wc_ode.i +19 -0
  64. vbi/models/cpp/_src/wc_ode.py +686 -0
  65. vbi/models/cpp/_src/wc_ode_wrap.cxx +24263 -0
  66. vbi/models/cpp/damp_oscillator.py +143 -0
  67. vbi/models/cpp/jansen_rit.py +543 -0
  68. vbi/models/cpp/km.py +187 -0
  69. vbi/models/cpp/mpr.py +289 -0
  70. vbi/models/cpp/vep.py +150 -0
  71. vbi/models/cpp/wc.py +216 -0
  72. vbi/models/cupy/__init__.py +0 -0
  73. vbi/models/cupy/bold.py +111 -0
  74. vbi/models/cupy/ghb.py +284 -0
  75. vbi/models/cupy/jansen_rit.py +473 -0
  76. vbi/models/cupy/km.py +224 -0
  77. vbi/models/cupy/mpr.py +475 -0
  78. vbi/models/cupy/mpr_modified_bold.py +12 -0
  79. vbi/models/cupy/utils.py +184 -0
  80. vbi/models/numba/__init__.py +0 -0
  81. vbi/models/numba/_ww_EI.py +444 -0
  82. vbi/models/numba/damp_oscillator.py +162 -0
  83. vbi/models/numba/ghb.py +208 -0
  84. vbi/models/numba/mpr.py +383 -0
  85. vbi/models/pytorch/__init__.py +0 -0
  86. vbi/models/pytorch/data/default_parameters.npz +0 -0
  87. vbi/models/pytorch/data/input/ROI_sim.mat +0 -0
  88. vbi/models/pytorch/data/input/fc_test.csv +68 -0
  89. vbi/models/pytorch/data/input/fc_train.csv +68 -0
  90. vbi/models/pytorch/data/input/fc_vali.csv +68 -0
  91. vbi/models/pytorch/data/input/fcd_test.mat +0 -0
  92. vbi/models/pytorch/data/input/fcd_test_high_window.mat +0 -0
  93. vbi/models/pytorch/data/input/fcd_test_low_window.mat +0 -0
  94. vbi/models/pytorch/data/input/fcd_train.mat +0 -0
  95. vbi/models/pytorch/data/input/fcd_vali.mat +0 -0
  96. vbi/models/pytorch/data/input/myelin.csv +68 -0
  97. vbi/models/pytorch/data/input/rsfc_gradient.csv +68 -0
  98. vbi/models/pytorch/data/input/run_label_testset.mat +0 -0
  99. vbi/models/pytorch/data/input/sc_test.csv +68 -0
  100. vbi/models/pytorch/data/input/sc_train.csv +68 -0
  101. vbi/models/pytorch/data/input/sc_vali.csv +68 -0
  102. vbi/models/pytorch/data/obs_kong0.npz +0 -0
  103. vbi/models/pytorch/ww_sde_kong.py +570 -0
  104. vbi/models/tvbk/__init__.py +9 -0
  105. vbi/models/tvbk/tvbk_wrapper.py +166 -0
  106. vbi/models/tvbk/utils.py +72 -0
  107. vbi/papers/__init__.py +0 -0
  108. vbi/papers/pavlides_pcb_2015/pavlides.py +211 -0
  109. vbi/tests/__init__.py +0 -0
  110. vbi/tests/_test_mpr_nb.py +36 -0
  111. vbi/tests/test_features.py +355 -0
  112. vbi/tests/test_ghb_cupy.py +90 -0
  113. vbi/tests/test_mpr_cupy.py +49 -0
  114. vbi/tests/test_mpr_numba.py +84 -0
  115. vbi/tests/test_suite.py +19 -0
  116. vbi/utils.py +402 -0
  117. vbi-0.1.3.dist-info/METADATA +166 -0
  118. vbi-0.1.3.dist-info/RECORD +121 -0
  119. vbi-0.1.3.dist-info/WHEEL +5 -0
  120. vbi-0.1.3.dist-info/licenses/LICENSE +201 -0
  121. 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)