unxt.experimental đź§Ş#
Experimental features.
Warning
These features may be removed or changed in the future without notice.
On some occasions JAX’s automatic differentiation functions do not work well with quantities. This is checked by enabling runtime type-checking (see the docs), which will raise an error if a quantity’s units do not match the expected input / output units of a function. In these cases, you can use the functions in this module to provide the units to the automatic differentiation functions. Instead of directly propagating the units through the automatic differentiation functions, the units are stripped and re-applied, while also being provided within the function being AD’d.
To import this experimental module
>>> from unxt import experimental
- unxt._src.experimental.grad(fun: Callable[[Unpack[Args]], R], argnums: int = 0, *, units: tuple[UnitBase | FunctionUnitBase | str | None, ...])#
Gradient of a function with units.
In general, if you can use
quax.quaxify(jax.grad(func))(or the syntactic sugarquaxed.grad(func)), that’s the better option! The difference from those functions is how units are handled.quaxifywill directly propagate the units through the automatic differentiation functions. But sometimes that doesn’t work and we need to strip the units and re-apply them. This function does that, using theunitskwarg.See also
jax.gradThe original JAX gradient function.
Examples
>>> import jax.numpy as jnp >>> import unxt as u
>>> def cube_volume(x: u.Q["length"]) -> u.Q["volume"]: ... return x**3
>>> grad_cube_volume = u.experimental.grad(cube_volume, units=("m",)) >>> grad_cube_volume(u.Q(2.0, "m")) Quantity(Array(12., dtype=float32...), unit='m2')
Inputs are converted to
unitsfirst, so a convertible input gives the same result (200 cmis2 m):>>> grad_cube_volume(u.Q(200.0, "cm")) Quantity(Array(12., dtype=float32...), unit='m2')
A
Noneentry inunitsmarks an argument as a plain (unitless) value rather than a Quantity, so functions can mix the two:>>> def scaled_area(distance, factor): ... return distance**2 * factor
>>> g = u.experimental.grad(scaled_area, argnums=0, units=("m", None)) >>> g(u.Q(3.0, "m"), 2.0) Quantity(Array(12., dtype=float32...), unit='m')
- Parameters:
fun (
Callable[[Unpack[TypeVarTuple]],TypeVar(R, bound=AbstractQuantity)])argnums (
int)units (
tuple[UnitBase|FunctionUnitBase|str|None,...])
- Return type:
Callable[[Unpack[TypeVarTuple]],TypeVar(R, bound=AbstractQuantity)]
- unxt._src.experimental.hessian(fun: Callable[[Unpack[Args]], R], argnums: int = 0, *, units: tuple[UnitBase | FunctionUnitBase | str | None, ...])#
Hessian.
In general, if you can use
quax.quaxify(jax.hessian(func))(or the syntactic sugarquax.hessian(func)), that’s the better option! The difference from those functions is how units are handled.quaxifywill directly propagate the units through the automatic differentiation functions. But sometimes that doesn’t work and we need to strip the units and re-apply them. This function does that, using theunitskwarg.See also
jax.hessianThe original JAX hessian function.
Examples
>>> import jax.numpy as jnp >>> import unxt as u
>>> def cubbe_volume(x: u.Q["length"]) -> u.Q["volume"]: ... return x**3
>>> hessian_cubbe_volume = u.experimental.hessian(cubbe_volume, units=("m",)) >>> hessian_cubbe_volume(u.Q(2.0, "m")) Quantity(Array(12., dtype=float32...), unit='m')
argnumsselects the argument to differentiate with respect to. Forf(x, y) = x y**2, the second derivative w.r.t.yis2 x:>>> def f(x, y): ... return x * y**2
>>> hess_y = u.experimental.hessian(f, argnums=1, units=("m", "s")) >>> hess_y(u.Q(3.0, "m"), u.Q(4.0, "s")) Quantity(Array(6., dtype=float32...), unit='m')
- Parameters:
fun (
Callable[[Unpack[TypeVarTuple]],TypeVar(R, bound=AbstractQuantity)])argnums (
int)units (
tuple[UnitBase|FunctionUnitBase|str|None,...])
- Return type:
Callable[[Unpack[TypeVarTuple]],TypeVar(R, bound=AbstractQuantity)]
- unxt._src.experimental.jacfwd(fun: Callable[[Unpack[Args]], R], argnums: int = 0, *, units: tuple[UnitBase | FunctionUnitBase | str | None, ...])#
Jacobian of
funevaluated column-by-column using forward-mode AD.In general, if you can use
quax.quaxify(jax.jacfwd(func))(or the syntactic sugarquax.jacfwd(func)), that’s the better option! The difference from those functions is how units are handled.quaxifywill directly propagate the units through the automatic differentiation functions. But sometimes that doesn’t work and we need to strip the units and re-apply them. This function does that, using theunitskwarg.See also
jax.jacfwdThe original JAX jacfwd function.
Examples
>>> import jax.numpy as jnp >>> import unxt as u
>>> def cubbe_volume(x: u.Q["length"]) -> u.Q["volume"]: ... return x**3
>>> jacfwd_cubbe_volume = u.experimental.jacfwd(cubbe_volume, units=("m",)) >>> jacfwd_cubbe_volume(u.Q(2.0, "m")) Quantity(Array(12., dtype=float32...), unit='m2')
- Parameters:
fun (
Callable[[Unpack[TypeVarTuple]],TypeVar(R, bound=AbstractQuantity)])argnums (
int)units (
tuple[UnitBase|FunctionUnitBase|str|None,...])
- Return type:
Callable[[Unpack[TypeVarTuple]],TypeVar(R, bound=AbstractQuantity)]
- unxt._src.experimental.where(condition: Array | ndarray | bool | number | bool | int | float | complex, x: R, y: AbstractQuantity, /)#
Unit-checked
where: both branches must be quantities (experimental).A strict alternative to
jax.numpy.where(). Bothxandymust be quantities (a dimensionlessQuantityis fine).yis converted tox’s unit – raising if they are not convertible – and the result is returned inx’s unit and concrete type (xtyped asRso a checker sees, e.g.,Anglein ->Angleout).Unlike
jnp.where, this will not silently reinterpret a raw array as being in the quantity’s unit (see the “jnp.whereadopts the quantity’s unit for a raw-array branch” sharp bit): a raw-array branch is rejected, so wrap it asunxt.Quantity(arr, unit)first.jnp.wherecannot itself be made strict, because JAX lowers masking ops (triu/tril/trace,where(mask, q, 0)) to the same primitive and relies on the raw zero-fill adopting the unit.Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> from unxt import experimental
>>> cond = jnp.asarray([True, False])
yis converted tox’s unit; the result is inx’s unit:>>> experimental.where(cond, u.Q([1.0, 2.0], "m"), u.Q([0.003, 0.004], "km")) Quantity(Array([1., 4.], dtype=float32), unit='m')
Incompatible units raise:
>>> try: ... experimental.where(cond, u.Q([1.0, 2.0], "m"), u.Q([1.0, 2.0], "s")) ... except Exception as e: ... print(type(e).__name__) UnitConversionError
A raw-array branch is rejected rather than silently adopting the unit:
>>> try: ... experimental.where(cond, u.Q([1.0, 2.0], "m"), jnp.asarray([3.0, 4.0])) ... except TypeError as e: ... print(e) unxt.experimental.where requires both branches to be Quantities; ...
- Parameters:
- Return type:
TypeVar(R, bound=AbstractQuantity)