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:
@@ -338,12 +338,52 @@ TEST(PeriodMatrix, ComputePeriodMatrix_UnitSquare)
|
||||
TEST(PeriodMatrix, ComputePeriodMatrix_ReducedTau_InFD)
|
||||
{
|
||||
// ω_1 = (1, 0), ω_2 = (0.5, 0.25) → τ = 0.5 + 0.25i
|
||||
// |τ| = sqrt(0.25 + 0.0625) ≈ 0.559 < 1 → needs S step
|
||||
// |τ| = sqrt(0.25 + 0.0625) ≈ 0.559 < 1 → needs S step.
|
||||
// compute_period_matrix reduces with normalizeModulus (Finding 6), whose
|
||||
// mirror-folded target domain is { 0 ≤ Re ≤ ½, Im > 0, |τ| ≥ 1 } — note the
|
||||
// RIGHT boundary Re = +½ is CLOSED here. This is NOT the half-open SL(2,ℤ)
|
||||
// domain of is_in_fundamental_domain (−½ ≤ Re < ½), which excludes Re = +½
|
||||
// because +½ ≡ −½ under T. For this input normalizeModulus lands exactly on
|
||||
// τ = ½ + i, so we must check the normalizeModulus domain, not the SL(2,ℤ)
|
||||
// one (asserting is_in_fundamental_domain here would wrongly fail on +½).
|
||||
HolonomyData hol;
|
||||
hol.translations = { Eigen::Vector2d(1.0, 0.0), Eigen::Vector2d(0.5, 0.25) };
|
||||
PeriodData pd = compute_period_matrix(hol, /*reduce=*/true);
|
||||
EXPECT_TRUE(pd.in_fundamental_domain);
|
||||
EXPECT_TRUE(is_in_fundamental_domain(pd.tau, 1e-9));
|
||||
const double tol = 1e-9;
|
||||
EXPECT_GT(pd.tau.imag(), 0.0);
|
||||
EXPECT_GE(pd.tau.real(), 0.0 - tol); // 0 ≤ Re (mirror fold)
|
||||
EXPECT_LE(pd.tau.real(), 0.5 + tol); // Re ≤ ½ (closed right edge)
|
||||
EXPECT_GE(std::abs(pd.tau), 1.0 - tol); // |τ| ≥ 1
|
||||
// Concretely: τ = ½ + i.
|
||||
EXPECT_NEAR(pd.tau.real(), 0.5, 1e-12);
|
||||
EXPECT_NEAR(pd.tau.imag(), 1.0, 1e-12);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Golden-value oracle — pin normalizeModulus (the τ reduction used by
|
||||
// compute_period_matrix, Finding 6) bit-for-bit against the upstream Java
|
||||
// reference (de.varylab.discreteconformal.util.DiscreteEllipticUtility.
|
||||
// normalizeModulus), captured by calling the compiled Java method (openjdk 17)
|
||||
// on these exact τ. This locks the sign/fold conventions of the SL(2,ℤ)+mirror
|
||||
// reduction (0 ≤ Re ≤ ½, Im ≥ 0, |τ| ≥ 1) against an independent implementation,
|
||||
// catching drift the existing in-FD membership checks cannot (they only assert
|
||||
// the result lies in F, not that it is the SAME representative Java picks).
|
||||
//
|
||||
// To regenerate: /tmp/oracle/TauOracle.java. Values are Java printf %.17g.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
TEST(PeriodMatrix, NormalizeModulus_GoldenJava)
|
||||
{
|
||||
auto chk = [](double re, double im, double re_g, double im_g) {
|
||||
C n = conformallab::normalizeModulus(C(re, im));
|
||||
EXPECT_NEAR(n.real(), re_g, 1e-12);
|
||||
EXPECT_NEAR(n.imag(), im_g, 1e-12);
|
||||
};
|
||||
chk(0.3, 0.5, 0.11764705882352933, 1.4705882352941178); // |τ|<1 → S + folds
|
||||
chk(-0.4, 1.3, 0.40000000000000000, 1.3000000000000000); // Re<0 mirror fold
|
||||
chk(2.7, 0.8, 0.41095890410958880, 1.0958904109589043); // large Re → T
|
||||
chk(0.1, 2.0, 0.10000000000000000, 2.0000000000000000); // already in F
|
||||
chk(-1.6, 0.9, 0.41237113402061850, 0.92783505154639180); // T + S + mirror
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user