fix+test: Euclidean holonomy/τ end-to-end + spherical edge-DOF oracle (2026-05-29 audit)
All checks were successful
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 1m3s
Markdown link check / check (pull_request) Successful in 44s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Successful in 2m11s

Bundles the 2026-05-29 Java↔C++ math-correctness audit (doc/reviewer/
java-port-audit.md, 11 findings) with two follow-up fixes.

Audit code changes:
- Finding 3 (spherical_functional): edge-DOF replacement parameterization via
  spher_eff_lambda; edge gradient α_opp⁺+α_opp⁻−θ_e (drops additive −(S⁺+S⁻)/2)
- Finding 4 (spherical_hessian): always-compiled edge-DOF throw guard
- Finding 6 (period_matrix): faithful normalizeModulus (0≤Re≤½, Im≥0, |τ|≥1)
- Finding 9 (inversive_distance): degenerate-face limiting angles, no skip
- Findings 1/2 (euclidean): degenerate gradient limiting angles + Hessian guard

Euclidean holonomy/τ fix: develop the cut surface across the dual spanning tree
only (cut_graph now exposes is_dual_tree), so genus-1 cut edges yield
non-degenerate lattice generators. Previously τ came out 0 / NaN / 1e13 on the
bundled tori; now matches the analytic revolution modulus i·√(R²−r²)/r. Re-enabled
τ reporting in the Euclidean CLI; rewrote validation.md §3/§4 accordingly.

Tests (240 CGAL, 0 skipped):
- HolonomyEndToEnd ×3 — tori of revolution (4×4, hex 6×6, 8×8) vs analytic modulus
- SphericalFunctional.EdgeGradient_RegularTetClosedForm — independent closed-form
  π/3 oracle locking the Finding-3 edge formula (the path-integral FD check cannot
  detect a wrong-but-conservative gradient)

Also documents the latent spherical/hyperbolic holonomy-extraction bug (same
single-development pattern, dead code today) in research-track.md (Phase 9c/10),
and adds favour/normalisations to the codespell ignore list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-29 12:50:16 +02:00
parent ca936b7652
commit a3ee9576d4
23 changed files with 1065 additions and 165 deletions

View File

@@ -39,7 +39,7 @@ where χ(M) = 2 2g is the Euler characteristic.
```cpp
#include "gauss_bonnet.hpp"
auto defect = gauss_bonnet_sum(mesh, maps); // Σ(2π Θᵥ)
auto chi = mesh.euler_characteristic();
auto chi = euler_characteristic(mesh); // free function, not a member
EXPECT_NEAR(defect, 2.0 * M_PI * chi, 1e-10);
```
@@ -71,59 +71,105 @@ Covered by: `cgal.PeriodMatrix.TauInFundamentalDomain_*` tests in `test_phase7.c
---
## 3 — Square-symmetric torus
## 3 — Torus of revolution: conformal modulus
**Setup.** Take a torus mesh with 4-fold rotational symmetry around the z-axis
(e.g. `code/data/off/torus_4x4.off`, which has M=4 columns of vertices).
**Setup.** The bundled torus meshes are surfaces of **revolution** (a tube of
minor radius `r` swept around a major circle of radius `R > r`), *not* abstract
square/hexagonal flat tori:
**Expected.** The symmetry group Z₄ acts conformally. Conformal automorphisms
of the torus correspond to SL(2,) symmetries of τ. The unique fixed point of
a rotation of order 4 in the modular group is τ = i. Therefore:
| mesh | major R | minor r | cross section |
|---|---|---|---|
| `torus_4x4.off` | 2 | 1 | square (4-gon) |
| `torus_hex_6x6.off` | 3 | 1 | hexagon (6-gon) |
| `torus_8x8.off` | 3 | 1 | octagon (8-gon) |
**Expected.** The induced metric `ds² = (R + r cos φ)² dθ² + r² dφ²` is made
flat by the conformal change of variable `dψ = r/(R + r cos φ) dφ`. The
ψ-period is `∮ r/(R + r cos φ) dφ = 2πr/√(R²r²)`, so the flat torus is the
rectangular lattice `2π· × (2πr/√(R²r²))·ℤ`. Its period ratio, reduced so
that `|τ| ≥ 1`, is therefore **purely imaginary**:
```
For a mesh with exact 4-fold symmetry and uniform edge lengths:
Re(τ) = 0 (to machine precision, by symmetry)
Im(τ) ≈ 1 (approaches 1 as mesh is refined)
Re(τ) = 0 (meridian ⟂ longitude reflection symmetry)
Im(τ) = √(R² r²) / r (reduced conformal modulus)
```
The coarse 4×4 mesh (`torus_4x4.off`) gives Im(τ) in (0.7, 1.3) depending on
the 3D embedding (R=2, r=1 torus of revolution has unequal inner/outer edge lengths).
The uniformization algorithm finds the conformal class of the *abstract* metric
encoded in the edge lengths.
giving the analytic targets
**Manual verification** (run from the build directory after adding a small
program or reading from the test output):
| mesh | analytic τ = i·√(R²r²)/r |
|---|---|
| `torus_4x4.off` | i·√3 ≈ **1.732 i** |
| `torus_hex_6x6.off` | i·√8 ≈ **2.828 i** |
| `torus_8x8.off` | i·√8 ≈ **2.828 i** |
> A common pitfall is to expect τ = i (or the order-6 fixed point e^{iπ/3})
> from the 4-fold (6-fold) symmetry. That reasoning is **wrong** here: a torus
> of revolution's rotational symmetry is a rotation about the axis, which acts
> on the surface as a *fixed-point-free* translation along the longitude — it is
> not an order-4 conformal automorphism with a fixed point, so it does not pin τ
> to a modular fixed point. The correct invariant is the rectangular modulus
> above.
**Reproduced end-to-end** (solve → `compute_cut_graph``euclidean_layout(…,
&cg, &hol)``compute_period_matrix`). The coarse polygonal cross sections
approximate the circular modulus from above; the gap shrinks as the cross
section gains sides:
| mesh | analytic | computed τ | rel. error |
|---|---|---|---|
| `torus_4x4.off` | 1.732 i | ≈ 1.79 i | ~3 % (4-gon) |
| `torus_hex_6x6.off` | 2.828 i | ≈ 2.85 i | ~0.8 % (6-gon) |
| `torus_8x8.off` | 2.828 i | ≈ 2.84 i | ~0.4 % (8-gon) |
Covered by: `cgal.HolonomyEndToEnd.Torus*_TauMatchesRevolutionModulus` in
`test_phase7.cpp`.
**Conformal flattening** of the torus is wired end-to-end and converges in a
handful of Newton steps (34 on the bundled meshes):
```bash
./bin/conformallab_core -i code/data/off/torus_4x4.off -g euclidean -v -o lay.off
# → topology: closed, free DOFs=15, genus=1
# → Euclidean: converged=yes iter=3 |grad|_inf≈1e-12
```
Equivalent C++ (current API — note `newton_euclidean` takes an `x0` vector and
the DOF indices must be assigned first):
```cpp
ConformalMesh mesh; load_mesh(mesh, "code/data/off/torus_4x4.off");
EuclideanMaps maps = setup_euclidean_maps(mesh);
ConformalMesh mesh = load_mesh("code/data/off/torus_4x4.off");
EuclideanMaps maps = setup_euclidean_maps(mesh); // Θ_v = 2π (flat target)
compute_euclidean_lambda0_from_mesh(mesh, maps);
// Pin one vertex (scale gauge), free the rest; make the flat target
// Gauss-Bonnet-consistent.
int idx = 0; bool pinned = false;
for (auto v : mesh.vertices())
maps.v_idx[v] = (!pinned ? (pinned = true, -1) : idx++);
enforce_gauss_bonnet(mesh, maps);
auto res = newton_euclidean(mesh, maps);
CutGraph cg = compute_cut_graph(mesh);
HolonomyData hol;
euclidean_layout(mesh, res.x, maps, &cg, &hol, true);
PeriodData pd = compute_period_matrix(hol);
// pd.tau_reduced satisfies the fundamental domain invariants above
std::vector<double> x0(idx, 0.0);
auto res = newton_euclidean(mesh, x0, maps); // converged after ~3 iters
```
The CLI reports the period ratio for genus-1 inputs, e.g.
```bash
./bin/conformallab_core -i code/data/off/torus_4x4.off -g euclidean -v
# → period ratio τ = 0.000000 + 1.793... i (genus 1, reduced to fundamental domain)
```
---
## 4 — Hexagonal-symmetric torus
## 4 — Higher-resolution cross sections converge to the circular modulus
**Setup.** Take a torus mesh with 6-fold rotational symmetry
(`code/data/off/torus_hex_6x6.off`, M=6).
**Expected.** The unique τ fixed under a rotation of order 6 in SL(2,) is
τ = e^{iπ/3} = ½ + i√3/2. So:
```
Re(τ) = 0.5 (to machine precision, by symmetry)
Im(τ) = √3/2 ≈ 0.8660
```
The coarse 6×6 torus of revolution approximates this: Re(τ) ≈ 0.5 by symmetry,
Im(τ) approaches √3/2 as the mesh is refined toward a flat hexagonal lattice.
`torus_hex_6x6.off` (R=3, r=1, hexagon) and `torus_8x8.off` (R=3, r=1, octagon)
share the same analytic modulus `i·√8 ≈ 2.828 i` (see §3). Because both
approximate the *circular* tube cross section, the computed τ approaches the
analytic value as the polygon gains sides: the 8-gon (≈ 2.84 i, ~0.4 %) is
closer than the 6-gon (≈ 2.85 i, ~0.8 %), which is closer than the 4-gon of
`torus_4x4.off` (~3 %). All three are asserted in
`cgal.HolonomyEndToEnd.Torus*_TauMatchesRevolutionModulus`.
---
@@ -136,12 +182,14 @@ Im(τ) approaches √3/2 as the mesh is refined toward a flat hexagonal lattice.
**fewer than 30 iterations** starting from u = 0.
```cpp
auto res = newton_euclidean(mesh, maps);
std::vector<double> x0(n_dofs, 0.0);
auto res = newton_euclidean(mesh, x0, maps);
EXPECT_LT(res.iterations, 30);
EXPECT_LT(res.gradient_norm, 1e-10);
EXPECT_LT(res.grad_inf_norm, 1e-8); // field is grad_inf_norm, default tol 1e-8
```
Covered by: `cgal.EuclideanPipeline.ConvRates_*` and similar tests.
Covered by: `cgal.NewtonSolver.*` (Euclidean ×3, Spherical ×4, HyperIdeal ×4)
and `cgal.NewtonPhase9a.*` (the two circle-packing solvers).
---
@@ -186,8 +234,12 @@ but the representation ρ: π₁(Σ_g) → SU(1,1) must still satisfy the relati
[T₁, T₂] · [T₃, T₄] · … = Id (product of g commutators = Id)
```
These are the **holonomy consistency** checks implemented in `test_phase7.cpp`
(`cgal.HolonomyData.*`).
The Möbius arithmetic these checks rely on (identity, inverse, composition,
`from_three`) is verified in `test_phase7.cpp` under `cgal.MobiusMap.*`. The
Euclidean end-to-end holonomy extraction is now validated against the analytic
torus-of-revolution modulus (§3, `cgal.HolonomyEndToEnd.*`). A standalone
`cgal.HolonomyData.*` commutator-closes-up suite for the *hyperbolic* (Möbius)
holonomy is still future work.
---
@@ -255,9 +307,8 @@ Run these in order to validate the implementation:
- [ ] `ctest --test-dir build -R cgal --output-on-failure` → all pass, 0 skipped (count: `doc/api/tests.md`)
- [ ] `cgal.GaussBonnet.*` all pass → topology is correctly read from mesh
- [ ] `cgal.EuclideanFunctional.GradientCheck_*` pass → energy = integral of gradient
- [ ] `cgal.PeriodMatrix.TauInFundamentalDomain_*` pass → SL(2,) reduction correct
- [ ] `cgal.MobiusMap.Compose_*` and `Inverse_*` pass → Möbius arithmetic correct
- [ ] `cgal.HolonomyData.*` pass → holonomy loops close up
- [ ] `cgal.PeriodMatrix.*` pass → SL(2,) reduction correct (on prescribed holonomy)
- [ ] `cgal.MobiusMap.*` pass → Möbius arithmetic (identity, inverse, compose) correct
All of the above are **deterministic, analytic tests** — no mesh loading, no
file I/O, no floating-point non-determinism beyond standard IEEE-754.