Skip to content

Return 0.0 for degenerate input in simplex_volume_in_embedding#15

Merged
basnijholt merged 1 commit into
mainfrom
fix-degenerate-simplex-volume
Jun 10, 2026
Merged

Return 0.0 for degenerate input in simplex_volume_in_embedding#15
basnijholt merged 1 commit into
mainfrom
fix-degenerate-simplex-volume

Conversation

@basnijholt

Copy link
Copy Markdown
Member

Summary

Fixes the last known behavioral divergence from the reference, the one called out in adaptive#493: simplex_volume_in_embedding raised ValueError on collinear/coplanar/coincident input where the reference returns 0.0.

This matters now that adaptive 1.5.0 auto-selects this backend: curvature losses feed degenerate vertex sets whenever the sampled function is locally flat, so LearnerND runs with curvature_loss_function() crashed with the Rust backend where the pure-Python backend returned a zero loss (the 7 known curvature test failures, plus test_learnernd_with_neighbor_aware_loss_runs in this repo).

What changed

The reference's tolerance contract is: a Cayley-Menger squared volume in (-1e-15, 0] is a degenerate input with volume 0.0; only below that band is it an error. This implementation raised for anything <= 0. Now:

  • The general Cayley-Menger path returns 0.0 inside the band (new documented EMBEDDED_VOLUME_SQ_EPS constant in tolerances.rs) and raises only below it.
  • 3 vertices in a >2D embedding (the curvature-loss shape) use the Cayley-Menger form expanded in squared distances instead of root-based Heron, so its rounding near zero behaves like the reference's numeric determinant and the same band applies. In 2D the reference's Heron path — which raises on any negative product — is mirrored unchanged.
  • 2 coincident vertices in a >2D embedding return 0.0 like the reference's Cayley-Menger path (2D keeps raising, as the reference does for any 2-vertex input there).

Testing

  • Full adaptive 1.5.0 test suite passes with the Rust backend active: 283 passed, 0 failures (previously 7 curvature failures). The 9 curvature tests pass individually too.
  • This repo's suite: 133 passed with adaptive 1.5.0 installed, including the previously-failing test_learnernd_with_neighbor_aware_loss_runs.
  • New Rust unit tests (collinear in 2D/3D, coplanar tetrahedron, coincident endpoints in 2D/3D) and Python parity tests, including 50 randomized near-collinear triangles asserting value-or-exception agreement with the reference.
  • cargo clippy -D warnings, cargo fmt, ruff 0.11.0 clean.

Follow-ups this unblocks

  1. Patch release (0.3.1).
  2. adaptive-side PR: bump _MIN_RUST_VERSION to (0, 3, 1), add the Rust-backend CI job deferred in adaptive#493, and adopt the 0.3.0 batched APIs (simplices_containing, default_loss).

The reference implementation treats a Cayley-Menger squared volume within
rounding noise of zero (> -1e-15) as a degenerate input with volume 0.0 and
only raises below that; this implementation raised for anything <= 0.
adaptive's curvature losses feed collinear/coplanar vertices whenever the
sampled function is locally flat, so with adaptive >= 1.5 auto-selecting
this backend, LearnerND runs with curvature_loss_function crashed where the
pure-Python backend returned a zero loss (7 known test failures noted in
adaptive#493).

- the general Cayley-Menger path now returns 0.0 in the reference's
  (-EMBEDDED_VOLUME_SQ_EPS, 0] band and raises only below it
- 3 vertices in a >2D embedding use the Cayley-Menger form expanded in
  squared distances instead of root-based Heron, so rounding near zero
  behaves like the reference's numeric determinant and the same band
  applies; in 2D the reference's Heron path (which raises on any negative
  product) is mirrored unchanged
- 2 coincident vertices in a >2D embedding are a zero-length segment, like
  the reference's Cayley-Menger path (2D keeps raising, as the reference
  does for any 2-vertex input)

With this fix the full adaptive 1.5.0 test suite passes with the Rust
backend active (283 passed; previously 7 curvature-loss failures plus
test_learnernd_with_neighbor_aware_loss_runs in this repo), clearing the
way for adaptive to bump _MIN_RUST_VERSION and add a Rust-backend CI job.
@basnijholt basnijholt merged commit 4fe925d into main Jun 10, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant