Expression Classes
Expression classes typically represent unary and binary operations. The following table
describes the standard operators in Python and their associated Pyomo expression class:
Operation |
Python Syntax |
Pyomo Class |
sum |
x + y
|
SumExpression
|
product |
x * y
|
ProductExpression
|
negation |
- x
|
NegationExpression
|
division |
x / y
|
DivisionExpression
|
power |
x ** y
|
PowExpression
|
inequality |
x <= y
|
InequalityExpression
|
equality |
x == y
|
EqualityExpression
|
Additionally, there are a variety of other Pyomo expression classes that capture more general
logical relationships, which are summarized in the following table:
Operation |
Example |
Pyomo Class |
external function |
myfunc(x,y,z)
|
ExternalFunctionExpression
|
logical if-then-else |
Expr_if(IF=x, THEN=y, ELSE=z)
|
Expr_ifExpression
|
intrinsic function |
sin(x)
|
UnaryFunctionExpression
|
absolute function |
abs(x)
|
AbsExpression
|
Expression objects are immutable. Specifically, the list of
arguments to an expression object (a.k.a. the list of child nodes
in the tree) cannot be changed after an expression class is
constructed. To enforce this property, expression objects have a
standard API for accessing expression arguments:
args - a class property that returns a generator that yields the expression arguments
arg(i) - a function that returns the i-th argument
nargs() - a function that returns the number of expression arguments
Warning
Developers should never use the _args_ property directly!
The semantics for the use of this data has changed since earlier
versions of Pyomo. For example, in some expression classes the
the value nargs() may not equal len(_args_)!
Expression trees can be categorized in four different ways:
constant expressions - expressions that do not contain numeric constants and immutable parameters.
mutable expressions - expressions that contain mutable parameters but no variables.
potentially variable expressions - expressions that contain variables, which may be fixed.
fixed expressions - expressions that contain variables, all of which are fixed.
These three categories are illustrated with the following example:
m = pyo.ConcreteModel()
m.p = pyo.Param(default=10, mutable=False)
m.q = pyo.Param(default=10, mutable=True)
m.x = pyo.Var()
m.y = pyo.Var(initialize=1)
m.y.fixed = True
The following table describes four different simple expressions
that consist of a single model component, and it shows how they
are categorized:
Category |
m.p |
m.q |
m.x |
m.y |
constant |
True |
False |
False |
False |
not potentially variable |
True |
True |
False |
False |
potentially_variable |
False |
False |
True |
True |
fixed |
True |
True |
False |
True |
Expressions classes contain methods to test whether an expression
tree is in each of these categories. Additionally, Pyomo includes
custom expression classes for expression trees that are not potentially
variable. These custom classes will not normally be used by
developers, but they provide an optimization of the checks for
potentially variability.
Special Expression Classes
The following classes are exceptions to the design principles describe above.
Named Expressions
Named expressions allow for changes to an expression after it has
been constructed. For example, consider the expression f defined
with the Expression component:
M = pyo.ConcreteModel()
M.v = pyo.Var()
M.w = pyo.Var()
M.e = pyo.Expression(expr=2 * M.v)
f = M.e + 3 # f == 2*v + 3
M.e += M.w # f == 2*v + 3 + w
Although f is an immutable expression, whose definition is
fixed, a sub-expressions is the named expression M.e. Named
expressions have a mutable value. In other words, the expression
that they point to can change. Thus, a change to the value of
M.e changes the expression tree for any expression that includes
the named expression.
Note
The named expression classes are not implemented as sub-classes
of NumericExpression.
This reflects design constraints related to the fact that these
are modeling components that belong to class hierarchies other
than the expression class hierarchy, and Pyomo’s design prohibits
the use of multiple inheritance for these classes.
Linear Expressions
Pyomo includes a special expression class for linear expressions.
The class LinearExpression provides a compact
description of linear polynomials. Specifically, it includes a
constant value constant and two lists for coefficients and
variables: linear_coefs and linear_vars.
This expression object does not have arguments, and thus it is
treated as a leaf node by Pyomo visitor classes. Further, the
expression API functions described above do not work with this
class. Thus, developers need to treat this class differently when
walking an expression tree (e.g. when developing a problem
transformation).
Sum Expressions
Pyomo does not have a binary sum expression class. Instead,
it has an n-ary summation class, SumExpression. This expression class
treats sums as n-ary sums for efficiency reasons; many large
optimization models contain large sums. But note that this class
maintains the immutability property described above. This class
shares an underlying list of arguments with other SumExpression objects. A particular
object owns the first n arguments in the shared list, but
different objects may have different values of n.
This class acts like a normal immutable expression class, and the
API described above works normally. But direct access to the shared
list could have unexpected results.
Mutable Expressions
Finally, Pyomo includes several mutable expression classes
that are private. These are not intended to be used by users, but
they might be useful for developers in contexts where the developer
can appropriately control how the classes are used. Specifically,
immutability eliminates side-effects where changes to a sub-expression
unexpectedly create changes to the expression tree. But within the context of
model transformations, developers may be able to limit the use of
expressions to avoid these side-effects. The following mutable private classes
are available in Pyomo:
_MutableSumExpressionThis class
is used in the nonlinear_expression context manager to
efficiently combine sums of nonlinear terms.
_MutableLinearExpressionThis class
is used in the linear_expression context manager to
efficiently combine sums of linear terms.
Context Managers
Pyomo defines several context managers that can be used to declare
the form of expressions, and to define a mutable expression object that
efficiently manages sums.
The linear_expression
object is a context manager that can be used to declare a linear sum. For
example, consider the following two loops:
M = pyo.ConcreteModel()
M.x = pyo.Var(range(5))
s = 0
for i in range(5):
s += M.x[i]
with pyo.linear_expression() as e:
for i in range(5):
e += M.x[i]
The first apparent difference in these loops is that the value of
s is explicitly initialized while e is initialized when the
context manager is entered. However, a more fundamental difference
is that the expression representation for s differs from e.
Each term added to s results in a new, immutable expression.
By contrast, the context manager creates a mutable expression
representation for e. This difference allows for both (a) a
more efficient processing of each sum, and (b) a more compact
representation for the expression.
The difference between linear_expression and
nonlinear_expression
is the underlying representation that each supports. Note that
both of these are instances of context manager classes. In
singled-threaded applications, these objects can be safely used to
construct different expressions with different context declarations.
Finally, note that these context managers can be passed into the start
method for the quicksum function. For example:
M = pyo.ConcreteModel()
M.x = pyo.Var(range(5))
M.y = pyo.Var(range(5))
with pyo.linear_expression() as e:
pyo.quicksum((M.x[i] for i in M.x), start=e)
pyo.quicksum((M.y[i] for i in M.y), start=e)
This sum contains terms for M.x[i] and M.y[i]. The syntax
in this example is not intuitive because the sum is being stored
in e.
Note
We do not generally expect users or developers to use these
context managers. They are used by the quicksum and sum_product functions to accelerate expression
generation, and there are few cases where the direct use of
these context managers would provide additional utility to users
and developers.