pyepo.func.surrogate

Surrogate Loss function

Classes

SPOPlus

SPO+ loss: a convex surrogate for the SPO regret of a linear-objective LP.

SPOPlusFunc

An autograd function for SPO+ Loss

perturbationGradient

Perturbation Gradient (PG): zeroth-order surrogate of the objective-value loss.

Module Contents

class pyepo.func.surrogate.SPOPlus(optmodel: pyepo.model.opt.optModel, processes: int = 1, solve_ratio: float = 1.0, reduction: pyepo.func.abcmodule.Reduction = 'mean', dataset: pyepo.data.dataset.optDataset | None = None)

Bases: pyepo.func.abcmodule.optModule

SPO+ loss: a convex surrogate for the SPO regret of a linear-objective LP.

SPO+ upper-bounds the SPO regret with a convex function of the predicted cost vector and provides an informative subgradient (via Danskin’s theorem) for end-to-end training. It is the strong default for predict-then-optimize when true optimal solutions \(\mathbf{w}^*(\mathbf{c})\) are available as supervision.

The forward pass solves the perturbed problem with cost \(2\hat{\mathbf{c}} - \mathbf{c}\) once per training instance and returns a scalar loss; the backward pass uses the cached solution to form the subgradient \(2(\mathbf{w}^*(\mathbf{c}) - \mathbf{w}^*(2\hat{\mathbf{c}} - \mathbf{c}))\) without any extra solver call.

Reference: Elmachtoub & Grigas (2022) https://doi.org/10.1287/mnsc.2020.3922

forward(pred_cost: torch.Tensor, true_cost: torch.Tensor, true_sol: torch.Tensor, true_obj: torch.Tensor) torch.Tensor

Forward pass

class pyepo.func.surrogate.SPOPlusFunc(*args, **kwargs)

Bases: torch.autograd.Function

An autograd function for SPO+ Loss

static forward(ctx, pred_cost: torch.Tensor, true_cost: torch.Tensor, true_sol: torch.Tensor, true_obj: torch.Tensor, module: SPOPlus) torch.Tensor

Forward pass for SPO+

Parameters:
  • pred_cost – a batch of predicted values of the cost

  • true_cost – a batch of true values of the cost

  • true_sol – a batch of true optimal solutions

  • true_obj – a batch of true optimal objective values

  • module – SPOPlus module

Returns:

SPO+ loss

Return type:

torch.tensor

static backward(ctx, grad_output: torch.Tensor) tuple[torch.Tensor | None, Ellipsis]

Backward pass for SPO+

class pyepo.func.surrogate.perturbationGradient(optmodel: pyepo.model.opt.optModel, sigma: float = 0.1, two_sides: bool = False, processes: int = 1, solve_ratio: float = 1.0, reduction: pyepo.func.abcmodule.Reduction = 'mean', dataset: pyepo.data.dataset.optDataset | None = None)

Bases: pyepo.func.abcmodule.optModule

Perturbation Gradient (PG): zeroth-order surrogate of the objective-value loss.

PG approximates the directional derivative of \(z^*(\hat{\mathbf{c}})\) along the true cost \(\mathbf{c}\) with a finite difference, yielding an informative gradient through the otherwise piecewise-constant solver layer. Two variants are exposed via two_sides: backward differencing (False, one extra solve per step) and central differencing (True, two extra solves but more accurate gradients).

Unlike SPO+, PG does not require true optimal solutions – it only needs the true cost vector \(\mathbf{c}\).

Reference: Gupta & Huang (2024) https://arxiv.org/abs/2402.03256

sigma = 0.1
two_sides = False
forward(pred_cost: torch.Tensor, true_cost: torch.Tensor) torch.Tensor

Forward pass