Skip to content

📝 docs(spec): an angle carries no branch cut; the context does - #740

Merged
nstarman merged 1 commit into
GalacticDynamics:mainfrom
nstarman:claude/spec-angle-branch-cut
Aug 19, 2026
Merged

📝 docs(spec): an angle carries no branch cut; the context does#740
nstarman merged 1 commit into
GalacticDynamics:mainfrom
nstarman:claude/spec-angle-branch-cut

Conversation

@nstarman

Copy link
Copy Markdown
Contributor

docs/spec.md contradicted itself about what an Angle is.

Maths section (line 40): "To store an angle as a real number one must choose a branch cut … The two standard choices are $[0, 2\pi)$ and $(-\pi, \pi]$."

Software section (line 1028): "Angles represent directions on $S^1$ and do not encode branch-cut convention in the type itself."

The second is right. Both halves of the first are wrong.

No cut is enforced

The stored value is an unreduced representative of a class in $\mathbb{R}/2\pi\mathbb{Z}$, not a value confined to an interval:

>>> u.Angle(7.0, "rad")
Angle(7., 'rad')
>>> u.Angle(-3.0, "rad")
Angle(-3., 'rad')

Nothing canonicalises on construction.

And the cut is not one of two

wrap_to honours whatever interval of width $2\pi$ the caller names, so the same direction has as many representatives as there are cuts:

>>> wrap_to(theta, u.Angle(0.0, "rad"), u.Angle(TAU, "rad")).round(8)
Angle(0.71681469, 'rad')
>>> wrap_to(theta, u.Angle(10.0, "rad"), u.Angle(10.0 + TAU, "rad")).round(8)
Angle(13.28318531, 'rad')

Neither is more correct. The cut belongs to the context that imposes it — a chart through its component domain, or a caller through an explicit wrap — and an angle that has met neither carries none.

What changed

The maths section is rewritten around representative rather than stored value, attributes the cut to the context, and drops the implication that only two exist. The Angle and wrap_to entries gain worked examples, so this is pinned rather than asserted — the arbitrary-cut one especially, since that is what makes "contextual" concrete instead of a claim about taste.

$S^1 = \mathbb{R}/2\pi\mathbb{Z}$, compactness, and "the cut is an artifact of representation" were already right and are kept.

Why it matters beyond the prose

This is the definition the rest of the library leans on. It is the reason POLAR = Interval("rad", 0, pi) and AZIMUTH = Interval("rad", -pi, pi) are domains rather than distinct types — topology is a property of the coordinate's domain, dimension a property of its container. Getting the definition wrong invites conflating the two, which is live right now in pt_project, where the container of a projected angle tracks the ambient chart (Angle via Spherical3D, plain Quantity via Cart3D) with nothing declaring it. That is a separate change; this one fixes the definition it would be argued from.

Verification

docs/spec.md: 343 passed — including the four new examples, which CI now gates thanks to #733. prek run --all-files clean; uv run --frozen nox -s docs succeeds with warnings as errors.

🤖 Generated with Claude Code

The maths section said "to store an angle as a real number one must choose a
branch cut" and offered "the two standard choices". Both halves are wrong, and
the software section three thousand lines down already said so -- `Angle`
"do[es] not encode branch-cut convention in the type itself". The file
contradicted itself.

No cut is enforced. `Angle(7.0, "rad")` and `Angle(-3.0, "rad")` are both
well-formed and neither is reduced on construction, so a stored angle is an
unreduced *representative* of a class in R/2piZ rather than a value confined to
an interval. And the cut is not one of two: `wrap_to` honours whatever interval
of width 2pi the caller names, so the same direction has as many representatives
as there are cuts --

    wrap_to(7 rad, [0, 2pi))        ->  Angle(0.71681469, 'rad')
    wrap_to(7 rad, [10, 10 + 2pi))  ->  Angle(13.28318531, 'rad')

-- neither more correct than the other. The cut belongs to the context that
imposes it: a chart through its component domain, or a caller through an
explicit wrap. An angle that has met neither carries none.

Rewritten around "representative" instead of "stored value", with the cut
attributed to the context, and pinned by examples rather than asserted -- the
arbitrary-cut one especially, since it is what makes "contextual" concrete. The
`wrap_to` entry no longer implies its two examples are the menu.

343 passed; `prek run --all-files` clean; docs build succeeds.
Copilot AI lite review requested due to automatic review settings August 19, 2026 02:19

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 resolves an internal contradiction in docs/spec.md by clarifying that an Angle stores an unreduced real representative of a class in ( \mathbb{R}/2\pi\mathbb{Z} ), and that any branch-cut choice is imposed by context (e.g., chart domain or explicit wrapping) rather than being encoded in the Angle type.

Changes:

  • Rewrites the math-spec definition of angular quantities around “representatives” (no canonicalization on construction) and moves branch-cut responsibility to context.
  • Expands the software-spec “Angle” section with worked pycon examples showing unreduced stored values.
  • Expands the wrap_to entry with examples demonstrating arbitrary caller-chosen cuts (any interval of width (2\pi)).

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

@github-actions github-actions Bot added the 📝 Add / update documentation Add or update documentation. label Aug 19, 2026
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #740   +/-   ##
=======================================
  Coverage   96.59%   96.59%           
=======================================
  Files         266      266           
  Lines        8934     8934           
=======================================
  Hits         8630     8630           
  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 added this to the v0.24.0 milestone Aug 19, 2026
@nstarman
nstarman merged commit 4d47ea4 into GalacticDynamics:main Aug 19, 2026
18 checks passed
@nstarman
nstarman deleted the claude/spec-angle-branch-cut branch August 19, 2026 06:13
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