Skip to content

API Reference

This page documents the public API of JAX-AMG.

Solver

jaxamg.solve(A, b, x0=None, config=None, block_dim=1, comm=None, nglobal=None, partition_info=None, save_stats_file=None, reuse_setup=False, nullspace=None, transpose_nullspace=None, **kwargs)

Solve Ax=b using the AmgX backend. See Examples for usage.

Parameters:

Name Type Description Default
A MatrixOrOperator

Matrix or callable operator A(x). All matrices/operators are converted to jax.experimental.sparse.bcsr sparse matrices internally. In MPI mode this is the local partition.

required
b ArrayLike

Right-hand-side vector. In MPI mode this is the local RHS partition.

required
x0 ArrayLike | None

Optional initial guess (same shape as b; local partition in MPI mode). Defaults to zero. A good warm start (e.g. the previous solution in a time-stepping or optimization loop) cuts iterations; it does not change the converged solution or its gradients. Note that with the default RELATIVE_INI convergence the tolerance is relative to the initial residual, so a very good x0 tightens the target; consider convergence="ABSOLUTE" for warm-started loops.

None
config dict | None

AmgX configuration dictionary (see Solver Configuration for details). If None, JAX-AMG defaults are used.

None
block_dim int

Treat the matrix as a block matrix with square block_dim x block_dim blocks (e.g. coupled multi-component PDE systems with node-major interleaved unknowns: row i*block_dim + c is component c of node i). A and b keep their ordinary scalar CSR/vector form; the conversion to AmgX's BSR format happens internally. Rows must be divisible by block_dim (each rank's local partition in MPI mode). Since AmgX's classical AMG does not support blocks, the AMG defaults switch to aggregation (SIZE_2 + BLOCK_JACOBI); explicitly configured CLASSICAL AMG is rejected. In MPI mode the aggregation defaults use block-Jacobi coarse sweeps instead of DENSE_LU_SOLVER (which is broken in AmgX for distributed block matrices on 3+ ranks and rejected if configured explicitly); see Solver Configuration.

1
comm Comm | None

MPI communicator (typically mpi4py.MPI.COMM_WORLD). If provided, the solve runs in MPI mode. If not provided, MPI mode can still be used if MPI metadata has already been attached via with_cache(..., mpi=...).

None
nglobal int | None

Global matrix row count for MPI mode. Required when comm is provided and MPI metadata is not pre-attached to A.

None
partition_info tuple[int, int] | None

(row_start, row_end) owned by this rank in MPI mode. Required when comm is provided and MPI metadata is not pre-attached to A.

None
save_stats_file str | PathLike | None

Optional file path to save detailed AmgX solver statistics. If None, no file is created.

None
reuse_setup bool

For repeated solves with the same sparsity pattern, skip warm AMGX_solver_resetup and keep the cached hierarchy. This is cheaper per solve but may require more iterations if matrix coefficients change significantly.

False
nullspace NullSpaceSpec

Basis of null(A) for singular systems: "constant", a length-n vector, or an (n, k) array (local rows in MPI mode). The solution is pinned orthogonal to it, and the transpose of that pin projects the adjoint right-hand side onto range(Aᵀ), which makes the backward solve converge. Gradients w.r.t. A assume perturbations that preserve the declared null spaces (dA·N = 0, Mᵀ·dA = 0), as coefficient changes of a conservative discretization do. Defaults to the basis attached with with_cache.

None
transpose_nullspace NullSpaceSpec

Basis of null(Aᵀ) (same formats). b is projected onto range(A) (removed fraction in info["rhs_inconsistency"]) and, by transposition, the adjoint solution is pinned: the forward returns A⁺b, jax.grad returns (Aᵀ)⁺g. Equals nullspace for symmetric A (filled in when A is marked symmetric); for nonsymmetric A it differs, e.g. A = D⁻¹L has nullspace="constant" but transpose_nullspace=V (cell volumes). Defaults to the basis attached with with_cache.

None
**kwargs Any

Additional AmgX config parameters. These override values in config when both are provided.

{}

Returns:

Name Type Description
x Array

Solution vector (float32 or float64). In MPI mode, returns local portion.

info dict

Dictionary containing iterations, residual, status, and residual_history (residual norm per outer iteration, entry 0 being the initial residual; inside jit it has fixed length max_iters + 1 with NaN padding past entry iterations). With transpose_nullspace, also rhs_inconsistency (‖b − b'‖/‖b‖).

Warns:

Type Description
NullSpaceWarning

A·1 = 0 without a declared nullspace; nullspace without transpose_nullspace (or vice versa) for a matrix not marked symmetric; a basis failing A·N ≈ 0 / Aᵀ·M ≈ 0 (the latter not checked in MPI mode); or a DENSE_LU_SOLVER coarse solve. Checks run only on concrete matrix values.

Status Codes

jaxamg.AMGXStatus

Bases: IntEnum

High-level AmgX solve status codes returned in info["status"] after calling jaxamg.solve.

These values are mapped from the native backend status for quick checks in Python code and in docs.

Members
  • SUCCESS: Solve converged successfully.
  • FAILED: Solver failed due to an internal/runtime error.
  • DIVERGED: Iterations diverged.
  • NOT_CONVERGED: Reached stopping criteria without convergence.

JAX Sharding

jaxamg.ShardedMatrix

Distributed CSR matrix created by :func:make_sharded_matrix.

local_matrix(data=None)

Return this rank's unpadded BCSR matrix for data or cached values.

This is an eager helper: it reads the addressable shard of the packed values (typically to unpack a computed matrix gradient), so it cannot be applied to traced values inside jax.jit or another JAX transformation.

jaxamg.ShardedSolve

Callable sharded solver created by :func:make_sharded_solver.

__call__(b, x0=None, *, A=None, save_stats_file=None)

Solve with the cached values or an explicit A.

A is this rank's (n_local, n_global) operator or matrix with the sparsity fixed at solver creation, as solve(A, b) takes it, or the packed global values in the layout of ShardedMatrix.data. Operator parameters must be identical on every rank and receive the gradient of the global loss; packed values receive their per-entry gradient. A is required inside a JAX transformation. save_stats_file writes AmgX statistics after a direct call (rank 0 writes the file; requires save_stats=True at creation).

local_vector(value)

Return this rank's unpadded rows of a solver vector.

This is an eager helper: it reads the vector's addressable shard, so it cannot be applied to traced values inside jax.jit or another JAX transformation.

jaxamg.make_sharded_matrix(A_local, b, *, comm=None, mesh=None, axis_name='rank')

Create a distributed CSR container without replicating the global matrix.

The CSR column indices and row pointers remain rank-local static structure. Matrix values are packed into a global JAX array sharded over the same mesh axis as b. Unequal local nonzero counts are padded to the largest count; the padding is ignored by solves and gradients.

Parameters:

Name Type Description Default
A_local MatrixOrOperator

This process's CSR row partition with shape (n_local, n_global) and global column indices. A matrix-free operator is also accepted, provided it carries cached coloring information (jaxamg.with_cache(op, coloring=...)) so its shape and sparsity pattern are known; it is materialized once here. Null-space bases attached with with_cache(A_local, nullspace=..., transpose_nullspace=...) (local rows) are kept and applied by every solve.

required
b Array

Global row-sharded RHS used to validate the matrix partition, mesh, and numerical dtype.

required
comm Comm | None

MPI communicator spanning every JAX process, with rank order matching the JAX process order. Defaults to MPI.COMM_WORLD.

None
mesh Mesh | None

One-dimensional JAX device mesh. If omitted, use the mesh from b.sharding.

None
axis_name str

Name of the mesh axis that partitions rows and packed values.

'rank'

Returns:

Name Type Description
A ShardedMatrix

class:ShardedMatrix whose data attribute contains the global

ShardedMatrix

sharded values. The original global matrix is never materialized.

jaxamg.make_sharded_vector(local_values, *, comm=None, mesh=None, global_size=None, axis_name='rank')

Create a row-sharded vector, padding unequal partitions.

The returned JAX array has equal physical shard sizes, as required by NamedSharding. make_sharded_solver ignores each shard's padding and uses the true row counts from A_local. JAX array inputs are padded and assembled on device; other array-like inputs use a NumPy host staging path.

Parameters:

Name Type Description Default
local_values Any

This rank's unpadded values with shape (n_local,).

required
comm Comm | None

MPI communicator spanning every JAX process, with rank order matching mesh. Defaults to MPI.COMM_WORLD.

None
mesh Mesh | None

One-dimensional JAX device mesh with one device per MPI rank. Defaults to a mesh over the first comm.size JAX devices (all devices in a typical multi-process job).

None
global_size int | None

Optional true global length. When provided, it is checked against the sum of local lengths.

None
axis_name str

Mesh axis used to partition the vector.

'rank'

Returns:

Type Description
Array

A global JAX array whose axis uses P(axis_name). Its physical

Array

length is comm.size * max(local_sizes); values are cast to

Array

float32 unless already float32 or float64.

jaxamg.make_sharded_solver(A, b, *, config=None, is_symmetric=False, block_dim=1, reuse_setup=False, save_stats=False)

Create a solver for a globally sharded RHS.

Whether the solve is compiled is entirely the caller's choice: this function adds no jax.jit of its own. A caller that wraps its own function in jax.jit gets the whole pipeline compiled as part of that single program (through jax.shard_map). A direct, untransformed call instead executes the rank-local pipeline on this process's shard -- the same code path as solve(..., comm=...), with mpi4jax for the gradient exchanges -- and reassembles global arrays, so it matches the MPI interface's eager speed.

This interface complements, rather than replaces, solve(..., comm=...). JAX manages the global input and output arrays through shard_map while the AmgX solve itself uses the supplied MPI communicator. The interface is experimental; it uses a one-dimensional mesh and requires one MPI process with one mesh-local GPU per rank and a communicator spanning every JAX process.

jax.distributed.initialize() must be called before this function in a multi-process job. The matrix owns the communicator, mesh, local CSR structure, and globally sharded packed values. Pass A.data through solver(..., A=A.data) to differentiate matrix values. Use jax.set_mesh(A.mesh) around outer transforms such as jax.grad.

.. note:: Importing jaxamg disables XLA's cross-process sharded autotuning, which deadlocks on the per-process programs a sharded solve compiles to. Import it before the first JAX device call; see :doc:sharding.

Parameters:

Name Type Description Default
A ShardedMatrix

Distributed matrix created with :func:make_sharded_matrix.

required
b Array

Global row-sharded JAX vector. Construct it with :func:make_sharded_vector, which handles unequal row counts.

required
config dict[str, Any] | None

AmgX configuration. Defaults to the JAX-AMG MPI configuration.

None
is_symmetric bool

Whether the global matrix is symmetric. When False (the default), the distributed transpose is prepared once during solver creation for the reverse-mode adjoint.

False
block_dim int

AmgX block dimension. The matrix retains its scalar CSR representation, and every local row partition must be divisible by this value.

1
reuse_setup bool

Reuse the cached AmgX hierarchy across solves with the same sparsity pattern.

False
save_stats bool

Prepare the AmgX configuration with solver-statistics output enabled so a later direct call with solver(..., save_stats_file=...) produces a complete stats file.

False

Returns:

Type Description
ShardedSolve

A callable solver(b, x0=None, *, A=None, save_stats_file=None).

ShardedSolve

A is this rank's operator or matrix with the sparsity fixed here

ShardedSolve

(its closed-over parameters must be identical on every rank) or the

ShardedSolve

packed global values A.data; it may be omitted for a direct call

ShardedSolve

but is required under a JAX transform. A.local_matrix(gradient)

ShardedSolve

unpacks a packed matrix gradient and solver.local_vector(value)

ShardedSolve

removes vector padding. Info values have one entry per rank.

Warnings

jaxamg.NullSpaceWarning

Bases: UserWarning

Null-space issue in jaxamg.solve: a singular matrix without a declared nullspace, a missing nullspace/transpose_nullspace, a basis that is not a null space, or a DENSE_LU_SOLVER coarse solve on a singular system.

Caching

jaxamg.with_cache(A, *, coloring=None, mpi=None, is_symmetric=False, nullspace=None, transpose_nullspace=None)

Attach cached metadata (coloring, MPI info, symmetry, null spaces) to a matrix or operator.

This cache allows using matrices/operators inside JIT-compiled functions without recomputing metadata or passing it as separate arguments. See Caching Guide for more details.

Parameters:

Name Type Description Default
A MatrixOrOperator

A matrix or operator.

required
coloring tuple[ndarray, ndarray, ndarray, int, tuple[int, int]] | None

Cached coloring information from cache_coloring().

None
mpi dict[str, Any] | None

Cached MPI metadata from cache_mpi_metadata().

None
is_symmetric bool

If True, indicates the matrix is symmetric, allowing optimizations like skipping transpose in backward pass.

False
nullspace ArrayLike | str | None

Default for jaxamg.solve's nullspace ("constant", a vector, or an (n, k) array; local rows in MPI mode).

None
transpose_nullspace ArrayLike | str | None

Default for jaxamg.solve's transpose_nullspace.

None

Returns:

Type Description
MatrixOrOperator

The same matrix/operator with requested cache attached.

jaxamg.cache_coloring(operator, shape)

Compute and cache coloring information for a callable operator.

Detection uses two methods, so the result is correct for ANY operator:

  1. Tracing: interpret the operator's jaxpr to recover the EXACT sparsity in a single trace (no probing), then colour and materialise it. Works for any JAX-expressed operator; skipped for operators that can't be traced structurally (opaque calls, data-dependent indexing).
  2. Probing (probe_sparsity_pattern + get_column_coloring): exhaustive one-hot basis-vector probing, correct for any operator -- the fallback when tracing is unavailable.

Parameters:

Name Type Description Default
operator Any

A callable operator A(x) that returns A @ x.

required
shape tuple[int, int] | int

Shape of the operator (n, m) or int size (for an n×n matrix). For a distributed operator this is the local block (n_local, n_global).

required

Returns:

Type Description
tuple[ndarray, ndarray, ndarray, int, tuple[int, int]]

Cached coloring information for reattachment with with_cache(..., coloring=...).

jaxamg.cache_mpi_metadata(config, comm, nglobal, partition_info, A, is_symmetric=False, save_stats=False, block_dim=1, singular=False)

Pre-compute and cache MPI metadata for JIT-compatible solver usage.

The cached metadata can be reused across multiple JIT-compiled function calls with different matrices or operators (same structure).

Note

This function performs all non-traceable MPI operations outside the JIT boundary:

  • Computes static MPI communication metadata (recvcounts, displs)
  • Prepares MPI communicator pointer and local rank
  • Prepares config string
  • Computes max nnz across all ranks

Parameters:

Name Type Description Default
config dict

AmgX configuration dict or string

required
comm Comm

MPI communicator (from mpi4py.MPI.COMM_WORLD)

required
nglobal int

Global matrix size (total rows across all ranks)

required
partition_info tuple[int, int]

tuple (row_start, row_end) indicating which rows this rank owns

required
A MatrixOrOperator

Matrix or operator to compute max nnz for buffer sizing

required
is_symmetric bool

If True, the backward pass never transposes, so the transpose output size (nnz_out) is left unset (None). Should match the is_symmetric passed to with_cache; the default (False) computes it, which is always safe.

False
save_stats bool

If True, prepare the config with solver statistics output enabled, so a later solve(..., save_stats_file=...) on the cached matrix produces a complete stats file.

False
block_dim int

BSR block size for AmgX (see jaxamg.solve). Each rank's local partition must be divisible by it.

1
singular bool

Use the singular-system AMG defaults (see Solver Configuration). Implied when null-space bases are already attached to A via with_cache.

False

Returns:

Type Description
dict[str, Any]

A dictionary containing MPI metadata.

Note

The returned dictionary includes the following keys:

  • recvcounts_tuple: Tuple of row counts per rank
  • comm_ptr: MPI communicator pointer
  • lrank: Local GPU rank
  • nglobal: Global matrix size
  • config_str: Prepared configuration string
  • max_nnz: Maximum nnz across all ranks
  • nnz_out: This rank's local nnz(A^T) for the transpose output, or None when is_symmetric is True
  • halo_plan: Backward-pass halo-exchange plan for the gradient w.r.t. A (fetches only referenced remote solution entries)
  • transpose_plan: Fixed transpose structure and value routing for a nonsymmetric matrix, or None when is_symmetric is True
  • row_indices: Local CSR row index for every matrix nonzero

Preconditioner

jaxamg.make_preconditioner(A, config=None, *, comm=None, nglobal=None, partition_info=None, save_stats_file=None, return_info=False, **kwargs)

Create a callable approximate inverse for external Krylov solvers.

The returned callable can be passed directly as the M argument to jax.scipy.sparse.linalg.cg(...) or jax.scipy.sparse.linalg.bicgstab(...).

By default the approximate inverse is a single AMG V-cycle (solver="AMG", max_iters=1), so each application is one cheap AMG sweep. This is deliberately different from jaxamg.solve, whose default is a full Krylov solve (PBICGSTAB) preconditioned by AMG: here AMG is the preconditioner and the outer Krylov method owns the iteration. Pass config/kwargs for a stronger inner application (e.g. more sweeps, a W-cycle, or max_iters=2).

Parameters:

Name Type Description Default
A MatrixOrOperator

Matrix or callable operator to precondition.

required
config dict[str, Any] | None

Optional AmgX configuration. If omitted, a single-cycle AMG approximate-inverse config is used.

None
comm Comm | None

Optional MPI communicator for distributed solves. If A already has MPI metadata attached via jaxamg.with_cache(..., mpi=...), this may be omitted.

None
nglobal int | None

Global matrix row count for MPI mode.

None
partition_info tuple[int, int] | None

Local row partition (row_start, row_end) for MPI mode.

None
save_stats_file str | None

Optional stats output path passed to jaxamg.solve(...).

None
return_info bool

If True, the returned callable yields (x, info) instead of only x.

False
**kwargs Any

Additional solver config overrides.

{}

Returns:

Type Description
Callable

A callable representing an approximate inverse M^{-1}.

jaxamg.make_lineax_preconditioner(operator, config=None, *, tags=_INHERIT_TAGS, comm=None, nglobal=None, partition_info=None, save_stats_file=None, **kwargs)

Wrap a Lineax operator as an AMG preconditioner operator.

This is the operator->operator counterpart of make_preconditioner: it maps a system operator A (a lineax.AbstractLinearOperator) to a preconditioner operator M with M.mv(r) ≈ A⁻¹ r, ready to hand to a Lineax solver via options={"preconditioner": M}. It folds the usual make_preconditioner plus FunctionLinearOperator wiring into a single call.

The operator's matrix-free action (operator.mv) is handed to JAX-AMG, whose sparsity detection assembles the explicit matrix AmgX needs (traced in one pass when possible, probed otherwise). The pattern is detected and cached eagerly here, since Lineax solvers apply the preconditioner under jax.jit where on-the-fly detection is impossible. A MatrixLinearOperator is assembled directly from its concrete matrix instead.

Parameters:

Name Type Description Default
operator AbstractLinearOperator

The system operator to precondition.

required
config dict[str, Any] | None

Optional AmgX configuration (see make_preconditioner).

None
tags Any

Lineax tags for the returned preconditioner. By default the operator's own tags are inherited (A⁻¹ shares A's symmetry/definiteness), which CG needs in order to accept the preconditioner. Pass an explicit value (e.g. ()) to override.

_INHERIT_TAGS
comm Comm | None

Optional MPI communicator for distributed solves.

None
nglobal int | None

Global matrix row count for MPI mode.

None
partition_info tuple[int, int] | None

Local row partition (row_start, row_end) for MPI mode.

None
save_stats_file str | None

Optional stats output path passed to jaxamg.solve(...).

None
**kwargs Any

Additional solver config overrides forwarded to make_preconditioner.

{}

Returns:

Type Description
FunctionLinearOperator

A lineax.FunctionLinearOperator approximating A⁻¹.

Runtime Utilities

jaxamg.get_solver_cache_info()

Inspect the internal C++ AmgX solver caches.

Returns:

Type Description
dict[str, Any]

A dictionary with cache size/capacity and entry summaries

dict[str, Any]

for single-GPU and MPI caches, plus whether isolated mode

dict[str, Any]

(JAXAMG_CACHE_SIZE=0) is active.

jaxamg.clear_solver_cache()

Clear the internal C++ AmgX solver cache. This releases all cached AmgX resources (matrices, solvers, vectors).

jaxamg.finalize()

Manually finalize AmgX resources. This clears the cache and calls AMGX_finalize. Normally only needed to be called manually in MPI mode to avoid shutdown-time resource warnings.