Phase Portraits and Fixed Point ClassificationΒΆ

This notebook explores the relationship between eigenvalues of a linear system's matrix and the qualitative behaviour of its phase portrait. We classify fixed points as stable nodes, saddle points, spirals, and centres, then apply the same tools to a nonlinear Lotka-Volterra system via linearization.

InΒ [121]:
load("numerics")$
load("ax-plots")$

Linear SystemsΒΆ

For a linear system $\dot{\mathbf{x}} = A\mathbf{x}$, the eigenvalues of $A$ completely determine the phase portrait near the origin. We work through each case.

Stable Node [S+N]ΒΆ

When both eigenvalues are real and negative, all trajectories converge to the origin along the eigenvector directions. The system is asymptotically stable.

InΒ [122]:
/* Stable node: both eigenvalues negative */
A_node : matrix([-2, 0], [0, -1])$
evals_node : eigenvalues(A_node);

print("Eigenvalues:", evals_node[1])$
print("Both negative real => stable node")$
[[-2,-1],[1,1]]
Eigenvalues: [-2,-1]
Both negative real => stable node
InΒ [123]:
/* Phase portrait: trajectories converge to origin */
ax_draw2d(
  ax_vector_field(-2*x, -y, x, -3, 3, y, -3, 3),
  ax_streamline(-2*x, -y, x, -3, 3, y, -3, 3),
  title="Stable Node",
  xlabel="x", ylabel="y",
  aspect_ratio=true
)$
No description has been provided for this image

Saddle Point [S+N]ΒΆ

When eigenvalues are real with opposite signs, trajectories approach the origin along the stable eigenvector and diverge along the unstable eigenvector. The fixed point is unstable.

InΒ [124]:
/* Saddle point: eigenvalues of opposite sign */
A_saddle : matrix([1, 0], [0, -2])$
evals_saddle : eigenvalues(A_saddle);

print("Eigenvalues:", evals_saddle[1])$
print("One positive, one negative => saddle point")$
[[-2,1],[1,1]]
Eigenvalues: [-2,1]
One positive, one negative => saddle point
InΒ [125]:
/* Saddle point phase portrait: hyperbolic trajectories */
ax_draw2d(
  ax_vector_field(x, -2*y, x, -3, 3, y, -3, 3),
  ax_streamline(x, -2*y, x, -3, 3, y, -3, 3),
  title="Saddle Point",
  xlabel="x", ylabel="y",
  aspect_ratio=true
)$
No description has been provided for this image

Stable Spiral [S+N]ΒΆ

When eigenvalues are complex conjugates with negative real part, trajectories spiral inward toward the origin. The imaginary part determines the rotation frequency.

InΒ [126]:
/* Stable spiral: complex eigenvalues with negative real part */
A_spiral : matrix([-1, 2], [-2, -1])$
evals_spiral : eigenvalues(A_spiral);

print("Eigenvalues:", evals_spiral[1])$
print("Complex with Re < 0 => stable spiral")$
[[-(2*%i)-1,2*%i-1],[1,1]]
Eigenvalues: [-(2*%i)-1,2*%i-1]
Complex with Re < 0 => stable spiral
InΒ [127]:
/* Stable spiral: trajectories wind inward */
ax_draw2d(
  ax_vector_field(-x + 2*y, -2*x - y, x, -3, 3, y, -3, 3),
  ax_streamline(-x + 2*y, -2*x - y, x, -3, 3, y, -3, 3),
  title="Stable Spiral",
  xlabel="x", ylabel="y",
  aspect_ratio=true
)$
No description has been provided for this image

Unstable SpiralΒΆ

When the real part of complex eigenvalues is positive, trajectories spiral outward from the origin.

InΒ [128]:
/* Unstable spiral: complex eigenvalues with positive real part */
A_uspiral : matrix([1, 2], [-2, 1])$
evals_uspiral : eigenvalues(A_uspiral);

print("Eigenvalues:", evals_uspiral[1])$
print("Complex with Re > 0 => unstable spiral")$
[[1-2*%i,2*%i+1],[1,1]]
Eigenvalues: [1-2*%i,2*%i+1]
Complex with Re > 0 => unstable spiral
InΒ [129]:
/* Unstable spiral: trajectories wind outward */
ax_draw2d(
  ax_vector_field(x + 2*y, -2*x + y, x, -3, 3, y, -3, 3),
  ax_streamline(x + 2*y, -2*x + y, x, -3, 3, y, -3, 3),
  title="Unstable Spiral",
  xlabel="x", ylabel="y",
  aspect_ratio=true
)$
No description has been provided for this image

CentreΒΆ

The simple harmonic oscillator $\dot{x} = y$, $\dot{y} = -x$ has pure imaginary eigenvalues. Trajectories form closed orbits -- neither converging nor diverging.

InΒ [130]:
/* Centre: pure imaginary eigenvalues */
A_centre : matrix([0, 1], [-1, 0])$
evals_centre : eigenvalues(A_centre);

print("Eigenvalues:", evals_centre[1])$
print("Pure imaginary => centre (closed orbits)")$
[[-%i,%i],[1,1]]
Eigenvalues: [-%i,%i]
Pure imaginary => centre (closed orbits)
InΒ [131]:
/* Centre: closed circular orbits */
ax_draw2d(
  ax_vector_field(y, -x, x, -3, 3, y, -3, 3),
  ax_streamline(y, -x, x, -3, 3, y, -3, 3),
  title="Centre (Simple Harmonic Oscillator)",
  xlabel="x", ylabel="y",
  aspect_ratio=true
)$
No description has been provided for this image

Nonlinear Example: Lotka-Volterra [S+N]ΒΆ

The classical predator-prey model:

$$\dot{x} = x(1-y) \qquad \dot{y} = y(x-1)$$

where $x$ is the prey population and $y$ is the predator population. We use Maxima's symbolic tools to compute the Jacobian, find fixed points, and classify their stability.

InΒ [132]:
/* Lotka-Volterra system */
f1 : x*(1 - y)$
f2 : y*(x - 1)$

/* Compute the Jacobian symbolically */
J_lv : matrix(
  [diff(f1, x), diff(f1, y)],
  [diff(f2, x), diff(f2, y)]
);

print("Jacobian:")$
J_lv;
matrix([1-y,-x],[y,x-1])
Jacobian:
Out[132]:
InΒ [133]:
/* Find fixed points */
fps_lv : solve([f1 = 0, f2 = 0], [x, y]);

print("Fixed points:")$
for fp in fps_lv do print(fp)$
[[x = 0,y = 0],[x = 1,y = 1]]
Fixed points:
[x = 0,y = 0]
[x = 1,y = 1]
InΒ [134]:
/* Classify each fixed point via eigenvalues of the Jacobian */
for fp in fps_lv do block(
  [J_fp, evals],
  J_fp : subst(fp, J_lv),
  evals : eigenvalues(J_fp),
  print("--- Fixed point:", fp, "---"),
  print("  J =", J_fp),
  print("  Eigenvalues =", evals[1]),
  if every(lambda([e], is(float(realpart(e)) < 0)), evals[1]) then
    print("  Classification: STABLE")
  elseif every(lambda([e], is(float(realpart(e)) > 0)), evals[1]) then
    print("  Classification: UNSTABLE")
  elseif every(lambda([e], is(float(realpart(e)) = 0)), evals[1]) then
    print("  Classification: CENTRE")
  else
    print("  Classification: SADDLE")
)$
--- Fixed point: [x = 0,y = 0] ---
  J = matrix([1,0],[0,-1])
  Eigenvalues = [-1,1]
  Classification: SADDLE
--- Fixed point: [x = 1,y = 1] ---
  J = matrix([0,-1],[1,0])
  Eigenvalues = [-%i,%i]
  Classification: SADDLE
InΒ [135]:
/* Phase portrait of the Lotka-Volterra system */
ax_draw2d(
  color="#cccccc",
  ax_vector_field(f1, f2, x, 0, 4, y, 0, 4),
  color="#e41a1c",
  initial_points=[[0.5, 0.5], [1.0, 2.0], [2.0, 0.5],
                  [0.5, 1.5], [3.0, 1.0], [1.5, 3.0]],
  ax_streamline(f1, f2, x, 0, 4, y, 0, 4),
  title="Lotka-Volterra Phase Portrait",
  xlabel="x (prey)", ylabel="y (predator)",
  aspect_ratio=true
)$
No description has been provided for this image

Summary: Eigenvalue ClassificationΒΆ

Eigenvalues Portrait Type Stability
Both real, both negative Stable node Asymptotically stable
Both real, both positive Unstable node Unstable
Both real, opposite signs Saddle point Unstable
Complex, Re < 0 Stable spiral Asymptotically stable
Complex, Re > 0 Unstable spiral Unstable
Pure imaginary Centre Marginally stable (Lyapunov)

For nonlinear systems, the same classification applies locally at each fixed point via the Jacobian. The Lotka-Volterra system demonstrates this: a saddle at the origin and a centre at the coexistence equilibrium, producing the characteristic closed predator-prey cycles.