From 64881ad28216bcc51f72b507605ccb0291537ab6 Mon Sep 17 00:00:00 2001 From: Zedong Peng Date: Thu, 26 Oct 2023 00:00:45 -0400 Subject: [PATCH] fix baron convexification --- pyomo/contrib/mindtpy/algorithm_base_class.py | 6 ++- pyomo/contrib/mindtpy/config_options.py | 6 +++ pyomo/contrib/mindtpy/cut_generation.py | 50 +++++++++++-------- pyomo/core/base/block.py | 3 -- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/pyomo/contrib/mindtpy/algorithm_base_class.py b/pyomo/contrib/mindtpy/algorithm_base_class.py index c254a8db72b..06d27810f05 100644 --- a/pyomo/contrib/mindtpy/algorithm_base_class.py +++ b/pyomo/contrib/mindtpy/algorithm_base_class.py @@ -23,7 +23,7 @@ from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver from pyomo.common.collections import ComponentMap, Bunch, ComponentSet from pyomo.common.errors import InfeasibleConstraintException -from pyomo.contrib.mindtpy.cut_generation import add_no_good_cuts +from pyomo.contrib.mindtpy.cut_generation import add_no_good_cuts, add_baron_cuts from operator import itemgetter from pyomo.common.errors import DeveloperError from pyomo.solvers.plugins.solvers.gurobi_direct import gurobipy @@ -2203,6 +2203,7 @@ def load_solution(self): if self.working_model.component("_int_to_binary_reform") is not None: self.working_model._int_to_binary_reform.deactivate() # exclude fixed variables here. This is consistent with the definition of variable_list. + self.working_model.del_component('baroncuts') working_model_variable_list = list( get_vars_from_components( block=self.working_model, @@ -2772,6 +2773,9 @@ def solve(self, model, **kwds): # Reformulate the objective function. self.objective_reformulation() + if config.use_baron_convexification: + add_baron_cuts(self.working_model) + config.logger.info("Use the baron to tighten the bounds of variables and add initial cuts") # Save model initial values. self.initial_var_values = list(v.value for v in MindtPy.variable_list) diff --git a/pyomo/contrib/mindtpy/config_options.py b/pyomo/contrib/mindtpy/config_options.py index 507cbd995f8..b6389493fb5 100644 --- a/pyomo/contrib/mindtpy/config_options.py +++ b/pyomo/contrib/mindtpy/config_options.py @@ -502,6 +502,12 @@ def _add_common_configs(CONFIG): domain=bool, ), ) + CONFIG.declare("use_baron_convexification", ConfigValue( + default=False, + domain=bool, + description="use baron to provide the convex relations for nonconvex MINLPs.", + doc="use baron to provide the convex relations for nonconvex MINLPs." + )) def _add_subsolver_configs(CONFIG): diff --git a/pyomo/contrib/mindtpy/cut_generation.py b/pyomo/contrib/mindtpy/cut_generation.py index 8389a799757..3fd7702efd2 100644 --- a/pyomo/contrib/mindtpy/cut_generation.py +++ b/pyomo/contrib/mindtpy/cut_generation.py @@ -11,7 +11,8 @@ """Cut generation.""" from math import copysign -from pyomo.core import minimize, value +from pyomo.core import minimize, value, RangeSet, Var, ConstraintList, Objective, Set +from pyomo.core.base.var import _GeneralVarData import pyomo.core.expr as EXPR from pyomo.contrib.gdpopt.util import time_code from pyomo.contrib.mcpp.pyomo_mcpp import McCormick as mc, MCPP_Error @@ -503,38 +504,46 @@ def add_baron_cuts(model): output_filename, symbol_map = model.baronwrite( "root_relaxation_baron.bar", format="bar") var_ids = symbol_map.byObject - print(var_ids) + # dolocal: Local search option for upper bounding. 0: no local search is done during upper bounding. 1: BARON automatically decides when to apply local search based on analyzing the results of previous local searches os.system("sed -i '1 a dolocal:0; ' root_relaxation_baron.bar") + # NumLoc: Number of local searches done in preprocessing. If NumLoc is set to −1, local searches in preprocessing will be done until proof of globality or MaxTime is reached. If NumLoc is set to −2, BARON decides the number of local searches in preprocessing based on problem and NLP solver characteristics. os.system("sed -i '1 a numloc:0; ' root_relaxation_baron.bar") os.system("sed -i '1 a maxtime:10000; ' root_relaxation_baron.bar") + # Option to control log output. 0: all log output is suppressed. 1: print log output. os.system("sed -i '1 a prlevel:0; ' root_relaxation_baron.bar") os.system("sed -i '1 a ppdo:0; ' root_relaxation_baron.bar") os.system("sed -i '1 a pscdo:0; ' root_relaxation_baron.bar") # os.system('''sed -i '1 a CplexLibName: "/opt/ibm/ILOG/CPLEX_Studio129/cplex/bin/x86-64_linux/libcplex1290.so"; ' root_relaxation_baron.bar''') - os.system('''sed -i '1 a CplexLibName: "/package/cplex/22.1/cplex/bin/x86-64_linux/libcplex12100.dylib"; ' root_relaxation_baron.bar''') - + os.system('''sed -i '1 a CplexLibName: "/package/cplex/22.1/cplex/bin/x86-64_linux/libcplex2210.so"; ' root_relaxation_baron.bar''') os.system(special_baron_path + " root_relaxation_baron.bar") - print('111111') cplex_model = cplex.Cplex("relax.lp") - timeb = time.time() print("lp file generation time", timeb - timea) # create additional variables in the pyomo model var_names = cplex_model.variables.get_names() num_bar_vars = sum("bar_var" in var for var in var_names) - model.bar_set = RangeSet(num_bar_vars) + num_bar_vars_list = [] + # sometimes the index of bar_var is not continuous + # Therefore, we cannot use RangeSet + for var in var_names: + if "bar_var" in var: + num_bar_vars_list.append(int(var.split("bar_var")[1])) + # model.bar_set = RangeSet(num_bar_vars) + model.bar_set = Set(initialize=num_bar_vars_list) model.bar_var = Var(model.bar_set) # create a map from cplex var id to pyomo var name varid_to_var = {} bar_var_indices = [] + cplex_var_names = cplex_model.variables.get_names() for vid in var_ids: name = symbol_map.byObject[vid] - var_data = symbol_map.bySymbol[name]() - varid_cplex = cplex_model.variables.get_indices(name) - varid_to_var[varid_cplex] = var_data - - cplex_var_names = cplex_model.variables.get_names() + var_data = symbol_map.bySymbol[name] + if isinstance(symbol_map.bySymbol[name], _GeneralVarData): + # sometimes some variables only appear in baron file not in cplex + if name in cplex_var_names: + varid_cplex = cplex_model.variables.get_indices(name) + varid_to_var[varid_cplex] = var_data for i in range(len(cplex_var_names)): varname = cplex_var_names[i] if "bar_var" in varname: @@ -570,15 +579,16 @@ def add_baron_cuts(model): if sense == 'E': model.baroncuts.add(expr == rhs) # change objective - next(model.component_data_objects(Objective, active=True)).deactivate() + # move nonlinear objective function to constraint + # next(model.component_data_objects(Objective, active=True)).deactivate() # model.obj.deactivate() - coeff = cplex_model.objective.get_linear() - if cplex_model.objective.get_sense() == 1: - model.baron_obj = Objective(expr=sum(varid_to_var[i] * coeff[i] for i in range( - cplex_model.variables.get_num()) if i in varid_to_var.keys()), sense=minimize) - else: - model.baron_obj = Objective(expr=sum(varid_to_var[i] * coeff[i] for i in range( - cplex_model.variables.get_num()) if i in varid_to_var.keys()), sense=maximize) + # coeff = cplex_model.objective.get_linear() + # if cplex_model.objective.get_sense() == 1: + # model.baron_obj = Objective(expr=sum(varid_to_var[i] * coeff[i] for i in range( + # cplex_model.variables.get_num()) if i in varid_to_var.keys()), sense=minimize) + # else: + # model.baron_obj = Objective(expr=sum(varid_to_var[i] * coeff[i] for i in range( + # cplex_model.variables.get_num()) if i in varid_to_var.keys()), sense=maximize) timec = time.time() print("time to add the cuts to pyomo model", timec-timeb) diff --git a/pyomo/core/base/block.py b/pyomo/core/base/block.py index a96fd9ce14e..dd043de86c3 100644 --- a/pyomo/core/base/block.py +++ b/pyomo/core/base/block.py @@ -2046,9 +2046,6 @@ def solver_capability(x): return True solver_capability, io_options) referenced_variable_ids = smap.byObject.keys() - print(smap.byObject) - print(smap.byObject.keys()) - print(referenced_variable_ids) smap_id = id(smap) if not hasattr(self, 'solutions'): # This is a bit of a hack. The write() method was moved