Linear Operator#

Defining new linear operators mainly involves the definition of the forward and adjoint routines. The following code shows the template for defining them. Similar to a proxable function, the operator is defined as a class inheriting from the base class LinOp.

class new_linop(LinOp): 
   def __init__(...):
      """ Custom initialization code. 
      """
      
   def forward(self, inputs):
        """The forward operator. Compute x -> Kx
        """

   def adjoint(self, inputs):
        """The adjoint operator. Compute x -> K^Tx
        """

   def is_diag(self, freq):
      """(Optional) Check if the linear operator is diagonalizable or
         diagonalizable in the frequency domain.
      """

   def get_diag(self, x, freq):
      """(Optional) Return the diagonal/frequency diagonal matrix that 
         matches the shape of input x.
      """

By default, the linear operator is not diagonal. To introduce a diagonal linear operator, one must implement the is_diag and get_diag for checking the diagonalizability and acquiring the diagonal matrix. These methods allow ∇-Prox to construct more efficient solvers, e.g., ADMM with a closed-form matrix inverse for the least-square update.

Sanity Check#

Typically, it is not always easy to correctly implement the forward and adjoint operations of the linear operator. To facilitate the testing of these operators, ∇-Prox provides an implementation of the dot-product test for verifying that the forward and adjoint are adjoint to each other.

Basically, the idea of the dot-product test comes from the associative property of linear algebra, which gives the following equation:

\[ y^T(Ax) = (A^Ty)^Tx \]

Here, \(x\) and \(y\) are randomly generated data, and \(A\) and \(A^T\) denote the forward and adjoint of the linear operator. ∇-Prox uses of this property and generates a large number of random data arguments to check if this equation always holds for a given precision.

To use this utility, users can call the validate(linop, tol=1e-6) and specify the tolerance of the difference between two sides of the equation.

import dprox as dp
from dprox.utils.examples import fspecial_gaussian

x = dp.Variable()
psf = fspecial_gaussian(15, 5)
op = dp.conv(x, psf)
assert dp.validate(op)

Indices#

class dprox.linop.base.LinOp(input_nodes=[])[source]#

Abstract class for all linear operator.

property T: LinOp#

The transpose \(A^T\) of this linear operator \(A\).

clone() LinOp[source]#

The deep copy of this linear operator.

property constants#

Returns a list of constants in the LinOp.

abstract forward(inputs)[source]#

The forward operator. Compute x -> Kx

get_diag(freq=False)[source]#

Returns the diagonal representation (K^TK)^(1/2).

Parameters:

freq (bool) – Is the diagonal representation in the frequency domain?

Returns:

The diagonal operator acting on each variable.

Return type:

dict of variable to ndarray

property gram: LinOp#

The gram \(A^TA\) of this linear operator \(A`$\).

is_constant()[source]#

Is the LinOp constant?

is_diag(freq=False)[source]#

Is the lin op K diagonal (in the frequency domain)?

is_gram_diag(freq=False)[source]#

Is the lin op’s Gram matrix K^TK diagonal (in the frequency domain)?

norm_bound(input_mags)[source]#

Gives an upper bound on the magnitudes of the outputs given inputs.

Parameters:

input_mags (list) – List of magnitudes of inputs.

Returns:

Magnitude of outputs.

Return type:

float

property offset#

Get the constant offset.

property variables#

Return the list of variables used in the LinOp.

class dprox.linop.conv.conv(arg, kernel)[source]#

Circular convolution of the input with a kernel.

adjoint(input, **kwargs)[source]#

The adjoint operator. Compute x -> K^Tx

forward(input, **kwargs)[source]#

The forward operator. Compute x -> Kx

class dprox.linop.conv.conv_doe(arg: LinOp, psf: Placeholder | Tensor | array, circular: bool = False)[source]#

Circular convolution of the input with a kernel.

adjoint(img, **kwargs)[source]#

The adjoint operator. Compute x -> K^Tx

forward(img, **kwargs)[source]#

The forward operator. Compute x -> Kx

class dprox.linop.sum.sum(input_nodes)[source]#

Sums its inputs.

adjoint(input, **kwargs)[source]#

The adjoint of sum spread of the input to all its child

forward(*inputs, **kwargs)[source]#

Just sum all the inputs, all inputs should have the same shape

class dprox.linop.sum.copy(arg)[source]#
adjoint(*inputs, **kwargs)[source]#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(inputs, **kwargs)[source]#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.vstack.vstack(input_nodes)[source]#

Vectorizes and stacks inputs.

adjoint(*inputs, **kwargs)[source]#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(*inputs, **kwargs)[source]#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.vstack.split(output_nodes)[source]#
adjoint(*inputs, **kwargs)[source]#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(*inputs, **kwargs)[source]#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.variable.Variable(shape=None, value=None, name=None)[source]#

A variable.

adjoint(inputs, **kwargs)[source]#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(inputs, **kwargs)[source]#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.subsample.mosaic(arg)[source]#
adjoint(input, **kwargs)[source]#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(input, **kwargs)[source]#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.scale.scale(scalar, arg)[source]#

Multiplication scale*X with a fixed scalar.

adjoint(input, **kwargs)[source]#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(input, **kwargs)[source]#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.placeholder.Placeholder(default=None)[source]#
adjoint(value, **kwargs)#

The adjoint operator.

Reads from inputs and writes to outputs.

forward(*value, **kwargs)#

The forward operator.

Reads from inputs and writes to outputs.

class dprox.linop.grad.grad(arg, dim=1)[source]#

gradient operation. can be defined for different dimensions. default is n-d gradient.

adjoint(input, **kwargs)#

The adjoint operator. Compute x -> K^Tx

forward(input, **kwargs)#

The forward operator. Compute x -> Kx