Optimization1847foundational10 min read

Méthode Générale pour la Résolution des Systèmes d'Équations Simultanées

الطريقة العامة لحل جُمَل المعادلات المتزامنة

Cauchy, A.-L. — Comptes Rendus de l'Académie des Sciences

The problem

In the mid-19th century, astronomers needed to solve large systems of equations — six or more unknowns — arising from celestial mechanics. The existing algebraic methods were exact but impractical: the computation grew explosively with the number of variables. There was no systematic, iterative procedure that could find approximate solutions to general nonlinear systems while guaranteeing steady progress toward the answer.

The contribution

Cauchy proposed a breathtakingly simple idea: to solve f(x₁,…,xₙ) = 0 where f ≥ 0, compute the ∇f — the vector of partial derivatives pointing uphill — then take a small step in the opposite direction. Each step is guaranteed to reduce f (for a small enough step), so the iterates march steadily toward a solution. This is the method of steepest descent, the ancestor of every gradient-based in modern machine learning.

The impact

The most consequential idea in history. Every ever trained — every CNN, every Transformer, every language model — learns by some variant of Cauchy's 1847 insight: compute the gradient, step opposite to it, repeat. , , RMSProp, AdaGrad — all are descendants. is, at its core, Cauchy's hill-descent scaled to billions of parameters with providing the gradients.

Imagine you are a blindfolded hiker on a mountain, trying to reach the lowest valley. You cannot see the landscape, but you can feel the slope under your boots.

Your strategy is simple: at every step, feel which direction is steepest downhill, then take a small step that way. You don't need a map — just the local slope and the discipline to keep stepping downward.

That is exactly what Cauchy proposed in 1847: replace the mountain with a mathematical function, replace "feeling the slope" with computing the gradient, and replace each footstep with an update rule. The method works for any number of dimensions — even millions — which is why every neural network in the world learns this way today.

The problem: too many unknowns, too much computation

In Cauchy's time, astronomers wrestled with systems of equations arising from planetary motion — often six or more unknowns. Direct algebraic methods (like Gaussian elimination) worked beautifully for small systems, but their computational cost grew rapidly with size:

  • Direct methods require on the order of n³ arithmetic operations, where n is the number of unknowns. For n = 6, that's already 216 operations — done by hand.

  • No computers existed. Every multiplication was done with pen and paper. A method that required fewer total operations, even if approximate, was desperately needed.

  • Nonlinear systems — where variables multiply each other or appear inside trigonometric functions — had no general solution procedure at all.

Cauchy's insight was that you don't need to solve the system in one shot. You can creep toward the solution by repeatedly reducing a single scalar quantity: the value of a function that measures how far you are from the answer.

Open in Lab
Drag the starting point, then press "Descend" to watch gradient descent trace a path toward the minimum. Try different starting positions.
The demo wakes as you arrive…

The idea: always step downhill

Cauchy's method starts from one critical observation from calculus: the gradient of a function at a point tells you the direction of steepest ascent. Therefore, the negative gradient tells you the direction of steepest descent.

Here is the full procedure:

  1. Start at an initial guess x0x_0.
  2. Compute the gradient ∇f(x)\nabla f(x) — the vector of all partial derivatives.
  3. Take a step in the opposite direction: xt+1=xt−γ∇f(xt)x_{t+1} = x_t - \gamma \nabla f(x_t).
  4. Repeat until the function value stops changing significantly.

The step size γ\gamma (called the in modern machine learning) controls how far you move per step. Too large and you overshoot; too small and you crawl. Cauchy recognized this tension and proposed choosing γ\gamma to minimize f along the descent direction — what we now call a .

The update rule

Before the formula: the goal of each iteration is to find a new point xt+1x_{t+1} where the function value is lower than at xtx_t. The strategy is to move in the direction that decreases f the fastest — the negative gradient. The remaining question is how far to move.

xt+1=xt−γ ∇f(xt)x_{t+1} = x_t - \gamma \, \nabla f(x_t)
The gradient descent update rule — Gradient descent improves a solution through a sequence of small corrections. At each step, it measures which direction would increase the objective most rapidly, then moves in the opposite direction to reduce the objective instead. The size of each move is controlled by a learning rate: steps that are too small lead to slow progress, while steps that are too large may overshoot good solutions. Repeating this process gradually moves the parameters toward a minimum of the objective function.

Think of it as a compass rule: the gradient says "uphill is that way," so you turn 180° and walk. The step size γ\gamma decides whether you take a cautious shuffle or a bold stride. Cauchy proposed choosing γ\gamma optimally at each step by minimizing f(xt−γ∇f(xt))f(x_t - \gamma \nabla f(x_t)) as a function of γ\gamma — an exact line search. Modern deep learning typically uses a fixed or scheduled γ\gamma instead, because exact line search is too expensive with millions of parameters.

Open in Lab
Drag the learning rate slider and watch how step size affects convergence. Too large = divergence. Too small = slow crawl.
The demo wakes as you arrive…

Why it works: guaranteed descent

The mathematical guarantee behind comes directly from the Taylor expansion. For a small enough step size γ>0\gamma > 0:

f(x−γ∇f)≈f(x)−γ∥∇f∥2f(x - \gamma \nabla f) \approx f(x) - \gamma \|\nabla f\|^2

Since ∥∇f∥2≥0\|\nabla f\|^2 \geq 0, every step decreases (or at worst holds) the function value. The function keeps falling, step after step, until the gradient becomes zero — meaning you have reached a point where no direction leads downhill. That is a : a , a , or (if you're lucky) the .

Crucially, Cauchy showed that this works regardless of how many variables the function has. The gradient always exists for smooth functions, and the negative gradient always points downhill. The method scales to any dimensionality.

Open in Lab
Watch gradient descent iterate on a 2D quadratic. Each arrow shows the negative gradient. Notice the zig-zag pattern on ill-conditioned functions.
The demo wakes as you arrive…

The weakness: zig-zagging on narrow valleys

Gradient descent has a well-known pathology: on functions shaped like a narrow valley (technically, when the eigenvalues of the matrix are very different), the method zig-zags back and forth across the valley instead of sliding along it.

This happens because the steepest local direction at the valley wall points across the valley, not along it. Each step overshoots the narrow axis and lands on the opposite wall. The iterates oscillate, making slow progress.

This zig-zag problem motivated 170 years of improvements: conjugate gradients (1952), Newton's method with Hessian, (1964), and modern adaptive optimizers like Adam (2014). All of them modify Cauchy's basic recipe to handle ill-conditioned landscapes better — but the core insight remains the same: use the gradient to decide where to step next.

Open in Lab
Adjust the valley's eccentricity and watch how gradient descent zig-zags more as the valley narrows. Compare with momentum, which smooths the path.
The demo wakes as you arrive…

The same idea in code

Gradient descent, completepython

Simplified to show the idea — not the real implementation.

import numpy as np

def gradient_descent(f, grad_f, x0, lr=0.01, tol=1e-6, max_iter=1000):
    """Cauchy's 1847 algorithm in 10 lines.
    f       : objective function to minimize
    grad_f  : function returning the gradient ∇f(x)
    x0      : starting point (numpy array)
    lr      : step size γ (learning rate)
    """
    x = x0.copy()
    for i in range(max_iter):
        g = grad_f(x)                      # compute the gradient
        if np.linalg.norm(g) < tol:        # gradient ≈ 0 → stop
            break
        x = x - lr * g                     # the update: step opposite the gradient
    return x

# Example: minimize f(x,y) = x² + 10y² (a narrow valley)
f      = lambda x: x[0]**2 + 10*x[1]**2
grad_f = lambda x: np.array([2*x[0], 20*x[1]])

result = gradient_descent(f, grad_f, x0=np.array([5.0, 3.0]), lr=0.05)
print(f"Minimum at: {result}")   # → close to [0, 0]

# Every neural network optimizer is a descendant of this loop.
# SGD adds noise (random mini-batches).
# Adam adds momentum and per-parameter adaptive rates.
# But the core is always: x ← x − γ · ∇f(x).

From 1847 to today: the gradient descent family tree

Cauchy's original method spawned an entire family of optimization algorithms, each addressing one of its limitations. The genealogy connects directly to every optimizer used in deep learning today:

  1. 1847

    Cauchy: Gradient Descent

    The original method — move opposite the gradient with a step size chosen by exact line search. Simple, general, but slow on ill-conditioned problems.

  2. 1951

    Robbins & Monro: Stochastic Approximation

    Showed that gradient descent still works when gradients are noisy — the theoretical foundation for SGD. The step size must shrink over time.

  3. 1952

    Hestenes & Stiefel: Conjugate Gradients

    Eliminated zig-zagging by choosing each direction to be conjugate to the previous ones. Solved quadratics in n steps exactly.

  4. 1964

    Polyak: Momentum

    Added a "velocity" term that accumulates past gradients, helping the optimizer roll through narrow valleys instead of zig-zagging.

  5. 1986

    Rumelhart, Hinton & Williams: Backpropagation

    Made gradient descent practical for deep networks by efficiently computing ∇f via the chain rule — backward through every layer.

  6. 2011

    Duchi et al.: AdaGrad

    Gave each parameter its own adaptive learning rate based on accumulated squared gradients. Solved the problem of rare features in NLP.

  7. 2014

    Kingma & Ba: Adam

    Combined momentum with adaptive rates and bias correction. Became the default optimizer for deep learning.

Convergence: when does gradient descent find the answer?

The speed depends on the function's shape. For a convex quadratic f(x)=12xTAxf(x) = \frac{1}{2} x^T A x, the error at step tt satisfies:

f(xt)−f(x∗)≤(κ−1κ+1)2t(f(x0)−f(x∗))f(x_t) - f(x^*) \leq \left(\frac{\kappa - 1}{\kappa + 1}\right)^{2t} (f(x_0) - f(x^*))

where κ=λmax⁡/λmin⁡\kappa = \lambda_{\max} / \lambda_{\min} is the of AA — the ratio of its largest to its smallest. The larger κ\kappa, the more elongated the contours and the worse the zig-zagging. For a well-conditioned function (κ≈1\kappa \approx 1), convergence is fast. For an ill-conditioned function (κ≫1\kappa \gg 1), convergence is painfully slow.

This is the fundamental trade-off: gradient descent is general (works on any smooth function) but first-order (uses only the gradient, not curvature information). Methods that use second-order information (Hessian) converge faster but are far more expensive per step.

κ(A)=λmax⁡(A)λmin⁡(A)\kappa(A) = \frac{\lambda_{\max}(A)}{\lambda_{\min}(A)}
Condition number — the shape metric that controls convergence — The condition number measures how difficult an optimization problem is for gradient-based methods. When the landscape is well balanced, progress toward the optimum is direct and convergence is rapid. When the landscape is highly stretched in some directions and compressed in others, gradient descent tends to bounce back and forth, making slow progress toward the solution. Smaller condition numbers therefore correspond to easier and faster optimization, while larger condition numbers indicate a more challenging problem.
Open in Lab
Adjust the condition number κ and watch the contour ellipses stretch. Gradient descent needs more steps as κ grows.
The demo wakes as you arrive…

Why this matters for deep learning

Every neural network loop is a direct descendant of Cauchy's algorithm:

  1. — compute the loss L(θ)L(\theta) (how wrong the predictions are).
  2. — compute ∇θL\nabla_\theta L using backpropagation (the chain rule).
  3. Update — θ←θ−γ∇θL\theta \leftarrow \theta - \gamma \nabla_\theta L (Cauchy's step).

The LL plays the role of Cauchy's ff, the model parameters θ\theta replace his variables (x1,…,xn)(x_1, \ldots, x_n), and the learning rate γ\gamma is his step size — except modern networks have billions of parameters instead of six, and the gradients come from mini-batches (stochastic gradient descent) rather than the full dataset.

The entire deep learning revolution rests on three legs: Cauchy's gradient descent (1847), Rumelhart/Hinton/Williams' backpropagation (1986) to compute the gradient efficiently, and hardware to execute the loop fast enough. Remove any one of these, and modern AI does not exist.

CitationCauchy, A.-L.. Méthode générale pour la résolution des systèmes d'équations simultanées. Comptes Rendus de l'Académie des Sciences, Paris, 1847.

Terms in this paper