cplexpyomodecompositiondocplex

Pyomo using CPLEX Automatic Benders Decomposition with Annotation


I'm trying to use Cplex with the automatic benders decomposition. There are multiple ways to do that. I want to force the decomposition to be according to my annotations - Cplex calls this Benders strategy 1.

However, I can't get it done in pyomo. I don't know how to annotate the variables so that Cplex recognises the annotation.

In Cplex

In Cplex using docplex I simply do

from docplex.mp.model import Model
model = Model()
...
Variable1 = model.binary_var_dict(E, name="ind_purchase")
for var in Variable1 .values():
    var.benders_annotation = 0  # Master problem (1-N for subproblem 1-N)
...

and then I set the benders strategy and solve


model.parameters.benders.strategy = 1
model.solve(log_output=True)

In pyomo

In pyomo I tried different things. Using the fully automatic benders decomposition (strategy=3) actually works using


from pyomo.environ import *
from pyomo.core import *

# create model...

opt = SolverFactory('cplex_direct')
opt.options['benders_strategy'] = 3

solution = opt.solve(model, tee=True)

Yet, using strategy=1 I need to set annotations - and these annotations do not work. I tried it (among others) like this


model.benders_annotation = Suffix(direction=Suffix.EXPORT)

# annotate variables
model.benders_annotation[Variable1] = 0

but it is simply not recognized: CPLEX Error 2000: No Benders decomposition available.

If it were recognized but faulty, cplex would throw the error: CPLEX Error 2002: Invalid Benders decomposition.

Any help greatly appreciated!


Solution

  • In pyomo you can write the model into an mps file

    model.write("/tmp/model.mps")
    

    and then within docplex python API you can do

    m = read_model(“c:/tmp/model.mps”, model_name=”model”, ignore_names=False)
    

    and that way you can use docplex API for the Benders annotation

    Full example with my zoo example

    import pyomo.environ as pyo
    from pyomo.opt import SolverFactory
    
    opt = pyo.SolverFactory("cplex")
    
    opt.options['mip limits solutions'] = 1
    
    model = pyo.ConcreteModel()
    
    model.nbBus = pyo.Var([40,30], domain=pyo.PositiveIntegers)
    
    model.OBJ = pyo.Objective(expr = 500*model.nbBus[40] + 400*model.nbBus[30])
    
    model.Constraint1 = pyo.Constraint(expr = 40*model.nbBus[40] + 30*model.nbBus[30] >= 300)
    
    #opt.solve(model)
    
    model.write("c:/temp/model.mps",io_options = {"symbolic_solver_labels":True})
    
    from docplex.mp.model import Model
    from docplex.mp.model_reader import ModelReader
    
    mdl = Model(name='buses')
    
    mdl = ModelReader.read('c:/temp/model.mps', ignore_names=False)
    
    mdl.solve(log_output=True,)
    
    for v in mdl.iter_integer_vars():
        print(v," = ",v.solution_value)
    

    https://github.com/AlexFleischerParis/zoodocplex/blob/master/zoopyomogenerateanddocplexsolve.py

    which gives

    nbBus(30)  =  2.0
    nbBus(40)  =  6.0