This page illustrates
the "linear operator" feature
of the Julia package
LinearMapsAA
.
Packages needed here.
using LinearMapsAA
using LinearAlgebra: tr, I
using InteractiveUtils: versioninfo
The "operator" aspect of this package
may seem unfamiliar
to some readers
who are used to thinking in terms of matrices and vectors,
so this page
describes a simple example:
the matrix
trace
operation.
The trace of a $N ร N$ matrix
is the sum of its $N$ diagonal elements.
We tend to think of this a function,
and indeed it is the tr
function
in the LinearAlgebra
package.
But it is a linear function
so we can represent it as a linear operator
$๐$ that maps a $N ร N$ matrix into its trace.
In other words,
$๐ : \mathbb{C}^{N ร N} \mapsto \mathbb{C}$
is defined by
$๐ X = \mathrm{tr}(X)$.
Note that the product
$๐ X$
is not a "matrix vector" product;
it is a linear operator acting on the matrix $X$.
(Note that we use a
fancy
unicode character
$๐$ here
just as a reminder that it is an operator;
in practical code we usually just use A
.)
The LinearMapsAA
package
can represent such an operator easily.
Here is the definition for $N = 5$:
N = 5
forw(X) = [tr(X)] # forward mapping function
๐ = LinearMapAA(forw, (1, N*N); idim = (N,N), odim = (1,))
The idim
argument specifies
that the input is a matrix of size N ร N
and
the odim
argument specifies
that the output is vector of size (1,)
.
One subtlety with this particular didactic example
is that the ordinary trace yields a scalar,
but
LinearMaps.jl
is (understandably) designed exclusively
for mapping vectors to vectors,
so we use [tr(X)]
above so that the output is a 1-element Vector
.
This behavior
is consistent with what happens
when one multiplies
a 1 ร N
matrix with a vector in $\mathbb{C}^N$.
Here is a verification that applying this operator to a matrix produces the correct result:
X = ones(5)*(1:5)'
๐ * X, tr(X), (N*(N+1))รท2
Although $๐$ here is not a matrix, we can convert it to a matrix (at least when $N$ is sufficiently small) to perhaps understand it better:
A = Matrix(๐)
A = Int8.(A) # just for nicer display
The pattern of 0 and 1 elements is more obvious when reshaped:
reshape(A, N, N)
Although this is largely a didactic example, there are optimization problems with trace constraints of the form $๐ X = b$. To solve such problems, often one would also need the adjoint of the operator $๐$.
Mathematically, and adjoint is a generalization of the (Hermitian) transpose of a matrix. For a (bounded) linear mapping $๐$ between inner product space $๐ณ$ with inner product $โจ \cdot, \cdot \rangle_๐ณ$ and inner product space $๐ด$ with inner product $โจ \cdot, \cdot \rangle_๐ด,$ the adjoint of $๐$, denoted $๐'$, is the unique bound linear mapping that satisfies $โจ ๐ x, y \rangle_๐ด = โจ x, ๐' y \rangle_๐ณ$ for all $x โ ๐ณ$ and $y โ ๐ด$.
Here, let $๐ณ$ denote the vector space of $N ร N$ matrices with the Frobenius inner product for matrices: $โจ A, B \rangle_๐ณ = \mathrm{tr}(A'B)$. Let $๐ด$ simply be $\mathbb{C}^1$ with the usual inner product $โจ x, y \rangle_๐ด = x_1^* y_1$.
With those definitions, one can verify that the adjoint of $๐$ is the mapping $๐' c = c_1 \mathbf{I}_N$, for $c โ \mathbb{C}^1$, where $\mathbf{I}_N$ denotes the $N ร N$ identity matrix.
Here is the LinearMapAO
that includes the adjoint:
back(y) = y[1] * I(N) # remember `y` is a 1-vector
๐ = LinearMapAA(forw, back, (1, N*N); idim = (N,N), odim = (1,))
Here is a verification that the adjoint is correct (very important!):
@assert Matrix(๐)' == Matrix(๐')
Int8.(Matrix(๐'))
This notebook was generated using Literate.jl.