codex/linear-combination-coordinates.py

linear-combination-coordinates.py
"""Plot a computed linear combination and its coordinate grid; PNG goes to CWD.

Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Run: make _media/linear-combination-coordinates.png
"""

from pathlib import Path

import matplotlib

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


def arrow(ax, start, end, color, width=2.6, style="-", zorder=4):
    ax.annotate("", xy=end, xytext=start,
                arrowprops=dict(arrowstyle="->", color=color, lw=width,
                                linestyle=style, shrinkA=0, shrinkB=0,
                                mutation_scale=17), zorder=zorder)


def main():
    v1, v2 = np.array([1., 1.]), np.array([1., -1.])
    basis = np.column_stack((v1, v2))
    target = np.array([3., 1.])
    coefficients = np.array([2., 1.])
    assert np.allclose(basis @ coefficients, target)
    assert np.allclose(np.linalg.solve(basis, target), coefficients)
    assert np.isclose(np.linalg.det(basis), -2.)
    # Check the general formula on a two-dimensional grid, including negatives.
    x, y = np.meshgrid(np.linspace(-4, 4, 17), np.linspace(-3, 3, 13))
    points = np.vstack((x.ravel(), y.ravel()))
    coords = np.vstack(((points[0] + points[1]) / 2,
                        (points[0] - points[1]) / 2))
    assert np.allclose(basis @ coords, points)
    assert np.allclose(basis @ [1, -1], [0, 2])

    plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
    fig, ax = plt.subplots(figsize=(7.4, 6.3), facecolor="white")
    fig.subplots_adjust(left=.10, right=.96, bottom=.22, top=.83)
    blue, orange, green = "#2466a3", "#c15a16", "#217346"
    ax.set_facecolor("white")
    ax.set_aspect("equal")
    ax.set_xlim(-1.4, 4.2)
    ax.set_ylim(-1.7, 3.0)
    ax.set_xticks(range(-1, 5))
    ax.set_yticks(range(-1, 4))
    ax.set_xlabel("x")
    ax.set_ylabel("y", rotation=0, labelpad=10)
    ax.axhline(0, color="#888888", lw=.8)
    ax.axvline(0, color="#888888", lw=.8)
    parameter = np.array([-8., 8.])
    for k in range(-5, 6):
        first_fixed = k * v1[:, None] + v2[:, None] * parameter
        second_fixed = v1[:, None] * parameter + k * v2[:, None]
        ax.plot(*first_fixed, color="#dddddd", lw=.8, zorder=0)
        ax.plot(*second_fixed, color="#dddddd", lw=.8, zorder=0)
    corners = np.array([[0, 0], 2*v1, target, v2])
    ax.fill(*corners.T, color="#eef3dc", zorder=1)
    arrow(ax, [0, 0], 2*v1, blue, width=3)
    arrow(ax, 2*v1, target, orange, width=3)
    arrow(ax, [0, 0], v2, orange)
    arrow(ax, v2, target, blue, style="--", width=2)
    arrow(ax, [0, 0], target, green, width=3.2)
    ax.plot(*target, "o", color=green, ms=6, zorder=5)
    ax.text(3.08, 1.18, r"$w=(3,1)$", color=green)
    ax.text(.05, 1.55, r"$2v_1=(2,2)$", color=blue,
            bbox=dict(facecolor="white", edgecolor="none", alpha=.9, pad=2))
    ax.text(2.70, 1.78, r"$+v_2$", color=orange)
    ax.text(.8, -1.3, r"$v_2=(1,-1)$", color=orange)
    ax.text(2.35, -.30, r"$+2v_1$", color=blue)
    ax.text(-.08, -.16, "O", ha="right", va="top")
    fig.suptitle("Two coefficients choose one arrow", fontsize=18, y=.96)
    fig.text(.5, .885, r"$v_1=(1,1),\quad v_2=(1,-1),\quad w=2v_1+v_2$",
             ha="center", fontsize=13)
    fig.text(.5, .10, "Diagonal grid: keep one coefficient fixed, vary the other.",
             ha="center", fontsize=11)
    fig.text(.5, .04, r"$(x,y)=\frac{x+y}{2}v_1+\frac{x-y}{2}v_2$",
             ha="center", fontsize=14)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"),
                dpi=140, facecolor="white", transparent=False)
    plt.close(fig)
    print("Verified target, unique coefficients, and formula at 221 points.")


if __name__ == "__main__":
    main()