Expressions
JuMP has three types of expressions: affine, quadratic, and nonlinear. These expressions can be inserted into constraints or into the objective. This is particularly useful if an expression is used in multiple places in the model.
Affine expressions
There are four ways of constructing an affine expression in JuMP: with the @expression
macro, with operator overloading, with the AffExpr
constructor, and with add_to_expression!
.
Macros
The recommended way to create an affine expression is via the @expression
macro.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = @expression(model, 2x + y - 1)
2 x + y - 1
This expression can be used in the objective or added to a constraint. For example:
julia> @objective(model, Min, 2 * ex - 1)
4 x + 2 y - 3
julia> objective_function(model)
4 x + 2 y - 3
Just like variables and constraints, named expressions can also be created. For example
julia> model = Model();
julia> @variable(model, x[i = 1:3]);
julia> @expression(model, expr[i = 1:3], i * sum(x[j] for j in i:3));
julia> expr
3-element Vector{AffExpr}:
x[1] + x[2] + x[3]
2 x[2] + 2 x[3]
3 x[3]
You can read more about containers in the Containers section.
Operator overloading
Expressions can also be created without macros. However, note that in some cases, this can be much slower that constructing an expression using macros.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = 2x + y - 1
2 x + y - 1
Constructors
A third way to create an affine expression is by the AffExpr
constructor. The first argument is the constant term, and the remaining arguments are variable-coefficient pairs.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = AffExpr(-1.0, x => 2.0, y => 1.0)
2 x + y - 1
add_to_expression!
The fourth way to create an affine expression is by using add_to_expression!
. Compared to the operator overloading method, this approach is faster because it avoids constructing temporary objects. The @expression
macro uses add_to_expression!
behind-the-scenes.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = AffExpr(-1.0)
-1
julia> add_to_expression!(ex, 2.0, x)
2 x - 1
julia> add_to_expression!(ex, 1.0, y)
2 x + y - 1
add_to_expression!
can also be used to sum expressions in-place:
julia> model = Model();
julia> @variable(model, x[1:2])
2-element Vector{VariableRef}:
x[1]
x[2]
julia> @expression(model, ex1, sum(x))
x[1] + x[2]
julia> @expression(model, ex2, 2 * sum(x))
2 x[1] + 2 x[2]
julia> add_to_expression!(ex1, ex2)
3 x[1] + 3 x[2]
julia> ex1
3 x[1] + 3 x[2]
julia> ex2
2 x[1] + 2 x[2]
Read the section Initializing arrays for some cases to be careful about when using add_to_expression!
.
Removing zero terms
Use drop_zeros!
to remove terms from an affine expression with a 0
coefficient.
julia> model = Model();
julia> @variable(model, x)
x
julia> @expression(model, ex, x + 1 - x)
0 x + 1
julia> drop_zeros!(ex)
julia> ex
1
Coefficients
Use coefficient
to return the coefficient associated with a variable in an affine expression.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> @expression(model, ex, 2x + 1)
2 x + 1
julia> coefficient(ex, x)
2.0
julia> coefficient(ex, y)
0.0
Quadratic expressions
Like affine expressions, there are four ways of constructing a quadratic expression in JuMP: macros, operator overloading, constructors, and add_to_expression!
.
Macros
The @expression
macro can be used to create quadratic expressions by including quadratic terms.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = @expression(model, x^2 + 2 * x * y + y^2 + x + y - 1)
x² + 2 x*y + y² + x + y - 1
Operator overloading
Operator overloading can also be used to create quadratic expressions. The same performance warning (discussed in the affine expression section) applies.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = x^2 + 2 * x * y + y^2 + x + y - 1
x² + 2 x*y + y² + x + y - 1
Constructors
Quadratic expressions can also be created using the QuadExpr
constructor. The first argument is an affine expression, and the remaining arguments are pairs, where the first term is a JuMP.UnorderedPair
and the second term is the coefficient.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> aff_expr = AffExpr(-1.0, x => 1.0, y => 1.0)
x + y - 1
julia> quad_expr = QuadExpr(
aff_expr,
UnorderedPair(x, x) => 1.0,
UnorderedPair(x, y) => 2.0,
UnorderedPair(y, y) => 1.0,
)
x² + 2 x*y + y² + x + y - 1
add_to_expression!
Finally, add_to_expression!
can also be used to add quadratic terms.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> ex = QuadExpr(x + y - 1.0)
x + y - 1
julia> add_to_expression!(ex, 1.0, x, x)
x² + x + y - 1
julia> add_to_expression!(ex, 2.0, x, y)
x² + 2 x*y + x + y - 1
julia> add_to_expression!(ex, 1.0, y, y)
x² + 2 x*y + y² + x + y - 1
Read the section Initializing arrays for some cases to be careful about when using add_to_expression!
.
Removing zero terms
Use drop_zeros!
to remove terms from a quadratic expression with a 0
coefficient.
julia> model = Model();
julia> @variable(model, x)
x
julia> @expression(model, ex, x^2 + x + 1 - x^2)
0 x² + x + 1
julia> drop_zeros!(ex)
julia> ex
x + 1
Coefficients
Use coefficient
to return the coefficient associated with a pair of variables in a quadratic expression.
julia> model = Model();
julia> @variable(model, x)
x
julia> @variable(model, y)
y
julia> @expression(model, ex, 2*x*y + 3*x)
2 x*y + 3 x
julia> coefficient(ex, x, y)
2.0
julia> coefficient(ex, x, x)
0.0
julia> coefficient(ex, y, x)
2.0
julia> coefficient(ex, x)
3.0
Nonlinear expressions
Nonlinear expressions in JuMP are represented by a NonlinearExpr
object. See Nonlinear expressions in detail for more details.
Initializing arrays
JuMP implements zero(AffExpr)
and one(AffExpr)
to support various functions in LinearAlgebra
(for example, accessing the off-diagonal of a Diagonal
matrix).
julia> zero(AffExpr)
0
julia> one(AffExpr)
1
However, this can result in a subtle bug if you call add_to_expression!
or the MutableArithmetics API on an element created by zeros
or ones
:
julia> x = zeros(AffExpr, 2)
2-element Vector{AffExpr}:
0
0
julia> add_to_expression!(x[1], 1.1)
1.1
julia> x
2-element Vector{AffExpr}:
1.1
1.1
Notice how we modified x[1]
, but we also changed x[2]
!
This happened because zeros(AffExpr, 2)
calls zero(AffExpr)
once to obtain a zero element, and then creates an appropriately sized array filled with the same element.
This also happens with broadcasting calls containing a conversion of 0
or 1
:
julia> x = Vector{AffExpr}(undef, 2)
2-element Vector{AffExpr}:
#undef
#undef
julia> x .= 0
2-element Vector{AffExpr}:
0
0
julia> add_to_expression!(x[1], 1.1)
1.1
julia> x
2-element Vector{AffExpr}:
1.1
1.1
The recommended way to create an array of empty expressions is as follows:
julia> x = Vector{AffExpr}(undef, 2)
2-element Vector{AffExpr}:
#undef
#undef
julia> for i in eachindex(x)
x[i] = AffExpr(0.0)
end
julia> add_to_expression!(x[1], 1.1)
1.1
julia> x
2-element Vector{AffExpr}:
1.1
0
Alternatively, use non-mutating operation to avoid updating x[1]
in-place:
julia> x = zeros(AffExpr, 2)
2-element Vector{AffExpr}:
0
0
julia> x[1] += 1.1
1.1
julia> x
2-element Vector{AffExpr}:
1.1
0
Note that for large expressions this will be slower due to the allocation of additional temporary objects.