示例#1
0
def Reference(reference, ctype=_NotSpecified):
    """Creates a component that references other components

    ``Reference`` generates a *reference component*; that is, an indexed
    component that does not contain data, but instead references data
    stored in other components as defined by a component slice.  The
    ctype parameter sets the :py:meth:`Component.type` of the resulting
    indexed component.  If the ctype parameter is not set and all data
    identified by the slice (at construction time) share a common
    :py:meth:`Component.type`, then that type is assumed.  If either the
    ctype parameter is ``None`` or the data has more than one ctype, the
    resulting indexed component will have a ctype of
    :py:class:`IndexedComponent`.

    If the indices associated with wildcards in the component slice all
    refer to the same :py:class:`Set` objects for all data identifed by
    the slice, then the resulting indexed component will be indexed by
    the product of those sets.  However, if all data do not share common
    set objects, or only a subset of indices in a multidimentional set
    appear as wildcards, then the resulting indexed component will be
    indexed by a :py:class:`SetOf` containing a
    :py:class:`_ReferenceSet` for the slice.

    Parameters
    ----------
    reference : :py:class:`_IndexedComponent_slice`
        component slice that defines the data to include in the
        Reference component

    ctype : :py:class:`type` [optional]
        the type used to create the resulting indexed component.  If not
        specified, the data's ctype will be used (if all data share a
        common ctype).  If multiple data ctypes are found or type is
        ``None``, then :py:class:`IndexedComponent` will be used.

    Examples
    --------

    .. doctest::

        >>> from pyomo.environ import *
        >>> m = ConcreteModel()
        >>> @m.Block([1,2],[3,4])
        ... def b(b,i,j):
        ...     b.x = Var(bounds=(i,j))
        ...
        >>> m.r1 = Reference(m.b[:,:].x)
        >>> m.r1.pprint()
        r1 : Size=4, Index=r1_index
            Key    : Lower : Value : Upper : Fixed : Stale : Domain
            (1, 3) :     1 :  None :     3 : False :  True :  Reals
            (1, 4) :     1 :  None :     4 : False :  True :  Reals
            (2, 3) :     2 :  None :     3 : False :  True :  Reals
            (2, 4) :     2 :  None :     4 : False :  True :  Reals

    Reference components may also refer to subsets of the original data:

    .. doctest::

        >>> m.r2 = Reference(m.b[:,3].x)
        >>> m.r2.pprint()
        r2 : Size=2, Index=b_index_0
            Key : Lower : Value : Upper : Fixed : Stale : Domain
              1 :     1 :  None :     3 : False :  True :  Reals
              2 :     2 :  None :     3 : False :  True :  Reals

    Reference components may have wildcards at multiple levels of the
    model hierarchy:

    .. doctest::

        >>> from pyomo.environ import *
        >>> m = ConcreteModel()
        >>> @m.Block([1,2])
        ... def b(b,i):
        ...     b.x = Var([3,4], bounds=(i,None))
        ...
        >>> m.r3 = Reference(m.b[:].x[:])
        >>> m.r3.pprint()
        r3 : Size=4, Index=r3_index
            Key    : Lower : Value : Upper : Fixed : Stale : Domain
            (1, 3) :     1 :  None :  None : False :  True :  Reals
            (1, 4) :     1 :  None :  None : False :  True :  Reals
            (2, 3) :     2 :  None :  None : False :  True :  Reals
            (2, 4) :     2 :  None :  None : False :  True :  Reals

    The resulting reference component may be used just like any other
    component.  Changes to the stored data will be reflected in the
    original objects:

    .. doctest::

        >>> m.r3[1,4] = 10
        >>> m.b[1].x.pprint()
        x : Size=2, Index=b[1].x_index
            Key : Lower : Value : Upper : Fixed : Stale : Domain
              3 :     1 :  None :  None : False :  True :  Reals
              4 :     1 :    10 :  None : False : False :  Reals

    """
    if isinstance(reference, _IndexedComponent_slice):
        pass
    elif isinstance(reference, Component):
        reference = reference[...]
    else:
        raise TypeError("First argument to Reference constructors must be a "
                        "component or component slice (received %s)" %
                        (type(reference).__name__, ))

    _data = _ReferenceDict(reference)
    _iter = iter(reference)
    if ctype is _NotSpecified:
        ctypes = set()
    else:
        # If the caller specified a ctype, then we will prepopulate the
        # list to improve our chances of avoiding a scan of the entire
        # Reference
        ctypes = set((1, 2))
    index = []
    for obj in _iter:
        ctypes.add(obj.type())
        if not isinstance(obj, ComponentData):
            # This object is not a ComponentData (likely it is a pure
            # IndexedComponent container).  As the Reference will treat
            # it as if it *were* a ComponentData, we will skip ctype
            # identification and return a base IndexedComponent, thereby
            # preventing strange exceptions in the writers and with
            # things like pprint().  Of course, all of this logic is
            # skipped if the User knows better and forced a ctype on us.
            ctypes.add(0)
        if index is not None:
            index = _identify_wildcard_sets(_iter._iter_stack, index)
        # Note that we want to walk the entire slice, unless we can
        # prove that BOTH there aren't common indexing sets AND there is
        # more than one ctype.
        elif len(ctypes) > 1:
            break
    if not index:
        index = SetOf(_ReferenceSet(reference))
    else:
        wildcards = sum(
            (sorted(iteritems(lvl)) for lvl in index if lvl is not None), [])
        index = wildcards[0][1]
        if not isinstance(index, _SetDataBase):
            index = SetOf(index)
        for lvl, idx in wildcards[1:]:
            if not isinstance(idx, _SetDataBase):
                idx = SetOf(idx)
            index = index * idx
    if ctype is _NotSpecified:
        if len(ctypes) == 1:
            ctype = ctypes.pop()
        else:
            ctype = IndexedComponent
    elif ctype is None:
        ctype = IndexedComponent

    obj = ctype(index, ctype=ctype)
    obj._constructed = True
    obj._data = _data
    return obj
示例#2
0
def Reference(reference, ctype=_NotSpecified):
    """Creates a component that references other components

    ``Reference`` generates a *reference component*; that is, an indexed
    component that does not contain data, but instead references data
    stored in other components as defined by a component slice.  The
    ctype parameter sets the :py:meth:`Component.type` of the resulting
    indexed component.  If the ctype parameter is not set and all data
    identified by the slice (at construction time) share a common
    :py:meth:`Component.type`, then that type is assumed.  If either the
    ctype parameter is ``None`` or the data has more than one ctype, the
    resulting indexed component will have a ctype of
    :py:class:`IndexedComponent`.

    If the indices associated with wildcards in the component slice all
    refer to the same :py:class:`Set` objects for all data identifed by
    the slice, then the resulting indexed component will be indexed by
    the product of those sets.  However, if all data do not share common
    set objects, or only a subset of indices in a multidimentional set
    appear as wildcards, then the resulting indexed component will be
    indexed by a :py:class:`SetOf` containing a
    :py:class:`_ReferenceSet` for the slice.

    Parameters
    ----------
    reference : :py:class:`IndexedComponent_slice`
        component slice that defines the data to include in the
        Reference component

    ctype : :py:class:`type` [optional]
        the type used to create the resulting indexed component.  If not
        specified, the data's ctype will be used (if all data share a
        common ctype).  If multiple data ctypes are found or type is
        ``None``, then :py:class:`IndexedComponent` will be used.

    Examples
    --------

    .. doctest::

        >>> from pyomo.environ import *
        >>> m = ConcreteModel()
        >>> @m.Block([1,2],[3,4])
        ... def b(b,i,j):
        ...     b.x = Var(bounds=(i,j))
        ...
        >>> m.r1 = Reference(m.b[:,:].x)
        >>> m.r1.pprint()
        r1 : Size=4, Index=r1_index, ReferenceTo=b[:, :].x
            Key    : Lower : Value : Upper : Fixed : Stale : Domain
            (1, 3) :     1 :  None :     3 : False :  True :  Reals
            (1, 4) :     1 :  None :     4 : False :  True :  Reals
            (2, 3) :     2 :  None :     3 : False :  True :  Reals
            (2, 4) :     2 :  None :     4 : False :  True :  Reals

    Reference components may also refer to subsets of the original data:

    .. doctest::

        >>> m.r2 = Reference(m.b[:,3].x)
        >>> m.r2.pprint()
        r2 : Size=2, Index=b_index_0, ReferenceTo=b[:, 3].x
            Key : Lower : Value : Upper : Fixed : Stale : Domain
              1 :     1 :  None :     3 : False :  True :  Reals
              2 :     2 :  None :     3 : False :  True :  Reals

    Reference components may have wildcards at multiple levels of the
    model hierarchy:

    .. doctest::

        >>> m = ConcreteModel()
        >>> @m.Block([1,2])
        ... def b(b,i):
        ...     b.x = Var([3,4], bounds=(i,None))
        ...
        >>> m.r3 = Reference(m.b[:].x[:])
        >>> m.r3.pprint()
        r3 : Size=4, Index=r3_index, ReferenceTo=b[:].x[:]
            Key    : Lower : Value : Upper : Fixed : Stale : Domain
            (1, 3) :     1 :  None :  None : False :  True :  Reals
            (1, 4) :     1 :  None :  None : False :  True :  Reals
            (2, 3) :     2 :  None :  None : False :  True :  Reals
            (2, 4) :     2 :  None :  None : False :  True :  Reals

    The resulting reference component may be used just like any other
    component.  Changes to the stored data will be reflected in the
    original objects:

    .. doctest::

        >>> m.r3[1,4] = 10
        >>> m.b[1].x.pprint()
        x : Size=2, Index=b[1].x_index
            Key : Lower : Value : Upper : Fixed : Stale : Domain
              3 :     1 :  None :  None : False :  True :  Reals
              4 :     1 :    10 :  None : False : False :  Reals

    """
    referent = reference
    if isinstance(reference, IndexedComponent_slice):
        _data = _ReferenceDict(reference)
        _iter = iter(reference)
        slice_idx = []
        index = None
    elif isinstance(reference, Component):
        reference = reference[...]
        _data = _ReferenceDict(reference)
        _iter = iter(reference)
        slice_idx = []
        index = None
    elif isinstance(reference, ComponentData):
        # Create a dummy IndexedComponent container with a "normal"
        # Scalar interface.  This relies on the assumption that the
        # Component uses a standard storage model.
        _idx = next(iter(UnindexedComponent_set))
        _parent = reference.parent_component()
        comp = _parent.__class__(SetOf(UnindexedComponent_set))
        comp.construct()
        comp._data[_idx] = reference
        #
        # HACK: Set the _parent to match the ComponentData's container's
        # parent so that block.clone() infers the correct block scope
        # for this "hidden" component
        #
        # TODO: When Block supports proper "hidden" / "anonymous"
        # components, switch this HACK over to that API
        comp._parent = _parent._parent
        #
        reference = comp[...]
        _data = _ReferenceDict(reference)
        _iter = iter(reference)
        slice_idx = []
        index = None
    elif isinstance(reference, Mapping):
        _data = _ReferenceDict_mapping(dict(reference))
        _iter = _data.values()
        slice_idx = None
        index = SetOf(_data)
    elif isinstance(reference, Sequence):
        _data = _ReferenceDict_mapping(OrderedDict(enumerate(reference)))
        _iter = _data.values()
        slice_idx = None
        index = OrderedSetOf(_data)
    else:
        raise TypeError(
            "First argument to Reference constructors must be a "
            "component, component slice, Sequence, or Mapping (received %s)" %
            (type(reference).__name__, ))

    if ctype is _NotSpecified:
        ctypes = set()
    else:
        # If the caller specified a ctype, then we will prepopulate the
        # list to improve our chances of avoiding a scan of the entire
        # Reference (by simulating multiple ctypes having been found, we
        # can break out as soon as we know that there are not common
        # subsets).
        ctypes = set((1, 2))

    for obj in _iter:
        ctypes.add(obj.ctype)
        if not isinstance(obj, ComponentData):
            # This object is not a ComponentData (likely it is a pure
            # IndexedComponent container).  As the Reference will treat
            # it as if it *were* a ComponentData, we will skip ctype
            # identification and return a base IndexedComponent, thereby
            # preventing strange exceptions in the writers and with
            # things like pprint().  Of course, all of this logic is
            # skipped if the User knows better and forced a ctype on us.
            ctypes.add(0)
        # Note that we want to walk the entire slice, unless we can
        # prove that BOTH there aren't common indexing sets (i.e., index
        # is None) AND there is more than one ctype.
        if slice_idx is not None:
            # As long as we haven't ruled out the possibility of common
            # wildcard sets, then we will use _identify_wildcard_sets to
            # identify the wilcards for this obj and check compatibility
            # of the wildcards with any previously-identified wildcards.
            slice_idx = _identify_wildcard_sets(_iter._iter_stack, slice_idx)
        elif len(ctypes) > 1:
            break

    if index is None:
        if not slice_idx:
            index = SetOf(_ReferenceSet(reference))
        else:
            wildcards = sum((sorted(lvl.items())
                             for lvl in slice_idx if lvl is not None), [])
            # Wildcards is a list of (coordinate, set) tuples.  Coordinate
            # is that within the subsets list, and set is a wildcard set.
            index = wildcards[0][1]
            # index is the first wildcard set.
            if not isinstance(index, _SetDataBase):
                index = SetOf(index)
            for lvl, idx in wildcards[1:]:
                if not isinstance(idx, _SetDataBase):
                    idx = SetOf(idx)
                index = index * idx
            # index is now either a single Set, or a SetProduct of the
            # wildcard sets.
    if ctype is _NotSpecified:
        if len(ctypes) == 1:
            ctype = ctypes.pop()
        else:
            ctype = IndexedComponent
    elif ctype is None:
        ctype = IndexedComponent

    obj = ctype(index, ctype=ctype)
    obj._constructed = True
    obj._data = _data
    obj.referent = referent
    return obj