test: Java golden-value oracles for the five DCE math cores + P1-2/P1-3 fixes
All checks were successful
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 59s
Markdown link check / check (pull_request) Successful in 51s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Successful in 2m19s
All checks were successful
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 59s
Markdown link check / check (pull_request) Successful in 51s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Successful in 2m19s
Add bit-for-bit (1e-12) golden-value oracle tests pinning the C++ pure-math and functional cores against the compiled upstream Java library (openjdk 17): - HyperIdealGoldenJava: Clausen/Л/ImLi2, ζ13/14/15/ζ, both tetrahedron-volume formulas (real de.varylab…Clausen / HyperIdealUtility). - EuclideanGoldenJava / SphericalGoldenJava: angle formulas + β relations + Л energy terms, plus FULL-MESH oracles driving the real EuclideanCyclicFunctional / SphericalFunctional on a shared tetrahedron — per-vertex gradient (Θ−Σα) and ΔE = E(x)−E(0) (C++ Gauss-Legendre path integral vs Java closed form). - SphericalGoldenJava.FullMeshEdgeDofGradient: edge-DOF gradient (vertex + edge components, α_opp⁺+α_opp⁻−θ_e) vs raw conformalEnergyAndGradient — locks Finding 3 at the solution level (audit items 4 & 5). - PeriodMatrix.NormalizeModulus_GoldenJava: τ-reduction fold convention vs the real DiscreteEllipticUtility.normalizeModulus (audit items 7 & 8). Subtlety documented: the spherical oracles call Java's raw conformalEnergyAndGradient, not evaluate() (which pre-runs a Brent gauge maximization that C++ factors into the Newton solver's spherical_gauge_shift). Also: - P1-2 (layout.hpp): Euclidean holonomy now uses a per-cut-edge rigid-motion fit g(z)=a·z+b, exposing residual_rotation = |arg(a)| as a diagnostic; non- regressive (flat case a=1 reduces to the old midpoint formula). - P1-3 (period_matrix.hpp): is_in_fundamental_domain fixed to the correct half-open SL(2,ℤ) domain (−½ ≤ Re < ½). Updated the now-exposed ComputePeriodMatrix_ReducedTau_InFD to assert the normalizeModulus domain (closed +½ edge) instead. Test counts (single source of truth = doc/api/tests.md): 272/272 pass, 0 skipped (26 non-CGAL + 246 CGAL). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,14 +7,14 @@ Pure-math tests, only Eigen required. Covers Java utilities ported in Phase 1–
|
||||
| File | What it tests |
|
||||
|---|---|
|
||||
| `test_clausen.cpp` | Clausen Cl₂, Lobachevsky Л, ImLi₂ — values at known points |
|
||||
| `test_hyper_ideal_utility.cpp` | Tetrahedron volumes (Meyerhoff / Kolpakov–Mednykh) |
|
||||
| `test_hyper_ideal_utility.cpp` | Tetrahedron volumes (Meyerhoff / Kolpakov–Mednykh) + Java golden-value oracle (Clausen/Л/ImLi₂, ζ₁₃/₁₄/₁₅/ζ, both volume formulas) |
|
||||
| `test_matrix_utility.cpp` | Matrix helpers |
|
||||
| `test_surface_curve_utility.cpp` | Surface curve utilities |
|
||||
| `test_discrete_elliptic_utility.cpp` | Discrete elliptic functions |
|
||||
| `test_p2_utility.cpp` | P2 projective utilities |
|
||||
| `test_hyper_ideal_visualization_utility.cpp` | Poincaré disk projection, circumcircle |
|
||||
|
||||
**Total: 23 tests, 0 skipped.**
|
||||
**Total: 26 tests, 0 skipped.**
|
||||
|
||||
---
|
||||
|
||||
@@ -30,7 +30,9 @@ All tests have CTest prefix `cgal.` (set via `TEST_PREFIX "cgal."` in CMakeLists
|
||||
| `ConformalMeshValidity` | `test_conformal_mesh.cpp` | 1 | CGAL validity for all factory meshes |
|
||||
| `HyperIdealFunctional` | `test_hyper_ideal_functional.cpp` | 7 | FD gradient checks + Hessian symmetry |
|
||||
| `SphericalFunctional` | `test_spherical_functional.cpp` | 13 | Angle formula + gradient + gauge-fix + cross-module Hessian check + closed-form edge-DOF oracle |
|
||||
| `SphericalGoldenJava` | `test_spherical_functional.cpp` | 3 | Java golden oracles: (1) law-of-cosines angles + β relations + Л energy term; (2) full-mesh tetrahedron — per-vertex gradient + ΔE; (3) full-mesh **edge-DOF** gradient (vertex + edge components, Finding 3) vs raw `conformalEnergyAndGradient` |
|
||||
| `EuclideanFunctional` | `test_euclidean_functional.cpp` | 12 | Angle formula + gradient + cross-module Hessian check |
|
||||
| `EuclideanGoldenJava` | `test_euclidean_functional.cpp` | 2 | Java golden oracles: (1) angle formula + 2·Л(α) energy term; (2) full-mesh tetrahedron — per-vertex gradient + ΔE vs real `EuclideanCyclicFunctional` |
|
||||
| `EuclideanHessian` | `test_euclidean_hessian.cpp` | 9 | Cotangent Laplacian structure, FD agreement, PSD, null space |
|
||||
| `SphericalHessian` | `test_spherical_hessian.cpp` | 8 | Derivative correctness, NSD at equilibrium |
|
||||
| `NewtonSolver` | `test_newton_solver.cpp` | 11 | Convergence: Euclidean ×3, Spherical ×4, HyperIdeal ×4 |
|
||||
@@ -48,7 +50,7 @@ All tests have CTest prefix `cgal.` (set via `TEST_PREFIX "cgal."` in CMakeLists
|
||||
| `HalfedgeUV` | `test_phase7.cpp` | 4 | Size = number of half-edges, seam consistency, boundary half-edges = 0 |
|
||||
| `PriorityBFS` | `test_phase7.cpp` | 3 | Success, no seam on open meshes, all vertices placed |
|
||||
| `NormaliseEuclidean` | `test_phase7.cpp` | 2 | UV centroid = 0, halfedge_uv centroid = 0 |
|
||||
| `PeriodMatrix` | `test_phase7.cpp` | 7 | τ ∈ ℍ, SL(2,ℤ) reduction, exception outside ℍ |
|
||||
| `PeriodMatrix` | `test_phase7.cpp` | 8 | τ ∈ ℍ, SL(2,ℤ) reduction, exception outside ℍ, Java `normalizeModulus` golden oracle |
|
||||
| `FundamentalDomain` | `test_phase7.cpp` | 7 | Genus-1 parallelogram CCW, generators, g > 1 empty |
|
||||
| `TilingCopy/Neighbourhood` | `test_phase7.cpp` | 4 | Translation correct, tile count |
|
||||
| `HolonomyEndToEnd` | `test_phase7.cpp` | 3 | Full pipeline τ on tori of revolution (4×4, hex 6×6, 8×8) vs analytic modulus i·√(R²−r²)/r |
|
||||
@@ -67,7 +69,7 @@ All tests have CTest prefix `cgal.` (set via `TEST_PREFIX "cgal."` in CMakeLists
|
||||
| `NewtonPhase9a` | `test_newton_phase9a.cpp` | 7 | Phase 9a-Newton — convergence for the two new circle-packing solvers |
|
||||
| `CGALPhase8bLite` | `test_cgal_phase8b_lite.cpp` | 17 | Phase 8b-Lite — CGAL entries for all 5 DCE models + `output_uv_map` (Euclidean, Spherical, HyperIdeal, Inversive-Distance) + CP-Euclidean throws-clearly + pipe-operator chaining |
|
||||
|
||||
**Total: 240 tests, 0 skipped.**
|
||||
**Total: 246 tests, 0 skipped.**
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -124,18 +124,26 @@ cycles, not unweighted BFS).
|
||||
requires integration of holomorphic differentials — not implemented") so it is
|
||||
consistent with the documented contract, not a regression.
|
||||
|
||||
### 8. ℹ️ NOTE — Holonomy translation assumes zero residual rotation
|
||||
### 8. ✅ FIXED — Holonomy now fits the full deck isometry (was: midpoint only)
|
||||
`detail::euclidean_holonomy` (`layout.hpp`) develops each face along a dual tree
|
||||
that never crosses a cut edge, then reads each generator's translation as the
|
||||
**midpoint displacement** `ω = midA − midB` of the shared cut edge between its two
|
||||
independent developments. This equals the true deck-translation **only when the
|
||||
two developments differ by a pure translation** (linear part = identity), i.e. for
|
||||
a perfectly flat cone metric (Θ ≡ 2π). When Newton has not fully converged (small
|
||||
residual cone-angle defect), there is a residual rotation and the midpoint
|
||||
displacement is a first-order approximation rather than the exact ω. A fully
|
||||
robust version would fit the rigid motion mapping edge `(Bs,Bt)↦(At,As)` and read
|
||||
its translation part. Acceptable for converged flat tori (the `torus_8x8` test
|
||||
passes); worth tightening if higher-genus or under-converged inputs are used.
|
||||
that never crosses a cut edge. It **previously** read each generator's
|
||||
translation as the **midpoint displacement** `ω = midA − midB` of the shared cut
|
||||
edge between its two independent developments — exact **only** when the two
|
||||
developments differ by a pure translation (linear part = identity), i.e. a
|
||||
perfectly flat cone metric (Θ ≡ 2π). Under residual cone-angle defect that was
|
||||
a first-order approximation.
|
||||
|
||||
**Fix (2026-05-29):** it now fits the full orientation-preserving deck isometry
|
||||
`g(z) = a·z + b` to the correspondence `(Bs,Bt) ↦ (As,At)` (`a = (At−As)/(Bt−Bs)`,
|
||||
`b = As − a·Bs`). Because both faces use the same edge length, `|a| = 1` exactly,
|
||||
so the fit is an exact isometry; `ω = b` is its translation part and `|arg(a)|`
|
||||
is exposed per cut edge as `HolonomyData::residual_rotation` (a convergence
|
||||
diagnostic). For a flat cone metric `a = 1` and `b` reduces to `midA − midB`, so
|
||||
converged flat tori are **bit-for-bit unchanged** — all 240 CGAL tests still pass,
|
||||
including `HolonomyEndToEnd.{Torus4x4,TorusHex6x6,Torus8x8}`. Note: when
|
||||
`|arg(a)|` is non-negligible the holonomy is a genuine rotation and *no* lattice
|
||||
translation is well-defined; the diagnostic now surfaces that instead of silently
|
||||
averaging.
|
||||
|
||||
---
|
||||
|
||||
@@ -200,37 +208,75 @@ Audited against the literature and found faithful:
|
||||
(This also fixed the Euclidean holonomy extraction — develop across the dual
|
||||
tree only — see `layout.hpp` / `cut_graph.hpp`.)
|
||||
|
||||
4. ⚠️ **PARTLY DONE (2026-05-29)** — added `EdgeGradient_RegularTetClosedForm`:
|
||||
an *independent closed-form* oracle that pins each edge-DOF gradient to
|
||||
`π/3` (= 2·(2π/3) − π) on the regular spherical tetrahedron, where the corner
|
||||
angle 2π/3 follows from the spherical law of cosines. This locks the
|
||||
Finding-3 formula `α_opp⁺ + α_opp⁻ − θ_e` against a value the path-integral FD
|
||||
check cannot detect (the energy is the integral of G, so FD only proves
|
||||
curl-freeness). **Still open:** (i) Newton-to-convergence *with* edge DOFs is
|
||||
blocked by the Finding-4 spherical-Hessian guard (`throw` on edge DOFs), so a
|
||||
converged-metric test needs an FD-Hessian or guard relaxation first; (ii) a
|
||||
live-Java golden-value oracle still requires running the upstream library.
|
||||
4. ✅ **DONE (2026-05-29)** — the Finding-3 spherical edge-DOF gradient is now
|
||||
locked at two levels: (a) `EdgeGradient_RegularTetClosedForm` — an independent
|
||||
*closed-form* oracle pinning each edge-DOF gradient to `π/3` (= 2·(2π/3) − π)
|
||||
on the regular spherical tetrahedron; and (b)
|
||||
`SphericalGoldenJava.FullMeshEdgeDofGradient_Tetrahedron` — a *live-Java*
|
||||
full-mesh oracle (item 5) on a generic asymmetric configuration that pins
|
||||
BOTH the vertex gradient AND the edge gradient `α_opp⁺ + α_opp⁻ − θ_e` to
|
||||
1e-12 vs the real raw `conformalEnergyAndGradient`. The vertex-DOF spherical
|
||||
path is likewise locked by `SphericalGoldenJava.FullMeshGradientAndEnergy_…`.
|
||||
**Still open (solver feature, not a correctness gap):** Newton-to-convergence
|
||||
*with* edge DOFs is blocked by the Finding-4 spherical-Hessian guard (`throw`
|
||||
on edge DOFs), so a converged-metric test needs an FD-Hessian or guard
|
||||
relaxation first.
|
||||
|
||||
5. **Cross-check vs Java numeric oracles** — there is no test that pins C++
|
||||
functional/gradient values against recorded Java outputs on a fixed mesh. A
|
||||
handful of golden-value tests (energy + gradient at a known `x`) would catch
|
||||
any future silent divergence from the reference far more directly than the
|
||||
self-consistent FD checks (which only verify curl-freeness, since the C++
|
||||
energy is itself the path-integral of its own gradient).
|
||||
5. ✅ **DONE (2026-05-29)** — golden-value oracles now span both levels:
|
||||
• *pure-math / per-triangle* (1e-12 vs compiled upstream, openjdk 17):
|
||||
`HyperIdealGoldenJava` (Clausen/Л/ImLi₂, ζ₁₃/₁₄/₁₅/ζ, both tetrahedron-volume
|
||||
formulas), `EuclideanGoldenJava` (angle formula + 2·Л energy),
|
||||
`SphericalGoldenJava` (law-of-cosines angles + β relations + Л energy),
|
||||
`PeriodMatrix.NormalizeModulus_GoldenJava` (item 8).
|
||||
• *full-mesh energy + gradient at a known `x`* — the originally-fragile part,
|
||||
now solved via a shared OBJ tetrahedron loaded identically in both languages:
|
||||
`EuclideanGoldenJava.FullMeshGradientAndEnergy_Tetrahedron` and
|
||||
`SphericalGoldenJava.FullMeshGradientAndEnergy_Tetrahedron` drive the REAL
|
||||
`EuclideanCyclicFunctional` / `SphericalFunctional` and pin every per-vertex
|
||||
gradient `Θ−Σα` (matched by vertex position) AND `ΔE = E(x)−E(0)` to 1e-12.
|
||||
The ΔE check is doubly independent: C++ computes energy as a Gauss-Legendre
|
||||
PATH INTEGRAL of its gradient, Java as a CLOSED-FORM sum — the initialEnergy
|
||||
constant and the φ·λ⁰ term cancel in the difference (no edge DOFs here).
|
||||
• *full-mesh EDGE-DOF gradient* —
|
||||
`SphericalGoldenJava.FullMeshEdgeDofGradient_Tetrahedron` makes two opposite
|
||||
tetrahedron edges variable (replacement parameterization λ_e = x[e_idx]) and
|
||||
pins BOTH the vertex gradient AND the edge gradient `α_opp⁺+α_opp⁻−θ_e` to
|
||||
1e-12 vs the real raw `conformalEnergyAndGradient`, closing the Finding-3
|
||||
solution-level gap (item 4). It is a pure gradient oracle (no ΔE): with an
|
||||
edge DOF, x=0 ⇒ λ_e=0 ⇒ arc length π is degenerate, so the path-integral-from-
|
||||
origin energy reference is ill-defined; the fixed-x gradient is the
|
||||
unambiguous quantity. It never touches the Hessian, so the Finding-4 edge-DOF
|
||||
Hessian guard does not block it. Harness: `/tmp/oracle/SphereEdgeOracle.java`.
|
||||
**Subtlety surfaced:** the spherical oracle must call Java's raw
|
||||
`conformalEnergyAndGradient`, not `evaluate()`; `evaluate()` first runs a 1-D
|
||||
Brent maximization over the global-scale gauge (`maximizeInNegativeDirection`),
|
||||
which the C++ `spherical_gradient` deliberately omits (C++ factors that gauge
|
||||
into the Newton solver's `spherical_gauge_shift`). Harnesses:
|
||||
`/tmp/oracle/{tet.obj,EucMeshOracle.java,SphereMeshOracle.java,SphereEdgeOracle.java}`.
|
||||
The *edge-DOF* full-mesh gradient oracle is now also done (item 4,
|
||||
`SphereEdgeOracle.java`).
|
||||
|
||||
6. **CP-Euclidean `clausen` vs `clausen2`** — a single test asserting
|
||||
`|clausen(x) − clausen2(x)| < 1e-12` across a sweep would document Finding 5
|
||||
and guard against an approximation regression.
|
||||
|
||||
7. **Period-matrix domain convention (Finding 6)** — production now uses
|
||||
`normalizeModulus` (folds `Re ≥ 0`). Add a torus whose true τ has `Re(τ) < 0`
|
||||
before reduction and assert the resulting `Re(τ) ≥ 0` convention, so any future
|
||||
switch back to `reduce_to_fundamental_domain` is caught. Pair with a golden τ
|
||||
recorded from the Java `DiscreteEllipticUtility` on the same mesh.
|
||||
7. ⚠️ **PARTLY DONE (2026-05-29)** — the `Re ≥ 0` fold convention is now locked at
|
||||
the function level by item 8 (`NormalizeModulus_GoldenJava`, including `Re < 0`
|
||||
inputs). **Still open:** an *end-to-end* torus whose true τ has `Re(τ) < 0`
|
||||
before reduction, asserting the production pipeline yields `Re(τ) ≥ 0` (so any
|
||||
future switch back to `reduce_to_fundamental_domain` is caught at the mesh
|
||||
level, not just the function level).
|
||||
|
||||
8. **`normalizeModulus` vs Java oracle (Finding 6)** — the faithful
|
||||
`normalizeModulus` is only checked against ad-hoc values. Add a few recorded
|
||||
Java `normalizeModulus` outputs as golden values to lock the `Re ≥ 0` fold.
|
||||
8. ✅ **DONE (2026-05-29)** — `PeriodMatrix.NormalizeModulus_GoldenJava` pins the
|
||||
C++ `normalizeModulus` against five recorded outputs of the real Java
|
||||
`DiscreteEllipticUtility.normalizeModulus` (1e-12), including `Re < 0` mirror
|
||||
folds, large-`Re` `T`-steps, and `|τ| < 1` `S`-inversions — locking the
|
||||
`0 ≤ Re ≤ ½` fold convention. (Generated via `/tmp/oracle/TauOracle.java`.)
|
||||
This also surfaced a real interaction: `compute_period_matrix` can land
|
||||
exactly on `Re = +½` (the closed right edge of `normalizeModulus`'s mirror-
|
||||
folded domain), which the half-open SL(2,ℤ) `is_in_fundamental_domain`
|
||||
(`−½ ≤ Re < ½`, P1-3) correctly *excludes*; `ComputePeriodMatrix_ReducedTau_InFD`
|
||||
was updated to assert the `normalizeModulus` domain rather than the SL(2,ℤ) one.
|
||||
|
||||
9. **Higher-genus cut graph (Finding 7)** — only genus-1 is exercised end-to-end.
|
||||
Add a genus-2 mesh and assert `compute_cut_graph` returns exactly `2g = 4` cut
|
||||
|
||||
Reference in New Issue
Block a user