Skip to content

fix(hardening): EPS harmonization, mypy gate, Zhou+Prony guards, anchor tests - #28

Merged
marcohost33-maker merged 2 commits into
mainfrom
claude/hardening-2026-06-04
Jun 4, 2026
Merged

marcohost33-maker merged 2 commits into
mainfrom
claude/hardening-2026-06-04

Conversation

@marcohost33-maker

Copy link
Copy Markdown
Owner

Summary

Targeted hardening of LiouScope (4 candidates from a verified deep-dive) plus a debug pass. No anchor values changed. No destructive git ops (ref AGENTS.md Working-agreements §2 / 2026-05-16 data-loss incident — branch-create + commit + push only).

1. EPS harmonization (S) — diagnostics/nonnormality.py

The hard-coded 1.0e-300 divide-by-zero floor for the Petermann inner product |<l,r>|^2 is now the named, shared _consts.EPS_DIV. The value is kept: 1.0e-300 is a floating-point underflow floor (≈ smallest normal double), not a physics tolerance — collapsing it onto EPS_GAP=1e-10 would wrongly flag near-defective-but-finite modes as inf. Verified empirically that the canonical fixtures have min|<l,r>|^2 ≈ 0.91…0.99, far from any threshold ⇒ no anchor change.

2. mypy CI gate (M) — ci.yml

Removed continue-on-error: true. Fixed all 18 previously type-blind findings (mostly no-any-return from numpy ops → annotated return locals; one is_dataclass instance-narrowing in io/export.py). No behaviour change. mypy src/liouscope now exits 0.

3. Zhou guard (M) — _zhou.py

Adopted canonical EPS_DIV (division floor) + EPS_GAP (zero-eigenvalue check) instead of inline magic numbers, matching nonnormality. New tests:

  • closed-form anchor (single-qubit pure dephasing, H=0, jump=Z, γ=0.5): dissipator spectrum {0,0,-1,-1} ⇒ gap=1 (hand-derived, numerically confirmed); diagonal ⇒ K=1; predictor collapses to t_lower = t_upper = log(1/eps)/gap = log(1000) = 6.9077….
  • defective-mode guard: a near-defective mode (denom→0) must be skipped (not poison K_max→inf), so t_upper stays finite.

4. Prony fallback (S) — fitting/prony.py

np.linalg.lstsq / np.roots on a near-singular Hankel matrix could raise or yield garbage. Now wrapped in try/except (LinAlgError, ValueError) → emits RuntimeWarning + falls back to a safe _default_seed (strictly positive amplitude).

fails-before proof (stashed prony.py, ran new tests against pre-guard code):

FAILED test_prony_seed_non_finite_input_falls_back[nan]   (LinAlgError: SVD did not converge)
FAILED test_prony_seed_non_finite_input_falls_back[inf]
FAILED test_prony_seed_non_finite_input_falls_back[-inf]
FAILED test_prony_seed_all_zero_signal_has_positive_amplitude  (assert 0.0 > 0)
4 failed → all pass after the guard

Debug pass

Property-probed fitting/ + diagnostics/ with empty / single / NaN / inf inputs. Prony was the single clear, provable bug. Two observations left as-is (documented/defensible, not bugs): choose_model({})"M0" (documented baseline fallback); estimate_neff_geyer(all-NaN)n (NaN propagation).

Anchor impact

pytest tests/test_anchors.py → 17 passed / 2 skipped (QuTiP), 0 anchor values changed.

Test plan

  • ruff check src tests → exit 0
  • mypy src/liouscope → exit 0 (18 → 0)
  • pytest -q → 162 passed / 4 skipped (was 155; +7 new tests)
  • pytest --cov=liouscope --cov-fail-under=80 → 87.95% coverage
  • anchor regressions green, 0 anchors changed
  • CI: 4 Python-matrix jobs (3.10–3.13) green — pending
  • CHANGELOG.md updated (Unreleased section)

Local verification on Python 3.14 surrogate; CI matrix 3.10–3.13 is the real gate.

🤖 Generated with Claude Code

…or tests

Four hardening candidates plus a debug pass:

1. EPS harmonization (nonnormality.petermann_factors): replace hard-coded
   1.0e-300 divide-by-zero floor with a named, shared _consts.EPS_DIV. Kept
   the value (underflow floor, NOT a physics tolerance) so no anchor changes;
   verified canonical fixtures have |<l,r>|^2 ~ O(1), far from any threshold.

2. mypy CI gate: removed continue-on-error in ci.yml; fixed all 18 prior
   type-blind findings (annotated numpy return locals, is_dataclass instance
   narrowing in io/export). No behaviour change. mypy now exits 0.

3. Zhou guard (_zhou): adopt the canonical EPS_DIV/EPS_GAP constants for the
   Petermann-style division floor and zero-eigenvalue check. New closed-form
   anchor test (single-qubit pure dephasing: gap=1, K=1 -> both bounds =
   log(1/eps)) + a defective-mode guard test.

4. Prony fallback (fitting.prony): wrap lstsq/np.roots in try/except, emit a
   RuntimeWarning and fall back to a safe default seed (positive amplitude)
   on near-singular/non-finite Hankel data. fails-before: the pre-guard code
   raised LinAlgError on all-NaN/inf input (proven by stashing prony.py).

Debug pass: property-probed fitting/ + diagnostics/ (empty/single/NaN/inf
inputs). Prony was the one clear, provable bug. choose_model({})->'M0' and
estimate_neff_geyer(NaN)->n are documented/defensible, left as observations.

Local gates: ruff 0, mypy 0, pytest 162 passed / 4 skipped, coverage 87.95%
(>=80). Anchor regressions 17 passed / 2 skipped, 0 anchor values changed.

Backup-First context: no destructive git ops (ref Working-agreements §2,
2026-05-16 data-loss incident) -- branch-create + commit + push only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@marcohost33-maker marcohost33-maker added the agent:claude PR authored by Claude Code label Jun 4, 2026
…jumps

CI 3.10 numpy stubs are stricter on ndarray shape-tuples than the local
numpy 2.4.6: 'target = target / norm' narrowed the 2D shape to a 1D-typed
result. Wrap the reassignment in np.asarray(..., dtype=complex) to keep the
generic ndarray annotation (same pattern as resolvent.py). Caught by the
now-enforcing mypy gate -- exactly its purpose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@marcohost33-maker
marcohost33-maker marked this pull request as ready for review June 4, 2026 11:55
@marcohost33-maker
marcohost33-maker merged commit 339e5ea into main Jun 4, 2026
10 checks passed
@marcohost33-maker
marcohost33-maker deleted the claude/hardening-2026-06-04 branch June 4, 2026 11:55
@chatgpt-codex-connector chatgpt-codex-connector Bot mentioned this pull request Jun 7, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:claude PR authored by Claude Code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant