Mathematical Foundations

VI.6 · Optimization

What changes when the optimizer remembers its direction?

Track a velocity vector alongside the parameters. Each gradient adds to decaying history, so the next displacement can keep moving after the current gradient changes or vanishes.

Track velocity before and after each gradient

Parameter space: θ₁ and θ₂

Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss. Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss. Stop: iteration budget. Parameters outside the displayed window are retained in the table; the view never changes the path.

Loss slice: θ₂ = 0

This is a slice of the objective in parameter space, with θ₂ fixed at zero.

Current parameter vector
(2, 1)
Current objective value
4
Actual gradient components
(2, 4)
Learning rate
0.250000
Update vector
(−0.5, −1)
Next parameter vector
(1.5, 0)
New objective value
1.125000
Previous velocity
(0, 0)
Updated velocity
(2, 4)
Momentum decay
0.800000

≈ binary64 calculation · = analytic classification. Replay selects the same evaluated step in every view.

Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss. Stop: iteration budget. Parameters outside the displayed window are retained in the table; the view never changes the path.

L=a2(θ1−c)2+2θ22L=\tfrac a2(\theta_1-c)^2+2\theta_2^2
vk+1=βvk+gk,θk+1=θk−ηvk+1v_{k+1}=\beta v_k+g_k,\quad\theta_{k+1}=\theta_k-\eta v_{k+1}
θ1≈[21]+[−0.5−1]≈[1.50]\theta_{1}\approx \begin{bmatrix}2\\1\end{bmatrix}+\begin{bmatrix}-0.5\\-1\end{bmatrix}\approx \begin{bmatrix}1.5\\0\end{bmatrix}
Evaluated binary64 history. Replay changes the selected row, never the stored mathematics.
kθ(k)L(k)gradientηupdateθ(k+1)L(k+1)previous velocityupdated velocity
0(2, 1)4(2, 4)0.25(−0.5, −1)(1.5, 0)1.125(0, 0)(2, 4)
1(1.5, 0)1.125(1.5, 0)0.25(−0.775, −0.8)(0.725, −0.8)1.542813(2, 4)(3.1, 3.2)
2(0.725, −0.8)1.542813(0.725, −3.2)0.25(−0.80125, 0.16)(−0.07625, −0.64)0.822107(3.1, 3.2)(3.205, −0.64)
3(−0.07625, −0.64)0.822107(−0.07625, −2.56)0.25(−0.621938, 0.768)(−0.698188, 0.128)0.276501(3.205, −0.64)(2.48775, −3.072)
4(−0.698188, 0.128)0.276501(−0.698188, 0.512)0.25(−0.323003, 0.4864)(−1.021191, 0.6144)1.27639(2.48775, −3.072)(1.292013, −1.9456)
5(−1.021191, 0.6144)1.27639(−1.021191, 2.4576)0.25(−0.003105, −0.22528)(−1.024295, 0.38912)0.827419(1.292013, −1.9456)(0.012419, 0.90112)
6(−1.024295, 0.38912)0.827419(−1.024295, 1.55648)0.25(0.25359, −0.569344)(−0.770705, −0.180224)0.361955(0.012419, 0.90112)(−1.01436, 2.277376)
7(−0.770705, −0.180224)0.361955(−0.770705, −0.720896)0.25(0.395548, −0.275251)(−0.375157, −0.455475)0.485287(−1.01436, 2.277376)(−1.582193, 1.101005)
8(−0.375157, −0.455475)0.485287(−0.375157, −1.821901)0.25(0.410228, 0.235274)(0.035071, −0.220201)0.097592(−1.582193, 1.101005)(−1.640912, −0.941097)
9(0.035071, −0.220201)0.097592(0.035071, −0.880804)0.25(0.319415, 0.40842)(0.354486, 0.188219)0.133683(−1.640912, −0.941097)(−1.277659, −1.633681)
10(0.354486, 0.188219)0.133683(0.354486, 0.752878)0.25(0.16691, 0.138517)(0.521396, 0.326736)0.34944(−1.277659, −1.633681)(−0.667641, −0.554068)
11(0.521396, 0.326736)0.34944(0.521396, 1.306945)0.25(0.003179, −0.215923)(0.524575, 0.110814)0.162149(−0.667641, −0.554068)(−0.012717, 0.863691)

Evaluated iteration 0

Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss.

θ1≈[21]+[−0.5−1]≈[1.50]\theta_{1}\approx \begin{bmatrix}2\\1\end{bmatrix}+\begin{bmatrix}-0.5\\-1\end{bmatrix}\approx \begin{bmatrix}1.5\\0\end{bmatrix}

from −3 to 3, up to 2 decimal places

from −3 to 3, up to 2 decimal places

from 0.25 to 4, up to 2 decimal places

from −2 to 2, up to 2 decimal places

from 0.01 to 2, up to 2 decimal places

from 1 to 40, whole numbers only

from 0 to 0.99, up to 2 decimal places

Read previous velocity, current gradient, updated velocity, and actual displacement together. Replay the canonical first two steps to see why the y coordinate moves again after reaching zero.

Start at (2,1)(2,1) with zero velocity, β=0.8\beta=0.8, and η=0.25\eta=0.25. The first point matches ordinary gradient descent: (3/2,0)(3/2,0). Before advancing again, predict whether zero current y gradient also means zero next y displacement. The stored velocity supplies the missing information.

Intuition: current slope plus a fading history

Plain gradient descent chooses a displacement from the current gradient alone. Momentum adds memory. Gradients that repeatedly point the same way accumulate, while older contributions fade. A newly opposing gradient must first cancel some retained history before the accumulated direction can reverse.

The mechanical analogy is motion with damping, but the velocity here is an optimizer state vector with a stated sign convention. Positive velocity produces a negative parameter displacement because the update subtracts it. It need not equal physical velocity, the current gradient, or a unit direction.

Memory can help persistent directions dominate short-lived reversals, but it can also carry a point past a low-loss region. Inspect the extra state instead of assuming that smoothing or convexity makes every step lower the objective.

Definition: the complete two-state recurrence

This chapter uses unnormalized accumulated gradients:

gk=∇L(θk),vk+1=βvk+gk,θk+1=θk−ηvk+1,v0=(0,0).g_k=\nabla L(\theta_k),\qquad v_{k+1}=\beta v_k+g_k,\qquad \theta_{k+1}=\theta_k-\eta v_{k+1}, \qquad v_0=(0,0).

The parameters satisfy η>0\eta>0 and 0≤β<10\leq\beta<1. Both θk\theta_k and vkv_k belong to the optimizer state. Two runs reaching the same parameter point with different velocities can take different next steps.

The recurring quadratic is L=a(x−c)2/2+2y2L=a(x-c)^2/2+2y^2, with gradient (a(x−c),4y)(a(x-c),4y). The canonical run uses a=1,c=0a=1,c=0, start (2,1)(2,1), η=1/4\eta=1/4, and β=4/5\beta=4/5. A normalized moving average βv+(1−β)g\beta v+(1-\beta)g is a different convention; the gradient factor is deliberately absent here.

Mechanics: inspect the retained direction before adding it

The selected step exposes the previous velocity, current gradient, updated velocity, decay, rate, displacement, and next parameters. First compute βvk\beta v_k, then add gkg_k, then multiply the resulting vector by −η-\eta. The arrow’s endpoint difference must equal that last vector, not merely −ηgk-\eta g_k.

Reset restores the canonical parameters and zero velocity. Changing the rate, start, or decay creates a new evaluated trajectory. Replay selects stored parameter-and-velocity states; it does not reconstruct velocity by guessing from a rounded path or reverse a floating-point update.

The no-memory preset sets β=0\beta=0. It should recover ordinary gradient descent with the same point and rate. Compare it with the canonical momentum run at the second step, where their histories first produce different displacements.

Derivation: what the memory actually contains

Unroll the velocity recurrence from v0=0v_0=0:

v1=g0,v2=βg0+g1,vk+1=∑j=0kβk−jgj.v_1=g_0,\qquad v_2=\beta g_0+g_1,\qquad v_{k+1}=\sum_{j=0}^{k}\beta^{k-j}g_j.

Every previous gradient is weighted by a power of the decay factor. For a constant gradient g, this becomes

vk+1=1−βk+11−βg⟶g1−β.v_{k+1}=\frac{1-\beta^{k+1}}{1-\beta}g \longrightarrow\frac{g}{1-\beta}.

At β=4/5\beta=4/5, persistent gradient magnitude is amplified toward five times g under this convention. The normalized average instead tends to g. For identical histories from zero state, its velocity is (1−β)(1-\beta) times the unnormalized one; matching displacements would require dividing its learning rate by 1−β1-\beta.

The parameter history gives another view. Since vk=(θk−1−θk)/ηv_k=(\theta_{k-1}-\theta_k)/\eta for k≥1k\geq1,

θk+1=(1+β)θk−βθk−1−ηgk.\theta_{k+1}=(1+\beta)\theta_k-\beta\theta_{k-1}-\eta g_k.

For a quadratic error mode of curvature λ\lambda, this is

ek+1=(1+β−ηλ)ek−βek−1.e_{k+1}=(1+\beta-\eta\lambda)e_k-\beta e_{k-1}.

It is a second-order recurrence, unlike gradient descent’s single error multiplier. The previous rate condition for plain descent is therefore not a complete momentum stability analysis. The memory and rate act together.

Worked example: the same first step, a different second step

At θ0=(2,1)\theta_0=(2,1), g0=(2,4)g_0=(2,4) and v0=(0,0)v_0=(0,0), so

v1=(2,4),Δθ0=−14(2,4)=(−1/2,−1),θ1=(3/2,0),L1=9/8.v_1=(2,4),\quad \Delta\theta_0=-\frac14(2,4)=(-1/2,-1),\quad \theta_1=(3/2,0),\quad L_1=9/8.

Now recompute g1=(3/2,0)g_1=(3/2,0), retaining v1v_1. The next velocity is

v2=45(2,4)+(3/2,0)=(31/10,16/5)=(3.1,3.2).v_2=\frac45(2,4)+(3/2,0) =(31/10,16/5)=(3.1,3.2).

Its displacement and next point are

Δθ1=(−31/40,−4/5)=(−0.775,−0.8),θ2=(29/40,−4/5)=(0.725,−0.8).\Delta\theta_1=(-31/40,-4/5)=(-0.775,-0.8),\qquad \theta_2=(29/40,-4/5)=(0.725,-0.8).

The new loss is

L2=12(2940)2+2(−45)2=841+40963200=49373200>98.L_2=\frac12\left(\frac{29}{40}\right)^2 +2\left(-\frac45\right)^2 =\frac{841+4096}{3200}=\frac{4937}{3200}>\frac98.

The loss rises from 1.1251.125 to 1.54281251.5428125 in exact decimal arithmetic. The y contribution rises because the retained positive y velocity moves the parameter below zero. All equalities here are hand calculations; the evaluated stage history is labeled binary64 approximation.

Worked example: Track velocity before and after each gradient

Parameter space: θ₁ and θ₂

Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss. Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss. Stop: iteration budget. Parameters outside the displayed window are retained in the table; the view never changes the path.

Loss slice: θ₂ = 0

This is a slice of the objective in parameter space, with θ₂ fixed at zero.

Current parameter vector
(2, 1)
Current objective value
4
Actual gradient components
(2, 4)
Learning rate
0.250000
Update vector
(−0.5, −1)
Next parameter vector
(1.5, 0)
New objective value
1.125000
Previous velocity
(0, 0)
Updated velocity
(2, 4)
Momentum decay
0.800000

≈ binary64 calculation · = analytic classification. Replay selects the same evaluated step in every view.

Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss. Stop: iteration budget. Parameters outside the displayed window are retained in the table; the view never changes the path.

L=a2(θ1−c)2+2θ22L=\tfrac a2(\theta_1-c)^2+2\theta_2^2
vk+1=βvk+gk,θk+1=θk−ηvk+1v_{k+1}=\beta v_k+g_k,\quad\theta_{k+1}=\theta_k-\eta v_{k+1}
θ1≈[21]+[−0.5−1]≈[1.50]\theta_{1}\approx \begin{bmatrix}2\\1\end{bmatrix}+\begin{bmatrix}-0.5\\-1\end{bmatrix}\approx \begin{bmatrix}1.5\\0\end{bmatrix}
Evaluated binary64 history. Replay changes the selected row, never the stored mathematics.
kθ(k)L(k)gradientηupdateθ(k+1)L(k+1)previous velocityupdated velocity
0(2, 1)4(2, 4)0.25(−0.5, −1)(1.5, 0)1.125(0, 0)(2, 4)
1(1.5, 0)1.125(1.5, 0)0.25(−0.775, −0.8)(0.725, −0.8)1.542813(2, 4)(3.1, 3.2)
2(0.725, −0.8)1.542813(0.725, −3.2)0.25(−0.80125, 0.16)(−0.07625, −0.64)0.822107(3.1, 3.2)(3.205, −0.64)
3(−0.07625, −0.64)0.822107(−0.07625, −2.56)0.25(−0.621938, 0.768)(−0.698188, 0.128)0.276501(3.205, −0.64)(2.48775, −3.072)
4(−0.698188, 0.128)0.276501(−0.698188, 0.512)0.25(−0.323003, 0.4864)(−1.021191, 0.6144)1.27639(2.48775, −3.072)(1.292013, −1.9456)
5(−1.021191, 0.6144)1.27639(−1.021191, 2.4576)0.25(−0.003105, −0.22528)(−1.024295, 0.38912)0.827419(1.292013, −1.9456)(0.012419, 0.90112)
6(−1.024295, 0.38912)0.827419(−1.024295, 1.55648)0.25(0.25359, −0.569344)(−0.770705, −0.180224)0.361955(0.012419, 0.90112)(−1.01436, 2.277376)
7(−0.770705, −0.180224)0.361955(−0.770705, −0.720896)0.25(0.395548, −0.275251)(−0.375157, −0.455475)0.485287(−1.01436, 2.277376)(−1.582193, 1.101005)
8(−0.375157, −0.455475)0.485287(−0.375157, −1.821901)0.25(0.410228, 0.235274)(0.035071, −0.220201)0.097592(−1.582193, 1.101005)(−1.640912, −0.941097)
9(0.035071, −0.220201)0.097592(0.035071, −0.880804)0.25(0.319415, 0.40842)(0.354486, 0.188219)0.133683(−1.640912, −0.941097)(−1.277659, −1.633681)
10(0.354486, 0.188219)0.133683(0.354486, 0.752878)0.25(0.16691, 0.138517)(0.521396, 0.326736)0.34944(−1.277659, −1.633681)(−0.667641, −0.554068)
11(0.521396, 0.326736)0.34944(0.521396, 1.306945)0.25(0.003179, −0.215923)(0.524575, 0.110814)0.162149(−0.667641, −0.554068)(−0.012717, 0.863691)

Evaluated iteration 0

Iteration 0: at θ ≈ (2, 1), L ≈ 4 and gradient ≈ (2, 4). Rate η ≈ 0.25 gives update ≈ (−0.5, −1). Adding that update gives θ next ≈ (1.5, 0) and L next ≈ 1.125. Velocity changes from ≈ (0, 0) to ≈ (2, 4) using v next = β v previous + g. The update uses the accumulated velocity; momentum can increase loss.

θ1≈[21]+[−0.5−1]≈[1.50]\theta_{1}\approx \begin{bmatrix}2\\1\end{bmatrix}+\begin{bmatrix}-0.5\\-1\end{bmatrix}\approx \begin{bmatrix}1.5\\0\end{bmatrix}

Replay the canonical momentum steps. At the second step, gradient (3/2,0) combines with stored velocity (2,4) to produce velocity (31/10,16/5) and point (29/40,−4/5).

Executable reference code

The standard-library reference records exact Fraction frames and verifies the literal first and second steps, including the rising loss 4937/32004937/3200. It then checks the same path with the independently derived second-order parameter recurrence. Additional assertions cover no memory, constant-gradient accumulation, and a zero current gradient with retained velocity.

The NumPy program advances several starts simultaneously. It independently builds a four-state linear transition matrix for quadratic errors and velocities, then cross-checks the trajectory with numpy.linalg.matrix_power. This library calculation verifies the bounded implementation; it does not create a universal guarantee for arbitrary momentum settings or objectives.

code/python/optimization_momentum.py
"""Exact unnormalized momentum: v_next=beta*v+g, theta_next=theta-eta*v_next."""
from fractions import Fraction as F


def step(theta, velocity=(0, 0), eta=F(1, 4), beta=F(4, 5), a=1, c=0):
    x, y = map(F, theta)
    vx, vy = map(F, velocity)
    eta, beta, a, c = map(F, (eta, beta, a, c))
    if eta <= 0 or a <= 0 or not 0 <= beta < 1:
        raise ValueError("Require eta>0, a>0, and 0<=beta<1")
    gradient = (a*(x-c), 4*y)
    next_velocity = (beta*vx+gradient[0], beta*vy+gradient[1])
    update = tuple(-eta*v for v in next_velocity)
    next_theta = (x+update[0], y+update[1])
    return {
        "theta": (x, y), "loss": a*(x-c)**2/2+2*y*y,
        "velocity": (vx, vy), "gradient": gradient, "next_velocity": next_velocity,
        "update": update, "next_theta": next_theta,
        "next_loss": a*(next_theta[0]-c)**2/2+2*next_theta[1]**2,
    }


def trajectory(start=(2, 1), iterations=8, eta=F(1, 4), beta=F(4, 5), a=1, c=0):
    if type(iterations) is not int or not 0 <= iterations <= 1000:
        raise ValueError("Require an integer iteration count in [0,1000]")
    eta, beta, a, c = map(F, (eta, beta, a, c))
    if eta <= 0 or a <= 0 or not 0 <= beta < 1:
        raise ValueError("Require eta>0, a>0, and 0<=beta<1")
    theta, velocity, frames = tuple(map(F, start)), (F(0), F(0)), []
    for _ in range(iterations):
        item = step(theta, velocity, eta, beta, a, c)
        frames.append(item)
        theta, velocity = item["next_theta"], item["next_velocity"]
    return frames


def main():
    frames = trajectory()
    first, second = frames[:2]
    assert first["next_velocity"] == (2, 4)
    assert first["next_theta"] == (F(3, 2), 0) and first["next_loss"] == F(9, 8)
    assert second["gradient"] == (F(3, 2), 0)
    assert second["next_velocity"] == (F(31, 10), F(16, 5))
    assert second["update"] == (F(-31, 40), F(-4, 5))
    assert second["next_theta"] == (F(29, 40), F(-4, 5))
    assert second["next_loss"] == F(4937, 3200) > second["loss"]
    # Eliminate velocity to check a different, second-order parameter recurrence.
    for k in range(1, len(frames)):
        previous, current = frames[k-1]["theta"], frames[k]["theta"]
        expected = tuple((1+F(4, 5)-F(1, 4)*lam)*now-F(4, 5)*old
                         for now, old, lam in zip(current, previous, (1, 4)))
        assert frames[k]["next_theta"] == expected
    residual = step((0, 0), (1, -2))
    assert residual["gradient"] == (0, 0)
    assert residual["next_velocity"] == (F(4, 5), F(-8, 5))
    assert residual["next_theta"] == (F(-1, 5), F(2, 5))
    no_memory = step((2, 1), (99, -99), beta=0)
    assert no_memory["next_velocity"] == (2, 4) and no_memory["next_theta"] == first["next_theta"]
    constant_velocity = F(0)
    for n in range(1, 5):
        constant_velocity = F(4, 5)*constant_velocity+1
        assert constant_velocity == (1-F(4, 5)**n)/(1-F(4, 5))
    for invalid in (-1, 1):
        try:
            step((2, 1), beta=invalid)
        except ValueError:
            pass
        else:
            raise AssertionError("Invalid momentum coefficient accepted")
    print("Exact first frame:", first)
    print("Exact second frame:", second)
    print("Momentum can keep moving at zero current gradient and can increase loss on a step.")


if __name__ == "__main__":
    main()
code/python/optimization_momentum_numpy.py
"""Binary64 batch momentum, cross-checked with powers of a four-state transition matrix."""
import numpy as np


def trajectories(starts, iterations=8, eta=0.25, beta=0.8, a=1.0, c=0.0):
    starts = np.asarray(starts, dtype=np.float64)
    if starts.ndim != 2 or starts.shape[1] != 2 or not np.all(np.isfinite(starts)):
        raise ValueError("Require finite starts of shape (n,2)")
    if not np.all(np.isfinite([eta, beta, a, c])) or eta <= 0 or a <= 0 or not 0 <= beta < 1:
        raise ValueError("Require finite parameters, eta>0, a>0, 0<=beta<1")
    if type(iterations) is not int or not 0 <= iterations <= 1000:
        raise ValueError("Require an integer iteration count in [0,1000]")
    center, hessian = np.array([c, 0.0]), np.diag([a, 4.0])
    path = np.empty((iterations+1, len(starts), 2), dtype=np.float64)
    velocity = np.zeros_like(path)
    path[0] = starts
    for k in range(iterations):
        gradient = (path[k]-center) @ hessian
        velocity[k+1] = beta*velocity[k]+gradient
        path[k+1] = path[k]-eta*velocity[k+1]
        if not np.all(np.isfinite(path[k+1])) or not np.all(np.isfinite(velocity[k+1])):
            raise OverflowError(f"Non-finite binary64 state at step {k+1}")
    losses = np.einsum("kni,ij,knj->kn", path-center, hessian, path-center)/2
    if not np.all(np.isfinite(losses)):
        raise OverflowError("Non-finite binary64 loss")
    return path, velocity, losses


def main():
    starts = np.array([[2, 1], [-2, -1], [0, 0]], dtype=np.float64)
    path, velocity, losses = trajectories(starts)
    h, identity = np.diag([1.0, 4.0]), np.eye(2)
    transition = np.block([[identity-0.25*h, -0.25*0.8*identity], [h, 0.8*identity]])
    initial = np.column_stack((starts, np.zeros_like(starts)))
    for k in range(9):
        direct = initial @ np.linalg.matrix_power(transition, k).T
        np.testing.assert_allclose(path[k], direct[:, :2], rtol=0, atol=1e-12)
        np.testing.assert_allclose(velocity[k], direct[:, 2:], rtol=0, atol=1e-12)
    np.testing.assert_allclose(velocity[2, 0], [3.1, 3.2], rtol=0, atol=1e-14)
    np.testing.assert_allclose(path[2, 0], [0.725, -0.8], rtol=0, atol=1e-14)
    np.testing.assert_allclose(losses[:3, 0], [4, 1.125, 1.5428125], rtol=0, atol=1e-14)
    assert losses[2, 0] > losses[1, 0]
    assert np.all(path[:, 2] == 0) and np.all(velocity[:, 2] == 0)
    plain, _, _ = trajectories([[2, 1]], iterations=2, beta=0)
    np.testing.assert_array_equal(plain[:, 0], [[2, 1], [1.5, 0], [1.125, 0]])
    print("Binary64 momentum path for the first start ≈", path[:, 0])
    print("Binary64 velocities ≈", velocity[:, 0])
    print("Matrix-power cross-check: absolute tolerance 1e-12 on this bounded run.")


if __name__ == "__main__":
    main()

Explore: compare damping, history, and displacement

Choose no memory and verify that the second point is (9/8,0)(9/8,0) instead of (29/40,−4/5)(29/40,-4/5). The parameter point at the start of the second step is the same, but the velocity rule is different. Return to the canonical decay and locate the first y sign reversal; compare the current gradient with the accumulated direction before each move.

Reduce the decay, keeping the rate and start fixed. Older gradients now lose influence faster. Predict which terms in vk+1=∑jβk−jgjv_{k+1}=\sum_j\beta^{k-j}g_j change before judging the plotted path. Increasing decay also changes accumulated magnitude under this unnormalized convention, so it is not solely a change in smoothness.

As a hand probe, suppose θ=(0,0)\theta=(0,0) and retained velocity is (1,−2)(1,-2). The current gradient is zero, but the next velocity is (4/5,−8/5)(4/5,-8/5) and next point (−1/5,2/5)(-1/5,2/5). This is a state-level counterexample to treating a zero current gradient as an automatic momentum stop. The explorer’s fresh runs always begin with zero velocity; this probe deliberately supplies nonzero history.

Common mistakes and counterexamples

Do not insert (1−β)(1-\beta) into this chapter’s recurrence. The normalized-average formula can be useful, but mixing it with an unnormalized learning rate changes the displacement. Likewise, multiplying the old velocity by the rate before updating it changes the chosen state convention unless the entire recurrence is transformed consistently.

A zero current gradient does not erase memory. A current gradient sign change need not immediately reverse the updated velocity. A convex objective still permits a momentum loss increase: its whole-domain minimum exists independently of whether this two-state algorithm descends on every step.

Numerical considerations

The exact reference uses rational inputs and arithmetic. Its binary64 counterpart rounds velocity accumulation and parameter addition; decimal values such as 3.13.1 are not exactly representable in binary64. The inspector therefore shows approximation signs even when a hand calculation has a terminating decimal.

Large retained velocities can overflow or create a non-finite displacement before the objective is evaluated at the next point. Check the optimizer state as well as the loss. When β\beta is close to 1, a long persistent-gradient history has large total weight 1/(1−β)1/(1-\beta), making scale and rate choices consequential.

Reproducible replay must preserve the stored velocity together with each parameter point. Reconstructing it from rounded coordinates can alter later updates. The matrix-power cross-check uses absolute tolerance 10−1210^{-12} for the bounded run; this tolerance verifies arithmetic agreement, not convergence for all possible histories.

Engineering and ML connections

The velocity recurrence resembles a first-order filter of a gradient signal: it discounts older samples while retaining direction. In learning systems, keeping optimizer state across updates affects behavior; restarting only the parameters and silently discarding velocity creates a different run.

Learning-rate behavior explains how curvature acts on a one-state method. Momentum changes that recurrence, while stochastic gradients will add sampling variation. Adam also retains a signed first moment, but uses a normalized average, a squared-gradient moment, and bias correction. Similar names or arrows should not hide these different equations.

Exercises

Keep previous velocity, updated velocity, and displacement separate. Verify the loss after a step rather than assuming it must fall.

calculation

Initialize the accumulated gradient

For L=x²/2+2y², θ₀=(2,1), v₀=(0,0), β=4/5, η=1/4, use v₁=βv₀+g₀ and θ₁=θ₀−ηv₁. Give both v₁ components and both θ₁ components.

calculation

Keep the memory in the second step

Now θ₁=(3/2,0), v₁=(2,4), β=4/5 and η=1/4. Give both next-velocity components, both update components, both next coordinates, and the new loss.

prediction

A zero gradient with nonzero memory

At θ=(0,0) on the default quadratic, the current gradient is zero but stored v=(1,−2). With β=4/5 and η=1/4, give next velocity x,y and next coordinates x,y.

explanation

Distinguish two momentum conventions

A scalar gradient stays equal to 1, v₀=0 and β=4/5. For this chapter’s v_next=βv+g, what is the limiting velocity?

Choose one answer

explanation

Interpret a rising loss

The canonical second momentum step raises loss from 9/8 to 4937/3200 on a strictly convex quadratic. Which claim is justified?

Choose one answer

Summary and checkpoints

Momentum updates a decaying accumulated gradient before moving the parameters. Check that you can recover the first two canonical velocities and points, explain why the second y move persists, and account for the loss increase to 4937/32004937/3200. The optimizer’s memory is mathematical state; a point and a current gradient alone no longer determine its next displacement.

The map of the book

97 of 97 chapters written · eight Parts

demo Foundation demo Try the live vector example Open as a page

Part I

Mathematical Language

The notation and mental objects everything else uses.

9 of 9 chapters written

  1. I.1 Scalars, quantities, units, powers and roots available
  2. I.2 Coordinates and points available
  3. I.3 Vectors: components, magnitude, and direction reference chapter available
  4. I.4 Algebraic expressions, equations and inequalities available
  5. I.5 Functions as input → transformation → output available
  6. I.6 Essential function families available
  7. I.7 Sets, intervals, and relations available
  8. I.8 Indexed quantities and summation available
  9. I.9 The notation map for ML available

Part II

Linear Algebra

Matrices as transformations: the strongest foundation in the book.

19 of 19 chapters written

  1. II.1 Vector addition and subtraction available
  2. II.2 Scalar multiplication and vector scaling available
  3. II.3 Norms, magnitude, and normalization available
  4. II.4 Dot product, angle, and orthogonality available
  5. II.5 Projection and distance to a direction available
  6. II.6 Linear combinations and span available
  7. II.7 Linear independence, basis, and coordinates available
  8. II.8 Matrices as linear transformations available
  9. II.9 Matrix × vector available
  10. II.10 Matrix × matrix as composition available
  11. II.11 Transpose and orthogonality available
  12. II.12 Determinant: orientation and area/volume scaling available
  13. II.13 Linear systems as intersecting constraints available
  14. II.14 Gaussian elimination and row operations available
  15. II.15 Rank and invertibility available
  16. II.16 Inverse matrices available
  17. II.17 Eigenvalues and eigenvectors available
  18. II.18 Orthogonal bases and least-squares geometry available
  19. II.19 Singular Value Decomposition available

Part III

Calculus

Change, local approximation, and sensitivity made visible.

15 of 15 chapters written

  1. III.1 Limits through approach available
  2. III.2 Continuity and local behavior available
  3. III.3 Derivative as secant → tangent available
  4. III.4 Derivative as rate of change and local linear model available
  5. III.5 Derivative rules available
  6. III.6 Chain rule available
  7. III.7 Multivariable functions and partial derivatives available
  8. III.8 Gradient and directional derivatives available
  9. III.9 Jacobian available
  10. III.10 Computational graphs and automatic differentiation available
  11. III.11 Hessian and curvature available
  12. III.12 Integrals as accumulation available
  13. III.13 Fundamental derivative/integral connection available
  14. III.14 Numerical differentiation available
  15. III.15 Numerical integration available

Part IV

Probability

Uncertainty through sample spaces, evidence, and repetition.

16 of 16 chapters written

  1. IV.1 Random experiments, outcomes, and sample spaces available
  2. IV.2 Events and probability mass available
  3. IV.3 Counting and finite probability where needed available
  4. IV.4 Conditional probability as restricted sample space available
  5. IV.5 Independence available
  6. IV.6 Bayes' theorem as probability-mass update available
  7. IV.7 Random variables available
  8. IV.8 PMF, PDF, and CDF available
  9. IV.9 Bernoulli and Binomial distributions available
  10. IV.10 Uniform and Gaussian distributions available
  11. IV.11 Expectation available
  12. IV.12 Variance and standard deviation available
  13. IV.13 Joint, marginal, and conditional distributions available
  14. IV.14 Covariance and correlation available
  15. IV.15 Law of Large Numbers available
  16. IV.16 Central Limit Theorem available

Part V

Statistics

From observed data to estimates, intervals, and likelihood.

10 of 10 chapters written

  1. V.1 Data summaries: mean, median, mode available
  2. V.2 Spread: variance, standard deviation, robust interpretation available
  3. V.3 Population, sample, and sampling variability available
  4. V.4 Estimators and estimation available
  5. V.5 Confidence intervals through repeated sampling available
  6. V.6 Covariance matrices and multivariate spread available
  7. V.7 Regression fundamentals available
  8. V.8 Likelihood available
  9. V.9 Maximum Likelihood Estimation available
  10. V.10 Maximum A Posteriori estimation available

Part VI

Optimization

Learning as repeated parameter updates governed by an objective.

10 of 10 chapters written

  1. VI.1 Objective functions and optimization landscapes available
  2. VI.2 Local vs global optima and stationary points available
  3. VI.3 Convexity available
  4. VI.4 Gradient descent flagship chapter available
  5. VI.5 Learning rate and convergence behavior available
  6. VI.6 Momentum available
  7. VI.7 Stochastic and mini-batch gradient descent available
  8. VI.8 Adam intuition available
  9. VI.9 Constraints and projected/penalized intuition available
  10. VI.10 Numerical stopping and optimization diagnostics available

Part VII

Information Theory

Logarithms, entropy, and the loss behind token prediction.

8 of 8 chapters written

  1. VII.1 Logarithms and log probabilities in computation available
  2. VII.2 Self-information available
  3. VII.3 Entropy available
  4. VII.4 Cross entropy and negative log-likelihood available
  5. VII.5 KL divergence available
  6. VII.6 Logits and softmax available
  7. VII.7 Numerical stability: log-sum-exp and stable softmax available
  8. VII.8 From token logits to language-model loss available

Part VIII

Numerical Computing

Why equivalent expressions behave differently on real machines.

10 of 10 chapters written

  1. VIII.1 Floating-point representation available
  2. VIII.2 Rounding and representation error available
  3. VIII.3 Error propagation and conditioning available
  4. VIII.4 Catastrophic cancellation available
  5. VIII.5 Overflow and underflow available
  6. VIII.6 Stable algorithms available
  7. VIII.7 Numerical linear algebra realities available
  8. VIII.8 Sparse matrices and sparse structure available
  9. VIII.9 Vectorized computation available
  10. VIII.10 Reproducibility, random seeds, and nondeterminism available