Skip to content

perf: Vectorize KDTree queries and edge insertion in connect_nodes_across_graphs#142

Open
Raj-Taware wants to merge 5 commits into
mllam:mainfrom
Raj-Taware:main
Open

perf: Vectorize KDTree queries and edge insertion in connect_nodes_across_graphs#142
Raj-Taware wants to merge 5 commits into
mllam:mainfrom
Raj-Taware:main

Conversation

@Raj-Taware

Copy link
Copy Markdown

Describe your changes

Replaces per-target Python loop with batch NumPy/SciPy operations in connect_nodes_across_graphs. Also fixes a latent bug: KDTree was built from list(G_source.nodes) but indices resolved against sorted(G_source.nodes), connecting the wrong source node when dict iteration order != sort order.

Motivation: Called for every edge type (g2m, m2g, m2m). Old code issued one KDTree query per target node and computed edge attributes scalar-by-scalar. Batch queries + vectorized NumPy ops eliminate the Python loop entirely.

Changes:

  • Batch KDTree query over all targets at once (all three methods)
  • nearest_neighbours: .ravel() + np.repeat for flat index arrays
  • within_radius: concatenate ragged query_ball_point results
  • Vectorized vdiff/len via np.linalg.norm across all edges
  • source_nodes_list sorted before KDTree build, fixing index mismatch
  • Removes _find_neighbour_node_idxs_in_source_mesh closure

Performance Benchmarks

Benchmark script: wmg#117.

Archetype Before After Speedup
graphcast ~58 s ~19 s ~3.1x
keisler ~52 s ~23 s ~2.3x
oskarsson_hierarchical ~38 s ~18 s ~2.1x

Scaling plots

graphcast

Before After
scaling_before_graphcast scaling_after_graphcast

keisler

Before After
scaling_before_keisler scaling_after_keisler

oskarsson_hierarchical

Before After
scaling_before_oskarsson scaling_after_oskarsson

Issue Link

Closes #138

Type of change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change
  • 📖 Documentation

Checklist before requesting a review

  • My branch is up-to-date with the target branch
  • I have performed a self-review of my code
  • For any new/modified functions/classes I have added docstrings that clearly describe its purpose, expected inputs and returned values
  • I have placed in-line comments to clarify the intent of any hard-to-understand passages of my code
  • I have updated the documentation to cover introduced code changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have given the PR a name that clearly describes the change, written in imperative form (context)
  • I have requested a reviewer and an assignee (assignee is responsible for merging)

Checklist for reviewers

  • the code is readable
  • the code is well tested
  • the code is documented (including return types and parameters)
  • the code is easy to maintain

Author checklist after completed review

  • I have added a line to the CHANGELOG:
    • added: new functionality
    • changed: default behaviour changed
    • fixes: bug fix

Checklist for assignee

  • PR is up to date with the base branch
  • the tests pass
  • author has added an entry to the changelog
  • Once ready, squash commits and merge.

Raj_taware added 5 commits April 23, 2026 02:30
- Fix 1: Add xfail marker for k=1 in nearest_neighbours parametrize (line 53-58)
  k=1 triggers scalar-vs-array bug in serial loop; fixed by vectorization
- Fix 2: Add non-empty guard in within_radius test (line 83)
  Assert that edges exist before testing their properties
- Fix 3: Change '<= k' to '== k' in nearest_neighbours test (line 68)
  Source has 25 nodes >= max k=8, so every target always gets exactly k neighbours
- Fix 4: Remove duplicate node-set check in containing_rectangle test (line 101-104)
  _assert_edge_attrs_valid already performs this check
- Fix 5: Fix import ordering (line 1-7)
  Add blank line between third-party and first-party imports
  Sort imports correctly per isort convention
…ross_graphs

- Batch all target positions and run one KDTree query per method
- Flatten nearest_neighbours result with ravel+repeat
- Flatten within_radius ragged output with np.repeat/np.concatenate
- Compute vdiff and len in one numpy pass over all edges
- Replace per-edge add_edge loop with add_edges_from
- Remove now-resolved xfail on nearest_neighbours k=1 (scalar-vs-array
  bug no longer exists in the vectorized path)
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.

Vectorize KDTree queries and edge insertion in connect_nodes_across_graphs

2 participants