Skip to content

Cholesky factorization¤

jaxmg.potrf computes \(A=LL^{\dagger}\) for a symmetric or Hermitian positive-definite matrix, returning the lower-triangular factor \(L\) without performing a solve.

Common setup¤

This example uses four Python processes and a \(2 \times 2\) process grid. See distributed execution for initialization and launch options.

import jax

jax.config.update("jax_enable_x64", True)
jax.distributed.initialize()

import jax.numpy as jnp
from jax.sharding import NamedSharding, PartitionSpec as P
from jaxmg import potrf


mesh = jax.make_mesh((2, 2), ("pr", "pc"))
jax.set_mesh(mesh)
matrix_sharding = NamedSharding(mesh, P("pr", "pc"))

T_A = 128
N = 512


@jax.jit
def make_matrix():
    expected = jnp.diag(jnp.linspace(1.0, 2.0, N, dtype=jnp.float64))
    expected += jnp.diag(jnp.full(N - 1, 0.5), k=-1)
    a = expected @ expected.T
    return jax.reshard(a, matrix_sharding), jax.reshard(expected, matrix_sharding)

Compute the factor¤

a, expected = make_matrix()
factor = potrf(a, T_A=T_A)

# Validate the factor against the known solution.
correct = jnp.allclose(factor, expected)
correct.block_until_ready()
if jax.process_index() == 0:
    print(correct)

Warning

The public wrapper donates a to the compiled factorization. Do not use the input array after the call. Use donate=False to preserve it. This is less memory efficient because the original input and working buffer must coexist.

Advanced: control the outer jax.jit¤

potrf_jit_ctx leaves the outer compilation and donation boundary to the caller. Its first result is the factor, which aliases the input work buffer.

from functools import partial

from jaxmg import potrf_jit_ctx


@partial(jax.jit, donate_argnums=(0,))
def compiled_potrf(a):
    return potrf_jit_ctx(a, T_A=T_A)


a, _ = make_matrix()
factor, status = compiled_potrf(a)
factor.block_until_ready()

When a is created inside the compiled function, XLA owns its lifetime and donate_argnums is unnecessary.

Advanced: call from inside jax.shard_map¤

For a factorization within an existing shard-local calculation, pass the local matrix block to potrf_shardmap_ctx. It returns the corresponding local factor block, with tile padding removed.

from jaxmg import potrf_shardmap_ctx


@partial(jax.jit, donate_argnums=(0,))
@partial(jax.shard_map, mesh=mesh, in_specs=P("pr", "pc"),
         out_specs=(P("pr", "pc"), P(("pr", "pc"))))
def local_factorize(a_block):
    return potrf_shardmap_ctx(a_block, T_A=T_A)


a, _ = make_matrix()
factor, status = local_factorize(a)
factor.block_until_ready()

Ready-to-run scripts for the direct and caller-jitted interfaces are provided in the examples directory. See the potrf API reference for the complete argument and return-value documentation.