Pareto fronts¶
The evolutionary search optimizes competing objectives at once, so it ends with a Pareto front: a set of partitions where no member is better than another on every objective — coarse trades against fine, tight communities against well-separated ones.
Every detector already resolves this — each applies the selection rule published with its algorithm (max-modularity, MDL, etc.; see Algorithms) and returns a single partition. The front accessors exist for making that choice yourself: ground truth, a known expected community count, or your own quality metric.
HpMocd.generate_pareto_front()¶
Returns every non-dominated solution as a list of (partition, objective_values) tuples:
partition:dict[node, community]objective_values:list[float], one value per objective, all minimized — the built-in intra and inter objectives included. With custom objectives, the list matches the objectives you passed, in order.
import networkx as nx
from pymocd import HpMocd
G = nx.karate_club_graph()
front = HpMocd(G).generate_pareto_front()
for partition, objectives in front:
k = len(set(partition.values()))
print(f"{k} communities, objectives = {objectives}")
Both objectives are minimized
Older documentation described intra as "higher is better". It is not: lower is better for every value in objective_values.
Picking a member yourself¶
Max modularity¶
def modularity(G, partition):
comms = {}
for node, c in partition.items():
comms.setdefault(c, set()).add(node)
return nx.community.modularity(G, comms.values())
best, _ = max(front, key=lambda sol: modularity(G, sol[0]))
Against ground truth¶
Score every member with pymocd.gt_metrics (or ari, nmi, ami, f1 individually). Karate club, using the club attribute as ground truth:
import pymocd
gt = {v: int(G.nodes[v]['club'] != 'Mr. Hi') for v in G}
best, _ = max(front, key=lambda sol: pymocd.ari(sol[0], gt))
nmi, ami, ari, f1 = pymocd.gt_metrics(best, gt)
print(f"NMI={nmi:.3f} AMI={ami:.3f} ARI={ari:.3f} F1={f1:.3f}")
The oracle may not be on the front
Even the best front member can fall short of ARI = 1.0: the ground-truth partition may be dominated under the search objectives and never survive to the final front. The front bounds what selection can recover.
Target community count¶
target = 2
best, _ = min(front, key=lambda sol: abs(len(set(sol[0].values())) - target))
scale_fronts and mmcomo_fronts¶
scale and mmcomo expose their candidate sets as a plain list[dict] of partitions — no objective values attached. Each list is exactly what the corresponding detector selects from:
candidates = pymocd.scale_fronts(G)
best = max(candidates, key=lambda p: pymocd.ari(p, gt))
scale_fronts takes the same kwargs as scale, plus two of its own: refine (apply union-refinement to the merged front, on by default) and topo_mode (topology-handling mode, integer).
See also¶
- Plotting — visualize the front in objective space and draw the selected partition.
- Fronts API — full signatures for
generate_pareto_front,scale_fronts, andmmcomo_fronts.