codex/quadratic-field-unit-powers.py

quadratic-field-unit-powers.py
"""Plot real embeddings of powers of 1+sqrt(2), with exact coefficients.

PNG output goes to the current working directory.
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 main():
    exponents = np.arange(-4, 5)
    epsilon = 1 + np.sqrt(2)
    plus = epsilon**exponents
    minus_absolute = epsilon**(-exponents)
    rows = []
    a, b = 1, 0
    for n in range(5):
        norm = a*a - 2*b*b
        assert norm == (-1)**n
        rows.append([str(n), str(a), str(b), f"{norm:+d}"])
        a, b = a + 2*b, a + b
    assert np.allclose(plus * minus_absolute, 1)

    plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
                         "axes.spines.right": False})
    fig, (ax, table_ax) = plt.subplots(1, 2, figsize=(9.6, 4.8), dpi=120,
                                     gridspec_kw={"width_ratios": [1.4, 1]})
    ax.semilogy(exponents, plus, "o-", color="#186b9c", linewidth=2,
                label=r"$|\sigma_+(\varepsilon^n)|=(1+\sqrt{2})^n$")
    ax.semilogy(exponents, minus_absolute, "s-", color="#a65419", linewidth=2,
                label=r"$|\sigma_-(\varepsilon^n)|=(1+\sqrt{2})^{-n}$")
    ax.axhline(1, color="#757575", linewidth=0.8, linestyle="--")
    ax.set(xlabel=r"integer exponent $n$", ylabel="absolute value (logarithmic scale)",
           xlim=(-4.35, 4.35), ylim=(0.02, 50))
    ax.set_xticks(exponents)
    ax.grid(alpha=0.22)
    ax.legend(loc="upper center", fontsize=10, framealpha=1)
    table_ax.axis("off")
    table_ax.set_title(r"$\varepsilon^n=a_n+b_n\sqrt{2}$", pad=18)
    table = table_ax.table(cellText=rows,
                           colLabels=[r"$n$", r"$a_n$", r"$b_n$", r"$N(\varepsilon^n)$"],
                           cellLoc="center", colWidths=[0.17, 0.22, 0.22, 0.39],
                           bbox=[0, 0.28, 1, 0.63])
    table.auto_set_font_size(False)
    table.set_fontsize(12)
    for (row, col), cell in table.get_celld().items():
        cell.set_edgecolor("#cbd4dd")
        if row == 0:
            cell.set_facecolor("#dbeaf7")
        elif row % 2 == 0:
            cell.set_facecolor("#f5edf7")
        else:
            cell.set_facecolor("#eff7f2")
    table_ax.text(0.5, 0.12, "Even powers: norm +1\nOdd powers: norm −1",
                   ha="center", va="center", fontsize=12, transform=table_ax.transAxes)
    fig.suptitle(r"Powers of the unit $\varepsilon=1+\sqrt{2}$", fontsize=16, y=0.97)
    fig.text(0.5, 0.035, r"One embedding grows as the other shrinks:"
             r" $\sigma_+(\varepsilon^n)\,\sigma_-(\varepsilon^n)=(-1)^n$.",
             ha="center", fontsize=12)
    fig.subplots_adjust(left=0.085, right=0.97, bottom=0.17, top=0.81, wspace=0.27)
    fig.savefig(Path.cwd() / "quadratic-field-unit-powers.png", facecolor="white",
                transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()