Skip to content

optora.dro.minimax_solver

Minimax solver wiring a divergence-based ambiguity set and a solver together.

MinimaxProblem dataclass

Distributionally robust minimization problem solved by MinimaxSolver.

Represents the DRO minimax problem

\[ \min_x \; \sup_{q:\, \mathrm{divergence}(q, \mathrm{nominal}) \,\le\, \mathrm{radius}} \mathbb{E}_q[\mathrm{loss\_fn}(x)] \]

Every AmbiguitySet subclass (KLAmbiguitySet, PhiAmbiguitySet, ChiSquareAmbiguitySet, TotalVariationAmbiguitySet, WassersteinAmbiguitySet) already reduces its inner supremum over candidate distributions q to a tractable convex dual objective or an exact closed form via worst_case_expectation. MinimaxProblem therefore only needs to describe the remaining outer minimization over the decision variable x.

Attributes:

Name Type Description
ambiguity_set AmbiguitySet

Divergence-based ambiguity set whose worst_case_expectation computes the inner supremum over candidate distributions.

loss_fn Callable[[Tensor], Tensor]

Differentiable per-scenario loss as a function of the decision variable, returning a tensor shaped like ambiguity_set.nominal (one entry per support point).

initial_point Tensor

Starting point for the decision variable.

MinimaxResult dataclass

Bases: ConvergenceDiagnostics

Outcome of a MinimaxSolver solve.

Attributes:

Name Type Description
point Tensor

Final decision-variable iterate.

value Tensor

Worst-case expected loss at point, i.e. ambiguity_set.worst_case_expectation(loss_fn(point)).

status ConvergenceStatus

Convergence diagnostics of the outer solve, exposed on the host as converged and num_iterations by ConvergenceDiagnostics and read back from the device only when one of those is accessed.

converged property

Whether the convergence criterion was met before the iteration budget.

num_iterations property

Number of iterations actually performed.

MinimaxSolver

Bases: Solver[MinimaxProblem, MinimaxResult]

Solves the DRO minimax problem by delegating to an ambiguity set's dual.

Wires together a divergence-based AmbiguitySet and an (outer) Solver to solve

\[ \min_x \; \sup_{q:\, \mathrm{divergence}(q, \mathrm{nominal}) \,\le\, \mathrm{radius}} \mathbb{E}_q[\mathrm{loss\_fn}(x)] \]

Because every AmbiguitySet subclass already reformulates its inner supremum as a tractable convex dual objective (or an exact closed form) via worst_case_expectation(loss), the composed function x -> ambiguity_set.worst_case_expectation(loss_fn(x)) is itself a plain differentiable scalar objective of x, so the remaining work is an ordinary minimization over x. worst_case_expectation stays differentiable with respect to x by the envelope theorem: each ambiguity set that needs an inner dual solve (KLAmbiguitySet, PhiAmbiguitySet, ChiSquareAmbiguitySet, WassersteinAmbiguitySet) finds its own dual variable in a detached inner solve, then re-evaluates the dual objective at that (detached) optimum with the still-attached loss tensor; since the dual objective's gradient with respect to its own dual variable vanishes at that optimum, the gradient of the re-evaluated objective with respect to x is exactly the gradient one would get by differentiating through the full inner solve, without actually needing to do so.

MinimaxSolver therefore reduces to wiring that composed objective into solver, the outer solver minimizing over x. This deliberately does not reimplement a primal-dual ascent-descent directly over the full candidate distribution q (as SaddlePointSolver does generically): every concrete AmbiguitySet already reduces that potentially high- dimensional inner maximization to a low-dimensional convex dual (or an exact closed form), which is more numerically direct than a generic ascent-descent over the simplex.

One outer iteration therefore costs an entire inner dual solve, which is orders of magnitude more expensive than the host synchronization a convergence check costs. Configure solver with check_interval=1: the library-wide default optora.core.convergence.DEFAULT_CHECK_INTERVAL is tuned for cheap inner loops and would keep running frozen — and therefore wasted — inner solves after the outer loop has converged.

Attributes:

Name Type Description
solver

Solver minimizing the composed worst-case-expectation objective over the decision variable. Should use check_interval=1.

__init__(solver)

Initialize the minimax solver.

Parameters:

Name Type Description Default
solver Solver[MinimizationProblem, MinimizationResult]

Solver minimizing the composed worst-case-expectation objective over the decision variable. Should use check_interval=1, since one of its iterations costs a full inner dual solve.

required

solve(problem)

Solve the DRO minimax problem described by problem.

Parameters:

Name Type Description Default
problem MinimaxProblem

Ambiguity set, per-scenario loss function, and initial decision-variable point to solve from.

required

Returns:

Type Description
MinimaxResult

A MinimaxResult holding the robust-optimal decision variable

MinimaxResult

and convergence diagnostics from solver.