From b0946c87046d415385053b7cf6cc041915575bc1 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Sun, 31 May 2026 14:08:58 +0200 Subject: [PATCH] refactor(constants): centralize magic constants from functionals (N4/N6 audit) - Add LOG_EDGE_LENGTH_FLOOR (-30.0) for degenerate edge handling - Add HYPER_IDEAL_SCALE_FLOOR (0.01) for negative-scale clamping - Add ASIN_DOMAIN_GUARD (1.0 - 1e-15) for asin argument bounding - Each constant is documented with rationale and units - Update 4 usage sites: euclidean_functional, hyper_ideal_functional, spherical_functional, spherical_geometry - Add constants.hpp include to hyper_ideal_functional and spherical_functional 282/282 tests pass. Addresses N4 (unnamed magic constants) and N6 (centralize tolerances) from numerical-stability audit. Co-Authored-By: Claude Haiku 4.5 --- code/include/constants.hpp | 17 +++++++++++++++++ code/include/euclidean_functional.hpp | 2 +- code/include/hyper_ideal_functional.hpp | 7 ++++--- code/include/spherical_functional.hpp | 3 ++- code/include/spherical_geometry.hpp | 2 +- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/code/include/constants.hpp b/code/include/constants.hpp index a4d5afa..aac6ef0 100644 --- a/code/include/constants.hpp +++ b/code/include/constants.hpp @@ -17,4 +17,21 @@ constexpr double PI = 3.14159265358979323846264338328; /// 2π (full turn). constexpr double TWO_PI = 2.0 * PI; +// ── Numerical thresholds and bounds (N4/N6 audit) ────────────────────────── + +/// Edge length floor for log-scale functionals (euclidean_functional, spherical_functional). +/// exp(-30.0) ≈ 3e-7: edges below this length are treated as degenerate/zero. +/// Rationale: avoids log(tiny) overflow and enforces a minimum edge scale. +constexpr double LOG_EDGE_LENGTH_FLOOR = -30.0; + +/// Hyper-ideal scale factor lower bound (hyper_ideal_functional:179-181). +/// If b (scale) becomes negative (infeasible), clamp to 0.01 to keep geometry valid. +/// Rationale: ensures b > 0 maintains the Lorentzian model's causal structure. +constexpr double HYPER_IDEAL_SCALE_FLOOR = 0.01; + +/// Domain guard for asin in spherical_l (spherical_geometry:34). +/// Clamps exp(λ/2) to slightly below 1.0 so asin stays in [−π/2, π/2]. +/// Rationale: asin(x) is undefined for |x| > 1; this prevents IEEE Inf/NaN. +constexpr double ASIN_DOMAIN_GUARD = 1.0 - 1e-15; + } // namespace conformallab diff --git a/code/include/euclidean_functional.hpp b/code/include/euclidean_functional.hpp index f8cac0c..5a36966 100644 --- a/code/include/euclidean_functional.hpp +++ b/code/include/euclidean_functional.hpp @@ -162,7 +162,7 @@ inline void compute_euclidean_lambda0_from_mesh(ConformalMesh& mesh, EuclideanMa if (len > 1e-15) m.lambda0[e] = 2.0 * std::log(len); else - m.lambda0[e] = -30.0; // degenerate edge + m.lambda0[e] = LOG_EDGE_LENGTH_FLOOR; // degenerate edge } } diff --git a/code/include/hyper_ideal_functional.hpp b/code/include/hyper_ideal_functional.hpp index d5e028c..7efcb1f 100644 --- a/code/include/hyper_ideal_functional.hpp +++ b/code/include/hyper_ideal_functional.hpp @@ -31,6 +31,7 @@ // // res.energy, res.gradient #include "conformal_mesh.hpp" +#include "constants.hpp" #include "hyper_ideal_geometry.hpp" #include "hyper_ideal_utility.hpp" #include @@ -181,9 +182,9 @@ inline FaceAngleOutputs face_angles_from_local_dofs( if (v1b && v2b && a12 < 0.0) a12 = 0.0; if (v2b && v3b && a23 < 0.0) a23 = 0.0; if (v3b && v1b && a31 < 0.0) a31 = 0.0; - if (v1b && b1 < 0.0) b1 = 0.01; - if (v2b && b2 < 0.0) b2 = 0.01; - if (v3b && b3 < 0.0) b3 = 0.01; + if (v1b && b1 < 0.0) b1 = HYPER_IDEAL_SCALE_FLOOR; + if (v2b && b2 < 0.0) b2 = HYPER_IDEAL_SCALE_FLOOR; + if (v3b && b3 < 0.0) b3 = HYPER_IDEAL_SCALE_FLOOR; double l12 = lij(b1, b2, a12, v1b, v2b); double l23 = lij(b2, b3, a23, v2b, v3b); diff --git a/code/include/spherical_functional.hpp b/code/include/spherical_functional.hpp index adbe623..3cb69be 100644 --- a/code/include/spherical_functional.hpp +++ b/code/include/spherical_functional.hpp @@ -35,6 +35,7 @@ // └──────────────────────────────────────────────────────────────────────────┘ #include "conformal_mesh.hpp" +#include "constants.hpp" #include "spherical_geometry.hpp" #include "gauss_legendre.hpp" #include @@ -176,7 +177,7 @@ inline void compute_spherical_lambda0_from_mesh(ConformalMesh& mesh, SphericalMa if (half_sin > 1e-15) m.lambda0[e] = 2.0 * std::log(half_sin); else - m.lambda0[e] = -30.0; // very short edge: essentially 0 + m.lambda0[e] = LOG_EDGE_LENGTH_FLOOR; // very short edge: essentially 0 } } diff --git a/code/include/spherical_geometry.hpp b/code/include/spherical_geometry.hpp index 991ebb7..1702f09 100644 --- a/code/include/spherical_geometry.hpp +++ b/code/include/spherical_geometry.hpp @@ -31,7 +31,7 @@ constexpr double PI_SPHER = PI; inline double spherical_l(double lambda) { double half = std::exp(lambda * 0.5); - if (half >= 1.0) half = 1.0 - 1e-15; + if (half >= 1.0) half = ASIN_DOMAIN_GUARD; if (half <= 0.0) return 0.0; return 2.0 * std::asin(half); }