neuromodes 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. neuromodes/__init__.py +11 -0
  2. neuromodes/basis.py +413 -0
  3. neuromodes/data/__init__.py +0 -0
  4. neuromodes/data/included_data.csv +18 -0
  5. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_fcgradient1.func.gii +6 -0
  6. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_medmask.label.gii +6 -0
  7. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_midthickness.surf.gii +196 -0
  8. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_myelinmap.func.gii +17 -0
  9. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_ndi.func.gii +63 -0
  10. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_odi.func.gii +63 -0
  11. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_sphere.surf.gii +109 -0
  12. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-L_thickness.func.gii +17 -0
  13. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-R_medmask.label.gii +6 -0
  14. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-R_midthickness.surf.gii +196 -0
  15. neuromodes/data/sp-human_tpl-fsLR_den-32k_hemi-R_sphere.surf.gii +109 -0
  16. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-L_medmask.label.gii +6 -0
  17. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-L_midthickness.surf.gii +110 -0
  18. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-L_myelinmap.func.gii +6 -0
  19. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-L_sphere.surf.gii +107 -0
  20. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-R_medmask.label.gii +6 -0
  21. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-R_midthickness.surf.gii +110 -0
  22. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-R_myelinmap.func.gii +105 -0
  23. neuromodes/data/sp-human_tpl-fsLR_den-4k_hemi-R_sphere.surf.gii +92 -0
  24. neuromodes/data/sp-macaque_tpl-fsLR_den-32k_hemi-L_medmask.label.gii +6 -0
  25. neuromodes/data/sp-macaque_tpl-fsLR_den-32k_hemi-L_midthickness.surf.gii +194 -0
  26. neuromodes/data/sp-macaque_tpl-fsLR_den-32k_hemi-R_medmask.label.gii +6 -0
  27. neuromodes/data/sp-macaque_tpl-fsLR_den-32k_hemi-R_midthickness.surf.gii +194 -0
  28. neuromodes/data/sp-marmoset_tpl-fsLR_den-32k_hemi-L_medmask.label.gii +6 -0
  29. neuromodes/data/sp-marmoset_tpl-fsLR_den-32k_hemi-L_midthickness.surf.gii +287 -0
  30. neuromodes/data/sp-marmoset_tpl-fsLR_den-32k_hemi-R_medmask.label.gii +6 -0
  31. neuromodes/data/sp-marmoset_tpl-fsLR_den-32k_hemi-R_midthickness.surf.gii +287 -0
  32. neuromodes/eigen.py +808 -0
  33. neuromodes/io.py +242 -0
  34. neuromodes/mesh.py +153 -0
  35. neuromodes/network.py +93 -0
  36. neuromodes/nulls.py +543 -0
  37. neuromodes/stats.py +729 -0
  38. neuromodes/waves.py +784 -0
  39. neuromodes-0.1.0.dist-info/METADATA +123 -0
  40. neuromodes-0.1.0.dist-info/RECORD +43 -0
  41. neuromodes-0.1.0.dist-info/WHEEL +5 -0
  42. neuromodes-0.1.0.dist-info/licenses/LICENCE-CC-BY-NC-SA-4.0.md +437 -0
  43. neuromodes-0.1.0.dist-info/top_level.txt +1 -0
neuromodes/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """
2
+ neuromodes
3
+ ==========
4
+ Eigenmode-based brain mapping and modelling toolbox developed by the Neural Systems and Behaviour
5
+ Lab. Documentation is available at https://neuromodes.readthedocs.io/, and source code
6
+ at https://github.com/NSBLab/neuromodes.
7
+ """
8
+
9
+ from neuromodes.eigen import EigenSolver
10
+
11
+ __all__ = ["EigenSolver"]
neuromodes/basis.py ADDED
@@ -0,0 +1,413 @@
1
+ """
2
+ Module for expressing brain maps as linear combinations of orthogonal basis vectors, such as
3
+ geometric eigenmodes.
4
+ """
5
+
6
+ from __future__ import annotations
7
+ from typing import TYPE_CHECKING, overload
8
+ from warnings import warn
9
+ import numpy as np
10
+ from neuromodes.eigen import EigenData
11
+ from neuromodes.stats import lstsqw, cdistw, _process_vertex_areas
12
+
13
+ if TYPE_CHECKING:
14
+ from typing import Any, TypeAlias, Literal
15
+ from collections.abc import Sequence
16
+ from numpy.typing import NDArray
17
+ from scipy.sparse import csc_matrix
18
+ from scipy.spatial.distance import _MetricCallback, _MetricKind
19
+ from neuromodes.eigen import _CheckKind
20
+
21
+ _IntSequenceKind: TypeAlias = Sequence[int] | NDArray[np.integer]
22
+ _SeqSequenceKind: TypeAlias = Sequence[_IntSequenceKind] | NDArray[Any]
23
+ _DecompositionKind: TypeAlias = Literal['project', 'regress']
24
+
25
+ @overload
26
+ def decompose(
27
+ data: NDArray[np.floating],
28
+ emodes: NDArray[np.floating],
29
+ method: _DecompositionKind = ...,
30
+ *,
31
+ mass: csc_matrix | None = ...,
32
+ mode_counts: int | None = ...,
33
+ mode_ids: None = ...,
34
+ checks: _CheckKind | None = ...
35
+ ) -> NDArray[np.floating]: ...
36
+
37
+ # 2. mode_counts is Sequence -> List of Arrays
38
+ @overload
39
+ def decompose(
40
+ data: NDArray[np.floating],
41
+ emodes: NDArray[np.floating],
42
+ method: _DecompositionKind = ...,
43
+ *,
44
+ mass: csc_matrix | None = ...,
45
+ mode_counts: _IntSequenceKind,
46
+ mode_ids: None = ...,
47
+ checks: _CheckKind | None = ...
48
+ ) -> list[NDArray[np.floating]]: ...
49
+
50
+ # 3. mode_ids is Sequence -> List of Arrays
51
+ @overload
52
+ def decompose(
53
+ data: NDArray[np.floating],
54
+ emodes: NDArray[np.floating],
55
+ method: _DecompositionKind = ...,
56
+ *,
57
+ mass: csc_matrix | None = ...,
58
+ mode_counts: None = ...,
59
+ mode_ids: _SeqSequenceKind,
60
+ checks: _CheckKind | None = ...
61
+ ) -> list[NDArray[np.floating]]: ...
62
+
63
+ def decompose(
64
+ data: NDArray[np.floating],
65
+ emodes: NDArray[np.floating],
66
+ method: _DecompositionKind = 'project',
67
+ *,
68
+ mass: csc_matrix | None = None,
69
+ mode_counts: _IntSequenceKind | int | None = None,
70
+ mode_ids: _SeqSequenceKind | None = None,
71
+ checks: _CheckKind | None = None,
72
+ ) -> NDArray[np.floating] | list[NDArray[np.floating]]:
73
+ """
74
+ Calculate the decomposition of the given data onto a basis set.
75
+
76
+ Parameters
77
+ ----------
78
+ data : array-like
79
+ The input data array of shape ``(n_verts, ...)``, where ``n_verts`` is the number of
80
+ vertices and additional axes represent maps to be decomposed independently.
81
+ emodes : array-like
82
+ The basis vectors array of shape ``(n_verts, n_modes)``, where ``n_modes`` is the number of
83
+ vectors.
84
+ method : str, optional
85
+ The method used for the decomposition, either ``'project'`` or ``'regress'``. Note that
86
+ ``'project'`` is faster and more accurate, while ``'regress'`` should only be used when
87
+ ``data`` contains missing values (NaNs; see Notes). The methods are otherwise equivalent.
88
+ Default is ``'project'``.
89
+ mass : array-like, optional
90
+ The mass matrix of shape ``(n_verts, n_verts)``. If vectors are orthonormal in Euclidean
91
+ space, leave as ``None``. See :func:`eigen.is_orthonormal_basis` for more details. Default
92
+ is ``None``.
93
+ mode_counts : array-like, optional
94
+ The sequence of vectors to be used for decomposition, of shape ``(n_recons,)``. For
95
+ example, ``mode_counts=np.array([10,20,30])`` will run three analyses: with the first 10
96
+ vectors, with the first 20 vectors, and with the first 30 vectors. Default is ``None``,
97
+ which uses ``n_modes``.
98
+ mode_ids : array-like, optional
99
+ The indices of the modes to be used for reconstruction, overriding ``mode_counts``. If
100
+ ``None``, all modes are used. Default is ``None``.
101
+ checks : str or bool, optional
102
+ Whether to validate arguments prior to analysis. Default is ``None``.
103
+
104
+ Returns
105
+ -------
106
+ numpy.ndarray or list
107
+ The coefficients array of shape ``(n_modes, ...)`` or ``list of (n_modes, ...)``
108
+ arrays, obtained from the decomposition.
109
+
110
+ Raises
111
+ ------
112
+ ValueError
113
+ If ``method`` is not ``'project'`` or ``'regress'``.
114
+ ValueError
115
+ If ``method`` is ``'project'`` and ``data`` contains NaNs.
116
+
117
+ Notes
118
+ -----
119
+ If ``data`` contains NaNs, these will be disregarded prior to decomposition (via
120
+ ``method='regress'``) by removing corresponding entries from ``data``, ``emodes``, and ``mass``.
121
+ Note that extreme values may accumulate in the reconstructed data at these vertices, where data
122
+ provides no constraint. Consider instead interpolating missing data prior to decomposition.
123
+ """
124
+ # Format / validate inputs
125
+ if method not in ['project', 'regress']:
126
+ raise ValueError(f"Invalid method '{method}'; must be 'project' or 'regress'.")
127
+
128
+ if checks is None:
129
+ checks = True if method == 'project' else 'maps'
130
+ if checks is not False:
131
+ ved = EigenData(emodes=emodes, mass=mass, data=data, checks=checks)
132
+ emodes, mass, data = ved.emodes, ved.mass, ved.data
133
+
134
+ mass = _process_vertex_areas(mass, data.shape[0])
135
+
136
+ mode_ids, squeeze_output = _process_mode_ids(mode_counts, mode_ids, emodes.shape[1])
137
+ n_modes = [len(x) for x in mode_ids]
138
+
139
+ # Manipulate input/output shapes
140
+ output_shapes = [(i,) + data.shape[1:] for i in n_modes]
141
+ coeffs = [np.empty(shape, dtype=data.dtype) for shape in output_shapes]
142
+ data_2d = data.reshape(data.shape[0], -1) # guaranteed 2d
143
+
144
+ # Handle NaNs with 'regress' by masking out afflicted vertices (separately for each NaN pattern)
145
+ data_isnan = np.isnan(data_2d)
146
+ if np.any(data_isnan):
147
+ if method == 'project':
148
+ raise ValueError("data contains NaNs; use method='regress' to mask out afflicted "
149
+ "vertices prior to decomposition, or consider interpolating missing "
150
+ "data.")
151
+ # method == 'regress'
152
+ if checks is True or checks == 'maps':
153
+ warn("NaN values detected in data; these will be disregarded during decomposition "
154
+ "by masking corresponding vertices from data, emodes, and mass. This may lead "
155
+ "to extreme values in affected areas of the reconstructed data. Consider "
156
+ "instead interpolating missing data prior to decomposition.")
157
+ masks, mask_indices = np.unique(~data_isnan, axis=1, return_inverse=True)
158
+ elif method == 'regress':
159
+ # Keep all vertices
160
+ masks = np.ones((data_2d.shape[0], 1), dtype=bool)
161
+ mask_indices = np.zeros(data_2d.shape[1], dtype=int)
162
+
163
+ # TODO : consider adding a method that does fitting/param estimation (like lstsqw) but using
164
+ # full (consistent) mass matrix (not just vertex areas). solvew?
165
+ if method == 'project':
166
+ # Find the unique mode IDs requested, and the inverse mapping back to mode_ids
167
+ unique_mids, inv = np.unique(np.concatenate(mode_ids), return_inverse=True)
168
+ inv = np.split(inv, np.cumsum([len(m) for m in mode_ids[:-1]])) # back in the same list pattern as mode_ids
169
+
170
+ # For each nan/inf pattern, get the coeffs for all the unique modes
171
+ coeffs_all = emodes[:, unique_mids].T @ (mass @ data_2d)
172
+
173
+ # Map the unique results back to the specific mode_ids requested
174
+ for j, idxs in enumerate(inv):
175
+ coeffs[j] = coeffs_all[idxs, :].reshape(output_shapes[j])
176
+
177
+ elif method == 'regress':
178
+ # Have to loop over each set of mode indices
179
+ for j in range(len(mode_ids)):
180
+ coeffs_current = np.empty((n_modes[j], data_2d.shape[1]), dtype=data.dtype)
181
+ # as well as each NaN pattern
182
+ for i, mask in enumerate(masks.T):
183
+ # Get indices of maps with this NaN/Inf pattern
184
+ # Remove verts with NaNs/Inf in this group from data and emodes
185
+ # Calculate coefficients for subset of data
186
+ map_indices = np.where(mask_indices == i)[0]
187
+ coeffs_current[:, map_indices] = lstsqw(
188
+ emodes[mask][:, mode_ids[j]],
189
+ data_2d[mask][:, map_indices],
190
+ mass=mass[mask][:, mask]
191
+ )[0]
192
+ coeffs[j] = coeffs_current.reshape(output_shapes[j])
193
+
194
+ return coeffs[0] if squeeze_output else coeffs # convert back to array if mode_counts was None/scalar
195
+
196
+ def reconstruct(
197
+ emodes: NDArray[np.floating],
198
+ data: NDArray[np.floating] | None = None,
199
+ coeffs: list[NDArray[np.floating]] | NDArray[np.floating] | None = None,
200
+ method: _DecompositionKind = 'project',
201
+ mass: csc_matrix | None = None,
202
+ mode_counts: _IntSequenceKind | int | None = None,
203
+ mode_ids: _SeqSequenceKind | None = None,
204
+ checks: _CheckKind | None = None
205
+ ) -> NDArray[np.floating]:
206
+ """
207
+ Calculate the reconstruction of the given independent data using the provided orthonormal
208
+ vectors (e.g., geometric eigenmodes).
209
+
210
+ Parameters
211
+ ----------
212
+ emodes : array-like
213
+ The basis vectors array of shape ``(n_verts, n_modes)``, where ``n_modes`` is the number of
214
+ vectors.
215
+ data : array-like
216
+ The input data array of shape ``(n_verts, ...)``, where ``n_verts`` is the number of
217
+ vertices and additional axes represent maps to be decomposed independently. If ``None``,
218
+ ``coeffs`` must be provided. Default is ``None``.
219
+ coeffs : array-like, optional
220
+ The modal coefficients array of shape ``(n_modes, ...)`` or ``list of (n_modes, ...)``
221
+ arrays, obtained from the decomposition. If ``None``, ``data`` must be provided. Default is
222
+ ``None``.
223
+ method : str, optional
224
+ The method used for the decomposition, either ``'project'`` or ``'regress'``. Note that
225
+ ``'project'`` is faster and more accurate, while ``'regress'`` should only be used when
226
+ ``data`` contains missing values (NaNs; see Notes). The methods are otherwise equivalent.
227
+ Default is ``'project'``.
228
+ mass : array-like, optional
229
+ The mass matrix of shape ``(n_verts, n_verts)``. If vectors are orthonormal in Euclidean
230
+ space, leave as ``None``. See :func:`eigen.is_orthonormal_basis` for more details. Default
231
+ is ``None``.
232
+ mode_counts : array-like, optional
233
+ The sequence of vectors to be used for reconstruction, of shape ``(n_recons,)``. For
234
+ example, ``mode_counts=np.array([10,20,30])`` will run three analyses: with the first 10
235
+ vectors, with the first 20 vectors, and with the first 30 vectors. Default is ``None``,
236
+ which uses ``n_modes``.
237
+ mode_ids : array-like, optional
238
+ The indices of the modes to be used for reconstruction, overriding ``mode_counts``. If
239
+ ``None``, all modes are used. Default is ``None``.
240
+ Default is ``None``.
241
+ checks : str or bool, optional
242
+ Whether to validate arguments prior to analysis. Default is ``True``.
243
+
244
+ Returns
245
+ -------
246
+ recon : numpy.ndarray
247
+ The reconstructed data array of shape ``(n_verts, ..., n_recons)``, where ``n_recons`` is
248
+ the number of different reconstructions ordered in ``mode_counts``. Each slice is the
249
+ independent reconstruction of each map. Note that if ``mode_counts`` includes any constant
250
+ vector (e.g., the first geometric eigenmode), the reconstructions will be constant for that
251
+ value of ``mode_counts`` (this may also result in warnings/nans for ``recon_error``).
252
+
253
+ Raises
254
+ ------
255
+ ValueError
256
+ If neither or both of ``data`` and ``coeffs`` are provided.
257
+
258
+ Notes
259
+ -----
260
+ If ``data`` contains NaNs, these will be disregarded prior to decomposition (via
261
+ ``method='regress'``) by removing corresponding entries from ``data``, ``emodes``, and ``mass``.
262
+ Note that extreme values may accumulate in the reconstructed data at these vertices, where data
263
+ provides no constraint. Consider instead interpolating missing data prior to decomposition.
264
+ """
265
+ # Format / validate inputs
266
+ if checks is None:
267
+ if method == 'regress':
268
+ checks = 'maps'
269
+ else:
270
+ checks = True
271
+ if checks is not False:
272
+ ved = EigenData(emodes=emodes, mass=mass, data=data, checks=checks)
273
+ emodes, mass, data = ved.emodes, ved.mass, ved.data
274
+
275
+ # This should stay here as it needs to be run in both coefficients and data modes
276
+ mode_ids, squeeze_output = _process_mode_ids(mode_counts, mode_ids, emodes.shape[1])
277
+
278
+ # Validate that exactly one of coefficients/data is provided & decompose if only data is provided
279
+ if coeffs is not None and data is not None:
280
+ raise ValueError("Exactly one of 'coefficients' or 'data' must be provided.")
281
+ elif coeffs is not None:
282
+ if isinstance(coeffs, np.ndarray): # equivalent to `if squeeze_output`, but keeps pyright happy
283
+ coeffs = [coeffs]
284
+ elif data is not None: # coefficients will never be squeezed in this case (as mode_ids is passed)
285
+ coeffs = decompose(data, emodes, method=method, mass=mass, mode_ids=mode_ids, checks=False)
286
+ else: # neither provided (this order keeps pyright happy)
287
+ raise ValueError("Exactly one of 'coefficients' or 'data' must be provided.")
288
+ n_recons = len(coeffs)
289
+
290
+ # Main computation
291
+ recon_3d_shape = (emodes.shape[0], int(np.prod(coeffs[0].shape[1:])), n_recons)
292
+ recon_3d = np.empty(recon_3d_shape, dtype=coeffs[0].dtype)
293
+ for j, mids in enumerate(mode_ids):
294
+ recon_3d[:, :, j] = emodes[:, mids] @ coeffs[j].reshape(len(mids), -1) # convert to col vec if 1D
295
+
296
+ # Reshape outputs
297
+ if squeeze_output:
298
+ recon_nd_shape = (emodes.shape[0],) + coeffs[0].shape[1:]
299
+ else:
300
+ recon_nd_shape = (emodes.shape[0],) + coeffs[0].shape[1:] + (n_recons,)
301
+ recon_nd = recon_3d.reshape(recon_nd_shape)
302
+
303
+ return recon_nd
304
+
305
+ def recon_error(
306
+ data: NDArray[np.floating],
307
+ recon: NDArray[np.floating],
308
+ mass: csc_matrix | None = None,
309
+ metric: _MetricCallback | _MetricKind = 'correlation',
310
+ checks: _CheckKind = 'maps',
311
+ **cdist_kwargs
312
+ ) -> NDArray[np.floating]:
313
+ """
314
+ Calculate the reconstruction error between the given data and each reconstruction, using the
315
+ specified metric.
316
+
317
+ Parameters
318
+ ----------
319
+ data : array-like
320
+ The input data array of shape ``(n_verts, ...)``, where ``n_verts`` is the number of
321
+ vertices and additional axes represent maps that have been reconstructed.
322
+ recon : array-like
323
+ The reconstructed data array of shape ``(n_verts, ..., n_recons)``, where ``n_recons`` is
324
+ the number of different reconstructions ordered in ``mode_counts``. Each slice contains the
325
+ reconstruction(s) of the corresponding map in ``data``.
326
+ mass : array-like, optional
327
+ The mass matrix of shape ``(n_verts, n_verts)``. If vectors are orthonormal in Euclidean
328
+ space, leave as ``None``. See :func:`eigen.is_orthonormal_basis` for more details. Default
329
+ is ``None``.
330
+ metric : str or callable, optional
331
+ The distance metric to use for calculating reconstruction error. Can be any metric accepted
332
+ by ``scipy.spatial.distance.cdist``, or a custom metric function. Default is
333
+ ``'correlation'``.
334
+ checks : str or bool, optional
335
+ Whether to validate arguments prior to analysis. Default is ``'maps'``.
336
+ **cdist_kwargs
337
+ Additional keyword arguments to be passed to ``scipy.spatial.distance.cdist`` when
338
+ calculating reconstruction error.
339
+
340
+ Returns
341
+ -------
342
+ recon_error : numpy.ndarray
343
+ The reconstruction error array of shape ``(..., n_recons)``, where ``n_recons`` is the
344
+ number of different reconstructions ordered in ``mode_counts``. Each slice contains the
345
+ error(s) of the corresponding map in ``data``.
346
+
347
+ Raises
348
+ ------
349
+ ValueError
350
+ If ``data`` and ``recon`` have incompatible shapes.
351
+ """
352
+
353
+ # Format / validate checks
354
+ if checks is not False:
355
+ ved = EigenData(mass=mass, data=(data, recon), checks=checks)
356
+ mass = ved.mass
357
+ data, recon = ved.data
358
+
359
+ # Get and check data/recon shapes
360
+ data_shape = data.shape
361
+ recon_shape = recon.shape
362
+ squeeze_output = len(data_shape) == len(recon_shape)
363
+ if squeeze_output:
364
+ if data_shape != recon_shape:
365
+ raise ValueError(f"data and recon must have the same shape; got {data_shape} and "
366
+ f"{recon_shape}.")
367
+ n_recons = 1
368
+ else:
369
+ if data_shape != recon_shape[:-1]:
370
+ raise ValueError("data and recon must have the same shape except for the last "
371
+ f"dimension; got {data_shape} and {recon_shape}.")
372
+ n_recons = recon_shape[-1]
373
+
374
+ # Main computation
375
+ data_2d = data.reshape(data.shape[0], -1)
376
+ recon_3d = recon.reshape(recon.shape[0], -1, n_recons)
377
+
378
+ error_2d_shape = (data_2d.shape[1],) + (n_recons,)
379
+ recon_error_2d = np.empty(error_2d_shape, dtype=data.dtype)
380
+ for i in range(data_2d.shape[1]):
381
+ recon_error_2d[i, :] = cdistw(data_2d[:, [i]], recon_3d[:, i, :],
382
+ mass=mass, metric=metric, **cdist_kwargs)
383
+
384
+ recon_error = recon_error_2d.reshape(recon.shape[1:])
385
+ return recon_error
386
+
387
+ def _process_mode_ids(
388
+ mode_counts: _IntSequenceKind | int | None,
389
+ mode_ids: _SeqSequenceKind | None,
390
+ n_modes: int
391
+ ) -> tuple[_SeqSequenceKind, bool]:
392
+ """
393
+ Process mode_counts and mode_ids into a consistent format for use in decompose/reconstruct.
394
+ """
395
+ # mode_counts is just shorthand for mode_ids
396
+ # If mode_counts is provided, reformat into mode_ids
397
+ if mode_counts is not None and mode_ids is not None:
398
+ raise UserWarning("Both mode_counts and mode_ids provided; mode_counts will be ignored.")
399
+
400
+ if isinstance(mode_ids, (list, tuple, np.ndarray)):
401
+ output = mode_ids
402
+ elif mode_ids is not None:
403
+ raise ValueError("mode_ids must be a list or tuple of arrays of mode indices.")
404
+ elif mode_counts is None:
405
+ output = (np.arange(n_modes),)
406
+ elif isinstance(mode_counts, int):
407
+ output = (np.arange(mode_counts),)
408
+ else:
409
+ output = [np.arange(mc) for mc in mode_counts]
410
+
411
+ squeeze_output = (mode_ids is None) and (mode_counts is None or isinstance(mode_counts, int))
412
+
413
+ return output, squeeze_output
File without changes
@@ -0,0 +1,18 @@
1
+ DATA_TYPE,SPECIES,TEMPLATE,DENSITY,HEMI,SOURCE,DOI,DESCRIPTION
2
+ midthickness,Homo sapiens,fsLR,4k,L,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Cortical midthickness surface mesh and medial wall mask (Conte69). Note that the medial wall mask has been updated to correctly mask out all medial wall vertices (num. cortical vertices reduced from 3636 to 3619)
3
+ midthickness,Homo sapiens,fsLR,4k,R,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Cortical midthickness surface mesh and medial wall mask (Conte69). Note that the medial wall mask has been updated to correctly mask out all medial wall vertices (num. cortical vertices reduced from 3636 to 3619)
4
+ midthickness,Homo sapiens,fsLR,32k,L,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Cortical midthickness surface mesh and medial wall mask (Conte69)
5
+ midthickness,Homo sapiens,fsLR,32k,R,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Cortical midthickness surface mesh and medial wall mask (Conte69)
6
+ midthickness,Macaca mulatta,fsLR,32k,L,Donahue et al. (2016) J Neurosci,10.1523/JNEUROSCI.0493-16.2016,Cortical midthickness surface mesh and medial wall mask (Yerkes19)
7
+ midthickness,Macaca mulatta,fsLR,32k,R,Donahue et al. (2016) J Neurosci,10.1523/JNEUROSCI.0493-16.2016,Cortical midthickness surface mesh and medial wall mask (Yerkes19)
8
+ midthickness,Callithrix jacchus,fsLR,32k,L,Hayashi et al. (2021) NeuroImage,10.1016/j.neuroimage.2021.117726,Cortical midthickness surface mesh and medial wall mask (Riken50)
9
+ midthickness,Callithrix jacchus,fsLR,32k,R,Hayashi et al. (2021) NeuroImage,10.1016/j.neuroimage.2021.117726,Cortical midthickness surface mesh and medial wall mask (Riken50)
10
+ sphere,Homo sapiens,fsLR,4k,L,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Mesh inflated to sphere (Conte69)
11
+ sphere,Homo sapiens,fsLR,4k,R,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Mesh inflated to sphere (Conte69)
12
+ sphere,Homo sapiens,fsLR,32k,L,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Mesh inflated to sphere (Conte69)
13
+ sphere,Homo sapiens,fsLR,32k,R,Glasser et al. (2011) J Neurosci,10.1523/JNEUROSCI.2180-11.2011,Mesh inflated to sphere (Conte69)
14
+ fcgradient1,Homo sapiens,fsLR,32k,L,Margulies et al. (2016) PNAS,10.1073/pnas.1608282113,First functional connectivity gradient derived from resting-state fMRI
15
+ myelinmap,Homo sapiens,fsLR,32k,L,Glasser et al. (2016) Nature,10.1038/nature18933,Myelin map derived from T1w/T2w ratio
16
+ ndi,Homo sapiens,fsLR,32k,L,Fukutomi et al. (2018) NeuroImage,10.1016/j.neuroimage.2018.02.017,Neurite density index derived from NODDI
17
+ odi,Homo sapiens,fsLR,32k,L,Fukutomi et al. (2018) NeuroImage,10.1016/j.neuroimage.2018.02.017,Orientation dispersion index derived from NODDI
18
+ thickness,Homo sapiens,fsLR,32k,L,Glasser et al. (2016) Nature,10.1038/nature18933,Cortical thickness map