Improve numerical robustness of hinfnorm/linfnorm#1066
Merged
Conversation
Poles on or outside the stability boundary whose modal residue is negligible relative to opnorm(B)*opnorm(C) are now treated as spurious remainders of imperfect pole-zero cancellations and no longer force the norm to Inf. The classification threshold is exposed through a new keyword resid_tol (default nx*sqrt(eps)); resid_tol = 0 recovers the strict behavior. Further robustness improvements to the two-step algorithm: - Evaluate the gain at detected crossing frequencies in addition to midpoints, guaranteeing progress when only a single crossing is detected (previously an infinite loop until maxIters). - Terminate when an iteration fails to improve the lower bound beyond the gamma-margin, handling tangential crossings and crossing detections that are artifacts of spurious near-boundary poles. - Make the imaginary-axis test relative in the eigenvalue magnitude so it remains meaningful for both slow and fast dynamics, for poles as well as Hamiltonian eigenvalues. - Guard all gain evaluations against non-finite values from evalfr at frequencies close to poles. - Seed the initial lower bound with the frequency of every pole, reducing iteration counts and avoiding a zero lower bound when the gain is singular at the previous candidate frequencies. - Add the missing zero-gain early return in the discrete-time path. - Unify non-convergence handling: both time domains now warn and return the best estimate instead of the discrete-time path throwing. The intermittent CI failure in test_conversion.jl was root-caused to minreal with default tolerance occasionally leaving a weakly observable mode on the unit circle whose gain contribution is genuine, in which case Inf is the correct answer for the resulting realization. The test now reduces with an explicit minreal tolerance and asserts against a threshold above that tolerance. The peak-frequency reference in test_matrix_comps.jl is only determined to within ~w*sqrt(tol) since the gain peak is flat; the tolerance at the default tol is relaxed and a tight assertion at tol=1e-10 is added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014nRhRodrFbP41QBEDQ9hTw
|
This is an automated message.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1066 +/- ##
==========================================
+ Coverage 91.64% 91.67% +0.03%
==========================================
Files 42 42
Lines 5660 5707 +47
==========================================
+ Hits 5187 5232 +45
- Misses 473 475 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.








Background
hinfnormintermittently failed in CI, most recently in run 29119351321 attest_conversion.jl:221, where the norm of an analytically-zero system difference evaluated toInf.Root-causing this revealed two separate things:
The specific CI failure is not a
hinfnormbug.minreal(::StateSpace)with its default tolerance (atol=0, rtol-based rank tests) occasionally leaves a weakly observable mode at/just outside the unit circle. Non-normality amplifies the modal residue of the leftover mode (the eigenvector condition number is ~1e4 for the failing realization), and evaluating the frequency response confirms the realization genuinely has gain ≈ 15 near the boundary —Infis the correct H∞ answer for the systemhinfnormwas handed. Whether the mode survivesminrealdepends on machine/BLAS rounding, hence the intermittency. The test now reduces with an explicitminrealtolerance, which deterministically removes the junk modes (verified over 100 perturbed reruns), and asserts against a threshold above that tolerance since truncated modes may contribute gain of the order of the truncation tolerance.The two-step algorithm had several genuine robustness weaknesses in adjacent scenarios, addressed here.
Changes to
hinfnorm/linfnormresid_tol*opnorm(B)*opnorm(C)are treated as spurious remainders of imperfect pole-zero cancellations and are ignored instead of forcingInf(e.g., the norm ofs/snow evaluates to 1). New keywordresid_tol, defaultnx*√eps;resid_tol = 0recovers the strict behavior. Residues are estimated from the eigenvector decomposition of the (Schur-form)A; if the decomposition fails, all suspect poles are conservatively treated as genuine.maxItersin continuous time, and a thrown error in discrete time). If an iteration fails to improve the lower bound beyond the γ-margin, the iteration terminates: this handles tangential crossings (converged) and crossing detections that are artifacts of spurious near-boundary poles.abs(real(z)) < approximag*max(1, abs(z))for both the pole check and Hamiltonian-eigenvalue crossing detection, so the test remains meaningful for fast dynamics (crossings at large |λ| were previously missed because eigenvalue rounding scales with |λ|). This also resolves the old in-code QUESTION comment about slow dynamics.evalfrat a frequency essentially coinciding with a pole returns non-finite values that previously could poison the lower bound).Ais triangular after the Schur reduction). This reduces iteration counts and is required so that systems whose gain is singular at the previous candidate frequencies do not start from a zero lower bound.R).@warnand return the best estimate; the discrete-time path previously threw a hard error, and the continuous-time message misidentified itself as_dt.Tests
test_linalg.jl: spurious vs. genuinely coupled boundary/unstable modes (both time domains, including small-absolute-gain systems where the coupling is genuine),resid_tol=0strictness, and termination for exactly-decoupled boundary modes.test_conversion.jl: the flaky assertions now useminreal(..., 1e-9)with thresholds above the truncation tolerance (see background).test_matrix_comps.jl: the gain peak ofS_testis flat, so the peak frequency is only determined to within ~w*√tol(the previous reference value was itself 8e-4 from the true argmax); the frequency tolerance at defaulttolis relaxed to 1e-2 and a tight assertion attol=1e-10is added.Verification
test_implicit_diff(differentiation throughhinfnormunaffected; the extension forwardskwargs...).🤖 Generated with Claude Code
https://claude.ai/code/session_014nRhRodrFbP41QBEDQ9hTw