def handle_main_max_timelimit(main_mip, main_mip_results, solve_data, config): """This function handles the result of the latest iteration of solving the MIP problem given that solving the MIP takes too long. Parameters ---------- main_mip : Pyomo model The MIP main problem. main_mip_results : [type] Results from solving the MIP main subproblem. solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. """ # TODO if we have found a valid feasible solution, we take that, if not, we can at least use the dual bound MindtPy = main_mip.MindtPy_utils config.logger.info('Unable to optimize MILP main problem ' 'within time limit. ' 'Using current solver feasible solution.') copy_var_list_values(main_mip.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list, config) update_suboptimal_dual_bound(solve_data, main_mip_results) config.logger.info( solve_data.log_formatter.format( solve_data.mip_iter, 'MILP', value(MindtPy.mip_obj.expr), solve_data.LB, solve_data.UB, solve_data.rel_gap, get_main_elapsed_time(solve_data.timing)))
def handle_main_other_conditions(main_mip, main_mip_results, solve_data, config): """This function handles the result of the latest iteration of solving the MIP problem (given any of a few edge conditions, such as if the solution is neither infeasible nor optimal). Parameters ---------- main_mip : Pyomo model The MIP main problem. main_mip_results : SolverResults Results from solving the MIP problem. solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. Raises ------ ValueError MindtPy unable to handle MILP main termination condition. """ if main_mip_results.solver.termination_condition is tc.infeasible: handle_main_infeasible(main_mip, solve_data, config) elif main_mip_results.solver.termination_condition is tc.unbounded: temp_results = handle_main_unbounded(main_mip, solve_data, config) elif main_mip_results.solver.termination_condition is tc.infeasibleOrUnbounded: temp_results = handle_main_unbounded(main_mip, solve_data, config) if temp_results.solver.termination_condition is tc.infeasible: handle_main_infeasible(main_mip, solve_data, config) elif main_mip_results.solver.termination_condition is tc.maxTimeLimit: handle_main_max_timelimit(main_mip, main_mip_results, solve_data, config) solve_data.results.solver.termination_condition = tc.maxTimeLimit elif (main_mip_results.solver.termination_condition is tc.other and main_mip_results.solution.status is SolutionStatus.feasible): # load the solution and suppress the warning message by setting # solver status to ok. MindtPy = main_mip.MindtPy_utils config.logger.info('MILP solver reported feasible solution, ' 'but not guaranteed to be optimal.') copy_var_list_values( main_mip.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list, config) update_suboptimal_dual_bound(solve_data, main_mip_results) config.logger.info( solve_data.log_formatter.format( solve_data.mip_iter, 'MILP', value(MindtPy.mip_obj.expr), solve_data.LB, solve_data.UB, solve_data.rel_gap, get_main_elapsed_time(solve_data.timing))) else: raise ValueError( 'MindtPy unable to handle MILP main termination condition ' 'of %s. Solver message: %s' % (main_mip_results.solver.termination_condition, main_mip_results.solver.message))
def init_rNLP(solve_data, config): """Initialize the problem by solving the relaxed NLP and then store the optimal variable values obtained from solving the rNLP. Parameters ---------- solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. Raises ------ ValueError MindtPy unable to handle the termination condition of the relaxed NLP. """ m = solve_data.working_model.clone() config.logger.debug('Relaxed NLP: Solve relaxed integrality') MindtPy = m.MindtPy_utils TransformationFactory('core.relax_integer_vars').apply_to(m) nlp_args = dict(config.nlp_solver_args) nlpopt = SolverFactory(config.nlp_solver) set_solver_options(nlpopt, solve_data, config, solver_type='nlp') with SuppressInfeasibleWarning(): results = nlpopt.solve(m, tee=config.nlp_solver_tee, **nlp_args) subprob_terminate_cond = results.solver.termination_condition if subprob_terminate_cond in {tc.optimal, tc.feasible, tc.locallyOptimal}: main_objective = MindtPy.objective_list[-1] if subprob_terminate_cond == tc.optimal: update_dual_bound(solve_data, value(main_objective.expr)) else: config.logger.info('relaxed NLP is not solved to optimality.') update_suboptimal_dual_bound(solve_data, results) dual_values = list( m.dual[c] for c in MindtPy.constraint_list) if config.calculate_dual else None config.logger.info( solve_data.log_formatter.format( '-', 'Relaxed NLP', value(main_objective.expr), solve_data.LB, solve_data.UB, solve_data.rel_gap, get_main_elapsed_time(solve_data.timing))) # Add OA cut if config.strategy in {'OA', 'GOA', 'FP'}: copy_var_list_values(m.MindtPy_utils.variable_list, solve_data.mip.MindtPy_utils.variable_list, config, ignore_integrality=True) if config.init_strategy == 'FP': copy_var_list_values( m.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list, config, ignore_integrality=True) if config.strategy in {'OA', 'FP'}: add_oa_cuts(solve_data.mip, dual_values, solve_data, config) elif config.strategy == 'GOA': add_affine_cuts(solve_data, config) for var in solve_data.mip.MindtPy_utils.discrete_variable_list: # We don't want to trigger the reset of the global stale # indicator, so we will set this variable to be "stale", # knowing that set_value will switch it back to "not # stale" var.stale = True var.set_value(int(round(var.value)), skip_validation=True) elif subprob_terminate_cond in {tc.infeasible, tc.noSolution}: # TODO fail? try something else? config.logger.info('Initial relaxed NLP problem is infeasible. ' 'Problem may be infeasible.') elif subprob_terminate_cond is tc.maxTimeLimit: config.logger.info( 'NLP subproblem failed to converge within time limit.') solve_data.results.solver.termination_condition = tc.maxTimeLimit elif subprob_terminate_cond is tc.maxIterations: config.logger.info( 'NLP subproblem failed to converge within iteration limit.') else: raise ValueError( 'MindtPy unable to handle relaxed NLP termination condition ' 'of %s. Solver message: %s' % (subprob_terminate_cond, results.solver.message))
def fix_dual_bound(solve_data, config, last_iter_cuts): """Fix the dual bound when no-good cuts or tabu list is activated. Parameters ---------- solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. last_iter_cuts : bool Whether the cuts in the last iteration have been added. """ if config.single_tree: config.logger.info( 'Fix the bound to the value of one iteration before optimal solution is found.') try: if solve_data.objective_sense == minimize: solve_data.LB = solve_data.stored_bound[solve_data.UB] else: solve_data.UB = solve_data.stored_bound[solve_data.LB] except KeyError: config.logger.info('No stored bound found. Bound fix failed.') else: config.logger.info( 'Solve the main problem without the last no_good cut to fix the bound.' 'zero_tolerance is set to 1E-4') config.zero_tolerance = 1E-4 # Solve NLP subproblem # The constraint linearization happens in the handlers if not last_iter_cuts: fixed_nlp, fixed_nlp_result = solve_subproblem(solve_data, config) handle_nlp_subproblem_tc( fixed_nlp, fixed_nlp_result, solve_data, config) MindtPy = solve_data.mip.MindtPy_utils # deactivate the integer cuts generated after the best solution was found. if config.strategy == 'GOA': try: if solve_data.objective_sense == minimize: valid_no_good_cuts_num = solve_data.num_no_good_cuts_added[solve_data.UB] else: valid_no_good_cuts_num = solve_data.num_no_good_cuts_added[solve_data.LB] if config.add_no_good_cuts: for i in range(valid_no_good_cuts_num+1, len(MindtPy.cuts.no_good_cuts)+1): MindtPy.cuts.no_good_cuts[i].deactivate() if config.use_tabu_list: solve_data.integer_list = solve_data.integer_list[:valid_no_good_cuts_num] except KeyError: config.logger.info('No-good cut deactivate failed.') elif config.strategy == 'OA': # Only deactive the last OA cuts may not be correct. # Since integer solution may also be cut off by OA cuts due to calculation approximation. if config.add_no_good_cuts: MindtPy.cuts.no_good_cuts[len( MindtPy.cuts.no_good_cuts)].deactivate() if config.use_tabu_list: solve_data.integer_list = solve_data.integer_list[:-1] if config.add_regularization is not None and MindtPy.find_component('mip_obj') is None: MindtPy.objective_list[-1].activate() mainopt = SolverFactory(config.mip_solver) # determine if persistent solver is called. if isinstance(mainopt, PersistentSolver): mainopt.set_instance(solve_data.mip, symbolic_solver_labels=True) if config.use_tabu_list: tabulist = mainopt._solver_model.register_callback( tabu_list.IncumbentCallback_cplex) tabulist.solve_data = solve_data tabulist.opt = mainopt tabulist.config = config mainopt._solver_model.parameters.preprocessing.reduce.set(1) # If the callback is used to reject incumbents, the user must set the # parameter c.parameters.preprocessing.reduce either to the value 1 (one) # to restrict presolve to primal reductions only or to 0 (zero) to disable all presolve reductions mainopt._solver_model.set_warning_stream(None) mainopt._solver_model.set_log_stream(None) mainopt._solver_model.set_error_stream(None) mip_args = dict(config.mip_solver_args) set_solver_options(mainopt, solve_data, config, solver_type='mip') main_mip_results = mainopt.solve( solve_data.mip, tee=config.mip_solver_tee, **mip_args) if main_mip_results.solver.termination_condition is tc.infeasible: config.logger.info( 'Bound fix failed. The bound fix problem is infeasible') else: update_suboptimal_dual_bound(solve_data, main_mip_results) config.logger.info( 'Fixed bound values: LB: {} UB: {}'. format(solve_data.LB, solve_data.UB)) # Check bound convergence if solve_data.LB + config.bound_tolerance >= solve_data.UB: solve_data.results.solver.termination_condition = tc.optimal
def solve_main(solve_data, config, fp=False, regularization_problem=False): """This function solves the MIP main problem. Parameters ---------- solve_data : MindtPySolveData Data container that holds solve-instance data. config : ConfigBlock The specific configurations for MindtPy. fp : bool, optional Whether it is in the loop of feasibility pump, by default False. regularization_problem : bool, optional Whether it is solving a regularization problem, by default False. Returns ------- solve_data.mip : Pyomo model The MIP stored in solve_data. main_mip_results : SolverResults Results from solving the main MIP. """ if not fp and not regularization_problem: solve_data.mip_iter += 1 # setup main problem setup_main(solve_data, config, fp, regularization_problem) mainopt = set_up_mip_solver(solve_data, config, regularization_problem) mip_args = dict(config.mip_solver_args) if config.mip_solver in { 'cplex', 'cplex_persistent', 'gurobi', 'gurobi_persistent' }: mip_args['warmstart'] = True set_solver_options(mainopt, solve_data, config, solver_type='mip', regularization=regularization_problem) try: with time_code( solve_data.timing, 'regularization main' if regularization_problem else ('fp main' if fp else 'main')): main_mip_results = mainopt.solve(solve_data.mip, tee=config.mip_solver_tee, **mip_args) except (ValueError, AttributeError): if config.single_tree: config.logger.warning('Single tree terminate.') if get_main_elapsed_time( solve_data.timing) >= config.time_limit - 2: config.logger.warning('due to the timelimit.') solve_data.results.solver.termination_condition = tc.maxTimeLimit if config.strategy == 'GOA' or config.add_no_good_cuts: config.logger.warning( 'ValueError: Cannot load a SolverResults object with bad status: error. ' 'MIP solver failed. This usually happens in the single-tree GOA algorithm. ' "No-good cuts are added and GOA algorithm doesn't converge within the time limit. " 'No integer solution is found, so the cplex solver will report an error status. ' ) return None, None if config.solution_pool: main_mip_results._solver_model = mainopt._solver_model main_mip_results._pyomo_var_to_solver_var_map = mainopt._pyomo_var_to_solver_var_map if main_mip_results.solver.termination_condition is tc.optimal: if config.single_tree and not config.add_no_good_cuts and not regularization_problem: update_suboptimal_dual_bound(solve_data, main_mip_results) if regularization_problem: config.logger.info( solve_data.log_formatter.format( solve_data.mip_iter, 'Reg ' + solve_data.regularization_mip_type, value(solve_data.mip.MindtPy_utils.loa_proj_mip_obj), solve_data.LB, solve_data.UB, solve_data.rel_gap, get_main_elapsed_time(solve_data.timing))) elif main_mip_results.solver.termination_condition is tc.infeasibleOrUnbounded: # Linear solvers will sometimes tell me that it's infeasible or # unbounded during presolve, but fails to distinguish. We need to # resolve with a solver option flag on. main_mip_results, _ = distinguish_mip_infeasible_or_unbounded( solve_data.mip, config) return solve_data.mip, main_mip_results if regularization_problem: solve_data.mip.MindtPy_utils.objective_constr.deactivate() solve_data.mip.MindtPy_utils.del_component('loa_proj_mip_obj') solve_data.mip.MindtPy_utils.cuts.del_component('obj_reg_estimate') if config.add_regularization == 'level_L1': solve_data.mip.MindtPy_utils.del_component('L1_obj') elif config.add_regularization == 'level_L_infinity': solve_data.mip.MindtPy_utils.del_component('L_infinity_obj') return solve_data.mip, main_mip_results