Skip to content

Commit

Permalink
Rewrite the Model documentation (#2478)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Feb 17, 2021
1 parent 10fa1f7 commit ade0f22
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 114 deletions.
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ makedocs(
"Introduction" => "index.md",
"installation.md",
"Manual" => [
"manual/models.md",
"manual/variables.md",
"manual/expressions.md",
"manual/objective.md",
"manual/constraints.md",
"manual/containers.md",
"manual/solvers.md",
"manual/solutions.md",
"manual/nlp.md",
"manual/callbacks.md",
Expand Down
10 changes: 5 additions & 5 deletions docs/src/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ Where:
file.

!!! note
Developing a solver or solver wrapper? See [Interacting with solvers](@ref)
and the [MathOptInterface docs](https://jump.dev/MathOptInterface.jl/stable/)
for more details on how JuMP interacts with solvers. Please get in touch
via the [Developer Chatroom](https://jump.dev/pages/governance/#developer-chatroom)
Developing a solver or solver wrapper? See [Models](@ref) and the
[MathOptInterface docs](https://jump.dev/MathOptInterface.jl/stable/) for
more details on how JuMP interacts with solvers. Please get in touch via the
[Developer Chatroom](https://jump.dev/pages/governance/#developer-chatroom)
with any questions about connecting new solvers with JuMP.

### Solver-specific notes
Expand Down Expand Up @@ -264,7 +264,7 @@ if you have interest in reviving a previously supported solver.

### Check the version of your packages

Each package is versioned with a [three-part number](https://semver.org) of the
Each package is versioned with a [three-part number](https://semver.org) of the
form `vX.Y.Z`. You can check which versions you have installed with
`import Pkg; Pkg.status()`.

Expand Down
338 changes: 338 additions & 0 deletions docs/src/manual/models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
```@meta
CurrentModule = JuMP
DocTestSetup = quote
using JuMP, GLPK
end
DocTestFilters = [r"≤|<=", r"≥|>=", r" == | = ", r" ∈ | in ", r"MathOptInterface|MOI"]
```

# Models

## Create a model

Create a model by passing an optimizer to [`Model`](@ref):
```jldoctest
julia> model = Model(GLPK.Optimizer)
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: GLPK
```
or by calling [`set_optimizer`](@ref) on an empty [`Model`](@ref):
```jldoctest
julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.
julia> set_optimizer(model, GLPK.Optimizer)
```

!!! info
JuMP uses "optimizer" as a synonym for "solver." Our convention is to use
"solver" to refer to the underlying software, and use "optimizer" to refer
to the Julia object that wraps the solver. For example, `GLPK` is a solver,
and `GLPK.Optimizer` is an optimizer.

!!! tip
Don't know what the fields `Model mode`, `CachingOptimizer state` mean? Read
the [Backends](@ref) section.

Use [`optimizer_with_attributes`](@ref) to create an optimizer with some
attributes initialized:
```jldoctest
julia> model = Model(optimizer_with_attributes(GLPK.Optimizer, "msg_lev" => 0))
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: GLPK
```

Alternatively, you can create a function which takes no arguments and returns
an initialized `Optimizer` object:
```jldoctest
julia> function my_optimizer()
model = GLPK.Optimizer()
MOI.set(model, MOI.RawParameter("msg_lev"), 0)
return model
end
my_optimizer (generic function with 1 method)
julia> model = Model(my_optimizer)
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: GLPK
```

## Turn off output

Use [`set_silent`](@ref) and [`unset_silent`](@ref) to disable or enable
printing output from the solver.
```jldoctest
julia> model = Model(GLPK.Optimizer);
julia> set_silent(model)
true
julia> unset_silent(model)
false
```

## Set a time limit

Use [`set_time_limit_sec`](@ref), [`unset_time_limit_sec`](@ref), and
[`time_limit_sec`](@ref) to manage time limits.
```jldoctest
julia> model = Model(GLPK.Optimizer);
julia> set_time_limit_sec(model, 60.0)
60.0
julia> time_limit_sec(model)
60.0
julia> unset_time_limit_sec(model)
julia> time_limit_sec(model)
2.147483647e6
```

## Write a model to file

JuMP can write models to a variety of file-formats using [`write_to_file`](@ref)
and [`Base.write`](@ref).

```jldoctest file_formats; setup=:(model = Model(); io = IOBuffer())
julia> write_to_file(model, "model.mps")
julia> write(io, model; format = MOI.FileFormats.FORMAT_MPS)
```

!!! info
The supported file formats are defined by the [MOI.FileFormats.FileFormat](https://jump.dev/MathOptInterface.jl/v0.9/apireference/#MathOptInterface.FileFormats.FileFormat)
enum.
```jldoctest
julia> MOI.FileFormats.FileFormat
Enum MathOptInterface.FileFormats.FileFormat:
FORMAT_AUTOMATIC = 0
FORMAT_CBF = 1
FORMAT_LP = 2
FORMAT_MOF = 3
FORMAT_MPS = 4
FORMAT_SDPA = 5
```

## Read a model from file

JuMP models can be created from file formats using [`read_from_file`](@ref) and
[`Base.read`](@ref).

```jldoctest file_formats
julia> model = read_from_file("model.mps")
A JuMP Model
Minimization problem with:
Variables: 0
Objective function type: GenericAffExpr{Float64,VariableRef}
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.
julia> seekstart(io);
julia> model2 = read(io, Model; format = MOI.FileFormats.FORMAT_MPS)
A JuMP Model
Minimization problem with:
Variables: 0
Objective function type: GenericAffExpr{Float64,VariableRef}
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.
```

## Backends

A JuMP [`Model`](@ref) is a thin layer around a *backend* of type [`MOI.ModelLike`](https://jump.dev/MathOptInterface.jl/v0.9/apireference/#Model-Interface)
that stores the optimization problem and acts as the optimization solver.

From JuMP, the MOI backend can be accessed using the [`backend`](@ref) function.
Let's see what the [`backend`](@ref) of a JuMP [`Model`](@ref) is:
```jldoctest models_backends
julia> model = Model(GLPK.Optimizer)
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: GLPK
julia> b = backend(model)
MOIU.CachingOptimizer{MOI.AbstractOptimizer,MOIU.UniversalFallback{MOIU.Model{Float64}}}
in state EMPTY_OPTIMIZER
in mode AUTOMATIC
with model cache MOIU.UniversalFallback{MOIU.Model{Float64}}
fallback for MOIU.Model{Float64}
with optimizer MOIB.LazyBridgeOptimizer{GLPK.Optimizer}
with 0 variable bridges
with 0 constraint bridges
with 0 objective bridges
with inner model A GLPK model
```

The backend is a `MOIU.CachingOptimizer` in the state `EMPTY_OPTIMIZER` and mode
`AUTOMATIC`.

### CachingOptimizer

A `MOIU.CachingOptimizer` is an MOI layer that abstracts the difference between
solvers that support incremental modification (e.g., they support adding
variables one-by-one), and solvers that require the entire problem in a single
API call (e.g., they only accept the `A`, `b` and `c` matrices of a linear
program).

It has two parts:

1. A cache, where the model can be built and modified incrementally
```jldoctest models_backends
julia> b.model_cache
MOIU.UniversalFallback{MOIU.Model{Float64}}
fallback for MOIU.Model{Float64}
```
2. An optimizer, which is used to solve the problem
```jldoctest models_backends
julia> b.optimizer
MOIB.LazyBridgeOptimizer{GLPK.Optimizer}
with 0 variable bridges
with 0 constraint bridges
with 0 objective bridges
with inner model A GLPK model
```
!!! info
The [LazyBridgeOptimizer](@ref) section explains what a
`LazyBridgeOptimizer` is.
The `CachingOptimizer` has logic to decide when to copy the problem from the
cache to the optimizer, and when it can efficiently update the optimizer
in-place.
A `CachingOptimizer` may be in one of three possible states:
* `NO_OPTIMIZER`: The CachingOptimizer does not have any optimizer.
* `EMPTY_OPTIMIZER`: The CachingOptimizer has an empty optimizer, and it is not
synchronized with the cached model.
* `ATTACHED_OPTIMIZER`: The CachingOptimizer has an optimizer, and it is
synchronized with the cached model.
A `CachingOptimizer` has two modes of operation:
* `AUTOMATIC`: The `CachingOptimizer` changes its state when necessary. For
example, [`optimize!`](@ref) will automatically call `attach_optimizer` (an
optimizer must have been previously set). Attempting to add a constraint or
perform a modification not supported by the optimizer results in a drop to
`EMPTY_OPTIMIZER` mode.
* `MANUAL`: The user must change the state of the `CachingOptimizer` using
[`MOIU.reset_optimizer(::JuMP.Model)`](@ref),
[`MOIU.drop_optimizer(::JuMP.Model)`](@ref), and
[`MOIU.attach_optimizer(::JuMP.Model)`](@ref). Attempting to perform
an operation in the incorrect state results in an error.
By default [`Model`](@ref) will create a `CachingOptimizer` in `AUTOMATIC` mode.
Use the `caching_mode` keyword to create a model in `MANUAL` mode:
```jldoctest
julia> Model(GLPK.Optimizer; caching_mode = MOI.Utilities.MANUAL)
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: MANUAL
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: GLPK
```

!!! tip
Only use `MANUAL` mode if you have a very good reason. If you want to reduce
the overhead between JuMP and the underlying solver, consider
[Direct mode](@ref) instead.

### LazyBridgeOptimizer

The second layer that JuMP applies automatically is a `LazyBridgeOptimizer`. A
`LazyBridgeOptimizer` is an MOI layer that attempts to transform constraints
added by the user into constraints supported by the solver. This may involve
adding new variables and constraints to the optimizer. The transformations are
selected from a set of known recipes called _bridges_.

A common example of a bridge is one that splits an interval constrait like
`@constraint(model, 1 <= x + y <= 2)` into two constraints,
`@constraint(model, x + y >= 1)` and `@constraint(model, x + y <= 2)`.

Use the `bridge_constraints=false` keyword to remove the bridging layer:
```jldoctest
julia> model = Model(GLPK.Optimizer; bridge_constraints = false)
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: GLPK
julia> backend(model)
MOIU.CachingOptimizer{MOI.AbstractOptimizer,MOIU.UniversalFallback{MOIU.Model{Float64}}}
in state EMPTY_OPTIMIZER
in mode AUTOMATIC
with model cache MOIU.UniversalFallback{MOIU.Model{Float64}}
fallback for MOIU.Model{Float64}
with optimizer A GLPK model
```

!!! tip
Only disable bridges if you have a very good reason. If you want to reduce
the overhead between JuMP and the underlying solver, consider
[Direct mode](@ref) instead.

## Direct mode

Using a `CachingOptimizer` results in an additional copy of the model being
stored by JuMP in the `.model_cache` field. To avoid this overhead, create a
JuMP model using [`direct_model`](@ref):
```jldoctest direct_mode
julia> model = direct_model(GLPK.Optimizer())
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: DIRECT
Solver name: GLPK
```

!!! warning
Solvers that do not support incremental modification do not support
`direct_model`. An error will be thrown, telling you to use a
`CachingOptimizer` instead.

The benefit of using [`direct_model`](@ref) is that there are no extra layers
(e.g., `Cachingoptimizer` or `LazyBridgeOptimizer`) between `model` and the
provided optimizer:
```jldoctest direct_mode
julia> typeof(backend(model))
GLPK.Optimizer
```

A downside of direct mode is that there is no bridging layer. Therefore, only
constraints which are natively supported by the solver are supported. For
example, `GLPK.jl` does not implement constraints of the form `l <= a' x <= u`.
```julia direct_mode
julia> @variable(model, x[1:2]);

julia> @constraint(model, 1 <= x[1] + x[2] <= 2)
ERROR: Constraints of type MathOptInterface.ScalarAffineFunction{Float64}-in-MathOptInterface.Interval{Float64} are not supported by the solver.
[...]
```
Loading

0 comments on commit ade0f22

Please sign in to comment.