Pyomo / pyomo

An object-oriented algebraic modeling language in Python for structured optimization problems.
https://www.pyomo.org
Other
1.97k stars 506 forks source link

How to specify continuous set of uncertain parameters per each discrete scenario in PyROS? #2431

Open makansij opened 2 years ago

makansij commented 2 years ago

Summary

My suggestion is to allow continuous set of uncertain parameters per each discrete scenario

Rationale

Many practical problems require it, and plus it is still within the GRCS algorithm formulation. See equation (3).

Description

For each scenario (each realization of the discrete parameters), allow the user to specify an uncertainty set for the uncertain continuous parameters.

Here's an example of how I would imagine this would work (warning: not runnable code)

# === Required import ===
import pyomo.kernel as pmo
import numpy as np
import pyomo.environ as pyo
import pyomo.contrib.pyros as pyros

# === Instantiate the PyROS solver object ===
pyros_solver = pyo.SolverFactory("pyros")
# === Construct the Pyomo model object ===
m = pyo.ConcreteModel()

# === Define continuous variables ===
m.x = pyo.Var(within=pyo.Reals,bounds=(0,15),initialize=1)

# === Define parameters ===
m.q_continuous = pyo.Param(initialize=0, mutable=True)

# === Specify the objective function ===
m.obj = pyo.Objective(expr= m.q_continuous*m.x, sense=pyo.minimize)

# === Specify the constraints ===
m.c1 = pyo.Constraint(expr= 0 <= m.q_continuous*m.x)
m.c2 = pyo.Constraint(expr= 0 <= m.x)

# === Specify which parameters are uncertain ===
uncertain_parameters = [m.q_continuous] # We can pass IndexedParams this way to PyROS, or as an expanded list per index

# === Define the pertinent data ===
bounds = ...
rhs_coefficients_mat =...
rhs_vec = ...
d1 = [pyros.BoxSet(bounds)]
d2 = [pyros.PolyhedralSet(rhs_coefficients_mat, rhs_vec)]
scenarios = [d1, d2]
# === Construct the desirable uncertainty set ===
scenario_uncertainty_set = pyros.DiscreteScenarioSet(scenarios)

# === Designate local and global NLP solvers ===
solvername = 'baron'
solverpath_exe = <path>
baron_solver = pyo.SolverFactory(solvername, executable=solverpath_exe)

local_solver = baron_solver
global_solver = baron_solver

# === Designate which variables correspond to first- and second-stage degrees of freedom ===
first_stage_variables =[m.x]
second_stage_variables = []
# The remaining variables are implicitly designated to be state variables

# === Call PyROS to solve the robust optimization problem ===
results_1 = pyros_solver.solve(model = m,
                                 first_stage_variables = first_stage_variables,
                                 second_stage_variables = second_stage_variables,
                                 uncertain_params = uncertain_parameters,
                                 uncertainty_set = scenario_uncertainty_set,
                                 local_solver = local_solver,
                                 global_solver= global_solver,
                                 tee=True,
                                 options = {
                                    "objective_focus": pyros.ObjectiveType.worst_case,
                                    "solve_master_globally": True,
                                    "load_solution":False
                                  })

# === Query results ===
time = results_1.time
iterations = results_1.iterations
termination_condition = results_1.pyros_termination_condition
objective = results_1.final_objective_value
# === Print some results ===
single_stage_final_objective = round(objective,-1)
print("Final objective value: %s" % single_stage_final_objective)

print("PyROS termination condition: %s" % termination_condition)

Additional information

There are no examples in the online documentation involving Discrete Uncertainty sets. So, I've had to read the source code to figure out how to use it. It might make it easier for users to understand if there were an example in the docs under "Solve with PyROS".

Corresponding SO question here: https://stackoverflow.com/questions/72620730

blnicho commented 2 years ago

@shermanjasonaf could you comment on this?

shermanjasonaf commented 2 years ago

If I understand correctly, are you asking about specifying a different type of constraint for each uncertain parameter (i.e. each dimension of the set)? That is, are you asking about constructing a Cartesian product of multiple uncertainty sets? If this is the case, then we are currently thinking about adding a CartesianProductSet class in the future, but there are currently issues with addressing the possibilities of constructing sets with mixed-integer variables/constraints.

Otherwise, can you clarify what you mean by specifying a "continuous set for each scenario", and clarify your reference to equation (3)?

Alternatively, per the documentation, you may implement a custom uncertainty set by defining your own subclass of the UncertaintySet class.

makansij commented 2 years ago

@shermanjasonaf YES! A CartesianProductSet is exactly what I had in mind – I would have used that language but I wasn’t sure if it would get interpreted correctly.

How can I help with adding this CartesianProductSet class?

Without it, I’m afraid PyROS cannot faithfully represent the problem formulation in equation (3) of the paper referred to in the docs as "GRCS": Screen Shot 2022-06-15 at 7 09 43 AM https://aiche.onlinelibrary.wiley.com/doi/epdf/10.1002/aic.17175

makansij commented 2 years ago

If I understand correctly, are you asking about specifying a different type of constraint for each uncertain parameter (i.e. each dimension of the set)? That is, are you asking about constructing a Cartesian product of multiple uncertainty sets? If this is the case, then we are currently thinking about adding a CartesianProductSet class in the future, but there are currently issues with addressing the possibilities of constructing sets with mixed-integer variables/constraints.

Otherwise, can you clarify what you mean by specifying a "continuous set for each scenario", and clarify your reference to equation (3)?

Alternatively, per the documentation, you may implement a custom uncertainty set by defining your own subclass of the UncertaintySet class.

@shermanjasonaf I'll try the custom Uncertainty Set, and see if I can solve it that way. The documentation seems to indicate that this should be able to support my use case, do you agree?

shermanjasonaf commented 2 years ago

You should be able to create an UncertaintySet with either of the following geometries: linear, convex, non-convex, or purely discrete. PyROS currently solves separation problems for purely discrete geometries differently than it does for all other geometries. Note that there is currently no support for sets with both discrete and continuous uncertain parameters; this is currently a work in-progress (adding support for such sets will entail writing a separation problem routine to handle these geometries). To this end, you can implement a custom-written Cartesian product set---so long as the arguments of the Cartesian product are all continuous, or all discrete.