# ____________________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright (c) 2008-2026 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 logging
from weakref import ref as weakref_ref
from pyomo.common.log import is_debug_set
from pyomo.core.base.set_types import Any
from pyomo.core.base.var import IndexedVar, VarData
from pyomo.core.base.constraint import IndexedConstraint, ConstraintData
from pyomo.core.base.objective import IndexedObjective, ObjectiveData
from pyomo.core.base.expression import IndexedExpression, ExpressionData
from collections.abc import MutableSequence
logger = logging.getLogger('pyomo.core')
#
# In the future I think ComponentDict and ComponentList should inherit
# directly from (Active)IndexedComponent, and that class should be
# stripped down to a minimal interface. The abstract interface should
# be implemented on top of these classes.
#
[docs]
class ComponentList(MutableSequence):
[docs]
def __init__(self, interface_datatype, *args):
self._interface_datatype = interface_datatype
self._data = []
if len(args) > 0:
if len(args) > 1:
raise TypeError(
"ComponentList expected at most 1 arguments, "
"got %s" % (len(args))
)
for item in args[0]:
self.append(item)
def construct(self, data=None):
if is_debug_set(logger):
logger.debug( # pragma:nocover
"Constructing ComponentList object, name=%s, from data=%s"
% (self.name, str(data))
)
if self._constructed: # pragma:nocover
return
self._constructed = True
#
# Note: The following methods add a flavor of the dict-interface
# to this component. This is simply a hack that allows us to
# create working Pyomo examples using this prototype. The
# correct way to go would be to remove these methods and to
# update pyomo code everywhere to first check whether it is
# dealing with a dict or a list interface before
# iterating. I don't think that would be difficult to do.
#
def keys(self):
return range(len(self))
iterkeys = keys
def values(self):
return list(iter(self))
itervalues = values
def items(self):
return zip(self.keys(), self.values())
iteritems = items
#
# Define the MutableSequence abstract methods
#
# Currently __setitem__ only supports assignment of an explicit
# instantiation of the interface datatype. Updates to an already
# existing object inside this container need to be done via that
# objects interface methods or by directly accessing object
# attributes.
#
# * See notes above the __setitem__ method for ComponentDict
# about potentially supporting implicit assignment / update
#
def __setitem__(self, i, item):
if isinstance(item, self._interface_datatype):
# release the current component (assuming we don't get
# an index error)
# * see __delitem__ for explanation
if item._component is None:
item._component = weakref_ref(self)
if hasattr(self, "_active"):
self._active |= getattr(item, '_active', True)
self._data[i]._component = None
self._data[i] = item
item._index = i
return
elif self._data[i] is item:
# a very special case that makes sense to handle
# because the implied order should be: (1) delete
# the object at the current index, (2) insert the
# the new object. This performs both without any
# actions, but it is an extremely rare case, so
# it should go last.
return
# see note about allowing components to live in more than
# one container
raise ValueError(
"Invalid component object assignment to ComponentList "
"%s at index %s. A parent component has already been "
"assigned the object: %s" % (self.name, i, item.parent_component().name)
)
# see note about implicit assignment and update
raise TypeError(
"ComponentList must be assigned objects "
"of type %s. Invalid type for key %s: %s"
% (self._interface_datatype.__name__, i, type(item))
)
# * Only supports explicit objects. See notes above __setitem__
# for more information
[docs]
def insert(self, i, item):
if isinstance(item, self._interface_datatype):
if item._component is None:
item._component = weakref_ref(self)
if hasattr(self, "_active"):
self._active |= getattr(item, '_active', True)
self._data.insert(i, item)
item._index = i
return
# see note about allowing components to live in more than
# one container
raise ValueError(
"Invalid component object assignment to ComponentList "
"%s at index %s. A parent component has already been "
"assigned the object: %s" % (self.name, i, item.parent_component().name)
)
# see note about implicit assignment and update
raise TypeError(
"ComponentList must be assigned objects "
"of type %s. Invalid type for key %s: %s"
% (self._interface_datatype.__name__, i, type(item))
)
# Since we don't currently allow objects to be assigned when their
# parent component is already set, it would make sense to reset
# the parent component back to None for an object being removed
# from this container; thus, allowing objects to be naturally
# transferred between containers if one so chooses. This would be
# more complicated if allowing components to live in more than one
# container (see note above __setitem__), and maybe this is the
# reason not to support that.
def __delitem__(self, i):
obj = self._data[i]
obj._component = None
del self._data[i]
def __getitem__(self, i):
return self._data[i]
def __len__(self):
return self._data.__len__()
#
# Override a few default implementations on MutableSequence
#
# We want to avoid generating Pyomo expressions by comparing values
def __contains__(self, item):
item_id = id(item)
return any(item_id == id(_v) for _v in self._data)
# We want to avoid generating Pyomo expressions by comparing values
[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)
item_id = id(item)
i = start
while stop is None or i < stop:
try:
if id(self[i]) == item_id:
return i
except IndexError:
break
i += 1
raise ValueError
# We want to avoid generating Pyomo expressions by comparing values
[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
# Avoid errors related to calling __setitem__
# with a component that is already owned
[docs]
def reverse(self):
'S.reverse() -- reverse *IN PLACE*'
n = len(self)
data = self._data
for i in range(n // 2):
data[i], data[n - i - 1] = data[n - i - 1], data[i]
#
# ComponentList needs to come before IndexedComponent
# (or subclasses of) so we can override certain methods
#
[docs]
class XVarList(ComponentList, IndexedVar):
[docs]
def __init__(self, *args, **kwds):
IndexedVar.__init__(self, Any, **kwds)
# Constructor for ComponentList needs to
# go last in order to handle any initialization
# iterable as an argument
ComponentList.__init__(self, VarData, *args, **kwds)
[docs]
class XConstraintList(ComponentList, IndexedConstraint):
[docs]
def __init__(self, *args, **kwds):
IndexedConstraint.__init__(self, Any, **kwds)
# Constructor for ComponentList needs to
# go last in order to handle any initialization
# iterable as an argument
ComponentList.__init__(self, ConstraintData, *args, **kwds)
[docs]
class XObjectiveList(ComponentList, IndexedObjective):
[docs]
def __init__(self, *args, **kwds):
IndexedObjective.__init__(self, Any, **kwds)
# Constructor for ComponentList needs to
# go last in order to handle any initialization
# iterable as an argument
ComponentList.__init__(self, ObjectiveData, *args, **kwds)
[docs]
class XExpressionList(ComponentList, IndexedExpression):
[docs]
def __init__(self, *args, **kwds):
IndexedExpression.__init__(self, Any, **kwds)
# Constructor for ComponentList needs to
# go last in order to handle any initialization
# iterable as an argument
ComponentList.__init__(self, ExpressionData, *args, **kwds)