codex/functional-norm-and-evaluation.py

functional-norm-and-evaluation.py
"""Illustrate continuous triangular peaks; write the PNG to the working directory.

Dependencies: numpy and matplotlib, as declared in the root pyproject.toml.
Tested with Python 3.14.4. Run from _media to produce the wiki asset.
"""

from pathlib import Path

import matplotlib

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


def peak(x, n):
    return np.maximum(1 - n * x, 0)


def check_integrals():
    """Check analytic peak and completion-example formulas by quadrature."""
    for n in (1, 4, 16, 100):
        # Resolve the support at every scale, rather than undersampling peaks.
        x = np.linspace(0, 1 / n, 4001)
        f = peak(x, n)
        integral = np.trapezoid(f, x)
        squared_norm = np.trapezoid(f * f, x)
        assert np.isclose(integral, 1 / (2 * n), rtol=1e-12)
        assert np.isclose(squared_norm, 1 / (3 * n), rtol=4e-8)
        assert peak(np.array([0.0]), n)[0] == 1
        print(f"n={n}: integral={integral:.9f}, "
              f"L2 norm={np.sqrt(squared_norm):.9f}, evaluation=1")

    x = np.linspace(0, 1, 4001)
    assert np.isclose(np.trapezoid(x * x, x), 1 / 3, rtol=4e-8)
    assert np.trapezoid(np.ones_like(x), x) == 1
    for n in (2, 4, 16, 100):
        # Only the ramp interval contributes to the squared error against h.
        x = np.linspace(0.5, 0.5 + 1 / n, 4001)
        h_n = np.clip(n * (x - 0.5), 0, 1)
        # The endpoint value of h does not affect its integral; use the right limit.
        error_squared = np.trapezoid((1 - h_n) ** 2, x)
        assert np.isclose(error_squared, 1 / (3 * n), rtol=4e-8)
    print("Ramp squared L2 errors checked: 1/(3n), n=2,4,16,100.")


def main():
    check_integrals()
    plt.rcParams.update({"font.size": 11, "axes.facecolor": "white"})
    fig, axes = plt.subplots(1, 2, figsize=(10.5, 4.7),
                             layout="constrained", facecolor="white")
    colors = ("#2873a7", "#d07628", "#238b68")
    x = np.linspace(0, 1, 4097)
    for n, color in zip((1, 4, 16), colors):
        axes[0].plot(x, peak(x, n), color=color, linewidth=2.4,
                     label=fr"$n={n}$")
    axes[0].fill_between(x, peak(x, 4), color=colors[1], alpha=0.18)
    axes[0].scatter([0], [1], s=45, color="#333333", zorder=5)
    axes[0].annotate(r"$f_n(0)=1$ for every $n$", (0, 1),
                     xytext=(0.23, 0.9),
                     arrowprops={"arrowstyle": "->", "color": "#555555"})
    axes[0].text(0.30, 0.15, "Shaded integral at n = 4:\n" + r"$F(f_4)=1/8$",
                 color=colors[1])
    axes[0].set(xlabel=r"$x$", ylabel=r"$f_n(x)$", xlim=(-0.015, 1.02),
                ylim=(-0.03, 1.13), title=r"Continuous peaks: $f_n(x)=\max(1-nx,0)$")
    axes[0].legend(loc="upper right", fontsize=10)
    axes[0].grid(alpha=0.18)

    n = np.geomspace(1, 128, 401)
    axes[1].loglog(n, np.ones_like(n), color="#333333", linewidth=2.3,
                   label=r"$f_n(0)=\|f_n\|_\infty=1$")
    axes[1].loglog(n, 1 / np.sqrt(3 * n), color=colors[0], linewidth=2.4,
                   label=r"$\|f_n\|_2=1/\sqrt{3n}$")
    axes[1].loglog(n, 1 / (2 * n), color=colors[1], linewidth=2.4,
                   label=r"$F(f_n)=1/(2n)$")
    axes[1].set(xlabel=r"Peak parameter $n$ (log scale)",
                ylabel="Size or measurement (log scale)",
                xlim=(1, 128), ylim=(0.003, 1.3),
                title="Integration shrinks; point evaluation stays fixed")
    axes[1].set_xticks([1, 4, 16, 64, 128], labels=["1", "4", "16", "64", "128"])
    axes[1].legend(loc="lower left", fontsize=10, framealpha=1)
    axes[1].grid(which="major", alpha=0.2)
    fig.suptitle("Small L2 error need not mean small point error", fontsize=15)
    destination = Path.cwd() / "functional-norm-and-evaluation.png"
    fig.savefig(destination, dpi=120, facecolor="white", transparent=False)
    plt.close(fig)
    print(f"Saved {destination.name}")


if __name__ == "__main__":
    main()