Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
af07642
♻️ refactor(internal): rename QuantityMatrix → QMatrix and split into…
nstarman May 17, 2026
2c28f79
✨ feat(metric): introduce _src/metric module with abstract field, mat…
nstarman May 17, 2026
c01b384
✨ feat(metric): add per-geometry register_metric dispatch files, scal…
nstarman May 17, 2026
6644c78
♻️ refactor(manifolds): update public API and re-exports for new metr…
nstarman May 17, 2026
260cb25
♻️ refactor: propagate QMatrix and metric class renames across charts…
nstarman May 17, 2026
752bab7
♻️ refactor(coordinax.astro): adapt to metric API renames and docstri…
nstarman May 17, 2026
7aa4441
♻️ refactor(coordinax.curveframes): adapt to updated frame and manifo…
nstarman May 17, 2026
1263f30
♻️ refactor(coordinax.hypothesis): adapt tests to updated chart and m…
nstarman May 17, 2026
19ef6fd
♻️ refactor(coordinax.interop.astropy): adapt to QMatrix rename and d…
nstarman May 17, 2026
8dc53d3
📝 docs: update spec and guides for metric refactor
nstarman May 17, 2026
24c8623
🔧 config: add import abbreviations and ARG002 test lint ignore
nstarman May 17, 2026
d8c2d48
🔧 config: add ty type-ignore comments to silence known false positives
nstarman May 17, 2026
8121ac7
✅ test: simplify numeric literals in test assertions
nstarman May 17, 2026
29f3994
✅ test(tests): replace whole-number float literals with ints in quant…
nstarman May 17, 2026
aed8dea
✅ test(tests): replace whole-number float literals with ints in strat…
nstarman May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Use the built-in $S^2$ two-sphere and its round metric to measure angles between
>>> cxm.S2
HyperSphericalManifold(ndim=2)
>>> cxm.S2.metric
HyperSphericalMetric(ndim=2)
RoundMetric(ndim=2)

>>> # At the equator, measure the angle between northward and eastward tangents
>>> at = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")}
Expand Down
8 changes: 4 additions & 4 deletions docs/api/internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ These utilities are primarily useful when implementing downstream transforms, Ja
```python
import jax.numpy as jnp
import unxt as u
from coordinax.internal import QuantityMatrix
from coordinax.internal import QMatrix

J = QuantityMatrix(
J = QMatrix(
value=jnp.eye(3),
unit=(
(u.unit("m/m"), u.unit("m/rad"), u.unit("m/rad")),
Expand All @@ -37,7 +37,7 @@ J = QuantityMatrix(
)
```

`QuantityMatrix` supports both 1-D and 2-D cases. This makes it suitable for heterogeneous vectors as well as Jacobians and metric tensors whose entries do not all share the same unit.
`QMatrix` supports both 1-D and 2-D cases. This makes it suitable for heterogeneous vectors as well as Jacobians and metric tensors whose entries do not all share the same unit.

## Packing Helpers

Expand Down Expand Up @@ -66,7 +66,7 @@ Use `pack_uniform_unit` when all components should be expressed in a shared unit

### Heterogeneous Unit Containers

- `QuantityMatrix`: N-D quantity container with per-element units; currently supports 1-D vectors and 2-D matrices
- `QMatrix`: N-D quantity container with per-element units; currently supports 1-D vectors and 2-D matrices
- `UnitsMatrix`: immutable nested tuple of units with tuple-style indexing and shape metadata

### Packing Utilities
Expand Down
2 changes: 1 addition & 1 deletion docs/api/manifolds.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ M3 = cxm.guess_manifold(cxc.sph2)
at = {"x": u.Q(0, "km"), "y": u.Q(0, "km"), "z": u.Q(0, "km")}
uvec = {"x": u.Q(1, "km"), "y": u.Q(0, "km"), "z": u.Q(0, "km")}
vvec = {"x": u.Q(0, "km"), "y": u.Q(1, "km"), "z": u.Q(0, "km")}
ang = M.angle_between(cxc.cart3d, uvec, vvec, at=at)
ang = cxm.angle_between(cxc.cart3d, uvec, vvec, at=at)
```

## Functional API
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/charts.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Product-chart transitions are factorwise.

### Direct call — quantity-valued dictionary input

Passing a component dictionary with `unxt.Quantity` values returns a `QuantityMatrix` whose element `[j, i]` carries the unit `output_unit_j / input_unit_i`:
Passing a component dictionary with `unxt.Quantity` values returns a `QMatrix` whose element `[j, i]` carries the unit `output_unit_j / input_unit_i`:

```{code-block} python
>>> import coordinax.charts as cxc
Expand All @@ -113,7 +113,7 @@ Passing a component dictionary with `unxt.Quantity` values returns a `QuantityMa
>>> at = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")}
>>> J = cxc.jac_pt_map(at, cxc.cart3d, cxc.sph3d)
>>> J
QuantityMatrix(
QMatrix(
[[ 1., 0., 0.],
[-0., -0., -1.],
[ 0., 1., 0.]],
Expand Down
31 changes: 13 additions & 18 deletions docs/guides/manifolds.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ False
>>> import coordinax.charts as cxc
>>> import coordinax.manifolds as cxm

>>> E2 = cxm.EuclideanManifold(2)
>>> E2.default_chart()
>>> R2 = cxm.EuclideanManifold(2)
>>> R2.default_chart()
Cart2D(M=Rn(2))
>>> E2.has_chart(cxc.cart2d)
>>> R2.has_chart(cxc.cart2d)
True
>>> E2.has_chart(cxc.polar2d)
>>> R2.has_chart(cxc.polar2d)
True
```

Expand All @@ -64,7 +64,7 @@ True
>>> import coordinax.charts as cxc
>>> import coordinax.manifolds as cxm

>>> S2 = cxm.HyperSphericalManifold()
>>> S2 = cxm.HyperSphericalManifold(2)
>>> S2.default_chart()
SphericalTwoSphere(M=Sn(2))
>>> S2.has_chart(cxc.sph2)
Expand Down Expand Up @@ -97,7 +97,6 @@ Use `cxc.pt_map` (or `cxm.pt_map`) to convert a point between two charts on the
>>> import coordinax.manifolds as cxm
>>> import unxt as u

>>> M = cxm.EuclideanManifold(2)
>>> p = {"x": u.Q(1, "km"), "y": u.Q(1, "km")}
>>> p_pol = cxc.pt_map(p, cxc.cart2d, cxc.polar2d)
>>> sorted(p_pol)
Expand All @@ -108,22 +107,21 @@ Use `cxc.pt_map` (or `cxm.pt_map`) to convert a point between two charts on the

Use `scale_factors` when you want the diagonal entries of the metric matrix in a chart.

This returns the metric diagonal $g_{ii}$, not the basis lengths $\sqrt{g_{ii}}$. The result is a 1-D `QuantityMatrix` because different coordinate directions can carry different units.
This returns the metric diagonal $g_{ii}$, not the basis lengths $\sqrt{g_{ii}}$. The result is a 1-D `QMatrix` because different coordinate directions can carry different units.

```{code-block} python
>>> import coordinax.charts as cxc
>>> import coordinax.manifolds as cxm
>>> import quaxed.numpy as jnp
>>> import unxt as u

>>> M = cxm.EuclideanManifold(3)
>>> at = {
... "r": u.Q(2, "km"),
... "theta": u.Angle(jnp.pi / 2, "rad"),
... "phi": u.Angle(0, "rad"),
... }

>>> gdiag = M.scale_factors(cxc.sph3d, at=at)
>>> gdiag = cxm.scale_factors(cxc.sph3d, at=at)
>>> gdiag.shape
(3,)
>>> jnp.allclose(gdiag.value, jnp.array([1.0, 4.0, 4.0]))
Expand All @@ -132,7 +130,7 @@ Array(True, dtype=bool)
'(, km2 / rad2, km2 / rad2)'
```

For generic metrics, `scale_factors` follows the metric matrix path and returns the diagonal. For `EuclideanMetric`, coordinax uses a more efficient specialization that avoids forming the full metric matrix.
For generic metrics, `scale_factors` follows the metric matrix path and returns the diagonal. For `FlatMetric`, coordinax uses a more efficient specialization that avoids forming the full metric matrix.

## Measuring Angles Between Tangent Vectors

Expand All @@ -146,20 +144,19 @@ This is a tangent-space operation, not a point-to-point operation. The vectors a
>>> import quaxed.numpy as jnp
>>> import unxt as u

>>> M = cxm.EuclideanManifold(2)
>>> at = {"x": u.Q(0, "m"), "y": u.Q(0, "m")}
>>> uvec = {"x": u.Q(1, "m"), "y": u.Q(0, "m")}
>>> vvec = {"x": u.Q(0, "m"), "y": u.Q(1, "m")}

>>> ang = M.angle_between(cxc.cart2d, uvec, vvec, at=at)
>>> ang = cxm.angle_between(cxc.cart2d, uvec, vvec, at=at)
>>> jnp.allclose(u.ustrip("rad", ang), jnp.pi / 2)
Array(True, dtype=bool)
```

For curvilinear charts, the angle is still intrinsic, but the metric weights the coordinate directions at the supplied base point:

```{code-block} python
>>> metric = cxm.HyperSphericalMetric(ndim=2)
>>> metric = cxm.RoundMetric(ndim=2)
>>> at = {"theta": jnp.array(jnp.pi / 2), "phi": jnp.array(0.0)}
>>> uvec = {"theta": jnp.array(1.0), "phi": jnp.array(0.0)}
>>> vvec = {"theta": jnp.array(1.0), "phi": jnp.array(1.0)}
Expand Down Expand Up @@ -237,8 +234,7 @@ Use `EmbeddedManifold` when you need explicit manifold objects with atlas compat
>>> import unxt as u

>>> em = cxm.EmbeddedManifold(
... intrinsic=cxm.HyperSphericalManifold(),
... ambient=cxm.EuclideanManifold(3),
... intrinsic=cxm.S2, ambient=cxm.R3,
... embed_map=cxm.TwoSphereIn3D(radius=u.Q(2.0, "km")),
... )
>>> em.ndim
Expand Down Expand Up @@ -300,7 +296,7 @@ When needed, build manifolds from explicit chart sets with `CustomAtlas` and `Cu
>>> import coordinax.manifolds as cxm

>>> A = cxm.CustomAtlas(charts=(cxc.Cart2D, cxc.Polar2D), chart_default=cxc.cart2d)
>>> M = cxm.CustomManifold(A, metric=cxm.EuclideanMetric(2))
>>> M = cxm.CustomManifold(A, metric=cxm.FlatMetric(2))

>>> M.has_chart(cxc.cart2d)
True
Expand All @@ -316,8 +312,7 @@ Product manifolds combine independent factors and sum dimensions.
>>> import coordinax.manifolds as cxm

>>> MP = cxm.CartesianProductManifold(
... factors=(cxm.HyperSphericalManifold(), cxm.EuclideanManifold(1)),
... factor_names=("S2", "R1"),
... factors=(cxm.S2, cxm.R1), factor_names=("S2", "R1")
... )
>>> MP.ndim
3
Expand Down
5 changes: 2 additions & 3 deletions docs/guides/representations.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ The representation design is intentionally extensible. Future geometric kinds (f
- supported basis changes: `CoordinateBasis` $\rightleftarrows$ `PhysicalBasis`
- supported representations: tangent representations such as `coord_disp` and `phys_disp`
- point representations are not supported as genuine basis-changing inputs; however, `NoBasis -> CoordinateBasis` and `NoBasis -> PhysicalBasis` are supported as identity reinterpretations when the dimensions are compatible
- non-Cartesian support: available for tangent basis changes on charts with basis-change rules (for example `sph3d`), and generally via an explicit metric/manifold
- non-Cartesian support: available for tangent basis changes on charts with basis-change rules (for example `sph3d`), and generally via an explicit manifold

```{code-block} python
>>> import coordinax.charts as cxc
Expand Down Expand Up @@ -169,8 +169,7 @@ The representation design is intentionally extensible. Future geometric kinds (f
>>> cxr.change_basis(v_sph, cxc.sph3d, cxr.coord_basis, cxr.phys_basis, at=at_sph)
{'r': Q(5, 'm / s'), 'theta': Q(2, 'm / s'), 'phi': Q(2., 'm / s')}

>>> metric = cxm.EuclideanMetric(3)
>>> cxr.change_basis(v_sph, cxc.sph3d, metric, cxr.coord_basis, cxr.phys_basis, at=at_sph)
>>> cxr.change_basis(v_sph, cxc.sph3d, cxm.R3, cxr.coord_basis, cxr.phys_basis, at=at_sph)
{'r': Q(5, 'm / s'), 'theta': Q(2, 'm / s'), 'phi': Q(2., 'm / s')}
```

Expand Down
Loading
Loading