Tags¤
Lineax offers a way to "tag" linear operators as exhibiting certain properties, e.g. that they are positive semidefinite.
If a linear operator is known to have a particular property, then this can be used to dispatch to a more efficient implementation, e.g. when solving a linear system.
Generally speaking, tags are an optional tool that can be used to improve your run time and/or compile time, by statically telling the linear solvers what properties they may assume about your system. However, if misused then you may find that the wrong result is silently returned.
In this way they are analogous to flags like scipy.linalg.solve(..., assume_a="pos")
.
Example
# Some rank-2 JAX array.
matrix = ...
# Some rank-1 JAX array.
vector = ...
# Declare that this matrix is positive semidefinite.
operator = lx.MatrixLinearOperator(matrix, lx.positive_semidefinite_tag)
# This tag is used to dispatch to a maximally-efficient linear solver.
# In this case, a Cholesky solver is used:
solution = lx.linear_solve(operator, vector)
# Whether operators are tagged can be checked:
assert lx.is_positive_semidefinite(operator)
Warning
Be careful, only the tag is actually checked, not the actual value of the matrix:
# Not a positive semidefinite matrix
matrix = jax.numpy.array([[1, 2], [3, 4]])
operator = lx.MatrixLinearOperator(matrix, lx.positive_semidefinite_tag)
lx.is_positive_semidefinite(operator) # True
lx.linear_solve(operator, vector) # Returns the wrong solution!
Of the built-in operators: lineax.MatrixLinearOperator
, lineax.PyTreeLinearOperator
, lineax.JacobianLinearOperator
, lineax.FunctionLinearOperator
, lineax.TaggedLinearOperator
directly support a tags
argument that mark them as having certain characteristics:
operator = lx.MatrixLinearOperator(matrix, lx.symmetric_tag)
You can pass multiple tags at once:
operator = lx.MatrixLinearOperator(matrix, (lx.symmetric_tag, lx.unit_diagonal_tag))
Other linear operators can be wrapped into a lineax.TaggedLinearOperator
if necessary:
operator = lx.MatrixLinearOperator(...)
symmetric_operator = operator + operator.T
lx.is_symmetric(symmetric_operator) # False
symmetric_operator = lx.TaggedLinearOperator(symmetric_operator, lx.symmetric_tag)
lx.is_symmetric(symmetric_operator) # True
Some linear operators are known to exhibit certain properties by construction, and need no additional tags:
lx.is_symmetric(lx.DiagonalLinearOperator(...)) # True
lx.is_positive_semidefinite(lx.IdentityLinearOperator(...)) # True
List of available tags¤
lineax.symmetric_tag
¤
Marks that an operator is symmetric. (As a matrix, \(A = A^\intercal\).)
lineax.diagonal_tag
¤
Marks than an operator is diagonal. (As a matrix, it must have zeros in the off-diagonal entries.)
For example, the default solver for lineax.linear_solve
uses this to dispatch to lineax.Diagonal
as the solver.
lineax.unit_diagonal_tag
¤
Marks than an operator has \(1\) for every diagonal element. (As a matrix \(A\), then it must have \(A_{ii} = 1\) for all \(i\).) Note that the whole matrix need not be diagonal.
For example, lineax.Triangular
uses this to cheapen its solve.
lineax.lower_triangular_tag
¤
Marks that an operator is lower triangular. (As a matrix \(A\), then it must have $A_{ij} = 0 for all \(i < j\).) Note that the diagonal may still have nonzero entries.
For example, the default solver for lineax.linear_solve
uses this to dispatch to lineax.Triangular
as the solver.
lineax.upper_triangular_tag
¤
Marks that an operator is upper triangular. (As a matrix \(A\), then it must have $A_{ij} = 0 for all \(i > j\).) Note that the diagonal may still have nonzero entries.
For example, the default solver for lineax.linear_solve
uses this to dispatch to lineax.Triangular
as the solver.
lineax.positive_semidefinite_tag
¤
Marks than operator is positive semidefinite.
For example, the default solver for lineax.linear_solve
uses this to dispatch to lineax.Cholesky
as the solver.
If you wish to mark that an operator is specifically postive definite then combine this with [lineax.nonsingular_tag
].
lineax.negative_semidefinite_tag
¤
Marks than operator is negative semidefinite.
For example, the default solver for lineax.linear_solve
uses this to dispatch to lineax.Cholesky
as the solver.
If you wish to mark that an operator is specifically postive definite then combine this with [lineax.nonsingular_tag
].