There are several ways that string representations can be created from an expression, but the expression_to_string
<pyomo.core.expr.current.expression_to_string>
function provides the most flexible mechanism for generating a string representation. The options to this function control distinct aspects of the string representation.
The default string representation is an algebraic form, which closely mimics the Python operations used to construct an expression. The verbose
flag can be set to True
to generate a string representation that is a nested functional form. For example:
../../tests/expr/managing_ex1.spy
The string representation used for variables in expression can be customized to define different label formats. If the labeler
option is specified, then this function (or class functor) is used to generate a string label used to represent the variable. Pyomo defines a variety of labelers in the pyomo.core.base.label module. For example, the NumericLabeler
defines a functor that can be used to sequentially generate simple labels with a prefix followed by the variable count:
../../tests/expr/managing_ex2.spy
The smap
option is used to specify a symbol map object (SymbolMap <pyomo.core.expr.symbol_map.SymbolMap>
), which caches the variable label data. This option is normally specified in contexts where the string representations for many expressions are being generated. In that context, a symbol map ensures that variables in different expressions have a consistent label in their associated string representations.
The standardize
option can be used to re-order the string representation to print polynomial terms before nonlinear terms. By default, standardize
is False
, and the string representation reflects the order in which terms were combined to form the expression. Pyomo does not guarantee that the string representation exactly matches the Python expression order, since some simplification and re-ordering of terms is done automatically to improve the efficiency of expression generation. But in most cases the string representation will closely correspond to the Python expression order.
If standardize
is True
, then the pyomo expression is processed to identify polynomial terms, and the string representation consists of the constant and linear terms followed by an expression that contains other nonlinear terms. For example:
../../tests/expr/managing_ex3.spy
There are two other standard ways to generate string representations:
- Call the
__str__
magic method (e.g. using the Pythonstr()
function. This callsexpression_to_string <pyomo.core.expr.current.expression_to_string>
with the optionstandardize
equal toTrue
(see below). - Call the
to_string
method on theExpressionBase <pyomo.core.expr.current.ExpressionBase>
class. This defaults to callingexpression_to_string <pyomo.core.expr.current.expression_to_string>
with the optionstandardize
equal toFalse
(see below).
In practice, we expect at the __str__
magic method will be used by most users, and the standardization of the output provides a consistent ordering of terms that should make it easier to interpret expressions.
Expressions are automatically cloned only during certain expression transformations. Since this can be an expensive operation, the clone_counter <pyomo.core.expr.current.clone_counter>
context manager object is provided to track the number of times the clone_expression <pyomo.core.expr.current.clone_expression>
function is executed.
For example:
../../tests/expr/managing_ex4.spy
Expressions can be evaluated when all variables and parameters in the expression have a value. The value <pyomo.core.expr.value>
function can be used to walk the expression tree and compute the value of an expression. For example:
../../tests/expr/managing_ex5.spy
Additionally, expressions define the __call__
method, so the following is another way to compute the value of an expression:
../../tests/expr/managing_ex6.spy
If a parameter or variable is undefined, then the value
<pyomo.core.expr.value>
function and __call__
method will raise an exception. This exception can be suppressed using the exception
option. For example:
../../tests/expr/managing_ex7.spy
This option is useful in contexts where adding a try block is inconvenient in your modeling script.
Note
Both the value <pyomo.core.expr.value>
function and __call__
method call the evaluate_expression
<pyomo.core.expr.current.evaluate_expression>
function. In practice, this function will be slightly faster, but the difference is only meaningful when expressions are evaluated many times.
Expression transformations sometimes need to find all nodes in an expression tree that are of a given type. Pyomo contains two utility functions that support this functionality. First, the identify_components <pyomo.core.expr.current.identify_components>
function is a generator function that walks the expression tree and yields all nodes whose type is in a specified set of node types. For example:
../../tests/expr/managing_ex8.spy
The identify_variables <pyomo.core.expr.current.identify_variables>
function is a generator function that yields all nodes that are variables. Pyomo uses several different classes to represent variables, but this set of variable types does not need to be specified by the user. However, the include_fixed
flag can be specified to omit fixed variables. For example:
../../tests/expr/managing_ex9.spy
Many of the utility functions defined above are implemented by walking an expression tree and performing an operation at nodes in the tree. For example, evaluating an expression is performed using a post-order depth-first search process where the value of a node is computed using the values of its children.
Walking an expression tree can be tricky, and the code requires intimate knowledge of the design of the expression system. Pyomo includes several classes that define so-called visitor patterns for walking expression tree:
SimpleExpressionVisitor <pyomo.core.expr.current.SimpleExpressionVisitor>
A
visitor
method is called for each node in the tree, and the visitor class collects information about the tree.ExpressionValueVisitor <pyomo.core.expr.current.ExpressionValueVisitor>
When the
visitor
method is called on each node in the tree, the values of its children have been computed. The value of the node is returned fromvisitor
.ExpressionReplacementVisitor <pyomo.core.expr.current.ExpressionReplacementVisitor>
When the
visitor
method is called on each node in the tree, it may clone or otherwise replace the node using objects for its children (which themselves may be clones or replacements from the original child objects). The new node object is returned fromvisitor
.
These classes define a variety of suitable tree search methods:
SimpleExpressionVisitor <pyomo.core.expr.current.SimpleExpressionVisitor>
- xbfs: breadth-first search where leaf nodes are immediately visited
- xbfs_yield_leaves: breadth-first search where leaf nodes are immediately visited, and the visit method yields a value
ExpressionValueVisitor <pyomo.core.expr.current.ExpressionValueVisitor>
- dfs_postorder_stack: postorder depth-first search using a stack
ExpressionReplacementVisitor <pyomo.core.expr.current.ExpressionReplacementVisitor>
- dfs_postorder_stack: postorder depth-first search using a stack
Note
The PyUtilib visitor classes define several other search methods that could be used with Pyomo expressions. But these are the only search methods currently used within Pyomo.
To implement a visitor object, a user creates a subclass of one of these classes. Only one of a few methods will need to be defined to implement the visitor:
visitor
Defines the operation that is performed when a node is visited. In the
ExpressionValueVisitor <pyomo.core.expr.current.ExpressionValueVisitor>
andExpressionReplacementVisitor <pyomo.core.expr.current.ExpressionReplacementVisitor>
visitor classes, this method returns a value that is used by its parent node.visiting_potential_leaf
Checks if the search should terminate with this node. If no, then this method returns the tuple
(False, None)
. If yes, then this method returns(False, value)
, where value is computed by this method. This method is not used in theSimpleExpressionVisitor <pyomo.core.expr.current.SimpleExpressionVisitor>
visitor class.finalize
This method defines the final value that is returned from the visitor. This is not normally redefined.
Detailed documentation of the APIs for these methods is provided with the class documentation for these visitors.
In this example, we describe an visitor class that counts the number of nodes in an expression (including leaf nodes). Consider the following class:
../../tests/expr/managing_visitor1.spy
The class constructor creates a counter, and the visit
method increments this counter for every node that is visited. The finalize
method returns the value of this counter after the tree has been walked. The following function illustrates this use of this visitor class:
../../tests/expr/managing_visitor2.spy
In this example, we describe an visitor class that clones the expression tree (including leaf nodes). Consider the following class:
../../tests/expr/managing_visitor3.spy
The visit
method creates a new expression node with children specified by values
. The visiting_potential_leaf
method performs a deepcopy
on leaf nodes, which are native Python types or non-expression objects.
../../tests/expr/managing_visitor4.spy
In this example, we describe an visitor class that replaces variables with scaled variables, using a mutable parameter that can be modified later. the following class:
../../tests/expr/managing_visitor5.spy
No visit
method needs to be defined. The visiting_potential_leaf
function identifies variable nodes and returns a product expression that contains a mutable parameter. The _LinearExpression
class has a different representation that embeds variables. Hence, this class must be handled in a separate condition that explicitly transforms this sub-expression.
../../tests/expr/managing_visitor6.spy
The scale_expression
function is called with an expression and a dictionary, scale
, that maps variable ID to model parameter. For example:
../../tests/expr/managing_visitor7.spy