Complete the drop-in API for neighbor-aware losses; numpy fast paths in the PyO3 layer#7
Merged
Conversation
LearnerND with a neighbor-aware loss (triangle_loss / curvature_loss, nth_neighbors >= 1) calls five Triangulation methods that were missing, plus two behaviors only that path exercises. Add them: - get_vertex, get_neighbors_from_vertices, get_simplices_attached_to_points, get_opposing_vertices, get_face_sharing_neighbors - same semantics and exception types as the reference; get_opposing_vertices and get_simplices_attached_to_points are answered natively from the facet/vertex indexes instead of the reference's set scans - get_vertices passes None entries through as None (adaptive feeds the result of get_opposing_vertices, which contains None on the hull, straight back in) - SimplicesProxy supports the binary set operators in both operand orders (adaptive computes `... - tri.simplices`), delegating to a snapshot set Pinned by cross-validation tests against the reference on random 2D/3D triangulations, exception-type parity for unknown simplices, and an end-to-end LearnerND run with curvature_loss_function() driving 100 points through the previously uncovered code path.
- parse_point / parse_points bulk-copy f64 numpy arrays instead of iterating them as Python objects (other dtypes and nested sequences keep the generic path) - VerticesProxy.__array__ builds a PyArray2 directly instead of round-tripping through a list of Python tuples and numpy.array; dtype is honored via astype, and copy needs no handling because the snapshot is always freshly allocated Output types are unchanged everywhere (tuples/lists/sets), so the drop-in contract is untouched; all 112 tests pass. A/B microbenchmark vs main (best of 5): np.asarray(tri.vertices) snapshots 50x faster (0.189s -> 0.004s per 2000 calls on a 500-vertex mesh), constructor from a numpy array ~14% faster, add_point unchanged.
This was referenced Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent improvements to the Python-facing layer.
1. Drop-in API completeness (found by integration testing, not review)
LearnerND with a neighbor-aware loss (
triangle_loss/curvature_loss_function(), i.e.nth_neighbors >= 1) crashes with the current release: it calls fiveTriangulationmethods we never implemented, and relies on two behaviors only that code path exercises. This PR adds:get_vertex,get_neighbors_from_vertices,get_simplices_attached_to_points,get_opposing_vertices,get_face_sharing_neighbors— same semantics and exception types as the reference;get_opposing_verticesandget_simplices_attached_to_pointsare answered natively from the facet/vertex indexes (single lookups) instead of the reference's Python set scansget_verticespassesNoneentries through asNone(adaptive feeds the result ofget_opposing_vertices, which containsNoneon hull facets, straight back into it)SimplicesProxysupports binary set operators in both operand orders (adaptive computes... - tri.simplices), delegating to a snapshot setPinned by cross-validation tests against the reference on random 2D/3D triangulations, exception-type parity, and an end-to-end LearnerND run with
curvature_loss_function()driving 100 points through the previously uncovered path.2. numpy fast paths
parse_point/parse_pointsbulk-copy f64 numpy arrays instead of iterating them as Python objects (other dtypes and nested sequences keep the generic path)VerticesProxy.__array__builds aPyArray2directly instead of round-tripping through a list of Python tuples andnumpy.arrayOutput types are unchanged everywhere (tuples/lists/sets), so the drop-in contract is untouched.
Benchmarks (A/B vs main, best of 5)
np.asarray(tri.vertices)×2000, 500-vertex meshadd_pointwith numpy rows (4D, 600 pts)examples/adaptive_learnernd.py: 3.6× end-to-end (unchanged).Verification
pytest: 112 passed (incl. new cross-validation + LearnerND integration tests)cargo test --lib --tests: 18 passed; pre-commit clean