linop.LinearOperator#
- class bartorch.linop.LinearOperator#
Bases:
OperatorA 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 byforward()andadjoint(), andnormal()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 ofA(x)isA.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()#
A x, without recording for autograd.outis written in place when given; for a BART-backed operator it must be a contiguous complex64 tensor ofoshapeon the input’s device.
- adjoint()#
A^H y, without recording for autograd.
- normal()#
A^H A x.A BART-backed operator applies its own normal, which for a non-Cartesian encoding built with
toeplitz=Trueis a convolution with a point spread function. Otherwiseadjoint(forward(x)).
- classmethod from_callbacks()#
An operator from Python functions, applied through BART.
forwardmapsishapetooshapeandadjointback;normal, where a cheaper form exists, isadjoint(forward(x))in one. Each receives a view of BART’s buffer, and every application crosses into Python.
- property plan#
The encoding form this operator was lowered into, or
None.Diagnostic: reading it changes nothing about the operator. An MRI encoding reports what the planner chose for it – the transform, the element-wise factors on each side of it, the contraction, what is streamed, how the normal is applied, and which executor path ran it. The algebra carries the plan through, so a composition with one encoding in it reports that encoding’s plan; anything else has none.
plan.fusedis the field to check: it is false where the coil-slab loop could not take the form and where a sum of terms was left as a chain, both of which give the same numbers several times slower.plan.executoris read back from the library after the build, so it reports the path that ran rather than the one intended.Accessing this on a composition builds it, since the plan is decided by lowering and lowering is what building does.
- 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 own adjoint constructor.The result is a BART operator rather than a Python wrapper, so
A.H @ Bis one operator and its normal isA A^H.
- property T#
A^T, the adjoint without the conjugation, asconj(A).H.
- conj()#
conj(A): conjugate the input, apply, conjugate the output.
- gram()#
A^H Aas an operator.BART’s own, so an encoding built with
toeplitz=Truegives the point-spread convolution rather than the two applications.
- cogram()#
A A^Has an operator.
- opnorm()#
The spectral norm, by BART’s power iteration on
A^H A.BART starts the iteration from its process-global generator, so this returns a slightly different number each time it is called. It is an estimate: what a step size or a Lipschitz constant needs, not an exact singular value.
- __call__()#
A x, recorded for autograd whenxrequires a gradient.- Parameters:
x (torch.Tensor) – Array of
ishape.out (torch.Tensor, default=None) – Contiguous complex64 array of
oshapeto write into, so that repeated applications reuse one buffer. Not allowed whenxrequires 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#
How many elements the domain holds.
- property codim_size#
How many elements the codomain holds.
- property dim_rank#
How many axes the domain has.
- property codim_rank#
How many axes the codomain has.
- to_nonlinear()#
The same operator as a
NonlinearOperator.
- pinv()#
(A^H A + damp I)^-1 A^H y, the damped least-squares solution.Solved in closed form where BART has one that has been checked, and by
bartorch.optim.CGotherwise; the same quantity either way.BART offers the closed form through a
norm_inv, which only linops/sum.c carries, and that routine divides by a count linop_sum_create overwrites afterwards – so it answers for a differently scaled operator than the one it is attached to. A class therefore opts in through_exact_pinvonce a test holds the closed form and the solver to the same answer, and onlyScaledSumdoes. Everything else takes the solver, chains, sums and adjoints included, since those drop thenorm_invregardless.- Parameters:
y (torch.Tensor) – Array of
oshape.damp (float, default=0.0) – The Tikhonov weight,
CG’slambda_.**kwargs – Passed to
CG, withx0as the warm start. Refused when the closed form applies, because there is then no solver for them to configure.