Skip to content
Menu

8. Stability

Stability is the most fundamental property of a dynamic system. This chapter introduces the tools — norms, eigenvalues, and Lyapunov’s direct method — needed to analyze it rigorously.

PowerPoint handouts: Stability via Eigenvalues (dark) · Stability via Eigenvalues (light) · Lyapunov Stability (dark) · Lyapunov Stability (light)

link to download code

For A = [[-1,1],[-1,0]] and Q = I, this solves the Lyapunov equation A^T P + P A = -Q for P, then checks its eigenvalues. Both matching the hand-solved P = [[1, -0.5], [-0.5, 1.5]] from the text: all eigenvalues of P are positive, so P is positive definite and the origin is asymptotically stable.

import control as ct
import numpy as np
A = np.array([[-1, 1], [-1, 0]])
Q = np.identity(2)
# ct.lyap solves A P + P A^T + Q = 0 by default, so we pass A.T to solve the
# Lyapunov equation A^T P + P A + Q = 0 used in the text
P = ct.lyap(A.transpose(), Q)
print("P =")
print(P)
w = np.linalg.eigvals(P)
print(f"\neigenvalues of P: {w}")
print("All eigenvalues positive -> P is positive definite -> asymptotically stable")

Continuous-time Lyapunov equation (mcimp/codes/lyapunov/ctlyapunov_sol.py)

Click "Run" to execute.

The Lyapunov equation as a linear operator

Section titled “The Lyapunov equation as a linear operator”
link to download code

A^T P + P A = -Q is linear in the unknown P, so it can be written as L_A(P) = -Q for a linear operator L_A built from A via the Kronecker product: L_A = I ⊗ A^T + A^T ⊗ I. This computes L_A’s eigenvalues directly and confirms the claim from the text: each eigenvalue of L_A is a pairwise sum lambda_i + lambda_j of A’s own eigenvalues — which is also exactly the condition (lambda_i + lambda_j != 0) that guarantees the Lyapunov equation has a unique solution.

import numpy as np
A = [[-1, 1], [-1, 0]]
I2 = np.eye(2)
AT = np.transpose(A)
# The Lyapunov equation A^T P + P A = -Q is linear in P; L_A is the matrix
# representation of that linear operator via the Kronecker product,
# L_A = I (x) A^T + A^T (x) I
L_A = np.kron(I2, AT) + np.kron(AT, I2)
eigLA = np.linalg.eigvals(L_A)
eigA = np.linalg.eigvals(A)
print("Eigenvalues of L_A:", eigLA)
print("Eigenvalues of A: ", eigA)
print("\nEach eigenvalue of L_A is a pairwise sum lambda_i + lambda_j of A's eigenvalues.")

The Lyapunov equation as a linear operator (mcimp/codes/lyapunov/lyapunov_operator.py)

Click "Run" to execute.
link to download code

The discrete-time analogue solves A^T P A - P = -Q instead. For the third-order A in the text’s example, this confirms all three eigenvalues of the resulting P are positive — so P is positive definite and the discrete-time system’s origin is asymptotically stable, the discrete-time counterpart of the continuous-time result above.

import control as ct
import numpy as np
from numpy.linalg import eig
A = np.array([[0, 1, 0], [0, 0, 1], [0.275, -0.225, -0.1]])
Q = np.identity(3)
# ct.dlyap solves A P A^T - P + Q = 0 by default, so we pass A.T to match
# the discrete-time Lyapunov equation A^T P A - P = -Q used in the text
P = ct.dlyap(A.transpose(), Q)
w, v = eig(P)
print("eigenvalues of P:", w)
print("All positive -> P is positive definite -> the origin is asymptotically stable")

Discrete-time Lyapunov equation (mcimp/codes/lyapunov/dtlyapunov_sol.py)

Click "Run" to execute.

← All chapters