codexpast-exam-of-the-mathematics-course-of-the-university-of-cambridge2020iipaper-1-bifurcation-diagrams.py
past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2020/ii/paper-1-bifurcation-diagrams.py
#!/usr/bin/env python3
"""Plot the bifurcation diagrams in 2020 Part II Paper 1, Question 32E."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
OUTPUT = Path(Path(__file__).stem + ".png")
def equilibria(mu: float, a: float, epsilon: float) -> list[tuple[float, bool]]:
"""Return real equilibria and whether each is linearly stable."""
roots = np.roots([1.0, 0.0, a - 3.0 * mu, 0.0, 2.0 * mu * (mu - a), -epsilon])
answer = []
for root in roots:
if abs(root.imag) < 1e-7:
x = float(root.real)
derivative = -(
5.0 * x**4
+ 3.0 * (a - 3.0 * mu) * x**2
+ 2.0 * mu * (mu - a)
)
answer.append((x, derivative < 0.0))
return answer
mu_values = np.linspace(-1.45, 1.75, 900)
fig, axes = plt.subplots(2, 3, figsize=(11.2, 7.0), sharex=True, sharey=True)
for row, epsilon in enumerate((0.0, 0.035)):
for column, a in enumerate((-1.0, 0.0, 1.0)):
ax = axes[row, column]
roots_by_mu = [sorted(equilibria(mu, a, epsilon)) for mu in mu_values]
for root_count in (1, 3, 5):
for root_index in range(root_count):
stable_x = np.full(mu_values.shape, np.nan)
unstable_x = np.full(mu_values.shape, np.nan)
for index, roots in enumerate(roots_by_mu):
if len(roots) == root_count:
x, stable = roots[root_index]
(stable_x if stable else unstable_x)[index] = x
ax.plot(mu_values, stable_x, color="#176b87", linewidth=1.5)
ax.plot(
mu_values,
unstable_x,
color="#c75146",
linewidth=1.35,
linestyle="--",
)
ax.axhline(0.0, color="0.78", linewidth=0.6)
ax.axvline(0.0, color="0.78", linewidth=0.6)
ax.set_title(f"$a={a:g}$")
if column == 0:
ax.set_ylabel("$x$\n" + ("$\\epsilon=0$" if row == 0 else "$\\epsilon=0.035$"))
if row == 1:
ax.set_xlabel("$\\mu$")
ax.set_xlim(mu_values[0], mu_values[-1])
ax.set_ylim(-2.15, 2.15)
handles = [
plt.Line2D([], [], color="#176b87", linewidth=1.5, label="stable"),
plt.Line2D([], [], color="#c75146", linewidth=1.35, linestyle="--", label="unstable"),
]
fig.legend(handles=handles, loc="upper center", ncol=2, frameon=False)
fig.tight_layout(rect=(0, 0, 1, 0.95))
fig.savefig(OUTPUT, dpi=100, facecolor="white")