codexbounded-monotone-sequence-limit-brackets.py
bounded-monotone-sequence-limit-brackets.py
"""Draw monotone limit brackets and their exact errors; write the PNG to cwd.
Tested with Python 3.14, matplotlib 3.10.7, and numpy 2.3.5.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 11, "axes.titlesize": 12})
blue, orange, green = "#176ba0", "#b65320", "#277842"
n = np.arange(1, 21)
lower, upper = 1 - 1 / n, 1 + 1 / n
assert np.all(np.diff(lower) > 0) and np.all(np.diff(upper) < 0)
assert np.all(lower < 1) and np.all(upper > 1)
assert np.all(np.abs(lower[n >= 6] - 1) < 0.2)
assert np.all(np.abs(upper[n >= 6] - 1) < 0.2)
fig, axes = plt.subplots(1, 2, figsize=(10.4, 4.5), dpi=120,
facecolor="white")
ax = axes[0]
ax.axhspan(0.8, 1.2, color=green, alpha=0.12,
label=r"Tolerance band: $1\pm0.2$")
ax.axhline(1, color="#38444e", linestyle="--", linewidth=1.4,
label=r"Common limit $L=1$")
ax.plot(n, lower, "o-", color=blue, markersize=4.5,
label=r"Increasing: $a_n=1-1/n$")
ax.plot(n, upper, "o-", color=orange, markersize=4.5,
label=r"Decreasing: $b_n=1+1/n$")
ax.axvline(6, color=green, linestyle=":", linewidth=1.5)
ax.text(6.3, 0.08, r"$n\geq6$: both errors $<0.2$", color=green,
fontsize=10)
ax.set(xlim=(0.5, 20.5), ylim=(-0.08, 2.1), xlabel="Index n",
ylabel="Sequence value", title="Two directions, one limiting destination")
ax.set_xticks([1, 6, 10, 15, 20])
ax.legend(loc="upper right", fontsize=9, framealpha=1)
m = np.arange(1, 17)
reciprocal_error = 1 / m
geometric_error = 2.0 ** (-m)
ax = axes[1]
ax.semilogy(m, reciprocal_error, "o-", color=blue, markersize=4.5,
label=r"$|1-(1-1/n)|=1/n$")
ax.semilogy(m, geometric_error, "s-", color=orange, markersize=4,
label=r"$|1-(1-2^{-n})|=2^{-n}$")
ax.axhline(0.01, color=green, linestyle="--", linewidth=1.2,
label="Tolerance 0.01")
ax.annotate(r"$n=7$: $2^{-7}<0.01$", xy=(7, 2.0 ** -7),
xytext=(8, 0.0012), fontsize=10, color=orange,
arrowprops={"arrowstyle": "->", "color": orange})
ax.text(0.04, 0.07, r"$1/n<0.01$ first at $n=101$", color=blue,
transform=ax.transAxes, fontsize=10)
ax.set(xlim=(0.5, 16.5), ylim=(8e-6, 2), xlabel="Index n",
ylabel="Absolute error (log scale)",
title="Convergence can have very different speeds")
ax.set_xticks([1, 4, 7, 10, 13, 16])
ax.legend(loc="upper right", fontsize=9, framealpha=1)
for ax in axes:
ax.set_facecolor("white")
ax.spines[["top", "right"]].set_visible(False)
ax.grid(alpha=0.2)
fig.suptitle("Bounded monotone sequences: limiting brackets and exact errors",
fontsize=14, y=0.97)
fig.subplots_adjust(left=0.07, right=0.98, bottom=0.14, top=0.83,
wspace=0.28)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()