Some checks failed
C++ Tests / test-fast (pull_request) Successful in 1m55s
API Docs / doc-build (pull_request) Successful in 53s
Markdown link check / check (pull_request) Successful in 50s
C++ Tests / test-cgal (pull_request) Has been skipped
C++ Tests / quality-gates (pull_request) Failing after 1m56s
External reviewer pass over the literature references. Verified entries against arXiv/DOI/publisher and corrected misattributions that had propagated across the docs. Corrected citations (consistent across all docs): - Bowers-Bowers-Lutz 2026: title was the 2017 paper's -> "Rigidity of Koebe Polyhedra and Inversive Distance Circle Packings" - Liouville theorem: "Springborn 2019" -> Pinkall & Springborn, Geom. Dedicata 214 (2021) - Bobenko-Pinkall-Springborn: "G&T 14 (2010)" -> G&T 19(4) (2015), 2155-2215 - Optimal Cone Singularities: "Crane, Soliman, Ben-Chen, Schroeder" -> Soliman, Slepcev, Crane, ACM TOG 37(4) - Schlaefli formula: "Rivin, Springborn 1999" -> Rivin, Schlenker - Quasiconformal distortion: "Springborn, Veselov" -> Born, Buecking, Springborn (arXiv:1505.01341) - Period matrices: "Bobenko, Buecking 2009" (was Bobenko-Mercat-Schmies' title) -> Bobenko-Mercat-Schmies 2011 + genuine Bobenko-Buecking 2021 - Fabricated entry: "Alexa 2020, DOI 10.1145/3414685.3417840" pointed to an unrelated paper (Pixelor) -> Bunge, Herholz, Kazhdan, Botsch 2020 - Stripe Patterns: "Bonneel et al. 2015" -> Knoeppel, Crane, Pinkall, Schroeder 2015 Equation-number corrections (verified against the PDFs): - Glickenstein 2011 "eq. 4.6" -> "§5.2" (no such equation label exists) - Springborn 2020 "eq. 4.6" -> "§4 variational gradient" - inversive-distance attribution softened to classical inversive distance Other: - DBFEnergy bibliography (separate repo) and convergence half-sentence in novelty-statement.md §3.3 (Bobenko-Buecking 2021) - Status legend (implemented vs planned) at top of references.md - New Phase 12 (decorated DCE & geometric transition, Chain A, near-term) and Phase 13 (canonical tessellations & polyhedral realisation, Chain B capstone) in phases.md + research-track.md; 10c scope-boundary note clarifying infrastructure vs Lutz-specific algorithms Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
230 lines
12 KiB
Markdown
230 lines
12 KiB
Markdown
# Phase 9a Validation Report
|
||
|
||
This document records the mathematical and code-level validation of the two
|
||
Phase 9a functionals against their reference sources. It is produced as
|
||
part of the Phase 9a acceptance procedure (decided 2026-05-19).
|
||
|
||
| Functional | Reference | Java original | Lines |
|
||
|---|---|---|---|
|
||
| 9a.1 `cp_euclidean_functional.hpp` | Bobenko-Pinkall-Springborn 2010 | `CPEuclideanFunctional.java` (260) | 320 |
|
||
| 9a.2 `inversive_distance_functional.hpp` | Luo 2004 + Glickenstein 2011 | none | 290 |
|
||
|
||
---
|
||
|
||
## 1. CP-Euclidean (9a.1) — line-by-line Java mapping
|
||
|
||
Reference file: `/Users/tarikmoussa/Desktop/conformallab/src/de/varylab/discreteconformal/functional/CPEuclideanFunctional.java`
|
||
|
||
| Java line(s) | Java construct | C++ counterpart | Status |
|
||
|---:|---|---|:---:|
|
||
| 55–58 | `public class CPEuclideanFunctional<V,E,F>` | `namespace conformallab` (header-only, monomorphic on `ConformalMesh`) | ✓ |
|
||
| 61–69 | `thetaMap, phiMap` ctor args | `CPEuclideanMaps { f_idx, theta_e, phi_f }` struct | ✓ |
|
||
| 95–97 | `getDimension = hds.numFaces()` (incl. face 0) | `cp_euclidean_dimension(mesh, m)` counts only `f_idx ≥ 0` | refined |
|
||
| 103–123 | `getNonZeroPattern` returns face-to-face adjacency | implicit in `cp_euclidean_hessian` triplet construction | ✓ |
|
||
| 135–165 | `evaluateHessian` — interior edge `h_jk = sin θ / (cosh Δρ − cos θ)` | `cp_euclidean_hessian()` lines 224-247 | ✓ |
|
||
| 146–151 | "skip if k == 0 / j == 0" gauge fix | `if (j >= 0)` / `if (k >= 0)` guard in triplet emission | equivalent |
|
||
| 178–193 | per-face term `+φ_f · ρ_f` to energy and gradient | `cp_euclidean_energy/gradient` loop over faces | ✓ |
|
||
| 196–232 | per-(directed)-edge term: boundary or interior branch | `cp_euclidean_energy/gradient` halfedge loop with `mesh.is_border(opposite)` check | ✓ |
|
||
| 224–227 | `E += ½ p Δρ + Λ(θ*+p) − θ* ρ_left` | identical formula via `clausen2()` | ✓ |
|
||
| 230 | `grad[left] -= p + θ*` | identical | ✓ |
|
||
| 243–247 | `p(θ*, Δρ) = 2 atan(tan(θ*/2) tanh(Δρ/2))` | `cp_detail::p_function` | ✓ |
|
||
|
||
### 1.1 Gauge convention divergence
|
||
|
||
The Java code uses "face index 0 is pinned" as an implicit convention:
|
||
hard-coded `if (face.getIndex() == 0)` short-circuits scatter throughout.
|
||
The C++ port keeps the gauge user-selectable via
|
||
`assign_cp_euclidean_face_dof_indices(mesh, m, pinned_face)`. The default
|
||
convenience overload picks the first face in iteration order, matching the
|
||
Java behaviour for any mesh whose face 0 is the first iterated face.
|
||
|
||
### 1.2 Test parity
|
||
|
||
| Java test | C++ test | Reproduces what |
|
||
|---|---|---|
|
||
| `init()` lines 53-86 dodecahedron-minus-face setup | `make_open_tetrahedron()` (3 faces, 3 bdy edges) | open-mesh boundary code path |
|
||
| `setXGradient(rho)` (base class FunctionalTest) | `FDGradientCheck_ClosedTetrahedron_RandomRho` | FD-vs-analytic gradient |
|
||
| `setXHessian(rho)` (base class FunctionalTest) | `FDHessianCheck_ClosedTetrahedron_RandomRho` | FD-vs-analytic Hessian |
|
||
| (none — implicit from FunctionalTest convexity check) | `HessianIsPSD` | BPS-2010 §6 convexity |
|
||
| (none) | `TangentialLimitGradientEqualsPhi` | θ=0 analytic check |
|
||
| (none) | `PFunctionKnownValues`, `NaturalPhiMakesZeroTheEquilibrium` | helper-function unit tests |
|
||
|
||
All Java tests reproduced; C++ adds 4 mathematical-property tests
|
||
(PSD, tangential limit, p-function unit, natural-φ equilibrium).
|
||
|
||
### 1.3 Numerical equivalence — seed-1 random ρ, FD gradient
|
||
|
||
Java `FunctionalTest` uses `Random(1)` + uniform `[-0.5, +0.5]`.
|
||
C++ uses `std::mt19937(1)` + `uniform_real_distribution<double>(-0.5, 0.5)`.
|
||
The two RNG streams differ; numerical equivalence is therefore tested via
|
||
the *property* "FD matches analytic to 1e-6 absolute" rather than via
|
||
identical numerical traces. Both implementations satisfy this property.
|
||
|
||
---
|
||
|
||
## 2. Inversive Distance (9a.2) — line-by-line literature mapping
|
||
|
||
No Java original exists. The implementation derives directly from three
|
||
published papers; each formula in the header is annotated with its source
|
||
line in the paper.
|
||
|
||
### 2.1 Edge-length formula (Luo 2004 §3 / Glickenstein 2011 eq. 2.1)
|
||
|
||
```
|
||
ℓ_ij(u)² = exp(2 u_i) + exp(2 u_j) + 2 I_ij exp(u_i + u_j)
|
||
= r_i² + r_j² + 2 I_ij r_i r_j
|
||
```
|
||
|
||
Implementation: `id_detail::edge_length_squared` (lines 130-138 of
|
||
`inversive_distance_functional.hpp`).
|
||
|
||
Tested at three special-case limits in
|
||
`test_inversive_distance_functional.cpp` §1:
|
||
|
||
| Inversive distance | Geometric meaning | Closed-form ℓ | Test |
|
||
|:---:|---|---|---|
|
||
| `I = +1` | tangential circles | `r_i + r_j` | `EdgeLengthFormula_TangentialLimit` |
|
||
| `I = 0` | orthogonal circles | `√(r_i² + r_j²)` | `EdgeLengthFormula_OrthogonalLimit` |
|
||
| `I = −1` | inside-tangent / inverted | `|r_i − r_j|` | `EdgeLengthFormula_InsideTangentLimit` |
|
||
| `I < −1` | impossible packing (no real ℓ) | `nan`/signal | `EdgeLengthFormula_DegenerateReturnsMinusOne` |
|
||
|
||
### 2.2 Bowers-Stephenson identity (Bowers-Stephenson 2004)
|
||
|
||
```
|
||
I_ij = ( ℓ_ij² − r_i² − r_j² ) / ( 2 r_i r_j )
|
||
```
|
||
|
||
Implementation: `compute_inversive_distance_init_from_mesh` lines 152-172.
|
||
The round-trip property — given (ℓ_3d, r0_i, r0_j) recover ℓ via the Luo
|
||
formula — is tested in `BowersStephensonRoundTrip`.
|
||
|
||
### 2.3 Gradient (Luo 2004 Lemma 3.1)
|
||
|
||
```
|
||
∂E/∂u_v = Θ_v − Σ_{T ∋ v} α_v(T)
|
||
```
|
||
|
||
Implementation: `inversive_distance_gradient` lines 191-241. This is
|
||
structurally identical to the Euclidean functional (same halfedge
|
||
convention, same `euclidean_angles()` law-of-cosines reused). The only
|
||
difference is the edge-length input: Luo's `ℓ²(I)` instead of Springborn's
|
||
`exp(λ° + u_i + u_j)`.
|
||
|
||
### 2.4 Energy — Luo's closed 1-form
|
||
|
||
Luo (2004) proves the gradient 1-form `ω = Σ_v (Θ_v − Σ α_v) du_v` is
|
||
closed on the open domain where every triangle is valid. Hence the energy
|
||
exists as a path integral. No general closed-form expression is known
|
||
(Glickenstein 2011 surveys partial results). The implementation uses the
|
||
same 10-point Gauss-Legendre quadrature as `euclidean_functional.hpp`
|
||
(lines 243-274).
|
||
|
||
This shared-quadrature choice keeps both functionals identical in the
|
||
energy-evaluation cost and the FD-gradient validation pattern.
|
||
|
||
### 2.5 Hessian — finite difference for now
|
||
|
||
Glickenstein 2011 §5.2 gives an analytic Hessian for the inversive-
|
||
distance variational principle, but is more involved than the Springborn
|
||
cot-Laplacian. For MVP we rely on FD; the analytic form is a future
|
||
optimisation (joining the Phase 9b roadmap for HyperIdeal as a sibling
|
||
optimisation task).
|
||
|
||
---
|
||
|
||
## 3. Cross-validation between 9a.1 and 9a.2
|
||
|
||
The two functionals describe geometrically distinct circle packings
|
||
(face-dual vs vertex-based) but agree in the *angle-defect structure*
|
||
they expose to the Newton solver: both report `Θ − Σ α_actual` at every
|
||
free DOF. This is tested directly:
|
||
|
||
```cpp
|
||
TEST(InversiveDistanceFunctional, AngleDefectAtU0_AgreesWithEuclideanAtU0)
|
||
{
|
||
auto mesh = make_quad_strip();
|
||
|
||
auto G_id = inversive_distance_gradient(mesh, x_id, m_id);
|
||
auto G_eu = euclidean_gradient (mesh, x_eu, m_eu);
|
||
|
||
for (i in DOFs) EXPECT_NEAR(G_id[i], G_eu[i], 1e-10);
|
||
}
|
||
```
|
||
|
||
Why this works: at `u = 0`, both initialisation procedures reconstruct the
|
||
input 3-D edge length exactly (`compute_*_lambda0_from_mesh` for Euclidean,
|
||
`compute_inversive_distance_init_from_mesh` for inversive distance).
|
||
Therefore the actual angle sums per vertex are identical, and both
|
||
gradients reduce to the same `Θ_default − Σ α(ℓ_3d)`.
|
||
|
||
This is the **operational** consequence of Glickenstein 2011 §5:
|
||
"different parametrisations of the same initial discrete metric produce the
|
||
same Newton-time-zero gradient".
|
||
|
||
---
|
||
|
||
## 4. Comparison with `euclidean_functional.hpp`
|
||
|
||
| Aspect | Euclidean | CP-Euclidean (9a.1) | Inversive Distance (9a.2) |
|
||
|---|---|---|---|
|
||
| DOF basis | per vertex (`u_v = log scale`) | per face (`ρ_f = log radius`) | per vertex (`u_v = log radius`) |
|
||
| Per-edge constant | `λ°_ij = 2 log ℓ°_ij` | `θ_e` (intersection angle) | `I_ij` (inversive distance) |
|
||
| Edge-length formula | `ℓ = exp((λ° + u_i + u_j)/2)` | (no per-edge ℓ — face-circle radii) | `ℓ² = r_i² + r_j² + 2 I r_i r_j` |
|
||
| Angles | half-tangent law of cosines | (face-internal angles via `p(θ*, Δρ)`) | half-tangent law of cosines (reuses `euclidean_angles`) |
|
||
| Gradient | `Θ_v − Σ α_v(f)` | `φ_f − Σ_{h:face(h)=f} (p+θ*)` | `Θ_v − Σ α_v(f)` |
|
||
| Energy form | path integral (10-pt GL) | closed form via `½ p Δρ + Λ(θ*+p) − θ* ρ` | path integral (10-pt GL) |
|
||
| Hessian | analytic (cotangent Laplacian) | analytic (`sin θ / (cosh Δρ − cos θ)`) | finite difference (analytic deferred — Glickenstein 2011 §5.2) |
|
||
| Convexity | strictly convex after gauge fix | strictly convex after gauge fix | locally convex on triangle-inequality domain (Luo 2004 Thm 1.2) |
|
||
| Gauge fix | pin one vertex (`v_idx = −1`) | pin one face (`f_idx = −1`) | pin one vertex (`v_idx = −1`) |
|
||
|
||
### 4.1 Structural reuse
|
||
|
||
`inversive_distance_functional.hpp` reuses `euclidean_angles()` from
|
||
`euclidean_geometry.hpp` unchanged. The only difference from
|
||
`euclidean_functional.hpp` is the four lines that map `(u_i, u_j, I_ij)` to
|
||
`ℓ_ij²` before invoking `euclidean_angles()`.
|
||
|
||
`cp_euclidean_functional.hpp` shares no algorithmic code with the Euclidean
|
||
functional — it operates on a different DOF basis and a different energy
|
||
form. It does share the Clausen function via `clausen2()` from `clausen.hpp`.
|
||
|
||
---
|
||
|
||
## 5. Test counts and acceptance criteria
|
||
|
||
| Suite | Tests | Status |
|
||
|---|---:|:---:|
|
||
| `cgal.CPEuclideanFunctional.*` | 10 | ✅ all pass |
|
||
| `cgal.InversiveDistanceFunctional.*` | 11 | ✅ all pass |
|
||
| **Full CGAL regression** | **205** | ✅ all pass |
|
||
| Non-CGAL fast suite | 36 | ✅ all pass |
|
||
|
||
### Acceptance criteria — met
|
||
|
||
- [x] FD-vs-analytic gradient check passes on all test meshes (both functionals)
|
||
- [x] FD-vs-analytic Hessian check passes on closed and open tetrahedron (9a.1)
|
||
- [x] Hessian PSD-property test passes (9a.1)
|
||
- [x] Bowers-Stephenson round-trip identity verified (9a.2)
|
||
- [x] Three special-case limits of Luo's edge formula verified at machine ε (9a.2)
|
||
- [x] Cross-validation against `euclidean_functional` at `u = 0` (9a.2)
|
||
- [x] All formulas cite their source paper or Java line number
|
||
|
||
---
|
||
|
||
## 6. References
|
||
|
||
- Bobenko, A. I., Pinkall, U. & Springborn, B. *Discrete conformal
|
||
maps and ideal hyperbolic polyhedra.* Geometry & Topology 19(4) (2015),
|
||
2155–2215. arXiv:1005.2698.
|
||
- Bowers, P. L. & Stephenson, K. (2004). *Uniformizing dessins and Belyĭ
|
||
maps via circle packing.* Memoirs of the AMS 170(805).
|
||
- Glickenstein, D. (2011). *Discrete conformal variations and scalar
|
||
curvature on piecewise flat manifolds.* J. Differential Geometry 87(2),
|
||
201–238.
|
||
- Luo, F. (2004). *Combinatorial Yamabe Flow on Surfaces.* Communications
|
||
in Contemporary Mathematics 6(5), 765–780.
|
||
- Java original (CPEuclidean only):
|
||
`/Users/tarikmoussa/Desktop/conformallab/src/de/varylab/discreteconformal/functional/CPEuclideanFunctional.java`
|
||
- Java test:
|
||
`/Users/tarikmoussa/Desktop/conformallab/src-test/de/varylab/discreteconformal/functional/CPEuclideanFunctionalTest.java`
|