codexcontraction-cosine-iteration.py
contraction-cosine-iteration.py
"""Plot cosine iteration and write the PNG basename to cwd.
Uses numpy/matplotlib from pyproject.toml; tested with Python 3.14.4.
Run from _media: MPLCONFIGDIR=/tmp/codex-wiki-mpl python ../contraction-cosine-iteration.py
"""
import math
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def compute_and_check():
"""Compare contraction bounds against a root independently found by bisection."""
lower, upper = 0.0, 1.0
for _ in range(52):
midpoint = (lower + upper) / 2
if math.cos(midpoint) > midpoint:
lower = midpoint
else:
upper = midpoint
root = (lower + upper) / 2
assert upper - lower < 5e-16
assert abs(math.cos(root) - root) < 5e-16
contraction = math.sin(1)
values = [0.0]
for _ in range(91):
values.append(math.cos(values[-1]))
values = np.array(values)
steps = np.arange(values.size)
initial_bounds = contraction**steps / (1 - contraction)
latest_bounds = contraction * np.abs(np.diff(values)) / (1 - contraction)
errors = np.abs(values - root)
# Floating arithmetic is not interval certification; stop the comparison
# while errors remain far larger than machine precision.
assert np.all(errors[:41] <= initial_bounds[:41])
assert np.all(errors[1:41] <= latest_bounds[:40])
assert np.all((values >= 0) & (values <= 1))
assert np.all((values[:41] - root) * (values[1:42] - root) < 0)
samples = np.linspace(0, 1, 1001)
assert np.max(np.sin(samples)) == contraction
assert initial_bounds[90] >= 1e-6 > initial_bounds[91]
assert latest_bounds[39] < 7.20e-7
print(f"L={contraction:.15f}; independently computed root={root:.15f}")
for n in (4, 10, 20, 40):
print(f"n={n}: x={values[n]:.15f}, error={errors[n]:.9g}, "
f"initial bound={initial_bounds[n]:.9g}, "
f"latest bound={latest_bounds[n - 1]:.9g}")
print("Initial bound crosses 1e-6 at n=91; latest-step bound is below 1e-6 at n=40.")
return values, root, initial_bounds, latest_bounds, errors
def main():
values, root, initial_bounds, latest_bounds, errors = compute_and_check()
plt.rcParams.update({"font.size": 11, "axes.facecolor": "white"})
fig, (left, right) = plt.subplots(1, 2, figsize=(10.5, 4.6),
layout="constrained", facecolor="white")
grid = np.linspace(0, 1, 401)
left.plot(grid, np.cos(grid), color="#2873a7", linewidth=2.5, label=r"$y=\cos x$")
left.plot(grid, grid, color="#666666", linewidth=1.8, label=r"$y=x$")
path_x, path_y = [values[0]], [0.0]
for n in range(12):
path_x.extend([values[n], values[n + 1]])
path_y.extend([values[n + 1], values[n + 1]])
left.plot(path_x, path_y, color="#d07628", linewidth=1.3, alpha=0.9,
label="First 12 iterations")
left.scatter([root], [root], color="#238b68", s=55, zorder=5)
left.annotate(r"$x_*\approx0.739085$", (root, root), xytext=(0.18, 0.42),
arrowprops={"arrowstyle": "->", "color": "#238b68"},
color="#187451")
left.set(xlabel=r"$x$", ylabel=r"$y$", xlim=(-0.03, 1.04), ylim=(-0.03, 1.04),
title="Iterates alternate around the fixed point")
left.set_aspect("equal", adjustable="box")
left.legend(loc="upper left", fontsize=9)
left.grid(alpha=0.18)
n = np.arange(41)
right.semilogy(n, initial_bounds[:41], color="#666666", linewidth=2.2,
linestyle="--", label=r"Initial: $L^n/(1-L)$")
right.semilogy(n[1:], latest_bounds[:40], color="#d07628", linewidth=2.2,
label=r"Latest: $L|x_n-x_{n-1}|/(1-L)$")
right.semilogy(n, errors[:41], color="#2873a7", linewidth=2.4,
label=r"Measured: $|x_n-x_*|$")
right.axhline(1e-6, color="#238b68", linestyle=":", linewidth=1.4)
right.text(1.1, 1.5e-6, r"Tolerance $10^{-6}$", color="#187451", fontsize=10)
right.set(xlabel=r"Iteration number $n$", ylabel="Error or bound (log scale)",
title=r"Guaranteed bounds with $L=\sin 1$",
xlim=(0, 41), ylim=(2e-8, 12))
right.legend(loc="upper right", fontsize=9)
right.grid(which="major", alpha=0.2)
fig.suptitle(r"A contraction turns repeated approximation into a guarantee", fontsize=15)
destination = Path.cwd() / "contraction-cosine-iteration.png"
fig.savefig(destination, dpi=120, facecolor="white", transparent=False)
plt.close(fig)
print(f"Saved {destination.name}")
if __name__ == "__main__":
main()