fix(gradient-checks): use relative error in all FD check functions
Finding-E from doc/reviewer/external-audit-2026-05-30.md. Scan also uncovered the same issue in inversive_distance_functional.hpp. Three functions used absolute error `|analytic - fd| > tol` while the rest of the library (euclidean_functional, spherical_functional, euclidean_hessian, hyper_ideal_functional) all use relative error `|analytic - fd| / max(1, |analytic|) > tol`. Absolute error is too strict for large gradients (false failures) and too lenient for small gradients. Fixed: cp_euclidean_functional.hpp gradient_check_cp_euclidean() cp_euclidean_functional.hpp hessian_check_cp_euclidean() inversive_distance_functional.hpp gradient_check_inversive_distance() All three now use the relative criterion and accumulate all failures before returning (ok=false instead of early return on first mismatch). Default tol updated from 1e-6/1e-5 to 1e-4, matching the Java FunctionalTest convention used by all other checks in the library. Error message updated to print rel-err instead of raw diff. 266/266 CGAL tests pass, 0 failed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -354,32 +354,37 @@ inline double inversive_distance_energy(
|
||||
}
|
||||
|
||||
/// FD gradient check for the Inversive-Distance functional (central diff).
|
||||
/// Uses the same **relative** error criterion as every other gradient check:
|
||||
/// `|analytic − fd| / max(1, |analytic|) < tol`.
|
||||
inline bool gradient_check_inversive_distance(
|
||||
const ConformalMesh& mesh,
|
||||
const std::vector<double>& x,
|
||||
const InversiveDistanceMaps& m,
|
||||
double eps = 1e-5,
|
||||
double tol = 1e-6)
|
||||
double tol = 1e-4)
|
||||
{
|
||||
auto G = inversive_distance_gradient(mesh, x, m);
|
||||
const std::size_t n = G.size();
|
||||
bool ok = true;
|
||||
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
std::vector<double> xp = x, xm = x;
|
||||
xp[i] += eps;
|
||||
xm[i] -= eps;
|
||||
double Ep = inversive_distance_energy(mesh, xp, m);
|
||||
double Em = inversive_distance_energy(mesh, xm, m);
|
||||
double fd = (Ep - Em) / (2.0 * eps);
|
||||
if (std::abs(G[i] - fd) > tol) {
|
||||
double Ep = inversive_distance_energy(mesh, xp, m);
|
||||
double Em = inversive_distance_energy(mesh, xm, m);
|
||||
double fd = (Ep - Em) / (2.0 * eps);
|
||||
double err = std::abs(G[i] - fd);
|
||||
double scale = std::max(1.0, std::abs(G[i]));
|
||||
if (err / scale > tol) {
|
||||
std::cerr << "[inversive-distance] FD gradient mismatch at DOF " << i
|
||||
<< ": analytic=" << G[i]
|
||||
<< " FD=" << fd
|
||||
<< " diff=" << (G[i] - fd) << "\n";
|
||||
return false;
|
||||
<< " rel-err=" << (err / scale) << "\n";
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
/// Newton equilibrium check: returns `true` iff the gradient at `x`
|
||||
|
||||
Reference in New Issue
Block a user