Files
ConformalLabpp/doc/reviewer/java-ignore-crossvalidation.md
Tarik Moussa 8619043b79
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m17s
API Docs / doc-build (pull_request) Successful in 1m1s
Markdown link check / check (pull_request) Successful in 43s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Failing after 1m52s
docs(crossvalidation): fix stale test count 243→259 after PRs #29-31 merged
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 17:36:51 +02:00

258 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Cross-validation potential of the Java `@Ignore`d tests
> **Purpose.** The upstream Java library (`de.varylab.discreteconformal`) has
> 22 test files with `@Ignore`d tests. This document classifies them by *why*
> they are ignored and by their *cross-validation value for the C++ port*, so
> that the highest-value golden oracles get ported first.
>
> **Created:** 2026-05-29 (external-reviewer pass). See also
> `doc/reviewer/java-port-audit.md` and PR #27 (`tests/java-golden-oracles`).
---
## 1 — Two root causes for `@Ignore`
| Cause | Count | Meaning for the C++ port |
|---|---|---|
| **`requires ARM64 PETSc native lib`** (class-level) | 15 files | **Not a logic problem.** Java solves via PETSc/TAO (native lib) which does not run on ARM64. The C++ port uses **Eigen** (no PETSc) and *can* run these scenarios — **iff** the feature is ported **and** the Java test carries a hard-coded expected value. |
| **Logic / known-issue** (method-level) | ~8 methods | Mostly mark **Java gaps** (no Hessian, layout unfinished) or **ARM64 FP flakiness**. Usually **not** reliable oracles. |
A critical distinction: PR #27 cross-validates **functional evaluations**
(energy/gradient — no solver, so no PETSc needed). The **full-solve** golden
values exist **only** as hard-coded vectors recorded from a historical x86
PETSc run — and those live exclusively in `HyperIdealConvergenceTest`.
---
## 2 — Already locked by PR #27 (do not duplicate)
`tests/java-golden-oracles` pins, bit-for-bit (1e-12), against compiled Java:
- `HyperIdealGoldenJava` — Clausen/Л/ImLi₂, ζ₁₃/₁₄/₁₅/ζ, both tetrahedron-volume formulas.
- `EuclideanGoldenJava` / `SphericalGoldenJava` — angle formulas, β relations, Л energy terms, **small full-mesh oracles** on a tetrahedron (gradient `Θ−Σα`, `ΔE = E(x)E(0)`).
- `SphericalGoldenJava.FullMeshEdgeDofGradient` — edge-DOF gradient.
- `PeriodMatrix.NormalizeModulus_GoldenJava` — τ-reduction fold convention.
These cover the **unit math cores** and **functional evaluation**. They do
**not** cover **solver/convergence** or **full uniformization**.
---
## 3 — Priority ranking for new cross-validation
### 🟢 HIGH — real golden oracle, feature already in C++
| Item | Java source | Oracle | Blocker / effort |
|---|---|---|---|
| **HyperIdeal Lawson convergence** ✅ | `HyperIdealConvergenceTest.testHyperIdealConvergence` | **hard-coded golden u\*** (perfectly symmetric — robust to DOF order) | **DONE — PR #30** via low-level half-edge `make_lawson_square_tiled` (CGAL `add_face` can't: multi-edges, §5). |
Golden solution (Lawson square-tiled, BLMVM, tol 1e-10), 22 DOFs:
```
vertices (×4, θ=2π) : 1.1462158341786262
edges (×12, θ=π/2) : 1.7627471737467797 / …7866 (symmetric pair)
edges (×6, θ=π aux): 2.633915794495759
```
Assertion strategy: because the values are symmetric per DOF-class, assert
*per class* (every vertex DOF ≈ 1.14621…, every π/2-edge ≈ 1.76274…, every
π-edge ≈ 2.63391…) to 1e-6 — **no DOF-order fidelity required**.
### 🟡 MEDIUM — golden/strong oracle, needs asset or near-term phase
| Item | Java source | Oracle | Blocker |
|---|---|---|---|
| **Genus-1 uniformization** | `UniformizerTest.testGenus1` (Wente, 1240 V) | angle-sum = 2π + vertex count (self-consistency) | needs `wente.obj` asset + a "load OBJ → uniformize" path; C++ Euclidean solver exists. |
| **HyperIdeal Lawson + branch points** ✅ | `…WithBranchPoints` | hard-coded golden u\* | **DONE — PR #30** (stellar subdivision + ideal centres; per-class symmetric @1e-4). |
| **HyperIdeal layout** | `HyperIdealLayoutTest` | layout positions | needs the generator + `layout.hpp` hyper-ideal path exercised. |
| **Euclidean big-model scale** | `EuclideanUnwrapperPetscTest.testStaticUnwrapBigModel` | mostly self-check | scale/robustness cross-check; low oracle strength. |
### 🔵 LATER — feature not yet in C++ (Phase 9d.2 / 10c / 13 / 11)
| Item | Java source | Maps to |
|---|---|---|
| Genus-2 uniformization | `UniformizerTest.testGenus2` (Lawson 2498) | Phase 10c / 13 |
| HyperIdeal hyperelliptic convergence | `…HyperEllipticCurveLawson` (has golden u\*) | Phase 13 |
| Hyperbolic disk uniformization | `HyperbolicDiskUniformizationTest` | Phase 10c (gated on holonomy bug) |
| Möbius centring (grad/Hessian/convergence golden) | `MobiusCenteringFunctionalTest` | Phase 9d.4 |
| Quasi-isothermic suite (SinCondition, SNES, Utility, Layout, ConformalStructure) | `…quasiisothermic.*` | Phase 10e |
| Schottky uniformisation | `SchottkyUtilityTest` | Phase 11a |
| Circle-domain (multiply-connected) | `CircleDomainUnwrapperTest` | Phase 11c |
| Electrostatic-sphere baseline | `ElectrostaticSphereFunctionalTest` | Phase 10c (optional) |
### ⚪ NON-ORACLE — do **not** port as cross-validation
| Item | Why it is not an oracle |
|---|---|
| `HyperIdealFunctionalTest.testHessian` (`@Ignore@Override`, empty) | Java has **no** HyperIdeal Hessian (`hasHessian()==false`). C++ *adds* it (block-FD, Phase 9b) → validate against C++ FD, not Java. Documents the C++ delta. |
| `ElectrostaticSphereFunctionalTest.testHessian` | same — Hessian not implemented in Java. |
| `HyperbolicLayoutTest.testDoLayout` | throws `"Not implemented correctly yet!"`**Java itself unfinished**; no reference to validate against. |
| `SurfaceCurveUtilityTest.testCreateSurfaceCurves` | known issue: non-deterministic edge count on ARM64 — **flaky**, not a stable oracle. |
| `HyperbolicCyclicConvergenceTest.testEuclideanConvergence` | known issue: ARM64 FP accumulation, angle sum outside 1e-8 — **flaky**. |
| `HyperIdealUtilityTest.testZeta13` | asserts ζ₁₃(0.1,0.1,0.1)=0.1, which looks **wrong**; the other volume/ζ tests already run and are ported. Verify the correct value before any use. |
| `HyperIdealPluginTest` | plugin/TAO-application wiring, not pure math. |
| `TwoHoleExample.testLetterB` | demo/example, no assertion of record. |
| `SphericalUnwrapperTest` PETSc variants | the **MTJ** variants (not ignored) already cover the same cases on `cathead`. |
---
## 3b — Effort-adjusted tiering (2026-05-29 inventory)
> A broader scan (not just `@Ignore`d tests) revealed that the §3 ranking was
> **effort-blind**: cheaper, equally-valuable oracles exist than the Lawson
> generator. Re-tiered by **value / cost**:
| Tier | Oracle | Mesh / asset | Solver | Golden | C++ status | Cost |
|---|---|---|---|---|---|---|
| **1** | `EuclideanCyclicConvergenceTest` | `cathead.obj` *(already in C++)* | MTJ BiCGstab (no PETSc) | `α_opp+α_opp = π0.1` @1e-12 (per-edge φ target) | `euclidean_functional` ✅, `phi_e` ✅, cathead ✅ | **near-zero** |
| **1** | `SphericalConvergenceTest` | octahedron | MTJ Newton (no PETSc) | self-consistency (const. curvature) | spherical ✅, `make_octahedron_face` ✅ | low |
| **2** | Wente genus-1 uniformization | `wente_torus02.obj` + `wente_uniformization.xml` | — (stored result) | exact `IsometryPSL2R` uniformizing-group generators | holonomy/`MobiusMap` ✅; **needs XML reader** | medium |
| ~~2~~**4** | `regular_uniformization.xml`, `LetterB01_canonical_group.xml` | (stored) | — | full `IsometryPSL2R` Fuchsian group + 12-edge `FundamentalPolygon` | **genus ≥ 2 hyperbolic** (not genus-1) → Phase 13 | future |
| **3** ✅ | HyperIdeal Lawson convergence — plain + branch-points | low-level half-edge `make_lawson_square_tiled` | `newton_hyper_ideal` | hard-coded golden u\* | **DONE — PR #30** (`test_lawson_hyperideal.cpp`) | done |
| **3** ⏸️ | HyperIdeal Lawson **hyperelliptic** | `lawson_curve_source.xml` + `HyperIdealHyperellipticUtility` | — | non-symmetric golden u\* | **deferred** — needs XML-format reader + jReality-geometry θ port | future |
| **4** | genus-2 (`genus2.xml`/`lawson2498.obj`), holomorphic forms (`HolomorphicEuclideanUniformization_*.xml`), Schottky (`genus2.xml`) | assets present in Java | — | various | Phases 10a/10c/11/13 | future |
**Tier-2 status (2026-05-30): exhausted.** The only genus-1 case is Wente
(deferred to Phase 9f, quad/cyclic). `regular_uniformization.xml` and
`LetterB01_canonical_group.xml` are **genus ≥ 2 hyperbolic** uniformizations
(full PSL(2,) Fuchsian groups + a 12-edge fundamental polygon) → Phase 13, not
Tier 2.
**Tier-3 status (2026-05-30): DONE for plain + branch-points (PR #30).** The
Lawson base is a genus-2 surface with **4 vertices / 12 edges → multi-edges**, so
CGAL `add_face`/OFF/polygon-soup cannot build it; `make_lawson_square_tiled`
(`test_lawson_hyperideal.cpp`) uses the **low-level Surface_mesh half-edge API**
(`add_edge` + `set_target`/`set_next`/`set_face`/`set_halfedge`), then
`triangulate_faces` (plain) or `Euler::add_center_vertex` (branch-points,
stellar). Two key findings made this clean:
1. The golden vectors are **symmetric per geometric class**, so the external jtem
`Triangulator`/`StellarLinear` orderings need **not** be replicated — any
consistent triangulation/stellar reproduces the values.
2. Java's index-based θ split is actually **geometric** (original vs diagonal;
base vs spoke); branch-point centres are **ideal vertices** (b = 0).
`newton_hyper_ideal` converges (from x0 = 1.0, **unconstrained** — Java needed
bounded BLMVM) to both golden vectors:
- plain: vertices 1.1462158341786262 / original 1.7627471737467797 / aux 2.633915794495759
- branch-points: vertices 1.3169579 / base 2.2924317 / spokes 0
**Hyperelliptic (the 3rd Lawson variant) is deferred (option c).** It needs a
reader for `lawson_curve_source.xml` (Java conformal-data `HalfedgeEmbedding`,
26 vtx / 144 half-edges, a *cut* surface) **and** a port of
`HyperIdealHyperellipticUtility.calculateCircleIntersections`, which derives θ via
jReality `Pn` (elliptic/hyperbolic), `MatrixBuilder` (rotate / hyperbolic
translate) and stereographic projection. Its golden vector is **not**
class-symmetric (mixed ±values), so no symmetry shortcut — mesh and θ must match
exactly. Viable approaches: (A) port the jReality geometry, or (B) run a Java
dumper for θ + connectivity (no PETSc needed) and hard-code them. Both are
multi-step with numerical-replication risk → its own future task.
**Key correction:** the Lawson generator (originally flagged HIGH) is actually
**Tier 3** — higher effort than Tier 1/2. Cheapest first wins:
1. **Tier 1 — `EuclideanCyclicConvergenceTest` on cathead** (asset already
present; the C++ edge gradient `G_e = α_opp(f⁺)+α_opp(f⁻) φ_e` makes the
Java assertion `α_opp+α_opp = π0.1` exactly the converged `G_e = 0`
condition with `phi_e = π0.1`). Fails if the solver ignores a non-default
`φ`, so it is a genuine check, not a tautology.
2. **Tier 1 — `SphericalConvergenceTest`** on the octahedron.
3. **Tier 2 — Wente genus-1 uniformization** — deferred to Phase 9f (quad/cyclic
pipeline; `wente_torus02.obj` is a quad mesh, not triangle).
4. **Tier 3 — Lawson generator** — ✅ DONE (PR #30): plain + branch-points golden
oracles GREEN; hyperelliptic deferred (option c).
### Status summary (2026-05-30)
- **Tier 1** ✅ done (PR #29): cyclic φ oracle + full analytic edge-DOF Hessian.
- **Tier 2** — exhausted; Wente → Phase 9f, the rest → Phase 13.
- **Tier 3** ✅ done (PR #30): plain + branch-points Lawson golden oracles;
hyperelliptic deferred.
### Build-verification finding (2026-05-29) → resolved (2026-05-30)
Attempting the Tier-1 `EuclideanCyclicConvergenceTest` port **build-verified** a
blocker first: `newton_euclidean``euclidean_hessian` threw *"edge DOFs are
not supported"*. The cyclic functional needs **vertex + edge** DOFs (the
*gradient* supported them; the *analytic cotangent Hessian* did not). That is
why PR #27 cross-validated only the cyclic *evaluation*, never a solve.
**Resolved (2026-05-30):** implemented a **block-FD edge-DOF Euclidean Hessian**
(`euclidean_hessian_block_fd` / `_sym` in `euclidean_hessian.hpp`, mirroring
`hyper_ideal_hessian_block_fd`); `newton_euclidean` now routes cyclic layouts
(edge DOFs present) through it, vertex-only layouts still use the analytic
cotangent Laplacian.
**Landed (`test_euclidean_functional.cpp`, all GREEN, 259/259 cgal tests pass):**
-`CyclicCircularEdge_PhiEntersGradient_CatHead` — solver-free check that the
circular-edge φ enters the gradient exactly (`ΔG_e = Δφ_e` @1e-12).
-`CyclicCircularEdge_CatHead_JavaXVal` — **the full Java convergence
oracle**: prescribe φ = π0.1 on one interior ("circular") edge of cathead,
solve the cyclic Newton, assert the realised `α_opp+α_opp = π0.1` @1e-9.
-`CyclicHessian_BlockFD_MatchesGradientFD_Tetrahedron` — the block-FD edge-DOF
Hessian matches the column-wise gradient FD on the full cyclic layout.
> DOF note: only the **single** circular edge gets an edge DOF (as in Java's
> one `circularHoleEdge`). Giving *every* edge a DOF makes λ_e redundant with
> u_i+u_j (a V-dim null space) and stalls Newton.
Spherical convergence (Tier-1 #2) was found **already covered** by
`test_newton_solver` (`Spherical_ConvergesFromPerturbation` et al.), and
`make_octahedron_face()` is a single face, not a closed octahedron — so it adds
little.
### Tier-2 (Wente) finding (2026-05-30) — needs a quad/cyclic pipeline
`wente_torus02.obj` is a **quad mesh** (1240 quads, genus 1), and the Java
`wente_uniformization.xml` golden (uniformizing group → τ = (√2+i√6)/(2√2) =
½ + i·√3/2, the hexagonal torus) comes from Java's **cyclic (quad) net**
uniformization. The C++ period-matrix pipeline (`run_torus_pipeline`) is
**triangle-based**, so a faithful bit-vs-Java comparison would require triangulating
(which changes the discrete conformal structure → no exact match) or a
**quad/cyclic period-matrix pipeline** — that belongs to **Phase 9f** (general
polygon meshes), not the triangle path. → Tier 2 deferred to Phase 9f; the
hexagonal golden τ is recorded here for when that lands.
### Done instead (2026-05-30): full analytic edge-DOF Hessian
With Tier-2 gated on a quad pipeline, the higher-value clean win was upgrading
the cyclic Hessian from block-FD to **fully analytic** (closed-form
`∂α_i/∂s_i = _i²/4A`, `∂α_i/∂s_j = ½cot α_i _j²/4A`), satisfying the
`novelty-statement.md §3.2` "analytic Hessians, not finite difference" claim for
the Euclidean path. Verified against block-FD and gradient-FD.
### Other generators in the Java tree
`HyperellipticCurveGenerator` (→ Phase 13), `SchottkyGenerator` (→ Phase 11a),
`FunctionalTest.createOctahedron/createTetrahedron` (trivial — C++ has
`make_octahedron_face` / `make_tetrahedron`).
---
## 4 — Recommended order of work
1.**DONE (PR #29)** — Tier 1: cyclic φ oracle + full analytic edge-DOF Hessian.
2.**DONE (PR #30)** — Tier 3: `make_lawson_square_tiled()` (low-level
half-edge; §5) + plain & branch-points golden oracles (per-class symmetric,
no DOF-order matching needed).
3. ⏸️ **Deferred** — Lawson **hyperelliptic** variant (XML-format reader +
`HyperIdealHyperellipticUtility` / jReality θ; non-symmetric golden).
4. ⏸️ **Phase 9f** — Wente genus-1 uniformization (quad/cyclic pipeline).
5. **Phase 10a/10c/11/13** — LATER items (genus-2, holomorphic forms, Schottky);
golden u\* / data recorded in the Java tests for reuse then.
---
## 5 — Why the Lawson mesh needs a low-level generator
`HyperIdealGenerator.createLawsonSquareTiledBase()` builds a **genus-2**
surface with **4 vertices and 12 edges** (24 half-edges), then triangulates
6 quad faces (adding 6 diagonal "aux" edges, θ=π; the 12 originals get θ=π/2).
With only 4 vertices and 12 edges, **most vertex pairs carry more than one
edge** (multi-edges). CGAL `Surface_mesh::add_face` and polygon-soup builders
key edges by their endpoint vertex pair and therefore **cannot represent
multi-edges** — they would either fail or glue the surface into the wrong
topology. Java sidesteps this with explicit half-edge IDs and
`linkOppositeEdge` / `linkNextEdge`.
The C++ port must replicate this with the **low-level half-edge API**
(`add_vertex`, `add_edge`, and explicit `set_next` / `set_target` /
`set_face` connectivity), mirroring the exact opposite/next links from
`createLawsonSquareTiledBase()` (edges 023). The symmetric golden vector
then makes the resulting test robust even though CGAL's DOF ordering differs
from Java's.