Skip to content

perf: closest() and near() use a matrix of coordinates, not a loop - #81

Open
petercorke wants to merge 1 commit into
mainfrom
perf/closest-vectorized
Open

petercorke wants to merge 1 commit into
mainfrom
perf/closest-vectorized

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Refs #74.

What

closest() and near() measured the metric of every vertex in a Python loop unless build_kdtree() had been called, and any add_vertex discards a tree, so a planner that interleaves add_vertex with closest (RTB's LatticePlanner, RRTPlanner) always paid for the loop.

The graph now keeps the coordinates of its embedded vertices as a matrix (_PointCache): built on first use, appended to by add_vertex, and the built-in metrics (L1, L2, SE2) are applied to all rows at once via the existing _metric_columns. A metric of your own is still applied to each vertex in turn. It is not pickled or copied and has its own staleness flag (not _spatialchange).

Measured

main this branch
closest, N=1,000 (examples/benchmark.py) 726 us 11.9 us
add_vertex 0.85 us 0.79 us (noise)
LatticePlanner.plan, 1,292 vertices 1.07 s 0.06 s
RRTPlanner.plan, 300 nodes 5.1 s 4.3 s

Vertices, edges and paths of the RTB RRT, PRM and Lattice planners are identical to main (digest of every coordinate and edge). RTB tests/test_mobile.py and MVTB tests/test_bundleadjust.py pass against this branch; make -C docs html builds.

Behaviour to know about

  • A coordinate changed in place (v.coord[0] = x) is not seen by closest/near, as with build_kdtree(). Assigning v.coord = x (or +=) is seen. Documented in the closest docstring; there is a test pinning it.
  • Removing a vertex or assigning v.coord on a vertex already in the graph drops the matrix, and the next query rebuilds it in O(n). That is still about 6x faster than the old loop (0.21 ms against 1.34 ms at n=2,000, measured), but the constructor form add_vertex(coord=x) keeps the matrix and is ~15x faster again. Not changed here: nothing in RTB or MVTB assigns coord after adding.
  • Coordinates that are not finite are left out of the matrix (the loop never picked them either); coordinates that are not all vectors of one length fall back to the loop, so the original errors still surface.

Tests

tests/test_point_cache.py (17 tests, expectations from a hand-written oracle): interleaved add/query across a buffer growth, L1/L2/SE2, ties, vertices without coordinates, removal, coord assignment, the in-place limit, non-finite and odd-shaped coordinates, user metric, near, pickle/copy/subgraph. 788 tests pass, mypy src is clean, and the changed lines are covered.

🤖 Generated with Claude Code

Without a k-d tree both measured the metric of every vertex in a Python
loop, and any add_vertex discarded the tree, so a planner that interleaves
add_vertex with closest (RTB's LatticePlanner, RRTPlanner) always paid for
the loop.

The graph now keeps the coordinates of its embedded vertices as a matrix,
built on first use and appended to by add_vertex, and applies the built-in
metrics to all rows at once.  Removing a vertex or assigning a coord drops
it.  A metric of your own is still applied to each vertex in turn.  It is
not pickled or copied, and has its own staleness flag.

closest at N=1,000: 726 us -> 12 us.  Lattice plan of 1,292 vertices: 1.07 s
-> 0.06 s.  Vertices, edges and paths of the RTB planners are unchanged.

A coordinate changed in place (v.coord[0] = x) is not seen, as with
build_kdtree(); documented.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 42 complexity · 0 duplication

Metric Results
Complexity 42
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@codecov

codecov Bot commented Oct 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.62%. Comparing base (74402d5) to head (a5d1285).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #81      +/-   ##
==========================================
+ Coverage   97.54%   97.62%   +0.08%     
==========================================
  Files           4        4              
  Lines        1586     1645      +59     
==========================================
+ Hits         1547     1606      +59     
  Misses         39       39              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
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