Encoding form and lowering#
The rules behind Encoding operators: what the planner lowers,
what the plan reports, how the normal operator is applied, and how encoding
and batch axes are told apart. The code is src/bartorch/linop/_form.py (the
form and the plan), src/bartorch/linop/_plan.py (matching and lowering) and
src/csrc/ops/sense.c (the executor); the tests are tests/test_plan.py,
tests/test_sense.py and tests/test_linop_mri.py.
Sensitivity encoding#
A receive coil measures the transverse magnetization weighted by its sensitivity and integrated against the phase of the gradients. With \(x(r)\) the object, \(S_c(r)\) the sensitivity of coil \(c\) and \(k_j\) the k-space position of sample \(j\),
which on a voxel grid is \(A = PFS\): \(S\) maps the image to one coil image per
channel, \(F\) is the Fourier transform of each, and \(P\) selects the acquired
samples. Undersampling a phase-encoding direction by \(R\) folds \(R\) voxels
\(\mathrm{FOV}/R\) apart onto each other; with \(C \ge R\) coils whose
sensitivities at those voxels are linearly independent the fold is
invertible, and the SNR of the unfolded image is that of the fully sampled one
divided by \(g\sqrt{R}\), where the g-factor \(g \ge 1\) depends on the coil
geometry voxel by voxel. ecalib() estimates the
sensitivities by ESPIRiT from the ACS region, nlinv()
jointly with the image, and ncalib() from non-Cartesian
samples.
Factors of the form#
Symbol |
Name |
Instances |
|---|---|---|
\(I\) |
Image-side factor |
Coil sensitivities; spatial weights of the terms of a contraction, such as the field-map phase of a time segment |
\(T\) |
Transform |
FFT on the image grid; NUFFT along a trajectory; wave encoding, an FFT with a point spread function between the readout and phase-encode transforms |
\(O\) |
K-space-side factor |
Sampling pattern or table of sampled phase encodes; density weights; subspace basis; sample weights of the terms of a contraction; a slice phase |
\(\sum_a\) |
Contraction |
Subspace coefficients; time segments; shots; slices of a simultaneous multislice acquisition |
Lowering#
Composing with @ and + records a description and builds nothing
(_Composition); the shapes are worked out from the operands. The first
operation that needs the handle – an application, .H, a solve, or reading
.plan – offers the whole description to _plan.describe and _plan.lower.
A product of
Diagonaloperators around one encoding is aChain; a sum of chains is aSum.A sum whose terms share one encoding is a contraction, and is lowered into the form’s own sum over terms (
contraction="segments"). Terms on two different encodings are not a contraction, and stay a sum.Along a trajectory, a contraction whose k-space weights vary along the samples is lowered into a subspace over the samples (
contraction="subspace"), which has a normal kernel per pair of terms.A term that picks one set of sensitivities whole, with a phase per set in k-space, is a slice (
contraction="slices"): the coil images keep the sets, the transform runs once per slice, and the slices are added after the k-space factor.An image weight that differs between sets of sensitivities is applied to each set before the sensitivities sum them, inside the coil loop.
Weights that vary along the coils cannot be cut into coil slabs, so such a sum is built as BART’s sum of chains (
contraction="chained").Anything that is not element-wise on either side of one of the three transforms has no description and is built as it stands. An operator around the encoding that is not a diagonal is chained to the lowered encoding, and the plan reported is the encoding’s.
Plan fields#
A.plan is read back from the library after the operator is built, so it
reports what ran.
Field |
Reports |
|---|---|
|
|
|
The element-wise factors on each side of the transform |
|
|
|
|
|
Coils per slab (0 is every coil at once, BART’s own chain), and what is walked a slab at a time |
|
|
|
|
|
Trajectory axes found on the image grid and transformed by an FFT, the NUFFT then over the others: |
|
Items along encoding axes the trajectory varies along and the image varies along too, each with its own transform and normal kernel |
Normal operator#
plan.normal is one of three.
"kernel", on a grid. With a pattern \(P\), \(A^H A = \sum_c \overline{S_c} F^H P F S_c\). With a subspace basis \(\Phi\) the pattern becomes a kernel over pairs of coefficients, \(K_{aa'}(k) = \sum_t \overline{\Phi_{at}}\, \Phi_{a't}\, P_t(k)\), computed once at build time, so the cost does not grow with the frames. A wave pattern that varies along the readout has no such kernel, because the wave point spread function sits between the readout and phase-encode transforms."kernel", along a trajectory. The transform’s normal \(Q = \mathrm{NUFFT}^H W^H W\, \mathrm{NUFFT}\) is a convolution with a point spread function (NUFFT substitution). The full normal \(\sum_c \overline{S_c}\, Q\, S_c\) is not translation invariant and is applied coil by coil. With a subspace basis the kernel of \(Q\) is taken over pairs of coefficients."transform"where there is no k-space factor and the transform’s own normal is the whole of it, and"applications"– the forward followed by the adjoint – for a Cartesian or wave contraction over segments or slices, for a wave pattern along the readout, and withtoeplitz=False.
Encoding and batch axes#
Axis |
Definition |
Examples |
Cost |
|---|---|---|---|
Encoding axis |
Indexed by the trajectory or the sampling pattern |
Frames, echoes, cardiac phases |
Joins the samples of one transform when the image does not vary along it; one transform per item, under the one coil loop, when it does ( |
Batch axis |
Not indexed by the trajectory; items share it and are encoded independently |
Slices with their own sensitivities, averages, repetitions |
One transform plan for all items |
Where a basis contracts an encoding axis, the image carries coefficients and
every sample the trajectory indexes belongs to one point set. A batch the
sensitivities vary along is written (batch, 1, coils, y, x), a batch axis in
front of a sets axis, and is a dimension of the operator rather than a block.
Under BART’s own commands, the NUFFT substitution refuses an image that varies
along a trajectory axis, with the reason.