pyepo.func.perturbed ==================== .. py:module:: pyepo.func.perturbed .. autoapi-nested-parse:: Perturbed optimization function Attributes ---------- .. autoapisummary:: pyepo.func.perturbed.DPO pyepo.func.perturbed.DPOMul pyepo.func.perturbed.PFY pyepo.func.perturbed.PFYMul pyepo.func.perturbed.IMLE pyepo.func.perturbed.AIMLE Classes ------- .. autoapisummary:: pyepo.func.perturbed.perturbedOpt pyepo.func.perturbed.perturbedOptFunc pyepo.func.perturbed.perturbedOptMul pyepo.func.perturbed.perturbedFenchelYoung pyepo.func.perturbed.perturbedFenchelYoungFunc pyepo.func.perturbed.perturbedFenchelYoungMul pyepo.func.perturbed.implicitMLE pyepo.func.perturbed.implicitMLEFunc pyepo.func.perturbed.adaptiveImplicitMLE pyepo.func.perturbed.adaptiveImplicitMLEFunc Module Contents --------------- .. py:class:: 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: :py:obj:`pyepo.func.abcmodule.optModule` Differentiable Perturbed Optimizer (DPO) -- additive-Gaussian variant. Estimates the **expected solution** :math:`\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 :math:`\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 :math:`\mathbf{w}^*(\mathbf{c})` is the standard choice). For sign-sensitive oracles, use ``perturbedOptMul`` instead. Reference: Berthet et al. (2020) ``_ .. py:attribute:: n_samples :value: 10 .. py:attribute:: sigma :value: 1.0 .. py:attribute:: seed :value: 135 .. py:attribute:: variance_reduction :value: True .. py:method:: forward(pred_cost: torch.Tensor) -> torch.Tensor Forward pass .. py:class:: perturbedOptFunc(*args, **kwargs) Bases: :py:obj:`torch.autograd.Function` An autograd function for perturbed optimizer .. py:method:: forward(ctx, pred_cost: torch.Tensor, module: perturbedOpt) -> torch.Tensor :staticmethod: Forward pass for perturbed :param pred_cost: a batch of predicted values of the cost :param module: perturbedOpt module :returns: solution expectations with perturbation :rtype: torch.tensor .. py:method:: backward(ctx, grad_output: torch.Tensor) -> tuple[torch.Tensor | None, Ellipsis] :staticmethod: Backward pass for perturbed .. py:class:: 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: :py:obj:`perturbedOpt` Multiplicative-perturbation variant of ``perturbedOpt`` for sign-sensitive oracles. Replaces additive noise with the multiplicative perturbation :math:`\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) ``_ .. py:class:: 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: :py:obj:`pyepo.func.abcmodule.optModule` Perturbed Fenchel-Young loss (PFY) -- additive-Gaussian variant. Pairs the same Monte-Carlo expected perturbed solution as ``perturbedOpt`` with the Fenchel-Young loss against the true optimum :math:`\mathbf{w}^*(\mathbf{c})`, returning a scalar loss directly -- no user-defined task loss needed. The gradient collapses to the simple residual :math:`\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) ``_ .. py:attribute:: n_samples :value: 10 .. py:attribute:: sigma :value: 1.0 .. py:attribute:: seed :value: 135 .. py:method:: forward(pred_cost: torch.Tensor, true_sol: torch.Tensor) -> torch.Tensor Forward pass .. py:class:: perturbedFenchelYoungFunc(*args, **kwargs) Bases: :py:obj:`torch.autograd.Function` An autograd function for Fenchel-Young loss using perturbation techniques. .. py:method:: forward(ctx, pred_cost: torch.Tensor, true_sol: torch.Tensor, module: perturbedFenchelYoung) -> torch.Tensor :staticmethod: Forward pass for perturbed Fenchel-Young loss :param pred_cost: a batch of predicted values of the cost :param true_sol: a batch of true optimal solutions :param module: perturbedFenchelYoung module :returns: solution expectations with perturbation :rtype: torch.tensor .. py:method:: backward(ctx, grad_output: torch.Tensor) -> tuple[torch.Tensor | None, Ellipsis] :staticmethod: Backward pass for perturbed Fenchel-Young loss .. py:class:: 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: :py:obj:`perturbedFenchelYoung` Multiplicative-perturbation variant of ``perturbedFenchelYoung`` for sign-sensitive oracles. Uses the same sign-preserving multiplicative perturbation :math:`\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) ``_ .. py:class:: 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: :py:obj:`pyepo.func.abcmodule.optModule` Implicit Maximum Likelihood Estimator (I-MLE) via perturb-and-MAP. Frames decision-focused learning as imitation: an upstream task gradient :math:`\mathbf{d}` induces a virtual update :math:`\hat{\mathbf{c}}' = \hat{\mathbf{c}} + \lambda \mathbf{d}`, and the gradient is estimated by a directional finite difference between smoothed solutions at :math:`\hat{\mathbf{c}}'` and :math:`\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 :math:`\mathbf{w}^*(\mathbf{c})` is standard). Reference: Niepert, Minervini & Franceschi (2021) ``_ .. py:attribute:: n_samples :value: 10 .. py:attribute:: sigma :value: 1.0 .. py:attribute:: lambd :value: 10 .. py:attribute:: distribution :value: None .. py:attribute:: two_sides :value: False .. py:method:: forward(pred_cost: torch.Tensor) -> torch.Tensor Forward pass .. py:class:: implicitMLEFunc(*args, **kwargs) Bases: :py:obj:`torch.autograd.Function` An autograd function for Implicit Maximum Likelihood Estimator .. py:method:: forward(ctx, pred_cost: torch.Tensor, module: implicitMLE) -> torch.Tensor :staticmethod: Forward pass for IMLE :param pred_cost: a batch of predicted values of the cost :param module: implicitMLE module :returns: predicted solutions :rtype: torch.tensor .. py:method:: backward(ctx, grad_output: torch.Tensor) -> tuple[torch.Tensor | None, Ellipsis] :staticmethod: Backward pass for IMLE .. py:class:: 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: :py:obj:`pyepo.func.abcmodule.optModule` Adaptive Implicit MLE (AI-MLE): I-MLE with an adaptive interpolation step. Replaces I-MLE's fixed finite-difference step :math:`\lambda` with the data-dependent choice :math:`\lambda_t = \alpha_t \cdot \|\hat{\mathbf{c}}\| / \|\mathbf{d}\|`, where the magnitude :math:`\alpha_t` is tuned online from a moving average of gradient sparsity. The rescaling keeps the perturbation commensurate with :math:`\hat{\mathbf{c}}` and removes the need to tune :math:`\lambda` by hand, while the rest of the forward / backward path is identical to ``implicitMLE``. Reference: Minervini, Franceschi & Niepert (2023) ``_ .. py:attribute:: n_samples :value: 10 .. py:attribute:: sigma :value: 1.0 .. py:attribute:: distribution :value: None .. py:attribute:: two_sides :value: False .. py:attribute:: alpha :value: 1.0 .. py:attribute:: grad_norm_avg :value: 1 .. py:attribute:: step :value: 0.001 .. py:method:: forward(pred_cost: torch.Tensor) -> torch.Tensor Forward pass .. py:class:: adaptiveImplicitMLEFunc(*args, **kwargs) Bases: :py:obj:`implicitMLEFunc` An autograd function for Adaptive Implicit Maximum Likelihood Estimator .. py:method:: backward(ctx, grad_output: torch.Tensor) -> tuple[torch.Tensor | None, Ellipsis] :staticmethod: Backward pass for IMLE .. py:data:: DPO .. py:data:: DPOMul .. py:data:: PFY .. py:data:: PFYMul .. py:data:: IMLE .. py:data:: AIMLE