codex/newton-root-iteration.py

newton-root-iteration.py
"""Draw Newton tangent geometry; save newton-root-iteration.png in 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, axes = plt.subplots(1, 2, figsize=(10.8, 4.8), layout="constrained")
    fig.patch.set_facecolor("white")
    blue, orange, green = "#2166ac", "#d46514", "#25834a"

    ax = axes[0]
    t = np.linspace(0.85, 1.85, 400)
    ax.plot(t, t * t - 2, color=blue, lw=2.5, label=r"$F(t)=t^2-2$")
    x0, x1, x2 = 1.0, 1.5, 17 / 12
    assert np.isclose(x1 - (x1 * x1 - 2) / (2 * x1), x2)
    ax.plot([0.94, 1.7], [2 * 0.94 - 3, 2 * 1.7 - 3],
            color=orange, lw=1.8, label=r"Tangent at $t_0=1$")
    ax.plot([1.28, 1.74], [3 * 1.28 - 4.25, 3 * 1.74 - 4.25],
            color=green, lw=1.8, label=r"Tangent at $t_1=1.5$")
    for start, end, color in [((x0, -1), (x1, 0), orange),
                              ((x1, 0.25), (x2, 0), green)]:
        ax.annotate("", xy=end, xytext=start,
                    arrowprops={"arrowstyle": "->", "color": color, "lw": 2.2})
    ax.plot([x0, x0], [0, -1], ":", color="0.5")
    ax.plot([x1, x1], [0, 0.25], ":", color="0.5")
    ax.scatter([x0, x1], [-1, 0.25], c=[orange, green], zorder=5)
    ax.scatter([x1, x2], [0, 0], c=[orange, green], zorder=5)
    ax.scatter([np.sqrt(2)], [0], color="black", marker="*", s=80, zorder=6)
    ax.annotate(r"$t_0=1$", (x0, 0), xytext=(0.90, 0.24),
                arrowprops={"arrowstyle": "-", "color": "0.4"})
    ax.annotate(r"$t_1=1.5$", (x1, 0), xytext=(1.60, -0.50),
                arrowprops={"arrowstyle": "-", "color": orange}, color=orange)
    ax.annotate(r"$t_2=17/12$", (x2, 0), xytext=(1.02, 0.65),
                arrowprops={"arrowstyle": "-", "color": green}, color=green)
    ax.annotate(r"Root: $\sqrt{2}\approx1.41421$", (np.sqrt(2), 0),
                xytext=(1.09, -0.85),
                arrowprops={"arrowstyle": "-", "color": "0.3"})
    ax.set(xlim=(0.85, 1.85), ylim=(-1.25, 1.65), xlabel=r"$t$", ylabel=r"$F(t)$",
           title="Two tangent steps toward a square root")
    ax.legend(loc="upper left", fontsize=9, framealpha=1)

    ax = axes[1]
    x = np.linspace(-2.05, 1.4, 600)
    f = lambda value: value**3 - 2 * value + 2
    df = lambda value: 3 * value**2 - 2
    assert 0 - f(0) / df(0) == 1
    assert 1 - f(1) / df(1) == 0
    ax.plot(x, f(x), color=blue, lw=2.5, label=r"$F(x)=x^3-2x+2$")
    ax.plot([-0.2, 1.35], [2.4, -0.7], color=orange, lw=1.8,
            label=r"Tangent at $x_0=0$")
    ax.plot([-0.2, 1.35], [-0.2, 1.35], color=green, lw=1.8,
            label=r"Tangent at $x_1=1$")
    for start, end, color in [((0, 2), (1, 0), orange),
                              ((1, 1), (0, 0), green)]:
        ax.annotate("", xy=end, xytext=start,
                    arrowprops={"arrowstyle": "->", "color": color, "lw": 2.2})
    for xx, yy in [(0, 2), (1, 1)]:
        ax.annotate("", xy=(xx, yy), xytext=(xx, 0),
                    arrowprops={"arrowstyle": "->", "linestyle": ":", "color": "0.4"})
    ax.scatter([0, 1], [2, 1], c=[orange, green], zorder=5)
    ax.scatter([0, 1], [0, 0], c=[green, orange], zorder=5)
    ax.text(0, -0.5, r"$0$", ha="center")
    ax.text(1, -0.5, r"$1$", ha="center")
    root = -1.8
    for _ in range(6):
        root -= f(root) / df(root)
    ax.scatter([root], [0], color="black", marker="*", s=80, zorder=6)
    ax.annotate(r"Root $\approx-1.76929$", (root, 0), xytext=(-1.94, 0.75),
                fontsize=10, arrowprops={"arrowstyle": "-", "color": "0.3"})
    ax.text(-1.75, -1.2, r"Iteration: $0\to1\to0\to1\to\cdots$", fontsize=11)
    ax.set(xlim=(-2.05, 1.4), ylim=(-2.65, 4.45), xlabel=r"$x$", ylabel=r"$F(x)$",
           title="A defined iteration can cycle forever")
    ax.legend(loc="upper left", fontsize=9, framealpha=1)

    for ax in axes:
        ax.set_facecolor("white")
        ax.axhline(0, color="0.25", lw=0.8, zorder=0)
        ax.grid(alpha=0.16)
        ax.spines[["top", "right"]].set_visible(False)
    fig.savefig(Path.cwd() / "newton-root-iteration.png", dpi=110,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()