def replace_uncertain_bounds_with_constraints(model, uncertain_params): """ For variables of which the bounds are dependent on the parameters in the list `uncertain_params`, remove the bounds and add explicit variable bound inequality constraints. :param model: Model in which to make the bounds/constraint replacements :type model: class:`pyomo.core.base.PyomoModel.ConcreteModel` :param uncertain_params: List of uncertain model parameters :type uncertain_params: list """ uncertain_param_set = ComponentSet(uncertain_params) # component for explicit inequality constraints uncertain_var_bound_constrs = ConstraintList() model.add_component(unique_component_name(model, 'uncertain_var_bound_cons'), uncertain_var_bound_constrs) # get all variables in active objective and constraint expression(s) vars_in_cons = ComponentSet(get_vars_from_component(model, Constraint)) vars_in_obj = ComponentSet(get_vars_from_component(model, Objective)) for v in vars_in_cons | vars_in_obj: # get mutable parameters in variable bounds expressions ub = v.upper mutable_params_ub = ComponentSet(identify_mutable_parameters(ub)) lb = v.lower mutable_params_lb = ComponentSet(identify_mutable_parameters(lb)) # add explicit inequality constraint(s), remove variable bound(s) if mutable_params_ub & uncertain_param_set: if type(ub) is NPV_MinExpression: upper_bounds = ub.args else: upper_bounds = (ub,) for u_bnd in upper_bounds: uncertain_var_bound_constrs.add(v - u_bnd <= 0) v.setub(None) if mutable_params_lb & uncertain_param_set: if type(ub) is NPV_MaxExpression: lower_bounds = lb.args else: lower_bounds = (lb,) for l_bnd in lower_bounds: uncertain_var_bound_constrs.add(l_bnd - v <= 0) v.setlb(None)
def _discretize_bilinear(self, b, v, v_idx, u, u_idx): _z = b.z _dv = b.dv[v_idx] _u = Var(b.DISCRETIZATION, within=u.domain, bounds=u.bounds) logger.info("Discretizing (v=%s)*(u=%s) as u%s_v%s" % ( v.cname(True), u.cname(True), u_idx, v_idx )) b.add_component( "u%s_v%s" % (u_idx, v_idx), _u) _lb, _ub = u.bounds if _lb is None or _ub is None: raise RuntimeError("Couldn't relax variable %s: missing " "finite lower/upper bounds." % (u.cname(True))) _c = ConstraintList() b.add_component( "c_disaggregate_u%s_v%s" % (u_idx, v_idx), _c ) for k in b.DISCRETIZATION: # _lb * z[v_idx,k] <= _u[k] <= _ub * z[v_idx,k] _c.add(expr= _lb*_z[v_idx,k] <= _u[k] ) _c.add(expr= _u[k] <= _ub*_z[v_idx,k] ) # _lb * (1-z[v_idx,k]) <= u - _u[k] <= _ub * (1-z[v_idx,k]) _c.add(expr= _lb * (1-_z[v_idx,k]) <= u - _u[k] ) _c.add(expr= u - _u[k] <= _ub * (1-_z[v_idx,k])) _v_lb, _v_ub = v.bounds _bnd_rng = (_v_lb*_lb, _v_lb*_ub, _v_ub*_lb, _v_ub*_ub) _w = Var(bounds=(min(_bnd_rng), max(_bnd_rng))) b.add_component( "w%s_v%s" % (u_idx, v_idx), _w) K = max(b.DISCRETIZATION) _dw = Var(bounds=( min(0, _lb*2**-K, _ub*2**-K), max(0, _lb*2**-K, _ub*2**-K) )) b.add_component( "dw%s_v%s" % (u_idx, v_idx), _dw) _c = Constraint(expr= _w == _v_lb*u + (_v_ub-_v_lb) * ( sum(2**-k * _u[k] for k in b.DISCRETIZATION) + _dw ) ) b.add_component( "c_bilinear_u%s_v%s" % (u_idx, v_idx), _c ) _c = ConstraintList() b.add_component( "c_mccormick_u%s_v%s" % (u_idx, v_idx), _c ) # u_lb * dv <= dw <= u_ub * dv _c.add(expr= _lb*_dv <= _dw ) _c.add(expr= _dw <= _ub*_dv ) # (u-u_ub)*2^-K + u_ub*dv <= dw <= (u-u_lb)*2^-K + u_lb*dv _c.add(expr= (u - _ub)*2**-K + _ub*_dv <= _dw ) _c.add(expr= _dw <= (u - _lb)*2**-K + _lb*_dv ) return _w
def _relax_bilinear(self, b, u, v): u_lb, u_ub = u.bounds v_lb, v_ub = v.bounds if u_lb is None or u_ub is None: raise RuntimeError("Couldn't relax variable %s: missing " "finite lower/upper bounds." % (u.cname(True))) if v_lb is None or v_ub is None: raise RuntimeError("Couldn't relax variable %s: missing " "finite lower/upper bounds." % (v.cname(True))) w = Var(bounds=(min(u_lb*v_lb, u_lb*v_ub, u_ub*v_lb, u_ub*v_ub), max(u_lb*v_lb, u_lb*v_ub, u_ub*v_lb, u_ub*v_ub))) b.add_component("w_%s_%s" % (u.cname(), v.cname()), w) _c = ConstraintList(noruleinit=True) b.add_component( "c_mccormick_%s_%s" % (u.cname(), v.cname()), _c ) _c.add(expr=w >= u * v_lb + u_lb * v - u_lb*v_lb) _c.add(expr=w >= u * v_ub + u_ub * v - u_ub*v_ub) _c.add(expr=w <= u * v_lb + u_ub * v - u_ub*v_lb) _c.add(expr=w <= u * v_ub + u_lb * v - u_lb*v_ub) return w
def _apply_to(self, instance, **kwds): if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug("Calling ConnectorExpander") connectorsFound = False for c in instance.component_data_objects(Connector): connectorsFound = True break if not connectorsFound: return if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug(" Connectors found!") self._name_buffer = {} # # At this point, there are connectors in the model, so we must # look for constraints that involve connectors and expand them. # # List of the connectors in the order in which we found them # (this should be deterministic, provided that the user's model # is deterministic) connector_list = [] # list of constraints with connectors: tuple(constraint, connector_set) # (this should be deterministic, provided that the user's model # is deterministic) constraint_list = [] # ID of the next connector group (set of matched connectors) groupID = 0 # connector_groups stars out as a dict of {id(set): (groupID, set)} # If you sort by the groupID, then this will be deterministic. connector_groups = dict() # map of connector to the set of connectors that must match it matched_connectors = ComponentMap() # The set of connectors found in the current constraint found = ComponentSet() connector_types = set([SimpleConnector, _ConnectorData]) for constraint in instance.component_data_objects( Constraint, sort=SortComponents.deterministic): ref = None for c in EXPR.identify_components(constraint.body, connector_types): found.add(c) if c in matched_connectors: if ref is None: # The first connector in this constraint has # already been seen. We will use that Set as # the reference ref = matched_connectors[c] elif ref is not matched_connectors[c]: # We already have a reference group; merge this # new group into it. # # Optimization: this merge is linear in the size # of the src set. If the reference set is # smaller, save time by switching to a new # reference set. src = matched_connectors[c] if len(ref) < len(src): ref, src = src, ref ref.update(src) for _ in src: matched_connectors[_] = ref del connector_groups[id(src)] # else: pass # The new group *is* the reference group; # there is nothing to do. else: # The connector has not been seen before. connector_list.append(c) if ref is None: # This is the first connector in the constraint: # start a new reference set. ref = ComponentSet() connector_groups[id(ref)] = (groupID, ref) groupID += 1 # This connector hasn't been seen. Record it. ref.add(c) matched_connectors[c] = ref if ref is not None: constraint_list.append((constraint, found)) found = ComponentSet() # Validate all connector sets and expand the empty ones known_conn_sets = {} for groupID, conn_set in sorted(itervalues(connector_groups)): known_conn_sets[id(conn_set)] \ = self._validate_and_expand_connector_set(conn_set) # Expand each constraint for constraint, conn_set in constraint_list: cList = ConstraintList() constraint.parent_block().add_component( '%s.expanded' % (constraint.getname( fully_qualified=False, name_buffer=self._name_buffer), ), cList) connId = next(iter(conn_set)) ref = known_conn_sets[id(matched_connectors[connId])] for k, v in sorted(iteritems(ref)): if v[1] >= 0: _iter = v[0] else: _iter = (v[0], ) for idx in _iter: substitution = {} for c in conn_set: if v[1] >= 0: new_v = c.vars[k][idx] elif k in c.aggregators: new_v = c.vars[k].add() else: new_v = c.vars[k] substitution[id(c)] = new_v cList.add((constraint.lower, EXPR.clone_expression(constraint.body, substitution), constraint.upper)) constraint.deactivate() # Now, go back and implement VarList aggregators for conn in connector_list: block = conn.parent_block() for var, aggregator in iteritems(conn.aggregators): c = Constraint(expr=aggregator(block, conn.vars[var])) block.add_component( '%s.%s.aggregate' % (conn.getname(fully_qualified=True, name_buffer=self._name_buffer), var), c)
def _apply_to(self, instance, **kwds): if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug("Calling ConnectorExpander") connectorsFound = False for c in instance.component_data_objects(Connector): connectorsFound = True break if not connectorsFound: return if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug(" Connectors found!") # # At this point, there are connectors in the model, so we must # look for constraints that involve connectors and expand them. # connector_types = set([SimpleConnector, _ConnectorData]) constraint_list = [] connector_list = [] matched_connectors = {} found = dict() for constraint in instance.component_data_objects(Constraint): for c in expr.identify_variables( constraint.body, include_potentially_variable=True): if c.__class__ in connector_types: found[id(c)] = c if not found: continue # Note that it is important to copy the set of found # connectors, since the matching routine below will # manipulate sets in place. found_this_constraint = dict(found) constraint_list.append( (constraint, found_this_constraint) ) # Find all the connectors that are used in the constraint, # so we know which connectors to validate against each # other. Note that the validation must be transitive (that # is, if con1 has a & b and con2 has b & c, then a,b, and c # must all validate against each other. for cId, c in iteritems(found_this_constraint): if cId in matched_connectors: oldSet = matched_connectors[cId] found.update( oldSet ) for _cId in oldSet: matched_connectors[_cId] = found else: connector_list.append(c) matched_connectors[cId] = found # Reset found back to empty (this is more efficient as the # bulk of the constraints in the model will not have # connectors - so if we did this at the top of the loop, we # would spend a lot of time clearing empty sets found = {} # Validate all connector sets and expand the empty ones known_conn_sets = {} for connector in connector_list: conn_set = matched_connectors[id(connector)] if id(conn_set) in known_conn_sets: continue known_conn_sets[id(conn_set)] \ = self._validate_and_expand_connector_set(conn_set) # Expand each constraint for constraint, conn_set in constraint_list: cList = ConstraintList() constraint.parent_block().add_component( '%s.expanded' % ( constraint.local_name, ), cList ) connId = next(iterkeys(conn_set)) ref = known_conn_sets[id(matched_connectors[connId])] for k,v in sorted(iteritems(ref)): if v[1] >= 0: _iter = v[0] else: _iter = (v[0],) for idx in _iter: substitution = {} for c in itervalues(conn_set): if v[1] >= 0: new_v = c.vars[k][idx] elif k in c.aggregators: new_v = c.vars[k].add() else: new_v = c.vars[k] substitution[id(c)] = new_v cList.add(( constraint.lower, expr.clone_expression( constraint.body, substitution ), constraint.upper )) constraint.deactivate() # Now, go back and implement VarList aggregators for conn in connector_list: block = conn.parent_block() for var, aggregator in iteritems(conn.aggregators): c = Constraint(expr=aggregator(block, conn.vars[var])) block.add_component( '%s.%s.aggregate' % (conn.local_name, var), c )
def _apply_to(self, instance, **kwds): if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug("Calling ConnectorExpander") connectorsFound = False for c in instance.component_data_objects(Connector): connectorsFound = True break if not connectorsFound: return if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug(" Connectors found!") # # At this point, there are connectors in the model, so we must # look for constraints that involve connectors and expand them. # connector_types = set([SimpleConnector, _ConnectorData]) constraint_list = [] connector_list = [] matched_connectors = {} found = dict() for constraint in instance.component_data_objects(Constraint): for c in expr.identify_variables( constraint.body, include_potentially_variable=True): if c.__class__ in connector_types: found[id(c)] = c if not found: continue # Note that it is important to copy the set of found # connectors, since the matching routine below will # manipulate sets in place. found_this_constraint = dict(found) constraint_list.append((constraint, found_this_constraint)) # Find all the connectors that are used in the constraint, # so we know which connectors to validate against each # other. Note that the validation must be transitive (that # is, if con1 has a & b and con2 has b & c, then a,b, and c # must all validate against each other. for cId, c in iteritems(found_this_constraint): if cId in matched_connectors: oldSet = matched_connectors[cId] found.update(oldSet) for _cId in oldSet: matched_connectors[_cId] = found else: connector_list.append(c) matched_connectors[cId] = found # Reset found back to empty (this is more efficient as the # bulk of the constraints in the model will not have # connectors - so if we did this at the top of the loop, we # would spend a lot of time clearing empty sets found = {} # Validate all connector sets and expand the empty ones known_conn_sets = {} for connector in connector_list: conn_set = matched_connectors[id(connector)] if id(conn_set) in known_conn_sets: continue known_conn_sets[id(conn_set)] \ = self._validate_and_expand_connector_set(conn_set) # Expand each constraint for constraint, conn_set in constraint_list: cList = ConstraintList() constraint.parent_block().add_component( '%s.expanded' % (constraint.local_name, ), cList) connId = next(iterkeys(conn_set)) ref = known_conn_sets[id(matched_connectors[connId])] for k, v in sorted(iteritems(ref)): if v[1] >= 0: _iter = v[0] else: _iter = (v[0], ) for idx in _iter: substitution = {} for c in itervalues(conn_set): if v[1] >= 0: new_v = c.vars[k][idx] elif k in c.aggregators: new_v = c.vars[k].add() else: new_v = c.vars[k] substitution[id(c)] = new_v cList.add((constraint.lower, expr.clone_expression(constraint.body, substitution), constraint.upper)) constraint.deactivate() # Now, go back and implement VarList aggregators for conn in connector_list: block = conn.parent_block() for var, aggregator in iteritems(conn.aggregators): c = Constraint(expr=aggregator(block, conn.vars[var])) block.add_component('%s.%s.aggregate' % (conn.local_name, var), c)
def _apply_to(self, instance, **kwds): if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug("Calling ConnectorExpander") connectorsFound = False for c in instance.component_data_objects(Connector): connectorsFound = True break if not connectorsFound: return if __debug__ and logger.isEnabledFor(logging.DEBUG): #pragma:nocover logger.debug(" Connectors found!") self._name_buffer = {} # # At this point, there are connectors in the model, so we must # look for constraints that involve connectors and expand them. # # List of the connectors in the order in which we found them # (this should be deterministic, provided that the user's model # is deterministic) connector_list = [] # list of constraints with connectors: tuple(constraint, connector_set) # (this should be deterministic, provided that the user's model # is deterministic) constraint_list = [] # ID of the next connector group (set of matched connectors) groupID = 0 # connector_groups stars out as a dict of {id(set): (groupID, set)} # If you sort by the groupID, then this will be deterministic. connector_groups = dict() # map of connector to the set of connectors that must match it matched_connectors = ComponentMap() # The set of connectors found in the current constraint found = ComponentSet() connector_types = set([SimpleConnector, _ConnectorData]) for constraint in instance.component_data_objects( Constraint, sort=SortComponents.deterministic): ref = None for c in EXPR.identify_components(constraint.body, connector_types): found.add(c) if c in matched_connectors: if ref is None: # The first connector in this constraint has # already been seen. We will use that Set as # the reference ref = matched_connectors[c] elif ref is not matched_connectors[c]: # We already have a reference group; merge this # new group into it. # # Optimization: this merge is linear in the size # of the src set. If the reference set is # smaller, save time by switching to a new # reference set. src = matched_connectors[c] if len(ref) < len(src): ref, src = src, ref ref.update(src) for _ in src: matched_connectors[_] = ref del connector_groups[id(src)] # else: pass # The new group *is* the reference group; # there is nothing to do. else: # The connector has not been seen before. connector_list.append(c) if ref is None: # This is the first connector in the constraint: # start a new reference set. ref = ComponentSet() connector_groups[id(ref)] = (groupID, ref) groupID += 1 # This connector hasn't been seen. Record it. ref.add(c) matched_connectors[c] = ref if ref is not None: constraint_list.append((constraint, found)) found = ComponentSet() # Validate all connector sets and expand the empty ones known_conn_sets = {} for groupID, conn_set in sorted(itervalues(connector_groups)): known_conn_sets[id(conn_set)] \ = self._validate_and_expand_connector_set(conn_set) # Expand each constraint for constraint, conn_set in constraint_list: cList = ConstraintList() constraint.parent_block().add_component( '%s.expanded' % ( constraint.getname( fully_qualified=False, name_buffer=self._name_buffer), ), cList ) connId = next(iter(conn_set)) ref = known_conn_sets[id(matched_connectors[connId])] for k,v in sorted(iteritems(ref)): if v[1] >= 0: _iter = v[0] else: _iter = (v[0],) for idx in _iter: substitution = {} for c in conn_set: if v[1] >= 0: new_v = c.vars[k][idx] elif k in c.aggregators: new_v = c.vars[k].add() else: new_v = c.vars[k] substitution[id(c)] = new_v cList.add(( constraint.lower, EXPR.clone_expression( constraint.body, substitution ), constraint.upper )) constraint.deactivate() # Now, go back and implement VarList aggregators for conn in connector_list: block = conn.parent_block() for var, aggregator in iteritems(conn.aggregators): c = Constraint(expr=aggregator(block, conn.vars[var])) block.add_component( '%s.%s.aggregate' % ( conn.getname( fully_qualified=True, name_buffer=self._name_buffer), var), c )