codexlinear-versus-nonlinear-ode.py
linear-versus-nonlinear-ode.py
"""Show why the sum of two solutions to y'=y^2 is not a solution.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Run from the desired output directory; writes linear-versus-nonlinear-ode.png.
Exact curves are restricted to 0 <= t <= 0.4, before blowup at 1 and 1/2.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
t = np.linspace(0.0, 0.4, 401)
y1, y2 = 1 / (1 - t), 2 / (1 - 2 * t)
defect = -2 * y1 * y2
blue, orange, red = "#2166ac", "#c36b13", "#b2182b"
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, axes = plt.subplots(1, 2, figsize=(11.2, 4.9), dpi=120,
constrained_layout=True, facecolor="white")
ax = axes[0]
ax.plot(t, y1, color=blue, lw=2.6, label=r"$y_1=1/(1-t)$: solution")
ax.plot(t, y2, color=orange, lw=2.6, label=r"$y_2=2/(1-2t)$: solution")
ax.plot(t, y1 + y2, color=red, lw=2.6, ls="--",
label=r"$s=y_1+y_2$: fails the equation")
ax.scatter([0.25] * 3, [4 / 3, 4, 16 / 3],
color=[blue, orange, red], s=30, zorder=4)
ax.axvline(0.25, color="#777777", lw=1, ls=":", zorder=0)
ax.set(title=r"Nonlinear: $y'=y^2$", ylabel="y (dimensionless)",
ylim=(0, 12.2))
ax.legend(loc="upper left", fontsize=9.8, framealpha=1)
ax.text(0.018, 6.6, "At t = 0: s = 3\nactual slope = 5\nrequired slope = 9",
fontsize=10, va="center", bbox={"facecolor": "white",
"edgecolor": "none", "alpha": 0.95})
ax = axes[1]
ax.axhline(0, color="#258568", lw=2, ls=":",
label="zero defect required for a solution")
ax.plot(t, defect, color=red, lw=2.7,
label=r"$s'-s^2=-2y_1y_2$")
ax.fill_between(t, defect, 0, color=red, alpha=0.08)
ax.scatter([0, 0.25], [-4, -32 / 3], color=red, s=35, zorder=4)
ax.annotate(r"$t=1/4$: defect $=-32/3$", xy=(0.25, -32 / 3),
xytext=(0.055, -22.5), fontsize=10,
arrowprops={"arrowstyle": "->", "color": "#555555"})
ax.set(title="A sum introduces a cross term",
ylabel=r"equation defect $s'-s^2$", ylim=(-35.5, 5))
ax.legend(loc="lower left", fontsize=9.8, framealpha=1)
ax.text(0.02, 2.0, "homogeneous linear sums have zero defect",
fontsize=9, color="#196649")
for ax in axes:
ax.set(xlabel="t (dimensionless)", xlim=(-0.006, 0.4))
ax.set_xticks([0, 0.1, 0.2, 0.3, 0.4])
ax.set_facecolor("white")
ax.grid(alpha=0.16)
fig.savefig(Path.cwd() / "linear-versus-nonlinear-ode.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()