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 - 1This 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 - 3Just 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 - 1Constructors
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 - 1add_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 - 1add_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
1Coefficients
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.0Quadratic 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 - 1Operator 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 - 1Constructors
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 - 1add_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 - 1Read 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 + 1Coefficients
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.0Nonlinear 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)
1However, 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.1Notice 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.
Note that this behaviour is not JuMP-specific. For example, zeros(BigInt, 2) will create two copies of the same instance of BigInt:
julia> x = zeros(BigInt, 2)
2-element Vector{BigInt}:
0
0
julia> x[1] === x[2]
true
julia> objectid(x[1]) == objectid(x[2])
trueThis also happens with broadcasting:
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.1The recommended way to create an array of empty expressions is as follows:
julia> x = [zero(AffExpr) for _ in 1: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
0Alternatively, 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
0Note that for large expressions this will be slower due to the allocation of additional temporary objects.