Skip to content
Permalink
Browse files

Expression docs (#1755)

* Begin expression docs

* Fix doc build

* Add more docs

* Fixes

* Address mlubin's comments

* Update nonlinear text
  • Loading branch information...
odow committed Jan 10, 2019
1 parent 5577c08 commit b49a5878181fa1f15971dffe824b74bf4dbdca6b
Showing with 248 additions and 21 deletions.
  1. +189 −15 docs/src/expressions.md
  2. +9 −1 docs/src/nlp.md
  3. +6 −0 docs/src/objective.md
  4. +44 −5 src/macros.jl
@@ -4,30 +4,204 @@ DocTestSetup = quote
end
```

Expressions
===========
# Expressions

DRAFT: JuMP has multiple types of expressions: affine, quadratic, and nonlinear.
Just talk about affine and quadratic here (see other section for nonlinear).
Describe the basic data structures and ways to construct expressions
(these are: constructors, operators, `add_to_expression!`, and macros).
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

Example code with doc tests:
There are four ways of constructing an affine expression in JuMP: using the
[`@expression`](@ref) macro, by operator overloading, using the `AffExpr`
constructor, and via [`JuMP.add_to_expression!`](@ref).

### Macros

The recommended way to create an affine expression is via the
[`@expression`](@ref) macro.

```jldoctest affine_macro
model = Model()
@variable(model, x)
@variable(model, y)
ex = @expression(model, 2x + y - 1)
# output
2 x + y - 1
```

This expression can be used in the objective, or added to a constraint. For
example:
```jldoctest affine_macro
@objective(model, Min, 2 * ex - 1)
JuMP.objective_function(model)
# output
4 x + 2 y - 3
```

Just like variables and constraints, named expressions can also be created. For
example
```jldoctest
model = Model()
@variable(model, x[i = 1:3])
@expression(model, expr[i=1:3], i * sum(x[j] for j in i:3))
expr
# output
3-element Array{JuMP.GenericAffExpr{Float64,VariableRef},1}:
x[1] + x[2] + x[3]
2 x[2] + 2 x[3]
3 x[3]
```

### Operator overloading

Expressions can also be created outside the macro. However, note that in some
cases, this can be much slower that constructing an expression using the macro.

```jldoctest
model = Model()
@variable(model, x)
@variable(model, y)
ex = 2x + y - 1
# output
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.

```jldoctest
model = Model()
@variable(model, x)
@variable(model, y)
ex = AffExpr(-1.0, x => 2.0, y => 1.0)
# output
2 x + y - 1
```

### `JuMP.add_to_expression!`

The fourth way to create an affine expression is by using
[`JuMP.add_to_expression!`](@ref). Compared to the operator overloading method,
this approach is fast, and is what the [`@expression`](@ref) macro implements
behind-the-scenes.
```jldoctest
m = Model()
@variable(m, x)
@variable(m, y)
AffExpr(-1.0, x => 2.0, y => 1.0)
model = Model()
@variable(model, x)
@variable(model, y)
ex = JuMP.AffExpr(-1.0)
JuMP.add_to_expression!(ex, 2.0, x)
JuMP.add_to_expression!(ex, 1.0, y)
# output
2 x + y - 1
```

Objective functions
-------------------
## Quadratic expressions

TODO: Describe how JuMP expressions relate to MOI functions. How to set, query,
and modify an objective function.
Like affine expressions, there are four ways of constructing a quadratic
expression in JuMP: using macros, operator overloading, constructors, and via
[`JuMP.add_to_expression!`](@ref).

### Macros

The [`@expression`](@ref) macro can be used to create quadratic expressions by
including quadratic terms.

```jldoctest
model = Model()
@variable(model, x)
@variable(model, y)
ex = @expression(model, x^2 + 2 * x * y + y^2 + x + y - 1)
# output
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.

```jldoctest
model = Model()
@variable(model, x)
@variable(model, y)
ex = x^2 + 2 * x * y + y^2 + x + y - 1
# output
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.

```jldoctest
model = Model()
@variable(model, x)
@variable(model, y)
aff_expr = AffExpr(-1.0, x => 1.0, y => 1.0)
quad_expr = JuMP.QuadExpr(aff_expr,
JuMP.UnorderedPair(x, x) => 1.0,
JuMP.UnorderedPair(x, y) => 2.0,
JuMP.UnorderedPair(y, y) => 1.0
)
# output
x² + 2 x*y + y² + x + y - 1
```

### `JuMP.add_to_expression!`

Finally, [`JuMP.add_to_expression!`](@ref) can also be used to add quadratic
terms.

```jldoctest
model = Model()
@variable(model, x)
@variable(model, y)
ex = JuMP.QuadExpr(x + y - 1.0)
JuMP.add_to_expression!(ex, 1.0, x, x)
JuMP.add_to_expression!(ex, 2.0, x, y)
JuMP.add_to_expression!(ex, 1.0, y, y)
# output
x² + 2 x*y + y² + x + y - 1
```

## Nonlinear expressions

Nonlinear expressions can be constructed only using the [`@NLexpression`](@ref)
macro, and nonlinear expressions can be used only in [`@NLobjective`](@ref),
[`@NLconstraint`](@ref), and other [`@NLexpression`](@ref)s. Moreover, quadratic
and affine expressions cannot be used in the nonlinear macros. For more details,
see the [Nonlinear Modeling](@ref) section.

## Reference

```@docs
@expression
JuMP.add_to_expression!
```
@@ -404,7 +404,7 @@ of specific variables. For example, in statistical maximum likelihood estimation
problems, one is often interested in the Hessian matrix at the optimal solution,
which can be queried using the `JuMP.NLPEvaluator`.

# Raw expression input
## Raw expression input

In addition to the `@NLobjective` and `@NLconstraint` macros, it is also
possible to provide Julia `Expr` objects directly by using
@@ -425,4 +425,12 @@ JuMP.add_NL_constraint(model, :($(x[1])*$(x[2])*$(x[3])*$(x[4]) >= 25))
See the Julia documentation for more examples and description of Julia
expressions.

## Reference

```@docs
@NLconstraint
@NLexpression
@NLobjective
```

[^1]: Dunning, Huchette, and Lubin, "JuMP: A Modeling Language for Mathematical Optimization", [arXiv](http://arxiv.org/abs/1508.01982).
@@ -3,6 +3,12 @@ Objective

TODO: Describe how the objective is represented (link to MOI docs)

Objective functions
-------------------

TODO: Describe how JuMP expressions relate to MOI functions. How to set, query,
and modify an objective function.

Setting the objective function and objective sense:
```@docs
@objective
@@ -932,15 +932,18 @@ end
"""
@expression(args...)
efficiently builds a linear, quadratic, or second-order cone expression but does not add to model immediately. Instead, returns the expression which can then be inserted in other constraints. For example:
Efficiently builds a linear or quadratic expression but does not add to model
immediately. Instead, returns the expression which can then be inserted in other
constraints. For example:
```julia
@expression(m, shared, sum(i*x[i] for i=1:5))
@constraint(m, shared + y >= 5)
@constraint(m, shared + z <= 10)
```
The `ref` accepts index sets in the same way as `@variable`, and those indices can be used in the construction of the expressions:
The `ref` accepts index sets in the same way as `@variable`, and those indices
can be used in the construction of the expressions:
```julia
@expression(m, expr[i=1:3], i*sum(x[j] for j=1:3))
@@ -1386,7 +1389,16 @@ macro variable(args...)
return assert_validmodel(model, macro_code)
end

# TODO: Add a docstring.
"""
@NLobjective(model, sense, expression)
Add a nonlinear objective to `model` with optimization sense `sense`.
`sense` must be `Max` or `Min`.
# Example
@NLobjective(model, Max, 2x + 1 + sin(x))
"""
macro NLobjective(model, sense, x)
_error(str...) = macro_error(:NLobjective, (model, sense, x), str...)
sense_expr = moi_sense(_error, sense)
@@ -1398,7 +1410,17 @@ macro NLobjective(model, sense, x)
return assert_validmodel(esc(model), macro_return(code, nothing))
end

# TODO: Add a docstring.
"""
@NLconstraint(m::Model, expr)
Add a constraint described by the nonlinear expression `expr`. See also
[`@constraint`](@ref). For example:
```julia
@NLconstraint(model, sin(x) <= 1)
@NLconstraint(model, [i = 1:3], sin(i * x) <= 1 / i)
```
"""
macro NLconstraint(m, x, extra...)
esc_m = esc(m)
# Two formats:
@@ -1476,7 +1498,24 @@ macro NLconstraint(m, x, extra...)
return assert_validmodel(esc_m, macro_code)
end

# TODO: Add a docstring.
"""
@NLexpression(args...)
Efficiently build a nonlinear expression which can then be inserted in other
nonlinear constraints and the objective. See also [`@expression`]. For example:
```julia
@NLexpression(model, my_expr, sin(x)^2 + cos(x^2))
@NLconstraint(model, my_expr + y >= 5)
@NLobjective(model, Min, my_expr)
```
Indexing over sets and anonymous expressions are also supported:
```julia
@NLexpression(m, my_expr_1[i=1:3], sin(i * x))
my_expr_2 = @NLexpression(m, log(1 + sum(exp(x[i])) for i in 1:2))
```
"""
macro NLexpression(args...)
args, kw_args, requestedcontainer = extract_kw_args(args)
if length(args) <= 1

0 comments on commit b49a587

Please sign in to comment.
You can’t perform that action at this time.