nltools 0.6.0.dev0__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.
- nltools/__init__.py +55 -0
- nltools/algorithms/__init__.py +90 -0
- nltools/algorithms/alignment/__init__.py +21 -0
- nltools/algorithms/alignment/procrustes.py +565 -0
- nltools/algorithms/alignment/srm.py +758 -0
- nltools/algorithms/backends.py +1059 -0
- nltools/algorithms/corrections.py +177 -0
- nltools/algorithms/decoding.py +327 -0
- nltools/algorithms/inference/__init__.py +50 -0
- nltools/algorithms/inference/bootstrap.py +1386 -0
- nltools/algorithms/inference/correlation.py +373 -0
- nltools/algorithms/inference/intersubject.py +422 -0
- nltools/algorithms/inference/isc.py +1554 -0
- nltools/algorithms/inference/matrix.py +602 -0
- nltools/algorithms/inference/one_sample.py +288 -0
- nltools/algorithms/inference/random.py +122 -0
- nltools/algorithms/inference/timeseries.py +347 -0
- nltools/algorithms/inference/two_sample.py +212 -0
- nltools/algorithms/inference/utils.py +58 -0
- nltools/algorithms/inference/validation.py +282 -0
- nltools/algorithms/neighborhoods.py +207 -0
- nltools/algorithms/outliers.py +308 -0
- nltools/algorithms/regression.py +83 -0
- nltools/algorithms/signal.py +303 -0
- nltools/algorithms/similarity.py +234 -0
- nltools/algorithms/validation.py +151 -0
- nltools/cross_validation.py +72 -0
- nltools/data/__init__.py +30 -0
- nltools/data/adjacency/__init__.py +875 -0
- nltools/data/adjacency/io.py +111 -0
- nltools/data/adjacency/modeling.py +569 -0
- nltools/data/adjacency/plotting.py +174 -0
- nltools/data/adjacency/state.py +349 -0
- nltools/data/adjacency/stats.py +596 -0
- nltools/data/adjacency/utils.py +79 -0
- nltools/data/atlases/__init__.py +23 -0
- nltools/data/atlases/labeling.py +158 -0
- nltools/data/atlases/loading.py +76 -0
- nltools/data/atlases/registry.py +96 -0
- nltools/data/atlases/reporting.py +456 -0
- nltools/data/braindata/__init__.py +2170 -0
- nltools/data/braindata/analysis.py +1381 -0
- nltools/data/braindata/bootstrap.py +398 -0
- nltools/data/braindata/io.py +896 -0
- nltools/data/braindata/modeling.py +594 -0
- nltools/data/braindata/plotting.py +501 -0
- nltools/data/braindata/prediction.py +1250 -0
- nltools/data/braindata/utils.py +348 -0
- nltools/data/braindata/validation.py +197 -0
- nltools/data/braindata/viewer.js +266 -0
- nltools/data/braindata/viewer.py +770 -0
- nltools/data/combine.py +27 -0
- nltools/data/designmatrix/__init__.py +1032 -0
- nltools/data/designmatrix/append.py +518 -0
- nltools/data/designmatrix/diagnostics.py +248 -0
- nltools/data/designmatrix/io.py +356 -0
- nltools/data/designmatrix/plotting.py +291 -0
- nltools/data/designmatrix/regressors.py +463 -0
- nltools/data/designmatrix/transforms.py +200 -0
- nltools/data/designmatrix/utils.py +350 -0
- nltools/data/ownership.py +129 -0
- nltools/data/results.py +291 -0
- nltools/data/roc/__init__.py +398 -0
- nltools/data/simulator/__init__.py +927 -0
- nltools/data/simulator/haxby.py +124 -0
- nltools/data/validation.py +83 -0
- nltools/datasets.py +218 -0
- nltools/io/__init__.py +10 -0
- nltools/io/events.py +67 -0
- nltools/io/h5.py +246 -0
- nltools/mask.py +403 -0
- nltools/models/__init__.py +11 -0
- nltools/models/glm.py +543 -0
- nltools/models/results.py +49 -0
- nltools/models/ridge.py +1303 -0
- nltools/models/validation.py +26 -0
- nltools/plotting/__init__.py +32 -0
- nltools/plotting/adjacency.py +421 -0
- nltools/plotting/brain.py +669 -0
- nltools/plotting/decomposition.py +111 -0
- nltools/plotting/prediction.py +110 -0
- nltools/resources/covariates_example.csv +161 -0
- nltools/resources/onsets_example.csv +40 -0
- nltools/templates/__init__.py +51 -0
- nltools/templates/config.py +144 -0
- nltools/templates/fetch.py +260 -0
- nltools/templates/matching.py +183 -0
- nltools/templates/paths.py +106 -0
- nltools/templates/registry.py +25 -0
- nltools/utils.py +230 -0
- nltools/version.py +13 -0
- nltools-0.6.0.dev0.dist-info/METADATA +95 -0
- nltools-0.6.0.dev0.dist-info/RECORD +95 -0
- nltools-0.6.0.dev0.dist-info/WHEEL +4 -0
- nltools-0.6.0.dev0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Bootstrap resampling for `BrainData` — aggregate statistics and Ridge model statistics.
|
|
2
|
+
|
|
3
|
+
`BrainData.bootstrap` delegates here.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from .utils import _result_from_array
|
|
9
|
+
|
|
10
|
+
#: Statistics that reduce `bd.data` directly and need no fitted model.
|
|
11
|
+
SIMPLE_STATS = ("mean", "median", "std", "sum", "min", "max")
|
|
12
|
+
|
|
13
|
+
#: Statistics that resample a fitted `_Ridge` and therefore need explicit
|
|
14
|
+
#: training features. `'predict'` additionally needs evaluation features.
|
|
15
|
+
FITTED_STATS = ("weights", "predict")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _bootstrap(
|
|
19
|
+
bd,
|
|
20
|
+
statistic,
|
|
21
|
+
*,
|
|
22
|
+
X=None,
|
|
23
|
+
X_test=None,
|
|
24
|
+
n_samples=5000,
|
|
25
|
+
confidence_level=0.95,
|
|
26
|
+
device="cpu",
|
|
27
|
+
memory_budget_gb=None,
|
|
28
|
+
return_samples=False,
|
|
29
|
+
n_jobs=-1,
|
|
30
|
+
random_state=None,
|
|
31
|
+
progress_bar=False,
|
|
32
|
+
):
|
|
33
|
+
"""Bootstrap a statistic and its uncertainty, on CPU workers or a GPU.
|
|
34
|
+
|
|
35
|
+
Resamples observations with replacement and aggregates the replicates as
|
|
36
|
+
they complete, into a running Welford variance plus just enough retained
|
|
37
|
+
order statistics per output element to reproduce the exact percentile
|
|
38
|
+
interval. What the run holds is that retained tail — about
|
|
39
|
+
`(1 - confidence_level)` of the replicates per element — plus one dispatch
|
|
40
|
+
window, rather than all `n_samples` maps. This is memory-efficient, not
|
|
41
|
+
constant-memory: the tail still grows with `n_samples`, and
|
|
42
|
+
`return_samples=True` keeps the whole distribution.
|
|
43
|
+
|
|
44
|
+
A Ridge bootstrap resamples the explicitly supplied training `X` together
|
|
45
|
+
with `bd.data`, using the same row indices for every feature space, and
|
|
46
|
+
refits with the fitted model's selected `alpha_` — and, for a banded model,
|
|
47
|
+
its `feature_space_weights_` — held fixed. It never reruns cross-validation
|
|
48
|
+
or the banded random search.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
bd (BrainData): Data to resample.
|
|
52
|
+
statistic (str): Statistic to bootstrap. Basic aggregates: ``'mean'``,
|
|
53
|
+
``'median'``, ``'std'``, ``'sum'``, ``'min'``, ``'max'``. Model
|
|
54
|
+
statistics (require a fitted `_Ridge`): ``'weights'`` or
|
|
55
|
+
``'predict'``.
|
|
56
|
+
X (np.ndarray | Mapping[str, np.ndarray] | None): Training features in
|
|
57
|
+
their original row order, required by both Ridge statistics and
|
|
58
|
+
rejected by the basic ones. A matrix for ordinary Ridge; a mapping
|
|
59
|
+
with exactly the fitted feature-space names for banded Ridge.
|
|
60
|
+
X_test (np.ndarray | Mapping[str, np.ndarray] | None): Evaluation
|
|
61
|
+
features for ``statistic='predict'``, in the same structure as `X`.
|
|
62
|
+
It may have any row count.
|
|
63
|
+
n_samples (int): Number of bootstrap replicates, at least two. Default
|
|
64
|
+
``5000``.
|
|
65
|
+
confidence_level (float): Confidence level of the reported interval,
|
|
66
|
+
strictly between zero and one. Default ``0.95``.
|
|
67
|
+
device (str): Compute device for Ridge refits: ``'cpu'`` (default) or
|
|
68
|
+
``'gpu'`` (PyTorch on CUDA/MPS; raises if neither is available).
|
|
69
|
+
Basic statistics reject ``'gpu'``.
|
|
70
|
+
memory_budget_gb (float | None): Working-memory budget in GB. It
|
|
71
|
+
governs the output preflight and CPU-worker planning for every
|
|
72
|
+
statistic, and GPU batch sizing for the Ridge ones. ``None``
|
|
73
|
+
(default) measures the device.
|
|
74
|
+
return_samples (bool): Retain and return every replicate. Default
|
|
75
|
+
``False``. It changes retention only, never interval semantics.
|
|
76
|
+
n_jobs (int): CPU worker ceiling. Default ``-1`` (all cores); the
|
|
77
|
+
planner may use fewer.
|
|
78
|
+
random_state (int | None): Random seed for reproducibility.
|
|
79
|
+
progress_bar (bool): Show a progress bar. Default ``False``.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
BootstrapResult: `estimate` (the statistic on the unresampled full
|
|
83
|
+
sample), `standard_error`, `ci_lower` and `ci_upper` as `BrainData`
|
|
84
|
+
maps of identical shape, plus `samples` as a NumPy array with the
|
|
85
|
+
bootstrap axis first when ``return_samples=True``.
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
ValueError: If `statistic` is unknown, a basic statistic is given `X`,
|
|
89
|
+
`X_test`, or ``device='gpu'``, a Ridge statistic is missing `X` (or
|
|
90
|
+
`X_test` for ``'predict'``), the fitted model is not a `_Ridge`, `X`
|
|
91
|
+
does not match the fitted feature structure and observation count,
|
|
92
|
+
an argument is out of range, or the retained output cannot fit the
|
|
93
|
+
memory budget.
|
|
94
|
+
|
|
95
|
+
Examples:
|
|
96
|
+
```python
|
|
97
|
+
boot = brain.bootstrap('mean', n_samples=1000)
|
|
98
|
+
boot.estimate.plot()
|
|
99
|
+
|
|
100
|
+
brain.fit(model='ridge', X=features, ridge_alpha=1.0)
|
|
101
|
+
boot = brain.bootstrap('weights', X=features, n_samples=1000)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Note:
|
|
105
|
+
This is an IID row bootstrap: rows must be exchangeable for the
|
|
106
|
+
interval to mean anything. Fitting retains no hidden copy of the
|
|
107
|
+
training features, so omitting `X` raises even when the same features
|
|
108
|
+
were supplied to `fit`.
|
|
109
|
+
"""
|
|
110
|
+
from nltools.algorithms.inference.bootstrap import (
|
|
111
|
+
_bootstrap_simple_cpu_parallel,
|
|
112
|
+
_bootstrap_ridge_weights_cpu_parallel,
|
|
113
|
+
_bootstrap_ridge_predict_cpu_parallel,
|
|
114
|
+
_bootstrap_ridge_weights_gpu_batched,
|
|
115
|
+
_bootstrap_ridge_predict_gpu_batched,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
_validate_statistic(statistic)
|
|
119
|
+
_validate_device(device)
|
|
120
|
+
|
|
121
|
+
if statistic in SIMPLE_STATS:
|
|
122
|
+
_reject_model_arguments(statistic, X, X_test, device)
|
|
123
|
+
result = _bootstrap_simple_cpu_parallel(
|
|
124
|
+
bd.data,
|
|
125
|
+
method=statistic,
|
|
126
|
+
n_samples=n_samples,
|
|
127
|
+
confidence_level=confidence_level,
|
|
128
|
+
memory_budget_gb=memory_budget_gb,
|
|
129
|
+
return_samples=return_samples,
|
|
130
|
+
n_jobs=n_jobs,
|
|
131
|
+
random_state=random_state,
|
|
132
|
+
progress_bar=progress_bar,
|
|
133
|
+
)
|
|
134
|
+
return _as_bootstrap_result(bd, result)
|
|
135
|
+
|
|
136
|
+
model = _fitted_ridge(bd, statistic)
|
|
137
|
+
spaces = _training_feature_spaces(model, X, statistic, bd.shape[0])
|
|
138
|
+
if statistic == "weights":
|
|
139
|
+
if X_test is not None:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
"bootstrap('weights') summarizes training coefficients and "
|
|
142
|
+
"takes no X_test; use statistic='predict' to evaluate on new rows."
|
|
143
|
+
)
|
|
144
|
+
elif X_test is None:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
"X_test parameter required for bootstrap(statistic='predict'). "
|
|
147
|
+
"Provide test features: bootstrap('predict', X=..., X_test=...)"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
backend = _resolve_device(device)
|
|
151
|
+
alpha = model.alpha_
|
|
152
|
+
feature_space_weights = model.feature_space_weights_
|
|
153
|
+
coefficients = np.asarray(model.coef_, dtype=np.float64)
|
|
154
|
+
|
|
155
|
+
shared = {
|
|
156
|
+
"n_samples": n_samples,
|
|
157
|
+
"confidence_level": confidence_level,
|
|
158
|
+
"feature_space_weights": feature_space_weights,
|
|
159
|
+
"return_samples": return_samples,
|
|
160
|
+
"random_state": random_state,
|
|
161
|
+
"progress_bar": progress_bar,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if statistic == "weights":
|
|
165
|
+
if backend is None:
|
|
166
|
+
result = _bootstrap_ridge_weights_cpu_parallel(
|
|
167
|
+
spaces,
|
|
168
|
+
bd.data,
|
|
169
|
+
alpha,
|
|
170
|
+
coefficients,
|
|
171
|
+
memory_budget_gb=memory_budget_gb,
|
|
172
|
+
n_jobs=n_jobs,
|
|
173
|
+
**shared,
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
result = _bootstrap_ridge_weights_gpu_batched(
|
|
177
|
+
spaces,
|
|
178
|
+
bd.data,
|
|
179
|
+
alpha,
|
|
180
|
+
coefficients,
|
|
181
|
+
backend=backend,
|
|
182
|
+
memory_budget_gb=memory_budget_gb,
|
|
183
|
+
**shared,
|
|
184
|
+
)
|
|
185
|
+
return _as_bootstrap_result(bd, result)
|
|
186
|
+
|
|
187
|
+
# `_Ridge` owns the alignment of both feature arguments, through the one
|
|
188
|
+
# seam this facade uses; the engines concatenate in coefficient order.
|
|
189
|
+
test_spaces = model._aligned_feature_spaces(X_test)
|
|
190
|
+
predictions = _stacked(test_spaces) @ coefficients
|
|
191
|
+
|
|
192
|
+
if backend is None:
|
|
193
|
+
result = _bootstrap_ridge_predict_cpu_parallel(
|
|
194
|
+
spaces,
|
|
195
|
+
bd.data,
|
|
196
|
+
test_spaces,
|
|
197
|
+
alpha,
|
|
198
|
+
predictions,
|
|
199
|
+
memory_budget_gb=memory_budget_gb,
|
|
200
|
+
n_jobs=n_jobs,
|
|
201
|
+
**shared,
|
|
202
|
+
)
|
|
203
|
+
else:
|
|
204
|
+
result = _bootstrap_ridge_predict_gpu_batched(
|
|
205
|
+
spaces,
|
|
206
|
+
bd.data,
|
|
207
|
+
test_spaces,
|
|
208
|
+
alpha,
|
|
209
|
+
predictions,
|
|
210
|
+
backend=backend,
|
|
211
|
+
memory_budget_gb=memory_budget_gb,
|
|
212
|
+
**shared,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return _as_bootstrap_result(bd, result)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _validate_statistic(statistic):
|
|
219
|
+
"""Reject anything outside the closed set of eight supported statistics.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
statistic (str): The requested statistic.
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
ValueError: If `statistic` is not one of the eight names.
|
|
226
|
+
"""
|
|
227
|
+
if statistic in SIMPLE_STATS or statistic in FITTED_STATS:
|
|
228
|
+
return
|
|
229
|
+
raise ValueError(
|
|
230
|
+
f"Unsupported statistic '{statistic}'. "
|
|
231
|
+
f"Supported basic statistics: {list(SIMPLE_STATS)}. "
|
|
232
|
+
f"Supported fitted model statistics: {list(FITTED_STATS)}. "
|
|
233
|
+
f"For fitted statistics, you must call .fit() first."
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _validate_device(device):
|
|
238
|
+
"""Reject a device name before the statistic decides what to do with it.
|
|
239
|
+
|
|
240
|
+
The range checks on `n_samples`, `confidence_level` and `memory_budget_gb`
|
|
241
|
+
belong to the engine, which every path reaches.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
device (str): Requested compute device.
|
|
245
|
+
|
|
246
|
+
Raises:
|
|
247
|
+
ValueError: If `device` is not ``'cpu'`` or ``'gpu'``.
|
|
248
|
+
"""
|
|
249
|
+
if device not in ("cpu", "gpu"):
|
|
250
|
+
raise ValueError(f"device must be 'cpu' or 'gpu', got {device!r}")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _reject_model_arguments(statistic, X, X_test, device):
|
|
254
|
+
"""Reject the arguments a basic statistic has no use for.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
statistic (str): The requested basic statistic, for the message.
|
|
258
|
+
X (Any): Training features, which must be absent.
|
|
259
|
+
X_test (Any): Evaluation features, which must be absent.
|
|
260
|
+
device (str): Requested compute device, which must be ``'cpu'``.
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
ValueError: If features were supplied, or a GPU was requested.
|
|
264
|
+
"""
|
|
265
|
+
if X is not None or X_test is not None:
|
|
266
|
+
raise ValueError(
|
|
267
|
+
f"bootstrap({statistic!r}) reduces the data itself and takes no "
|
|
268
|
+
f"features; X and X_test belong to statistic='weights' or 'predict'."
|
|
269
|
+
)
|
|
270
|
+
if device != "cpu":
|
|
271
|
+
raise ValueError(
|
|
272
|
+
f"bootstrap({statistic!r}) is a NumPy reduction over rows and runs "
|
|
273
|
+
f"on the CPU; device='gpu' applies only to the Ridge statistics."
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _stacked(spaces):
|
|
278
|
+
"""Concatenate aligned feature spaces into one matrix in coefficient order.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
spaces (Sequence[np.ndarray]): One matrix per fitted feature space.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
np.ndarray: A `(n_rows, n_features)` float64 matrix.
|
|
285
|
+
"""
|
|
286
|
+
matrices = [np.asarray(space, dtype=np.float64) for space in spaces]
|
|
287
|
+
return matrices[0] if len(matrices) == 1 else np.concatenate(matrices, axis=1)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _fitted_ridge(bd, statistic):
|
|
291
|
+
"""Return the fitted `_Ridge` a model bootstrap needs, or raise.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
bd (BrainData): The object being resampled.
|
|
295
|
+
statistic (str): The requested model statistic, for the error message.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
_Ridge: The fitted estimator.
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
ValueError: If nothing is fitted, or the fit is not a `_Ridge`.
|
|
302
|
+
"""
|
|
303
|
+
from nltools.models import _Ridge
|
|
304
|
+
|
|
305
|
+
model = getattr(bd, "model_", None)
|
|
306
|
+
if model is None or not getattr(model, "is_fitted_", False):
|
|
307
|
+
raise ValueError(
|
|
308
|
+
f"Must call .fit(model='ridge', X=features) before bootstrap('{statistic}')"
|
|
309
|
+
)
|
|
310
|
+
if not isinstance(model, _Ridge):
|
|
311
|
+
raise ValueError(
|
|
312
|
+
f"bootstrap('{statistic}') only supports a fitted Ridge, but this "
|
|
313
|
+
f"BrainData holds a fitted {type(model).__name__}."
|
|
314
|
+
)
|
|
315
|
+
return model
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _training_feature_spaces(model, X, statistic, n_obs):
|
|
319
|
+
"""Align the explicit training `X` to the fitted feature-space order.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
model (Ridge): The fitted estimator.
|
|
323
|
+
X (np.ndarray | Mapping | None): Caller-supplied training features.
|
|
324
|
+
statistic (str): The requested model statistic, for the error message.
|
|
325
|
+
n_obs (int): Observation count of the `BrainData` being resampled.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
list[np.ndarray]: One matrix per fitted feature space, in coefficient
|
|
329
|
+
order.
|
|
330
|
+
|
|
331
|
+
Raises:
|
|
332
|
+
ValueError: If `X` is missing, does not match the fitted feature
|
|
333
|
+
structure, or has a different number of rows than the response.
|
|
334
|
+
"""
|
|
335
|
+
if X is None:
|
|
336
|
+
raise ValueError(
|
|
337
|
+
f"bootstrap('{statistic}') requires the training features as X=. "
|
|
338
|
+
f"Fitting keeps no copy of them, so pass the same features you "
|
|
339
|
+
f"passed to fit()."
|
|
340
|
+
)
|
|
341
|
+
spaces = model._aligned_feature_spaces(X)
|
|
342
|
+
rows = spaces[0].shape[0]
|
|
343
|
+
if rows != n_obs:
|
|
344
|
+
raise ValueError(
|
|
345
|
+
f"X has {rows} rows, but the fitted BrainData has {n_obs} "
|
|
346
|
+
f"observations; the training features must be in their original "
|
|
347
|
+
f"row order."
|
|
348
|
+
)
|
|
349
|
+
return spaces
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _resolve_device(device):
|
|
353
|
+
"""Resolve the bootstrap `device` request to a GPU backend, or None for CPU.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
device (str): ``'cpu'`` or ``'gpu'``, already checked by
|
|
357
|
+
`_validate_device`.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Backend | None: A resolved GPU backend, or None to stay on the CPU.
|
|
361
|
+
|
|
362
|
+
Raises:
|
|
363
|
+
ValueError: If ``'gpu'`` was requested with no accelerator available.
|
|
364
|
+
"""
|
|
365
|
+
from nltools.algorithms.backends import check_gpu_available, _resolve_backend
|
|
366
|
+
|
|
367
|
+
if device == "cpu":
|
|
368
|
+
return None
|
|
369
|
+
if not check_gpu_available()[0]:
|
|
370
|
+
raise ValueError(
|
|
371
|
+
"GPU requested via device='gpu' but no CUDA or MPS device is "
|
|
372
|
+
"available. Use device='cpu'."
|
|
373
|
+
)
|
|
374
|
+
return _resolve_backend("gpu")
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _as_bootstrap_result(bd, result):
|
|
378
|
+
"""Wrap an engine's arrays as a `BootstrapResult` of `BrainData` maps.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
bd (BrainData): Template whose mask and spatial state the outputs
|
|
382
|
+
inherit; row metadata and fitted state are cleared.
|
|
383
|
+
result (dict): Engine output with `'estimate'`, `'standard_error'`,
|
|
384
|
+
`'ci_lower'`, `'ci_upper'`, and optionally `'samples'`.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
BootstrapResult: The four summaries as `BrainData`, and the retained
|
|
388
|
+
replicates as a NumPy array when present.
|
|
389
|
+
"""
|
|
390
|
+
from nltools.data.results import BootstrapResult
|
|
391
|
+
|
|
392
|
+
return BootstrapResult(
|
|
393
|
+
estimate=_result_from_array(bd, result["estimate"], rows="clear"),
|
|
394
|
+
standard_error=_result_from_array(bd, result["standard_error"], rows="clear"),
|
|
395
|
+
ci_lower=_result_from_array(bd, result["ci_lower"], rows="clear"),
|
|
396
|
+
ci_upper=_result_from_array(bd, result["ci_upper"], rows="clear"),
|
|
397
|
+
samples=result.get("samples"),
|
|
398
|
+
)
|