Newton’s Method#

[1]:
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import numpy as np
[2]:
mpl.rcParams['text.usetex'] = True
mpl.rcParams['text.latex.preamble'] = r'\usepackage{amsmath}'
plt.rcParams["font.size"] = 16

Problem to solve#

Find the solution to

\[f(x) = x^2 + 2x - 10 = 0\]
[3]:
xs = np.linspace(-10, 10, 1000)
ys = xs**2 + 2*xs - 10

fig, ax = plt.subplots(1, 1, figsize=(5, 5))
ax.plot(xs, ys, linewidth=2, color="cornflowerblue")

ax.xaxis.set_major_locator(MultipleLocator(5.0))
ax.xaxis.set_minor_locator(MultipleLocator(1.25))
ax.yaxis.set_major_locator(MultipleLocator(40.0))
ax.yaxis.set_minor_locator(MultipleLocator(10.0))

ax.grid(True, "minor", color="0.85", linewidth=0.50, zorder=-20)
ax.grid(True, "major", color="0.65", linewidth=0.75, zorder=-10)
ax.tick_params(which="both", bottom=False, left=False)

ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$f(x)$", rotation=0)
ax.yaxis.set_label_coords(-0.2, 0.5)

ax.set_xlim([-10, 10])

plt.show()
../_images/notebooks_NewtonsMethod_4_0.png

Implementation#

The derivative of \(f(x)\) is

\[\nabla f(x) = 2x + 2\]

The linear approximation at \(\hat{x}\) has the form of

\[\tilde{f}(\hat{x}) = f(\hat{x}) + \nabla f(\hat{x})(x - \hat{x})\]

Then the update is

\[\hat{x} \leftarrow \hat{x} - \frac{f(\hat{x})}{\nabla f(\hat{x})} = \hat{x} - \frac{\hat{x}^2 + 2\hat{x} - 10}{2\hat{x} + 2} = \frac{\hat{x}^2 + 10}{2\hat{x} + 2}\]
[4]:
x = 10
fx = x**2 + 2*x - 10
_xs = []

sol_info = {
    "x": x,
    "f(x)": fx,
    "∇f(x)": 2 * x + 2,
}

_xs.append(sol_info)

while fx > 1e-3:
    x = (x**2 + 10) / (2*x+2)
    fx = x**2 + 2*x - 10
    print(f"x: {x}, f(x): {fx}")

    sol_info = {
        "x": x,
        "f(x)": fx,
        "∇f(x)": 2 * x + 2,
    }
    _xs.append(sol_info)

# One additional iteration for plotting
x = (x**2 + 10) / (2*x+2)
fx = x**2 + 2*x - 10
print(f"x: {x}, f(x): {fx}")
sol_info = {
    "x": x,
    "f(x)": fx,
    "∇f(x)": 2 * x + 2,
}
_xs.append(sol_info)
x: 5.0, f(x): 25.0
x: 2.9166666666666665, f(x): 4.340277777777775
x: 2.3625886524822692, f(x): 0.30700244580252445
x: 2.3169389347304574, f(x): 0.002083896730821877
x: 2.3166248052315686, f(x): 9.867734007684703e-08
x: 2.3166247903554, f(x): 0.0
[5]:
x = -10
fx = x**2 + 2*x - 10

while fx > 1e-3:
    x = (x**2 + 10) / (2*x+2)
    fx = x**2 + 2*x - 10
    print(f"x: {x}, f(x): {fx}")
x: -6.111111111111111, f(x): 15.123456790123456
x: -4.631642512077295, f(x): 2.1888273355270815
x: -4.330287501465984, f(x): 0.09081484242054216
x: -4.316652816415433, f(x): 0.00018590463642631505

Visualize Solution#

[6]:
plt.rcParams["font.size"] = 24

fig, axs = plt.subplots(2, 3, figsize=(20, 10), sharey=True, sharex=True)

xs = np.linspace(-10, 10, 1000)
ys = xs**2 + 2*xs - 10

for i in range(2):
    for j in range(3):
        axs[i][j].plot(xs, ys, linewidth=3, color="cornflowerblue")

        _ys = _xs[i*3+j]["f(x)"] + _xs[i*3+j]["∇f(x)"] * (xs - _xs[i*3+j]["x"])
        axs[i][j].plot(xs, _ys, linewidth=3, color="darkorange")
        axs[i][j].plot(_xs[i*3+j+1]["x"], 0.0, 'o', color="thistle", markersize=10)

        axs[i][j].xaxis.set_major_locator(MultipleLocator(10.0))
        axs[i][j].xaxis.set_minor_locator(MultipleLocator(2.5))
        axs[i][j].yaxis.set_major_locator(MultipleLocator(50.0))
        axs[i][j].yaxis.set_minor_locator(MultipleLocator(12.5))

        axs[i][j].grid(True, "minor", color="0.85", linewidth=0.50, zorder=-20)
        axs[i][j].grid(True, "major", color="0.65", linewidth=0.75, zorder=-10)
        axs[i][j].tick_params(which="both", bottom=False, left=False)

        axs[1][j].set_xlabel(r"$x$")
        axs[i][j].set_xlim([-10, 10])
        axs[i][j].set_ylim([-20, 120])
        axs[i][j].set_title(f"Iteration {i*3+j+1}", pad=20)

axs[0][0].set_ylabel(r"$f(x)$", rotation=0)
axs[0][0].yaxis.set_label_coords(-0.2, 0.5)
axs[1][0].set_ylabel(r"$f(x)$", rotation=0)
axs[1][0].yaxis.set_label_coords(-0.2, 0.5)

plt.subplots_adjust(wspace=0.1, hspace=0.2)
plt.savefig("../images/newtons_method_illustration.png", dpi=200, transparent=False, bbox_inches="tight")
plt.show()
../_images/notebooks_NewtonsMethod_9_0.png
[ ]: