pyepo.func.perturbed

Perturbed optimization function

Classes

perturbedOpt

Differentiable Perturbed Optimizer (DPO) -- additive-Gaussian variant.

perturbedOptFunc

An autograd function for perturbed optimizer

perturbedOptMul

Multiplicative-perturbation variant of perturbedOpt for sign-sensitive oracles.

perturbedFenchelYoung

Perturbed Fenchel-Young loss (PFYL) -- additive-Gaussian variant.

perturbedFenchelYoungFunc

An autograd function for Fenchel-Young loss using perturbation techniques.

perturbedFenchelYoungMul

Multiplicative-perturbation variant of perturbedFenchelYoung for sign-sensitive oracles.

implicitMLE

Implicit Maximum Likelihood Estimator (I-MLE) via perturb-and-MAP.

implicitMLEFunc

An autograd function for Implicit Maximum Likelihood Estimator

adaptiveImplicitMLE

Adaptive Implicit MLE (AI-MLE): I-MLE with an adaptive interpolation step.

adaptiveImplicitMLEFunc

An autograd function for Adaptive Implicit Maximum Likelihood Estimator

Module Contents

class pyepo.func.perturbed.perturbedOpt(optmodel: pyepo.model.opt.optModel, n_samples: int = 10, sigma: float = 1.0, processes: int = 1, seed: int = 135, variance_reduction: bool = True, solve_ratio: float = 1.0, dataset: pyepo.data.dataset.optDataset | None = None)

Bases: pyepo.func.abcmodule.optModule

Differentiable Perturbed Optimizer (DPO) – additive-Gaussian variant.

Estimates the expected solution \(\mathbb{E}_{\boldsymbol{\xi}}[\mathbf{w}^*(\hat{\mathbf{c}} + \sigma\boldsymbol{\xi})]\) by Monte Carlo averaging over n_samples Gaussian perturbations of the predicted cost vector. The smoothed map varies continuously with \(\hat{\mathbf{c}}\) – small perturbations only re-weight the distribution over active vertices – giving an informative gradient where the bare LP solver gives zero.

Returns a solution, not a loss: the user supplies a task loss (MSE against \(\mathbf{w}^*(\mathbf{c})\) is the standard choice). For sign-sensitive oracles, use perturbedOptMul instead.

Reference: Berthet et al. (2020) https://papers.nips.cc/paper/2020/hash/6bb56208f672af0dd65451f869fedfd9-Abstract.html

n_samples = 10
sigma = 1.0
seed = 135
variance_reduction = True
forward(pred_cost: torch.Tensor) torch.Tensor

Forward pass

class pyepo.func.perturbed.perturbedOptFunc(*args, **kwargs)

Bases: torch.autograd.Function

An autograd function for perturbed optimizer

static forward(ctx, pred_cost: torch.Tensor, module: perturbedOpt) torch.Tensor

Forward pass for perturbed

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

  • module – perturbedOpt module

Returns:

solution expectations with perturbation

Return type:

torch.tensor

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

Backward pass for perturbed

class pyepo.func.perturbed.perturbedOptMul(optmodel: pyepo.model.opt.optModel, n_samples: int = 10, sigma: float = 1.0, processes: int = 1, seed: int = 135, variance_reduction: bool = True, solve_ratio: float = 1.0, dataset: pyepo.data.dataset.optDataset | None = None)

Bases: perturbedOpt

Multiplicative-perturbation variant of perturbedOpt for sign-sensitive oracles.

Replaces additive noise with the multiplicative perturbation \(\hat{\mathbf{c}} \odot \exp(\sigma\boldsymbol{\xi} - \tfrac{1}{2}\sigma^2)\), which preserves the sign of each cost entry – required when the solver expects, e.g., strictly nonnegative edge costs. Predicted costs must already carry the intended nonzero sign; for nonnegative problems pair this module with a positive-output prediction layer (e.g. nn.Softplus() plus a small epsilon).

Reference: Dalle et al. (2022) https://arxiv.org/abs/2207.13513

class pyepo.func.perturbed.perturbedFenchelYoung(optmodel: pyepo.model.opt.optModel, n_samples: int = 10, sigma: float = 1.0, processes: int = 1, seed: int = 135, solve_ratio: float = 1.0, reduction: pyepo.func.abcmodule.Reduction = 'mean', dataset: pyepo.data.dataset.optDataset | None = None)

Bases: pyepo.func.abcmodule.optModule

Perturbed Fenchel-Young loss (PFYL) – additive-Gaussian variant.

Pairs the same Monte-Carlo expected perturbed solution as perturbedOpt with the Fenchel-Young loss against the true optimum \(\mathbf{w}^*(\mathbf{c})\), returning a scalar loss directly – no user-defined task loss needed. The gradient collapses to the simple residual \(\mathbf{w}^*(\mathbf{c}) - \mathbb{E}_{\boldsymbol{\xi}} [\mathbf{w}^*(\hat{\mathbf{c}} + \sigma\boldsymbol{\xi})]\), so no explicit Jacobian through the solver is required. For sign-sensitive oracles, use perturbedFenchelYoungMul.

Reference: Berthet et al. (2020) https://papers.nips.cc/paper/2020/hash/6bb56208f672af0dd65451f869fedfd9-Abstract.html

n_samples = 10
sigma = 1.0
seed = 135
forward(pred_cost: torch.Tensor, true_sol: torch.Tensor) torch.Tensor

Forward pass

class pyepo.func.perturbed.perturbedFenchelYoungFunc(*args, **kwargs)

Bases: torch.autograd.Function

An autograd function for Fenchel-Young loss using perturbation techniques.

static forward(ctx, pred_cost: torch.Tensor, true_sol: torch.Tensor, module: perturbedFenchelYoung) torch.Tensor

Forward pass for perturbed Fenchel-Young loss

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

  • true_sol – a batch of true optimal solutions

  • module – perturbedFenchelYoung module

Returns:

solution expectations with perturbation

Return type:

torch.tensor

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

Backward pass for perturbed Fenchel-Young loss

class pyepo.func.perturbed.perturbedFenchelYoungMul(optmodel: pyepo.model.opt.optModel, n_samples: int = 10, sigma: float = 1.0, processes: int = 1, seed: int = 135, solve_ratio: float = 1.0, reduction: pyepo.func.abcmodule.Reduction = 'mean', dataset: pyepo.data.dataset.optDataset | None = None)

Bases: perturbedFenchelYoung

Multiplicative-perturbation variant of perturbedFenchelYoung for sign-sensitive oracles.

Uses the same sign-preserving multiplicative perturbation \(\hat{\mathbf{c}} \odot \exp(\sigma\boldsymbol{\xi} - \tfrac{1}{2}\sigma^2)\) as perturbedOptMul. Predicted costs must carry the intended nonzero sign; for nonnegative problems pair this module with a positive-output prediction layer (e.g. nn.Softplus() plus a small epsilon).

Reference: Dalle et al. (2022) https://arxiv.org/abs/2207.13513

class pyepo.func.perturbed.implicitMLE(optmodel: pyepo.model.opt.optModel, n_samples: int = 10, sigma: float = 1.0, lambd: float = 10, distribution: pyepo.func.utils.sumGammaDistribution | None = None, two_sides: bool = False, processes: int = 1, solve_ratio: float = 1.0, dataset: pyepo.data.dataset.optDataset | None = None)

Bases: pyepo.func.abcmodule.optModule

Implicit Maximum Likelihood Estimator (I-MLE) via perturb-and-MAP.

Frames decision-focused learning as imitation: an upstream task gradient \(\mathbf{d}\) induces a virtual update \(\hat{\mathbf{c}}' = \hat{\mathbf{c}} + \lambda \mathbf{d}\), and the gradient is estimated by a directional finite difference between smoothed solutions at \(\hat{\mathbf{c}}'\) and \(\hat{\mathbf{c}}\), sharing the same Sum-of-Gamma noise realization across the two evaluations to reduce variance.

Returns the (perturbation-smoothed) predicted solution; the user supplies a task loss (L1 against \(\mathbf{w}^*(\mathbf{c})\) is standard).

Reference: Niepert, Minervini & Franceschi (2021) https://proceedings.neurips.cc/paper_files/paper/2021/hash/7a430339c10c642c4b2251756fd1b484-Abstract.html

n_samples = 10
sigma = 1.0
lambd = 10
distribution = None
two_sides = False
forward(pred_cost: torch.Tensor) torch.Tensor

Forward pass

class pyepo.func.perturbed.implicitMLEFunc(*args, **kwargs)

Bases: torch.autograd.Function

An autograd function for Implicit Maximum Likelihood Estimator

static forward(ctx, pred_cost: torch.Tensor, module: implicitMLE) torch.Tensor

Forward pass for IMLE

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

  • module – implicitMLE module

Returns:

predicted solutions

Return type:

torch.tensor

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

Backward pass for IMLE

class pyepo.func.perturbed.adaptiveImplicitMLE(optmodel: pyepo.model.opt.optModel, n_samples: int = 10, sigma: float = 1.0, distribution: pyepo.func.utils.sumGammaDistribution | None = None, two_sides: bool = False, processes: int = 1, solve_ratio: float = 1.0, dataset: pyepo.data.dataset.optDataset | None = None)

Bases: pyepo.func.abcmodule.optModule

Adaptive Implicit MLE (AI-MLE): I-MLE with an adaptive interpolation step.

Replaces I-MLE’s fixed finite-difference step \(\lambda\) with the data-dependent choice \(\lambda_t = \alpha_t \cdot \|\hat{\mathbf{c}}\| / \|\mathbf{d}\|\), where the magnitude \(\alpha_t\) is tuned online from a moving average of gradient sparsity. The rescaling keeps the perturbation commensurate with \(\hat{\mathbf{c}}\) and removes the need to tune \(\lambda\) by hand, while the rest of the forward / backward path is identical to implicitMLE.

Reference: Minervini, Franceschi & Niepert (2023) https://ojs.aaai.org/index.php/AAAI/article/view/26103

n_samples = 10
sigma = 1.0
distribution = None
two_sides = False
alpha = 1.0
grad_norm_avg = 1
step = 0.001
forward(pred_cost: torch.Tensor) torch.Tensor

Forward pass

class pyepo.func.perturbed.adaptiveImplicitMLEFunc(*args, **kwargs)

Bases: implicitMLEFunc

An autograd function for Adaptive Implicit Maximum Likelihood Estimator

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

Backward pass for IMLE