def __setitem__(self, ndx, val): """ Define the setitem operation: var[ndx] = val """ # # Get the variable data object # vardata = None if ndx in self._data: vardata = self._data[ndx] elif normalize_index.flatten: _ndx = normalize_index(ndx) if _ndx in self._data: vardata = self._data[_ndx] if vardata is None: if self.is_indexed(): vardata = self.add(ndx) else: msg = "Cannot set the value of a simple variable '%s' with index '%s'" raise KeyError(msg % (self.name, str(ndx))) # # Set the value # vardata.set_value(val)
def __setitem__(self, ndx, val): """ Define the setitem operation: var[ndx] = val """ # # Get the variable data object # vardata = None if ndx in self._data: vardata = self._data[ndx] elif normalize_index.flatten: _ndx = normalize_index(ndx) if _ndx in self._data: vardata = self._data[_ndx] if vardata is None: if self.is_indexed(): vardata = self.add(ndx) else: msg = "Cannot set the value of a simple variable '%s' with index '%s'" raise KeyError(msg % ( self.cname(True), str(ndx) )) # # Set the value # vardata.set_value(val)
def slice_component_along_sets(comp, sets, context=None): """Slice a component along the indices corresponding to some sets, wherever they appear in the component's block hierarchy. Given a component or component data object, for all parent components and parent blocks between the object and the `context` block, replace any index corresponding to a set in `sets` with slices or an ellipsis. Parameters: ----------- comp: `pyomo.core.base.component.Component` or `pyomo.core.base.component.ComponentData` Component whose parent structure to search and replace sets: `pyomo.common.collections.ComponentSet` Contains the sets to replace with slices context: `pyomo.core.base.block.Block` or `pyomo.core.base.block._BlockData` Block below which to search for sets Returns: -------- `pyomo.core.base.indexed_component_slice.IndexedComponent_slice`: Slice of `comp` with wildcards replacing the indices of `sets` """ # Cast to ComponentSet so a tuple or list of sets is an appropriate # argument. Otherwise a tuple or list of sets will potentially # silently do the wrong thing (as inclusion in a tuple or list does # not distinguish between different sets with the same elements). sets = ComponentSet(sets) if context is None: context = comp.model() call_stack = get_component_call_stack(comp, context) # Maintain a pointer to the component so we can get # the index set and know which locations to slice. comp = context sliced_comp = context while call_stack: call, arg = call_stack.pop() if call is IndexedComponent_slice.get_attribute: comp = getattr(comp, arg) sliced_comp = getattr(sliced_comp, arg) elif call is IndexedComponent_slice.get_item: index_set = comp.index_set() comp = comp[arg] # Process arg to replace desired indices with slices. location_set_map = get_location_set_map(arg, index_set) arg = replace_indices(arg, location_set_map, sets) if normalize_index.flatten: arg = normalize_index(arg) sliced_comp = sliced_comp[arg] return sliced_comp
def flatten_tuple(x): """ This wraps around normalize_index. It flattens a nested sequence into a single tuple and always returns a tuple, even for single element inputs. Returns ------- tuple """ x = normalize_index(x) if isinstance(x, tuple): return x return (x, )
def __setitem__(self, ndx, val): # # Get the expression data object # exprdata = None if ndx in self._data: exprdata = self._data[ndx] else: _ndx = normalize_index(ndx) if _ndx in self._data: exprdata = self._data[_ndx] if exprdata is None: raise KeyError("Cannot set the value of Expression '%s' with " "invalid index '%s'" % (self.name, str(ndx))) # # Set the value # exprdata.set_value(val)
def __setitem__(self, ndx, val): """ Add a parameter value to the index. """ # # TBD: Potential optimization: if we find that updating a Param is # more common than setting it in the first place, then first # checking the _data and then falling back on the _index *might* # be more efficient. # if self._constructed and not self._mutable: raise TypeError( """Attempting to set the value of the immutable parameter %s after the parameter has been constructed. If you intend to change the value of this parameter dynamically, please declare the parameter as mutable [i.e., Param(mutable=True)]""" % (self.cname(True), )) # # Check if we have a valid index. # We assume that most calls to this method will send either a # scalar or a valid tuple. So, for efficiency, we will check the # index *first*, and only go through the hassle of # flattening things if the ndx is not found. # ndx_ = () if ndx in self._index: ndx_ = ndx elif normalize_index.flatten: ndx = normalize_index(ndx) if ndx in self._index: ndx_ = ndx if ndx_ == (): if not self.is_indexed(): msg = "Error setting parameter value: " \ "Cannot treat the scalar Param '%s' as an array" \ % ( self.cname(True), ) else: msg = "Error setting parameter value: " \ "Index '%s' is not valid for array Param '%s'" \ % ( ndx, self.cname(True), ) raise KeyError(msg) # We have a valid index, so do the actual set operation. self._raw_setitem(ndx_, val)
def __setitem__(self, ndx, val): """ Add a parameter value to the index. """ # # TBD: Potential optimization: if we find that updating a Param is # more common than setting it in the first place, then first # checking the _data and then falling back on the _index *might* # be more efficient. # if self._constructed and not self._mutable: raise TypeError( """Attempting to set the value of the immutable parameter %s after the parameter has been constructed. If you intend to change the value of this parameter dynamically, please declare the parameter as mutable [i.e., Param(mutable=True)]""" % (self.name,)) # # Check if we have a valid index. # We assume that most calls to this method will send either a # scalar or a valid tuple. So, for efficiency, we will check the # index *first*, and only go through the hassle of # flattening things if the ndx is not found. # ndx_ = () if ndx in self._index: ndx_ = ndx elif normalize_index.flatten: ndx = normalize_index(ndx) if ndx in self._index: ndx_ = ndx if ndx_ == (): if not self.is_indexed(): msg = "Error setting parameter value: " \ "Cannot treat the scalar Param '%s' as an array" \ % ( self.name, ) else: msg = "Error setting parameter value: " \ "Index '%s' is not valid for array Param '%s'" \ % ( ndx, self.name, ) raise KeyError(msg) # We have a valid index, so do the actual set operation. self._raw_setitem(ndx_, val)
def __call__(self, *args): model = args[0] idx = args[1:] if len(idx) > 1 and idx not in self.disj._index: logger.warning( "Constructing disjunction from " "unrecognized index: %s", str(idx)) elif len(idx) == 1 and idx[0] not in self.disj._index: logger.warning( "Constructing disjunction from " "unrecognized index: %s", str(idx[0])) elif not idx: idx = None if self.disj._disjunction_rule is not None: tmp = list(args) #tmp.append(self.disj) tmp = tuple(tmp) disjuncts = self.disj._disjunction_rule(*tmp) elif type(self.disj._disjunctive_set) in (tuple, list): # explicit disjunction over a user-specified list of disjuncts disjuncts = self.disj._disjunctive_set elif isinstance(self.disj._disjunctive_set, Disjunct): # pick one of all disjuncts if len(self.disj._disjunctive_set._data): disjuncts = self.disj._data.values() else: disjuncts = (self.disj._disjunctive_set) else: msg = 'Bad expression passed to Disjunction' raise TypeError(msg) for d in disjuncts: if not isinstance(d, _DisjunctData): msg = 'Non-disjunct (type="%s") found in ' \ 'disjunctive set for disjunction %s' % \ (type(d), self.disj.name) raise ValueError(msg) self.disj._disjuncts[normalize_index(idx)] = disjuncts # sum(disjuncts) == 1 return (sum(d.indicator_var for d in disjuncts), 1.0)
def __setitem__(self, ndx, val): # # Get the expression data object # exprdata = None if ndx in self._data: exprdata = self._data[ndx] else: _ndx = normalize_index(ndx) if _ndx in self._data: exprdata = self._data[_ndx] if exprdata is None: raise KeyError( "Cannot set the value of Expression '%s' with " "invalid index '%s'" % (self.name, str(ndx))) # # Set the value # exprdata.set_value(val)
def __call__(self, *args): model = args[0] idx = args[1:] if len(idx)>1 and idx not in self.disj._index: logger.warning("Constructing disjunction from " "unrecognized index: %s", str(idx)) elif len(idx) == 1 and idx[0] not in self.disj._index: logger.warning("Constructing disjunction from " "unrecognized index: %s", str(idx[0])) elif not idx: idx = None if self.disj._disjunction_rule is not None: tmp = list(args) #tmp.append(self.disj) tmp = tuple(tmp) disjuncts = self.disj._disjunction_rule(*tmp) elif type(self.disj._disjunctive_set) in (tuple, list): # explicit disjunction over a user-specified list of disjuncts disjuncts = self.disj._disjunctive_set elif isinstance(self.disj._disjunctive_set, Disjunct): # pick one of all disjuncts if len(self.disj._disjunctive_set._data): disjuncts = self.disj._data.values() else: disjuncts = ( self.disj._disjunctive_set ) else: msg = 'Bad expression passed to Disjunction' raise TypeError(msg) for d in disjuncts: if not isinstance(d, _DisjunctData): msg = 'Non-disjunct (type="%s") found in ' \ 'disjunctive set for disjunction %s' % \ ( type(d), self.disj.name ) raise ValueError(msg) self.disj._disjuncts[normalize_index(idx)] = disjuncts # sum(disjuncts) == 1 return (sum(d.indicator_var for d in disjuncts), 1.0)
def test_normalize_index(self): # Test that normalize_index works correctly self.assertEqual("abc", normalize_index("abc")) self.assertEqual(1, normalize_index(1)) self.assertEqual(1, normalize_index([1])) self.assertEqual((1, 2, 3), normalize_index((1, 2, 3))) self.assertEqual((1, 2, 3), normalize_index([1, 2, 3])) self.assertEqual((1, 2, 3, 4), normalize_index((1, 2, [3, 4]))) self.assertEqual((1, 2, 'abc'), normalize_index((1, 2, 'abc'))) self.assertEqual((1, 2, 'abc'), normalize_index((1, 2, ('abc', )))) a = [0, 9, 8] self.assertEqual((1, 2, 0, 9, 8), normalize_index((1, 2, a))) self.assertEqual((1, 2, 3, 4, 5), normalize_index([[], 1, [], 2, [[], 3, [[], 4, []], []], 5, []])) self.assertEqual((), normalize_index([[[[], []], []], []])) self.assertEqual((), normalize_index([[], [[], [ [], ]]])) # Test that normalize_index doesn't expand component-like things m = ConcreteModel() m.x = Var() m.y = Var([1]) m.i = Set(initialize=[1]) m.j = Set([1], initialize=[1]) self.assertIs(m, normalize_index(m)) self.assertIs(m.x, normalize_index(m.x)) self.assertIs(m.y, normalize_index(m.y)) self.assertIs(m.y[1], normalize_index(m.y[1])) self.assertIs(m.i, normalize_index(m.i)) self.assertIs(m.j, normalize_index(m.j)) self.assertIs(m.j[1], normalize_index(m.j[1]))
def slice_component_along_sets( component, sets, context_slice=None, normalize=None, ): """ This function generates all possible slices of the provided component along the provided sets. That is, it will iterate over the component's other indexing sets and, for each index, yield a slice along the sets specified in the call signature. Arguments --------- component: Component The component whose slices will be yielded sets: ComponentSet ComponentSet of Pyomo sets that will be sliced along context_slice: IndexedComponent_slice If provided, instead of creating a new slice, we will extend this one with appropriate getattr and getitem calls. normalize: Bool If False, the returned index (from the product of "other sets") is not normalized, regardless of the value of normalize_index.flatten. This is necessary to use this index with _fill_indices. Yields ------ tuple The first entry is the index in the product of "other sets" corresponding to the slice, and the second entry is the slice at that index. """ set_set = ComponentSet(sets) subsets = list(component.index_set().subsets()) temp_idx = [get_slice_for_set(s) if s in set_set else _NotAnIndex for s in subsets] other_sets = [s for s in subsets if s not in set_set] if context_slice is None: base_component = component else: base_component = getattr(context_slice, component.local_name) if component.is_indexed(): # We need to iterate over sets that aren't sliced # `c.is_indexed()` covers the case when UnindexedComponent_set # is in `other_sets`. if other_sets: cross_prod = other_sets[0].cross(*other_sets[1:]) else: # If we are only indexed by sets we need to slice, we # should just use tuple(temp_idx) as our index. We spoof # a cross_prod here so we don't have to repeat the try/except # logic below in a separate branch. An empty tuple is the right # singleton to work in the embedded call to _fill_indices. cross_prod = [tuple()] for prod_index, new_index in _fill_indices_from_product( temp_idx, cross_prod, ): try: if normalize_index.flatten: # This index is always normalized if normalize_index.flatten # is True. I have not encountered a situation where # "denormalization" makes sense here. # As normalization is also done in the IndexedComponent, # normalizing here primarily just affects what the resulting # slice "looks like." E.g. slice(None) vs (slice(None),). # This has implications for generating CUIDs from these # slices, where we would like consistency in the string # representation. # TODO: Should CUID normalize (slice(None),)? new_index = normalize_index(new_index) c_slice = base_component[new_index] if type(c_slice) is IndexedComponent_slice: # This is just to make sure we do not have an # empty slice. # # Note that c_slice is not necessarily a slice. # We enter this loop even if no sets need slicing. temp_slice = c_slice.duplicate() next(iter(temp_slice)) if ((normalize is None and normalize_index.flatten) or normalize): # Most users probably want this index to be normalized, # so they can more conveniently use it as a key in a # mapping. (E.g. they will get "a" as opposed to ("a",).) # However, to use it in the calling routine # generate_sliced_components, we need this index to not # have been normalized, so that indices are tuples, # partitioned according to their "factor sets." # This is why we allow the argument normalize=False to # override normalize_index.flatten. prod_index = normalize_index(prod_index) yield prod_index, c_slice except StopIteration: # We have an empty slice for some reason, e.g. # a coordinate of `new_index` from the cross # product was skipped in the original component. pass except KeyError: # We are creating scalar components from a product of # sets. Components may be undefined for certain indices. # We want to simply skip that index and move on. pass else: # Component is a data object c_slice = base_component yield (), c_slice