codexfunctional-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()