How to check quantity types at runtime#
A Quantity carries its dtype and shape in its type, so jaxtyping annotations work on it directly and are checked both statically and at runtime.
from jaxtyping import Float
import unxt as u
def velocity(
x: Float[u.Quantity, "N"],
t: Float[u.Quantity, "N"],
) -> Float[u.Quantity, "N"]:
return x / t
Annotations alone are inert β something has to enforce them. There are two ways to turn enforcement on.
Turn it on for a single function#
To check one function without touching the rest of your program, decorate it with jaxtypingβs jaxtyped and a typechecker:
>>> from jaxtyping import Shaped, jaxtyped
>>> from beartype import beartype as typechecker
>>> import unxt as u
>>> @jaxtyped(typechecker=typechecker)
... def velocity(
... x: Shaped[u.Quantity, "N"],
... t: Shaped[u.Quantity, "N"],
... ) -> Shaped[u.Quantity, "N"]:
... return x / t
>>> x = u.Q([2.], "m")
>>> t = u.Q([1.], "s")
>>> velocity(x, t)
Quantity(Array([2.], dtype=float32), unit='m / s')
The check earns its keep when an argument violates the annotation. Both parameters above share the axis name "N", so mismatched shapes are rejected before the body runs:
>>> x2 = u.Q([2.0, 3.0], "m") # shape (2,)
>>> t2 = u.Q([1.0], "s") # shape (1,)
>>> try:
... velocity(x2, t2)
... except Exception as e:
... print(type(e).__name__)
TypeCheckError
Without a typechecker active the call would simply broadcast (2,) against (1,) and return a wrong answer quietly.
Turn it on for everything#
To check all of unxt β and, through jaxtypingβs import hook, your own annotated functions β set an environment variable to any runtime typechecker backend jaxtyping supports:
export UNXT_ENABLE_RUNTIME_TYPECHECKING="beartype.beartype"
The variable takes three values:
Value |
Effect |
|---|---|
|
No import hook is installed. The default, absent the variable. |
|
Import hook installed with no typechecker: only |
any other string |
Used as the |
To turn checking back off, set it to "False":
export UNXT_ENABLE_RUNTIME_TYPECHECKING="False"
If you would rather not manage the shell environment, set it from Python β but it must happen before importing unxt, or anything that imports unxt:
import os
os.environ["UNXT_ENABLE_RUNTIME_TYPECHECKING"] = "beartype.beartype"
Attention
Enable runtime type checking during development. For production runs, measure with it on and off: the overhead is usually small, but it can affect how long jax.jit takes to compile.
Constrain the physical dimension#
Annotations on the default Quantity constrain dtype and shape but not dimension: Quantity["length"] is Quantity, and the subscript does nothing. To have an argumentβs dimension checked, use ParametricQuantity from the separate unxts.parametric package, which encodes the dimension in its type β see Dimension annotations for type checking.
See also#
Quantity β what the default
Quantitydoes and does not carry in its type.Why Quantity is not parametric β why the default stops at dtype and shape.
The
jaxtypinglibrary and thetypingmodule.