Imagine you build a simple control system, or a digital filter, or some tiny feedback loop in electronics or software. A very common way to model it is with a discrete-time linear system:
next state = (some matrix) × (current state)
written as: x₍k+1₎ = A · x₍k₎
Here:
k (e.g. position and velocity),The big question engineers ask is:
If I let this system run on its own, will it blow up, oscillate forever, or settle down?
That’s the notion of stability.
Our case is a tiny, self-contained Python “laboratory” for that question. It does three things:
It treats each test as a “world” and asks, in a logical way:
The aim is to show, in a very concrete way, how a real-world engineering notion (stability of a system) can be wired into the “higher-order look, first-order core” style: the serious math lives in simple numeric computations, and the talk about “theorems” and “worlds” is handled by a small logical wrapper.
For the system
x₍k+1₎ = A · x₍k₎
the fundamental result is:
The system is stable if all eigenvalues of A have magnitude at most 1. (Think: their distance from the origin in the complex plane is ≤ 1.)
The system is damped (strictly stable) if all eigenvalues have magnitude at most 0.9. (We choose 0.9 just as a clear “safety margin inside the unit circle”.)
In our code:
We compute the eigenvalues of a 2×2 matrix analytically from the quadratic formula:
[[a, b], [c, d]], the characteristic polynomial is
λ² − (a + d) λ + (ad − bc)We define:
stable_true(A) = “spectral radius ≤ 1”damped_true(A) = “spectral radius ≤ 0.9”This is world 0.
You can think of world 0 as the world where we are willing and able to do proper math: we compute the eigenvalues and answer correctly. It’s what you’d do in a serious control or signal processing toolkit.
So in world 0:
Later, when we talk about “theorems” like CorrectStable and CorrectDamped, world 0 will be the only one that passes both.
Because in practice, we often use shortcuts.
Maybe you are working with a large system and exact eigenvalues are hard or expensive to get. Or maybe you’re reasoning on a whiteboard and want a quick bound. Or you have legacy code with some old heuristic.
Those shortcuts:
So the script introduces three more worlds, each with its own approximate stability test. All of them answer the same two questions (stable? damped?), but by different recipes.
This creates a perfect playground to:
Idea: Use a classical analytic bound from matrix theory: Gershgorin circles.
For a 2×2 matrix
A = [[a₁₁, a₁₂], [a₂₁, a₂₂]]
Gershgorin’s theorem says that all eigenvalues lie in the union of discs:
| Disc 1: center a₁₁, radius | a₁₂ |
| Disc 2: center a₂₂, radius | a₂₁ |
A very cheap bound on “how far eigenvalues can wander” is then:
max( a₁₁ + a₁₂ , a₂₂ + a₂₁ )
If that max is small, we can be sure the eigenvalues are also small. If it’s large, we might still be safe, but we can’t be sure.
In world 1 we define:
This test is:
Sound but incomplete for stability:
For damping, we similarly get a very cautious test.
So world 1 is like a strict safety inspector:
That’s why, when we check whether world 1’s answers always match the true answers, it fails: there are matrices where world 1 says “unstable” even though the true eigenvalue test says “stable”.
In world 2, we do something even simpler and more naive:
Look only at the diagonal entries a₁₁ and a₂₂.
We define:
| “stable in world 2” if max( | a₁₁ | , | a₂₂ | ) ≤ 1.0 |
| “damped in world 2” if max( | a₁₁ | , | a₂₂ | ) ≤ 0.9 |
We completely ignore the off-diagonal entries a₁₂ and a₂₁, which encode how state variables influence each other.
This heuristic is:
For example, you can have a matrix like:
[[0, 2], [-2, 0]]
whose diagonal is zero, so world 2 calls it “very stable”, but its eigenvalues lie on the unit circle of radius 2 (actually ±2i), meaning it’s very unstable in the true sense.
So world 2 is like the optimistic engineer who only looks at “self-effects” (diagonal terms) and ignores couplings. It often declares systems safe that are, in reality, dangerous.
As a result, world 2 fails our correctness tests dramatically.
In world 3, we try a different kind of shortcut:
If the entire matrix is tiny, the system is probably stable.
We measure “tiny” with the Frobenius norm:
A _F = sqrt(sum of aᵢⱼ ² over all entries)
Then we define:
| “stable in world 3” if | A | _F ≤ 0.5 |
| “damped in world 3” if | A | _F ≤ 0.4 |
This is deliberately absurdly strict. For example, the identity matrix I has norm sqrt(2) ≈ 1.41, but its eigenvalues are exactly 1. So in the true sense:
I is stable (on the boundary of the unit circle),So world 3 represents a crude rule of thumb that only accepts extremely tiny matrices and rejects almost everything else, including many perfectly acceptable systems.
In real life, such a heuristic might correspond to someone saying “if all gains are below 0.1, I guess we’re safe”, without thinking about the actual eigenvalues.
We now shift from the engineering view to a more logical, “meta” view.
Given any world w (0, 1, 2, or 3), we can ask:
We treat these as theorem names (intensions):
thm:CorrectStable
means: “this world’s stable predicate is always correct.”thm:CorrectDamped
means: “this world’s damped predicate is always correct.”Of course, we can’t test “all matrices”, so in the code we approximate that by:
For each, compare:
stable_truedamped_trueThis is the first-order core: concrete matrices, concrete computations.
To connect with the “higher-order look” story, we introduce:
We define a function (or relation):
Holds1(P, w)= “the theorem named by P holds in world w”.
Implementation-wise:
EXT1 that maps each theorem-name to a set of worlds where it holds.Holds1(P, w) is true exactly if w is in that set.So for example:
EXT1["thm:CorrectStable"] and EXT1["thm:CorrectDamped"].Conceptually:
Holds1(thm:CorrectStable, 0) is our way of saying
“In world 0, the stable test is correct for all samples (and we treat that as a theorem).”Holds1(thm:CorrectStable, 1) is false: world 1 sometimes gets it wrong.We also introduce a binary relation-name:
rel:stronger
and a binary predicate:
Holds2(rel:stronger, X, Y)= “the theorem X is stronger than Y”.
In this case, we want to express the idea that:
If a world is always correct about damping, then it is also always correct about stability.
Why does that make sense?
We encode this by:
(thm:CorrectDamped, thm:CorrectStable) into a set EXT2["rel:stronger"].Holds2(rel:stronger, X, Y) is true exactly when (X, Y) is in that set.So:
Holds2(rel:stronger, thm:CorrectDamped, thm:CorrectStable) is true.rel:stronger are false.Now the “higher-order” statement:
“CorrectDamped is stronger than CorrectStable in our tiny universe.”
becomes a simple extensional fact about membership in EXT2.
When you run the script, it does roughly this:
Sample matrices and compute for each world:
Build EXT1 from those results:
EXT1["thm:CorrectStable"] = set of worlds where no stability mismatch occurred.EXT1["thm:CorrectDamped"] = set of worlds where no damping mismatch occurred.Define EXT2 with the single pair (thm:CorrectDamped, thm:CorrectStable).
Print the “Answer” section:
Holds1(thm:CorrectStable, w) and Holds1(thm:CorrectDamped, w).Print the “Reason why” section:
Run the “Check (harness)”:
A bunch of tests making sure:
Holds1 and EXT1 are consistent.Holds2 is respected:
whenever Holds1(thm:CorrectDamped, w) is true,
Holds1(thm:CorrectStable, w) is also true.This little case is doing two things at once:
Real-world side It really does something meaningful: given a 2×2 complex matrix A, it can compute its eigenvalues and tell you whether the associated system is stable or damped in the standard control-theory sense. And it demonstrates how various common heuristics (norm bounds, Gershgorin discs, ignoring off-diagonal terms) can succeed or fail.
Logical / semantic side
It shows how you can package properties of whole tests as first-class
objects (“theorems”) and talk about them with predicates like Holds1 and
Holds2, without ever leaving a simple numeric core. The mathematics of
eigenvalues, discs, norms, etc. is all first-order and concrete; the
“higher-order look” comes only from how we choose to represent which
theorems hold in which worlds.
You can read it as a tiny story:
That combination—real engineering semantics plus a clean logical “meta-view”— is exactly the sweet spot this case is trying to illustrate.