Skip to content

fix(esn): scale by true spectral radius, not infinity-norm (obligation 1.1)#101

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/esn-spectral-radius
Jun 2, 2026
Merged

fix(esn): scale by true spectral radius, not infinity-norm (obligation 1.1)#101
hyperpolymath merged 1 commit into
mainfrom
claude/esn-spectral-radius

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

What

esn::scale_to_spectral_radius scaled the recurrent matrix by its max absolute row sum (the matrix ∞-norm), which only bounds the spectral radius from above (ρ(W) ≤ ‖W‖∞). So the configured spectral_radius (e.g. 0.9) was never the value actually realised — the true ρ was some unknown smaller number. This is the genuine correctness bug behind obligation 1.1 (Echo State Property).

Fix

  • Add estimate_spectral_radius()power iteration (deterministic non-degenerate start vector, convergence-checked, nilpotent/zero-matrix guards). This estimates |λ_max|, the quantity that actually governs the Echo State Property.
  • scale_to_spectral_radius() now scales by the estimated spectral radius, so the configured value is achieved.

Tests (added)

  • test_spectral_radius_estimate_diagonal — diagonal matrix ρ = max|diag| (exact for power iteration).
  • test_scale_to_spectral_radius_achieves_target — after scaling, the re-estimated ρ = 0.9, proving the target is realised (not merely bounded).
  • Full suite green: cargo test -p esn → 11 unit + 6 integration pass.

Notes

Closes #88

https://claude.ai/code/session_01Gu1JFCZHuBtBhAWPr4sMQw


Generated by Claude Code

…igation 1.1)

scale_to_spectral_radius() scaled the recurrent matrix by its max absolute row
sum (the infinity-norm), which only bounds the spectral radius from above — so
the configured spectral_radius (e.g. 0.9) was never the value actually realised.

- Add estimate_spectral_radius() via power iteration (deterministic start vector,
  convergence-checked) — the quantity that genuinely governs the Echo State Property.
- scale_to_spectral_radius() now scales by the estimated spectral radius, so the
  configured value is achieved.
- Tests: diagonal-matrix radius estimate, and that scaling realises the target ρ.

https://claude.ai/code/session_01Gu1JFCZHuBtBhAWPr4sMQw
@hyperpolymath hyperpolymath marked this pull request as ready for review June 2, 2026 10:35
@hyperpolymath hyperpolymath merged commit 15da603 into main Jun 2, 2026
22 of 24 checks passed
@hyperpolymath hyperpolymath deleted the claude/esn-spectral-radius branch June 2, 2026 10:35
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
…igration in flight) (#105)

## Summary

The `android/` subtree is owner-authored Kotlin/Gradle work-in-flight
currently being migrated to Gossamer (see RFC PR #97). It is not
vendored upstream — it is intentional code. Two complementary governance
checks currently treat it as a banned-language violation:

- `Check for Banned Languages` (this repo's
`.github/workflows/language-policy.yml` — self-contained `find -name
'*.kt' ...`)
- `governance / Language / package anti-pattern policy` (the standards
reusable's `Check banned-language files` step — honours
`.hypatia-ignore` + `.hypatia-baseline.json`)

This PR adds a coordinated carve-out for both. After it lands on `main`,
the recently-merged PR #101 (ESN spectral-radius scaling — obligation
1.1) inherits the carve-out for any follow-up CI runs, and future
Android-touching PRs go green by default until the Gossamer migration
lands and `android/` is removed.

## Changes

**Commit 1 — `chore(cicd): add android/** carve-out to
.hypatia-ignore`**

Adds two complementary exemption files at the repo root:

- `.hypatia-baseline.json` — canonical glob form (`file_pattern:
"android/**"`) with `tracking_issue: "#97"`.
Consumed by the standards governance-reusable workflow's `in_baseline()`
helper (glob → regex translation: `**` → `.*`).
- `.hypatia-ignore` — legacy per-file enumeration (10 lines, one per
currently-tracked `.kt`/`.kts` file). Belt-and-braces for any tool that
only reads the flat-file form (the governance reusable's `is_exempt()`
checks both; `grep -qxF` requires literal lines, not globs).

**Commit 2 — `ci(language-policy): exclude android/ from Java/Kotlin
banned-language scan (Gossamer migration in flight — RFC #97)`**

Edits the `Check for Java/Kotlin files` step in
`.github/workflows/language-policy.yml`. The inline workflow does not
read `.hypatia-ignore`, so it needs its own path exclusion. Replaces the
single-line `find -name '*.kt' ...` with a `-prune` + `-o` form that
excludes `./android/` and `./.git/`:

```bash
VIOLATIONS=$(find . \
  \( -path './android' -o -path './android/*' -o -path './.git/*' \) -prune -o \
  \( -name "*.java" -o -name "*.kt" -o -name "*.kts" \) -type f -print)
```

## Verification

Local verification before push:

- New `find` pattern returns empty on the current tree.
- Old `find` would have caught 10 files under `android/`.
- `git ls-files '*.kt' '*.kts' '*.java' | grep -v '^android/'` returns
empty — no non-Android Kotlin/Java anywhere, so the carve-out is
precise.

## Rollback condition

Once the Gossamer migration (RFC PR #97) lands and `android/` is removed
from the repo, all three additions (`.hypatia-baseline.json`,
`.hypatia-ignore`, and the `find -prune` clause in
`language-policy.yml`) should be removed together.

## Related

- RFC PR #97 — Android Kotlin → Rust/Gossamer migration (the work that
makes this carve-out time-bounded).
- PR #101 — ESN spectral-radius scaling (already MERGED; the two
governance checks were the last red on it).
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
## Proof-obligation map (#84) — Tiers 0–3, batched

Full lowest-tier-first. Entire workspace: `cargo clippy --workspace
--all-targets` clean, `cargo test --workspace` all green.

### Tier 0 — foundations
- **0.1 panic-freedom (#86)** — 6 operational `unwrap`/`expect` removed;
`cfg_attr(not(test), deny(clippy::unwrap_used, expect_used))` on every
crate.
- **0.2 numeric containment (#87)** — proptest no-NaN/Inf for
`esn::step`, `lsm::step`, sensors IIR.
- **0.3 unsafe discipline** — `deny`/`forbid` (on main).

### Tier 1 — core algorithmic correctness
- **1.1 Echo State Property (#88)** — true spectral radius via power
iteration (merged in #101).
- **1.2 LSM bounded dynamics (#89)** — same spectral-radius fix in lsm +
membrane/spike-history bounds proptest.
- **1.3 bridge soundness (#90)** — totality / determinism /
bounded-output proptest.

### Tier 2 — system orchestration
- **2.1 lifecycle (#91)** — compile-time **typestate**
(`Created→Active→Down`, terminal shutdown); illegal transitions don't
compile. Matches `proofs/tla/Lifecycle.tla`.
- **2.2 concurrency (#92)** — no-lost-updates stress test (8×250);
data-race & deadlock freedom argued from safe-Rust + single-lock.
- **2.3 resource/affine (#93)** — design spec (`proofs/affine/`); Rust
ownership gives acquire-release-once today, AffineScript linear handles
deferred.

### Tier 3 — trust boundary
- **3.1 data-egress (#94)** — egress allow-list test (API key stays a
header; no device/sensor fields in body) +
neural-context-not-sent-when-disabled.
- **3.2 bounded external interaction (#95)** — saturating/capped backoff
(fixes a latent `pow` overflow); injection-safety via typed JSON.

Closes #86 #87 #89 #90 #91 #92 #94 #95 (1.1/#88 already merged via #101;
2.3/#93 remains a tracked spec).

_The two Kotlin banned-language CI checks are red from `main`'s
`android/` app (tracked in #83), not from this PR._

https://claude.ai/code/session_01Gu1JFCZHuBtBhAWPr4sMQw

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proof(1.1): prove the Echo State Property (+ fix spectral-radius misnomer)

2 participants