linop.LinearOperator#

class bartorch.linop.LinearOperator#

Bases: Operator

A linear map between two C-order shapes, with an adjoint.

A subclass is defined either by _create(), which builds one of BART’s operators, or in Python by forward() and adjoint(), and normal() where a cheaper form exists. BART reaches a Python-defined operator through callbacks, one crossing into Python per application.

Both kinds compose into a single BART operator, are solved by bartorch.optim, and differentiate in torch. The backward pass of A(x) is A.adjoint, which for complex tensors is the conjugate Wirtinger gradient torch expects, not the transpose.

ishape, oshape

Domain and codomain, C order.

Type:

tuple of int

device#

Where the operator does its arithmetic, when that is not where its operands are.

Type:

torch.device or None

Examples

An operator defined in Python, composed with a BART operator into one:

>>> class Phase(linop.LinearOperator):
...     def __init__(self, phase):
...         self.phase = phase
...         self.ishape = self.oshape = tuple(phase.shape)
...         super().__init__()
...     def forward(self, x, out=None):
...         return x * self.phase
...     def adjoint(self, y, out=None):
...         return y * self.phase.conj()
>>> A = linop.FFT((64, 64), axes=(-1, -2)) @ Phase(torch.exp(1j * torch.rand(64, 64)))
>>> y = A(x)        # recorded for autograd when x requires a gradient
>>> z = A.H(y)      # the adjoint; A.gram() is the normal operator A^H A
forward()#

Apply A x without recording for autograd.

Parameters:
  • x (tensor, shape ishape) – Input.

  • out (tensor, shape oshape, default=None) – Written in place when given. For a BART-backed operator, a contiguous complex64 tensor on the input’s device.

Returns:

out when given.

Return type:

tensor, shape oshape

adjoint()#

Apply A^H y without recording for autograd.

Parameters:
  • y (tensor, shape oshape) – Input.

  • out (tensor, shape ishape, default=None) – Written in place when given, as for forward().

Return type:

tensor, shape ishape

normal()#

Apply A^H A x.

A BART-backed operator applies BART’s normal, which for an encoding built with toeplitz=True is a convolution with a point spread function; a Python-defined one computes adjoint(forward(x)).

Parameters:
  • x (tensor, shape ishape) – Input.

  • out (tensor, shape ishape, default=None) – Written in place when given, as for forward().

Return type:

tensor, shape ishape

classmethod from_callbacks()#

Build a BART operator from Python functions.

Each function receives a view of BART’s buffer, without a copy, and returns a tensor; every application calls back into Python.

Parameters:
  • oshape (tuple of int) – Codomain and domain, C order.

  • ishape (tuple of int) – Codomain and domain, C order.

  • forward (callable) – x -> A x, from ishape to oshape.

  • adjoint (callable) – y -> A^H y, from oshape to ishape.

  • normal (callable, default=None) – x -> A^H A x in one function; None chains adjoint onto forward.

Return type:

LinearOperator

property plan#

The encoding form this operator was lowered into, or None.

An MRI encoding reports the transform, the element-wise factors on each side of it, the contraction, what is streamed, how the normal is applied and which executor built it. A composition containing exactly one encoding reports that encoding’s plan; any other operator has none.

plan.fused is false where the coil-slab loop could not take the form or a sum of terms was left as a chain: the same result, computed more slowly. plan.executor is read back from the library after the build. Reading the property on a composition builds it.

Return type:

bartorch.linop._form.Plan or None

Examples

>>> A = linop.CartesianSense(maps, (64, 64), pattern=mask)
>>> A.plan.transform, A.plan.normal, A.plan.executor
('fft', 'kernel', 'slab')
>>> A.plan.fused
True
property H#

A^H, from BART’s adjoint constructor.

The result is a BART operator: A.H @ B is one operator, and the normal of A.H is A A^H.

property T#

A^T, the adjoint without the conjugation, as conj(A).H.

conj()#

conj(A): conjugate the input, apply, conjugate the output.

gram()#

A^H A as an operator.

BART’s normal: for an encoding built with toeplitz=True, the convolution with a point spread function.

cogram()#

A A^H as an operator.

opnorm()#

Estimate the spectral norm by BART’s power iteration on A^H A.

The starting vector comes from BART’s process-global generator, so repeated calls differ slightly. Accurate enough for a step size or a Lipschitz constant, not an exact singular value.

Return type:

float

Raises:

BartError – When the power iteration does not converge.

__call__()#

Apply A x, recorded for autograd when x requires a gradient.

The backward pass applies adjoint().

Parameters:
  • x (tensor, shape ishape) – Input.

  • out (tensor, shape oshape, default=None) – Contiguous complex64 tensor written in place.

Return type:

tensor, shape oshape

Raises:

ValueError – When out is given and x requires a gradient.

property dim_shape#

The domain, under pyxu’s name for it; the same as ishape.

property codim_shape#

The codomain, under pyxu’s name for it; the same as oshape.

property dim_size#

Number of elements in the domain.

property codim_size#

Number of elements in the codomain.

property dim_rank#

Number of axes of the domain.

property codim_rank#

Number of axes of the codomain.

to_nonlinear()#

The same operator as a NonlinearOperator.

pinv()#

Solve (A^H A + damp I) x = A^H y, the damped least-squares problem.

ScaledSum is solved by BART’s closed-form pseudo-inverse; every other operator, including compositions, by CG.

Parameters:
  • y (tensor, shape oshape) – Data.

  • damp (float, default=0.0) – Tikhonov weight, CG’s lambda_.

  • **kwargs – Passed to CG; x0 is the warm start.

Return type:

tensor, shape ishape

Raises:

TypeError – When kwargs are given and the closed form applies.

Notes

BART attaches a closed form (norm_inv) only in linops/sum.c, and it is correct for linop_scaled_sum alone: for the plain sum it solves a differently scaled problem. Sum therefore takes the solver.

Examples using LinearOperator#

7. Basics of non-Cartesian imaging

7. Basics of non-Cartesian imaging

8. Radial SENSE

8. Radial SENSE

9. Nonlinear inversion for T2 mapping

9. Nonlinear inversion for T2 mapping

Off-resonance correction of spiral imaging

Off-resonance correction of spiral imaging

Dynamic golden-angle radial MRI

Dynamic golden-angle radial MRI

Subspace-constrained T1 mapping

Subspace-constrained T1 mapping