Skip to content

tenet.pytree

JAX pytree registration: importing this module makes SymmetricTensor a pytree.

tenet.pytree

Opt-in JAX PyTree registration. import tenet.pytree; core never imports this.

tenet.enable_jax is the one-call spelling of that import, and is what the docs teach; this module stays importable directly and its effect is unchanged.

The contract: the leaves of a SymmetricTensor are its data -- one dense matrix per coupled sector, in map_layout(structure).sectors order; the aux data (the treedef) is its structure, which is frozen, hashable and array-free (invariant 8) and therefore a sound JIT cache key.

That is a handful of large leaves rather than tens of thousands of small ones, which is a strict improvement for JAX: the per-block slices whose VJPs used to produce thousands of single-primitive XLA compilations are gone, and jax.tree.map over a tensor now walks the coupled sectors.

_unflatten deliberately does not validate, because JAX calls it with sentinels, tracers and whatever a jax.tree.map returned (https://docs.jax.dev/en/latest/custom_pytrees.html). The public constructor remains the trust boundary: SymmetricTensor(T.structure, T.blocks) re-runs the full check.

Batching recipe::

batched = jax.tree.map(lambda *bs: jnp.stack(bs), T1, T2, T3)
jax.vmap(lambda T: tenet.norm(T) ** 2)(batched)

batched is a transport container, not a tensor: its block shapes do not match its structure and nothing in tenet.* should be called on it. Inside the vmapped function the leaves are BatchTracers whose .shape is the unbatched shape, so ops and their validation behave normally.

Complex-dtype gradients are unverified: norm goes through abs, whose JAX derivative needs care for complex input. Tests here are real float64.