Source code for pyomo.core.expr.base

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

from pyomo.common.dependencies import attempt_import
from pyomo.common.numeric_types import native_types
from pyomo.core.pyomoobject import PyomoObject
from pyomo.core.expr.expr_common import OperatorAssociativity

visitor, _ = attempt_import('pyomo.core.expr.visitor')


[docs] class ExpressionBase(PyomoObject): """The base class for all Pyomo expression systems. This class is used to define nodes in a general expression tree. Individual expression systems (numeric, logical, etc.) will mix this class in with their fundamental base data type (NumericValue, BooleanValue, etc) to form the base node of that expression system. """ __slots__ = () PRECEDENCE = 0 # Most operators in Python are left-to-right associative """Return the associativity of this operator. Returns 1 if this operator is left-to-right associative or -1 if it is right-to-left associative. Any other return value will be interpreted as "not associative" (implying any arguments that are at this operator's PRECEDENCE will be enclosed in parens). """ ASSOCIATIVITY = OperatorAssociativity.LEFT_TO_RIGHT
[docs] def nargs(self): """Returns the number of child nodes. Note ---- Individual expression nodes may use different internal storage schemes, so it is imperative that developers use this method and not assume the existence of a particular attribute! Returns ------- int: A nonnegative integer that is the number of child nodes. """ raise NotImplementedError( f"Derived expression ({self.__class__}) failed to implement nargs()" )
[docs] def arg(self, i): """Return the i-th child node. Parameters ---------- i: int Index of the child argument to return Returns: The i-th child node. """ if i < 0: i += self.nargs() if i < 0: raise KeyError( "Invalid index for expression argument: %d" % i - self.nargs() ) elif i >= self.nargs(): raise KeyError("Invalid index for expression argument: %d" % i) return self._args_[i]
@property def args(self): """Return the child nodes Returns ------- list or tuple: Sequence containing only the child nodes of this node. The return type depends on the node storage model. Users are not permitted to change the returned data (even for the case of data returned as a list), as that breaks the promise of tree immutability. """ raise NotImplementedError( f"Derived expression ({self.__class__}) failed to implement args()" ) def __call__(self, exception=True): """Evaluate the value of the expression tree. Parameters ---------- exception: bool If :const:`False`, then an exception raised while evaluating is captured, and the value returned is :const:`None`. Default is :const:`True`. Returns ------- The value of the expression or :const:`None`. """ return visitor.evaluate_expression(self, exception) def __str__(self): """Returns a string description of the expression. Note: The value of ``pyomo.core.expr.expr_common.TO_STRING_VERBOSE`` is used to configure the execution of this method. If this value is :const:`True`, then the string representation is a nested function description of the expression. The default is :const:`False`, which returns an algebraic (infix notation) description of the expression. Returns ------- str """ return visitor.expression_to_string(self)
[docs] def to_string(self, verbose=None, labeler=None, smap=None, compute_values=False): """Return a string representation of the expression tree. Parameters ---------- verbose: bool If :const:`True`, then the string representation consists of nested functions. Otherwise, the string representation is an algebraic (infix notation) equation. Defaults to :const:`False`. labeler: An object that generates string labels for variables in the expression tree. Defaults to :const:`None`. smap: If specified, this :class:`SymbolMap <pyomo.core.expr.symbol_map.SymbolMap>` is used to cache labels for variables. compute_values (bool): If :const:`True`, then parameters and fixed variables are evaluated before the expression string is generated. Default is :const:`False`. Returns: A string representation for the expression tree. """ return visitor.expression_to_string( self, verbose=verbose, labeler=labeler, smap=smap, compute_values=compute_values, )
def _to_string(self, values, verbose, smap): """ Construct a string representation for this node, using the string representations of its children. This method is called by the :class:`_ToStringVisitor <pyomo.core.expr.current._ToStringVisitor>` class. It must must be defined in subclasses. Args: values (list): The string representations of the children of this node. verbose (bool): If :const:`True`, then the string representation consists of nested functions. Otherwise, the string representation is an algebraic equation. smap: If specified, this :class:`SymbolMap <pyomo.core.expr.symbol_map.SymbolMap>` is used to cache labels for variables. Returns: A string representation for this node. """ raise NotImplementedError( f"Derived expression ({self.__class__}) failed to implement _to_string()" )
[docs] def getname(self, *args, **kwds): """Return the text name of a function associated with this expression object. In general, no arguments are passed to this function. Args: *arg: a variable length list of arguments **kwds: keyword arguments Returns: A string name for the function. """ raise NotImplementedError( f"Derived expression ({self.__class__}) failed to implement getname()" )
[docs] def clone(self, substitute=None): """ Return a clone of the expression tree. Note: This method does not clone the leaves of the tree, which are numeric constants and variables. It only clones the interior nodes, and expression leaf nodes like :class:`_MutableLinearExpression<pyomo.core.expr.current._MutableLinearExpression>`. However, named expressions are treated like leaves, and they are not cloned. Args: substitute (dict): a dictionary that maps object ids to clone objects generated earlier during the cloning process. Returns: A new expression tree. """ return visitor.clone_expression(self, substitute=substitute)
[docs] def create_node_with_local_data(self, args, classtype=None): """ Construct a node using given arguments. This method provides a consistent interface for constructing a node, which is used in tree visitor scripts. In the simplest case, this returns:: self.__class__(args) But in general this creates an expression object using local data as well as arguments that represent the child nodes. Args: args (list): A list of child nodes for the new expression object Returns: A new expression object with the same type as the current class. """ if classtype is None: classtype = self.__class__ return classtype(args)
[docs] def is_constant(self): """Return True if this expression is an atomic constant This method contrasts with the is_fixed() method. This method returns True if the expression is an atomic constant, that is it is composed exclusively of constants and immutable parameters. NumericValue objects returning is_constant() == True may be simplified to their numeric value at any point without warning. Note: This defaults to False, but gets redefined in sub-classes. """ return False
[docs] def is_fixed(self): """ Return :const:`True` if this expression contains no free variables. Returns: A boolean. """ return visitor._expression_is_fixed(self)
def _is_fixed(self, values): """ Compute whether this expression is fixed given the fixed values of its children. This method is called by the :class:`_IsFixedVisitor <pyomo.core.expr.current._IsFixedVisitor>` class. It can be over-written by expression classes to customize this logic. Args: values (list): A list of boolean values that indicate whether the children of this expression are fixed Returns: A boolean that is :const:`True` if the fixed values of the children are all :const:`True`. """ return all(values)
[docs] def is_potentially_variable(self): """ Return :const:`True` if this expression might represent a variable expression. This method returns :const:`True` when (a) the expression tree contains one or more variables, or (b) the expression tree contains a named expression. In both cases, the expression cannot be treated as constant since (a) the variables may not be fixed, or (b) the named expressions may be changed at a later time to include non-fixed variables. Returns: A boolean. Defaults to :const:`True` for expressions. """ return True
[docs] def is_named_expression_type(self): """ Return :const:`True` if this object is a named expression. This method returns :const:`False` for this class, and it is included in other classes within Pyomo that are not named expressions, which allows for a check for named expressions without evaluating the class type. Returns: A boolean. """ return False
[docs] def is_expression_type(self, expression_system=None): """ Return :const:`True` if this object is an expression. This method obviously returns :const:`True` for this class, but it is included in other classes within Pyomo that are not expressions, which allows for a check for expressions without evaluating the class type. Returns: A boolean. """ return expression_system is None or expression_system == self.EXPRESSION_SYSTEM
[docs] def size(self): """ Return the number of nodes in the expression tree. Returns: A nonnegative integer that is the number of interior and leaf nodes in the expression tree. """ return visitor.sizeof_expression(self)
def _apply_operation(self, result): """ Compute the values of this node given the values of its children. This method is called by the :class:`_EvaluationVisitor <pyomo.core.expr.current._EvaluationVisitor>` class. It must be over-written by expression classes to customize this logic. Note: This method applies the logical operation of the operator to the arguments. It does *not* evaluate the arguments in the process, but assumes that they have been previously evaluated. But note that if this class contains auxiliary data (e.g. like the numeric coefficients in the :class:`LinearExpression <pyomo.core.expr.current.LinearExpression>` class) then those values *must* be evaluated as part of this function call. An uninitialized parameter value encountered during the execution of this method is considered an error. Args: values (list): A list of values that indicate the value of the children expressions. Returns: A floating point value for this expression. """ raise NotImplementedError( f"Derived expression ({self.__class__}) failed to " "implement _apply_operation()" )
[docs] class NPV_Mixin(object): __slots__ = () def is_potentially_variable(self): return False def create_node_with_local_data(self, args, classtype=None): assert classtype is None try: npv_args = all( type(arg) in native_types or not arg.is_potentially_variable() for arg in args ) except AttributeError: # We can hit this during expression replacement when the new # type is not a PyomoObject type, but is not in the # native_types set. We will play it safe and clear the NPV flag npv_args = False if npv_args: return super().create_node_with_local_data(args, None) else: return super().create_node_with_local_data( args, self.potentially_variable_base_class() ) def potentially_variable_base_class(self): cls = list(self.__class__.__bases__) cls.remove(NPV_Mixin) assert len(cls) == 1 return cls[0]
[docs] class ExpressionArgs_Mixin(object): __slots__ = ('_args_',)
[docs] def __init__(self, args): self._args_ = args
def nargs(self): return len(self._args_) @property def args(self): """ Return the child nodes Returns ------- list or tuple: Sequence containing only the child nodes of this node. The return type depends on the node storage model. Users are not permitted to change the returned data (even for the case of data returned as a list), as that breaks the promise of tree immutability. """ return self._args_