Source code for pyomo.core.expr.expr_common

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

from contextlib import nullcontext

from pyomo.common import enums
from pyomo.common.deprecation import deprecated
from pyomo.common.modeling import NOTSET

TO_STRING_VERBOSE = False

# logical propositions
_and = 0
_or = 1
_inv = 2
_equiv = 3
_xor = 4
_impl = 5


#
# Provide a global value that indicates which expression system is being used
#
[docs] class Mode(enums.IntEnum): # coopr: Original Coopr/Pyomo expression system coopr_trees = 1 # coopr3: leverage reference counts to reduce the amount of required # expression cloning to ensure independent expression trees. coopr3_trees = 3 # pyomo4: rework the expression system to remove reliance on # reference counting. This enables pypy support (which doesn't have # reference counting). This version never became the default. pyomo4_trees = 4 # pyomo5: refinement of pyomo4. Expressions are now immutable by # contract, which tolerates "entangled" expression trees. Added # specialized classes for NPV expressions and LinearExpressions. pyomo5_trees = 5 # pyomo6: refinement of pyomo5 expression generation to leverage # multiple dispatch. Standardized expression storage and argument # handling (significant rework of the LinearExpression structure). pyomo6_trees = 6 # CURRENT = pyomo6_trees
_mode = Mode.CURRENT # We no longer support concurrent expression systems. _mode is left # primarily so we can support expression system-specific baselines assert _mode == Mode.pyomo6_trees
[docs] class OperatorAssociativity(enums.IntEnum): """Enum for indicating the associativity of an operator. LEFT_TO_RIGHT(1) if this operator is left-to-right associative or RIGHT_TO_LEFT(-1) if it is right-to-left associative. Any other values will be interpreted as "not associative" (implying any arguments that are at this operator's PRECEDENCE will be enclosed in parens). """ RIGHT_TO_LEFT = -1 NON_ASSOCIATIVE = 0 LEFT_TO_RIGHT = 1
[docs] class ExpressionType(enums.Enum): NUMERIC = 0 RELATIONAL = 1 LOGICAL = 2
[docs] class NUMERIC_ARG_TYPE(enums.IntEnum): MUTABLE = -2 ASNUMERIC = -1 INVALID = 0 NATIVE = 1 NPV = 2 PARAM = 3 VAR = 4 MONOMIAL = 5 LINEAR = 6 SUM = 7 OTHER = 8
[docs] class RELATIONAL_ARG_TYPE(enums.IntEnum, metaclass=enums.ExtendedEnumType): __base_enum__ = NUMERIC_ARG_TYPE INEQUALITY = 100 INVALID_RELATIONAL = 101
def _invalid(*args): return NotImplemented def _recast_mutable(expr): expr.make_immutable() if expr._nargs > 1: return expr elif not expr._nargs: return 0 else: return expr._args_[0] def _unary_op_dispatcher_type_mapping(dispatcher, updates, TYPES=NUMERIC_ARG_TYPE): # # Special case (wrapping) operators # def _asnumeric(a): a = a.as_numeric() return dispatcher[a.__class__](a) def _mutable(a): a = _recast_mutable(a) return dispatcher[a.__class__](a) mapping = { TYPES.ASNUMERIC: _asnumeric, TYPES.MUTABLE: _mutable, TYPES.INVALID: _invalid, } mapping.update(updates) return mapping def _binary_op_dispatcher_type_mapping(dispatcher, updates, TYPES=NUMERIC_ARG_TYPE): # # Special case (wrapping) operators # def _any_asnumeric(a, b): b = b.as_numeric() return dispatcher[a.__class__, b.__class__](a, b) def _asnumeric_any(a, b): a = a.as_numeric() return dispatcher[a.__class__, b.__class__](a, b) def _asnumeric_asnumeric(a, b): a = a.as_numeric() b = b.as_numeric() return dispatcher[a.__class__, b.__class__](a, b) def _any_mutable(a, b): b = _recast_mutable(b) return dispatcher[a.__class__, b.__class__](a, b) def _mutable_any(a, b): a = _recast_mutable(a) return dispatcher[a.__class__, b.__class__](a, b) def _mutable_mutable(a, b): if a is b: # Note: _recast_mutable is an in-place operation: make sure # that we don't call it twice on the same object. a = b = _recast_mutable(a) else: a = _recast_mutable(a) b = _recast_mutable(b) return dispatcher[a.__class__, b.__class__](a, b) mapping = {} # Because ASNUMERIC and MUTABLE re-call the dispatcher, we want to # resolve ASNUMERIC first, MUTABLE second, and INVALID last. That # means we will add them to the dispatcher dict in opposite order so # "higher priority" callbacks override lower priority ones. mapping.update({(i, TYPES.INVALID): _invalid for i in TYPES}) mapping.update({(TYPES.INVALID, i): _invalid for i in TYPES}) mapping.update({(i, TYPES.MUTABLE): _any_mutable for i in TYPES}) mapping.update({(TYPES.MUTABLE, i): _mutable_any for i in TYPES}) mapping[TYPES.MUTABLE, TYPES.MUTABLE] = _mutable_mutable mapping.update({(i, TYPES.ASNUMERIC): _any_asnumeric for i in TYPES}) mapping.update({(TYPES.ASNUMERIC, i): _asnumeric_any for i in TYPES}) mapping[TYPES.ASNUMERIC, TYPES.ASNUMERIC] = _asnumeric_asnumeric mapping.update(updates) return mapping
[docs] @deprecated( """The clone counter has been removed and will always return 0. Beginning with Pyomo5 expressions, expression cloning (detangling) no longer occurs automatically within expression generation. As a result, the 'clone counter' has lost its utility and is no longer supported. This context manager will always report 0.""", version='6.4.3', ) class clone_counter(nullcontext): """Context manager for counting cloning events. This context manager counts the number of times that the :func:`clone_expression <pyomo.core.expr.current.clone_expression>` function is executed. """ _count = 0
[docs] def __init__(self): super().__init__(enter_result=self)
@property def count(self): """A property that returns the clone count value.""" return clone_counter._count
def _type_check_exception_arg(cls, exception): if exception is NOTSET: return True elif type(exception) is not bool: raise ValueError( f"{cls.ctype.__name__} '{cls.name}' was called with a non-bool " f"argument for 'exception': {exception}" ) else: return exception