Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
## 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 (95.7 % containment, within/seam ratio 1.54) 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. (Davis-Kahan is the *mechanism* — see body — NOT the numeric evidence; the gate is the containment ratio.)

**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.

**The measurement (ES largest component, 261 buses / 348 lines; containment = fraction of L⁺ dipole-response energy kept in the seed's basin):**

| tier | basins | within | seam | ratio | verdict |
|---|---|---|---|---|---|
| **HEEL (top split)** | 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) — 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.

**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.

**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.

**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.

---

## 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

**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.
Expand Down
4 changes: 4 additions & 0 deletions crates/perturbation-sim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,7 @@ path = "examples/basin_placement_learning.rs"
[[example]]
name = "outage_over_hhtl_hops"
path = "examples/outage_over_hhtl_hops.rs"

[[example]]
name = "family_basin_weyl_multihop"
path = "examples/family_basin_weyl_multihop.rs"
260 changes: 260 additions & 0 deletions crates/perturbation-sim/examples/family_basin_weyl_multihop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
//! PROBE — family-basin partitioning recovers hop-locality on the Weyl axis
//! (the reinstatement test for `E-OUTAGE-CASCADE-IS-NON-LOCAL`).
//!
//! Claim (operator): the un-partitioned DC cascade is non-local (measured), but
//! **with family basins** the perturbation math becomes hop-local — a rank-1
//! trip inside a basin localizes to that block (Davis-Kahan), and only crosses
//! to the next basin at the weak seam. So "multi-hop Weyl" = chained per-block
//! first-hop Weyl, gated at the seams.
//!
//! Why the earlier outage probe found non-locality: it seeded the **max-flow
//! line** — which is the *seam* (the bottleneck carrying cross-basin flow). A
//! seam trip leaks globally by construction. This probe separates the two cases.
//!
//! Measurement (real ES grid, the old PyPSA data), swept over **every HHTL tier**
//! (HEEL top split → HIP → LEAF):
//! 1. `hhtl_keys` → the family-basin partition at that tier.
//! 2. CONTAINMENT — inject a balanced dipole, DC-solve θ = L⁺p (the
//! effective-resistance response = the perturbation shape), and measure the
//! fraction of response energy Σθ² that stays in the seed's basin:
//! - WITHIN-basin dipoles → block-localized (high) ⇒ hop-local;
//! - SEAM-straddling dipoles → leaks across (the hop boundary).
//! 3. WEYL — for a within-basin line trip, confirm `weyl_satisfied`.
//!
//! Verdict gate (honest, no rubber-stamp): a tier is HOP-LOCAL only if within-basin
//! containment ≥ 0.70 (block-tight) AND within/seam ratio ≥ 1.30 (a real margin)
//! AND Weyl satisfied. No "2×null" (the null ≈ 0.5 for coarse tiers and breaks the
//! test) and no Davis-Kahan bound as evidence (it is vacuous when the eigengap is
//! tiny). The ratio, not the absolute, is the hop-boundary test.
//!
//! Run: cargo run --manifest-path crates/perturbation-sim/Cargo.toml \
//! --example family_basin_weyl_multihop -- /tmp/pypsa/buses.csv /tmp/pypsa/lines.csv ES

use perturbation_sim::{
from_pypsa_csv, hhtl_keys, spectral_perturbation, symmetric_eigen, Grid, HhtlKey,
};
use std::collections::BTreeMap;
use std::fs;

const MIN_BASIN: usize = 6; // basins smaller than this are too tiny for a meaningful dipole
const REL_TOL: f64 = 1e-9;

type Basin = (u16, u16, u16);
/// A tier partitioner: a node's HHTL key → its basin at one HHTL tier.
type TierPart = fn(HhtlKey) -> Basin;

/// Fraction of response energy `Σθ²` carried by `members`.
fn containment(theta: &[f64], members: &[usize]) -> f64 {
let total: f64 = theta.iter().map(|x| x * x).sum();
if total <= 0.0 {
return 0.0;
}
let inb: f64 = members.iter().map(|&i| theta[i] * theta[i]).sum();
inb / total
}

/// Balanced ±1 dipole between `s` and `t`.
fn dipole(n: usize, s: usize, t: usize) -> Vec<f64> {
let mut p = vec![0.0; n];
p[s] = 1.0;
p[t] = -1.0;
p
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/// The honest HOP-LOCAL gate (single source, used by `main` and tested):
/// block-tight within-basin containment (`wm ≥ 0.70`) AND a real within/seam
/// margin (`ratio ≥ 1.30`) AND a valid within-basin Weyl check — **and the
/// evidence must actually exist** (`has_within && has_straddle`). Missing
/// evidence ⇒ NOT supported (no ∞-ratio or defaulted-true Weyl false positives).
/// No "2×null" gate (the null ≈ 0.5 for coarse tiers and breaks the test); no
/// Davis-Kahan bound (it is vacuous when the eigengap is tiny).
fn tier_supported(
has_within: bool,
has_straddle: bool,
wm: f64,
ratio: f64,
weyl_ok: bool,
) -> bool {
has_within && has_straddle && wm >= 0.70 && ratio >= 1.30 && weyl_ok
}

fn main() {
let args: Vec<String> = std::env::args().collect();
let (bpath, lpath, country) = (
args.get(1)
.map(String::as_str)
.unwrap_or("/tmp/pypsa/buses.csv"),
args.get(2)
.map(String::as_str)
.unwrap_or("/tmp/pypsa/lines.csv"),
args.get(3).map(String::as_str).unwrap_or("ES"),
);
let buses = fs::read_to_string(bpath).expect("read buses.csv");
let lines = fs::read_to_string(lpath).expect("read lines.csv");
let import = from_pypsa_csv(&buses, &lines, Some(country))
.expect("parse pypsa")
.largest_component();
let grid: &Grid = &import.grid;
let (n, m) = (grid.n, grid.edges.len());

// Family-basin partition (per tier) + eigendecomposition (once).
let keys = hhtl_keys(grid);
let eig = symmetric_eigen(&grid.laplacian_of(&vec![true; m]), n);
let mut deg = vec![0usize; n];
for e in &grid.edges {
deg[e.from] += 1;
deg[e.to] += 1;
}

println!("PROBE — family-basin Weyl multi-hop (reinstatement test, real {country} grid)");
println!(" grid: {n} buses, {m} lines");
println!(
" containment = fraction of L⁺ dipole-response energy in the seed's basin.\n \
hop-local ⇔ WITHIN-basin dipoles contained, SEAM-straddle dipoles leak. Per HHTL tier:\n"
);

// Tier partitioners: HEEL (top crisp split), HIP (+ the 2-line seam), LEAF (finest).
let tiers: [(&str, TierPart); 3] = [
("HEEL", |k| (k.heel, 0, 0)),
("HIP", |k| (k.heel, k.hip, 0)),
("LEAF", |k| (k.heel, k.hip, k.twig)),
];

let mean = |v: &[f64]| {
if v.is_empty() {
0.0
} else {
v.iter().sum::<f64>() / v.len() as f64
}
};
let alive = vec![true; m];

println!(
" {:<5} {:>6} {:>10} {:>10} {:>10} {:>8} verdict",
"tier", "basins", "within", "seam", "ratio", "weyl"
);

let mut any_tier_supported = false;
for (name, part) in tiers {
let mut basins: BTreeMap<Basin, Vec<usize>> = BTreeMap::new();
for (i, k) in keys.iter().enumerate() {
basins.entry(part(*k)).or_default().push(i);
}

// WITHIN-basin containment.
let mut within: Vec<f64> = Vec::new();
for members in basins.values() {
if members.len() < MIN_BASIN {
continue;
}
let mut by_deg = members.clone();
by_deg.sort_by_key(|&i| std::cmp::Reverse(deg[i]));
let theta = eig.pseudo_apply(&dipole(n, by_deg[0], by_deg[1]), REL_TOL);
within.push(containment(&theta, members));
}
// SEAM-straddle containment (dipole across an inter-basin edge).
let mut straddle: Vec<f64> = Vec::new();
for e in &grid.edges {
let (ba, bb) = (part(keys[e.from]), part(keys[e.to]));
if ba == bb {
continue;
}
let (ma, mb) = (&basins[&ba], &basins[&bb]);
if ma.len() < MIN_BASIN || mb.len() < MIN_BASIN {
continue;
}
let theta = eig.pseudo_apply(&dipole(n, e.from, e.to), REL_TOL);
straddle.push(containment(&theta, ma).max(containment(&theta, mb)));
}
// Weyl on a within-basin line trip at this tier. No within-basin line ⇒
// `false` (no valid check ⇒ not supported), NOT a defaulted `true`.
let weyl_ok = grid
.edges
.iter()
.position(|e| part(keys[e.from]) == part(keys[e.to]))
.map(|line| spectral_perturbation(grid, &alive, line).weyl_satisfied)
.unwrap_or(false);

let (has_within, has_straddle) = (!within.is_empty(), !straddle.is_empty());
let (wm, sm) = (mean(&within), mean(&straddle));
// ratio = 0 (not ∞) when there are no seam samples — a missing seam
// comparison must never pass the margin trivially.
let ratio = if has_straddle && sm > 0.0 {
wm / sm
} else {
0.0
};
let supported = tier_supported(has_within, has_straddle, wm, ratio, weyl_ok);
any_tier_supported |= supported;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
println!(
" {name:<5} {:>6} {wm:>10.3} {sm:>10.3} {ratio:>10.2} {:>8} {}",
basins.values().filter(|m| m.len() >= MIN_BASIN).count(),
weyl_ok,
if supported { "HOP-LOCAL" } else { "leaks" }
);
}

println!("\n VERDICT:");
if any_tier_supported {
println!(
" [SUPPORTED at the crisp tier(s)] where the bisection is stable, a within-basin \
perturbation stays block-contained (≥0.70) and clearly beats the seam-straddle \
(ratio ≥1.30) with Weyl satisfied → multi-hop Weyl IS hop-local there. REINSTATES \
'hop bounds reach' on the eigenvalue axis — at the tier where the family-basin block \
structure is real. Finer tiers leak (loose coupling), exactly as the out-of-family \
tie growth (0→2→9→20) and the marginal deep-tier DK gaps predict."
);
} else {
println!(
" [NOT SUPPORTED on this grid] no tier shows block-tight containment (≥0.70) with a \
≥1.30 within/seam ratio. Family basins concentrate the perturbation above random, but \
the blocks leak too much to call the seam a hop boundary on real ES — report honestly, \
do not promote. The earlier outage non-locality finding stands unrefined."
);
}
println!(
" NOTE: containment is the L⁺ (effective-resistance) response energy; a high WITHIN vs \
low SEAM ratio is the block-localization signature (Cheeger/spectral-clustering). The \
ratio, not the absolute, is the hop-boundary test."
);
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn containment_basic_and_edges() {
// All energy on members → 1.0.
assert!((containment(&[3.0, 4.0, 0.0], &[0, 1]) - 1.0).abs() < 1e-12);
// θ=[3,4] → energies 9,16; members={0} → 9/25.
assert!((containment(&[3.0, 4.0], &[0]) - 9.0 / 25.0).abs() < 1e-12);
// Zero field → 0.0, never NaN.
assert_eq!(containment(&[0.0, 0.0], &[0]), 0.0);
// Empty members → 0.0.
assert_eq!(containment(&[1.0, 2.0], &[]), 0.0);
}

#[test]
fn dipole_is_balanced() {
let p = dipole(5, 1, 3);
assert_eq!(p, vec![0.0, 1.0, 0.0, -1.0, 0.0]);
assert!(p.iter().sum::<f64>().abs() < 1e-12); // Σp = 0
}

#[test]
fn gate_requires_evidence_not_just_thresholds() {
// Strong, complete evidence → supported.
assert!(tier_supported(true, true, 0.96, 1.54, true));
// Missing seam samples (ratio would have been ∞) → NOT supported.
assert!(!tier_supported(true, false, 0.96, 0.0, true));
// Missing within-basin line / Weyl false → NOT supported.
assert!(!tier_supported(true, true, 0.96, 1.54, false));
// No within evidence → NOT supported.
assert!(!tier_supported(false, true, 0.96, 1.54, true));
// Thin margin (the leaky tiers) → NOT supported.
assert!(!tier_supported(true, true, 0.58, 0.93, true));
// Block-tight but margin just under 1.30 → NOT supported.
assert!(!tier_supported(true, true, 0.95, 1.29, true));
}
}
Loading