Skip to content

Optimization

Generic iterative solvers that work against any LinearOperator — they know nothing about MRI, k-space, or coils. See Architecture → Layer 6, Pipeline for where solvers sit relative to the operator and reconstruction layers.

conjugate_gradient solves the normal equations

$$ (A^H A + \lambda I)\,x = A^H y $$

for any LinearOperator A and data y, using only A.adjoint and A.normal — it never calls A.forward directly. This is the solver behind reconstruct(method="cg") (CG-SENSE, Pruessmann et al., MRM 2001), where A = F @ S (a FourierOperator or NUFFTOperator composed with a SensitivityOperator): the same solver code runs on Cartesian and non-Cartesian data because only A changes with the trajectory, not the optimization.

from unimri.operators import FourierOperator, SensitivityOperator, unchecked
from unimri.optimization import conjugate_gradient

A = unchecked(FourierOperator(image_shape) @ SensitivityOperator(coil_maps))
image = conjugate_gradient(A, kspace, n_iter=15, l2=1e-4)

l2 is Tikhonov regularization toward zero (helps on undersampled / noisy data at the cost of some bias); tol stops early once the residual norm is small enough; callback(i, x, residual_norm) runs after every iteration, useful for convergence plots or early stopping on your own criterion.

Solve (Aᴴ A + l2 I) x = Aᴴ y by linear conjugate gradient.

Parameters:

Name Type Description Default
operator LinearOperator

Exposes .adjoint(y) and .normal(x) (AᴴA x).

required
y Array

Measured data (e.g. multi-coil k-space).

required
n_iter int

Maximum number of iterations.

10
l2 float

Tikhonov regularization strength (0 = none).

0.0
x0 Array | None

Initial guess; defaults to zero.

None
tol float

Stop early once ||r|| < tol.

1e-10
callback Callable[[int, Array, float], None] | None

Optional callback(iteration, x, residual_norm), called each step.

None