linop.CartesianSense#
- bartorch.linop.CartesianSense()#
Cartesian SENSE encoding operator.
The forward model is
\[A = P F S\]with \(S\) multiplication by the coil sensitivities, \(F\) the centred unitary Fourier transform over the spatial axes, and \(P\) the diagonal sampling operator given by
pattern. This is the encoding BART uses for Cartesian data.With a temporal basis \(\Phi\) of shape
(coeffs, frames)the optimization variable holds subspace coefficients, and the basis maps them to the acquired frames after the Fourier transform and before sampling:\[y[c, t] = P[t] \odot \sum_a \Phi[a, t] \, F(S[c] \odot x[a])\]so the domain carries coefficients where the codomain carries frames. This is the subspace model used for T2 shuffling.
Given neither a pattern nor a basis the operator reduces to sensitivity encoding followed by the Fourier transform, and
NoncartesianSensebuilds it directly. Given either, the pattern and the basis are applied inside the coil-slab loop together with the transform, so the samples are not revisited afterwards.- Parameters:
sensitivities (tensor) – Coil sensitivities
([batch, sets,] coils, [z,] y, x), or their k-space kernels withkernels=True.image_shape (tuple of int) – The image,
(*batches, [batch,] [sets,] [coeffs,] [z,] y, x). The samples are(*batches, coils, [frames,] [z,] y, x).pattern (tensor, default=None) – Binary sampling mask, one at acquired positions and zero elsewhere, broadcast over one coil’s samples
([frames,] [z,] y, x)– so(y, 1)undersamples a phase encode for every coil and batch item.positions (tensor, default=None) – Instead of a pattern, the acquired phase encodes as integer indices
([frames,] shots, d):(y,)in 2D,(z, y)in 3D,-1for padding where frames sample different numbers. The samples are then(*batches, coils, [frames,] shots, readout), the whole readout along each phase encode, and nothing the size of the phase-encode plane is held for them.readout ({"kspace", "image"}, default='kspace') – With positions, whether the samples are in k-space along the readout or already transformed back along it (hybrid space). Either way the volume is not transformed along the readout: the samples are.
basis (tensor, default=None) – Temporal subspace basis
(coeffs, frames).toeplitz (bool, default=True) – Apply the normal in closed form rather than as the forward and adjoint applications; see the notes.
kernels (bool, default=False) – Read
sensitivitiesas k-space kernels rather than maps.coil_batch (int, default=1) – Coils applied at once; 0 applies every coil together.
modulated (bool, default=False) – Answer in BART’s uncentred sample convention rather than the centred one. The two differ by an
fftmodon the sample axes; the centred convention is the default and is the onebartorch.fft()produces.device (device, default=None) – Where the operator is built and does its arithmetic.
ndim (int, default=None) – Number of spatial axes, where the sensitivities and the image do not determine it.
kspace_shape (tuple of int, default=None) – Sample shape, where it is not the default above.
fold_maps (bool, default=True) – Apply the sensitivities inside the normal’s transform where the arrangement allows it.
Notes
With a pattern or a basis,
toeplitzgives the normal as:(A^H A x)[k'] = sum_k ( sum_t |P[t]|^2 conj(B[k',t]) B[k,t] ) x[k]
between transforms along only the axes the pattern varies on. The sum over the frames is done once, when the operator is built, so an iteration never makes the frames, and a pattern flat along the readout never transforms it. In BART’s modulated convention the samples differ from the centred ones by a phase of modulus one, which leaves this normal as it is.
Examples
>>> A = CartesianSense(maps, (y, x), pattern=mask) # mask (y, 1) >>> x = bartorch.optim.CG(maxiter=30)(kspace, A)
>>> A = CartesianSense(maps, (4, y, x), pattern=mask, basis=phi) >>> A.ishape, A.oshape # phi (4, 64) ((4, y, x), (coils, 64, y, x))