Skip to content

Complete the drop-in API for neighbor-aware losses; numpy fast paths in the PyO3 layer - #7

Merged
basnijholt merged 3 commits into
mainfrom
pyo3-surface
Jun 10, 2026
Merged

Complete the drop-in API for neighbor-aware losses; numpy fast paths in the PyO3 layer#7
basnijholt merged 3 commits into
mainfrom
pyo3-surface

Conversation

@basnijholt

Copy link
Copy Markdown
Member

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 five Triangulation methods 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_vertices and get_simplices_attached_to_points are answered natively from the facet/vertex indexes (single lookups) instead of the reference's Python set scans
  • get_vertices passes None entries through as None (adaptive feeds the result of get_opposing_vertices, which contains None on hull facets, straight back into it)
  • SimplicesProxy supports 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, 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_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

Output types are unchanged everywhere (tuples/lists/sets), so the drop-in contract is untouched.

Benchmarks (A/B vs main, best of 5)

operationmainthis PR
np.asarray(tri.vertices) ×2000, 500-vertex mesh0.189 s0.004 s (50×)
constructor from numpy array, 2000×2D0.0089 s0.0077 s
add_point with numpy rows (4D, 600 pts)0.410 s0.400 s
macro insertion benchmarks (2D/3D/4D, hull-heavy)unchanged

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

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.
@basnijholt
basnijholt merged commit 4b44e56 into mainJun 10, 2026
17 checks passed
Sign up for freeto 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

@basnijholt