bartorch.linop.CartesianSense

Contents

bartorch.linop.CartesianSense#

bartorch.linop.CartesianSense(sensitivities, image_shape, pattern=None, *, basis=None, toeplitz=True, **kwargs)#

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

What pics encodes on a grid: sensitivities, the centred unitary transform, and a pattern that keeps the samples the sequence acquired.

The transform is NoncartesianSense over BART’s own FFT instead of a NUFFT – the same operator, coil batching and all – with Sampling chained onto it. Without a pattern and without a basis it is that operator, returned unchanged, because there is nothing to add.

With a basis it is what grecon/model.c chains: the encoding over the coefficient images, the basis contracting them into frames, the pattern keeping the samples. The image is then (coeffs, 1, 1, 1, *spatial) and the samples (1, frames, 1, coils, *spatial).

Parameters:
  • sensitivities (tensor) – Coil sensitivities, (coils, *spatial), or their k-space kernels with kernels=True.

  • image_shape (tuple of int) – Coil-image shape, (coils, *spatial). A two-dimensional problem may leave BART’s third spatial axis out.

  • pattern (tensor, optional) – Ones where a sample was taken and zeros where it was not, broadcast over the axes it has one of – so (1, 1, y, 1) undersamples a phase encode across every coil and slice. With a basis it is read against the sample shape, so its second axis is the frames.

  • basis (tensor, optional) – Temporal subspace basis (coeffs, frames, 1, ...), contracting the image’s coefficients into the frames that were acquired. This is T2 shuffling and what pics -B takes.

  • toeplitz (bool) – With a basis, apply the normal as one coefficient-by-coefficient kernel rather than as the two applications. See the notes.

  • **kwargs – Passed to NoncartesianSense: coil_batch, kernels, modulated, device and the rest. modulated asks for BART’s own sample convention – the one pics works in – instead of the centred one, and does so whatever the slab.

Notes

Without a basis the normal is the two applications: a mask does not commute with the transform, so there is no shortcut, and on a grid there is nothing to gain from one anyway.

With a basis there is a great deal to gain, and it is the same shortcut the non-Cartesian encoding calls Toeplitz – the product in closed form rather than the two applications:

(A^H A x)[k'] = sum_k ( sum_t P[t] conj(B[k',t]) B[k,t] ) x[k]

The sum over the frames is done once, when the operator is built, so an iteration never makes the frames at all: sixty-four echoes over four coefficients is sixteen times less k-space in the middle of every step. Nothing is convolved and no grid is doubled – the pattern already lies on the grid the transform is circular over – which is the one way this differs from the non-Cartesian normal. It costs a kernel of coeffs x coeffs over the axes the pattern varies on, so a pattern that is flat along the readout keeps it flat too.

Examples

>>> A = CartesianSense(maps, (coils, y, x), pattern=mask)
>>> x = bartorch.optim.CG(maxiter=30)(kspace, A)
>>> A = CartesianSense(maps, (coils, y, x), pattern=mask, basis=phi)
>>> A.ishape, A.oshape
((4, 1, 1, 1, 1, y, x), (1, 64, 1, coils, 1, y, x))