def test_var(self): m = ConcreteModel() m.x = Var(bounds=(-1, 1), initialize=3) mc_var = mc(m.x) self.assertEqual(mc_var.lower(), -1) self.assertEqual(mc_var.upper(), 1) m.no_ub = Var(bounds=(0, None), initialize=3) output = StringIO() with LoggingIntercept(output, 'pyomo.contrib.mcpp', logging.WARNING): mc_var = mc(m.no_ub) self.assertIn("Var no_ub missing upper bound.", output.getvalue().strip()) self.assertEqual(mc_var.lower(), 0) self.assertEqual(mc_var.upper(), 500000) m.no_lb = Var(bounds=(None, -3), initialize=-1) output = StringIO() with LoggingIntercept(output, 'pyomo.contrib.mcpp', logging.WARNING): mc_var = mc(m.no_lb) self.assertIn("Var no_lb missing lower bound.", output.getvalue().strip()) self.assertEqual(mc_var.lower(), -500000) self.assertEqual(mc_var.upper(), -3) m.no_val = Var(bounds=(0, 1)) output = StringIO() with LoggingIntercept(output, 'pyomo.contrib.mcpp', logging.WARNING): mc_var = mc(m.no_val) mc_var.subcv() self.assertIn("Var no_val missing value.", output.getvalue().strip()) self.assertEqual(mc_var.lower(), 0) self.assertEqual(mc_var.upper(), 1)
def test_trig(self): m = ConcreteModel() m.x = Var(bounds=(pi / 4, pi / 2), initialize=pi / 4) mc_expr = mc(tan(atan((m.x)))) self.assertAlmostEqual(mc_expr.lower(), pi / 4) self.assertAlmostEqual(mc_expr.upper(), pi / 2) m.y = Var(bounds=(0, sin(pi / 4)), initialize=0) mc_expr = mc(asin((m.y))) self.assertEqual(mc_expr.lower(), 0) self.assertAlmostEqual(mc_expr.upper(), pi / 4) m.z = Var(bounds=(0, cos(pi / 4)), initialize=0) mc_expr = mc(acos((m.z))) self.assertAlmostEqual(mc_expr.lower(), pi / 4) self.assertAlmostEqual(mc_expr.upper(), pi / 2)
def make2dPlot(expr, numticks=10, show_plot=False): mc_ccVals = [None] * (numticks + 1) mc_cvVals = [None] * (numticks + 1) aff_cc = [None] * (numticks + 1) aff_cv = [None] * (numticks + 1) fvals = [None] * (numticks + 1) mc_expr = mc(expr) x = next(identify_variables(expr)) # get the first variable tick_length = (x.ub - x.lb) / numticks xaxis = [x.lb + tick_length * n for n in range(numticks + 1)] x_val = value(x) # initial value of x cc = mc_expr.subcc() # Concave overestimator subgradient at x_val cv = mc_expr.subcv() # Convex underestimator subgradient at x_val f_cc = mc_expr.concave() # Concave overestimator value at x_val f_cv = mc_expr.convex() # Convex underestimator value at x_val for i, x_tick in enumerate(xaxis): aff_cc[i] = cc[x] * (x_tick - x_val) + f_cc aff_cv[i] = cv[x] * (x_tick - x_val) + f_cv mc_expr.changePoint(x, x_tick) mc_ccVals[i] = mc_expr.concave() mc_cvVals[i] = mc_expr.convex() fvals[i] = value(expr) if show_plot: import matplotlib.pyplot as plt plt.plot(xaxis, fvals, 'r', xaxis, mc_ccVals, 'b--', xaxis, mc_cvVals, 'b--', xaxis, aff_cc, 'k|', xaxis, aff_cv, 'k|') plt.show() return mc_ccVals, mc_cvVals, aff_cc, aff_cv
def test_reciprocal(self): m = ConcreteModel() m.x = Var(bounds=(1, 2), initialize=1) m.y = Var(bounds=(2, 3), initialize=2) mc_expr = mc(m.x / m.y) self.assertEqual(mc_expr.lower(), 1 / 3) self.assertEqual(mc_expr.upper(), 1)
def test_linear_expression(self): m = ConcreteModel() m.x = Var(bounds=(1, 2), initialize=1) with self.assertRaises(NotImplementedError): mc_expr = mc(quicksum([m.x, m.x], linear=True)) self.assertEqual(mc_expr.lower(), 2) self.assertEqual(mc_expr.upper(), 4)
def test_powers(self): m = ConcreteModel() m.x = Var(bounds=(0, 2), initialize=1) m.y = Var(bounds=(1e-4, 2), initialize=1) with self.assertRaisesRegexp(MCPP_Error, "Log with negative values in range"): mc(m.x ** 1.5) mc_expr = mc(m.y ** 1.5) self.assertAlmostEqual(mc_expr.lower(), 1e-4**1.5) self.assertAlmostEqual(mc_expr.upper(), 2**1.5) mc_expr = mc(m.y ** m.x) self.assertAlmostEqual(mc_expr.lower(), 1e-4**2) self.assertAlmostEqual(mc_expr.upper(), 4) m.z = Var(bounds=(-1, 1), initialize=0) mc_expr = mc(m.z ** 2) self.assertAlmostEqual(mc_expr.lower(), 0) self.assertAlmostEqual(mc_expr.upper(), 1)
def make2dPlot(expr, numticks=10, show_plot=False): mc_ccVals = [None] * (numticks + 1) mc_cvVals = [None] * (numticks + 1) aff_cc = [None] * (numticks + 1) aff_cv = [None] * (numticks + 1) fvals = [None] * (numticks + 1) mc_expr = mc(expr) x = next(identify_variables(expr)) # get the first variable tick_length = (x.ub - x.lb) / numticks xaxis = [x.lb + tick_length * n for n in range(numticks + 1)] x_val = value(x) # initial value of x cc = mc_expr.subcc() # Concave overestimator subgradient at x_val cv = mc_expr.subcv() # Convex underestimator subgradient at x_val f_cc = mc_expr.concave() # Concave overestimator value at x_val f_cv = mc_expr.convex() # Convex underestimator value at x_val for i, x_tick in enumerate(xaxis): aff_cc[i] = cc[x] * (x_tick - x_val) + f_cc aff_cv[i] = cv[x] * (x_tick - x_val) + f_cv mc_expr.changePoint(x, x_tick) mc_ccVals[i] = mc_expr.concave() mc_cvVals[i] = mc_expr.convex() fvals[i] = value(expr) if show_plot: plt.plot(xaxis, fvals, 'r', xaxis, mc_ccVals, 'b--', xaxis, mc_cvVals, 'b--', xaxis, aff_cc, 'k|', xaxis, aff_cv, 'k|') plt.show() return mc_ccVals, mc_cvVals, aff_cc, aff_cv
def add_affine_cuts(nlp_result, solve_data, config): with time_code(solve_data.timing, "affine cut generation"): m = solve_data.linear_GDP if config.calc_disjunctive_bounds: with time_code(solve_data.timing, "disjunctive variable bounding"): TransformationFactory( 'contrib.compute_disj_var_bounds').apply_to( m, solver=config.mip_solver if config.obbt_disjunctive_bounds else None) config.logger.info("Adding affine cuts.") GDPopt = m.GDPopt_utils counter = 0 for var, val in zip(GDPopt.variable_list, nlp_result.var_values): if val is not None and not var.fixed: var.value = val for constr in constraints_in_True_disjuncts(m, config): # Note: this includes constraints that are deactivated in the current model (linear_GDP) disjunctive_var_bounds = disjunctive_bounds(constr.parent_block()) if constr.body.polynomial_degree() in (1, 0): continue vars_in_constr = list(identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff mc_eqn = mc(constr.body, disjunctive_var_bounds) # mc_eqn = mc(constr.body) ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() ub_int = min( constr.upper, mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max( constr.lower, mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() parent_block = constr.parent_block() # Create a block on which to put outer approximation cuts. aff_utils = parent_block.component('GDPopt_aff') if aff_utils is None: aff_utils = parent_block.GDPopt_aff = Block( doc="Block holding affine constraints") aff_utils.GDPopt_aff_cons = ConstraintList() aff_cuts = aff_utils.GDPopt_aff_cons concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr) + ccStart >= lb_int convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr) + cvStart <= ub_int aff_cuts.add(expr=concave_cut) aff_cuts.add(expr=convex_cut) counter += 2 config.logger.info("Added %s affine cuts" % counter)
def test_powers(self): m = ConcreteModel() m.x = Var(bounds=(0, 2), initialize=1) m.y = Var(bounds=(1e-4, 2), initialize=1) with self.assertRaisesRegex(MCPP_Error, "Log with negative values in range"): mc(m.x ** 1.5) mc_expr = mc(m.y ** 1.5) self.assertAlmostEqual(mc_expr.lower(), 1e-4**1.5) self.assertAlmostEqual(mc_expr.upper(), 2**1.5) mc_expr = mc(m.y ** m.x) self.assertAlmostEqual(mc_expr.lower(), 1e-4**2) self.assertAlmostEqual(mc_expr.upper(), 4) m.z = Var(bounds=(-1, 1), initialize=0) mc_expr = mc(m.z ** 2) self.assertAlmostEqual(mc_expr.lower(), 0) self.assertAlmostEqual(mc_expr.upper(), 1)
def test_fixed_var(self): m = ConcreteModel() m.x = Var(bounds=(-50, 80), initialize=3) m.y = Var(bounds=(0, 6), initialize=2) m.y.fix() mc_expr = mc(m.x * m.y) self.assertEqual(mc_expr.lower(), -100) self.assertEqual(mc_expr.upper(), 160)
def test_lmtd(self): m = ConcreteModel() m.x = Var(bounds=(0.1, 500), initialize=33.327) m.y = Var(bounds=(0.1, 500), initialize=14.436) m.z = Var(bounds=(0, 90), initialize=22.5653) mc_expr = mc(m.z - (m.x * m.y * (m.x + m.y) / 2) ** (1/3)) self.assertAlmostEqual(mc_expr.convex(), -407.95444629965016) self.assertAlmostEqual(mc_expr.lower(), -499.99999999999983)
def test_improved_bounds(self): m = ConcreteModel() m.x = Var(bounds=(0, 100), initialize=5) improved_bounds = ComponentMap() improved_bounds[m.x] = (10, 20) mc_expr = mc(m.x, improved_var_bounds=improved_bounds) self.assertEqual(mc_expr.lower(), 10) self.assertEqual(mc_expr.upper(), 20)
def add_affine_cuts(nlp_result, solve_data, config): with time_code(solve_data.timing, "affine cut generation"): m = solve_data.linear_GDP if config.calc_disjunctive_bounds: with time_code(solve_data.timing, "disjunctive variable bounding"): TransformationFactory('contrib.compute_disj_var_bounds').apply_to( m, solver=config.mip_solver if config.obbt_disjunctive_bounds else None ) config.logger.info("Adding affine cuts.") GDPopt = m.GDPopt_utils counter = 0 for var, val in zip(GDPopt.variable_list, nlp_result.var_values): if val is not None and not var.fixed: var.value = val for constr in constraints_in_True_disjuncts(m, config): # Note: this includes constraints that are deactivated in the current model (linear_GDP) disjunctive_var_bounds = disjunctive_bounds(constr.parent_block()) if constr.body.polynomial_degree() in (1, 0): continue vars_in_constr = list( identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff mc_eqn = mc(constr.body, disjunctive_var_bounds) # mc_eqn = mc(constr.body) ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() ub_int = min(constr.upper, mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max(constr.lower, mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() parent_block = constr.parent_block() # Create a block on which to put outer approximation cuts. aff_utils = parent_block.component('GDPopt_aff') if aff_utils is None: aff_utils = parent_block.GDPopt_aff = Block( doc="Block holding affine constraints") aff_utils.GDPopt_aff_cons = ConstraintList() aff_cuts = aff_utils.GDPopt_aff_cons concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr ) + ccStart >= lb_int convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr ) + cvStart <= ub_int aff_cuts.add(expr=concave_cut) aff_cuts.add(expr=convex_cut) counter += 2 config.logger.info("Added %s affine cuts" % counter)
def test_fixed_var(self): m = ConcreteModel() m.x = Var(bounds=(-50, 80), initialize=3) m.y = Var(bounds=(0, 6), initialize=2) m.y.fix() mc_expr = mc(m.x * m.y) self.assertEqual(mc_expr.lower(), -100) self.assertEqual(mc_expr.upper(), 160) self.assertEqual( str(mc_expr), "[ -1.00000e+02 : 1.60000e+02 ] [ 6.00000e+00 : 6.00000e+00 ] [ ( 2.00000e+00) : ( 2.00000e+00) ]")
def test_powers(self): m = ConcreteModel() m.x = Var(bounds=(0, 2), initialize=1) m.y = Var(bounds=(1e-4, 2), initialize=1) m.z = Var(bounds=(-1, 1), initialize=0) # Note: the exception raised prior to 2.1 was # "Log with negative values in range" # This was corrected in 2.1 to # "Square-root with nonpositive values in range" with self.assertRaisesRegex( MCPP_Error, r"(Square-root with nonpositive values in range)" r"|(Log with negative values in range)"): mc(m.z ** 1.5) mc_expr = mc(m.y ** 1.5) self.assertAlmostEqual(mc_expr.lower(), 1e-4**1.5) self.assertAlmostEqual(mc_expr.upper(), 2**1.5) mc_expr = mc(m.y ** m.x) self.assertAlmostEqual(mc_expr.lower(), 1e-4**2) self.assertAlmostEqual(mc_expr.upper(), 4) mc_expr = mc(m.z ** 2) self.assertAlmostEqual(mc_expr.lower(), 0) self.assertAlmostEqual(mc_expr.upper(), 1)
def add_affine_cuts(nlp_result, solve_data, config): m = solve_data.linear_GDP config.logger.info("Adding affine cuts.") GDPopt = m.GDPopt_utils for var, val in zip(GDPopt.working_var_list, nlp_result.var_values): if val is not None and not var.fixed: var.value = val for constr in constraints_in_True_disjuncts(m, config): # for constr in GDPopt.working_nonlinear_constraints: if constr not in GDPopt.working_nonlinear_constraints: continue # if constr.body.polynomial_degree() in (1, 0): # continue # TODO check that constraint is on active Disjunct vars_in_constr = list(EXPR.identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff mc_eqn = mc(constr.body) ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() ub_int = min(constr.upper, mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max(constr.lower, mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() parent_block = constr.parent_block() # Create a block on which to put outer approximation cuts. aff_utils = parent_block.component('GDPopt_aff') if aff_utils is None: aff_utils = parent_block.GDPopt_aff = Block( doc="Block holding affine constraints") aff_utils.GDPopt_aff_cons = ConstraintList() aff_cuts = aff_utils.GDPopt_aff_cons concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr) + ccStart >= lb_int convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr) + cvStart <= ub_int aff_cuts.add(expr=concave_cut) aff_cuts.add(expr=convex_cut)
def test_lmtd(self): m = ConcreteModel() m.x = Var(bounds=(0.1, 500), initialize=33.327) m.y = Var(bounds=(0.1, 500), initialize=14.436) m.z = Var(bounds=(0, 90), initialize=22.5653) e = m.z - (m.x * m.y * (m.x + m.y) / 2) ** (1/3) mc_expr = mc(e) for _x in [m.x.lb, m.x.ub]: m.x.value = _x mc_expr.changePoint(m.x, _x) for _y in [m.y.lb, m.y.ub]: m.y.value = _y mc_expr.changePoint(m.y, _y) for _z in [m.z.lb, m.z.ub]: m.z.value = _z mc_expr.changePoint(m.z, _z) self.assertGreaterEqual(mc_expr.concave() + 1e-8, value(e)) self.assertLessEqual(mc_expr.convex() - 1e-6, value(e)) m.x.value = m.x.lb m.y.value = m.y.lb m.z.value = m.z.lb mc_expr.changePoint(m.x, m.x.value) mc_expr.changePoint(m.y, m.y.value) mc_expr.changePoint(m.z, m.z.value) self.assertAlmostEqual(mc_expr.convex(), value(e)) self.assertAlmostEqual(mc_expr.concave(), value(e)) m.x.value = m.x.ub m.y.value = m.y.ub m.z.value = m.z.ub mc_expr.changePoint(m.x, m.x.value) mc_expr.changePoint(m.y, m.y.value) mc_expr.changePoint(m.z, m.z.value) self.assertAlmostEqual(mc_expr.convex(), value(e)) self.assertAlmostEqual(mc_expr.concave(), value(e)) self.assertAlmostEqual(mc_expr.lower(), -500) self.assertAlmostEqual(mc_expr.upper(), 89.9)
def add_lazy_affine_cuts(self, solve_data, config, opt): """ Adds affine cuts using MCPP; add affine cuts through Cplex inherent function self.add() Parameters ---------- solve_data: MindtPy Data Container data container that holds solve-instance data config: ConfigBlock contains the specific configurations for the algorithm opt: SolverFactory the mip solver """ m = solve_data.mip config.logger.info("Adding affine cuts") counter = 0 for constr in m.MindtPy_utils.constraint_list: if constr.body.polynomial_degree() in (1, 0): continue vars_in_constr = list(identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff try: mc_eqn = mc(constr.body) except MCPP_Error as e: config.logger.debug( "Skipping constraint %s due to MCPP error %s" % (constr.name, str(e))) continue # skip to the next constraint # TODO: check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this. ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() concave_cut_valid = True convex_cut_valid = True for var in vars_in_constr: if not var.fixed: if ccSlope[var] == float('nan') or ccSlope[var] == float( 'inf'): concave_cut_valid = False if cvSlope[var] == float('nan') or cvSlope[var] == float( 'inf'): convex_cut_valid = False if ccStart == float('nan') or ccStart == float('inf'): concave_cut_valid = False if cvStart == float('nan') or cvStart == float('inf'): convex_cut_valid = False # check if the value of ccSlope and cvSlope all equals zero. if so, we skip this. if not any(list(ccSlope.values())): concave_cut_valid = False if not any(list(cvSlope.values())): convex_cut_valid = False if (concave_cut_valid or convex_cut_valid) is False: continue ub_int = min( constr.upper, mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max( constr.lower, mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() parent_block = constr.parent_block() # Create a block on which to put outer approximation cuts. # TODO: create it at the beginning. aff_utils = parent_block.component('MindtPy_aff') if aff_utils is None: aff_utils = parent_block.MindtPy_aff = Block( doc="Block holding affine constraints") aff_utils.MindtPy_aff_cons = ConstraintList() aff_cuts = aff_utils.MindtPy_aff_cons if concave_cut_valid: pyomo_concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + ccStart cplex_concave_rhs = generate_standard_repn( pyomo_concave_cut).constant cplex_concave_cut, _ = opt._get_expr_from_pyomo_expr( pyomo_concave_cut) self.add(constraint=cplex.SparsePair( ind=cplex_concave_cut.variables, val=cplex_concave_cut.coefficients), sense="G", rhs=lb_int - cplex_concave_rhs) counter += 1 if convex_cut_valid: pyomo_convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + cvStart cplex_convex_rhs = generate_standard_repn( pyomo_convex_cut).constant cplex_convex_cut, _ = opt._get_expr_from_pyomo_expr( pyomo_convex_cut) self.add(constraint=cplex.SparsePair( ind=cplex_convex_cut.variables, val=cplex_convex_cut.coefficients), sense="L", rhs=ub_int - cplex_convex_rhs) # aff_cuts.add(expr=convex_cut) counter += 1 config.logger.info("Added %s affine cuts" % counter)
def add_affine_cuts(solve_data, config): """Adds affine cuts using MCPP. Parameters ---------- solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. """ with time_code(solve_data.timing, 'Affine cut generation'): m = solve_data.mip config.logger.debug('Adding affine cuts') counter = 0 for constr in m.MindtPy_utils.nonlinear_constraint_list: vars_in_constr = list( identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff try: mc_eqn = mc(constr.body) except MCPP_Error as e: config.logger.debug( 'Skipping constraint %s due to MCPP error %s' % (constr.name, str(e))) continue # skip to the next constraint ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() # check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this. concave_cut_valid = True convex_cut_valid = True for var in vars_in_constr: if not var.fixed: if ccSlope[var] == float('nan') or ccSlope[var] == float('inf'): concave_cut_valid = False if cvSlope[var] == float('nan') or cvSlope[var] == float('inf'): convex_cut_valid = False # check if the value of ccSlope and cvSlope all equals zero. if so, we skip this. if not any(list(ccSlope.values())): concave_cut_valid = False if not any(list(cvSlope.values())): convex_cut_valid = False if ccStart == float('nan') or ccStart == float('inf'): concave_cut_valid = False if cvStart == float('nan') or cvStart == float('inf'): convex_cut_valid = False if not (concave_cut_valid or convex_cut_valid): continue ub_int = min(value(constr.upper), mc_eqn.upper() ) if constr.has_ub() else mc_eqn.upper() lb_int = max(value(constr.lower), mc_eqn.lower() ) if constr.has_lb() else mc_eqn.lower() aff_cuts = m.MindtPy_utils.cuts.aff_cuts if concave_cut_valid: concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + ccStart >= lb_int aff_cuts.add(expr=concave_cut) counter += 1 if convex_cut_valid: convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + cvStart <= ub_int aff_cuts.add(expr=convex_cut) counter += 1 config.logger.debug('Added %s affine cuts' % counter)
def add_affine_cuts(solve_data, config): """ Adds affine cuts using MCPP; modifies the model to include affine cuts Parameters ---------- solve_data: MindtPy Data Container data container that holds solve-instance data config: ConfigBlock contains the specific configurations for the algorithm """ m = solve_data.mip config.logger.info("Adding affine cuts") counter = 0 for constr in m.MindtPy_utils.constraint_list: if constr.body.polynomial_degree() in (1, 0): continue vars_in_constr = list(identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff try: mc_eqn = mc(constr.body) except MCPP_Error as e: config.logger.debug("Skipping constraint %s due to MCPP error %s" % (constr.name, str(e))) continue # skip to the next constraint ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() # check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this. concave_cut_valid = True convex_cut_valid = True for var in vars_in_constr: if not var.fixed: if ccSlope[var] == float('nan') or ccSlope[var] == float( 'inf'): concave_cut_valid = False if cvSlope[var] == float('nan') or cvSlope[var] == float( 'inf'): convex_cut_valid = False # check if the value of ccSlope and cvSlope all equals zero. if so, we skip this. if not any(list(ccSlope.values())): concave_cut_valid = False if not any(list(cvSlope.values())): convex_cut_valid = False if ccStart == float('nan') or ccStart == float('inf'): concave_cut_valid = False if cvStart == float('nan') or cvStart == float('inf'): convex_cut_valid = False if (concave_cut_valid or convex_cut_valid) is False: continue ub_int = min(constr.upper, mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max(constr.lower, mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() parent_block = constr.parent_block() # Create a block on which to put outer approximation cuts. # TODO: create it at the beginning. aff_utils = parent_block.component('MindtPy_aff') if aff_utils is None: aff_utils = parent_block.MindtPy_aff = Block( doc="Block holding affine constraints") aff_utils.MindtPy_aff_cons = ConstraintList() aff_cuts = aff_utils.MindtPy_aff_cons if concave_cut_valid: concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + ccStart >= lb_int aff_cuts.add(expr=concave_cut) counter += 1 if convex_cut_valid: convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + cvStart <= ub_int aff_cuts.add(expr=convex_cut) counter += 1 config.logger.info("Added %s affine cuts" % counter)
def test_outofbounds(self): m = ConcreteModel() m.x = Var(bounds=(-1, 5), initialize=2) with self.assertRaisesRegexp(MCPP_Error, '.*Log with negative values in range'): mc(log(m.x))
def make3dPlot(expr, numticks=30, show_plot=False): ccSurf = [None] * ((numticks + 1)**2) cvSurf = [None] * ((numticks + 1)**2) fvals = [None] * ((numticks + 1)**2) xaxis2d = [None] * ((numticks + 1)**2) yaxis2d = [None] * ((numticks + 1)**2) ccAffine = [None] * ((numticks + 1)**2) cvAffine = [None] * ((numticks + 1)**2) eqn = mc(expr) vars = identify_variables(expr) x = next(vars) y = next(vars) x_tick_length = (x.ub - x.lb) / numticks y_tick_length = (y.ub - y.lb) / numticks xaxis = [x.lb + x_tick_length * n for n in range(numticks + 1)] yaxis = [y.lb + y_tick_length * n for n in range(numticks + 1)] # Making the affine tangent planes ccSlope = eqn.subcc() cvSlope = eqn.subcv() x_val = value(x) y_val = value(y) f_cc = eqn.concave() f_cv = eqn.convex() # To Visualize Concave Affine Plane for different points for i, x_tick in enumerate(xaxis): eqn.changePoint(x, x_tick) for j, y_tick in enumerate(yaxis): ccAffine[i + (numticks + 1) * j] = ( ccSlope[x] * (x_tick - x_val) + ccSlope[y] * (y_tick - y_val) + f_cc) cvAffine[i + (numticks + 1) * j] = ( cvSlope[x] * (x_tick - x_val) + cvSlope[y] * (y_tick - y_val) + f_cv) xaxis2d[i + (numticks + 1) * j] = x_tick yaxis2d[i + (numticks + 1) * j] = y_tick eqn.changePoint(y, y_tick) ccSurf[i + (numticks + 1) * j] = eqn.concave() cvSurf[i + (numticks + 1) * j] = eqn.convex() fvals[i + (numticks + 1) * j] = value(expr) if show_plot: import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D assert Axes3D # silence pyflakes # Plotting Solutions in 3D fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') ax.scatter(xaxis2d, yaxis2d, cvSurf, color='b') ax.scatter(xaxis2d, yaxis2d, fvals, color='r') ax.scatter(xaxis2d, yaxis2d, ccSurf, color='b') # To Visualize Concave Affine Plane for different points ax.scatter(xaxis2d, yaxis2d, cvAffine, color='k') # Create a better view ax.view_init(10, 270) plt.show() return ccSurf, cvSurf, ccAffine, cvAffine
def test_nonpyomo_numeric(self): mc_expr = mc(-2) self.assertEqual(mc_expr.lower(), -2) self.assertEqual(mc_expr.upper(), -2)
def test_abs(self): m = ConcreteModel() m.x = Var(bounds=(-1, 1), initialize=0) mc_expr = mc(abs((m.x))) self.assertEqual(mc_expr.lower(), 0) self.assertEqual(mc_expr.upper(), 1)
def make3dPlot(expr, numticks=30, show_plot=False): ccSurf = [None] * ((numticks + 1)**2) cvSurf = [None] * ((numticks + 1)**2) fvals = [None] * ((numticks + 1)**2) xaxis2d = [None] * ((numticks + 1)**2) yaxis2d = [None] * ((numticks + 1)**2) ccAffine = [None] * ((numticks + 1)**2) cvAffine = [None] * ((numticks + 1)**2) eqn = mc(expr) vars = identify_variables(expr) x = next(vars) y = next(vars) x_tick_length = (x.ub - x.lb) / numticks y_tick_length = (y.ub - y.lb) / numticks xaxis = [x.lb + x_tick_length * n for n in range(numticks + 1)] yaxis = [y.lb + y_tick_length * n for n in range(numticks + 1)] # Making the affine tangent planes ccSlope = eqn.subcc() cvSlope = eqn.subcv() x_val = value(x) y_val = value(y) f_cc = eqn.concave() f_cv = eqn.convex() # To Visualize Concave Affine Plane for different points for i, x_tick in enumerate(xaxis): eqn.changePoint(x, x_tick) for j, y_tick in enumerate(yaxis): ccAffine[i + (numticks + 1) * j] = ( ccSlope[x] * (x_tick - x_val) + ccSlope[y] * (y_tick - y_val) + f_cc) cvAffine[i + (numticks + 1) * j] = ( cvSlope[x] * (x_tick - x_val) + cvSlope[y] * (y_tick - y_val) + f_cv) xaxis2d[i + (numticks + 1) * j] = x_tick yaxis2d[i + (numticks + 1) * j] = y_tick eqn.changePoint(y, y_tick) ccSurf[i + (numticks + 1) * j] = eqn.concave() cvSurf[i + (numticks + 1) * j] = eqn.convex() fvals[i + (numticks + 1) * j] = value(expr) if show_plot: from mpl_toolkits.mplot3d import Axes3D assert Axes3D # silence pyflakes # Plotting Solutions in 3D fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') ax.scatter(xaxis2d, yaxis2d, cvSurf, color='b') ax.scatter(xaxis2d, yaxis2d, fvals, color='r') ax.scatter(xaxis2d, yaxis2d, ccSurf, color='b') # To Visualize Concave Affine Plane for different points ax.scatter(xaxis2d, yaxis2d, cvAffine, color='k') # Create a better view ax.view_init(10, 270) plt.show() return ccSurf, cvSurf, ccAffine, cvAffine
def test_outofbounds(self): m = ConcreteModel() m.x = Var(bounds=(-1, 5), initialize=2) with self.assertRaisesRegex(MCPP_Error, '.*Log with negative values in range'): mc(log(m.x))
def add_lazy_affine_cuts(self, solve_data, config, opt): """Adds affine cuts using MCPP. Add affine cuts through Cplex inherent function self.add(). Parameters ---------- solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. opt : SolverFactory The cplex_persistent solver. """ with time_code(solve_data.timing, 'Affine cut generation'): m = solve_data.mip config.logger.debug('Adding affine cuts') counter = 0 for constr in m.MindtPy_utils.nonlinear_constraint_list: vars_in_constr = list(identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff try: mc_eqn = mc(constr.body) except MCPP_Error as e: config.logger.debug( 'Skipping constraint %s due to MCPP error %s' % (constr.name, str(e))) continue # skip to the next constraint # TODO: check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this. ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() concave_cut_valid = True convex_cut_valid = True for var in vars_in_constr: if not var.fixed: if ccSlope[var] == float( 'nan') or ccSlope[var] == float('inf'): concave_cut_valid = False if cvSlope[var] == float( 'nan') or cvSlope[var] == float('inf'): convex_cut_valid = False if ccStart == float('nan') or ccStart == float('inf'): concave_cut_valid = False if cvStart == float('nan') or cvStart == float('inf'): convex_cut_valid = False # check if the value of ccSlope and cvSlope all equals zero. if so, we skip this. if not any(ccSlope.values()): concave_cut_valid = False if not any(cvSlope.values()): convex_cut_valid = False if not (concave_cut_valid or convex_cut_valid): continue ub_int = min( value(constr.upper), mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max( value(constr.lower), mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() if concave_cut_valid: pyomo_concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + ccStart cplex_concave_rhs = generate_standard_repn( pyomo_concave_cut).constant cplex_concave_cut, _ = opt._get_expr_from_pyomo_expr( pyomo_concave_cut) self.add(constraint=cplex.SparsePair( ind=cplex_concave_cut.variables, val=cplex_concave_cut.coefficients), sense='G', rhs=lb_int - cplex_concave_rhs) counter += 1 if convex_cut_valid: pyomo_convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr if not var.fixed) + cvStart cplex_convex_rhs = generate_standard_repn( pyomo_convex_cut).constant cplex_convex_cut, _ = opt._get_expr_from_pyomo_expr( pyomo_convex_cut) self.add(constraint=cplex.SparsePair( ind=cplex_convex_cut.variables, val=cplex_convex_cut.coefficients), sense='L', rhs=ub_int - cplex_convex_rhs) counter += 1 config.logger.info('Added %s affine cuts' % counter)