codex/singular-value-ellipse.py

singular-value-ellipse.py
"""Map a unit circle to an ellipse; write an opaque PNG to CWD.

Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
"""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def arrow(ax, end, color):
    ax.annotate("", end, (0, 0), arrowprops={
        "arrowstyle": "->", "color": color, "lw": 2.6,
        "shrinkA": 0, "shrinkB": 0, "mutation_scale": 16}, zorder=5)


def main():
    matrix = np.array([[3., 4.], [-4 / 5, 3 / 5]])
    right = np.array([[3., -4.], [4., 3.]]) / 5
    sigma = np.diag([5., 1.])
    assert np.allclose(right.T @ right, np.eye(2))
    assert np.allclose(sigma @ right.T, matrix)
    assert np.allclose(matrix @ right, sigma)
    assert np.allclose(matrix.T @ matrix, np.array([[241, 288], [288, 409]]) / 25)
    assert np.allclose(np.linalg.eigvalsh(matrix.T @ matrix), [1, 25])
    assert np.allclose(np.linalg.eigvals(matrix), (9 + np.array([1j, -1j]) * np.sqrt(44)) / 5)
    mixed = right @ np.array([1., 1.]) / np.sqrt(2)
    assert np.isclose(np.linalg.norm(mixed), 1)
    assert np.isclose(np.linalg.norm(matrix @ mixed), np.sqrt(13))
    epsilon = 0.01
    weak_matrix = np.diag([5., epsilon]) @ right.T
    assert np.allclose(weak_matrix, [[3, 4], [-0.008, 0.006]])
    assert np.isclose(np.linalg.cond(weak_matrix), 500)
    assert np.allclose(np.linalg.solve(weak_matrix, [0, 0.001]), [-0.08, 0.06])
    angle = np.linspace(0, 2 * np.pi, 600)
    circle = right @ np.array([np.cos(angle), np.sin(angle)])
    ellipse = matrix @ circle
    assert np.allclose(ellipse[0] ** 2 / 25 + ellipse[1] ** 2, 1)

    plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
    fig, axes = plt.subplots(1, 2, figsize=(10.5, 4.4),
                             gridspec_kw={"width_ratios": [1, 3.7]})
    fig.subplots_adjust(left=0.055, right=0.98, bottom=0.22, top=0.73, wspace=0.22)
    blue, orange, ink = "#2365a0", "#c05621", "#253b46"
    for ax, limit in zip(axes, [1.55, 5.75]):
        ax.set_facecolor("white")
        ax.set_aspect("equal")
        ax.set_xlim(-limit, limit)
        ax.set_ylim(-1.65, 1.65)
        ax.axhline(0, color="#bbbbbb", lw=0.8)
        ax.axvline(0, color="#bbbbbb", lw=0.8)
        ax.grid(alpha=0.15)
        ax.spines[["top", "right"]].set_visible(False)
        ax.set_yticks([-1, 0, 1])
        ax.plot(0, 0, "o", color=ink, ms=4, zorder=6)
    source, target = axes
    source.set_title("Input: unit circle", fontsize=13, pad=10)
    target.set_title("Output: ellipse with semiaxes 5 and 1", fontsize=13, pad=10)
    source.set_xticks([-1, 0, 1])
    target.set_xticks([-5, -3, -1, 0, 1, 3, 5])
    source.plot(*circle, color=ink, lw=2)
    target.plot(*ellipse, color=ink, lw=2)
    for j, color in enumerate([blue, orange]):
        arrow(source, right[:, j], color)
        arrow(target, sigma[:, j], color)
        source.plot(*(-right[:, j]), "o", color=color, ms=5)
        target.plot(*(-sigma[:, j]), "o", color=color, ms=5)
    source.text(0.7, 1.03, r"$v_1$", color=blue)
    source.text(-1.22, 0.75, r"$v_2$", color=orange)
    target.text(2.5, 0.15, r"$Av_1=5u_1$", color=blue, ha="center")
    target.text(0.25, 1.12, r"$Av_2=u_2$", color=orange)
    target.text(-3.8, -1.30, r"$y_1^2/25+y_2^2=1$", color=ink)
    fig.suptitle("Singular directions: perpendicular in, perpendicular out", fontsize=17, y=0.97)
    fig.text(0.5, 0.84, "A = [[3, 4], [−4/5, 3/5]]     " + r"$\sigma_1=5,\ \sigma_2=1$",
             ha="center", fontsize=14)
    fig.text(0.5, 0.09, "Each panel has equal horizontal and vertical units. Colored endpoints correspond.",
             ha="center", fontsize=11)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=130,
                facecolor="white", transparent=False)
    plt.close(fig)
    print("Checked: A=UΣVᵀ; Av₁=5u₁; Av₂=u₂; eigenvalues of AᵀA = 25,1; ellipse equation; A has no real eigenvalues.")


if __name__ == "__main__":
    main()