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 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-31 14:08:58 +02:00
parent 101f3138ce
commit b0946c8704
5 changed files with 25 additions and 6 deletions

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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 <CGAL/boost/graph/iterator.h>
@@ -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);

View File

@@ -35,6 +35,7 @@
// └──────────────────────────────────────────────────────────────────────────┘
#include "conformal_mesh.hpp"
#include "constants.hpp"
#include "spherical_geometry.hpp"
#include "gauss_legendre.hpp"
#include <CGAL/boost/graph/iterator.h>
@@ -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
}
}

View File

@@ -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);
}