test: add end-to-end skewed-torus and synthetic holonomy Re(τ)<0 tests

Finding-H from doc/reviewer/external-audit-2026-05-30.md
(java-port-audit missing-test item 7).

Guards against a regression where compute_period_matrix reverts to
reduce_to_fundamental_domain (no mirror fold) instead of normalizeModulus
(Finding 6 fix, Java-faithful): such a revert would silently produce
Re(τ) < 0 for lattices where the natural generators give a negative
real part.

Two new tests in test_phase7.cpp:

1. SkewedTorus_ReTauNegativeBeforeNorm_FoldedToPositive
   - New mesh: code/data/off/torus_skewed_4x4.off
     Flat 4×4 torus on parallelogram lattice ω₁=(4,0) ω₂=(-1,4);
     16 vertices, 32 triangles, χ=0 (genus 1).
   - Full pipeline: newton_euclidean → cut_graph → euclidean_layout
     → compute_period_matrix(reduce=false) + compute_period_matrix(reduce=true)
   - Asserts: raw Re(τ) < 0, normalized Re(τ) ∈ [0,½], Im(τ) > 0, |τ| ≥ 1

2. SyntheticHolonomy_NegativeReTau_NormalizedToPositive
   - Bypasses mesh; supplies explicit ω₁=(4,0) ω₂=(-1,4) directly
   - Asserts raw τ = -0.25+i (to 1e-10), normalized τ = 0.25+i (to 1e-9)
   - Pin-points the mirror fold: Re(-0.25) → Re(+0.25)

277/277 CGAL tests pass, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-31 00:39:35 +02:00
parent adbf682f0f
commit 2325328f77
3 changed files with 154 additions and 1 deletions

View File

@@ -0,0 +1,50 @@
OFF
16 32 0
0.0 0.0 0.0
1.0 0.0 0.0
2.0 0.0 0.0
3.0 0.0 0.0
-0.25 1.0 0.0
0.75 1.0 0.0
1.75 1.0 0.0
2.75 1.0 0.0
-0.5 2.0 0.0
0.5 2.0 0.0
1.5 2.0 0.0
2.5 2.0 0.0
-0.75 3.0 0.0
0.25 3.0 0.0
1.25 3.0 0.0
2.25 3.0 0.0
3 0 1 5
3 0 5 4
3 1 2 6
3 1 6 5
3 2 3 7
3 2 7 6
3 3 0 4
3 3 4 7
3 4 5 9
3 4 9 8
3 5 6 10
3 5 10 9
3 6 7 11
3 6 11 10
3 7 4 8
3 7 8 11
3 8 9 13
3 8 13 12
3 9 10 14
3 9 14 13
3 10 11 15
3 10 15 14
3 11 8 12
3 11 12 15
3 12 13 1
3 12 1 0
3 13 14 2
3 13 2 1
3 14 15 3
3 14 3 2
3 15 12 0
3 15 0 3

View File

@@ -502,6 +502,109 @@ TEST(HolonomyEndToEnd, Torus8x8_TauMatchesRevolutionModulus)
check_torus("torus_8x8.off", /*R=*/3.0, /*r=*/1.0, /*rel_tol=*/0.05);
}
// ════════════════════════════════════════════════════════════════════════════
// Finding-H (java-port-audit item 7, external-audit-2026-05-30):
// End-to-end torus with Re(τ) < 0 before normalizeModulus
//
// torus_skewed_4x4.off is a flat torus on a parallelogram lattice
// ω₁ = (4, 0) ω₂ = (1, 4)
// The raw τ = ω₂/ω₁ = (0.25 + i), Re < 0.
// After normalizeModulus the mirror fold gives τ = (0.25 + i), Re ≥ 0.
//
// This guards against a regression where compute_period_matrix uses
// reduce_to_fundamental_domain (old code, no mirror fold) instead of
// normalizeModulus (Java-faithful, finding 6 fix) — in that case the
// pipeline would silently report τ with Re < 0 instead of Re ≥ 0.
// ════════════════════════════════════════════════════════════════════════════
TEST(HolonomyEndToEnd, SkewedTorus_ReTauNegativeBeforeNorm_FoldedToPositive)
{
// ── Load the skewed flat torus ────────────────────────────────────────
const std::string path =
std::string(CONFORMALLAB_DATA_DIR) + "/off/torus_skewed_4x4.off";
ConformalMesh mesh = load_mesh(path);
ASSERT_GT(mesh.number_of_vertices(), 0u) << "Failed to load torus_skewed_4x4.off";
ASSERT_EQ(conformallab::euler_characteristic(mesh), 0)
<< "Mesh must be a torus (χ=0)";
// ── Run the full pipeline ─────────────────────────────────────────────
EuclideanMaps maps = setup_euclidean_maps(mesh);
compute_euclidean_lambda0_from_mesh(mesh, maps);
int idx = 0;
bool pinned = false;
for (auto v : mesh.vertices()) {
if (!pinned) { maps.v_idx[v] = -1; pinned = true; }
else maps.v_idx[v] = idx++;
}
enforce_gauss_bonnet(mesh, maps);
std::vector<double> x0(static_cast<std::size_t>(idx), 0.0);
auto res = newton_euclidean(mesh, x0, maps);
ASSERT_TRUE(res.converged) << "Newton did not converge on skewed flat torus";
CutGraph cg = compute_cut_graph(mesh);
HolonomyData hol;
euclidean_layout(mesh, res.x, maps, &cg, &hol, /*normalise=*/false);
ASSERT_EQ(hol.translations.size(), 2u) << "Expected exactly 2 holonomy generators";
// ── Raw τ (no normalization) must have Re < 0 ─────────────────────────
// This confirms the mesh geometry does produce a τ with negative real
// part, making the normalizeModulus step non-trivial.
PeriodData pd_raw = compute_period_matrix(hol, /*reduce=*/false);
EXPECT_LT(pd_raw.tau.real(), 0.0)
<< "Raw τ must have Re < 0 for this skewed lattice"
<< " (got Re = " << pd_raw.tau.real() << ")";
// ── Normalized τ must have Re ≥ 0 (normalizeModulus was applied) ─────
PeriodData pd = compute_period_matrix(hol, /*reduce=*/true);
EXPECT_GE(pd.tau.real(), -1e-10)
<< "Normalized τ must have Re ≥ 0 (normalizeModulus mirror fold)"
<< " (got Re = " << pd.tau.real() << ")";
EXPECT_GT(pd.tau.imag(), 0.0)
<< "τ must lie in the upper half-plane";
EXPECT_GE(std::abs(pd.tau), 1.0 - 1e-9)
<< "|τ| ≥ 1 (fundamental domain condition)";
// ── Additional fundamental-domain conditions ───────────────────────────
// These are the normalizeModulus guarantees (Finding 6 / java-port-audit).
EXPECT_LE(pd.tau.real(), 0.5 + 1e-9)
<< "normalizeModulus must produce Re(τ) ≤ ½";
// The exact value depends on which generators tree-cotree finds;
// we do NOT assert a specific numeric value here (generator choice is
// an implementation detail of the tree-cotree algorithm, not of
// normalizeModulus). The assertions above are sufficient to confirm
// that the mirror fold was applied.
}
// ════════════════════════════════════════════════════════════════════════════
// Finding-H synthetic sanity: compute_period_matrix with explicit Re(τ)<0
// holonomy verifies the mirror fold numerically (no mesh, no tree-cotree).
// ════════════════════════════════════════════════════════════════════════════
TEST(HolonomyEndToEnd, SyntheticHolonomy_NegativeReTau_NormalizedToPositive)
{
// Lattice: ω₁=(4,0), ω₂=(-1,4) → τ_raw = (-1+4i)/4 = -0.25+i
// normalizeModulus: Re=-0.25 < 0 → mirror: τ = -conj(τ) = +0.25+i
HolonomyData hol;
hol.translations = {
Eigen::Vector2d(4.0, 0.0),
Eigen::Vector2d(-1.0, 4.0)
};
PeriodData pd_raw = compute_period_matrix(hol, /*reduce=*/false);
EXPECT_NEAR(pd_raw.tau.real(), -0.25, 1e-10) << "Raw Re(τ) must be -0.25";
EXPECT_NEAR(pd_raw.tau.imag(), 1.0, 1e-10) << "Raw Im(τ) must be 1.0";
PeriodData pd = compute_period_matrix(hol, /*reduce=*/true);
EXPECT_GE(pd.tau.real(), 0.0 - 1e-9) << "Normalized Re(τ) ≥ 0";
EXPECT_LE(pd.tau.real(), 0.5 + 1e-9) << "Normalized Re(τ) ≤ ½";
EXPECT_NEAR(pd.tau.real(), 0.25, 1e-9) << "Mirror fold: Re = -0.25 → +0.25";
EXPECT_NEAR(pd.tau.imag(), 1.0, 1e-9) << "Im(τ) preserved by mirror fold";
EXPECT_GE(std::abs(pd.tau), 1.0 - 1e-9) << "|τ| ≥ 1";
}
// ════════════════════════════════════════════════════════════════════════════
// FundamentalDomain — genus-1 parallelogram
// ════════════════════════════════════════════════════════════════════════════

View File

@@ -706,7 +706,7 @@ index. Add a comment:
| E | `cp_euclidean_functional.hpp` | 338349, 373387 | Inconsistency | Medium | ✅ Fixed 2026-05-31 |
| F | test files | — | Test gap | Medium | ✅ Fixed 2026-05-31 |
| G | test files | — | Test gap | Medium | ✅ Fixed 2026-05-31 |
| H | test files | — | Test gap | Medium | 🟠 Open |
| H | test files | — | Test gap | Medium | ✅ Fixed 2026-05-31 |
| I | CI workflow | — | Arch risk | High | 🔵 Open |
| MINOR-1 | `spherical_functional.hpp` | 404 | Doc error | Minor | 🟡 Open |
| MINOR-2 | `spherical_functional.hpp` | 470471 | Accuracy | Minor | 🟡 Open |