Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
 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)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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, )
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
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]))
Ejemplo n.º 12
0
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