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
vbi/models/cupy/mpr.py
ADDED
@@ -0,0 +1,475 @@
|
|
1
|
+
import os
|
2
|
+
import tqdm
|
3
|
+
import logging
|
4
|
+
import numpy as np
|
5
|
+
from copy import copy
|
6
|
+
from vbi.models.cupy.utils import *
|
7
|
+
|
8
|
+
try:
|
9
|
+
import cupy as cp
|
10
|
+
except ImportError:
|
11
|
+
logging.warning("Cupy is not installed. Using Numpy instead.")
|
12
|
+
|
13
|
+
|
14
|
+
class Bold:
|
15
|
+
|
16
|
+
def __init__(self, par: dict = {}) -> None:
|
17
|
+
|
18
|
+
self._par = self.get_default_parameters()
|
19
|
+
self.valid_parameters = list(self._par.keys())
|
20
|
+
self.check_parameters(par)
|
21
|
+
self._par.update(par)
|
22
|
+
|
23
|
+
for item in self._par.items():
|
24
|
+
setattr(self, item[0], item[1])
|
25
|
+
self.update_dependent_parameters()
|
26
|
+
|
27
|
+
|
28
|
+
def get_default_parameters(self):
|
29
|
+
"""get balloon model parameters."""
|
30
|
+
|
31
|
+
vo = 0.08
|
32
|
+
theta = 40.3
|
33
|
+
TE = 0.04
|
34
|
+
Eo = 0.4
|
35
|
+
r0 = 25.0
|
36
|
+
epsilon = 0.34
|
37
|
+
k1 = 4.3 * theta * Eo * TE
|
38
|
+
k2 = epsilon * r0 * Eo * TE
|
39
|
+
k3 = 1 - epsilon
|
40
|
+
|
41
|
+
par = {
|
42
|
+
"kappa": 0.65,
|
43
|
+
"gamma": 0.41,
|
44
|
+
"tau": 0.98,
|
45
|
+
"alpha": 0.32,
|
46
|
+
"epsilon": epsilon,
|
47
|
+
"Eo": Eo,
|
48
|
+
"TE": TE,
|
49
|
+
"vo": vo,
|
50
|
+
"r0": r0,
|
51
|
+
"theta": theta,
|
52
|
+
"t_min": 0.0,
|
53
|
+
"rtol": 1e-5,
|
54
|
+
"atol": 1e-8,
|
55
|
+
"k1": k1,
|
56
|
+
"k2": k2,
|
57
|
+
"k3": k3
|
58
|
+
}
|
59
|
+
return par
|
60
|
+
|
61
|
+
def update_dependent_parameters(self):
|
62
|
+
self.k1 = 4.3 * self.theta * self.Eo * self.TE
|
63
|
+
self.k2 = self.epsilon * self.r0 * self.Eo * self.TE
|
64
|
+
self.k3 = 1 - self.epsilon
|
65
|
+
|
66
|
+
def check_parameters(self, par):
|
67
|
+
for key in par.keys():
|
68
|
+
if key not in self.valid_parameters:
|
69
|
+
raise ValueError(f"Invalid parameter {key:s} provided.")
|
70
|
+
|
71
|
+
def allocate_memory(self, xp, nn, ns, n_steps, bold_decimate, dtype):
|
72
|
+
|
73
|
+
self.s = xp.zeros((2, nn, ns), dtype=dtype)
|
74
|
+
self.f = xp.zeros((2, nn, ns), dtype=dtype)
|
75
|
+
self.ftilde = xp.zeros((2, nn, ns), dtype=dtype)
|
76
|
+
self.vtilde = xp.zeros((2, nn, ns), dtype=dtype)
|
77
|
+
self.qtilde = xp.zeros((2, nn, ns), dtype=dtype)
|
78
|
+
self.v = xp.zeros((2, nn, ns), dtype=dtype)
|
79
|
+
self.q = xp.zeros((2, nn, ns), dtype=dtype)
|
80
|
+
self.vv = np.zeros((n_steps // bold_decimate, nn, ns), dtype="f")
|
81
|
+
self.qq = np.zeros((n_steps // bold_decimate, nn, ns), dtype="f")
|
82
|
+
self.s[0] = 1
|
83
|
+
self.f[0] = 1
|
84
|
+
self.v[0] = 1
|
85
|
+
self.q[0] = 1
|
86
|
+
self.ftilde[0] = 0
|
87
|
+
self.vtilde[0] = 0
|
88
|
+
self.qtilde[0] = 0
|
89
|
+
|
90
|
+
def do_bold_step(self, r_in, dtt):
|
91
|
+
|
92
|
+
Eo = self.Eo
|
93
|
+
tau = self.tau
|
94
|
+
kappa = self.kappa
|
95
|
+
gamma = self.gamma
|
96
|
+
alpha = self.alpha
|
97
|
+
ialpha = 1 / alpha
|
98
|
+
|
99
|
+
v = self.v
|
100
|
+
q = self.q
|
101
|
+
s = self.s
|
102
|
+
f = self.f
|
103
|
+
ftilde = self.ftilde
|
104
|
+
vtilde = self.vtilde
|
105
|
+
qtilde = self.qtilde
|
106
|
+
|
107
|
+
s[1] = s[0] + dtt * (r_in - kappa * s[0] - gamma * (f[0] - 1))
|
108
|
+
f[0] = np.clip(f[0], 1, None)
|
109
|
+
ftilde[1] = ftilde[0] + dtt * (s[0] / f[0])
|
110
|
+
fv = v[0] ** ialpha # outflow
|
111
|
+
vtilde[1] = vtilde[0] + dtt * ((f[0] - fv) / (tau * v[0]))
|
112
|
+
q[0] = np.clip(q[0], 0.01, None)
|
113
|
+
ff = (1 - (1 - Eo) ** (1 / f[0])) / Eo # oxygen extraction
|
114
|
+
qtilde[1] = qtilde[0] + dtt * ((f[0] * ff - fv * q[0] / v[0]) / (tau * q[0]))
|
115
|
+
|
116
|
+
f[1] = np.exp(ftilde[1])
|
117
|
+
v[1] = np.exp(vtilde[1])
|
118
|
+
q[1] = np.exp(qtilde[1])
|
119
|
+
|
120
|
+
f[0] = f[1]
|
121
|
+
s[0] = s[1]
|
122
|
+
ftilde[0] = ftilde[1]
|
123
|
+
vtilde[0] = vtilde[1]
|
124
|
+
qtilde[0] = qtilde[1]
|
125
|
+
v[0] = v[1]
|
126
|
+
q[0] = q[1]
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
class MPR_sde:
|
131
|
+
"""
|
132
|
+
Montbrio-Pazo-Roxin model Cupy and Numpy implementation.
|
133
|
+
|
134
|
+
Parameters
|
135
|
+
----------
|
136
|
+
|
137
|
+
G: float. np.ndarray
|
138
|
+
global coupling strength
|
139
|
+
dt: float
|
140
|
+
time step
|
141
|
+
dt_bold: float
|
142
|
+
time step for Balloon model
|
143
|
+
J: float, np.ndarray
|
144
|
+
model parameter
|
145
|
+
eta: float, np.ndarray
|
146
|
+
model parameter
|
147
|
+
tau:
|
148
|
+
model parameter
|
149
|
+
delta:
|
150
|
+
model parameter
|
151
|
+
tr: float
|
152
|
+
repetition time of fMRI
|
153
|
+
noise_amp: float
|
154
|
+
amplitude of noise
|
155
|
+
same_noise_per_sim:
|
156
|
+
same noise for all simulations
|
157
|
+
iapp: float, np.ndarray
|
158
|
+
external input
|
159
|
+
t_start: float
|
160
|
+
initial time
|
161
|
+
t_cut: float
|
162
|
+
transition time
|
163
|
+
t_end: float
|
164
|
+
end time
|
165
|
+
num_nodes: int
|
166
|
+
number of nodes
|
167
|
+
weights: np.ndarray
|
168
|
+
weighted connection matrix
|
169
|
+
rv_decimate: int
|
170
|
+
sampling step from r and v
|
171
|
+
output: str
|
172
|
+
output directory
|
173
|
+
RECORD_TS: bool
|
174
|
+
store r and v time series
|
175
|
+
num_sim: int
|
176
|
+
number of simulations
|
177
|
+
method: str
|
178
|
+
integration method
|
179
|
+
engine: str
|
180
|
+
cpu or gpu
|
181
|
+
seed: int
|
182
|
+
seed for random number generator
|
183
|
+
dtype: str
|
184
|
+
float or f
|
185
|
+
initial_state: np.ndarray
|
186
|
+
initial state
|
187
|
+
same_initial_state: bool
|
188
|
+
same initial state for all simulations
|
189
|
+
|
190
|
+
"""
|
191
|
+
|
192
|
+
def __init__(self, par: dict = {}, Bpar:dict = {}) -> None:
|
193
|
+
|
194
|
+
self._par = self.get_default_parameters()
|
195
|
+
self.valid_parameters = list(self._par.keys())
|
196
|
+
self.check_parameters(par)
|
197
|
+
self._par.update(par)
|
198
|
+
|
199
|
+
for item in self._par.items():
|
200
|
+
name = item[0]
|
201
|
+
value = item[1]
|
202
|
+
setattr(self, name, value)
|
203
|
+
|
204
|
+
self.B = Bold(Bpar)
|
205
|
+
|
206
|
+
self.xp = get_module(self.engine)
|
207
|
+
if self.seed is not None:
|
208
|
+
self.xp.random.seed(self.seed)
|
209
|
+
|
210
|
+
os.makedirs(self.output, exist_ok=True)
|
211
|
+
|
212
|
+
def __call__(self):
|
213
|
+
print("Montbrió, Pazó, Roxin model.")
|
214
|
+
return self._par
|
215
|
+
|
216
|
+
def __str__(self) -> str:
|
217
|
+
print("Montbrió, Pazó, Roxin model.")
|
218
|
+
print("----------------")
|
219
|
+
for item in self._par.items():
|
220
|
+
name = item[0]
|
221
|
+
value = item[1]
|
222
|
+
print(f"{name} = {value}")
|
223
|
+
return ""
|
224
|
+
|
225
|
+
def set_initial_state(self):
|
226
|
+
self.initial_state = set_initial_state(
|
227
|
+
self.nn,
|
228
|
+
self.num_sim,
|
229
|
+
self.engine,
|
230
|
+
self.seed,
|
231
|
+
self.same_initial_state,
|
232
|
+
self.dtype,
|
233
|
+
)
|
234
|
+
|
235
|
+
def get_default_parameters(self):
|
236
|
+
|
237
|
+
par = {
|
238
|
+
"G": 0.72, # global coupling strength
|
239
|
+
"dt": 0.01, # dt for mpr model [ms]
|
240
|
+
"dt_bold": 0.001, # dt for Balloon model [s]
|
241
|
+
"J": 14.5, # model parameter
|
242
|
+
"eta": -4.6,
|
243
|
+
"tau": 1.0, # model parameter
|
244
|
+
"delta": 0.7, # model parameter
|
245
|
+
"tr": 500.0, # repetition time [ms]
|
246
|
+
"noise_amp": 0.037, # amplitude of noise
|
247
|
+
"same_noise_per_sim": False, # same noise for all simulations
|
248
|
+
"sti_apply": False, # apply stimulation
|
249
|
+
"iapp": 0.0, # external input
|
250
|
+
"t_start": 0.0, # initial time [ms]
|
251
|
+
"t_cut": 0, # transition time [ms]
|
252
|
+
"t_end": 300_000, # end time [ms]
|
253
|
+
"num_nodes": None, # number of nodes
|
254
|
+
"weights": None, # weighted connection matrix
|
255
|
+
"rv_decimate": 10, # sampling step from r and v
|
256
|
+
"output": "output", # output directory
|
257
|
+
"RECORD_RV": False, # store r and v time series
|
258
|
+
"RECORD_BOLD": True, # store BOLD signal
|
259
|
+
"RECORD_AVG_r": False, # store average_r
|
260
|
+
"num_sim": 1,
|
261
|
+
"method": "heun",
|
262
|
+
"engine": "cpu",
|
263
|
+
"seed": None,
|
264
|
+
"dtype": "float",
|
265
|
+
"initial_state": None,
|
266
|
+
"same_initial_state": False,
|
267
|
+
}
|
268
|
+
dt = par["dt"]
|
269
|
+
noise_amp = par["noise_amp"]
|
270
|
+
sigma_r = np.sqrt(dt) * np.sqrt(2 * noise_amp)
|
271
|
+
sigma_v = np.sqrt(dt) * np.sqrt(4 * noise_amp)
|
272
|
+
par["sigma_r"] = sigma_r
|
273
|
+
par["sigma_v"] = sigma_v
|
274
|
+
# par.update(self.get_balloon_parameters())
|
275
|
+
|
276
|
+
return par
|
277
|
+
|
278
|
+
def check_parameters(self, par):
|
279
|
+
for key in par.keys():
|
280
|
+
if key not in self.valid_parameters:
|
281
|
+
raise ValueError(f"Invalid parameter {key:s} provided.")
|
282
|
+
|
283
|
+
def prepare_input(self):
|
284
|
+
|
285
|
+
self.G = self.xp.array(self.G)
|
286
|
+
assert self.weights is not None, "weights must be provided"
|
287
|
+
self.weights = self.xp.array(self.weights).T # ! Directed network #!TODO: check
|
288
|
+
self.weights = move_data(self.weights, self.engine)
|
289
|
+
self.nn = self.num_nodes = self.weights.shape[0]
|
290
|
+
|
291
|
+
if self.initial_state is None:
|
292
|
+
self.set_initial_state()
|
293
|
+
|
294
|
+
self.t_end = self.t_end / 10.0
|
295
|
+
self.t_start = self.t_start / 10.0
|
296
|
+
self.t_cut = self.t_cut / 10.0
|
297
|
+
self.eta = prepare_vec(self.eta, self.num_sim, self.engine, self.dtype)
|
298
|
+
self.J = prepare_vec(self.J, self.num_sim, self.engine, self.dtype)
|
299
|
+
self.delta = prepare_vec(self.delta, self.num_sim, self.engine, self.dtype)
|
300
|
+
|
301
|
+
def f_mpr(self, x, t):
|
302
|
+
"""
|
303
|
+
MPR model
|
304
|
+
"""
|
305
|
+
|
306
|
+
G = self.G
|
307
|
+
J = self.J
|
308
|
+
xp = self.xp
|
309
|
+
weights = self.weights
|
310
|
+
tau = self.tau
|
311
|
+
eta = self.eta
|
312
|
+
iapp = self.iapp
|
313
|
+
ns = self.num_sim
|
314
|
+
delta = self.delta
|
315
|
+
nn = self.num_nodes
|
316
|
+
rtau = 1.0 / tau
|
317
|
+
x0 = x[:nn, :]
|
318
|
+
x1 = x[nn:, :]
|
319
|
+
dxdt = xp.zeros((2 * nn, ns)).astype(self.dtype)
|
320
|
+
tau_pi_inv = 1.0 / (tau * np.pi)
|
321
|
+
pi2 = np.pi * np.pi
|
322
|
+
tau2 = tau * tau
|
323
|
+
|
324
|
+
coupling = weights @ x0
|
325
|
+
dxdt[:nn, :] = rtau * (delta * tau_pi_inv + 2 * x0 * x1)
|
326
|
+
dxdt[nn:, :] = rtau * (
|
327
|
+
x1 * x1 + eta + iapp + J * tau * x0 - (pi2 * tau2 * x0 * x0) + G * coupling
|
328
|
+
)
|
329
|
+
|
330
|
+
return dxdt
|
331
|
+
|
332
|
+
def heunStochastic(self, y, t, dt):
|
333
|
+
"""Heun scheme to integrate MPR model with noise."""
|
334
|
+
|
335
|
+
xp = self.xp
|
336
|
+
nn = self.num_nodes
|
337
|
+
ns = self.num_sim
|
338
|
+
|
339
|
+
if not self.same_noise_per_sim:
|
340
|
+
dW_r = self.sigma_r * xp.random.randn(nn, ns)
|
341
|
+
dW_v = self.sigma_v * xp.random.randn(nn, ns)
|
342
|
+
else:
|
343
|
+
dW_r = self.sigma_r * xp.random.randn(nn, 1)
|
344
|
+
dW_v = self.sigma_v * xp.random.randn(nn, 1)
|
345
|
+
|
346
|
+
k1 = self.f_mpr(y, t)
|
347
|
+
tmp = y + dt * k1
|
348
|
+
tmp[:nn, :] += dW_r
|
349
|
+
tmp[nn:, :] += dW_v
|
350
|
+
|
351
|
+
k2 = self.f_mpr(tmp, t + dt)
|
352
|
+
y += 0.5 * dt * (k1 + k2)
|
353
|
+
y[:nn, :] += dW_r
|
354
|
+
y[:nn, :] = (y[:nn, :] > 0) * y[:nn, :] # set zero if negative
|
355
|
+
y[nn:, :] += dW_v
|
356
|
+
|
357
|
+
def sync_(self, engine="gpu"):
|
358
|
+
if engine == "gpu":
|
359
|
+
cp.cuda.Stream.null.synchronize()
|
360
|
+
else:
|
361
|
+
pass
|
362
|
+
|
363
|
+
def run(self, verbose=True):
|
364
|
+
|
365
|
+
self.prepare_input()
|
366
|
+
dt = self.dt
|
367
|
+
rv_decimate = self.rv_decimate
|
368
|
+
r_period = dt * 10 # extenting time
|
369
|
+
dtt = r_period / 1000.0 # in seconds
|
370
|
+
tr = self.tr
|
371
|
+
xp = self.xp
|
372
|
+
ns = self.num_sim
|
373
|
+
nn = self.num_nodes
|
374
|
+
engine = self.engine
|
375
|
+
|
376
|
+
n_steps = int(self.t_end / dt)
|
377
|
+
bold_decimate = int(np.round(tr / r_period))
|
378
|
+
|
379
|
+
B = self.B
|
380
|
+
B.allocate_memory(xp, nn, ns, n_steps, bold_decimate, self.dtype)
|
381
|
+
|
382
|
+
rv_curr = copy(self.initial_state)
|
383
|
+
rv_d = np.array([])
|
384
|
+
rv_t = np.array([])
|
385
|
+
avg_r = np.array([])
|
386
|
+
bold_d = np.array([])
|
387
|
+
bold_t = np.array([])
|
388
|
+
|
389
|
+
if self.RECORD_RV:
|
390
|
+
rv_d = np.zeros((n_steps // rv_decimate, 2 * nn, ns), dtype="f")
|
391
|
+
rv_t = np.zeros((n_steps // rv_decimate), dtype="f")
|
392
|
+
|
393
|
+
if self.RECORD_AVG_r:
|
394
|
+
avg_r = np.zeros((nn, ns), dtype="f")
|
395
|
+
|
396
|
+
cc = 0
|
397
|
+
for i in tqdm.trange(n_steps - 1, disable=not verbose, desc="Integrating"):
|
398
|
+
|
399
|
+
t_curr = i * dt
|
400
|
+
self.heunStochastic(rv_curr, t_curr, dt)
|
401
|
+
|
402
|
+
if ((i % rv_decimate) == 0) and ((i // rv_decimate) < rv_d.shape[0]):
|
403
|
+
|
404
|
+
if self.RECORD_RV:
|
405
|
+
rv_d[i // rv_decimate] = get_(rv_curr, engine, "f")
|
406
|
+
rv_t[i // rv_decimate] = t_curr
|
407
|
+
|
408
|
+
if self.RECORD_AVG_r and i > n_steps // 2:
|
409
|
+
avg_r += get_(rv_curr[:nn, :], engine, "f")
|
410
|
+
cc += 1
|
411
|
+
|
412
|
+
if self.RECORD_BOLD:
|
413
|
+
B.do_bold_step(rv_curr[:nn, :], dtt)
|
414
|
+
|
415
|
+
if (i % bold_decimate == 0) and ((i // bold_decimate) < B.vv.shape[0]):
|
416
|
+
B.vv[i // bold_decimate] = get_(B.v[1], engine, "f")
|
417
|
+
B.qq[i // bold_decimate] = get_(B.q[1], engine, "f")
|
418
|
+
|
419
|
+
if self.RECORD_BOLD:
|
420
|
+
bold_d = B.vo * (B.k1 * (1 - B.qq) + B.k2 * (1 - B.qq / B.vv) + B.k3 * (1 - B.vv))
|
421
|
+
bold_t = np.linspace(0, self.t_end - dt * bold_decimate, len(bold_d))
|
422
|
+
bold_d = bold_d[bold_t > self.t_cut, ...]
|
423
|
+
bold_t = bold_t[bold_t > self.t_cut]
|
424
|
+
bold_t = bold_t * 10.0
|
425
|
+
avg_r = avg_r / cc
|
426
|
+
|
427
|
+
if self.RECORD_RV:
|
428
|
+
rv_t = np.asarray(rv_t).astype("f")
|
429
|
+
rv_d = rv_d[rv_t > self.t_cut, ...]
|
430
|
+
rv_t = rv_t[rv_t > self.t_cut] * 10.0
|
431
|
+
|
432
|
+
return {
|
433
|
+
"rv_t": rv_t,
|
434
|
+
"rv_d": rv_d,
|
435
|
+
"fmri_t": bold_t,
|
436
|
+
"fmri_d": bold_d,
|
437
|
+
"avg_r": avg_r,
|
438
|
+
}
|
439
|
+
|
440
|
+
|
441
|
+
def set_initial_state(nn, ns, engine, seed=None, same_initial_state=False, dtype=float):
|
442
|
+
"""
|
443
|
+
Set initial state
|
444
|
+
|
445
|
+
Parameters
|
446
|
+
----------
|
447
|
+
nn : int
|
448
|
+
number of nodes
|
449
|
+
ns : int
|
450
|
+
number of simulations
|
451
|
+
engine : str
|
452
|
+
cpu or gpu
|
453
|
+
same_initial_condition : bool
|
454
|
+
same initial condition for all simulations
|
455
|
+
seed : int
|
456
|
+
random seed
|
457
|
+
dtype : str
|
458
|
+
float: float64
|
459
|
+
f : float32
|
460
|
+
"""
|
461
|
+
|
462
|
+
if seed is not None:
|
463
|
+
np.random.seed(seed)
|
464
|
+
|
465
|
+
if same_initial_state:
|
466
|
+
y0 = np.random.rand(2 * nn)
|
467
|
+
y0 = repmat_vec(y0, ns, engine)
|
468
|
+
else:
|
469
|
+
y0 = np.random.rand(2 * nn, ns)
|
470
|
+
y0 = move_data(y0, engine)
|
471
|
+
|
472
|
+
y0[:nn, :] = y0[:nn, :] * 1.5
|
473
|
+
y0[nn:, :] = y0[nn:, :] * 4 - 2
|
474
|
+
|
475
|
+
return y0.astype(dtype)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import warnings
|
2
|
+
from vbi.models.cupy.mpr import MPR_sde
|
3
|
+
|
4
|
+
warnings.warn(
|
5
|
+
"The module 'vbi.models.cupy.mpr_modified_bold' is deprecated and will be removed in a future version. "
|
6
|
+
"Please use 'vbi.models.cupy.mpr' instead.",
|
7
|
+
DeprecationWarning,
|
8
|
+
stacklevel=2,
|
9
|
+
)
|
10
|
+
|
11
|
+
# Re-export the object(s) from the new module for backward compatibility
|
12
|
+
__all__ = ["MPR_sde"]
|
vbi/models/cupy/utils.py
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
try:
|
4
|
+
import cupy as cp
|
5
|
+
except:
|
6
|
+
cp = None
|
7
|
+
|
8
|
+
|
9
|
+
def get_module(engine="gpu"):
|
10
|
+
"""
|
11
|
+
Switches the computational engine between GPU and CPU.
|
12
|
+
|
13
|
+
Parameters
|
14
|
+
----------
|
15
|
+
engine : str, optional
|
16
|
+
The computational engine to use. Can be either "gpu" or "cpu".
|
17
|
+
Default is "gpu".
|
18
|
+
|
19
|
+
Returns
|
20
|
+
-------
|
21
|
+
module
|
22
|
+
The appropriate array module based on the specified engine.
|
23
|
+
If "gpu", returns the CuPy module. If "cpu", returns the NumPy module.
|
24
|
+
|
25
|
+
Raises
|
26
|
+
------
|
27
|
+
ValueError
|
28
|
+
- If the specified engine is not "gpu" or "cpu".
|
29
|
+
- If CuPy is not installed.
|
30
|
+
"""
|
31
|
+
|
32
|
+
if engine == "gpu":
|
33
|
+
if cp is None:
|
34
|
+
raise ValueError("CuPy is not installed.")
|
35
|
+
else:
|
36
|
+
return cp.get_array_module(cp.array([1]))
|
37
|
+
else:
|
38
|
+
return np
|
39
|
+
# return cp.get_array_module(np.array([1]))
|
40
|
+
|
41
|
+
|
42
|
+
def tohost(x):
|
43
|
+
'''
|
44
|
+
move data to cpu if it is on gpu
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
x: array
|
49
|
+
data
|
50
|
+
|
51
|
+
Returns
|
52
|
+
-------
|
53
|
+
array
|
54
|
+
data moved to cpu
|
55
|
+
'''
|
56
|
+
if cp is not None and isinstance(x, cp.ndarray):
|
57
|
+
return cp.asnumpy(x)
|
58
|
+
return x
|
59
|
+
|
60
|
+
|
61
|
+
def todevice(x):
|
62
|
+
'''
|
63
|
+
move data to gpu
|
64
|
+
|
65
|
+
Parameters
|
66
|
+
----------
|
67
|
+
x: array
|
68
|
+
data
|
69
|
+
|
70
|
+
Returns
|
71
|
+
-------
|
72
|
+
array
|
73
|
+
data moved to gpu
|
74
|
+
|
75
|
+
'''
|
76
|
+
return cp.asarray(x)
|
77
|
+
|
78
|
+
|
79
|
+
def move_data(x, engine):
|
80
|
+
if engine == "cpu":
|
81
|
+
return tohost(x)
|
82
|
+
elif engine == "gpu":
|
83
|
+
return todevice(x)
|
84
|
+
|
85
|
+
|
86
|
+
def repmat_vec(vec, ns, engine):
|
87
|
+
'''
|
88
|
+
repeat vector ns times
|
89
|
+
|
90
|
+
Parameters
|
91
|
+
----------
|
92
|
+
vec: array 1d
|
93
|
+
vector to be repeated
|
94
|
+
ns: int
|
95
|
+
number of repetitions
|
96
|
+
engine: str
|
97
|
+
cpu or gpu
|
98
|
+
|
99
|
+
Returns
|
100
|
+
-------
|
101
|
+
vec: array [len(vec), n_sim]
|
102
|
+
repeated vector
|
103
|
+
|
104
|
+
'''
|
105
|
+
vec = np.tile(vec, (ns, 1)).T
|
106
|
+
vec = move_data(vec, engine)
|
107
|
+
return vec
|
108
|
+
|
109
|
+
|
110
|
+
def is_seq(x):
|
111
|
+
'''
|
112
|
+
check if x is a sequence
|
113
|
+
|
114
|
+
Parameters
|
115
|
+
----------
|
116
|
+
x: any
|
117
|
+
variable to be checked
|
118
|
+
|
119
|
+
Returns
|
120
|
+
-------
|
121
|
+
bool
|
122
|
+
True if x is a sequence
|
123
|
+
|
124
|
+
'''
|
125
|
+
return hasattr(x, '__iter__')
|
126
|
+
|
127
|
+
|
128
|
+
def prepare_vec(x, ns, engine, dtype="float"):
|
129
|
+
'''
|
130
|
+
check and prepare vector dimension and type
|
131
|
+
|
132
|
+
Parameters
|
133
|
+
----------
|
134
|
+
x: array 1d
|
135
|
+
vector to be prepared, if x is a scalar, only the type is changed
|
136
|
+
ns: int
|
137
|
+
number of simulations
|
138
|
+
engine: str
|
139
|
+
cpu or gpu
|
140
|
+
|
141
|
+
Returns
|
142
|
+
-------
|
143
|
+
x: array [len(x), n_sim]
|
144
|
+
prepared vector
|
145
|
+
|
146
|
+
'''
|
147
|
+
xp = get_module(engine)
|
148
|
+
|
149
|
+
if not is_seq(x):
|
150
|
+
return eval(f"{dtype}({x})")
|
151
|
+
else:
|
152
|
+
x = np.array(x)
|
153
|
+
if x.ndim == 1:
|
154
|
+
x = repmat_vec(x, ns, engine)
|
155
|
+
elif x.ndim == 2:
|
156
|
+
assert(x.shape[1] == ns), "second dimension of x must be equal to ns"
|
157
|
+
x = move_data(x, engine)
|
158
|
+
else:
|
159
|
+
raise ValueError("x.ndim must be 1 or 2")
|
160
|
+
return x.astype(dtype)
|
161
|
+
|
162
|
+
|
163
|
+
def get_(x, engine="cpu", dtype="f"):
|
164
|
+
"""
|
165
|
+
Parameters
|
166
|
+
----------
|
167
|
+
x : array-like
|
168
|
+
The input array to be converted.
|
169
|
+
engine : str, optional
|
170
|
+
The computation engine to use. If "gpu", the array is transferred from GPU to CPU. Defaults to "cpu".
|
171
|
+
dtype : str, optional
|
172
|
+
The desired data type for the output array. Defaults to "f".
|
173
|
+
|
174
|
+
Returns
|
175
|
+
-------
|
176
|
+
array-like
|
177
|
+
The converted array with the specified data type.
|
178
|
+
|
179
|
+
"""
|
180
|
+
|
181
|
+
if engine == "gpu":
|
182
|
+
return x.get().astype(dtype)
|
183
|
+
else:
|
184
|
+
return x.astype(dtype)
|
File without changes
|