def _apply_to(self, instance, **kwds): self._deterministic = kwds.get('deterministic', False) # # Process options # submodel = self._preprocess('bilevel.linear_mpec', instance, **kwds) instance.reclassify_component_type(submodel, Block) # # Create a block with optimality conditions # setattr(instance, self._submodel + '_kkt', self._add_optimality_conditions(instance, submodel)) instance._transformation_data[ 'bilevel.linear_mpec'].submodel_cuid = ComponentUID(submodel) instance._transformation_data[ 'bilevel.linear_mpec'].block_cuid = ComponentUID( getattr(instance, self._submodel + '_kkt')) #------------------------------------------------------------------------------- # # Disable the original submodel and # #instance.reclassify_component_type(submodel, SubModel) #submodel.deactivate() # TODO: Cache the list of components that were deactivated for (name, data) in submodel.component_map(active=True).items(): if not isinstance(data, Var) and not isinstance(data, Set): data.deactivate()
def pyro_sample_sp(self, size, **kwds): assert size > 0 model = self.reference_model.clone() scenario_tree_model = \ self._create_scenario_tree_model(size) factory = ScenarioTreeInstanceFactory( model=self.reference_model, scenario_tree=scenario_tree_model) options = \ ScenarioTreeManagerClientPyro.register_options() for key in kwds: options[key] = kwds[key] manager = ScenarioTreeManagerClientPyro(options, factory=factory) try: init = manager.initialize(async_call=True) pcuids = ComponentMap() for param in self.stochastic_data: pcuids[param] = ComponentUID(param) init.complete() for scenario in manager.scenario_tree.scenarios: data = [] for param, dist in self.stochastic_data.items(): data.append((pcuids[param], dist.sample())) manager.invoke_function( "_update_data", thisfile, invocation_type=InvocationType.OnScenario(scenario.name), function_args=(data, ), oneway_call=True) manager.reference_model = model except: manager.close() raise return manager
def _apply_to(self, instance, **kwds): options = kwds.pop('options', {}) # # Find the free variables # free_vars = {} id_list = [] for vdata in instance.component_data_objects( Var, active=True, sort=SortComponents.deterministic): id_list.append(id(vdata)) free_vars[id(vdata)] = vdata # # Iterate over the Complementarity components # cobjs = [] for bdata in instance.block_data_objects( active=True, sort=SortComponents.deterministic): for cobj in bdata.component_objects(Complementarity, active=True, descend_into=False): cobjs.append(cobj) for index in sorted(iterkeys(cobj)): _cdata = cobj[index] if not _cdata.active: continue # # Apply a variant of the standard form logic # self.to_common_form(_cdata, free_vars) # tdata = instance._transformation_data['mpec.nl'] tdata.compl_cuids = [] for cobj in cobjs: tdata.compl_cuids.append(ComponentUID(cobj)) cobj.parent_block().reclassify_component_type(cobj, Block)
def _preprocess(self, tname, instance, **kwds): options = kwds.pop('options', {}) sub = options.get('submodel', None) # # Iterate over the model collecting variable data, # until the submodel is found. # var = {} submodel = None for (name, data) in instance.component_map(active=True).items(): if isinstance(data, Var): var[name] = data elif isinstance(data, SubModel): if sub is None or sub == name: sub = name submodel = data break if submodel is None: raise RuntimeError("Missing submodel: " + str(sub)) # instance._transformation_data[tname].submodel = [name] # # Fix variables # if submodel._fixed: fixed = [i.name for i in submodel._fixed] unfixed = [] for v in var: if not v in fixed: unfixed.append((v, getattr(submodel._parent(), v).is_indexed())) elif submodel._var: # NOTE: This argument is undocumented _var = set(submodel._var) unfixed = [(v, getattr(submodel._parent(), v).is_indexed()) for v in _var] fixed = [] for v in var: if not v in _var: fixed.append(v) else: # # By default, we assume that variables are fixed if they are not part of the # local model. # fixed = [v for v in var] unfixed = [] for (name, data) in submodel.component_map(active=True).items(): if isinstance(data, Var): unfixed.append((data.cname(), data.is_indexed())) # self._submodel = sub self._upper_vars = var self._fixed_upper_vars = fixed self._unfixed_upper_vars = unfixed instance._transformation_data[tname].fixed = [ ComponentUID(var[v]) for v in fixed ] return submodel
def _apply_to(self, instance, **kwds): options = kwds.pop('options', {}) bound = kwds.pop('mpec_bound', 0.0) # # Create a mutable parameter that defines the value of the upper bound # on the constraints # bound = options.get('mpec_bound', bound) instance.mpec_bound = Param(mutable=True, initialize=bound) # # Setup transformation data # tdata = instance._transformation_data['mpec.simple_nonlinear'] tdata.compl_cuids = [] # # Iterate over the model finding Complementarity components # for complementarity in instance.component_objects( Complementarity, active=True, descend_into=(Block, Disjunct), sort=SortComponents.deterministic): block = complementarity.parent_block() for index in sorted(complementarity.keys()): _data = complementarity[index] if not _data.active: continue _data.to_standard_form() # _type = getattr(_data.c, "_complementarity_type", 0) if _type == 1: # # Constraint expression is bounded below, so we can replace # constraint c with a constraint that ensures that either # constraint c is active or variable v is at its lower bound. # _data.ccon = Constraint( expr=(_data.c.body - _data.c.lower) * _data.v <= instance.mpec_bound) del _data.c._complementarity_type elif _type == 3: # # Variable v is bounded above and below. We can define # _data.ccon_l = Constraint( expr=(_data.v - _data.v.bounds[0]) * _data.c.body <= instance.mpec_bound) _data.ccon_u = Constraint( expr=(_data.v - _data.v.bounds[1]) * _data.c.body <= instance.mpec_bound) del _data.c._complementarity_type elif _type == 2: #pragma:nocover raise ValueError( "to_standard_form does not generate _type 2 expressions" ) tdata.compl_cuids.append(ComponentUID(complementarity)) block.reclassify_component_type(complementarity, Block)
def _preprocess(self, tname, instance, **kwds): options = kwds.pop('options', {}) sub = options.get('submodel', None) # # Iterate over the model collecting variable data, # until the submodel is found. # var = {} submodel = None for (name, data) in instance.component_map(active=True).items(): if isinstance(data, Var): var[name] = data elif isinstance(data, SubModel): if sub is None or sub == name: sub = name submodel = data break if submodel is None: raise RuntimeError("Missing submodel: " + str(sub)) # instance._transformation_data[tname].submodel = [name] # # Fix variables # if submodel._fixed: fixed = [i.name for i in submodel._fixed] unfixed = [] for v in var: if not v in fixed: unfixed.append((v, getattr(submodel._parent(), v).is_indexed())) elif submodel._var: _var = set(submodel._var) unfixed = [(v, getattr(submodel._parent(), v).is_indexed()) for v in _var] fixed = [] for v in var: if not v in _var: fixed.append(v) else: raise RuntimeError( "Must specify either 'fixed' or 'var' option for SubModel") # self._submodel = sub self._upper_vars = var self._fixed_upper_vars = fixed self._unfixed_upper_vars = unfixed instance._transformation_data[tname].fixed = [ ComponentUID(var[v]) for v in fixed ] return submodel
def _apply_to(self, instance, **kwds): self._deterministic = kwds.get('deterministic', False) # # Process options # submodel = self._preprocess('bilevel.linear_mpec', instance, **kwds) instance.reclassify_component_type(submodel, Block) # # Create a block with optimality conditions # setattr(instance, self._submodel + '_kkt', self._add_optimality_conditions(instance, submodel)) instance._transformation_data[ 'bilevel.linear_mpec'].submodel_cuid = ComponentUID(submodel) instance._transformation_data[ 'bilevel.linear_mpec'].block_cuid = ComponentUID( getattr(instance, self._submodel + '_kkt')) #------------------------------------------------------------------------------- # # Disable the original submodel and # execute the preprocessor # instance.reclassify_component_type(submodel, SubModel) submodel.deactivate()
def _apply_to(self, instance, **kwds): # # Find the free variables # free_vars = {} id_list = [] # [ESJ 07/12/2019] Look on the whole model in case instance is a Block or a Disjunct for vdata in instance.model().component_data_objects( Var, active=True, sort=SortComponents.deterministic, descend_into=(Block, Disjunct)): id_list.append(id(vdata)) free_vars[id(vdata)] = vdata # # Iterate over the Complementarity components # cobjs = [] for cobj in instance.component_objects( Complementarity, active=True, descend_into=(Block, Disjunct), sort=SortComponents.deterministic): cobjs.append(cobj) for index in sorted(cobj.keys()): _cdata = cobj[index] if not _cdata.active: continue # # Apply a variant of the standard form logic # self.to_common_form(_cdata, free_vars) # tdata = instance._transformation_data['mpec.nl'] tdata.compl_cuids = [] for cobj in cobjs: tdata.compl_cuids.append(ComponentUID(cobj)) cobj.parent_block().reclassify_component_type(cobj, Block)
def _apply_to(self, instance, **kwds): # # Setup transformation data # tdata = instance._transformation_data['mpec.simple_disjunction'] tdata.compl_cuids = [] # # Iterate over the model finding Complementarity components # for complementarity in instance.component_objects(Complementarity, active=True, descend_into=(Block, Disjunct), sort=SortComponents.deterministic): block = complementarity.parent_block() for index in sorted(complementarity.keys()): _data = complementarity[index] if not _data.active: continue # _e1 = _data._canonical_expression(_data._args[0]) _e2 = _data._canonical_expression(_data._args[1]) if len(_e1)==3 and len(_e2) == 3 and (_e1[0] is None) + (_e1[2] is None) + (_e2[0] is None) + (_e2[2] is None) != 2: raise RuntimeError("Complementarity condition %s must have exactly two finite bounds" % _data.name) if len(_e1) == 3 and _e1[0] is None and _e1[2] is None: # # Swap _e1 and _e2. The ensures that # only e2 will be an unconstrained expression # _e1, _e2 = _e2, _e1 if _e2[0] is None and _e2[2] is None: if len(_e1) == 2: _data.c = Constraint(expr=_e1) else: _data.expr1 = Disjunct() _data.expr1.c0 = Constraint(expr= _e1[0] == _e1[1]) _data.expr1.c1 = Constraint(expr= _e2[1] >= 0) # _data.expr2 = Disjunct() _data.expr2.c0 = Constraint(expr= _e1[1] == _e1[2]) _data.expr2.c1 = Constraint(expr= _e2[1] <= 0) # _data.expr3 = Disjunct() _data.expr3.c0 = Constraint(expr= inequality(_e1[0], _e1[1], _e1[2])) _data.expr3.c1 = Constraint(expr= _e2[1] == 0) _data.complements = Disjunction(expr=(_data.expr1, _data.expr2, _data.expr3)) else: if _e1[0] is None: tmp1 = _e1[2] - _e1[1] else: tmp1 = _e1[1] - _e1[0] if _e2[0] is None: tmp2 = _e2[2] - _e2[1] else: tmp2 = _e2[1] - _e2[0] _data.expr1 = Disjunct() _data.expr1.c0 = Constraint(expr= tmp1 >= 0) _data.expr1.c1 = Constraint(expr= tmp2 == 0) # _data.expr2 = Disjunct() _data.expr2.c0 = Constraint(expr= tmp1 == 0) _data.expr2.c1 = Constraint(expr= tmp2 >= 0) # _data.complements = Disjunction(expr=(_data.expr1, _data.expr2)) tdata.compl_cuids.append( ComponentUID(complementarity) ) block.reclassify_component_type(complementarity, Block)
def __init__(self, reference_model): self.reference_model = None self.objective = None self.time_stages = None self.stage_to_variables_map = {} self.variable_to_stage_map = {} # the set of stochastic data objects # (possibly mapped to some distribution) self.stochastic_data = None # maps between variables and objectives self.variable_to_objectives_map = ComponentMap() self.objective_to_variables_map = ComponentMap() # maps between variables and constraints self.variable_to_constraints_map = ComponentMap() self.constraint_to_variables_map = ComponentMap() # maps between stochastic data and objectives self.stochastic_data_to_objectives_map = ComponentMap() self.objective_to_stochastic_data_map = ComponentMap() # maps between stochastic data and constraints self.stochastic_data_to_constraints_map = ComponentMap() self.constraint_to_stochastic_data_map = ComponentMap() # maps between stochastic data and variable lower and upper bounds self.stochastic_data_to_variables_lb_map = ComponentMap() self.variable_to_stochastic_data_lb_map = ComponentMap() self.stochastic_data_to_variables_ub_map = ComponentMap() self.variable_to_stochastic_data_ub_map = ComponentMap() self.variable_symbols = ComponentMap() if not isinstance(reference_model, Block): raise TypeError("reference model input must be a Pyomo model") self.reference_model = reference_model # # Extract stochastic parameters from the # StochasticDataAnnotation object # self.stochastic_data = \ _extract_stochastic_data(self.reference_model) # # Get the variable stages from the # VariableStageAnnotation object # (self.stage_to_variables_map, self.variable_to_stage_map, self._variable_stage_assignments) = \ _map_variable_stages(self.reference_model) self.time_stages = tuple(sorted(self.stage_to_variables_map)) assert self.time_stages[0] == 1 self.variable_symbols = ComponentUID.generate_cuid_string_map( self.reference_model, ctype=Var, repr_version=tree_structure.CUID_repr_version) # remove the parent blocks from this map keys_to_delete = [] for var in self.variable_symbols: if var.parent_component().ctype is not Var: keys_to_delete.append(var) for key in keys_to_delete: del self.variable_symbols[key] # # Get the stage cost components from the StageCostAnnotation # and generate a dummy single-scenario scenario tree # stage_cost_annotation = locate_annotations( self.reference_model, StageCostAnnotation, max_allowed=1) if len(stage_cost_annotation) == 0: raise ValueError( "Reference model is missing stage cost " "annotation: %s" % (StageCostAnnotation.__name__)) else: assert len(stage_cost_annotation) == 1 stage_cost_annotation = stage_cost_annotation[0][1] stage_cost_assignments = ComponentMap( stage_cost_annotation.expand_entries()) stage1_cost = None stage2_cost = None for cdata, stagenum in stage_cost_assignments.items(): if stagenum == 1: stage1_cost = cdata elif stagenum == 2: stage2_cost = cdata if stage1_cost is None: raise ValueError("Missing stage cost annotation " "for time stage: 1") if stage2_cost is None: raise ValueError("Missing stage cost annotation " "for time stage: 2") assert stage1_cost is not stage2_cost self._stage1_cost = stage1_cost self._stage2_cost = stage2_cost # # Extract the locations of variables and stochastic data # within the model # sto_obj = StochasticObjectiveAnnotation() for objcntr, obj in enumerate( self.reference_model.component_data_objects( Objective, active=True, descend_into=True), 1): if objcntr > 1: raise ValueError( "Reference model can not contain more than one " "active objective") self.objective = obj self.objective_sense = obj.sense obj_params = tuple( self._collect_mutable_parameters(obj.expr).values()) self.objective_to_stochastic_data_map[obj] = [] for paramdata in obj_params: if paramdata in self.stochastic_data: self.stochastic_data_to_objectives_map.\ setdefault(paramdata, []).append(obj) self.objective_to_stochastic_data_map[obj].\ append(paramdata) if len(self.objective_to_stochastic_data_map[obj]) == 0: del self.objective_to_stochastic_data_map[obj] else: # TODO: Can we make this declaration sparse # by identifying which variables have # stochastic coefficients? How to handle # non-linear expressions? sto_obj.declare(obj) obj_variables = tuple( self._collect_variables(obj.expr).values()) self.objective_to_variables_map[obj] = [] for var in obj_variables: self.variable_to_objectives_map.\ setdefault(var, []).append(obj) self.objective_to_variables_map[obj].append(var) if len(self.objective_to_variables_map[obj]) == 0: del self.objective_to_variables_map[obj] sto_conbounds = StochasticConstraintBoundsAnnotation() sto_conbody = StochasticConstraintBodyAnnotation() for con in self.reference_model.component_data_objects( Constraint, active=True, descend_into=True): lower_params = tuple( self._collect_mutable_parameters(con.lower).values()) body_params = tuple( self._collect_mutable_parameters(con.body).values()) upper_params = tuple( self._collect_mutable_parameters(con.upper).values()) # TODO: Can we make this declaration sparse # by idenfifying which variables have # stochastic coefficients? How to handle # non-linear expressions? Currently, this # code also fails to detect that mutable # "constant" expressions might fall out # of the body and into the bounds. if len(body_params): sto_conbody.declare(con) if len(body_params) or \ len(lower_params) or \ len(upper_params): sto_conbounds.declare(con, lb=bool(len(lower_params) or len(body_params)), ub=bool(len(upper_params) or len(body_params))) all_stochastic_params = {} for param in itertools.chain(lower_params, body_params, upper_params): if param in self.stochastic_data: all_stochastic_params[id(param)] = param if len(all_stochastic_params) > 0: self.constraint_to_stochastic_data_map[con] = [] # no params will appear twice in this iteration for param in all_stochastic_params.values(): self.stochastic_data_to_constraints_map.\ setdefault(param, []).append(con) self.constraint_to_stochastic_data_map[con].\ append(param) body_variables = tuple( self._collect_variables(con.body).values()) self.constraint_to_variables_map[con] = [] for var in body_variables: self.variable_to_constraints_map.\ setdefault(var, []).append(con) self.constraint_to_variables_map[con].append(var) # For now, it is okay to have SOSConstraints in the # representation of a problem, but the SOS # constraints can't have custom weights that # represent stochastic data for soscon in self.reference_model.component_data_objects( SOSConstraint, active=True, descend_into=True): for var, weight in soscon.get_items(): weight_params = tuple( self._collect_mutable_parameters(weight).values()) if param in self.stochastic_data: raise ValueError( "SOSConstraints with stochastic data are currently" " not supported in embedded stochastic programs. " "The SOSConstraint component '%s' has a weight " "term for variable '%s' that references stochastic" " parameter '%s'" % (soscon.name, var.name, param.name)) self.variable_to_constraints_map.\ setdefault(var, []).append(soscon) self.constraint_to_variables_map.\ setdefault(soscon, []).append(var) sto_varbounds = StochasticVariableBoundsAnnotation() for var in self.reference_model.component_data_objects( Var, descend_into=True): lower_params = tuple( self._collect_mutable_parameters(var.lb).values()) upper_params = tuple( self._collect_mutable_parameters(var.ub).values()) if (len(lower_params) > 0) or \ (len(upper_params) > 0): sto_varbounds.declare(var, lb=bool(len(lower_params) > 0), ub=bool(len(upper_params) > 0)) self.variable_to_stochastic_data_lb_map[var] = [] for param in lower_params: if param in self.stochastic_data: self.stochastic_data_to_variables_lb_map.\ setdefault(param, []).append(var) self.variable_to_stochastic_data_lb_map[var].\ append(param) if len(self.variable_to_stochastic_data_lb_map[var]) == 0: del self.variable_to_stochastic_data_lb_map[var] self.variable_to_stochastic_data_ub_map[var] = [] for param in upper_params: if param in self.stochastic_data: self.stochastic_data_to_variables_ub_map.\ setdefault(param, []).append(var) self.variable_to_stochastic_data_ub_map[var].\ append(param) if len(self.variable_to_stochastic_data_ub_map[var]) == 0: del self.variable_to_stochastic_data_ub_map[var] # # Generate the explicit annotations # # first make sure these annotations do not already exist if len(locate_annotations(self.reference_model, StochasticConstraintBoundsAnnotation)) > 0: raise ValueError("Reference model can not contain " "a StochasticConstraintBoundsAnnotation declaration.") if len(locate_annotations(self.reference_model, StochasticConstraintBodyAnnotation)) > 0: raise ValueError("Reference model can not contain " "a StochasticConstraintBodyAnnotation declaration.") if len(locate_annotations(self.reference_model, StochasticObjectiveAnnotation)) > 0: raise ValueError("Reference model can not contain " "a StochasticObjectiveAnnotation declaration.") # now add any necessary annotations if sto_obj.has_declarations: assert not hasattr(self.reference_model, ".pyspembeddedsp_stochastic_objective_annotation") setattr(self.reference_model, ".pyspembeddedsp_stochastic_objective_annotation", sto_obj) if sto_conbody.has_declarations: assert not hasattr(self.reference_model, ".pyspembeddedsp_stochastic_constraint_body_annotation") setattr(self.reference_model, ".pyspembeddedsp_stochastic_constraint_body_annotation", sto_conbody) if sto_conbounds.has_declarations: assert not hasattr(self.reference_model, ".pyspembeddedsp_stochastic_constraint_bounds_annotation") setattr(self.reference_model, ".pyspembeddedsp_stochastic_constraint_bounds_annotation", sto_conbounds) if sto_varbounds.has_declarations: assert not hasattr(self.reference_model, ".pyspembeddedsp_stochastic_variable_bounds_annotation") setattr(self.reference_model, ".pyspembeddedsp_stochastic_variable_bounds_annotation", sto_varbounds)