🎉 We're happy to announce the PyEPO 2.2.1 release. 🎉
This is a maintenance release driven by a full-codebase audit: every loss, backend, metric, and the DSL were adversarially reviewed, and each fix ships with a regression test. Highlights: correct MAXIMIZE gradients for the one-sided estimators, a repaired sklearn scorer, exact setObj semantics for partial prediction, and JAX losses that are now safe under jax.jit.
What's New
Bug Fixes
- Fixed DBB and one-sided I-MLE / AI-MLE: the informative perturbation direction now flips for MAXIMIZE problems (torch & JAX)
- Fixed listwise LTR: the Boltzmann target puts its mass on the best pool solutions, per Mandi et al. (2022)
- Fixed multiplicative perturbation (
DPOMul/PFYLMul): known fixed costs keep factor exactly 1 under partial prediction - Fixed sklearn / auto-sklearn scorers:
SPOErrornow receives(pred, true)in the right order - Fixed
vrpRCIModel: rounded-capacity cuts now cover 2-customer routes - Fixed
calUnambRegret: loose-side tie-set rounding for MAXIMIZE and uniform precision scaling - Fixed regret metrics to include the DSL objective offset and reject quadratic objectives
- Fixed DSL
setObj: predicted costs always scatter onto fixed costs (full-coverage bases and permutations are no longer dropped); internal solves route through_setFullObj - Fixed DSL expressions: numpy-style broadcasting, objective-only variables, bare objective constants, negative-axis
sum, and the missing-//operators - Fixed
relax()to preserveaddConstrcuts across all backends - Fixed the away-step Frank-Wolfe active set: a full buffer merges into the smallest atom instead of corrupting slot 0 (torch & JAX)
- Fixed JAX RNG under
jax.jit: implicit keys are rejected (the noise was constant-folded);loss(..., key=...)is now callable; PG / CaVE labels carry no gradient - Fixed solver failures to raise clear
RuntimeErrors on every backend, plus guarded Gurobi / COPT imports, dataset input validation, and the CaVE binary-vertex check
Performance
- Sparse node-arc incidence for Gurobi / COPT shortest path (dense build was O(nodes × arcs) memory)
- Tolerance-based solution-pool dedup: first-order solver noise no longer grows the pool unboundedly
optDatasetConstrsreuses one model instead of rebuilding per instance- Opt-in cut recycling (
recycle_cuts=True) for DFJ / RCI: discovered cuts join Gurobi's lazy pool across solves
Tests
- Closed-form MAXIMIZE gradient gates, scorer-orientation and capacity-cut regressions, torch-JAX parity for every fix
