Chance Constraints

As a placeholder, mpi-sppy supports a PySP-style, sample-average-approximation (SAA) chance constraint of the form

\[P(\text{risky constraint holds}) \ge 1 - \alpha .\]

As in PySP, you define a binary indicator variable in each scenario model together with your own big-M constraints linking it to satisfaction; mpi-sppy adds the single aggregating constraint that turns the per-scenario indicators into a probabilistic guarantee.

Warning

Scope: chance constraints are supported only for the extensive-form (EF) solve. A chance constraint \(\sum_s p_s z_s \ge 1 - \alpha\) is a single constraint that couples a (binary) indicator variable from every scenario, so – unlike CVaR – it does not separate across scenarios and is not inherited by the PH / APH / Lagrangian / xhat decomposition cylinders. This matches PySP, which only solves the EF. Decomposing a chance constraint (e.g. Lagrangian dualization of the coupling constraint with a scalar price) is possible but is a substantially harder effort with an integrality duality gap. If you need chance constraints under decomposition, please contact the mpi-sppy developers.

The indicator convention

For each scenario s define a binary variable z_s with

\[z_s = 1 \iff \text{the risky constraint is satisfied in scenario } s ,\]

and add your own big-M constraint(s) enforcing the link (so that z_s = 1 forces satisfaction). mpi-sppy then adds

\[\sum_s p_s\, z_s \ \ge\ 1 - \alpha ,\]

i.e. E[z] >= 1 - alpha: the probability mass of satisfying scenarios is at least \(1 - \alpha\), so the violation probability is at most \(\alpha\). Setting alpha = 0 forces satisfaction in every scenario (a robust constraint); a larger alpha buys a cheaper objective by letting the worst (most expensive to satisfy) scenarios fail. The indicator must be binary for the constraint to be exact; a continuous “indicator” yields a relaxation and triggers a warning.

If the indicator variable is indexed, mpi-sppy adds one chance constraint per index of the variable.

Command line (generic_cylinders)

Use the --EF flag together with:

  • --cc-indicator-var NAME – the name of your per-scenario binary indicator (its presence enables the chance constraint);

  • --cc-alpha ALPHA – the allowed violation probability, 0 <= alpha < 1 (default 0.0).

For example, the bundled capacity example builds enough capacity to meet demand with probability at least \(1 - \alpha\):

python -m mpisppy.generic_cylinders \
    --module-name examples/chance_constraint/cc_capacity \
    --num-scens 10 --EF --EF-solver-name gurobi \
    --cc-indicator-var served --cc-alpha 0.2

With the deterministic ramp demands in that example, the cost-minimizing capacity is the \((1-\alpha)\)-quantile of demand (here, 80). Requesting a chance constraint without --EF is rejected at parse time.

Programmatic use

When you build the EF yourself (via ExtensiveForm or sputils.create_EF), call mpisppy.utils.chance_constraint.add_chance_constraint() on the assembled model after construction and before solving:

import mpisppy.utils.sputils as sputils
from mpisppy.utils import chance_constraint

ef = sputils.create_EF(scenario_names, scenario_creator)
chance_constraint.add_chance_constraint(
    ef, cc_indicator_var_name="served", cc_alpha=0.2)
# ... now solve ef ...
mpisppy.utils.chance_constraint.add_chance_constraint(ef_model, *, cc_indicator_var_name, cc_alpha)[source]

Add a PySP-style SAA chance constraint to an already-built EF model.

For a scalar indicator variable, adds to ef_model the single constraint

Sum_s p_s * z_s >= (1 - cc_alpha) * Sum_s p_s

where z_s = getattr(scenario_s, cc_indicator_var_name). The Sum_s p_s factor on the right is the total probability of the scenarios in this model (1.0 for a full EF); carrying it makes the constraint correct even for normalized / bundled EFs and reduces to PySP’s plain >= 1 - alpha when the probabilities sum to one.

For an indexed indicator variable, adds one such constraint per index of the variable (one chance constraint per index, joint over scenarios).

The user is responsible for defining z_s (binary) and the big-M constraints linking it to satisfaction, exactly as in PySP. This function adds only the aggregator.

Parameters:
  • ef_model (Pyomo ConcreteModel) – an assembled extensive form, i.e. ExtensiveForm.ef or the result of sputils.create_EF. It must carry _ef_scenario_names and expose each scenario as a sub-block with a _mpisppy_probability attribute.

  • cc_indicator_var_name (str) – the name of the per-scenario indicator variable (scalar or indexed).

  • cc_alpha (float) – the allowed violation probability, 0 <= alpha < 1. alpha = 0 forces satisfaction in every scenario (a robust constraint).

Returns:

the constraint component added to ef_model (scalar or indexed), also reachable as ef_model._mpisppy_chance_constraint.

Return type:

pyo.Constraint