Skip to content
Merged
4 changes: 2 additions & 2 deletions doc/OnlineDocs/explanation/solvers/pyros/uncertainty_sets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ subclasses are provided below.
- :math:`\begin{array}{l} q^{0} \in \mathbb{R}^{n}, \\ b \in \mathbb{R}_{+}^{L}, \\ B \in \{0, 1\}^{L \times n} \end{array}`
- :math:`\left\{ q \in \mathbb{R}^{n} \middle| \begin{array}{l} \begin{pmatrix} B \\ -I \end{pmatrix} q \leq \begin{pmatrix} b + Bq^{0} \\ -q^{0} \end{pmatrix} \end{array} \right\}`
* - :class:`~pyomo.contrib.pyros.uncertainty_sets.CardinalitySet`
- :math:`\begin{array}{l} q^{0} \in \mathbb{R}^{n}, \\ \hat{q} \in \mathbb{R}_{+}^{n}, \\ \Gamma \in [0, n] \end{array}`
- :math:`\left\{ q \in \mathbb{R}^{n} \middle| \begin{array}{l} \exists\,\xi \in [0, 1]^n\,:\\ \quad \,q = q^{0} + \hat{q} \circ \xi \\ \quad \displaystyle \sum_{i=1}^{n} \xi_{i} \leq \Gamma \end{array} \right\}`
- :math:`\begin{array}{l} q^{0} \in \mathbb{R}^{n}, \\ \hat{q}^+ \in \mathbb{R}_{+}^{n}, \\ \hat{q}^- \in \mathbb{R}_{+}^{n}, \\ \Gamma \in [0, n] \end{array}`
- :math:`\left\{ q \in \mathbb{R}^{n} \middle| \begin{array}{l} \exists\,\xi^+, \xi^- \in [0, 1]^n\,:\\ \quad \,q = q^{0} + \hat{q}^+ \circ \xi^+ - \hat{q}^- \circ \xi^- \\ \quad \displaystyle \sum_{i=1}^{n} (\xi_{i}^+ + \xi_{i}^-) \leq \Gamma \\ \quad \xi_i^+ = 0 \quad\forall\, i : \hat{q}_i^+ = 0 \\ \quad \xi_i^- = 0 \quad\forall\, i : \hat{q}_i^- = 0 \end{array} \right\}`
* - :class:`~pyomo.contrib.pyros.uncertainty_sets.CartesianProductSet`
- :math:`\begin{array}{l} \mathcal{Q}_{1} \subset \mathbb{R}^{n_1}, \\ \mathcal{Q}_{2} \subset \mathbb{R}^{n_2}, \\ \vdots \\ \mathcal{Q}_{m} \subset \mathbb{R}^{n_m} \end{array}`
- :math:`\displaystyle \mathcal{Q}_{1} \times \mathcal{Q}_{2} \times \cdots \times \mathcal{Q}_{m}`
Expand Down
4 changes: 4 additions & 0 deletions doc/OnlineDocs/reference/bibliography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ Bibliography
*SIAM Journal on Applied Mathematics* 23(1), 61-19, 1972.
DOI `10.1137/0123007 <https://doi.org/10.1137/0123007>`_

.. [BS04] D. Bertsimas and M. Sim. "The price of robustness",
*Operations research*, 52(1), 35-53, 2004. DOI
`10.1287/opre.1030.0065 <https://doi.org/10.1287/opre.1030.0065>`_.

.. [Dje20] H. Djelassi. "Discretization-based algorithms for the
global solution of hierarchical programs".
Dissertation, Rheinisch-Westfälische Technische Hochschule Aachen, 2020.
Expand Down
6 changes: 6 additions & 0 deletions pyomo/contrib/pyros/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ PyROS CHANGELOG
===============


-------------------------------------------------------------------------------
PyROS 1.3.15 12 May 2026
-------------------------------------------------------------------------------
- Extend `CardinalitySet` to allow for negative deviations


-------------------------------------------------------------------------------
PyROS 1.3.14 12 May 2026
-------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion pyomo/contrib/pyros/pyros.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
ModelData,
)

__version__ = "1.3.14"
__version__ = "1.3.15"


default_pyros_solver_logger = setup_pyros_logger()
Expand Down
57 changes: 57 additions & 0 deletions pyomo/contrib/pyros/tests/test_grcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,63 @@ def build_leyffer_two_cons_two_params():
return m


class TestPyROSSolveCardinalitySet(unittest.TestCase):
"""
Test PyROS successfully solves model with cardinality-constrained
uncertainty set.
"""

@unittest.skipUnless(ipopt_available, "IPOPT is not available.")
def test_cardinality_set_solve(self):
m = ConcreteModel()
m.q = Param(range(4), initialize=1, mutable=True)
m.x = Var(initialize=0, bounds=(0, None))
m.obj = Objective(expr=m.x)
m.ineq_con = Constraint(expr=m.x >= m.q[0] + m.q[1] - m.q[2] - m.q[3])

cset = CardinalitySet(
origin=[1] * 4, positive_deviation=[1, 0, 2, 0.5], gamma=2
)
res = SolverFactory("pyros").solve(
model=m,
first_stage_variables=m.x,
second_stage_variables=[],
uncertain_params=m.q,
uncertainty_set=cset,
local_solver="ipopt",
global_solver="ipopt",
objective_focus="worst_case",
solve_master_globally=True,
)
self.assertEqual(res.iterations, 2)
# worst-case objective is just maximum sum of uncertain
# parameters (per cardinality constraints)
self.assertAlmostEqual(res.final_objective_value, 1)
self.assertEqual(
res.pyros_termination_condition, pyrosTerminationCondition.robust_optimal
)

cset.negative_deviation = [0, 4.5, 0.5, 3]
res2 = SolverFactory("pyros").solve(
model=m,
first_stage_variables=m.x,
second_stage_variables=[],
uncertain_params=m.q,
uncertainty_set=cset,
local_solver="ipopt",
global_solver="ipopt",
objective_focus="worst_case",
solve_master_globally=True,
)
self.assertEqual(res2.iterations, 2)
# worst-case objective changes due to
# change of maximum negative deviations
self.assertAlmostEqual(res2.final_objective_value, 4)
self.assertEqual(
res.pyros_termination_condition, pyrosTerminationCondition.robust_optimal
)


class TestPyROSSolveFactorModelSet(unittest.TestCase):
"""
Test PyROS successfully solves model with factor model uncertainty.
Expand Down
Loading
Loading