Source code for pyomo.core.kernel.heterogeneous_container

#  ___________________________________________________________________________
#
#  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.
#  ___________________________________________________________________________

from pyomo.core.kernel.base import (
    _no_ctype,
    _convert_ctype,
    _convert_descend_into,
    ICategorizedObjectContainer,
)


[docs]def heterogeneous_containers(node, ctype=_no_ctype, active=True, descend_into=True): """ A generator that yields all heterogeneous containers included in an object storage tree, including the root object. Heterogeneous containers are categorized objects with a category type different from their children. Args: node: The root object. ctype: Indicates the category of objects to include. The default value indicates that all categories should be included. active (:const:`True`/:const:`None`): Controls whether or not to filter the iteration to include only the active part of the storage tree. The default is :const:`True`. Setting this keyword to :const:`None` causes the active status of objects to be ignored. descend_into (bool, function): Indicates whether or not to descend into a heterogeneous container. Default is True, which is equivalent to `lambda x: True`, meaning all heterogeneous containers will be descended into. Returns: iterator of heterogeneous containers in the storage tree, include the root object. """ assert active in (None, True) # if not active, then nothing below is active if (active is not None) and (not node.active): return if not node.ctype._is_heterogeneous_container: return if not node._is_heterogeneous_container: assert node._is_container # a homogeneous container that stores # heterogeneous objects for obj in node.components(active=active): assert obj._is_heterogeneous_container yield from heterogeneous_containers( obj, ctype=ctype, active=active, descend_into=descend_into ) return # convert AML types into Kernel types (hack for the # solver interfaces) ctype = _convert_ctype.get(ctype, ctype) assert (ctype is _no_ctype) or ctype._is_heterogeneous_container # convert descend_into to a function if # it is not already one descend_into = _convert_descend_into(descend_into) # a heterogeneous container if (ctype is _no_ctype) or (node.ctype is ctype): yield node if not descend_into(node): return for child_ctype in node.child_ctypes(): if not child_ctype._is_heterogeneous_container: continue for child in node.children(ctype=child_ctype): assert child._is_container if (active is not None) and (not child.active): continue yield from heterogeneous_containers( child, ctype=ctype, active=active, descend_into=descend_into )
[docs]class IHeterogeneousContainer(ICategorizedObjectContainer): """ A partial implementation of the ICategorizedObjectContainer interface for implementations that store multiple categories of objects. Complete implementations need to set the _ctype attribute and declare the remaining required abstract properties of the ICategorizedObjectContainer base class. """ __slots__ = () _is_heterogeneous_container = True # # Interface #
[docs] def collect_ctypes(self, active=True, descend_into=True): """Returns the set of object category types that can be found under this container. Args: active (:const:`True`/:const:`None`): Controls whether or not to filter the iteration to include only the active part of the storage tree. The default is :const:`True`. Setting this keyword to :const:`None` causes the active status of objects to be ignored. descend_into (bool, function): Indicates whether or not to descend into a heterogeneous container. Default is True, which is equivalent to `lambda x: True`, meaning all heterogeneous containers will be descended into. Returns: A set of object category types """ assert active in (None, True) ctypes = set() # if not active, then nothing below is active if (active is not None) and (not self.active): return ctypes # convert descend_into to a function if # it is not already one descend_into = _convert_descend_into(descend_into) for child_ctype in self.child_ctypes(): for obj in self.components( ctype=child_ctype, active=active, descend_into=_convert_descend_into._false, ): ctypes.add(child_ctype) # just need 1 to appear in order to # count the child_ctype break if descend_into is _convert_descend_into._false: return ctypes # iterate over a copy of the set as we will # modify it in this loop for child_ctype in tuple(ctypes): if child_ctype._is_heterogeneous_container: for obj in self.components( ctype=child_ctype, active=active, descend_into=_convert_descend_into._false, ): assert obj._is_heterogeneous_container if descend_into(obj): ctypes.update( obj.collect_ctypes(active=active, descend_into=descend_into) ) return ctypes
[docs] def child_ctypes(self, *args, **kwds): """Returns the set of child object category types stored in this container.""" raise NotImplementedError # pragma:nocover
# # Define the ICategorizedObjectContainer abstract methods # # def child(self, *args, **kwds): # ... not defined here # def children(self, *args, **kwds): # ... not defined here
[docs] def components(self, ctype=_no_ctype, active=True, descend_into=True): """ Generates an efficient traversal of all components stored under this container. Components are categorized objects that are either (1) not containers, or (2) are heterogeneous containers. Args: ctype: Indicates the category of components to include. The default value indicates that all categories should be included. active (:const:`True`/:const:`None`): Controls whether or not to filter the iteration to include only the active part of the storage tree. The default is :const:`True`. Setting this keyword to :const:`None` causes the active status of objects to be ignored. descend_into (bool, function): Indicates whether or not to descend into a heterogeneous container. Default is True, which is equivalent to `lambda x: True`, meaning all heterogeneous containers will be descended into. Returns: iterator of components in the storage tree """ assert active in (None, True) # if not active, then nothing below is active if (active is not None) and (not self.active): return # convert AML types into Kernel types (hack for the # solver interfaces) ctype = _convert_ctype.get(ctype, ctype) # convert descend_into to a function if # it is not already one descend_into = _convert_descend_into(descend_into) if ctype is _no_ctype: for child in self.children(): if (active is not None) and (not child.active): continue if not child._is_container: yield child elif child._is_heterogeneous_container: yield child if descend_into(child): yield from child.components( active=active, descend_into=descend_into ) elif (descend_into is _convert_descend_into._false) or ( not child.ctype._is_heterogeneous_container ): assert child._is_container yield from child.components(active=active) else: assert child._is_container for obj in child.components(active=active): assert obj._is_heterogeneous_container yield obj if descend_into(obj): yield from obj.components( active=active, descend_into=descend_into ) else: for item in heterogeneous_containers( self, active=active, descend_into=descend_into ): for child in item.children(ctype=ctype): if (not child._is_container) or child._is_heterogeneous_container: if (active is None) or child.active: yield child else: assert child._is_container yield from child.components(active=active)