def _preprocess(model, objective=True, constraints=True): objective_found = False if objective: for block in model.block_data_objects(active=True): for obj in block.component_data_objects(Objective, active=True, descend_into=False): objective_found = True preprocess_block_objectives(block) break if objective_found: break if constraints: for block in model.block_data_objects(active=True): preprocess_block_constraints(block)
def _preprocess(model, objective=True, constraints=True): objective_found = False if objective: for block in model.block_data_objects(active=True): for obj in block.component_data_objects(Objective, active=True, descend_into=False): objective_found = True preprocess_block_objectives(block) break if objective_found: break if constraints: for block in model.block_data_objects(active=True): preprocess_block_constraints(block)
def compute_standard_repn(data, model=None): """ This plugin computes the standard representation for all objectives and constraints. All results are stored in a ComponentMap named "_repn" at the block level. We break out preprocessing of the objectives and constraints in order to avoid redundant and unnecessary work, specifically in contexts where a model is iteratively solved and modified. we don't have finer-grained resolution, but we could easily pass in a Constraint and an Objective if warranted. Required: model: A concrete model instance. """ idMap = {} for block in model.block_data_objects(active=True): preprocess_block_constraints(block, idMap=idMap) preprocess_block_objectives(block, idMap=idMap)
def compute_standard_repn(data, model=None): """ This plugin computes the standard representation for all objectives and constraints. All results are stored in a ComponentMap named "_repn" at the block level. We break out preprocessing of the objectives and constraints in order to avoid redundant and unnecessary work, specifically in contexts where a model is iteratively solved and modified. we don't have finer-grained resolution, but we could easily pass in a Constraint and an Objective if warranted. Required: model: A concrete model instance. """ idMap = {} for block in model.block_data_objects(active=True): preprocess_block_constraints(block, idMap=idMap) preprocess_block_objectives(block, idMap=idMap)
def get_modified_instance( ph, scenario_tree, scenario_or_bundle, **options): # Find the model if scenario_tree.contains_bundles(): model = ph._bundle_binding_instance_map[scenario_or_bundle._name] else: model = ph._instances[scenario_or_bundle._name] b = model.component('_interscenario_plugin') if b is not None: return model # # We need to add the interscenario information to this model # model._interscenario_plugin = b = Block() # Save our options # b.epsilon = options.pop('epsilon') b.cut_scale = options.pop('cut_scale') b.allow_slack = options.pop('allow_slack') b.enable_rho = options.pop('enable_rho') b.enable_cuts = options.pop('enable_cuts') assert( len(options) == 0 ) # Information for generating cuts # b.cutlist = ConstraintList() b.abs_int_vars = VarList(within=NonNegativeIntegers) b.abs_binary_vars = VarList(within=Binary) # Note: the var_ids are on the ORIGINAL scenario models rootNode = scenario_tree.findRootNode() var_ids = list(iterkeys(rootNode._variable_datas)) # Right now, this is hard-coded for 2-stage problems - so we only # need to worry about the variables from the root node. These # variables should exist on all scenarios. Set up a (trivial) # equality constraint for each variable: # var == current_value{param} + separation_variable{var, fixed=0} b.STAGE1VAR = _S1V = Set(initialize=var_ids) b.separation_variables = _sep = Var( _S1V, dense=True ) b.fixed_variable_values = _param = Param(_S1V, mutable=True, initialize=0) b.rho = weakref.ref(model.component('PHRHO_%s' % rootNode._name)) b.weights = weakref.ref(model.component('PHWEIGHT_%s' % rootNode._name)) if b.allow_slack: for idx in _sep: _sep[idx].setlb(-b.epsilon) _sep[idx].setub(b.epsilon) else: _sep.fix(0) _cuidBuffer = {} _src = b.local_stage1_varmap = {} for i in _S1V: # Note indexing: for each 1st stage var, pick an arbitrary # (first) scenario and return the variable (and not it's # probability) _cuid = ComponentUID(rootNode._variable_datas[i][0][0], _cuidBuffer) _src[i] = weakref.ref(_cuid.find_component_on(model)) #_base_src[i] = weakref.ref(_cuid.find_component_on(base_model)) def _set_var_value(b, i): return _param[i] + _sep[i] - _src[i]() == 0 b.fixed_variables_constraint \ = _con = Constraint( _S1V, rule=_set_var_value ) # # TODO: When we get the duals of the first-stage variables, do we # want the dual WRT the original objective, or the dual WRT the # augmented objective? # # Move the objective to a standardized place so we can easily find it later if PYOMO_4_0: _orig_objective = list( x[2] for x in model.all_component_data( Objective, active=True, descend_into=True ) ) else: _orig_objective = list( model.component_data_objects( Objective, active=True, descend_into=True ) ) assert(len(_orig_objective) == 1) _orig_objective = _orig_objective[0] b.original_obj = weakref.ref(_orig_objective) # add (and deactivate) the objective for the infeasibility # separation problem. b.separation_obj = Objective( expr= sum( _sep[i]**2 for i in var_ids ), sense = minimize ) # Make sure we get dual information if 'dual' not in model: # Export and import floating point data model.dual = Suffix(direction=Suffix.IMPORT_EXPORT) #if 'rc' not in model: # model.rc = Suffix(direction=Suffix.IMPORT_EXPORT) if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: _map = {} preprocess_block_constraints(b, idMap=_map) # Note: we wait to deactivate the objective until after we # preprocess so that the obective is correctly processed. b.separation_obj.deactivate() # (temporarily) deactivate the fixed stage-1 variables _con.deactivate() toc("InterScenario plugin: generated modified problem instance") return model
def solve_fixed_scenario_solutions( ph, scenario_tree, scenario_or_bundle, scenario_solutions, **model_options ): model = get_modified_instance( ph, scenario_tree, scenario_or_bundle, **model_options ) _block = model._interscenario_plugin _param = _block.fixed_variable_values _sep = _block.separation_variables _con = _block.fixed_variables_constraint # We need to know which scenarios are local to this instance ... so # we don't waste time repeating work. if scenario_tree.contains_bundles(): local_scenarios = scenario_or_bundle._scenario_names else: local_scenarios = [ scenario_or_bundle._name ] ipopt = SolverFactory("ipopt") # # Turn off RHO! # _saved_rho_values = _block.rho().extract_values() _block.rho().store_values(0) # Enable the constraints to fix the Stage 1 variables: _con.activate() # Solve each solution here and cache the resulting objective cutlist = [] obj_values = [] dual_values = [] for var_values, scenario_name_list in scenario_solutions: local = False for scenario in local_scenarios: if scenario in scenario_name_list: local = True break if local: # Here is where we could save some time and not repeat work # ... for now I am being lazy and re-solving so that we get # the dual values, etc for this scenario as well. If nothing # else, it makes averaging easier. pass assert( len(var_values) == len(_param) ) for var_id, var_value in iteritems(var_values): _param[var_id] = var_value # TODO: We only need to update the StandardRepn for the binding # constraints ... so we could save a LOT of time by not # preprocessing the whole model. # if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: var_id_map = {} preprocess_block_constraints(_block, idMap=var_id_map) toc("preprocessed scenario %s" % ( scenario_or_bundle._name, )) output_buffer = StringIO() pyutilib.misc.setup_redirect(output_buffer) try: results = ph._solver.solve(model, tee=True) # warmstart=True) except: logger.warning("Exception raised solving the interscenario " "evaluation subproblem") logger.warning("Solver log:\n%s" % output_buffer.getvalue()) raise finally: pyutilib.misc.reset_redirect() toc("solved solution from scenario set %s on scenario %s" % ( scenario_name_list, scenario_or_bundle._name, )) ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: state = 0 #'FEASIBLE' if PYOMO_4_0: model.load(results) else: model.solutions.load_from(results) # # Turn off W, recompute the objective # _saved_w_values = _block.weights().extract_values() _block.weights().store_values(0) obj_values.append(value(_block.original_obj())) _block.weights().store_values(_saved_w_values) # NOTE: Getting the dual values resolves the model # (potentially relaxing second state variables. if _block.enable_rho: dual_values.append( get_dual_values(ph._solver, model) ) else: dual_values.append(None) cutlist.append(". ") elif True or tc in _infeasible_termination_conditions: state = 1 #'INFEASIBLE' obj_values.append(None) dual_values.append(None) if _block.enable_cuts: cut = solve_separation_problem(ph._solver, model, True) if cut == '????': if ph._solver.problem_format() != ProblemFormat.nl: model.preprocess() #preprocess_block_objectives(_block) #preprocess_block_constraints(_block) cut = solve_separation_problem(ipopt, model, False) else: cut = "X " cutlist.append( cut ) toc("solved separation problem for solution from scenario set " "%s on scenario %s" % ( scenario_name_list, scenario_or_bundle._name, )) else: state = 2 #'NONOPTIMAL' obj_values.append(None) dual_values.append(None) cutlist.append("? ") logger.warning("Solving the interscenario evaluation " "subproblem failed (%s)." % (state,) ) logger.warning("Solver log:\n%s" % output_buffer.getvalue()) # # Turn RHO, W back on! # _block.weights().store_values(_saved_w_values) _block.rho().store_values(_saved_rho_values) # Disable the constraints to fix the Stage 1 variables: _con.deactivate() return obj_values, dual_values, cutlist
def add_new_cuts( ph, scenario_tree, scenario_or_bundle, feasibility_cuts, incumbent_cuts, resolve ): # Find the model m = get_modified_instance(ph, scenario_tree, scenario_or_bundle) _block = m._interscenario_plugin epsilon = _block.epsilon cut_scale = _block.cut_scale # Add the cuts to the ConstraintList on the model _cutlist = _block.cutlist _src = _block.local_stage1_varmap for cut_obj, cut in feasibility_cuts: expr = sum( 2 * (_sep*(1-cut_scale)) * (_src[i]() - (_par+_sep*(1-cut_scale))) for i, (_sep, _par) in iteritems(cut) if abs(_sep) > epsilon*max(1,_par) ) if expr is not 0: _cutlist.add( expr >= 0 ) for cut in incumbent_cuts: _int_binaries = [] for vid, val in iteritems(cut[1]): # Deal with integer variables # b + c >= z # b <= M*y # c <= M*(1-y) # x - val = c - b # b,c >= 0 b = _block.abs_int_vars.add() c = _block.abs_int_vars.add() z = _block.abs_binary_vars.add() y = _block.abs_binary_vars.add() _cutlist.add( b + c >= z ) _cutlist.add( b <= _src[vid]().ub * y ) _cutlist.add( c <= _src[vid]().ub * (1-y) ) _cutlist.add( _src[vid]() - val == c - b ) _int_binaries.append( z ) _cutlist.add( sum(_int_binaries) + sum( _src[vid]() if val<0.5 else (1-_src[vid]()) for vid,val in iteritems(cut[0]) ) >= 1 ) if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: m.preprocess() else: _map = {} preprocess_block_constraints(_block, idMap=_map) if resolve: results = ph._solver.solve(m, warmstart=True) ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: if PYOMO_4_0: m.load(results) else: m.solutions.load_from(results) _src = _block.local_stage1_varmap return ( # Note: _src is {id: weakref} and original_obj is a # weakref; so dereference weakref before computing value dict((_id, value(_var())) for _id, _var in iteritems(_src)), value(_block.original_obj()) ) else: return None
def solve_separation_problem(solver, model, fallback): xfrm = TransformationFactory('core.relax_discrete') if PYOMO_4_0: xfrm.apply(model, inplace=True) else: xfrm.apply_to(model) _block = model._interscenario_plugin # Switch objectives _block.original_obj().deactivate() _block.separation_obj.activate() #_block.separation_variables.unfix() _par = _block.fixed_variable_values _sep = _block.separation_variables allow_slack = _block.allow_slack if allow_slack: epsilon = _block.epsilon for idx in _sep: _sep[idx].setlb(None) _sep[idx].setub(None) else: _sep.unfix() # Note: preprocessing is only necessary if we are changing a # fixed/freed variable. if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: _map = {} preprocess_block_objectives(_block, idMap=_map) preprocess_block_constraints(_block, idMap=_map) #SOLVE output_buffer = StringIO() pyutilib.misc.setup_redirect(output_buffer) try: results = solver.solve(model, tee=True) except: logger.warning("Exception raised solving the interscenario " "evaluation subproblem") logger.warning("Solver log:\n%s" % output_buffer.getvalue()) raise finally: pyutilib.misc.reset_redirect() ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: state = '' if PYOMO_4_0: model.load(results) else: model.solutions.load_from(results) elif tc in _infeasible_termination_conditions: state = 'INFEASIBLE' ans = "!!!!" else: state = 'NONOPTIMAL' ans = "????" if state: if fallback: #logger.warning("Initial attempt to solve the interscenario cut " # "separation subproblem failed with the default " # "solver (%s)." % (state,) ) pass else: logger.warning("Solving the interscenario cut separation " "subproblem failed (%s)." % (state,) ) logger.warning("Solver log:\n%s" % output_buffer.getvalue()) else: cut = dict((vid, (value(_sep[vid]), value(_par[vid]))) for vid in _block.STAGE1VAR) obj = value(_block.separation_obj) ans = (math.sqrt(obj), cut) output_buffer.close() # Restore the objective _block.original_obj().activate() _block.separation_obj.deactivate() # Turn off the separation variables if allow_slack: for idx in _sep: _sep[idx].setlb(-epsilon) _sep[idx].setub(epsilon) else: _sep.fix(0) if PYOMO_4_0: xfrm.apply(model, inplace=True, undo=True) else: xfrm.apply_to(model, undo=True) if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: pass else: _map = {} preprocess_block_objectives(_block, idMap=_map) return ans
def get_dual_values(solver, model): if id(model) not in get_dual_values.discrete_stage2_vars: # 1st attempt to get duals: we need to see if the model has # discrete variables (solvers won't give duals if there are # still active discrete variables) try: get_dual_values.discrete_stage2_vars[id(model)] = False return get_dual_values(solver, model) except: get_dual_values.discrete_stage2_vars[id(model)] = True # Find the discrete variables to populate the list return get_dual_values(solver, model) duals = {} _con = model._interscenario_plugin.fixed_variables_constraint if get_dual_values.discrete_stage2_vars[id(model)]: # Fix all discrete variables xfrm = TransformationFactory('core.relax_discrete') if PYOMO_4_0: xfrm.apply(model, inplace=True) else: xfrm.apply_to(model) # Note: preprocessing is only necessary if we are changing a # fixed/freed variable. if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: _map = {} preprocess_block_constraints( model._interscenario_plugin, idMap=_map) #SOLVE results = solver.solve(model, warmstart=True) ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: state = '' elif tc in _infeasible_termination_conditions: state = 'INFEASIBLE' else: state = 'NONOPTIMAL' if state: logger.warning( "Resolving subproblem model with relaxed second-stage " "discrete variables failed (%s). " "Dual values not available." % (state,) ) else: # Get the duals if PYOMO_4_0: model.load(results) else: model.solutions.load_from(results) #model.dual.pprint() for varid in model._interscenario_plugin.STAGE1VAR: duals[varid] = model.dual[_con[varid]] # Free the discrete second-stage variables if PYOMO_4_0: xfrm.apply(model, inplace=True, undo=True) else: xfrm.apply_to(model, undo=True) else: # return the duals for varid in model._interscenario_plugin.STAGE1VAR: duals[varid] = model.dual[_con[varid]] return duals
def preprocess_bundles(self, bundles=None, force_preprocess_bundle_objective=False, force_preprocess_bundle_constraints=False): # TODO: Does this import need to be delayed because # it is in a plugins subdirectory from pyomo.solvers.plugins.solvers.persistent_solver import \ PersistentSolver start_time = time.time() if len(self._bundle_instances) == 0: raise RuntimeError( "Unable to preprocess scenario bundles. Bundling " "does not seem to be activated.") if bundles is None: bundles = self._bundle_instances.keys() if self.get_option("verbose"): print("Preprocessing %s bundles" % len(bundles)) preprocess_bundle_objective = 0b01 preprocess_bundle_constraints = 0b10 for bundle_name in bundles: preprocess_bundle = 0 solver = self._bundle_solvers[bundle_name] persistent_solver_in_use = isinstance(solver, PersistentSolver) bundle_ef_instance = self._bundle_instances[bundle_name] if persistent_solver_in_use and \ (not solver.has_instance()): assert self._bundle_first_preprocess[bundle_name] solver.set_instance(bundle_ef_instance) self._bundle_first_preprocess[bundle_name] = False for scenario_name in self._bundle_scenarios[bundle_name]: self._scenario_solvers[scenario_name].set_instance( self._scenario_instances[scenario_name]) # We've preprocessed the instance, reset the relevant flags self._scenario_first_preprocess[scenario_name] = False self.clear_update_flags(scenario_name) self.clear_fixed_variables(scenario_name) self.clear_freed_variables(scenario_name) else: if persistent_solver_in_use: assert not self._bundle_first_preprocess[bundle_name] for scenario_name in self._bundle_scenarios[bundle_name]: if self.objective_updated[scenario_name]: preprocess_bundle |= preprocess_bundle_objective if ((len(self.fixed_variables[scenario_name]) > 0) or \ (len(self.freed_variables[scenario_name]) > 0)) and \ self.get_option("preprocess_fixed_variables"): preprocess_bundle |= \ preprocess_bundle_objective | \ preprocess_bundle_constraints if self._bundle_first_preprocess[bundle_name]: preprocess_bundle |= \ preprocess_bundle_objective | \ preprocess_bundle_constraints self._bundle_first_preprocess[bundle_name] = False if persistent_solver_in_use: # also preprocess on the scenario solver scenario_solver = self._scenario_solvers[scenario_name] isinstance(scenario_solver, PersistentSolver) self._preprocess_scenario(scenario_name, scenario_solver) self._preprocess_scenario(scenario_name, solver) # We've preprocessed the instance, reset the relevant flags self._scenario_first_preprocess[scenario_name] = False self.clear_update_flags(scenario_name) self.clear_fixed_variables(scenario_name) self.clear_freed_variables(scenario_name) if force_preprocess_bundle_objective: preprocess_bundle |= preprocess_bundle_objective if force_preprocess_bundle_constraints: preprocess_bundle |= preprocess_bundle_constraints if preprocess_bundle: if persistent_solver_in_use: assert solver.has_instance() if preprocess_bundle & preprocess_bundle_objective: obj_count = 0 for obj in bundle_ef_instance.component_data_objects( ctype=Objective, descend_into=False, active=True): obj_count += 1 if obj_count > 1: raise RuntimeError( "Persistent solver interface only " "supports a single active objective.") solver.set_objective(obj) if preprocess_bundle & preprocess_bundle_constraints: # we assume the bundle constraints are just simple # linking constraints (e.g., no SOSConstraints) for con in bundle_ef_instance.component_data_objects( ctype=Constraint, descend_into=False, active=True): solver.remove_constraint(con) solver.add_constraint(con) else: idMap = {} if preprocess_bundle & preprocess_bundle_objective: preprocess_block_objectives(bundle_ef_instance, idMap=idMap) if preprocess_bundle & preprocess_bundle_constraints: preprocess_block_constraints(bundle_ef_instance, idMap=idMap) end_time = time.time() if self.get_option("output_times"): print("Bundle preprocessing time=%.2f seconds" % (end_time - start_time))
def get_modified_instance(ph, scenario_tree, scenario_or_bundle, **options): # Find the model if scenario_tree.contains_bundles(): model = ph._bundle_binding_instance_map[scenario_or_bundle._name] else: model = ph._instances[scenario_or_bundle._name] b = model.component('_interscenario_plugin') if b is not None: return model # # We need to add the interscenario information to this model # model._interscenario_plugin = b = Block() # Save our options # b.epsilon = options.pop('epsilon') b.cut_scale = options.pop('cut_scale') b.allow_slack = options.pop('allow_slack') b.enable_rho = options.pop('enable_rho') b.enable_cuts = options.pop('enable_cuts') assert (len(options) == 0) # Information for generating cuts # b.cutlist = ConstraintList() b.abs_int_vars = VarList(within=NonNegativeIntegers) b.abs_binary_vars = VarList(within=Binary) # Note: the var_ids are on the ORIGINAL scenario models rootNode = scenario_tree.findRootNode() var_ids = list(iterkeys(rootNode._variable_datas)) # Right now, this is hard-coded for 2-stage problems - so we only # need to worry about the variables from the root node. These # variables should exist on all scenarios. Set up a (trivial) # equality constraint for each variable: # var == current_value{param} + separation_variable{var, fixed=0} b.STAGE1VAR = _S1V = Set(initialize=var_ids) b.separation_variables = _sep = Var(_S1V, dense=True) b.fixed_variable_values = _param = Param(_S1V, mutable=True, initialize=0) b.rho = weakref.ref(model.component('PHRHO_%s' % rootNode._name)) b.weights = weakref.ref(model.component('PHWEIGHT_%s' % rootNode._name)) if b.allow_slack: for idx in _sep: _sep[idx].setlb(-b.epsilon) _sep[idx].setub(b.epsilon) else: _sep.fix(0) _cuidBuffer = {} _src = b.local_stage1_varmap = {} for i in _S1V: # Note indexing: for each 1st stage var, pick an arbitrary # (first) scenario and return the variable (and not it's # probability) _cuid = ComponentUID(rootNode._variable_datas[i][0][0], _cuidBuffer) _src[i] = weakref.ref(_cuid.find_component_on(model)) #_base_src[i] = weakref.ref(_cuid.find_component_on(base_model)) def _set_var_value(b, i): return _param[i] + _sep[i] - _src[i]() == 0 b.fixed_variables_constraint \ = _con = Constraint( _S1V, rule=_set_var_value ) # # TODO: When we get the duals of the first-stage variables, do we # want the dual WRT the original objective, or the dual WRT the # augmented objective? # # Move the objective to a standardized place so we can easily find it later if PYOMO_4_0: _orig_objective = list(x[2] for x in model.all_component_data( Objective, active=True, descend_into=True)) else: _orig_objective = list( model.component_data_objects(Objective, active=True, descend_into=True)) assert (len(_orig_objective) == 1) _orig_objective = _orig_objective[0] b.original_obj = weakref.ref(_orig_objective) # add (and deactivate) the objective for the infeasibility # separation problem. b.separation_obj = Objective(expr=sum(_sep[i]**2 for i in var_ids), sense=minimize) # Make sure we get dual information if 'dual' not in model: # Export and import floating point data model.dual = Suffix(direction=Suffix.IMPORT_EXPORT) #if 'rc' not in model: # model.rc = Suffix(direction=Suffix.IMPORT_EXPORT) if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: _map = {} preprocess_block_constraints(b, idMap=_map) # Note: we wait to deactivate the objective until after we # preprocess so that the obective is correctly processed. b.separation_obj.deactivate() # (temporarily) deactivate the fixed stage-1 variables _con.deactivate() toc("InterScenario plugin: generated modified problem instance") return model
def solve_fixed_scenario_solutions(ph, scenario_tree, scenario_or_bundle, scenario_solutions, **model_options): model = get_modified_instance(ph, scenario_tree, scenario_or_bundle, **model_options) _block = model._interscenario_plugin _param = _block.fixed_variable_values _sep = _block.separation_variables _con = _block.fixed_variables_constraint # We need to know which scenarios are local to this instance ... so # we don't waste time repeating work. if scenario_tree.contains_bundles(): local_scenarios = scenario_or_bundle._scenario_names else: local_scenarios = [scenario_or_bundle._name] ipopt = SolverFactory("ipopt") # # Turn off RHO! # _saved_rho_values = _block.rho().extract_values() _block.rho().store_values(0) # Enable the constraints to fix the Stage 1 variables: _con.activate() # Solve each solution here and cache the resulting objective cutlist = [] obj_values = [] dual_values = [] for var_values, scenario_name_list in scenario_solutions: local = False for scenario in local_scenarios: if scenario in scenario_name_list: local = True break if local: # Here is where we could save some time and not repeat work # ... for now I am being lazy and re-solving so that we get # the dual values, etc for this scenario as well. If nothing # else, it makes averaging easier. pass assert (len(var_values) == len(_param)) for var_id, var_value in iteritems(var_values): _param[var_id] = var_value # TODO: We only need to update the StandardRepn for the binding # constraints ... so we could save a LOT of time by not # preprocessing the whole model. # if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: var_id_map = {} preprocess_block_constraints(_block, idMap=var_id_map) toc("preprocessed scenario %s" % (scenario_or_bundle._name, )) output_buffer = StringIO() setup_redirect(output_buffer) try: results = ph._solver.solve(model, tee=True) # warmstart=True) except: logger.warning("Exception raised solving the interscenario " "evaluation subproblem") logger.warning("Solver log:\n%s" % output_buffer.getvalue()) raise finally: reset_redirect() toc("solved solution from scenario set %s on scenario %s" % ( scenario_name_list, scenario_or_bundle._name, )) ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: state = 0 #'FEASIBLE' if PYOMO_4_0: model.load(results) else: model.solutions.load_from(results) # # Turn off W, recompute the objective # _saved_w_values = _block.weights().extract_values() _block.weights().store_values(0) obj_values.append(value(_block.original_obj())) _block.weights().store_values(_saved_w_values) # NOTE: Getting the dual values resolves the model # (potentially relaxing second state variables. if _block.enable_rho: dual_values.append(get_dual_values(ph._solver, model)) else: dual_values.append(None) cutlist.append(". ") elif True or tc in _infeasible_termination_conditions: state = 1 #'INFEASIBLE' obj_values.append(None) dual_values.append(None) if _block.enable_cuts: cut = solve_separation_problem(ph._solver, model, True) if cut == '????': if ph._solver.problem_format() != ProblemFormat.nl: model.preprocess() #preprocess_block_objectives(_block) #preprocess_block_constraints(_block) cut = solve_separation_problem(ipopt, model, False) else: cut = "X " cutlist.append(cut) toc("solved separation problem for solution from scenario set " "%s on scenario %s" % ( scenario_name_list, scenario_or_bundle._name, )) else: state = 2 #'NONOPTIMAL' obj_values.append(None) dual_values.append(None) cutlist.append("? ") logger.warning("Solving the interscenario evaluation " "subproblem failed (%s)." % (state, )) logger.warning("Solver log:\n%s" % output_buffer.getvalue()) # # Turn RHO, W back on! # _block.weights().store_values(_saved_w_values) _block.rho().store_values(_saved_rho_values) # Disable the constraints to fix the Stage 1 variables: _con.deactivate() return obj_values, dual_values, cutlist
def add_new_cuts(ph, scenario_tree, scenario_or_bundle, feasibility_cuts, incumbent_cuts, resolve): # Find the model m = get_modified_instance(ph, scenario_tree, scenario_or_bundle) _block = m._interscenario_plugin epsilon = _block.epsilon cut_scale = _block.cut_scale # Add the cuts to the ConstraintList on the model _cutlist = _block.cutlist _src = _block.local_stage1_varmap for cut_obj, cut in feasibility_cuts: expr = sum(2 * (_sep * (1 - cut_scale)) * (_src[i]() - (_par + _sep * (1 - cut_scale))) for i, (_sep, _par) in iteritems(cut) if abs(_sep) > epsilon * max(1, _par)) if expr != 0: _cutlist.add(expr >= 0) for cut in incumbent_cuts: _int_binaries = [] for vid, val in iteritems(cut[1]): # Deal with integer variables # b + c >= z # b <= M*y # c <= M*(1-y) # x - val = c - b # b,c >= 0 b = _block.abs_int_vars.add() c = _block.abs_int_vars.add() z = _block.abs_binary_vars.add() y = _block.abs_binary_vars.add() _cutlist.add(b + c >= z) _cutlist.add(b <= _src[vid]().ub * y) _cutlist.add(c <= _src[vid]().ub * (1 - y)) _cutlist.add(_src[vid]() - val == c - b) _int_binaries.append(z) _cutlist.add( sum(_int_binaries) + sum(_src[vid]() if val < 0.5 else (1 - _src[vid]()) for vid, val in iteritems(cut[0])) >= 1) if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: m.preprocess() else: _map = {} preprocess_block_constraints(_block, idMap=_map) if resolve: results = ph._solver.solve(m, warmstart=True) ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: if PYOMO_4_0: m.load(results) else: m.solutions.load_from(results) _src = _block.local_stage1_varmap return ( # Note: _src is {id: weakref} and original_obj is a # weakref; so dereference weakref before computing value dict((_id, value(_var())) for _id, _var in iteritems(_src)), value(_block.original_obj())) else: return None
def solve_separation_problem(solver, model, fallback): xfrm = TransformationFactory('core.relax_discrete') if PYOMO_4_0: xfrm.apply(model, inplace=True) else: xfrm.apply_to(model) _block = model._interscenario_plugin # Switch objectives _block.original_obj().deactivate() _block.separation_obj.activate() #_block.separation_variables.unfix() _par = _block.fixed_variable_values _sep = _block.separation_variables allow_slack = _block.allow_slack if allow_slack: epsilon = _block.epsilon for idx in _sep: _sep[idx].setlb(None) _sep[idx].setub(None) else: _sep.unfix() # Note: preprocessing is only necessary if we are changing a # fixed/freed variable. if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: _map = {} preprocess_block_objectives(_block, idMap=_map) preprocess_block_constraints(_block, idMap=_map) #SOLVE output_buffer = StringIO() setup_redirect(output_buffer) try: results = solver.solve(model, tee=True) except: logger.warning("Exception raised solving the interscenario " "evaluation subproblem") logger.warning("Solver log:\n%s" % output_buffer.getvalue()) raise finally: reset_redirect() ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: state = '' if PYOMO_4_0: model.load(results) else: model.solutions.load_from(results) elif tc in _infeasible_termination_conditions: state = 'INFEASIBLE' ans = "!!!!" else: state = 'NONOPTIMAL' ans = "????" if state: if fallback: #logger.warning("Initial attempt to solve the interscenario cut " # "separation subproblem failed with the default " # "solver (%s)." % (state,) ) pass else: logger.warning("Solving the interscenario cut separation " "subproblem failed (%s)." % (state, )) logger.warning("Solver log:\n%s" % output_buffer.getvalue()) else: cut = dict((vid, (value(_sep[vid]), value(_par[vid]))) for vid in _block.STAGE1VAR) obj = value(_block.separation_obj) ans = (math.sqrt(obj), cut) output_buffer.close() # Restore the objective _block.original_obj().activate() _block.separation_obj.deactivate() # Turn off the separation variables if allow_slack: for idx in _sep: _sep[idx].setlb(-epsilon) _sep[idx].setub(epsilon) else: _sep.fix(0) if PYOMO_4_0: xfrm.apply(model, inplace=True, undo=True) else: xfrm.apply_to(model, undo=True) if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: pass else: _map = {} preprocess_block_objectives(_block, idMap=_map) return ans
def get_dual_values(solver, model): if id(model) not in get_dual_values.discrete_stage2_vars: # 1st attempt to get duals: we need to see if the model has # discrete variables (solvers won't give duals if there are # still active discrete variables) try: get_dual_values.discrete_stage2_vars[id(model)] = False return get_dual_values(solver, model) except: get_dual_values.discrete_stage2_vars[id(model)] = True # Find the discrete variables to populate the list return get_dual_values(solver, model) duals = {} _con = model._interscenario_plugin.fixed_variables_constraint if get_dual_values.discrete_stage2_vars[id(model)]: # Fix all discrete variables xfrm = TransformationFactory('core.relax_discrete') if PYOMO_4_0: xfrm.apply(model, inplace=True) else: xfrm.apply_to(model) # Note: preprocessing is only necessary if we are changing a # fixed/freed variable. if FALLBACK_ON_BRUTE_FORCE_PREPROCESS: model.preprocess() else: _map = {} preprocess_block_constraints(model._interscenario_plugin, idMap=_map) #SOLVE results = solver.solve(model, warmstart=True) ss = results.solver.status tc = results.solver.termination_condition #self.timeInSolver += results['Solver'][0]['Time'] if ss == SolverStatus.ok and tc in _acceptable_termination_conditions: state = '' elif tc in _infeasible_termination_conditions: state = 'INFEASIBLE' else: state = 'NONOPTIMAL' if state: logger.warning( "Resolving subproblem model with relaxed second-stage " "discrete variables failed (%s). " "Dual values not available." % (state, )) else: # Get the duals if PYOMO_4_0: model.load(results) else: model.solutions.load_from(results) #model.dual.pprint() for varid in model._interscenario_plugin.STAGE1VAR: duals[varid] = model.dual[_con[varid]] # Free the discrete second-stage variables if PYOMO_4_0: xfrm.apply(model, inplace=True, undo=True) else: xfrm.apply_to(model, undo=True) else: # return the duals for varid in model._interscenario_plugin.STAGE1VAR: duals[varid] = model.dual[_con[varid]] return duals