Two-Stage Optimization

ExaModels supports two-stage optimization problems. This feature enables efficient modeling of optimization problems where decisions are made in two stages:

  • Design (first-stage) variables: Decisions made before uncertainty is revealed, shared across all scenarios
  • Recourse (second-stage) variables: Scenario-specific decisions made after uncertainty is revealed
  • Scenarios: Each scenario has its own parameters θ that affect the objective and constraints
using ExaModels, NLPModelsIpopt

Define the problem dimensions and scenario parameters:

ns = 3   ## number of scenarios
nv = 2   ## recourse variables per scenario
nd = 1   ## design variables
weight = 1.0 / ns
0.3333333333333333

Two annotate the scenario for each variable and constraint, we can use the scenario we need to start with a special ExaCore that supports such scenario annotations, which can be created by calling TwoStageExaCore().

core = TwoStageExaCore()
An ExaCore

  Float type: ...................... Float64
  Array type: ...................... Vector{Float64}
  Backend: ......................... Nothing

  number of objective patterns: .... 0
  number of constraint patterns: ... 0

Now we can define the design variable and recourse variables. The scenario keyword argument allows us to specify which scenario(s) each variable belongs to. For the design variable d, we set scenario = 0 to indicate that it is shared across all scenarios.

d = variable(core; start = 1.0, lvar = 0.0, uvar = Inf, scenario = 0)  ## design variable d
ExaModels.Var{Int64}(1)

For the recourse variables v, we specify scenario = [i for i=1:ns, j=1:nv] to indicate that each variable v[s,i] belongs to scenario s. This allows us to define scenario-specific constraints and objectives that involve these recourse variables.

v = variable(core, ns, nv; start = 1.0, lvar = 0.0, uvar = Inf, scenario = [i for i=1:ns, j=1:nv])  ## recourse variables v
Variable

  x ∈ R^{3 × 2}

Now we can define the constraints and objective function. The scenario keyword argument in the constraint and objective functions allows us to specify which scenario(s) each constraint or objective term belongs to.

constraint(core, v[s,1] - v[s,2]^2 for s in 1:ns; lcon = 0.0, scenario = 1:ns)

objective(core, d^2)
objective(core, weight * (v[s,i] - d)^2 for s in 1:ns, i in 1:nv)

m = ExaModel(core)
An ExaModel{Float64, Vector{Float64}, ...}

  Problem name: Generic
   All variables: ████████████████████ 7      All constraints: ████████████████████ 3     
            free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                 free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
           lower: ████████████████████ 7                lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
           upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
         low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0              low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
           fixed: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                fixed: ████████████████████ 3     
          infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0               infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
            nnzh: ( 21.43% sparsity)   22              linear: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
                                                    nonlinear: ████████████████████ 3     
                                                         nnzj: ( 71.43% sparsity)   6     
                                                     lin_nnzj: (------% sparsity)         
                                                     nln_nnzj: ( 71.43% sparsity)   6     

Now we can solve the model as usual.

ipopt(m)
"Execution stats: first-order stationary"

If the solver knows how to exploit the scenario structure, the structure-exploiting method can be used.


This page was generated using Literate.jl.