Communities and Constraints Update#
In February 2026 the way DPG computes communities (node groupings) and class boundaries (per-class feature constraints) was redesigned. This page documents the before/after outputs produced by the
export_iris_communities.py#
What changed#
Aspect |
Old (pre-Feb 2026) |
New (current) |
|---|---|---|
Community detection |
NetworkX |
Absorbing Markov-chain: class nodes are absorbing states, each predicate node gets a probability distribution over classes |
Node assignment |
Flat sets of node labels, one set per community |
Deterministic per-class clusters with an explicit Ambiguous bucket for low-confidence nodes |
Class boundaries |
Derived from graph predecessors of each class node |
Derived from community membership: only predicates that belong to a class cluster contribute to that class’s bounds |
Extra outputs |
None |
Probability (per-node absorption probabilities) and Confidence (margin between top-two class probabilities) |
Key commits:
Reproduction#
Both outputs below were generated with the same parameters
(5 estimators, random_state=42, default config) using the script at
counterfactual/scripts/export_iris_communities.py#
Old output — extracted at commit
8bb3583 (Jan 28 2026, last
commit before the rework):
{
"api": "old (LPA-based)",
"communities": [
[
"Class 1",
"petal length (cm) <= 4.45",
"petal length (cm) <= 4.85",
"petal length (cm) <= 4.95",
"petal length (cm) <= 5.05",
"petal length (cm) <= 5.2",
"petal length (cm) <= 5.35",
"petal length (cm) > 2.45",
"petal length (cm) > 4.45",
"petal width (cm) <= 1.35",
"petal width (cm) <= 1.55",
"petal width (cm) <= 1.65",
"petal width (cm) <= 1.7",
"petal width (cm) <= 1.75",
"petal width (cm) > 0.8",
"petal width (cm) > 1.35",
"sepal length (cm) <= 5.75",
"sepal length (cm) > 5.25",
"sepal width (cm) <= 2.75",
"sepal width (cm) > 2.9",
"sepal width (cm) > 3.1"
],
[
"Class 2",
"petal length (cm) <= 4.65",
"petal length (cm) > 2.5",
"petal length (cm) > 4.65",
"petal length (cm) > 4.85",
"petal length (cm) > 4.95",
"petal length (cm) > 5.05",
"petal length (cm) > 5.2",
"petal length (cm) > 5.35",
"petal width (cm) > 1.55",
"petal width (cm) > 1.65",
"petal width (cm) > 1.7",
"petal width (cm) > 1.75",
"sepal length (cm) <= 5.25",
"sepal length (cm) <= 6.05",
"sepal length (cm) > 5.75",
"sepal length (cm) > 6.05",
"sepal width (cm) <= 2.9",
"sepal width (cm) <= 3.1",
"sepal width (cm) > 2.75"
],
[
"Class 0",
"petal length (cm) <= 2.45",
"petal length (cm) <= 2.5",
"petal width (cm) <= 0.8"
]
],
"class_boundaries": {
"Class 0": [
"petal width (cm) <= 1.65",
"petal length (cm) <= 2.5"
],
"Class 1": [
"2.45 < petal length (cm) <= 5.35",
"0.8 < petal width (cm) <= 1.75",
"2.75 < sepal width (cm) <= 2.9",
"5.25 < sepal length (cm) <= 6.05"
],
"Class 2": [
"2.45 < petal length (cm) <= 5.35",
"0.8 < petal width (cm) <= 1.75",
"2.75 < sepal width (cm) <= 3.1",
"5.75 < sepal length (cm) <= 6.05"
]
},
"clusters": null
}
New output — extracted at commit
730dabd (current main):
{
"api": "new (absorbing Markov chain)",
"communities": null,
"class_boundaries": {
"Class Bounds": {
"Class 0": [
"petal width (cm) <= 1.65",
"petal length (cm) <= 2.5"
],
"Class 1": [
"0.8 < petal width (cm) <= 1.75",
"2.75 < sepal width (cm) <= 2.75",
"2.45 < petal length (cm) <= 5.35",
"5.25 < sepal length (cm) <= 6.05"
],
"Class 2": [
"petal width (cm) > 1.55",
"4.65 < petal length (cm) <= 4.65",
"5.25 < sepal length (cm) <= 6.05",
"sepal width (cm) <= 3.1"
]
}
},
"clusters": {
"Clusters": {
"Class 0": [
"petal width (cm) <= 1.65", "petal width (cm) <= 0.8",
"petal length (cm) <= 2.45", "petal length (cm) <= 2.5", "Class 0"
],
"Class 1": [
"petal width (cm) <= 1.7", "sepal width (cm) > 2.75",
"petal width (cm) <= 1.75", "Class 1",
"petal length (cm) <= 4.85", "sepal width (cm) > 3.1",
"sepal length (cm) <= 6.05", "petal length (cm) <= 5.05",
"sepal width (cm) <= 2.75", "petal length (cm) > 4.45",
"petal length (cm) > 2.45", "petal length (cm) <= 5.35",
"petal width (cm) > 1.35", "sepal length (cm) > 5.25",
"sepal width (cm) > 2.9", "petal width (cm) <= 1.55",
"sepal length (cm) <= 5.75", "petal width (cm) > 0.8",
"petal length (cm) <= 4.95", "petal length (cm) <= 5.2",
"petal length (cm) <= 4.45", "petal length (cm) > 2.5",
"petal width (cm) <= 1.35"
],
"Class 2": [
"Class 2", "petal width (cm) > 1.75",
"petal length (cm) > 4.95", "petal length (cm) > 4.65",
"sepal length (cm) > 6.05", "petal length (cm) <= 4.65",
"sepal length (cm) <= 5.25", "petal length (cm) > 5.05",
"petal width (cm) > 1.65", "petal length (cm) > 4.85",
"petal width (cm) > 1.55", "petal length (cm) > 5.35",
"sepal width (cm) <= 3.1", "sepal width (cm) <= 2.9",
"petal width (cm) > 1.7", "sepal length (cm) > 5.75",
"petal length (cm) > 5.2"
],
"Ambiguous": []
},
"Probability": {
"petal width (cm) <= 1.7": {"Class 0": 0.0, "Class 1": 0.69, "Class 2": 0.31},
"petal width (cm) <= 1.75": {"Class 0": 0.0, "Class 1": 0.96, "Class 2": 0.04},
"petal width (cm) > 1.75": {"Class 0": 0.0, "Class 1": 0.02, "Class 2": 0.98},
"petal length (cm) <= 2.45": {"Class 0": 1.0, "Class 1": 0.0, "Class 2": 0.0},
"..."
},
"Confidence Interval": {
"petal width (cm) <= 1.7": 0.38,
"petal width (cm) <= 1.75": 0.92,
"petal width (cm) > 1.75": 0.96,
"petal length (cm) <= 2.45": 1.0,
"..."
}
}
}
(The full JSON files live in outputs/iris_dpg_communities_OLD.json and
outputs/iris_dpg_communities_NEW.json.)
Analysis#
Class 0 boundaries are identical in both versions — this class is well-separated by
petal width <= 1.65andpetal length <= 2.5.Class 1 boundaries are nearly the same, but the new version derives them only from nodes whose absorption probability favours Class 1. In the old version the boundaries came from all predecessors of the Class 1 terminal node, regardless of how strongly they were associated.
Class 2 boundaries changed the most. The old version produced ranges identical to Class 1 (
2.45 < petal length <= 5.35,0.8 < petal width <= 1.75) because both classes shared many predecessor nodes. The new version narrows these topetal width > 1.55and4.65 < petal length <= 4.65, reflecting only the nodes that are probabilistically absorbed by Class 2.Community structure moved from non-deterministic to deterministic. LPA can return different groupings on each run; the absorbing-chain method always produces the same clusters for the same graph.
Richer per-node information. The new output includes absorption probabilities (e.g.,
petal width <= 1.75→ 96 % Class 1, 4 % Class 2) and confidence margins, enabling finer-grained analysis of borderline nodes.