codex/lie-affine-noncommutation.py

lie-affine-noncommutation.py
"""Generate lie-affine-noncommutation.png in the current directory.

Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Run from _media. Products act on column vectors, rightmost factor first.
"""

from pathlib import Path

import matplotlib

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


def scale(a):
    return np.array([[np.exp(a), 0.], [0., 1.]])


def translate(b):
    return np.array([[1., b], [0., 1.]])


def main():
    d = np.array([[1., 0.], [0., 0.]])
    t = np.array([[0., 1.], [0., 0.]])
    assert np.array_equal(d @ t - t @ d, t)
    point = np.array([1., 1.])
    a = np.log(2.)
    first = (scale(a) @ translate(1.) @ point)[0]
    second = (translate(1.) @ scale(a) @ point)[0]
    assert np.isclose(first, 4.) and np.isclose(second, 3.)
    for a, b in [(np.log(2), 1.), (.1, .1), (-.2, .3)]:
        product = scale(a) @ translate(b) @ scale(-a) @ translate(-b)
        assert np.allclose(product, translate(np.expm1(a) * b))
    displacement = .1 * np.expm1(.1)
    assert np.isclose(displacement, .010517091807564762, atol=1e-15)
    eps_checks = np.array([.1, .05, .01])
    ratios = np.expm1(eps_checks) / eps_checks
    assert np.all(np.diff(np.abs(ratios - 1)) < 0)

    blue, orange, gray = "#276ba3", "#bf6224", "#667887"
    fig, axes = plt.subplots(1, 2, figsize=(10.8, 4.7), dpi=120,
                             facecolor="white",
                             gridspec_kw={"width_ratios": [1.2, 1]})
    ax = axes[0]
    ax.set_title("Reverse the order, change the endpoint", fontsize=12, pad=15)
    ax.set(xlim=(.6, 4.4), ylim=(-.55, 2.15), xlabel="Position on the real line")
    ax.set_xticks([1, 2, 3, 4])
    ax.set_yticks([])
    ax.spines[["top", "right", "left"]].set_visible(False)
    for y, end, color, title, first_label, second_label in [
            (1.25, 4., blue, r"Translate, then double: $S_{\log 2}U_1$",
             "+1", "×2"),
            (.1, 3., orange, r"Double, then translate: $U_1S_{\log 2}$",
             "×2", "+1")]:
        ax.axhline(y, color="#d4dce1", lw=1, zorder=0)
        ax.text(.72, y + .65, title, color=color, fontsize=11)
        for start, finish, label in [(1., 2., first_label), (2., end, second_label)]:
            ax.annotate("", xy=(finish - .07, y), xytext=(start + .07, y),
                        arrowprops=dict(arrowstyle="->", color=color, lw=2))
            ax.text((start + finish) / 2, y + .17, label, color=color,
                    fontsize=12, ha="center")
        ax.scatter([1, 2, end], [y] * 3, color=color, s=40, zorder=5)
        for value in [1, 2, end]:
            ax.text(value, y - .27, f"{value:g}", ha="center", color=color,
                    fontsize=12)

    ax = axes[1]
    eps = np.linspace(0., .4, 400)
    ax.plot(eps, eps * np.expm1(eps), color=blue, lw=2,
            label=r"Exact: $\varepsilon(e^\varepsilon-1)$")
    ax.plot(eps, eps**2, "--", color=orange, lw=2,
            label=r"Leading term: $\varepsilon^2$")
    ax.scatter([.1], [displacement], color=blue, s=35, zorder=4)
    ax.annotate("ε = 0.1\nshift = 0.01051709", xy=(.1, displacement),
                xytext=(.04, .085), fontsize=10, color=gray,
                arrowprops=dict(arrowstyle="->", color=gray))
    ax.set(xlim=(0, .4), ylim=(0, .21), xlabel=r"Equal parameters $a=b=\varepsilon$",
           ylabel="Residual translation")
    ax.set_title("Four moves leave a second-order shift", fontsize=12, pad=15)
    ax.spines[["top", "right"]].set_visible(False)
    ax.grid(alpha=.18)
    ax.legend(loc="upper left", fontsize=10, frameon=False)
    fig.tight_layout(pad=1.5, w_pad=2)
    output = Path.cwd() / Path(__file__).with_suffix(".png").name
    fig.savefig(output, facecolor="white", transparent=False)
    plt.close(fig)
    print(f"endpoints: {first:g}, {second:g}")
    print(f"commutator displacement at a=b=.1 = {displacement:.12f}")
    print(f"displacement/epsilon^2 for .1, .05, .01 = {ratios}")
    print(output.name)


if __name__ == "__main__":
    main()