nifreeze.model.lovo module

Closed-form leave-one-volume-out (LOVO) prediction for kernel models.

A kernel predictor with fixed hyperparameters is linear in the data, and its weights depend only on the gradient table. Given the covariance \(\mathbf{A}\) of the \(N\) diffusion-weighted volumes, the prediction of volume \(i\) from all the others is (Rasmussen and Williams[1], Eq. 5.12)

\[\hat{y}_i = -\sum_{j \neq i} \frac{P_{ij}}{P_{ii}} y_j, \quad \mathbf{P} = \mathbf{A}^{-1},\]

so all \(N\) LOVO predictions are given by a single smoother matrix \(\mathbf{S} = \mathbf{I} - \operatorname{diag}(\mathbf{P})^{-1}\mathbf{P}\), with a zero diagonal. One inverse replaces \(N\) model fits.

References

class nifreeze.model.lovo.ClosedFormLOVOMixin(self, dataset: 'Any', *, nvox_prefit: 'int | None' = 1000, random_state: 'int | None' = 0, **kwargs: 'Any') → 'None'[source]

Bases: object

Exact leave-one-volume-out prediction from a single linear smoother.

Subclasses implement _covariance(), which returns the (N, N) covariance of the volumes given the gradient table. The smoother is built once and rebuilt only when the gradients change. Each prediction is then one row of the smoother times the signal matrix. Because the held-out volume never contributes to its own prediction, single-fit mode returns the same genuine LOVO predictions and is not a canary.

Hyperparameters (if any) are estimated once, on a random subsample of voxels across all volumes. This lets a handful of scalars see the held-out volume, while every prediction remains held-out.

fit_predict(index: int | None = None, **kwargs: Any) → ndarray | None[source]

Predict volume index from all the other volumes (closed form).

single_fit_is_canary = False
property smoother: ndarray

The (N, N) smoother for the current gradient table (read-only).

Row i holds the weights that predict volume i from the others.

update(index: int) → None[source]

Replace row index of the cached signals with the dataset’s volume.

The smoother is rebuilt at the next fit if the gradient table changed.

nifreeze.model.lovo.DEFAULT_KRR_LAMBDA_GRID = (np.float64(0.0001), np.float64(0.00031622776601683794), np.float64(0.001), np.float64(0.0031622776601683794), np.float64(0.01), np.float64(0.03162277660168379), np.float64(0.1), np.float64(0.31622776601683794), np.float64(1.0), np.float64(3.1622776601683795), np.float64(10.0), np.float64(31.622776601683793), np.float64(100.0))

Default grid of ridge strengths (in units of sphere_gram()).

nifreeze.model.lovo.HYPERPARAMETERS_SCHEMA = 'nifreeze-gp-hyperparameters/1'

Schema identifier written to (and required from) hyperparameter files.

nifreeze.model.lovo.ILL_CONDITIONED_THRESHOLD = 1000000000000.0

Condition number above which the LOVO smoother is considered unreliable.

nifreeze.model.lovo.ILL_CONDITIONED_WARN_MSG = 'The covariance matrix is ill-conditioned (condition number {cond:.2e}); closed-form LOVO predictions may be inaccurate.'

Warning raised when the covariance is close to singular.

nifreeze.model.lovo.fit_pooled_hyperparameters(kernel: sklearn.gaussian_process.kernels.Kernel, tables: Sequence[ndarray], signals: Sequence[ndarray], alpha: float = 1e-10, maxiter: int = 200) → sklearn.gaussian_process.kernels.Kernel[source]

Fit GP hyperparameters shared by several datasets (e.g., subjects).

Maximizes \(\sum_s \log p(\mathbf{Y}_s \mid \mathbf{X}_s, \theta)\), where each dataset keeps its own gradient table \(\mathbf{X}_s\). With identical tables, this equals fitting the concatenated voxels.

Parameters:
  • kernel (Kernel) – Kernel providing the starting values and bounds.

  • tables (Sequence of ndarray) – One design matrix per dataset, of shape (N_s, n_features).

  • signals (Sequence of ndarray) – One signal matrix per dataset, of shape (N_s, V_s).

  • alpha (float, optional) – Value added to the diagonal of each covariance (numerical jitter).

  • maxiter (int, optional) – Maximum number of L-BFGS-B iterations.

Returns:

A copy of kernel with the fitted hyperparameters.

Return type:

Kernel

nifreeze.model.lovo.kernel_with_hyperparameters(kernel: sklearn.gaussian_process.kernels.Kernel, values: Mapping[str, float]) → sklearn.gaussian_process.kernels.Kernel[source]

Return a copy of kernel with the given hyperparameter values.

Parameters:
  • kernel (Kernel) – Kernel defining the structure (and the expected hyperparameter names).

  • values (Mapping) – Hyperparameter values in natural units, keyed by scikit-learn name.

Returns:

The kernel with its hyperparameters set.

Return type:

Kernel

nifreeze.model.lovo.load_hyperparameters(source: Path | str | Mapping[str, Any]) → dict[str, Any][source]

Read and validate a GP hyperparameter record.

Parameters:

source (os.pathlike or Mapping) – A JSON file written by save_hyperparameters(), or its parsed content.

Returns:

The record, with keys schema, kernel_model, hyperparameters and provenance.

Return type:

dict

nifreeze.model.lovo.lovo_error(smoother: ndarray, signals: ndarray) → float[source]

Compute the closed-form LOVO error, normalized by each voxel’s signal energy.

Parameters:
Returns:

\(\sum_v \lVert \mathbf{y}_v - \mathbf{S}\mathbf{y}_v \rVert^2 / \lVert \mathbf{y}_v \rVert^2\).

Return type:

float

nifreeze.model.lovo.lovo_smoother(covariance: ndarray, center: bool = False, shells: ndarray | None = None) → ndarray[source]

Build the closed-form leave-one-volume-out smoother from a covariance matrix.

Parameters:
  • covariance (ndarray) – Symmetric positive-definite covariance \(\mathbf{A}\) of shape (N, N), including any noise or ridge term on its diagonal.

  • center (bool, optional) – Whether the predictor estimates a constant mean on the \(N - 1\) training volumes (as scikit-learn’s normalize_y=True does). The standard deviation of the normalization cancels in the posterior mean, so only the mean needs correcting.

  • shells (ndarray, optional) – The shell (b-value) of each volume. The predictor then estimates the mean of each shell on the training volumes, as predict() does, and predicts each volume around its shell’s mean (the nearest shell’s, if no other volume is in its shell). Excludes center.

Returns:

The smoother \(\mathbf{S}\) of shape (N, N). Row \(i\) holds the weights predicting volume \(i\) from all the others, and its diagonal is zero. With center=True or shells, every row sums to one.

Return type:

ndarray

Examples

>>> A = np.array([[2.0, 1.0, 0.5], [1.0, 2.0, 1.0], [0.5, 1.0, 2.0]])
>>> S = lovo_smoother(A)
>>> bool(np.allclose(np.diag(S), 0.0))
True
>>> bool(np.allclose(lovo_smoother(A, center=True).sum(axis=1), 1.0))
True
>>> bool(np.allclose(lovo_smoother(A, shells=np.zeros(3)), lovo_smoother(A, center=True)))
True
nifreeze.model.lovo.masked_signal_matrix(dataset: ~typing.Any, mask: ~numpy.ndarray, dtype: ~typing.Any = <class 'numpy.float32'>) → ndarray[source]

Gather the masked signals of all volumes, one volume at a time.

Parameters:
  • dataset (BaseDataset) – Dataset whose dataobj has volumes along its first axis.

  • mask (ndarray) – Boolean mask of the voxels to gather.

  • dtype (dtype, optional) – Output data type.

Returns:

Signals of shape (N, V) (volumes by masked voxels).

Return type:

ndarray

nifreeze.model.lovo.save_hyperparameters(filename: Path | str, kernel: sklearn.gaussian_process.kernels.Kernel, kernel_model: str, **provenance: Any) → Path[source]

Write fitted GP hyperparameters to a JSON file.

Parameters:
  • filename (os.pathlike) – Output file.

  • kernel (Kernel) – Kernel holding the hyperparameters.

  • kernel_model (str) – The kernel_model used to build the kernel (e.g., "multishell").

  • **provenance – Free-form, JSON-serializable metadata (e.g., dataset, number of subjects).

Returns:

The written file.

Return type:

Path

nifreeze.model.lovo.select_krr_lambda(grams: Sequence[ndarray], signals: Sequence[ndarray], grid: Sequence[float] = (np.float64(0.0001), np.float64(0.00031622776601683794), np.float64(0.001), np.float64(0.0031622776601683794), np.float64(0.01), np.float64(0.03162277660168379), np.float64(0.1), np.float64(0.31622776601683794), np.float64(1.0), np.float64(3.1622776601683795), np.float64(10.0), np.float64(31.622776601683793), np.float64(100.0)), shells: Sequence[ndarray | None] | None = None) → float[source]

Choose a ridge strength by closed-form LOVO error, pooled over datasets.

Parameters:
  • grams (Sequence of ndarray) – One Gram matrix of shape (N_s, N_s) per dataset (e.g., subject).

  • signals (Sequence of ndarray) – One signal matrix of shape (N_s, V_s) per dataset.

  • grid (Sequence of float, optional) – Candidate ridge strengths, added to the diagonal of each Gram matrix.

  • shells (Sequence of ndarray, optional) – The shell of each volume, per dataset, when the predictor estimates a mean per shell (see lovo_smoother()).

Returns:

The ridge strength with the lowest summed LOVO error.

Return type:

float