Skip to content

Add mutual k-nearest neighbor graphs and require fastersom 0.5.0 - #33

Merged
maltekuehl merged 3 commits into
mainfrom
feat/mnn-graph
Oct 26, 2026
Merged

maltekuehl merged 3 commits into
mainfrom
feat/mnn-graph

Conversation

@maltekuehl

Copy link
Copy Markdown
Contributor

Mutual k-nearest neighbor graphs

so.neighbor.mnn_graph.create(data, neighbor_count=20, connect_isolated_nodes=True, distance_metric=..., accuracy=..., job_count=..., use_gpu=...) connects two data points only if each is among the neighbor_count nearest neighbors of the other. The result is an undirected, unweighted nx.Graph with one node per data point, built with the same neighbor finder as knn_graph and snn_graph. It accepts an array or a fitted SOM.

  • In a k-nearest neighbor graph, a small group of data points far from the rest is still connected to it whenever the group has fewer members than neighbor_count. In the mutual graph it is not, because the rest of the data does not list the group's points among its own nearest neighbors.
  • With connect_isolated_nodes=True (the default), a data point without any mutual neighbor keeps the edge to its nearest neighbor, as in snn_graph.
  • The removal of each data point from its own neighbor list moves from KnnGraph.get_edges into a private helper, _exclude_self, which both graphs use. knn_graph is unchanged.

Tests (test/neighbor/test_mnn_graph.py, 15):

  • The edges equal a brute-force mutual k-nearest neighbor set for neighbor_count 1, 5 and 15 under all four distance metrics.
  • A distant group of 4 points among 500 is connected to the rest in the kNN graph but not in the mutual graph.
  • connect_isolated_nodes on and off.
  • A fitted SOM as input.

fastersom 0.5.0

  • pyproject.toml and uv.lock require fastersom 0.5.0, which finds the best-matching units of so.dimension.som on Apple GPUs (MPS) with fused Metal kernels.
  • The full example tutorial is re-run with it.

Release notes in docs/source/release-notes/0.12.0.md; please rename the file if the next release gets another number.

Checks

  • test/neighbor, test/cluster, test/dimension and test/test_public_api.py with fastersom 0.5.0 on the CPU: 130 passed (-m "not gpu and not no_github_ci").
  • ruff, ruff format, mypy (-p spatiomic) and bandit clean.
  • The full CPU suite (make unittest) was not run locally.

🤖 Generated with Claude Code

maltekuehl and others added 3 commits October 25, 2026 21:40
so.neighbor.mnn_graph.create connects two data points only if each is
among the neighbor_count nearest neighbors of the other. Data points
without mutual neighbors optionally keep the edge to their nearest
neighbor (connect_isolated_nodes). Accepts arrays or a fitted SOM and
uses the same neighbor finder as knn_graph and snn_graph.

The removal of each data point from its own neighbor list is factored
out of KnnGraph.get_edges into _exclude_self for reuse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fastersom 0.5.0 finds the best-matching units on Apple GPUs (MPS) with fused
Metal kernels.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.51%. Comparing base (3d167c9) to head (57e04d4).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #33      +/-   ##
==========================================
+ Coverage   82.22%   82.51%   +0.28%     
==========================================
  Files          55       56       +1     
  Lines        1975     2007      +32     
==========================================
+ Hits         1624     1656      +32     
  Misses        351      351              

☔ 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.

@maltekuehl
maltekuehl merged commit 87954a0 into main Oct 26, 2026
3 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.

2 participants