Skip to content

📝 docs(spec): specify the lorentzian verbs - #734

Merged
nstarman merged 3 commits into
GalacticDynamics:mainfrom
nstarman:claude/spec-lorentzian-verbs
Aug 18, 2026
Merged

📝 docs(spec): specify the lorentzian verbs#734
nstarman merged 3 commits into
GalacticDynamics:mainfrom
nstarman:claude/spec-lorentzian-verbs

Conversation

@nstarman

Copy link
Copy Markdown
Contributor

coordinax.manifolds.lorentzian had no entry in the spec at all — not the namespace, and none of its four verbs. causal_character and rapidity_between were not so much as mentioned; proper_time and proper_distance appeared once each, inside geodesic_distance's "use these instead" line.

Five sections: the namespace, then a verb each. The namespace section carries what they share, so the verb entries stay short.

verb reads defined for
causal_character the sign of interval any pair
proper_time $\sqrt{-\Delta s^2}/c$ timelike pairs
proper_distance $\sqrt{+\Delta s^2}$ spacelike pairs
rapidity_between the hyperbolic angle two timelike tangents

What's worth pinning

  • causal_character returns a Python str — not an enum, not a number, so it cannot be accidentally arithmetic. It takes atol, because a null separation is measure-zero in floating point and needs a band rather than an equality test.
  • proper_time divides by $c$ and proper_distance does not, so one is a duration and the other a length out of the same length-squared interval.
  • Each refuses the other's causal class with ValueError, not nan. No clock travels between spacelike-separated events, so the question does not apply rather than being undefined.
  • rapidity_between is the odd one out and gets the most prose: tangents rather than points, at= required because there is no first point to take the base from, and a bare dimensionless Array rather than an Angle — rapidity is unbounded, and rad would wrongly suggest it wraps.

That last one also gets the why it exists: angle_between refuses timelike pairs, and correctly — $g(u,u)$ and $g(v,v)$ are both negative, so arccos of their ratio clips to $0$ or $\pi$ and reports two observers in relative motion as parallel. The invariant that separates them is hyperbolic, so it gets its own name rather than a branch inside a circular-angle function.

The namespace section also records why it is named for the signature: charts.galileanct is a 4-dimensional spacetime and is not Lorentzian, so a spacetime namespace would promise membership these verbs refuse; minkowski would wrongly exclude Schwarzschild and FLRW, which carry the marker and acquire all four.

Verification — and one trap worth naming

All 34 examples were executed under the harness that will run them, not a bare interpreter.

That distinction is load-bearing. JAX_ENABLE_X64 = "1" is set in [tool.pytest.ini_options], so an ad-hoc python session gives float32 and every literal would have been wrong:

ad-hoc python : Q(1.6341238e-08, 's')      Array(0.6931472, dtype=float32)
under pytest  : Q(1.63412366e-08, 's')     Array(0.69314718, dtype=float64)

I gathered the first set before noticing, and every one of them would have failed CI. Verified with pytest docs/spec.md: 34 collected, 34 passed, 0 failed in the added range. prek run --all-files clean (blacken-docs reformatted; re-verified after, still 0 failing). uv run --frozen nox -s docs succeeds with warnings as errors.

The 17 pre-existing failures elsewhere in docs/spec.md are untouched here — they are #733's subject.

Stacking

Touches docs/spec.md, as do #732 and #733, and all three insert near the end of the verb cluster. They are independent; whichever lands last needs a trivial rebase. Once #733 is in, these examples are CI-gated automatically.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 18, 2026 20:47
@github-actions github-actions Bot added the 📝 Add / update documentation Add or update documentation. label Aug 18, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the repository’s authoritative specification (docs/spec.md) to formally specify the coordinax.manifolds.lorentzian namespace and its four public verbs, including signatures, semantics, dispatch expectations, and executable examples.

Changes:

  • Adds a new spec section for coordinax.manifolds.lorentzian, documenting the Lorentzian-signature gate and listing the supported verbs.
  • Specifies behavior for causal_character, proper_time, proper_distance, and rapidity_between, each with examples intended to run under the project’s doctest/Sybil harness.
Suppressed comments (1)

docs/spec.md:3178

  • proper_distance also accepts an atol keyword argument (same causal-class tolerance logic as proper_time), but the spec signatures omit it. The signatures should include atol to match the implemented API.
    ```
    cxm.lorentzian.proper_distance(chart, a, b, /, *, usys=None)
    cxm.lorentzian.proper_distance(metric, chart, a, b, /, *, usys=None)
    ```

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/spec.md Outdated
Comment thread docs/spec.md
Comment thread docs/spec.md Outdated
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.59%. Comparing base (c79e7c1) to head (335049a).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #734      +/-   ##
==========================================
+ Coverage   96.56%   96.59%   +0.03%     
==========================================
  Files         266      266              
  Lines        8852     8934      +82     
==========================================
+ Hits         8548     8630      +82     
  Misses        304      304              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nstarman
nstarman force-pushed the claude/spec-lorentzian-verbs branch from d220e5b to 7351a70 Compare August 18, 2026 22:24
nstarman and others added 2 commits August 18, 2026 18:44
The `coordinax.manifolds.lorentzian` sub-namespace had no entry in the spec at
all -- not the namespace, and none of its four verbs. `causal_character` and
`rapidity_between` were not so much as mentioned; `proper_time` and
`proper_distance` appeared once each, inside `geodesic_distance`'s "use these
instead" line.

Five sections: the namespace, then a verb each. The namespace section carries
what they share, so the verb entries stay short -- the gate is
`AbstractLorentzianMetricField`, meaning *exactly one* timelike direction, and
the reason each refuses rather than returning something readable as a
classification. Also why the namespace is named for the signature: `galileanct`
is a 4-dimensional spacetime and is not Lorentzian, so a `spacetime` namespace
would promise membership these verbs refuse, while a `minkowski` one would
exclude Schwarzschild and FLRW, which carry the marker and acquire all four.

Worth pinning, and now pinned:

- `causal_character` returns a Python `str`, not an enum and not a number, so
  it cannot be accidentally arithmetic. It takes `atol`, because a null
  separation is measure-zero in floating point and needs a band rather than an
  equality test.
- `proper_time` divides by `c` and `proper_distance` does not, so one is a
  duration and the other a length out of the same length-squared interval.
- Each refuses the other's causal class with `ValueError` rather than `nan`:
  no clock travels between spacelike-separated events, so the question does not
  apply rather than being undefined.
- `rapidity_between` is the odd one -- tangents rather than points, `at=`
  required because there is no first point to take the base from, and a bare
  dimensionless `Array` rather than an `Angle`, since rapidity is unbounded and
  `rad` would wrongly suggest it wraps. It exists because `angle_between`
  refuses timelike pairs correctly: both `g(u,u)` and `g(v,v)` are negative, so
  `arccos` would clip and report two observers in relative motion as parallel.

All 34 examples were executed under the harness that will run them, not a bare
interpreter. That distinction is load-bearing here: `JAX_ENABLE_X64 = "1"` is
set in `[tool.pytest.ini_options]`, so an ad-hoc `python` session yields
`float32` and every literal would have been wrong -- `Q(1.6341238e-08, 's')`
against the `Q(1.63412366e-08, 's')` the suite actually produces.

Explanatory prose sits outside the `pycon` fences rather than inside them, per
the review on GalacticDynamics#733: everything within a fence renders as code, taking its
inline markup with it.

The 17 pre-existing failures elsewhere in the file are GalacticDynamics#733's subject.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three inaccuracies from review, each checked against the implementation:

`causal_character` was described as symmetric in `a` and `b`. It is the sign of
`interval`, which evaluates the metric at the *first* point, so the guarantee
only holds where the metric is constant along the path. That covers everything
shipped -- `MinkowskiMetric` is the sole Lorentzian metric and it is flat -- but
not the curved case the namespace explicitly invites, where the module docstring
promises Schwarzschild and FLRW inherit these verbs. Reworded to say where the
symmetry holds and to warn against relying on it elsewhere.

`proper_time` and `proper_distance` both accept `atol` and neither signature
listed it. It is the same band `causal_character` uses, forwarded through, so it
is what decides how near null a pair may be before being refused -- worth
stating, since it is the knob that moves the refusal boundary.

`rapidity_between` was documented as raising `ValueError` on a non-timelike
operand. True eagerly; under `jax.jit` the condition is traced, so the same test
becomes a mask and the result is `nan`:

    eager  -> ValueError: ... only between two timelike tangent vectors
    jitted -> Array(nan, dtype=float64)

Both behaviours are now stated, with the instruction to check the output rather
than expect the exception inside a traced function.

That last one generalises, so the namespace section gains a **Tracing** note:
the three causal verbs return or branch on a Python value -- a `str`, or an
exception chosen by the class -- and so are eager-only, raising
`ConcretizationTypeError` under `jit`. `rapidity_between` is the exception, and
is the only one of the four that traces.

334 passed; `prek run --all-files` clean; docs build succeeds.
@nstarman
nstarman force-pushed the claude/spec-lorentzian-verbs branch from 7351a70 to c40add8 Compare August 18, 2026 22:49
@nstarman nstarman added this to the v0.24.0 milestone Aug 18, 2026
nstarman added a commit to nstarman/coordinax that referenced this pull request Aug 18, 2026
Audit prompted by the review on GalacticDynamics#734, whose middle finding was a signature that
omitted a keyword the implementation accepts. Checking these four the same way
turned up the same class of error and one wrong rule.

`pt_project` has seven overloads and the spec listed five. The two missing are
the explicit-embedding-map form and the `(from_chart, EmbeddedChart)` form, and
the `Point` overload takes `usys` like every other -- it was written as
`pt_project(p, M, /)`, promising a narrower API than exists.

The diagonal rule was stated as a property of orthogonality and is not. It is
declared per (manifold, chart) pair, and the counter-example is on the sphere
already:

    S2 / sph2            DiagonalMetric
    S2 / lonlat_sph2     DiagonalMetric
    S2 / loncoslat_sph2  DenseMetric

Reworded to say the chart decides, with `loncoslat_sph2` shown as a worked
example rather than left as a trap for whoever assumed the manifold decides.

Also pinned what the `(AbstractManifold, AbstractChart)` fallback actually
returns -- `DenseMetric`, verified -- and why that is the right default: dense
is only slower, diagonal would be wrong.

331 passed; `prek run --all-files` clean; docs build succeeds.
Self-review pass for redundancy. 290 lines to 276, with no contract dropped.

`A non-Lorentzian metric raises NotImplementedError` appeared as a bullet under
all four verbs, and the namespace section above already states it for all four.
Said once there, in bold, and left out of the verb entries; the refusal is still
pinned by the executable example under `causal_character`.

The paragraph re-deriving the gate restated the two sentences directly above it.
The `rapidity_between` preamble spent ten lines arguing why `angle_between`
cannot serve, which needs four. `proper_time`'s return bullet re-derived
`tau = sqrt(-ds^2) / c` from the formula three lines above. `causal_character`
described `metric` once under Arguments and again at length under Dispatch.

Also folded away a `pycon` fence that held three imports and nothing else,
immediately followed by another fence -- an artifact of moving prose out of the
blocks, not a deliberate split.

Left alone: every example. They are the executable checks, and the three
refusal cases are exactly what would catch this contract drifting now that the
file gates CI. Also left the per-section import preambles, which duplicate each
other but match how the rest of the file is written.

334 passed; `prek run --all-files` clean; docs build succeeds.
@nstarman
nstarman merged commit 4020513 into GalacticDynamics:main Aug 18, 2026
5 checks passed
@nstarman
nstarman deleted the claude/spec-lorentzian-verbs branch August 18, 2026 23:21
nstarman added a commit to nstarman/coordinax that referenced this pull request Aug 18, 2026
Audit prompted by the review on GalacticDynamics#734, whose middle finding was a signature that
omitted a keyword the implementation accepts. Checking these four the same way
turned up the same class of error and one wrong rule.

`pt_project` has seven overloads and the spec listed five. The two missing are
the explicit-embedding-map form and the `(from_chart, EmbeddedChart)` form, and
the `Point` overload takes `usys` like every other -- it was written as
`pt_project(p, M, /)`, promising a narrower API than exists.

The diagonal rule was stated as a property of orthogonality and is not. It is
declared per (manifold, chart) pair, and the counter-example is on the sphere
already:

    S2 / sph2            DiagonalMetric
    S2 / lonlat_sph2     DiagonalMetric
    S2 / loncoslat_sph2  DenseMetric

Reworded to say the chart decides, with `loncoslat_sph2` shown as a worked
example rather than left as a trap for whoever assumed the manifold decides.

Also pinned what the `(AbstractManifold, AbstractChart)` fallback actually
returns -- `DenseMetric`, verified -- and why that is the right default: dense
is only slower, diagonal would be wrong.

331 passed; `prek run --all-files` clean; docs build succeeds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📝 Add / update documentation Add or update documentation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants