The factor contract¶
These functions factor and solve a sparse matrix in code of your own, with any backend (linear solvers).
import jax.numpy as jnp
import numpy as np
from diff_soup.linear import Dense, SparseTopology, analyze, factor, solve
rows, cols = np.nonzero(np.ones((3, 3)))
analysis = analyze(SparseTopology(rows=rows, cols=cols, n_dofs=3), backend=Dense())
f = factor(analysis, jnp.asarray([4.0, -1.0, 0.0, -1.0, 4.0, -1.0, 0.0, -1.0, 3.0]))
x, f = solve(f, jnp.asarray([1.0, 2.0, -1.0]))
x_t, f = solve(f, jnp.asarray([1.0, 2.0, -1.0]), transpose=True)
Functions¶
Each function on a factor returns the factor to use next.
diff_soup.linear.analyze
¶
analyze(topology: SparseTopology, *, backend: LinearSolver, options: Any = None, layout: Optional[Layout] = None, precision: str = 'fp64', symmetric: bool = False, spd: bool = False) -> Analysis
Analyze a sparsity pattern for one backend, before any values are factored.
Equal arguments return the same Analysis, so the work runs once per pattern. A request
the backend cannot serve raises here, before anything is traced.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topology
|
SparseTopology
|
The |
required |
backend
|
LinearSolver
|
The backend object, for example |
required |
options
|
Any
|
Backend settings as a dict or a |
None
|
layout
|
Optional[Layout]
|
The device placement, or |
None
|
precision
|
str
|
The precision of the factors, |
'fp64'
|
symmetric
|
bool
|
Declare the matrix symmetric, which is never detected from the values. |
False
|
spd
|
bool
|
Declare the matrix symmetric positive definite, which implies |
False
|
Returns:
| Type | Description |
|---|---|
Analysis
|
The |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
For a setting, precision or layout the backend does not support, or
|
diff_soup.linear.factor
¶
factor(analysis: Analysis, values: ArrayLike, *, ctx: Optional[SolveContext] = None, orientation: str = 'A', tol: Optional[Tolerance] = None, after: Any = ()) -> Factor
Factor a matrix from its values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
analysis
|
Analysis
|
The |
required |
values
|
ArrayLike
|
One value per pattern entry, shape |
required |
ctx
|
Optional[SolveContext]
|
The |
None
|
orientation
|
str
|
|
'A'
|
tol
|
Optional[Tolerance]
|
A |
None
|
after
|
Any
|
A factor, or a sequence of factors, whose operations this one must follow. |
()
|
Returns:
| Type | Description |
|---|---|
Factor
|
The |
Raises:
| Type | Description |
|---|---|
FactorContractError
|
For values of the wrong shape, or no |
diff_soup.linear.empty_factor
¶
empty_factor(analysis: Analysis, *, ctx: Optional[SolveContext] = None, orientation: str = 'A', tol: Optional[Tolerance] = None, dtype: Any = None, storage: Optional[str] = None, after: Any = ()) -> Factor
A factor that holds no factorization yet, the start of a loop that refactors it.
Its solves return nan with SolveStatus.EMPTY until a refactor with a true predicate
fills it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
analysis
|
Analysis
|
The |
required |
ctx
|
Optional[SolveContext]
|
The |
None
|
orientation
|
str
|
|
'A'
|
tol
|
Optional[Tolerance]
|
As for |
None
|
dtype
|
Any
|
The dtype of the values, by default float64 or float32 to match the analysis. |
None
|
storage
|
Optional[str]
|
A name of 1 to 64 characters, and the factors of one name in one context share one storage. |
None
|
after
|
Any
|
A factor, or a sequence of factors, whose operations this one must follow. |
()
|
Returns:
| Type | Description |
|---|---|
Factor
|
The empty |
Example
def step(f, v):
f = refactor(f, v, True)
x, f = solve(f, b)
return f, x
f, xs = jax.lax.scan(step, empty_factor(analysis), values)
diff_soup.linear.refactor
¶
refactor(f: Factor, values: ArrayLike, predicate: ArrayLike, *, adopt: bool = False) -> Factor
Factor new values where predicate is true, and keep the factor where it is false.
Under jax.vmap each matrix of the batch takes its own predicate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f
|
Factor
|
The factor to update. |
required |
values
|
ArrayLike
|
The new values, in the dtype of |
required |
predicate
|
ArrayLike
|
A boolean scalar, |
required |
adopt
|
bool
|
|
False
|
Returns:
| Type | Description |
|---|---|
Factor
|
The updated |
Raises:
| Type | Description |
|---|---|
FactorContractError
|
For values of another shape or dtype, or a predicate that is not a scalar. |
diff_soup.linear.solve
¶
solve(f: Factor, b: ArrayLike, *, transpose: bool = False, tol: Optional[Tolerance] = None, weights: Optional[ArrayLike] = None, x0: Optional[ArrayLike] = None) -> tuple[Array, Factor]
Solve M x = b with a factor of M, or M^T x = b with transpose=True.
A factor that is singular, non-finite or empty solves to nan. An iterative solve that
stops short of tol returns its last iterate, and solve_checked reports the status.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f
|
Factor
|
The factor of |
required |
b
|
ArrayLike
|
The right-hand side, shape |
required |
transpose
|
bool
|
A Python bool, |
False
|
tol
|
Optional[Tolerance]
|
The |
None
|
weights
|
Optional[ArrayLike]
|
The error weights of an iterative solve's root-mean-square norm. |
None
|
x0
|
Optional[ArrayLike]
|
The initial guess of an iterative solve, zeros by default. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Array, Factor]
|
The solution |
diff_soup.linear.solve_checked
¶
solve_checked(f: Factor, b: ArrayLike, *, transpose: bool = False, tol: Optional[Tolerance] = None, weights: Optional[ArrayLike] = None, x0: Optional[ArrayLike] = None) -> tuple[Array, Factor, SolveReport]
Solve as solve does, and also return a SolveReport with the status and the residual.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f
|
Factor
|
The factor of |
required |
b
|
ArrayLike
|
The right-hand side, shape |
required |
transpose
|
bool
|
A Python bool, |
False
|
tol
|
Optional[Tolerance]
|
The |
None
|
weights
|
Optional[ArrayLike]
|
The error weights, which also make the reported residual a weighted root-mean-square norm. |
None
|
x0
|
Optional[ArrayLike]
|
The initial guess of an iterative solve, zeros by default. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Array, Factor, SolveReport]
|
The solution |
diff_soup.linear.invalidate
¶
invalidate(factor: Factor) -> Factor
Mark a factor's stored factorization out of date, so its next solve refactors from its values.
Use it when the storage may hold another factorization than the factor's values describe.
A factor held in arrays, as on Dense, has nothing to mark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
factor
|
Factor
|
The factor. |
required |
Returns:
| Type | Description |
|---|---|
Factor
|
The factor to use next. |
diff_soup.linear.refine
¶
refine(f: Factor, b: ArrayLike, *, values: ArrayLike, residual_target: float = DEFAULT_RESIDUAL_TARGET, max_steps: int = DEFAULT_MAX_STEPS, transpose: bool = False) -> tuple[Array, Factor, RefineReport]
Solve with a factor, then refine the solution against the values of the matrix.
Use it with a float32 factor of a float64 matrix, or a factor of a nearby matrix. It stops
at residual_target, after max_steps, or when a step fails to halve the residual.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f
|
Factor
|
A factor from a direct backend. |
required |
b
|
ArrayLike
|
The right-hand side. |
required |
values
|
ArrayLike
|
The values of the matrix to solve, one per pattern entry. |
required |
residual_target
|
float
|
The relative residual |
DEFAULT_RESIDUAL_TARGET
|
max_steps
|
int
|
The most correction steps, default |
DEFAULT_MAX_STEPS
|
transpose
|
bool
|
Solve with the transpose instead. |
False
|
Returns:
| Type | Description |
|---|---|
tuple[Array, Factor, RefineReport]
|
The solution, the factor to use next, and a |
Raises:
| Type | Description |
|---|---|
FactorContractError
|
For a factor from an iterative backend. |
diff_soup.linear.after
¶
after(f: Factor, *others: Factor) -> Factor
Order the next operations on a factor after those on other factors.
Use it for factors that share one named storage, so that work on one waits for the others.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f
|
Factor
|
The factor whose operations must wait. |
required |
*others
|
Factor
|
The factors to wait for. |
()
|
Returns:
| Type | Description |
|---|---|
Factor
|
|
Types¶
diff_soup.linear.SparseTopology
¶
The sparsity pattern of a square matrix, given by the coordinates of its entries.
The values passed to factor follow the order of rows and cols, one per entry, and
duplicate coordinates add. A Dirichlet row becomes an identity row, so the pattern must
hold its diagonal entry (i, i).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
Any
|
The row index of each entry. |
required |
cols
|
Any
|
The column index of each entry. |
required |
n_dofs
|
int
|
The number of rows and columns. |
required |
bc_indices
|
Any
|
The Dirichlet rows, or |
None
|
bc_indices
property
¶
bc_indices: Optional[ndarray]
The Dirichlet rows as a read-only int32 NumPy array, or None.
full
classmethod
¶
full(n: int) -> 'SparseTopology'
The pattern of a dense n by n matrix, with its entries in row-major order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
The number of rows and columns. |
required |
Returns:
| Type | Description |
|---|---|
'SparseTopology'
|
The |
diff_soup.linear.Analysis
¶
The analysis of a sparsity pattern for one backend, as returned by analyze.
Pass it to factor or empty_factor. It is immutable and hashable, and unpickling it
analyzes the pattern again.
Attributes:
| Name | Type | Description |
|---|---|---|
topology |
The |
|
backend |
The backend object. |
|
options |
The settings, as sorted |
|
layout |
The device placement. |
|
precision |
|
|
symmetric |
Whether the matrix was declared symmetric. |
|
spd |
Whether the matrix was declared symmetric positive definite. |
|
n |
int
|
The number of rows. |
nnz |
int
|
The number of pattern entries. |
diff_soup.linear.Factor
¶
A factorization of a matrix, as returned by factor, empty_factor and refactor.
It is a JAX pytree, so it passes through jax.jit, jax.vmap and loop carries. Pass on
the factor each operation returns, which keeps the operations in order.
Attributes:
| Name | Type | Description |
|---|---|---|
analysis |
Analysis
|
The |
orientation |
str
|
|
precision |
str
|
|
exact |
bool
|
Whether solves apply the exact inverse. |
values |
Array
|
The values the factor was built from. |
generation |
Array
|
|
storage |
Optional[str]
|
The storage name given to |
diff_soup.linear.SolveContext
¶
The owner of the factors that KLU, SuperLU and cuDSS store outside JAX arrays.
Pass it as ctx= to factor or empty_factor. Releasing it, or leaving its with
block, frees every factor it owns, and their solves raise from then on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
Optional[str]
|
A label shown in its |
None
|
Example
with SolveContext() as ctx:
f = factor(analysis, values, ctx=ctx)
x, f = solve(f, b)
release
¶
release() -> None
Free every factor this context owns.
A context that holds stored factors first waits for all running JAX work in the process. A second call does nothing.
Raises:
| Type | Description |
|---|---|
FactorContractError
|
For the default context of the solvers, which is never released. |
resident_bytes
¶
resident_bytes() -> int
The bytes of stored factors this context owns.
Returns:
| Type | Description |
|---|---|
int
|
The total, |
diff_soup.linear.SolveReport
¶
The status and the residual of one solve_checked call.
Its fields are arrays, so a report passes through jax.jit and jax.vmap.
Attributes:
| Name | Type | Description |
|---|---|---|
status |
Array
|
The |
residual |
Array
|
The largest |
iterations |
Array
|
The iteration count, |
applications |
Array
|
The count of operator or factor applications, |
ok |
Array
|
|
converged |
Array
|
The same as |
diff_soup.linear.SolveStatus
¶
The status code of a factorization or a solve, one int32 per matrix.
Attributes:
| Name | Type | Description |
|---|---|---|
SUCCESS |
The solve succeeded. |
|
SINGULAR |
The factorization met a zero pivot, and its solves return |
|
NONFINITE |
The values or the factor hold a non-finite number, and solves return |
|
MAX_ITERATIONS |
An iterative solve reached its iteration cap. |
|
STAGNATION |
An iterative solve stopped making progress. |
|
BREAKDOWN |
An iterative solve broke down. |
|
PRECONDITIONER_FAILURE |
The preconditioner failed to factor or to apply. |
|
EMPTY |
The factor holds no factorization yet, and its solves return |
diff_soup.linear.Tolerance
¶
The stopping rule of an iterative solve.
A solve of M x = b stops when ||w * (b - M x)||_rms <= atol + rtol * ||w * b||_rms,
with w the solve's weights, or ones without them.
Attributes:
| Name | Type | Description |
|---|---|---|
rtol |
float
|
The relative tolerance, at least 0. |
atol |
float
|
The absolute tolerance, at least 0. |
max_iter |
int
|
The iteration cap, at least 0. |
restart |
int
|
The iterations between GMRES restarts, at least 1, default |
diff_soup.linear.RefineReport
¶
The outcome of one refine call.
Attributes:
| Name | Type | Description |
|---|---|---|
steps |
Array
|
The number of corrections applied. |
stalled |
Array
|
|
residual |
Array
|
The final relative residual |
diff_soup.linear.FactorContractError
¶
Raised for a factor built or used in a way its backend does not allow.
It is a ConfigurationError. Examples are a released SolveContext, values of the
wrong shape, and a KLU factor in a function compiled for a GPU.
Matrix-free operators¶
The iterative solves work on a matrix given by its action.
diff_soup.linear.LinearOperator
¶
A matrix given by its action, for the iterative solves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mv
|
Callable[[Array], Array]
|
The function |
required |
rmv
|
Optional[Callable[[Array], Array]]
|
The function |
required |
shape
|
tuple[int, int]
|
The shape, |
required |
dtype
|
Any
|
The dtype of the entries. |
required |
tags
|
Iterable[str]
|
Structure tags from |
('square', 'nonsymmetric')
|
apply
¶
apply(x: ArrayLike, *, transpose: bool = False) -> Array
Apply the operator, M x, or M^T x with transpose=True.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ArrayLike
|
The vector to apply it to. |
required |
transpose
|
bool
|
Apply the transpose instead. |
False
|
Returns:
| Type | Description |
|---|---|
Array
|
The product. |
Raises:
| Type | Description |
|---|---|
FactorContractError
|
For |
diff_soup.linear.operator_from_values
¶
operator_from_values(analysis: Analysis, values: ArrayLike, *, transpose: bool = False) -> LinearOperator
The operator of an assembled matrix, from its analysis and values.
Its structure tags follow the symmetric and spd declarations of the analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
analysis
|
Analysis
|
The |
required |
values
|
ArrayLike
|
One value per pattern entry, shape |
required |
transpose
|
bool
|
Build the operator of the transpose instead. |
False
|
Returns:
| Type | Description |
|---|---|
LinearOperator
|
The |
diff_soup.linear.operator_from_jvp
¶
operator_from_jvp(residual: Callable[[Array], Array], primal: ArrayLike, *, shape: Optional[tuple[int, int]] = None, dtype: Any = None, tags: Optional[Iterable[str]] = None) -> LinearOperator
The Jacobian of residual at primal, as a matrix-free operator.
It linearizes residual once, and applying the operator never evaluates residual again.
The Jacobian is never assembled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
residual
|
Callable[[Array], Array]
|
A function from a vector |
required |
primal
|
ArrayLike
|
The point |
required |
shape
|
Optional[tuple[int, int]]
|
The expected shape |
None
|
dtype
|
Any
|
The dtype of the entries, by default the residual's output dtype. |
None
|
tags
|
Optional[Iterable[str]]
|
Structure tags, by default |
None
|
Returns:
| Type | Description |
|---|---|
LinearOperator
|
The |
Raises:
| Type | Description |
|---|---|
FactorContractError
|
If |
diff_soup.linear.gmres
¶
gmres(operator: LinearOperator, b: ArrayLike, *, precondition: Any = None, tol: Tolerance, weights: Optional[ArrayLike] = None, x0: Optional[ArrayLike] = None, transpose: bool = False) -> tuple[Array, SolveReport]
Solve M x = b with restarted GMRES on a matrix-free operator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
LinearOperator
|
The |
required |
b
|
ArrayLike
|
The right-hand side |
required |
precondition
|
Any
|
A |
None
|
tol
|
Tolerance
|
The |
required |
weights
|
Optional[ArrayLike]
|
The error weights of the stopping rule's root-mean-square norm. |
None
|
x0
|
Optional[ArrayLike]
|
The initial guess, zeros by default. |
None
|
transpose
|
bool
|
Solve |
False
|
Returns:
| Type | Description |
|---|---|
tuple[Array, SolveReport]
|
The solution and a |