"""Compare quadratic and tilted quartic minima; save basename.png to cwd. Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7. Run from _media: MPLCONFIGDIR=/tmp/convex-mpl python3 ../convex-vs-nonconvex.py """ from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt import numpy as np def quartic(x): return (x * x - 1) ** 2 + 0.2 * x def main(): roots = np.roots([4.0, 0.0, -4.0, 0.2]) assert np.all(np.abs(roots.imag) < 1e-12) roots = np.sort(roots.real) values = quartic(roots) curvature = 12 * roots**2 - 4 assert np.max(np.abs(4 * roots**3 - 4 * roots + 0.2)) < 1e-12 assert np.allclose(roots, [-1.024120300, 0.050125947, 0.973994353], atol=1e-9, rtol=0) assert np.allclose(values, [-0.202440434, 1.005006281, 0.197434153], atol=1e-9, rtol=0) assert curvature[0] > 0 and curvature[1] < 0 and curvature[2] > 0 assert values[0] < values[2] assert np.isclose(values[2] - values[0], 0.399874587203099) plt.rcParams.update({"font.size": 11}) fig, axes = plt.subplots(1, 2, figsize=(9.7, 4.5), layout="constrained", facecolor="white") x = np.linspace(-1.65, 1.65, 801) axes[0].plot(x, x**2, color="#447ca5", lw=2.5) axes[0].scatter([0], [0], color="#26765c", s=55, zorder=4) axes[0].annotate("Unique global minimum\n(0, 0)", (0, 0), xytext=(-1.45, 1.25), fontsize=10, arrowprops={"arrowstyle": "->"}) axes[0].set(title=r"Strictly convex: $f(x)=x^2$", xlabel="x", ylabel="Objective value", xlim=(-1.65, 1.65), ylim=(-0.4, 3.5)) ax = axes[1] ax.plot(x, quartic(x), color="#ad6a30", lw=2.5) ax.scatter(roots, values, color=["#26765c", "#63717c", "#b94f42"], s=55, zorder=4) ax.annotate("Global minimum\nx ≈ −1.024120\nh ≈ −0.202440", (roots[0], values[0]), xytext=(-1.56, 1.6), fontsize=9.5, arrowprops={"arrowstyle": "->", "color": "#26765c"}) ax.annotate("Higher local minimum\nx ≈ 0.973994\nh ≈ 0.197434", (roots[2], values[2]), xytext=(0.18, 2.25), fontsize=9.5, arrowprops={"arrowstyle": "->", "color": "#b94f42"}) ax.annotate("Local maximum", (roots[1], values[1]), xytext=(-0.43, 0.57), fontsize=9.5, arrowprops={"arrowstyle": "->", "color": "#63717c"}) ax.set(title=r"Nonconvex: $h(x)=(x^2-1)^2+0.2x$", xlabel="x", xlim=(-1.65, 1.65), ylim=(-0.4, 3.5)) for ax in axes: ax.axhline(0, color="#a1a8ac", lw=0.7) ax.grid(alpha=0.18) fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=120, facecolor="white", transparent=False) plt.close(fig) for x0, y0, c0 in zip(roots, values, curvature): print(f"x={x0:.12f}, h={y0:.12f}, h''={c0:.12f}") print(f"Local-to-global objective gap: {values[2] - values[0]:.12f}") if __name__ == "__main__": main()