# Managing Expressions¶

## Creating a String Representation of an Expression¶

There are several ways that string representations can be created
from an expression, but the `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.

### Algebraic vs. Nested Functional Form¶

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:

```
from pyomo.core.expr import current as EXPR
M = ConcreteModel()
M.x = Var()
e = sin(M.x) + 2*M.x
# sin(x) + 2*x
print(EXPR.expression_to_string(e))
# sum(sin(x), prod(2, x))
print(EXPR.expression_to_string(e, verbose=True))
```

### Labeler and Symbol Map¶

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:

```
from pyomo.core.expr import current as EXPR
M = ConcreteModel()
M.x = Var()
M.y = Var()
e = sin(M.x) + 2*M.y
# sin(x1) + 2*x2
print(EXPR.expression_to_string(e, labeler=NumericLabeler('x')))
```

The `smap`

option is used to specify a symbol map object
(`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.

### Standardized 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:

```
from pyomo.core.expr import current as EXPR
M = ConcreteModel()
M.x = Var()
M.y = Var()
e = sin(M.x) + 2*M.y + M.x*M.y - 3
# -3 + 2*y + sin(x) + x*y
print(EXPR.expression_to_string(e, standardize=True))
```

### Other Ways to Generate String Representations¶

There are two other standard ways to generate string representations:

- Call the
`__str__()`

magic method (e.g. using the Python`str()`

function. This calls`expression_to_string`

with the option`standardize`

equal to`True`

(see below). - Call the
`to_string()`

method on the`ExpressionBase`

class. This defaults to calling`expression_to_string`

with the option`standardize`

equal to`False`

(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.

## Cloning Expressions¶

Expressions are automatically cloned only during certain expression
transformations. Since this can be an expensive operation, the
`clone_counter`

context
manager object is provided to track the number of times the
`clone_expression`

function is executed.

For example:

```
from pyomo.core.expr import current as EXPR
M = ConcreteModel()
M.x = Var()
with EXPR.clone_counter() as counter:
start = counter.count
e1 = sin(M.x)
e2 = e1.clone()
total = counter.count - start
assert(total == 1)
```

## Evaluating Expressions¶

Expressions can be evaluated when all variables and parameters in
the expression have a value. The `value`

function can be used to walk the expression tree and compute the
value of an expression. For example:

```
M = ConcreteModel()
M.x = Var()
M.x.value = math.pi/2.0
val = value(M.x)
assert(isclose(val, math.pi/2.0))
```

Additionally, expressions define the `__call__()`

method, so the
following is another way to compute the value of an expression:

```
val = M.x()
assert(isclose(val, math.pi/2.0))
```

If a parameter or variable is undefined, then the `value`

function and `__call__()`

method will
raise an exception. This exception can be suppressed using the
`exception`

option. For example:

```
M = ConcreteModel()
M.x = Var()
val = value(M.x, exception=False)
assert(val is None)
```

This option is useful in contexts where adding a try block is inconvenient in your modeling script.

Note

Both the `value`

function and
`__call__()`

method call the `evaluate_expression`

function. In
practice, this function will be slightly faster, but the
difference is only meaningful when expressions are evaluated
many times.

## Identifying Components and Variables¶

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`

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:

```
from pyomo.core.expr import current as EXPR
M = ConcreteModel()
M.x = Var()
M.p = Param(mutable=True)
e = M.p+M.x
s = set([type(M.p)])
assert(list(EXPR.identify_components(e, s)) == [M.p])
```

The `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:

```
from pyomo.core.expr import current as EXPR
M = ConcreteModel()
M.x = Var()
M.y = Var()
e = M.x+M.y
M.y.value = 1
M.y.fixed = True
assert(set(id(v) for v in EXPR.identify_variables(e)) == set([id(M.x), id(M.y)]))
assert(set(id(v) for v in EXPR.identify_variables(e, include_fixed=False)) == set([id(M.x)]))
```

## Walking an Expression Tree with a Visitor Class¶

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`

- A
`visitor()`

method is called for each node in the tree, and the visitor class collects information about the tree. `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 from`visitor()`

. `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 from`visitor()`

.

These classes define a variety of suitable tree search methods:

`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`

**dfs_postorder_stack**: postorder depth-first search using a stack

`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`

and`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 the`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.

### SimpleExpressionVisitor Example¶

In this example, we describe an visitor class that counts the number of nodes in an expression (including leaf nodes). Consider the following class:

```
from pyomo.core.expr import current as EXPR
class SizeofVisitor(EXPR.SimpleExpressionVisitor):
def __init__(self):
self.counter = 0
def visit(self, node):
self.counter += 1
def finalize(self):
return self.counter
```

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:

```
def sizeof_expression(expr):
#
# Create the visitor object
#
visitor = SizeofVisitor()
#
# Compute the value using the :func:`xbfs` search method.
#
return visitor.xbfs(expr)
```

### ExpressionValueVisitor Example¶

In this example, we describe an visitor class that clones the expression tree (including leaf nodes). Consider the following class:

```
from pyomo.core.expr import current as EXPR
class CloneVisitor(EXPR.ExpressionValueVisitor):
def __init__(self):
self.memo = {'__block_scope__': { id(None): False }}
def visit(self, node, values):
#
# Clone the interior node
#
return node.construct_clone(tuple(values), self.memo)
def visiting_potential_leaf(self, node):
#
# Clone leaf nodes in the expression tree
#
if node.__class__ in native_numeric_types or\
node.__class__ not in pyomo5_expression_types:\
return True, copy.deepcopy(node, self.memo)
return False, None
```

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.

```
def clone_expression(expr):
#
# Create the visitor object
#
visitor = CloneVisitor()
#
# Clone the expression using the :func:`dfs_postorder_stack`
# search method.
#
return visitor.dfs_postorder_stack(expr)
```

### ExpressionReplacementVisitor Example¶

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:

```
from pyomo.core.expr import current as EXPR
class ScalingVisitor(EXPR.ExpressionReplacementVisitor):
def __init__(self, scale):
super(ScalingVisitor, self).__init__()
self.scale = scale
def visiting_potential_leaf(self, node):
#
# Clone leaf nodes in the expression tree
#
if node.__class__ in native_numeric_types:
return True, node
if node.is_variable_type():
return True, self.scale[id(node)]*node
if isinstance(node, EXPR.LinearExpression):
node_ = copy.deepcopy(node)
node_.constant = node.constant
node_.linear_vars = copy.copy(node.linear_vars)
node_.linear_coefs = []
for i,v in enumerate(node.linear_vars):
node_.linear_coefs.append( node.linear_coefs[i]*self.scale[id(v)] )
return True, node_
return False, None
```

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.

```
def scale_expression(expr, scale):
#
# Create the visitor object
#
visitor = ScalingVisitor(scale)
#
# Scale the expression using the :func:`dfs_postorder_stack`
# search method.
#
return visitor.dfs_postorder_stack(expr)
```

The `scale_expression()`

function is called with an expression and
a dictionary, `scale`

, that maps variable ID to model parameter. For example:

```
M = ConcreteModel()
M.x = Var(range(5))
M.p = Param(range(5), mutable=True)
scale={}
for i in M.x:
scale[id(M.x[i])] = M.p[i]
e = quicksum(M.x[i] for i in M.x)
f = scale_expression(e,scale)
# p[0]*x[0] + p[1]*x[1] + p[2]*x[2] + p[3]*x[3] + p[4]*x[4]
print(f)
```