Getting started¶
Installation¶
pymocd requires Python 3.10 or newer. Prebuilt wheels are published for Linux, macOS, and Windows:
pip install pymocd
To build from source you need a Rust toolchain and maturin:
git clone https://github.com/oliveira-sh/pymocd
cd pymocd
make build
First detection¶
pymocd.scale is the recommended entry point:
import networkx as nx
import pymocd
G = nx.karate_club_graph()
communities = pymocd.scale(G)
Graph format
Every detector accepts a NetworkX or igraph graph with integer node ids and returns a crisp dict[node, community]. Isolated nodes are always assigned community -1.
Tuning¶
scale and mmcomo share the same evolutionary knobs:
communities = pymocd.scale(
G,
pop_size=100,
num_gens=50,
cross_rate=0.1,
mut_rate=0.1,
gap=10,
beta=0.05,
)
gap is the macro/micro co-evolution interval and beta the micro-stage perturbation strength.
scale additionally supports an adaptive stopping rule:
communities = pymocd.scale(G, adaptive_stop=True, conv_pval=0.1)
With adaptive_stop=True, a Welch t-test detects when the front has plateaued and stops early; num_gens then acts as a safety ceiling rather than a fixed budget.
For tuned HP-MOCD runs, use the HpMocd class — it exposes custom objectives, a per-generation callback, and the raw Pareto front:
alg = pymocd.HpMocd(G, pop_size=100, num_gens=100, cross_rate=0.7, mut_rate=0.5)
alg.set_on_generation(lambda gen, total, front_size: print(gen, total, front_size))
best = alg.run()
front = alg.generate_pareto_front()
run returns the max-Q partition from the front; generate_pareto_front returns [(partition, objectives), ...].
Custom objectives are callables (graph, partition) -> float to minimize, passed via objectives= or set_objectives; an empty list reverts to the built-in intra/inter pair.
See Algorithms for what each detector optimizes, and the detector API reference for every signature.
Threads¶
All detectors run on a shared Rayon thread pool. To cap it:
pymocd.max_cores(4)
Note
The Rayon pool is global and initialized once, so call max_cores before the first detection; repeat calls are ignored.
Evaluating results¶
When you have ground-truth labels, gt_metrics computes four scores at once over the shared nodes of two {node: community} dicts:
gt = {node: (0 if G.nodes[node]["club"] == "Mr. Hi" else 1) for node in G}
nmi, ami, ari, f1 = pymocd.gt_metrics(communities, gt)
Each metric is also available on its own: pymocd.nmi, pymocd.ami, pymocd.ari, and pymocd.f1, all with the same (partition, gt) signature. Details in the metrics API reference.
Inspecting Pareto fronts¶
scale and mmcomo each pick one partition from a Pareto front of candidates. To see the whole candidate set, use scale_fronts / mmcomo_fronts, which accept the same evolutionary kwargs (scale_fronts adds refine and topo_mode) and return a list[dict[node, community]]:
front = pymocd.scale_fronts(G)
best = max(front, key=lambda p: pymocd.ari(p, gt))
See the fronts API reference for details.