Tutorial: a heterogeneous metric#
This tutorial walks through a small end-to-end example: representing a diagonal metric whose entries have mixed physical dimensions, then computing its determinant, inverse, and diagonal — all while unxts.linalg keeps the units straight. A single-unit unxt.Quantity could not represent such an object.
>>> import jax.numpy as jnp
>>> import quax
>>> import quaxed.numpy as qnp
>>> import unxt as u
>>> import unxts.linalg as ul
The setup#
Consider a 2-D configuration space with a radial coordinate (metres) and an angular coordinate (radians). A diagonal metric g that turns coordinate differences into a squared length has diagonal entries with different units: the radial part is m2 (length²) while the angular part carries m2 / rad2 so that g · dθ² still has units of length².
We build g as a 2×2 QuantityMatrix with per-element units:
>>> g = ul.QuantityMatrix(
... jnp.array([[1.0, 0.0], [0.0, 4.0]]),
... unit=(("m2", "m2"), ("m2", "m2 / rad2")),
... )
>>> g.unit.to_string()
'((m2, m2), (m2, m2 / rad2))'
Reading off the diagonal#
.diag() extracts the diagonal as a 1-D QuantityMatrix, preserving each entry’s (heterogeneous) unit — this is the part of the metric that actually scales each coordinate:
>>> d = g.diag()
>>> d.unit.to_string()
'(m2, m2 / rad2)'
>>> d.value
Array([1., 4.], dtype=float32)
Determinant#
The determinant of a diagonal metric is the product of its diagonal units — here m2 · m2/rad2:
>>> quax.quaxify(ul.det)(g).unit.to_string()
'm4 / rad2'
Inverse#
inv mixes the matrix entries, so it is only unit-correct when the units are uniform (the reciprocal then applies throughout). Our metric is heterogeneous, so inv refuses it rather than return a misleading per-element reciprocal:
>>> try:
... quax.quaxify(ul.inv)(g)
... except ValueError as e:
... print(str(e).split(";")[0])
inv on a QuantityMatrix requires uniform units (all entries equal)
For a uniform-unit metric the inverse carries the single reciprocal unit:
>>> h = ul.QuantityMatrix(jnp.array([[4.0, 0.0], [0.0, 1.0]]),
... unit=(("m2", "m2"), ("m2", "m2")))
>>> quax.quaxify(ul.inv)(h).unit.to_string()
'((1 / m2, 1 / m2), (1 / m2, 1 / m2))'
Recap#
A
QuantityMatrixlet us attach a distinct unit to every entry of the metric — impossible with a single-unitunxt.Quantity..diag()anddetpropagated those per-element units automatically;invis defined for uniform units (see Sharp bits).Everything is plain JAX underneath, so the same objects flow through
jax.jit,jax.grad, andjax.vmap.
See Sharp bits for the current restrictions (1-D/2-D only, and the uniform-unit requirements of inv and of diag under jax.jit).