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/csilab-ufop/pymocd
cd pymocd
make build
First detection¶
pymocd.rimpso is the recommended entry point:
import networkx as nx
import pymocd
G = nx.karate_club_graph()
communities = pymocd.rimpso(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¶
rimpso takes its budget as keyword arguments, shown here at its defaults:
communities = pymocd.rimpso(
G,
pop_size=100,
num_gens=100,
inertia=0.4,
cognitive=0.7,
social=0.7,
local_rate=0.35,
archive=100,
ls_period=10,
seed=0,
)
num_gens is the generation count: the search always runs all of them.
inertia, cognitive and social are the swarm's three velocity terms;
local_rate is the per-node rate of the resolution-directed local move;
archive is the capacity of the external Pareto archive, one slot per
particle so it holds the whole profile; ls_period is how often the full
local search runs; and seed is the run seed, whose default of 0 reproduces
the single trajectory the search flew before the seed was a parameter.
Resolution is not a parameter — a single run covers the whole ladder.
mmcomo takes a different four knobs plus gap and beta, at its own
paper's defaults (pop_size=100, num_gens=50, cross_rate=0.1,
mut_rate=0.1, gap=10).
Every other detector takes its own paper's parameters as keyword arguments —
r and alpha for moga_net and ccm, divisions for ccm and krm, w
/ c1 / c2 / lpa_sweeps for gdpso, n_walk / alpha_mut /
mut_sweeps for cdrme, rand_networks for mocd_d. The
detector API reference lists every signature with its
default.
hpmocd is the exception: it takes the graph and nothing else, running at its
published configuration and returning the max-Q partition from its Pareto
front (the front itself is available via
hpmocd_fronts). To vary its budget, or
to plug in your own Python objective functions, use the pymocd.HpMocd class:
detector = pymocd.HpMocd(G, pop_size=200, num_gens=150)
communities = detector.run()
front = detector.generate_pareto_front() # [(partition, objectives), ...]
See Algorithms for what each detector optimizes, which paper it comes from, and whether its original authors released code.
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¶
Six detectors pick one partition from a Pareto front of candidates:
rimpso, hpmocd, mmcomo, ccm, krm and moga_net. To see the whole
candidate set, use rimpso_fronts, hpmocd_fronts, mmcomo_fronts,
ccm_fronts, krm_fronts or moga_net_fronts, which accept the same kwargs
as their detector and return a list[dict[node, community]]:
front, points, selected = pymocd.rimpso_fronts(G)
best = max(front, key=lambda p: pymocd.ari(p, gt))
gdpso and cdrme optimize a single scalar, so they have no front;
mocd_q and mocd_d do not expose theirs.
rimpso_fronts returns (partitions, points, selected): every member, its
(cut, pair) point, and the index the selector picked.
rimpso_select runs that selection
rule alone over partitions produced elsewhere.
See the fronts API reference for details.