codex/gauss-seidel-two-variable-trajectory-contraction.py

gauss-seidel-two-variable-trajectory-contraction.py
"""Draw original exact-system trajectory and contraction; PNG goes to the CWD.

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():
    plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 11})
    a = np.array([[4.0, 1.0], [1.0, 3.0]])
    b = np.array([1.0, 2.0])
    solution = np.linalg.solve(a, b)
    gs, jacobi = [np.zeros(2)], [np.zeros(2)]
    row_path = [gs[0].copy()]
    for _ in range(9):
        x_new = (1 - gs[-1][1]) / 4
        row_path.append(np.array([x_new, gs[-1][1]]))
        y_new = (2 - x_new) / 3
        gs.append(np.array([x_new, y_new]))
        row_path.append(gs[-1].copy())
        jacobi.append(np.array([(1 - jacobi[-1][1]) / 4, (2 - jacobi[-1][0]) / 3]))
    gs, jacobi, row_path = np.array(gs), np.array(jacobi), np.array(row_path)
    k = np.arange(len(gs))
    gs_error = np.linalg.norm(gs - solution, axis=1)
    jacobi_error = np.linalg.norm(jacobi - solution, axis=1)
    # An independent formula checks the plotted direct row updates.
    np.testing.assert_allclose(gs_error[1:], 7 * np.sqrt(10) / (11 * 12.0 ** k[1:]),
                               rtol=3e-6, atol=1e-16)
    blue, orange, gray = "#146b88", "#b05012", "#6b7280"
    fig, (ax, err) = plt.subplots(1, 2, figsize=(11, 4.8), dpi=110, facecolor="white")
    fig.suptitle("Fresh updates select a faster error mode", fontsize=18, y=0.98)
    ax.set_title("Rows move one coordinate at a time", fontsize=13, pad=11)
    x = np.linspace(-0.04, 0.30, 250)
    ax.plot(x, 1 - 4*x, color=gray, alpha=0.55, linewidth=1.2, label=r"$4x+y=1$")
    ax.plot(x, (2 - x)/3, color=gray, linestyle="--", alpha=0.8, linewidth=1.2,
            label=r"$x+3y=2$")
    ax.plot(row_path[:7, 0], row_path[:7, 1], color=blue, linewidth=2, alpha=0.85)
    for start, end in zip(row_path[:4], row_path[1:5]):
        ax.annotate("", xy=end, xytext=start,
                    arrowprops={"arrowstyle": "->", "color": blue, "lw": 1.7})
    ax.scatter(gs[:4, 0], gs[:4, 1], color=blue, s=30, zorder=4, label="Sweep endpoints")
    ax.scatter(*solution, marker="*", color=orange, s=120, zorder=5, label="Exact solution")
    ax.annotate(r"$k=0$", gs[0], xytext=(5, 8), textcoords="offset points")
    ax.annotate(r"$k=1:\ (1/4,7/12)$", gs[1], xytext=(-5, -25),
                textcoords="offset points", ha="right", color=blue)
    ax.annotate(r"$k=2$", gs[2], xytext=(25, 5), textcoords="offset points", color=blue,
                arrowprops={"arrowstyle": "-", "color": blue})
    ax.annotate(r"$(1/11,7/11)$", solution, xytext=(-48, 22), textcoords="offset points",
                color=orange, arrowprops={"arrowstyle": "-", "color": orange})
    ax.set(xlim=(-0.025, 0.30), ylim=(-0.04, 0.76), xlabel=r"$x$", ylabel=r"$y$")
    ax.legend(loc="lower left", fontsize=9, framealpha=1)
    ax.grid(alpha=0.15)
    err.set_title("Computed error norms and exact decay", fontsize=13, pad=11)
    err.semilogy(k, gs_error, "o", color=blue, markersize=5, label="Gauss–Seidel iterates")
    err.semilogy(k[1:], 7*np.sqrt(10)/(11*12.0**k[1:]), "-", color=blue,
                 linewidth=1.6, label=r"$7\sqrt{10}/(11\cdot12^k),\ k\geq1$")
    err.semilogy(k, jacobi_error, "s--", color=orange, markersize=4, linewidth=1.3,
                 label="Jacobi iterates (same start)")
    err.text(0.04, 0.10, "Gauss–Seidel: factor 1/12 after sweep 1\n"
             "Jacobi: factor 1/12 every two sweeps", transform=err.transAxes,
             fontsize=10, bbox={"facecolor": "white", "edgecolor": "#d1d5db", "pad": 7})
    err.set(xlabel=r"Completed sweeps $k$", ylabel=r"Forward error $\|z^{(k)}-z_*\|_2$",
            xticks=np.arange(0, 10), ylim=(1e-10, 2))
    err.legend(loc="upper right", fontsize=9)
    err.grid(alpha=0.2, which="major")
    fig.subplots_adjust(left=0.075, right=0.975, bottom=0.17, top=0.84, wspace=0.30)
    fig.text(0.5, 0.04, "Original constructed system: A = [[4, 1], [1, 3]], b = [1, 2] • exact-arithmetic formulas",
             ha="center", fontsize=10, color="#374151")
    fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()