Operator Algebra Logic#

At the heart of Pyxu’s microservice architecture is a powerful operator algebra logic allowing to create versatile and complex operators/functionals from fundamental building blocks. In this section, we’ll guide you through how to create and manipulate operators to construct intricate functionals and mappings. By the end, you’ll be an expert in leveraging the power of Pyxu’s operator algebra!

Arithmetic Operations on Operators#

In Pyxu, you can use a variety of arithmetic operations to combine basic operators into more complex ones. Here are some simple but powerful commands you can use:

op1 + op2       # Addition of two operators
op1 * op2       # Composition of two operators
op.argscale(c)  # Dilation by a scalar 'c'
op.argshift(x)  # Shifting by a vector 'x'
4 * op          # Scaling by a scalar
op.T            # Transposing

How Does it Work? 🛠️#

Every time you perform an arithmetic operation, Pyxu automatically infers the output type based on the properties of the operators involved in the operation. This type inference is super convenient because it saves you from manual calculations!

For example, Pyxu takes care of updating as needed methods like apply()🔗, jacobian()🔗, grad()🔗, prox()🔗, and adjoint()🔗 according to arithmetic rules. This means you can plug these composite operators directly into proximal gradient algorithms without having to implement manually gradients or proximal steps yourself.

Behind the Scenes: Arithmetic Rules#

Arithmetic inference is done via a set of arithmetic rules located in the pyxu.abc.arithmetic🔗 module:

  • Rule🔗: Base class for all arithmetic rules.

  • ScaleRule🔗: Handles scaling of operators by scalars.

  • ArgScaleRule🔗: Manages the dilation of operator parameters.

  • ArgShiftRule🔗: Takes care of shifting operator parameters.

  • AddRule🔗: Manages the addition of two operators.

  • ChainRule🔗: Deals with the composition of two operators.

  • TransposeRule🔗: Takes care of transposing a linear operator.

Rules are described in detail in the docstrings of the classes above. As an example, consider the composition of a DiffFunc🔗 f with a DiffMap🔗 L. Then their composition h = f * L is also a DiffFunc, with gradient given by:

h.grad(x) = L.jacobian(x).adjoint(f.grad(L(x)))

Building Block-Operators#

Block-operators, created using high-level functions like block_diag🔗 and stack🔗, are powerful tools in Pyxu for constructing complex operators from simpler components. They allow for flexible and efficient manipulation of operator structures.

For instance, consider the following example:

import pyxu.operator as pxo

# Define a Sum operator with dimension shape and codimension shape
op = pxo.Sum(dim_shape=(3, 4), axis=-1)  # (3,4)      ->   (3,1)

# Stack two instances of 'op', one being scaled by 2
A = pxo.stack([op, 2*op])                # (3,4)      ->   (2,3,1)

# Create a block diagonal operator using 'op' and '2*op'
B = pxo.block_diag([op, 2*op])           # (2,3,4)    ->   (2,3,1)

In this example, A and B are two distinct block sparse operators. A is a stacked operator combining op and 2*op, while B forms a block diagonal operator with the same components. Pyxu simplifies the creation of such complex operators by automatically inferring the output type and arithmetic methods from its building blocks, enabling efficient and expressive operator design.