Skip to content

optora.solvers.gradient_descent

Gradient descent solver for differentiable objectives, with warm starts.

GradientDescent

Bases: Solver[MinimizationProblem, MinimizationResult]

Fixed-step-size gradient descent for a differentiable objective.

Repeatedly steps the current point against the objective's gradient,

\[ \mathrm{point} \leftarrow \mathrm{point} - \mathrm{step\_size} \cdot \nabla\, \mathrm{objective}(\mathrm{point}), \]

until the gradient norm falls below tol or max_iter steps are exhausted. Solves MinimizationProblem, the shared unconstrained-minimization contract, so it can be injected wherever a solver of that problem class is expected — for example as the inner dual solver of an optora.dro ambiguity set.

The gradient-norm test is evaluated on the iterate's device and read back to the host only every check_interval steps; steps taken after convergence are frozen, so the solution does not depend on that interval. Keep check_interval=1 when the objective is expensive, as it is when this solver is the outer solver of an optora.dro.MinimaxSolver and every step costs an inner dual solve. See optora.core.convergence.ConvergenceTracker. The returned diagnostics stay on the device as well (see optora.core.convergence.ConvergenceStatus), so a solve whose converged and num_iterations nobody reads — every inner dual solve of an optora.dro ambiguity set — never synchronizes for them.

The point is a single tensor of any shape, and the stopping rule is the norm of the whole gradient. A batch of independent problems stacked into one point (as an optora.dro ambiguity set does for a batched loss) is therefore stopped jointly: iteration continues until every element is stationary. Per-element early exit is deliberately not offered, since retiring elements individually needs a host-side read of a per-element mask on every step, which is exactly the synchronization ConvergenceTracker exists to avoid. The frozen-step rule makes the extra steps exact no-ops for elements that already converged.

Attributes:

Name Type Description
step_size

Positive learning rate applied to each gradient step.

max_iter

Maximum number of gradient steps.

tol

Convergence tolerance on the gradient norm.

check_interval

Number of steps between host reads of the convergence flag.

__init__(step_size=0.01, max_iter=1000, tol=1e-06, check_interval=DEFAULT_CHECK_INTERVAL)

Initialize the gradient descent solver.

Parameters:

Name Type Description Default
step_size float

Positive learning rate applied to each gradient step.

0.01
max_iter int

Maximum number of gradient steps.

1000
tol float

Convergence tolerance on the gradient norm.

1e-06
check_interval int

Number of steps between host reads of the convergence flag. Raise it to trade redundant frozen steps for fewer device synchronizations, but only when a step is cheap relative to a synchronization.

DEFAULT_CHECK_INTERVAL

Raises:

Type Description
ValueError

If step_size, max_iter, tol, or check_interval are not positive.

solve(problem)

Minimize problem.objective starting from problem.initial_point.

Parameters:

Name Type Description Default
problem MinimizationProblem

Objective and initial point to solve from.

required

Returns:

Type Description
MinimizationResult

A MinimizationResult holding the final iterate and

MinimizationResult

convergence diagnostics.

Raises:

Type Description
ValueError

If problem.objective does not depend on its argument through autograd.