Skip to content

Commit 41e75cf

Browse files
committed
perturbation-sim: probe — family-basin Weyl multi-hop is hop-local at the crisp tier (reinstatement)
Tests the operator hypothesis "with family basin it works now with weyl multihop" on real ES PyPSA data, sweeping every HHTL tier. Containment = fraction of L⁺ dipole-response energy kept in the seed's basin: tier basins within seam ratio verdict HEEL 2 0.957 0.621 1.54 HOP-LOCAL HIP 4 0.579 0.621 0.93 leaks LEAF 7 0.529 0.485 1.09 leaks At the crisp top split a within-basin perturbation stays 95.7% contained and beats the seam-straddle (ratio 1.54) — Davis-Kahan block-localization under weak inter-block coupling. REINSTATES "hop bounds reach" on the eigenvalue axis (tier-scoped): the earlier E-OUTAGE-CASCADE-IS-NON-LOCAL was the un-partitioned global solve AND seeded the max-flow line = the seam itself. Finer tiers leak (within ~ seam), matching probe C (bisection stable at top, marginal deeper) and the out-of-family tie growth 0->2->9->20. Self-correction: first verdict rubber-stamped a 0.044 gap + a vacuous DK bound (2199) as SUPPORTED; tightened to per-tier within>=0.70 AND within/seam>=1.30 AND Weyl, dropped DK-as-evidence. Epiphany E-FAMILY-BASIN-WEYL-HOP-LOCAL-AT- CRISP-TIER prepended (paired reinstatement). Zero-dep, deterministic; clippy + fmt clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
1 parent da264f3 commit 41e75cf

3 files changed

Lines changed: 223 additions & 0 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
## 2026-06-19 — E-FAMILY-BASIN-WEYL-HOP-LOCAL-AT-CRISP-TIER — REINSTATEMENT (tier-scoped) of `E-OUTAGE-CASCADE-IS-NON-LOCAL`: WITH the family-basin partition, a within-basin perturbation is block-localized (Davis-Kahan) at the **crisp top split**, so multi-hop Weyl IS hop-local there; finer tiers leak. The earlier non-locality was the *un-partitioned* global solve.
2+
3+
**Status:** FINDING (measured, real ES PyPSA data; probe `perturbation-sim/examples/family_basin_weyl_multihop.rs`). **Tier-scoped reinstatement** of the reach claim that `E-OUTAGE-CASCADE-IS-NON-LOCAL` had corrected. Operator hypothesis: "with family basin it works now with weyl multihop." Confirmed — at the crisp tier only.
4+
5+
**The measurement (ES largest component, 261 buses / 348 lines; containment = fraction of L⁺ dipole-response energy kept in the seed's basin):**
6+
7+
| tier | basins | within | seam | ratio | verdict |
8+
|---|---|---|---|---|---|
9+
| **HEEL (top split)** | 2 | **0.957** | 0.621 | **1.54** | **HOP-LOCAL** |
10+
| HIP | 4 | 0.579 | 0.621 | 0.93 | leaks |
11+
| LEAF | 7 | 0.529 | 0.485 | 1.09 | leaks |
12+
13+
At the **crisp top split** a within-basin perturbation stays **95.7 % contained** and beats the seam-straddle (ratio 1.54) — block-localized, Weyl satisfied. This **reinstates "hop bounds reach" on the eigenvalue axis**: the mechanism is Davis-Kahan block-localization under weak inter-block coupling (Cheeger/spectral-clustering). The earlier non-locality (`E-OUTAGE-CASCADE-IS-NON-LOCAL`) was measured on the **un-partitioned global solve** AND seeded the **max-flow line = the seam itself** (a seam trip leaks by construction); both errors are removed here.
14+
15+
**The honest scope — it is the COARSE crisp split, not all tiers.** HIP/LEAF leak (within ≈ seam): the perturbation is NOT contained at finer tiers. This matches the spectral structure exactly — probe C (bisection stability) is solid at the top (ES `(λ₃−λ₂)/λ₂ = 3.23`) and marginal deeper; the operator's "without family nodes" table shows out-of-family ties growing 0→2→9→20 HEEL→HIP→TWIG→LEAF (the deeper blocks are loosely coupled). **The 2-basin "HEEL" split here IS that table's HIP 2-line seam** (lines 46, 150) — `hhtl_keys` puts the first crisp Cheeger cut at the heel nibble; labels offset by one, structure agrees.
16+
17+
**The three-axis close (a "surge" decomposes, and each axis owns one aspect):** WHERE it breaks = CHAODA anomaly (manifold repulsion, `ndarray::hpc::clam`); HOW MUCH = Weyl magnitude (probes A–D, [G] exact in code); HOW it spreads = **family-basin block-localized Weyl multi-hop, hop-local at the crisp tier** (this finding) + the ketchup yield as the seam gate. The family-basin layer (`E-BASIN-IS-A-NODE`) is the keystone that block-diagonalizes the Laplacian so per-basin Weyl chains seam-to-seam.
18+
19+
**Self-correction (process):** the probe's first verdict rubber-stamped a 0.044 within-vs-seam gap + a **vacuous Davis-Kahan bound (2199)** as "SUPPORTED" — the same confirmation-bias trap. Tightened the gate to per-tier within ≥ 0.70 AND within/seam ratio ≥ 1.30 AND Weyl, dropped the DK bound as evidence (it is uninformative when the eigengap is tiny). Under the honest gate only HEEL passes — which is the correct, nuanced result. Cross-refs: `E-OUTAGE-CASCADE-IS-NON-LOCAL` (the claim reinstated), `E-BASIN-IS-A-NODE` (the family-basin keystone), `perturbation-sim::{spectral_perturbation, hhtl_keys}`, probes A–E (the spectral foundation, real PyPSA), the "without family nodes" tier table.
20+
21+
---
22+
123
## 2026-06-19 — E-OUTAGE-CASCADE-IS-NON-LOCAL — CORRECTION to `E-BASIN-IS-A-NODE`: the electricity-outage perturbation does NOT decay with HHTL hop; the "cascade round = hop / hop bounds reach" identity is a property of the COGNITIVE substrate (propagation rides basin-tree EDGES by construction), NOT of the DC-power-flow electrical metaphor — they must not be conflated
224

325
**Status:** FINDING (measured correction; probe `perturbation-sim/examples/outage_over_hhtl_hops.rs`). **Corrects** the second [H] sub-claim posted in `E-BASIN-IS-A-NODE` ("cascade round k = nodes at hop-distance k; Weyl bounds magnitude per round, hop-count bounds the reach"). Operator prompt: "model the electricity outage perturbation again with the HHTL L1-L4." The honest answer: I did, and it **refutes** the literal-electrical reading.

crates/perturbation-sim/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,7 @@ path = "examples/basin_placement_learning.rs"
120120
[[example]]
121121
name = "outage_over_hhtl_hops"
122122
path = "examples/outage_over_hhtl_hops.rs"
123+
124+
[[example]]
125+
name = "family_basin_weyl_multihop"
126+
path = "examples/family_basin_weyl_multihop.rs"
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
//! PROBE — family-basin partitioning recovers hop-locality on the Weyl axis
2+
//! (the reinstatement test for `E-OUTAGE-CASCADE-IS-NON-LOCAL`).
3+
//!
4+
//! Claim (operator): the un-partitioned DC cascade is non-local (measured), but
5+
//! **with family basins** the perturbation math becomes hop-local — a rank-1
6+
//! trip inside a basin localizes to that block (Davis-Kahan), and only crosses
7+
//! to the next basin at the weak seam. So "multi-hop Weyl" = chained per-block
8+
//! first-hop Weyl, gated at the seams.
9+
//!
10+
//! Why the earlier outage probe found non-locality: it seeded the **max-flow
11+
//! line** — which is the *seam* (the bottleneck carrying cross-basin flow). A
12+
//! seam trip leaks globally by construction. This probe separates the two cases.
13+
//!
14+
//! Measurement (real ES grid, the old PyPSA data), swept over **every HHTL tier**
15+
//! (HEEL top split → HIP → LEAF):
16+
//! 1. `hhtl_keys` → the family-basin partition at that tier.
17+
//! 2. CONTAINMENT — inject a balanced dipole, DC-solve θ = L⁺p (the
18+
//! effective-resistance response = the perturbation shape), and measure the
19+
//! fraction of response energy Σθ² that stays in the seed's basin:
20+
//! - WITHIN-basin dipoles → block-localized (high) ⇒ hop-local;
21+
//! - SEAM-straddling dipoles → leaks across (the hop boundary).
22+
//! 3. WEYL — for a within-basin line trip, confirm `weyl_satisfied`.
23+
//!
24+
//! Verdict gate (honest, no rubber-stamp): a tier is HOP-LOCAL only if within-basin
25+
//! containment ≥ 0.70 (block-tight) AND within/seam ratio ≥ 1.30 (a real margin)
26+
//! AND Weyl satisfied. No "2×null" (the null ≈ 0.5 for coarse tiers and breaks the
27+
//! test) and no Davis-Kahan bound as evidence (it is vacuous when the eigengap is
28+
//! tiny). The ratio, not the absolute, is the hop-boundary test.
29+
//!
30+
//! Run: cargo run --manifest-path crates/perturbation-sim/Cargo.toml \
31+
//! --example family_basin_weyl_multihop -- /tmp/pypsa/buses.csv /tmp/pypsa/lines.csv ES
32+
33+
use perturbation_sim::{
34+
from_pypsa_csv, hhtl_keys, spectral_perturbation, symmetric_eigen, Grid, HhtlKey,
35+
};
36+
use std::collections::BTreeMap;
37+
use std::fs;
38+
39+
const MIN_BASIN: usize = 6; // basins smaller than this are too tiny for a meaningful dipole
40+
const REL_TOL: f64 = 1e-9;
41+
42+
type Basin = (u16, u16, u16);
43+
/// A tier partitioner: a node's HHTL key → its basin at one HHTL tier.
44+
type TierPart = fn(HhtlKey) -> Basin;
45+
46+
/// Fraction of response energy `Σθ²` carried by `members`.
47+
fn containment(theta: &[f64], members: &[usize]) -> f64 {
48+
let total: f64 = theta.iter().map(|x| x * x).sum();
49+
if total <= 0.0 {
50+
return 0.0;
51+
}
52+
let inb: f64 = members.iter().map(|&i| theta[i] * theta[i]).sum();
53+
inb / total
54+
}
55+
56+
/// Balanced ±1 dipole between `s` and `t`.
57+
fn dipole(n: usize, s: usize, t: usize) -> Vec<f64> {
58+
let mut p = vec![0.0; n];
59+
p[s] = 1.0;
60+
p[t] = -1.0;
61+
p
62+
}
63+
64+
fn main() {
65+
let args: Vec<String> = std::env::args().collect();
66+
let (bpath, lpath, country) = (
67+
args.get(1)
68+
.map(String::as_str)
69+
.unwrap_or("/tmp/pypsa/buses.csv"),
70+
args.get(2)
71+
.map(String::as_str)
72+
.unwrap_or("/tmp/pypsa/lines.csv"),
73+
args.get(3).map(String::as_str).unwrap_or("ES"),
74+
);
75+
let buses = fs::read_to_string(bpath).expect("read buses.csv");
76+
let lines = fs::read_to_string(lpath).expect("read lines.csv");
77+
let import = from_pypsa_csv(&buses, &lines, Some(country))
78+
.expect("parse pypsa")
79+
.largest_component();
80+
let grid: &Grid = &import.grid;
81+
let (n, m) = (grid.n, grid.edges.len());
82+
83+
// Family-basin partition (per tier) + eigendecomposition (once).
84+
let keys = hhtl_keys(grid);
85+
let eig = symmetric_eigen(&grid.laplacian_of(&vec![true; m]), n);
86+
let mut deg = vec![0usize; n];
87+
for e in &grid.edges {
88+
deg[e.from] += 1;
89+
deg[e.to] += 1;
90+
}
91+
92+
println!("PROBE — family-basin Weyl multi-hop (reinstatement test, real {country} grid)");
93+
println!(" grid: {n} buses, {m} lines");
94+
println!(
95+
" containment = fraction of L⁺ dipole-response energy in the seed's basin.\n \
96+
hop-local ⇔ WITHIN-basin dipoles contained, SEAM-straddle dipoles leak. Per HHTL tier:\n"
97+
);
98+
99+
// Tier partitioners: HEEL (top crisp split), HIP (+ the 2-line seam), LEAF (finest).
100+
let tiers: [(&str, TierPart); 3] = [
101+
("HEEL", |k| (k.heel, 0, 0)),
102+
("HIP", |k| (k.heel, k.hip, 0)),
103+
("LEAF", |k| (k.heel, k.hip, k.twig)),
104+
];
105+
106+
let mean = |v: &[f64]| {
107+
if v.is_empty() {
108+
0.0
109+
} else {
110+
v.iter().sum::<f64>() / v.len() as f64
111+
}
112+
};
113+
let alive = vec![true; m];
114+
115+
println!(
116+
" {:<5} {:>6} {:>10} {:>10} {:>10} {:>8} verdict",
117+
"tier", "basins", "within", "seam", "ratio", "weyl"
118+
);
119+
120+
let mut any_tier_supported = false;
121+
for (name, part) in tiers {
122+
let mut basins: BTreeMap<Basin, Vec<usize>> = BTreeMap::new();
123+
for (i, k) in keys.iter().enumerate() {
124+
basins.entry(part(*k)).or_default().push(i);
125+
}
126+
127+
// WITHIN-basin containment.
128+
let mut within: Vec<f64> = Vec::new();
129+
for members in basins.values() {
130+
if members.len() < MIN_BASIN {
131+
continue;
132+
}
133+
let mut by_deg = members.clone();
134+
by_deg.sort_by_key(|&i| std::cmp::Reverse(deg[i]));
135+
let theta = eig.pseudo_apply(&dipole(n, by_deg[0], by_deg[1]), REL_TOL);
136+
within.push(containment(&theta, members));
137+
}
138+
// SEAM-straddle containment (dipole across an inter-basin edge).
139+
let mut straddle: Vec<f64> = Vec::new();
140+
for e in &grid.edges {
141+
let (ba, bb) = (part(keys[e.from]), part(keys[e.to]));
142+
if ba == bb {
143+
continue;
144+
}
145+
let (ma, mb) = (&basins[&ba], &basins[&bb]);
146+
if ma.len() < MIN_BASIN || mb.len() < MIN_BASIN {
147+
continue;
148+
}
149+
let theta = eig.pseudo_apply(&dipole(n, e.from, e.to), REL_TOL);
150+
straddle.push(containment(&theta, ma).max(containment(&theta, mb)));
151+
}
152+
// Weyl on a within-basin line trip at this tier.
153+
let weyl_ok = grid
154+
.edges
155+
.iter()
156+
.position(|e| part(keys[e.from]) == part(keys[e.to]))
157+
.is_none_or(|line| spectral_perturbation(grid, &alive, line).weyl_satisfied);
158+
159+
let (wm, sm) = (mean(&within), mean(&straddle));
160+
let ratio = if sm > 0.0 { wm / sm } else { f64::INFINITY };
161+
// Honest gate: block-tight containment AND a real within/seam margin.
162+
// (No "2×null" — the null is ~0.5 for coarse tiers and breaks the test;
163+
// no DK bound — it's vacuous when the eigengap is tiny.)
164+
let supported = wm >= 0.70 && ratio >= 1.30 && weyl_ok;
165+
any_tier_supported |= supported;
166+
println!(
167+
" {name:<5} {:>6} {wm:>10.3} {sm:>10.3} {ratio:>10.2} {:>8} {}",
168+
basins.values().filter(|m| m.len() >= MIN_BASIN).count(),
169+
weyl_ok,
170+
if supported { "HOP-LOCAL" } else { "leaks" }
171+
);
172+
}
173+
174+
println!("\n VERDICT:");
175+
if any_tier_supported {
176+
println!(
177+
" [SUPPORTED at the crisp tier(s)] where the bisection is stable, a within-basin \
178+
perturbation stays block-contained (≥0.70) and clearly beats the seam-straddle \
179+
(ratio ≥1.30) with Weyl satisfied → multi-hop Weyl IS hop-local there. REINSTATES \
180+
'hop bounds reach' on the eigenvalue axis — at the tier where the family-basin block \
181+
structure is real. Finer tiers leak (loose coupling), exactly as the out-of-family \
182+
tie growth (0→2→9→20) and the marginal deep-tier DK gaps predict."
183+
);
184+
} else {
185+
println!(
186+
" [NOT SUPPORTED on this grid] no tier shows block-tight containment (≥0.70) with a \
187+
≥1.30 within/seam ratio. Family basins concentrate the perturbation above random, but \
188+
the blocks leak too much to call the seam a hop boundary on real ES — report honestly, \
189+
do not promote. The earlier outage non-locality finding stands unrefined."
190+
);
191+
}
192+
println!(
193+
" NOTE: containment is the L⁺ (effective-resistance) response energy; a high WITHIN vs \
194+
low SEAM ratio is the block-localization signature (Cheeger/spectral-clustering). The \
195+
ratio, not the absolute, is the hop-boundary test."
196+
);
197+
}

0 commit comments

Comments
 (0)