# ___________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright (c) 2008-2024
# National Technology and Engineering Solutions of Sandia, LLC
# Under the terms of Contract DE-NA0003525 with National Technology and
# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
# rights in this software.
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________
import collections.abc
from pyomo.core.kernel.homogeneous_container import IHomogeneousContainer
[docs]
class TupleContainer(IHomogeneousContainer, collections.abc.Sequence):
"""
A partial implementation of the IHomogeneousContainer
interface that provides tuple-like storage functionality.
Complete implementations need to set the _ctype property
at the class level and initialize the remaining
ICategorizedObject attributes during object creation. If
using __slots__, a slot named "_data" must be included.
Note that this implementation allows nested storage of
other ICategorizedObjectContainer implementations that
are defined with the same ctype.
"""
__slots__ = ()
_child_storage_delimiter_string = ""
_child_storage_entry_string = "[%s]"
[docs]
def __init__(self, *args):
self._data = []
self._init(args)
self._data = tuple(self._data)
def _init(self, args):
if len(args) > 0:
if len(args) > 1:
raise TypeError(
"%s expected at most 1 arguments, "
"got %s" % (self.__class__.__name__, len(args))
)
for item in args[0]:
self._insert(len(self), item)
def _fast_insert(self, i, item):
item._update_parent_and_storage_key(self, i)
self._data.insert(i, item)
def _insert(self, i, item):
if item.ctype is self.ctype:
if item._parent is None:
self._fast_insert(i, item)
return
# see note about allowing objects to live
# in more than one container
raise ValueError(
"Invalid assignment to type %s with index %s. "
"A parent container has already been "
"assigned to the object being inserted: %s"
% (self.__class__.__name__, i, item.parent.name)
)
else:
raise TypeError(
"Invalid assignment to type %s with index %s. "
"The object being inserted has the wrong "
"category type: %s" % (self.__class__.__name__, i, item.ctype)
)
#
# Define the ICategorizedObjectContainer abstract methods
#
[docs]
def child(self, key):
"""Get the child object associated with a given
storage key for this container.
Raises:
KeyError: if the argument is not a storage key
for any children of this container
"""
try:
return self.__getitem__(key)
except (IndexError, TypeError):
raise KeyError(str(key))
[docs]
def children(self):
"""A generator over the children of this container."""
return self._data.__iter__()
#
# Define the Sequence abstract methods
#
def __getitem__(self, i):
return self._data[i]
def __len__(self):
return self._data.__len__()
#
# Extend the interface to allow for equality comparison
#
# We want to avoid generating Pyomo expressions due to
# comparison of values.
# Convert both objects to a plain tuple of (type(val),
# id(val)) tuples and compare that instead.
def __eq__(self, other):
if not isinstance(other, (collections.abc.Set, collections.abc.Sequence)):
return False
return tuple((type(val), id(val)) for val in self) == tuple(
(type(val), id(val)) for val in other
)
def __ne__(self, other):
return not (self == other)
#
# Override a few default implementations on Sequence
#
# We want to avoid generating Pyomo expressions due to
# comparison of values.
def __iter__(self):
return self._data.__iter__()
def __contains__(self, item):
item_id = id(item)
return any(item_id == id(_v) for _v in self._data)
[docs]
def index(self, item, start=0, stop=None):
"""S.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
"""
if start is not None and start < 0:
start = max(len(self) + start, 0)
if stop is not None and stop < 0:
stop += len(self)
i = start
while stop is None or i < stop:
try:
if self[i] is item:
return i
except IndexError:
break
i += 1
raise ValueError
[docs]
def count(self, item):
'S.count(value) -> integer -- return number of occurrences of value'
item_id = id(item)
cnt = sum(1 for _v in self._data if id(_v) == item_id)
assert cnt == 1
return cnt