Pareto Front Generation

Unlike HP-MOCD — whose front trades intra-density against inter-separation — PRISM’s front sweeps community resolution. Each non-dominated member of the archive is (locally) optimal for a different granularity of community structure, from fine splits (γ = 2.0) through standard Newman modularity (γ = 1.0) down to coarse super-communities (γ = 0.5).

A single .generate_pareto_front() call gives you the whole spectrum, so you can pick the partition whose resolution matches your domain — no rerunning with different resolution parameters.

Instantiate the Algorithm

Same setup as the Basic Usage page:

  import networkx as nx
from pymocd import Prism

G = nx.karate_club_graph()
alg = Prism(
    graph=G,
    debug_level=1,
    swarm_size=100,
    num_gens=100,
)
  

Generate the Pareto Front

  alg.run()                                  # evolve the swarm
frontier = alg.generate_pareto_front()     # read the archive
  
  • Return value: list[tuple[dict[int, int], list[float]]]

    • First element: partition dict — {node_id: community_id}

    • Second element: objective values in the order PRISM minimizes them

      • [0] = -Q_0.5 — negated modularity at coarse resolution (γ = 0.5)
      • [1] = -Q_1.0 — negated classical Newman modularity (γ = 1.0)
      • [2] = -Q_2.0 — negated modularity at fine resolution (γ = 2.0)

Inspect a Solution

  labels, objs = frontier[0]
neg_q05, neg_q10, neg_q20 = objs

print(f"Communities: {len(set(labels.values()))}")
print(f"Q(γ=0.5) = {-neg_q05:.4f}   # coarse")
print(f"Q(γ=1.0) = {-neg_q10:.4f}   # standard")
print(f"Q(γ=2.0) = {-neg_q20:.4f}   # fine")
  

Selecting Your Preferred Solution

The front is ordered arbitrarily — pick by objective, not by index:

GoalHow
Standard modularity (classical Newman Q)Minimize objs[1] (equivalently, maximize -objs[1]).
Coarse partitions (fewer, larger communities)Minimize objs[0]. γ = 0.5 escapes the resolution limit upward.
Fine partitions (many small communities)Minimize objs[2]. γ = 2.0 splits aggressively.
Knee point / balancedPick the member closest to the origin in normalized objective space.
Domain metricCompute your metric (conductance, NMI vs ground truth, etc.) on each labels dict and pick the best.

Example — grab the partition with the best classical modularity:

  best_idx = min(range(len(frontier)), key=lambda i: frontier[i][1][1])
best_labels, best_objs = frontier[best_idx]

print(f"Best Q_1.0 = {-best_objs[1]:.4f}")
print(f"k = {len(set(best_labels.values()))} communities")
  

The Convenience Accessor

If you just want the single scalar best Q score from the archive (after negation), use:

  alg.best_q()   # float — max over Pareto members of (−min(objs[1]))
  

This is handy for quick comparisons against Louvain/Leiden without iterating the front yourself.


Why a resolution-diverse front helps

Classical modularity has a well-known resolution limit: it cannot detect communities smaller than a threshold set by the total edge count. PRISM’s multi-γ front sidesteps this by optimizing three resolutions simultaneously — the fine-resolution member (γ = 2.0) will split a Q-merged super-community whenever doing so is justified under γ > 1, and the coarse member (γ = 0.5) will merge small-but-related communities when that is justified under γ < 1. You get to choose the resolution after seeing the data, not before.

Last updated 18 Apr 2026, 22:19 -0300 . history