def MindtPy_iteration_loop(solve_data, config): m = solve_data.working_model main_objective = next(m.component_data_objects(Objective, active=True)) MindtPy = m.MindtPy_utils while solve_data.mip_iter < config.iteration_limit: config.logger.info( '---MindtPy Master Iteration %s---' % solve_data.mip_iter) if algorithm_should_terminate(solve_data, config): break solve_data.mip_subiter = 0 # solve MILP master problem if config.strategy == 'OA': solve_OA_master(solve_data, config) else: raise NotImplementedError() if algorithm_should_terminate(solve_data, config): break # Solve NLP subproblem solve_NLP_subproblem(solve_data, config) # If the hybrid algorithm is not making progress, switch to OA. progress_required = 1E-6 if main_objective.sense == minimize: log = solve_data.LB_progress sign_adjust = 1 else: log = solve_data.UB_progress sign_adjust = -1 # Maximum number of iterations in which the lower (optimistic) # bound does not improve before switching to OA max_nonimprove_iter = 5 making_progress = True for i in range(1, max_nonimprove_iter + 1): try: if (sign_adjust * log[-i] <= (log[-i - 1] + progress_required) * sign_adjust): making_progress = False else: making_progress = True break except IndexError: # Not enough history yet, keep going. making_progress = True break if not making_progress and ( config.strategy == 'hPSC' and config.strategy == 'PSC'): config.logger.info( 'Not making enough progress for {} iterations. ' 'Switching to OA.'.format(max_nonimprove_iter)) config.strategy = 'OA'
def __call__(self): solve_data = self.solve_data config = self.config opt = self.opt master_mip = self.master_mip cpx = opt._solver_model # Cplex model self.handle_lazy_master_mip_feasible_sol(master_mip, solve_data, config, opt) # solve subproblem # Solve NLP subproblem # The constraint linearization happens in the handlers fixed_nlp, fixed_nlp_result = solve_NLP_subproblem(solve_data, config) # add oa cuts if fixed_nlp_result.solver.termination_condition is tc.optimal or fixed_nlp_result.solver.termination_condition is tc.locallyOptimal: self.handle_lazy_NLP_subproblem_optimal(fixed_nlp, solve_data, config, opt) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: self.handle_lazy_NLP_subproblem_infeasible(fixed_nlp, solve_data, config, opt) else: self.handle_lazy_NLP_subproblem_other_termination( fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config)
def MindtPy_initialize_master(solve_data, config): """Initialize the decomposition algorithm. This includes generating the initial cuts require to build the master problem. """ m = solve_data.mip = solve_data.working_model.clone() MindtPy = m.MindtPy_utils m.dual.activate() if config.strategy == 'OA': calc_jacobians(solve_data, config) # preload jacobians MindtPy.MindtPy_linear_cuts.oa_cuts = ConstraintList( doc='Outer approximation cuts') # elif config.strategy == 'ECP': # calc_jacobians(solve_data, config) # preload jacobians # MindtPy.MindtPy_linear_cuts.ecp_cuts = ConstraintList( # doc='Extended Cutting Planes') # elif config.strategy == 'PSC': # detect_nonlinear_vars(solve_data, config) # MindtPy.MindtPy_linear_cuts.psc_cuts = ConstraintList( # doc='Partial surrogate cuts') # elif config.strategy == 'GBD': # MindtPy.MindtPy_linear_cuts.gbd_cuts = ConstraintList( # doc='Generalized Benders cuts') # Set default initialization_strategy if config.init_strategy is None: if config.strategy == 'OA': config.init_strategy = 'rNLP' else: config.init_strategy = 'max_binary' # Do the initialization elif config.init_strategy == 'rNLP': init_rNLP(solve_data, config) elif config.init_strategy == 'max_binary': init_max_binaries(solve_data, config) # if config.strategy == 'ECP': # add_ecp_cut(solve_data, config) # else: solve_NLP_subproblem(solve_data, config)
def MindtPy_initialize_master(solve_data, config): """Initialize the decomposition algorithm. This includes generating the initial cuts require to build the master problem. """ # if single tree is activated, we need to add bounds for unbounded variables in nonlinear constraints to avoid unbounded master problem. if config.single_tree: var_bound_add(solve_data, config) m = solve_data.mip = solve_data.working_model.clone() MindtPy = m.MindtPy_utils m.dual.deactivate() if config.strategy == 'OA': calc_jacobians(solve_data, config) # preload jacobians MindtPy.MindtPy_linear_cuts.oa_cuts = ConstraintList( doc='Outer approximation cuts') # elif config.strategy == 'ECP': # calc_jacobians(solve_data, config) # preload jacobians # MindtPy.MindtPy_linear_cuts.ecp_cuts = ConstraintList( # doc='Extended Cutting Planes') # elif config.strategy == 'PSC': # detect_nonlinear_vars(solve_data, config) # MindtPy.MindtPy_linear_cuts.psc_cuts = ConstraintList( # doc='Partial surrogate cuts') # elif config.strategy == 'GBD': # MindtPy.MindtPy_linear_cuts.gbd_cuts = ConstraintList( # doc='Generalized Benders cuts') # Set default initialization_strategy if config.init_strategy is None: if config.strategy == 'OA': config.init_strategy = 'rNLP' else: config.init_strategy = 'max_binary' # Do the initialization elif config.init_strategy == 'rNLP': init_rNLP(solve_data, config) elif config.init_strategy == 'max_binary': init_max_binaries(solve_data, config) # if config.strategy == 'ECP': # add_ecp_cut(solve_data, config) # else: fixed_nlp, fixed_nlp_result = solve_NLP_subproblem(solve_data, config) if fixed_nlp_result.solver.termination_condition is tc.optimal or fixed_nlp_result.solver.termination_condition is tc.locallyOptimal: handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) else: handle_NLP_subproblem_other_termination(fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config)
def __call__(self): """ This is an inherent function in LazyConstraintCallback in cplex. This funtion is call whenever the a integer solution is found during the branch and bound process """ solve_data = self.solve_data config = self.config opt = self.opt master_mip = self.master_mip cpx = opt._solver_model # Cplex model self.handle_lazy_master_mip_feasible_sol(master_mip, solve_data, config, opt) if solve_data.LB + config.bound_tolerance >= solve_data.UB: config.logger.info('MindtPy exiting on bound convergence. ' 'LB: {} + (tol {}) >= UB: {}\n'.format( solve_data.LB, config.bound_tolerance, solve_data.UB)) solve_data.results.solver.termination_condition = tc.optimal return # else: # solve subproblem # Solve NLP subproblem # The constraint linearization happens in the handlers fixed_nlp, fixed_nlp_result = solve_NLP_subproblem(solve_data, config) # add oa cuts if fixed_nlp_result.solver.termination_condition in { tc.optimal, tc.locallyOptimal, tc.feasible }: self.handle_lazy_NLP_subproblem_optimal(fixed_nlp, solve_data, config, opt) if solve_data.LB + config.bound_tolerance >= solve_data.UB: config.logger.info('MindtPy exiting on bound convergence. ' 'LB: {} + (tol {}) >= UB: {}\n'.format( solve_data.LB, config.bound_tolerance, solve_data.UB)) solve_data.results.solver.termination_condition = tc.optimal return elif fixed_nlp_result.solver.termination_condition is tc.infeasible: self.handle_lazy_NLP_subproblem_infeasible(fixed_nlp, solve_data, config, opt) else: self.handle_lazy_NLP_subproblem_other_termination( fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config)
def MindtPy_iteration_loop(solve_data, config): working_model = solve_data.working_model main_objective = next( working_model.component_data_objects(Objective, active=True)) while solve_data.mip_iter < config.iteration_limit: config.logger.info('---MindtPy Master Iteration %s---' % solve_data.mip_iter) if algorithm_should_terminate(solve_data, config, check_cycling=False): break solve_data.mip_subiter = 0 # solve MILP master problem if config.strategy == 'OA': master_mip, master_mip_results = solve_OA_master( solve_data, config) if master_mip_results.solver.termination_condition is tc.optimal: handle_master_mip_optimal(master_mip, solve_data, config) else: handle_master_mip_other_conditions(master_mip, master_mip_results, solve_data, config) # Call the MILP post-solve callback config.call_after_master_solve(master_mip, solve_data) else: raise NotImplementedError() if algorithm_should_terminate(solve_data, config, check_cycling=True): break if config.single_tree is False: # if we don't use lazy callback, i.e. LP_NLP # Solve NLP subproblem # The constraint linearization happens in the handlers fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( solve_data, config) if fixed_nlp_result.solver.termination_condition is tc.optimal or fixed_nlp_result.solver.termination_condition is tc.locallyOptimal: handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) else: handle_NLP_subproblem_other_termination( fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config) # Call the NLP post-solve callback config.call_after_subproblem_solve(fixed_nlp, solve_data)
def MindtPy_iteration_loop(solve_data, config): working_model = solve_data.working_model main_objective = next( working_model.component_data_objects(Objective, active=True)) while solve_data.mip_iter < config.iteration_limit: config.logger.info('---MindtPy Master Iteration %s---' % solve_data.mip_iter) if algorithm_should_terminate(solve_data, config): break solve_data.mip_subiter = 0 # solve MILP master problem if config.strategy == 'OA': master_mip, master_mip_results = solve_OA_master( solve_data, config) if master_mip_results.solver.termination_condition is tc.optimal: handle_master_mip_optimal(master_mip, solve_data, config) else: handle_master_mip_other_conditions(master_mip, master_mip_results, solve_data, config) # Call the MILP post-solve callback config.call_after_master_solve(master_mip, solve_data) else: raise NotImplementedError() if algorithm_should_terminate(solve_data, config): break # Solve NLP subproblem # The constraint linearization happens in the handlers fix_nlp, fix_nlp_result = solve_NLP_subproblem(solve_data, config) if fix_nlp_result.solver.termination_condition is tc.optimal: handle_NLP_subproblem_optimal(fix_nlp, solve_data, config) elif fix_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fix_nlp, solve_data, config) else: handle_NLP_subproblem_other_termination( fix_nlp, fix_nlp_result.solver.termination_condition, solve_data, config) # Call the NLP post-solve callback config.call_after_subproblem_solve(fix_nlp, solve_data) if config.strategy == 'PSC': # If the hybrid algorithm is not making progress, switch to OA. progress_required = 1E-6 if main_objective.sense == minimize: log = solve_data.LB_progress sign_adjust = 1 else: log = solve_data.UB_progress sign_adjust = -1 # Maximum number of iterations in which the lower (optimistic) # bound does not improve before switching to OA max_nonimprove_iter = 5 making_progress = True # TODO-romeo Unneccesary for OA and LOA, right? for i in range(1, max_nonimprove_iter + 1): try: if (sign_adjust * log[-i] <= (log[-i - 1] + progress_required) * sign_adjust): making_progress = False else: making_progress = True break except IndexError: # Not enough history yet, keep going. making_progress = True break if not making_progress and (config.strategy == 'hPSC' or config.strategy == 'PSC'): config.logger.info( 'Not making enough progress for {} iterations. ' 'Switching to OA.'.format(max_nonimprove_iter)) config.strategy = 'OA'
def bound_fix(solve_data, config, last_iter_cuts): if config.single_tree: config.logger.info( 'Fix the bound to the value of one iteration before optimal solution is found.' ) if solve_data.results.problem.sense == ProblemSense.minimize: solve_data.LB = solve_data.stored_bound[solve_data.UB] else: solve_data.UB = solve_data.stored_bound[solve_data.LB] else: config.logger.info( 'Solve the master problem without the last nogood 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 last_iter_cuts is False: fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( solve_data, config) if fixed_nlp_result.solver.termination_condition in { tc.optimal, tc.locallyOptimal, tc.feasible }: handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) else: handle_NLP_subproblem_other_termination( fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config) MindtPy = solve_data.mip.MindtPy_utils # only deactivate the last integer cut. if config.strategy == 'GOA': if solve_data.results.problem.sense == ProblemSense.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] for i in range(valid_no_good_cuts_num + 1, len(MindtPy.MindtPy_linear_cuts.integer_cuts) + 1): MindtPy.MindtPy_linear_cuts.integer_cuts[i].deactivate() elif config.strategy == 'OA': MindtPy.MindtPy_linear_cuts.integer_cuts[len( MindtPy.MindtPy_linear_cuts.integer_cuts)].deactivate() # MindtPy.MindtPy_linear_cuts.oa_cuts.activate() masteropt = SolverFactory(config.mip_solver) # determine if persistent solver is called. if isinstance(masteropt, PersistentSolver): masteropt.set_instance(solve_data.mip, symbolic_solver_labels=True) mip_args = dict(config.mip_solver_args) elapsed = get_main_elapsed_time(solve_data.timing) remaining = int(max(config.time_limit - elapsed, 1)) if config.mip_solver == 'gams': mip_args['add_options'] = mip_args.get('add_options', []) mip_args['add_options'].append('option optcr=0.001;') if config.threads > 0: masteropt.options["threads"] = config.threads master_mip_results = masteropt.solve(solve_data.mip, tee=config.solver_tee, **mip_args) main_objective = next( solve_data.working_model.component_data_objects(Objective, active=True)) if main_objective.sense == minimize: solve_data.LB = max([master_mip_results.problem.lower_bound] + solve_data.LB_progress[:-1]) solve_data.LB_progress.append(solve_data.LB) else: solve_data.UB = min([master_mip_results.problem.upper_bound] + solve_data.UB_progress[:-1]) solve_data.UB_progress.append(solve_data.UB) 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 MindtPy_iteration_loop(solve_data, config): """ Main loop for MindtPy Algorithms This is the outermost function for the algorithms in this package; this function controls the progression of solving the model. Parameters ---------- solve_data: MindtPy Data Container data container that holds solve-instance data config: ConfigBlock contains the specific configurations for the algorithm """ working_model = solve_data.working_model main_objective = next( working_model.component_data_objects(Objective, active=True)) while solve_data.mip_iter < config.iteration_limit: config.logger.info('---MindtPy Master Iteration %s---' % solve_data.mip_iter) solve_data.mip_subiter = 0 # solve MILP master problem if config.strategy in {'OA', 'GOA', 'ECP'}: master_mip, master_mip_results = solve_OA_master( solve_data, config) if config.single_tree is False: if master_mip_results.solver.termination_condition is tc.optimal: handle_master_mip_optimal(master_mip, solve_data, config) elif master_mip_results.solver.termination_condition is tc.infeasible: handle_master_mip_infeasible(master_mip, solve_data, config) last_iter_cuts = True break else: handle_master_mip_other_conditions(master_mip, master_mip_results, solve_data, config) # Call the MILP post-solve callback config.call_after_master_solve(master_mip, solve_data) else: raise NotImplementedError() if algorithm_should_terminate(solve_data, config, check_cycling=True): last_iter_cuts = False break if config.single_tree is False and config.strategy != 'ECP': # if we don't use lazy callback, i.e. LP_NLP # Solve NLP subproblem # The constraint linearization happens in the handlers fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( solve_data, config) if fixed_nlp_result.solver.termination_condition in { tc.optimal, tc.locallyOptimal, tc.feasible }: handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) else: handle_NLP_subproblem_other_termination( fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config) # Call the NLP post-solve callback config.call_after_subproblem_solve(fixed_nlp, solve_data) if algorithm_should_terminate(solve_data, config, check_cycling=False): last_iter_cuts = True break if config.strategy == 'ECP': add_ecp_cuts(solve_data.mip, solve_data, config) # if config.strategy == 'PSC': # # If the hybrid algorithm is not making progress, switch to OA. # progress_required = 1E-6 # if main_objective.sense == minimize: # log = solve_data.LB_progress # sign_adjust = 1 # else: # log = solve_data.UB_progress # sign_adjust = -1 # # Maximum number of iterations in which the lower (optimistic) # # bound does not improve before switching to OA # max_nonimprove_iter = 5 # making_progress = True # # TODO-romeo Unneccesary for OA and LOA, right? # for i in range(1, max_nonimprove_iter + 1): # try: # if (sign_adjust * log[-i] # <= (log[-i - 1] + progress_required) # * sign_adjust): # making_progress = False # else: # making_progress = True # break # except IndexError: # # Not enough history yet, keep going. # making_progress = True # break # if not making_progress and ( # config.strategy == 'hPSC' or # config.strategy == 'PSC'): # config.logger.info( # 'Not making enough progress for {} iterations. ' # 'Switching to OA.'.format(max_nonimprove_iter)) # config.strategy = 'OA' # if add_nogood_cuts is True, the bound obtained in the last iteration is no reliable. # we correct it after the iteration. if config.add_nogood_cuts: bound_fix(solve_data, config, last_iter_cuts)
def MindtPy_initialize_master(solve_data, config): """ Initializes the decomposition algorithm and creates the master MIP/MILP problem. This function initializes the decomposition problem, which includes generating the initial cuts required to build the master MIP/MILP Parameters ---------- solve_data: MindtPy Data Container data container that holds solve-instance data config: ConfigBlock contains the specific configurations for the algorithm """ # if single tree is activated, we need to add bounds for unbounded variables in nonlinear constraints to avoid unbounded master problem. if config.single_tree: var_bound_add(solve_data, config) m = solve_data.mip = solve_data.working_model.clone() MindtPy = m.MindtPy_utils if config.use_dual: m.dual.deactivate() if config.strategy == 'OA': calc_jacobians(solve_data, config) # preload jacobians MindtPy.MindtPy_linear_cuts.oa_cuts = ConstraintList( doc='Outer approximation cuts') elif config.strategy == 'ECP': calc_jacobians(solve_data, config) # preload jacobians MindtPy.MindtPy_linear_cuts.ecp_cuts = ConstraintList( doc='Extended Cutting Planes') # elif config.strategy == 'PSC': # detect_nonlinear_vars(solve_data, config) # MindtPy.MindtPy_linear_cuts.psc_cuts = ConstraintList( # doc='Partial surrogate cuts') # elif config.strategy == 'GBD': # MindtPy.MindtPy_linear_cuts.gbd_cuts = ConstraintList( # doc='Generalized Benders cuts') # Set default initialization_strategy if config.init_strategy is None: if config.strategy in {'OA', 'GOA'}: config.init_strategy = 'rNLP' else: config.init_strategy = 'max_binary' config.logger.info( '{} is the initial strategy being used.' '\n'.format( config.init_strategy)) # Do the initialization if config.init_strategy == 'rNLP': init_rNLP(solve_data, config) elif config.init_strategy == 'max_binary': init_max_binaries(solve_data, config) elif config.init_strategy == 'initial_binary': if config.strategy != 'ECP': fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( solve_data, config) if fixed_nlp_result.solver.termination_condition in {tc.optimal, tc.locallyOptimal, tc.feasible}: handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) else: handle_NLP_subproblem_other_termination(fixed_nlp, fixed_nlp_result.solver.termination_condition, solve_data, config)