Skip to content

Optimization Tutorial

cds.optimization provides four pure-Python optimizers that work on both scalar and vector objectives. Each returns an OptResult dataclass reporting the minimizer, the objective value, the iteration count, a convergence flag, and (for Adam) a resumable state checkpoint.

1. Gradient Descent

First-order minimization. Pass a scalar or vector starting point, the return type tracks the input (OptResult[float] for a scalar x0, OptResult[list[float]] for a list).

from cds.optimization import gradient_descent

# Scalar: minimize (x-3)^2 starting from x=10
res = gradient_descent(lambda x: (x - 3) ** 2, x0=10.0, lr=0.1)
print(f"x={res.x:.6f}, f(x)={res.value:.2e}, iters={res.iterations}, converged={res.converged}")
# x=3.000000, f(x)=1.89e-17, iters=95, converged=True

# Vector: minimize a bowl x0^2 + x1^2
res = gradient_descent(lambda v: v[0] ** 2 + v[1] ** 2, x0=[5.0, -3.0], lr=0.1)
print(res.x)  # ≈ [0.0, 0.0]

2. Newton's Method (root finding)

Second-order root finding for scalar f. Returns the x where f(x) ≈ 0.

from cds.optimization import newton_method

res = newton_method(lambda x: x**3 - 2, x0=2.0)
print(f"cbrt(2) ≈ {res.x:.10f}  (true = {2 ** (1 / 3):.10f})")
# cbrt(2) ≈ 1.2599210499  (true = 1.2599210499)

3. Adam

Adaptive learning-rate optimizer. Stateful: pass the returned state back in to resume a run, and supply an analytic gradient via grad_f when you have one.

import math
from cds.optimization import adam

res = adam(lambda x: math.sin(x) + 0.1 * x**2, x0=2.0, lr=0.05)
print(f"x={res.x:.6f}, f(x)={res.value:.6f}")
# x=3.837467, f(x)=0.831559

4. Line Search (golden section)

Bracketed scalar minimization. Give it an interval [a, b] assumed to be unimodal.

from cds.optimization import line_search

res = line_search(lambda x: (x - 2.5) ** 4, a=0, b=5)
print(f"min at x={res.x:.8f}")  # min at x=2.50000000

5. Nelder–Mead Simplex (derivative-free)

Derivative-free minimization for smooth or noisy vector objectives where you cannot supply gradients. Convergence requires both the objective spread and the simplex diameter to fall below tolerance.

from cds.optimization import nelder_mead


def rosenbrock(v):
    x, y = v
    return (1.0 - x) ** 2 + 100.0 * (y - x * x) ** 2


res = nelder_mead(rosenbrock, [-1.2, 1.0], step=0.5)
print(res.x)  # ≈ [1.0, 1.0], converged=True

6. Simulated Annealing (global search, seeded)

Escapes local minima via Metropolis acceptance with an exponentially cooling temperature schedule. Deterministic given seed; optional per-axis box bounds.

import math
from cds.optimization import simulated_annealing


def bumpy(v):
    return math.sin(3.0 * v[0]) + 0.2 * v[0] ** 2  # two basins


res = simulated_annealing(
    bumpy, [-1.5], t_init=2.0, cooling=0.99, sigma=0.5, max_iter=20_000, seed=42
)
print(res.value < bumpy([-1.5]) - 0.3)  # True — escaped the local basin

Run the full demo with python examples/optimization_demo.py.