Linear operators#

bartorch.linop. An operator maps between two C-order shapes. They compose and combine into a single BART operator through the algebra below – every one of those is a BART constructor applied to BART operators, so what a solver drives is one operator in BART’s own loop, with no arithmetic in Python between the steps. Applying an operator to a tensor that requires a gradient records it for autograd.

MRI encoding#

These are what most reconstructions here are: an encoding, and a solver driving it. They come first because they are what the rest of the page is for.

CartesianSense

Coils, a Fourier transform, and the samples that were taken.

NoncartesianSense

Sensitivities followed by a NUFFT, applied coil_batch coils at a time.

WaveSense

Wave-CAIPI encoding, as BART's wave builds it.

Coils

Coil sensitivities, without the transform that usually follows them.

FieldCorrected

An encoding with off-resonance during the readout, by time segmentation.

Sampling

Multiplication by a sampling pattern, broadcast over the axes where it is one.

NoncartesianSense is BART’s own operator – the coil batching and the Toeplitz normal are what make it what it is. CartesianSense is that same operator over BART’s FFT rather than a NUFFT, with Sampling chained on; WaveSense is a composition of operators BART already has, chained by linop_chain, and is the same six in the same order that src/wave.c chains. Each is a single BART operator once built.

Coils is the sensitivity multiply on its own, with the same slab loop and the same kernels, for an encoding whose transform is not a Fourier transform and so cannot be a SENSE operator. It is what carries the coils through WaveSense, which is why sensitivities held as the k-space kernels nlinv produces work there too.

All of them take several sets of maps – ESPIRiT’s second, ENLIVE’s relaxed model – as (sets, coils, *spatial), which is what tools.ecalib and tools.nlinv return for maps > 1 and needs no reshaping in between. The image then carries the sets and the samples do not: the encoding is \(y_c = \sum_m S_{m,c} x_m\), contracted in BART’s own md_ztenmul rather than by anything in Python.

CartesianSense and WaveSense read a temporal subspace when given a basis – T2 shuffling and Wave-Shuffling, the forward pics -B builds. With toeplitz=True the normal collapses the frames into one coefficient-by-coefficient kernel, so an iteration never makes them: on sixty-four echoes over four coefficients that is sixteen times less k-space in the middle of every step. Nothing is convolved and no grid is doubled, since the pattern already lies on the grid the transform is circular over.

FieldCorrected wraps any of them and is a sum of chains rather than one: off-resonance during the readout is a different transform per sample, and time segmentation stands in for it with a short sum of ordinary encodings. The coefficients come from mri-nufft; everything applied is BART’s.

Linear operator class#

An operator is combined with others through the algebra its class defines, rather than by naming a combining class: A @ B composes, A + B and A - B add, c * A scales, A ** n repeats, A.H and A.T transpose, A.gram() and A.cogram() are the normal operators, and A[key] restricts its output the way indexing a tensor does. What each returns is private, because the algebra promises the operator, not the shape of the tree it is made of.

LinearOperator

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

Elementary operators#

Identity

The identity on shape, BART's linop_identity.

Zero

The operator that sends everything to zero, BART's linop_null.

Diagonal

Pointwise multiplication by diag, broadcast over the axes where it is one.

ComponentDiagonal

A diagonal on the real part and another on the imaginary part.

Conj

Complex conjugation, BART's linop_zconj.

Real

The real part, BART's linop_zreal.

FFT

BART's unitary Fourier transform along axes, centred by default.

NUFFT

Non-uniform FFT from coil images to samples along a trajectory.

MultiplySum

Multiply by a tensor and sum over the axes absent from the codomain.

Filtering and differencing#

Matrix

Multiplication by a matrix along one axis, BART's linop_matrix.

Convolve

Convolution with a fixed kernel, BART's linop_conv.

Gradient

Finite differences along axes, stacked on a new leading axis.

Combining operators#

The only functions here. Everything else BART offers maps a tensor to a tensor and is an operator in its own right, which makes it a class; these take operators and give back an operator, which is the one thing that cannot be written as arithmetic or as indexing. Each is one BART operator, so what a solver drives is a single operator rather than a list walked per iteration.

concatenate

Apply every operator to the same input and lay the results end to end.

stack

Apply every operator to the same input and put the results on a new axis.

hstack

Split the input between the operators and add up what they return.

block_diag

Give each operator its own part of the input and its own part of the output.

block

A grid of operators, as a block matrix is.

Shape#

Rearranging, reducing and restricting are operators like any other: each maps a tensor to a tensor, so each is a class, composes with @ and +, and is solved by bartorch.optim. Only something that takes operators and gives back an operator would be a function here.

Reshape

Read the same elements under another shape, BART's linop_reshape.

Transpose

Swap two axes, BART's linop_transpose.

Permute

Rearrange the axes, BART's linop_permute.

Flip

Reverse axes, BART's linop_flip.

Roll

Shift along one axis, BART's linop_shift.

Pad

Make each axis longer, BART's linop_padding.

Resize

Crop or zero-fill about the centre of each axis, BART's resize -c.

Extract

Take the block of oshape that starts at start, BART's linop_extract.

Hankel

A sliding window along axis, BART's linop_hankelization.

Sum

Sum over axes, BART's linop_sum.

ScaledSum

Sum over axes, divided by the square root of how many were summed.

Mean

Average over axes, BART's linop_avg.

Repeat

Repeat along axes up to shape, BART's linop_repmat.

Python-defined operators#

Callback

A linear operator from Python functions, applied through BART.