This package implements Automatic Sparse Differentiation (ASD) in JAX.
ASD exploits sparsity to reduce the cost of computing sparse Jacobians and Hessians:
- Detection: Analyze the jaxpr computation graph to detect the global sparsity pattern
- Coloring: Assign colors to rows so that rows sharing non-zero columns get different colors
- Decompression: Compute one VJP/HVP per color instead of one per row, then extract the sparse matrix
src/asdex/
βββ __init__.py # Public API
βββ _arguments.py # Argument handling and validation (argnums, kwargs binding, dtype/structure checks)
βββ _check.py # Correctness checks (check_jacobian_correctness, check_hessian_correctness)
βββ _display.py # Display/formatting utilities
βββ _errors.py # Errors and warnings (VerificationError, InvalidColoringError, DenseColoringWarning)
βββ _plotting.py # Matplotlib visualizations for SparsityPattern and ColoredPattern
βββ coloring/ # Graph coloring (row, column, symmetric) and convenience functions
βββ decompression/ # Compress (one VJP/JVP/HVP per color, producing B) then decompress it into the sparse matrix, plus the public API
βββ detection/ # Jacobian and Hessian sparsity detection via jaxpr analysis
β βββ _interpret/ # Custom jaxpr interpreter for index set propagation
βββ _differentiation.py # Batched-AD engine: one VJP/JVP/HVP per color, producing the compressed matrix B
βββ _pattern.py # SparsityPattern and ColoredPattern data structures
βββ _pytree.py # Generic PyTree <-> flat array plumbing (flatten, unflatten, size, dtype)
βββ _types.py # Type aliases for AD modes and output formats (JacobianMode, HessianMode, OutputFormat) and mode validators
The interpreter internals are described in src/asdex/detection/_interpret/CLAUDE.md.
The decompression internals are described in src/asdex/decompression/CLAUDE.md.
The structure of the test folder is described in tests/CLAUDE.md.
uv run ruff check --fix . # lint + auto-fix
uv run ruff format . # format
uv run ty check # type check
uv run pytest # run tests- Favor
matchstatements over long if-else chains. Use explicit cases and default tocase _ as unreachable: assert_never(unreachable). - Use plain
# Section namecomments for section separators, not banner-style# -- Section name ---.
jacobian(f, input_shape)(x) # one-call API
jacobian_from_coloring(f, coloring)(x) # from pre-computed coloring
β
ββ 1. DETECTION
β jacobian_sparsity(f, input_shape)
β ββ make_jaxpr(f) β jaxpr
β ββ _prop_jaxpr() β index sets
β ββ SparsityPattern
β
ββ 2. COLORING
β jacobian_coloring_from_sparsity(sparsity)
β
ββ 3. DECOMPRESSION
Compress: one VJP or JVP per color β B
Decompress: scatter B into the pattern
Precompute: jacobian_coloring(f, shape) = detect + color
hessian(f, input_shape)(x) # one-call API
hessian_from_coloring(f, coloring)(x) # from pre-computed coloring
β
ββ 1. DETECTION
β hessian_sparsity(f, input_shape)
β ββ jacobian_sparsity(grad(f), input_shape)
β
ββ 2. COLORING
β hessian_coloring_from_sparsity(sparsity)
β
ββ 3. DECOMPRESSION
Compress: one HVP per color (fwd-over-rev) β B
Decompress: scatter B into the pattern
Precompute: hessian_coloring(f, shape) = detect + color_symmetric
The decompression step splits into compress (build the dense B) and decompress (scatter B into the pattern),
which never import each other.
The compressed_* / value_and_compressed_* entry points stop at B,
and decompress / decompress_data turn a caller-supplied B back into a sparse matrix.
See src/asdex/decompression/CLAUDE.md.
Use Conventional Commits for all commit messages (e.g. feat:, fix:, docs:, refactor:, test:).
For breaking changes, add ! after the type (e.g. feat!:).
When writing new code, adhere to these design principles:
-
Minimize complexity: The primary goal of software design is to minimize complexityβanything that makes a system hard to understand and modify.
-
Information hiding: Each module should encapsulate design decisions that other modules don't need to know about, preventing information leakage across boundaries.
-
Pull complexity downward: It's better for a module to be internally complex if it keeps the interface simple for others. Don't expose complexity to callers.
-
Favor exceptions over wrong results: Raise errors for unknown edge cases rather than guessing.