codex/simulated-annealing-five-state-barrier.py

simulated-annealing-five-state-barrier.py
"""Plot an original finite optimization example; run from _media.

Tested with Python 3.14, matplotlib 3.10.7 and NumPy 2.3.5.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    states = np.arange(5)
    energy = np.array([2, 1, 4, 2, 0])
    assert energy[1] < min(energy[0], energy[2])
    assert np.argmin(energy) == 4
    plt.rcParams.update({"font.size": 12, "axes.spines.top": False,
                         "axes.spines.right": False})
    fig, ax = plt.subplots(figsize=(8, 4.5), dpi=120, facecolor="white")
    fig.subplots_adjust(left=0.09, right=0.97, bottom=0.24, top=0.86)
    ax.plot(states, energy, color="#9ca3af", linewidth=2, zorder=1)
    ax.scatter(states, energy, s=95,
               c=["#687584", "#d97706", "#b83245", "#687584", "#087e8b"],
               edgecolor="white", linewidth=1, zorder=3)
    for state, value in zip(states, energy):
        ax.text(state, value - 0.28, f"E = {value}", ha="center", va="top")
    for start, end in [(1, 2), (2, 3), (3, 4)]:
        ax.annotate("", xy=(end - 0.08, energy[end] + 0.18),
                    xytext=(start + 0.08, energy[start] + 0.18),
                    arrowprops={"arrowstyle": "->", "color": "#087e8b", "lw": 2})
    ax.annotate("Start: local minimum\ngreedy search stays here",
                xy=(1, 1), xytext=(-0.1, 3.55), ha="left", fontsize=11,
                arrowprops={"arrowstyle": "->", "color": "#d97706"})
    ax.text(1.43, 2.8, "Uphill\nΔE = +3", color="#b83245", ha="right", fontsize=11)
    ax.text(3.1, 3.35, "One possible escape:\n1 → 2 → 3 → 4",
            ha="center", color="#087e8b")
    ax.annotate("Global minimum", xy=(4, 0), xytext=(3.22, 1.05),
                ha="center", color="#087e8b", fontsize=11,
                arrowprops={"arrowstyle": "->", "color": "#087e8b"})
    ax.set(xticks=states, xlabel="State (only the five dots are feasible)",
           ylabel="Objective E to minimize", ylim=(-0.65, 4.65), xlim=(-0.42, 4.42))
    ax.grid(axis="y", alpha=0.2)
    fig.suptitle("Accepting an uphill step can unlock a lower minimum", fontsize=15, y=0.97)
    fig.text(0.5, 0.075,
             "Symmetric proposals: left/right each ½; endpoints hold with probability ½.\n"
             "At T = 1/ln 2: accept 1 → 2 with probability ⅛ given that proposal.",
             ha="center", va="center", fontsize=11)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()