codexbisection-root-brackets.py
bisection-root-brackets.py
"""Draw the first square-root bisection brackets; write the PNG to cwd.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 11, "axes.titlesize": 13})
fig, (graph, rows) = plt.subplots(
2, 1, figsize=(9.2, 7.0), sharex=True,
gridspec_kw={"height_ratios": [1.1, 1]}, layout="constrained",
)
fig.patch.set_facecolor("white")
blue, red, green, gold = "#2166ac", "#b43b35", "#25834a", "#b37500"
root = np.sqrt(2)
x = np.linspace(0.96, 2.04, 500)
graph.plot(x, x**2 - 2, color=blue, lw=2.5, label=r"$f(x)=x^2-2$")
graph.axhline(0, color="0.35", lw=0.8)
graph.axvline(root, color="0.3", ls="--", lw=1.1,
label=r"Root $\sqrt{2}\approx1.41421$")
candidates = [1.5, 1.25, 1.375, 1.4375]
for k, m in enumerate(candidates):
value = m**2 - 2
color = red if value > 0 else blue
graph.scatter(m, value, color=color, s=48, zorder=5)
offsets = [(38, 27), (-59, -21), (-77, 22), (40, -22)]
graph.annotate(
rf"$m_{k}={m:g}$" + (" (+)" if value > 0 else " (−)"),
(m, value), xytext=offsets[k], textcoords="offset points",
color=color, fontsize=10,
arrowprops={"arrowstyle": "-", "color": color},
)
graph.scatter([1, 2], [-1, 2], color=green, s=45, zorder=5)
graph.set(ylim=(-1.22, 2.28), ylabel=r"$f(x)$",
title="Evaluate the old midpoint; keep the half with opposite signs")
graph.legend(loc="upper left", fontsize=10, framealpha=1)
a, b = 1.0, 2.0
brackets = [(a, b)]
for m in candidates:
assert m == (a + b) / 2
if m**2 > 2:
b = m
else:
a = m
brackets.append((a, b))
assert brackets[-1] == (1.375, 1.4375)
rows.axvline(root, color="0.3", ls="--", lw=1.1)
for k, (a, b) in enumerate(brackets):
y = 4 - k
rows.plot([a, b], [y, y], color=green, lw=5, solid_capstyle="butt")
rows.scatter([a, b], [y, y], color=green, marker="|", s=140, zorder=4)
rows.text(b + 0.025, y, f"[{a:g}, {b:g}]", va="center", fontsize=10)
if k < 4:
m = candidates[k]
rows.scatter(m, y, marker="D", color=gold, s=42, zorder=5)
rows.scatter([], [], marker="D", color=gold, label="Candidate for next update")
rows.scatter([1.40625], [0], marker="o", color="black", s=45, zorder=6,
label=r"Reported after 4 updates: $m_4=1.40625$")
rows.set(xlim=(0.96, 2.28), ylim=(-1.05, 4.5), xlabel=r"$x$ (same scale in both panels)",
yticks=[4, 3, 2, 1, 0], yticklabels=[f"k = {k}" for k in range(5)],
title="Retained brackets after k halvings; every row still contains the root")
rows.legend(loc="lower right", fontsize=9, framealpha=1)
for ax in (graph, rows):
ax.set_facecolor("white")
ax.grid(alpha=0.15)
ax.spines[["top", "right"]].set_visible(False)
fig.savefig(Path.cwd() / "bisection-root-brackets.png", dpi=120,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()