Skip to content

Adding "networkit" flavour to scanpy.tl.leiden for parallel community detection#4170

Draft
amalia-k510 wants to merge 3 commits into
scverse:mainfrom
amalia-k510:networkit_gve
Draft

Adding "networkit" flavour to scanpy.tl.leiden for parallel community detection#4170
amalia-k510 wants to merge 3 commits into
scverse:mainfrom
amalia-k510:networkit_gve

Conversation

@amalia-k510

Copy link
Copy Markdown
Contributor

This PR adds NetworKit's ParallelLeiden as a new flavor option for scanpy.tl.leiden, enabling multithreaded Leiden community detection via scanpy.tl.leiden(adata, flavor="networkit"). The motivation behind is that scanpy's current Leiden backends (igraph, leidenalg) are single-threaded. For atlas-scale datasets (500k+ cells), clustering becomes a bottleneck. NetworKit's ParallelLeiden is a C++ parallel implementation available via pip with no additional compilation, making it the lowest-friction path to parallel Leiden in scanpy.

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 30.23256% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.70%. Comparing base (8f1d746) to head (7435700).
⚠️ Report is 27 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/scanpy/tools/_leiden.py 30.00% 21 Missing ⚠️
src/scanpy/_utils/__init__.py 18.18% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4170      +/-   ##
==========================================
+ Coverage   79.61%   79.70%   +0.08%     
==========================================
  Files         120      121       +1     
  Lines       12786    12972     +186     
==========================================
+ Hits        10180    10339     +159     
- Misses       2606     2633      +27     
Flag Coverage Δ
hatch-test.low-vers 78.83% <30.23%> (-0.02%) ⬇️
hatch-test.pre 79.64% <30.23%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/scanpy/_settings/__init__.py 91.72% <100.00%> (ø)
src/scanpy/_settings/presets.py 90.84% <100.00%> (+8.03%) ⬆️
src/scanpy/_utils/__init__.py 74.04% <18.18%> (-1.31%) ⬇️
src/scanpy/tools/_leiden.py 75.86% <30.00%> (-16.10%) ⬇️

... and 35 files with indirect coverage changes

@ilan-gold ilan-gold left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Would be best to run the installation check :)

Also please add an extra to our pyproject.toml.

Let's try to use a heuristic about warning users about large inputs to igraph and the faster alternative

Comment thread src/scanpy/_utils/__init__.py Outdated
raise ImportError(msg)


def ensure_network() -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used?

Suggested change
def ensure_network() -> None:
def ensure_networkit() -> None:

Comment thread src/scanpy/_utils/__init__.py Outdated


def ensure_network() -> None:
if importlib.util.find_spec("netowrkit"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if importlib.util.find_spec("netowrkit"):
if importlib.util.find_spec("networkit"):

Comment on lines +193 to +194
seed = int(rng.integers(np.iinfo(np.int64).max))
networkit.setSeed(seed, useThreadId=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite this, things aren't deterministic, right? Just making sure, but if that's the case, please comment why we do this (helps with determinism even if it's not perfect maybe?)

networkit.setSeed(seed, useThreadId=True)
# only undirected for Parallel Leiden
g = _utils.get_networkit_from_adjacency(adjacency, weighted=use_weights)
iterations = n_iterations if n_iterations > 0 else 3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did the number 3 come from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the the NetworKit's header file for the ParallelLeiden and found that default for ParallelLeiden.iterations is set to three; hence why I decided to use it here as well.
explicit ParallelLeiden(const Graph &graph, int iterations = 3, bool randomize = true, double gamma = 1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document that? Also, why isn't it the default then for python?

@ilan-gold

Copy link
Copy Markdown
Contributor

Also can you point this branch at networkit/networkit#1422 (and then main after merge followed by the correct min version when released) and use this method (also in your notebook) to make sure it works properly?

Comment on lines +314 to +315
if importlib.util.find_spec("networkit") is None:
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would still warn even if there is no networkit installed :)

Do you have a benchmarking notebook as a gist somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is already handled. It returns early if find_spec("networkit") is None, so the hint only fires when networkit's actually installed. Might've been from before I pushed the guard? Let me know if you're still seeing it warn without networkit and I'll take another look.

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