Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/src/apimanual.md
Original file line number Diff line number Diff line change
Expand Up @@ -919,9 +919,9 @@ MOI.set(model, MyPackage.PrintLevel(), 0)
### Implementing copy

Avoid storing extra copies of the problem when possible. This means that solver
wrappers should not use `CachingOptimizer` as part of the wrapper. Instead, do
one of the following to load the problem (assuming the solver wrapper type is
called `Optimizer`):
wrappers should not use [`Utilities.CachingOptimizer`](@ref) as part of the wrapper.
Instead, do one of the following to load the problem (assuming the solver
wrapper type is called `Optimizer`):

* If the solver supports loading the problem incrementally, implement
[`add_variable`](@ref), [`add_constraint`](@ref) for supported constraints and
Expand Down
52 changes: 52 additions & 0 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,58 @@ Utilities.load
Utilities.load_constraint
```

### Caching optimizer

Some solvers do not support incremental definition of optimization
models. Nevertheless, you are still able to build incrementally an optimization
model with such solvers. MathOptInterface provides a utility,
[`Utilities.CachingOptimizer`](@ref), that will store in a [`ModelLike`](@ref)
the optimization model during its incremental definition. Once the
model is completely defined, the `CachingOptimizer` specifies all problem
information to the underlying solver, all at once.

The function [`Utilities.state`](@ref) allows to query the state
of the optimizer cached inside a `CachingOptimizer`. The state
could be:
* `NO_OPTIMIZER`, if no optimizer is attached;
* `EMPTY_OPTIMIZER`, if the attached optimizer is empty;
* `ATTACHED_OPTIMIZER`, if the attached optimizer is synchronized with the
cached model defined in `CachingOptimizer`.

The following methods modify the state of the attached optimizer:
* [`Utilities.attach_optimizer`](@ref) attachs a new `optimizer`
to a `cached_optimizer` with state `EMPTY_OPTIMIZER`.
The state of `cached_optimizer` is set to `ATTACHED_OPTIMIZER` after the call.
* [`Utilities.drop_optimizer`](@ref) drops the underlying `optimizer`
from `cached_optimizer`, without emptying it. The state of `cached_optimizer`
is set to `NO_OPTIMIZER` after the call.
* [`Utilities.reset_optimizer`](@ref) empties `optimizer` inside
`cached_optimizer`, without droping it. The state of `cached_optimizer`
is set to `EMPTY_OPTIMIZER` after the call.

The way to operate a `CachingOptimizer` depends whether the mode
is set to `AUTOMATIC` or to `MANUAL`.
* In `MANUAL` mode, the state of the `CachingOptimizer` changes only
if the methods [`Utilities.attach_optimizer`](@ref),
[`Utilities.reset_optimizer`](@ref) or [`Utilities.drop_optimizer`](@ref)
are being called. Any unattended operation results in an error.
* In `AUTOMATIC` mode, the state of the `CachingOptimizer` changes when
necessary. Any modification not supported by the solver (e.g. dropping
a constraint) results in a drop to the state `EMPTY_OPTIMIZER`.

When calling [`Utilities.attach_optimizer`](@ref), the `CachingOptimizer` copies
the cached model to the optimizer with [`MathOptInterface.copy_to`](@ref).
We refer to [Implementing copy](@ref) for more details.

```@docs
Utilities.CachingOptimizer
Utilities.attach_optimizer
Utilities.reset_optimizer
Utilities.drop_optimizer
Utilities.state
Utilities.mode
```

## Benchmarks

Functions to help benchmark the performance of solver wrappers. See
Expand Down
1 change: 0 additions & 1 deletion src/Benchmarks/Benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ Use `exclude` to exclude a subset of benchmarks.
suite() do
GLPK.Optimizer()
end

suite(exclude = [r"delete"]) do
Gurobi.Optimizer(OutputFlag=0)
end
Expand Down
31 changes: 21 additions & 10 deletions src/Utilities/cachingoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,22 @@ construction and modification even when the optimizer doesn't.

A `CachingOptimizer` may be in one of three possible states (`CachingOptimizerState`):

- `NO_OPTIMIZER`: The CachingOptimizer does not have any optimizer.
- `EMPTY_OPTIMIZER`: The CachingOptimizer an empty optimizer. The optimizer is not synchronized with the cached model.
- `ATTACHED_OPTIMIZER`: The CachingOptimizer a optimizer, and it is synchronized with the cached model.
* `NO_OPTIMIZER`: The CachingOptimizer does not have any optimizer.
* `EMPTY_OPTIMIZER`: The CachingOptimizer has an empty optimizer.
The optimizer 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 (`CachingOptimizerMode`):

- `MANUAL`: The only methods that change the state of the `CachingOptimizer` are [`reset_optimizer`](@ref), [`drop_optimizer`](@ref), and [`attach_optimizer`](@ref). Attempting to perform an operation in the incorrect state results in an error.
- `AUTOMATIC`: The `CachingOptimizer` changes its state when necessary. For example, `optimize!` 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 only methods that change the state of the `CachingOptimizer`
are [`Utilities.reset_optimizer`](@ref), [`Utilities.drop_optimizer`](@ref),
and [`Utilities.attach_optimizer`](@ref).
Attempting to perform an operation in the incorrect state results in an error.
* `AUTOMATIC`: The `CachingOptimizer` changes its state when necessary. For
example, `optimize!` 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.
"""
mutable struct CachingOptimizer{OptimizerType, ModelType<:MOI.ModelLike} <: MOI.AbstractOptimizer
optimizer::Union{Nothing, OptimizerType}
Expand All @@ -41,7 +49,7 @@ end
CachingOptimizer(model_cache::MOI.ModelLike, optimizer::AbstractOptimizer)

Creates an `CachingOptimizer` in `AUTOMATIC` mode, with the optimizer `optimizer`.
The model_cache manager returned behaves like an `AbstractOptimizer` as long as no
The `model_cache` manager returned behaves like an `AbstractOptimizer` as long as no
`CachingOptimizer`-specific functions (e.g. `reset_optimizer`) are called on it.
The type of the optimizer returned is `CachingOptimizer{typeof(optimizer),
typeof(model_cache)}` so it does not support the function
Expand All @@ -59,14 +67,14 @@ end
"""
state(m::CachingOptimizer)::CachingOptimizerState

Returns the state of the CachingOptimizer `m`. See [`CachingOptimizer`](@ref).
Returns the state of the CachingOptimizer `m`. See [`Utilities.CachingOptimizer`](@ref).
"""
state(m::CachingOptimizer) = m.state

"""
mode(m::CachingOptimizer)::CachingOptimizerMode

Returns the operating mode of the CachingOptimizer `m`. See [`CachingOptimizer`](@ref).
Returns the operating mode of the CachingOptimizer `m`. See [`Utilities.CachingOptimizer`](@ref).
"""
mode(m::CachingOptimizer) = m.mode

Expand Down Expand Up @@ -121,7 +129,7 @@ end
Attaches the optimizer to `model`, copying all model data into it. Can be called
only from the `EMPTY_OPTIMIZER` state. If the copy succeeds, the
`CachingOptimizer` will be in state `ATTACHED_OPTIMIZER` after the call,
otherwise an error is thrown; see [`copy_to`](@ref) for more details on which
otherwise an error is thrown; see [`MathOptInterface.copy_to`](@ref) for more details on which
errors can be thrown.
"""
function attach_optimizer(model::CachingOptimizer)
Expand Down Expand Up @@ -387,7 +395,10 @@ function MOI.set(m::CachingOptimizer, attr::MOI.AbstractModelAttribute, value)
MOI.set(m.model_cache, attr, value)
end

function MOI.set(m::CachingOptimizer, attr::Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute}, index::MOI.Index, value)
function MOI.set(m::CachingOptimizer,
attr::Union{MOI.AbstractVariableAttribute,
MOI.AbstractConstraintAttribute},
index::MOI.Index, value)
if m.state == ATTACHED_OPTIMIZER
optimizer_index = m.model_to_optimizer_map[index]
optimizer_value = attribute_value_map(m.model_to_optimizer_map, value)
Expand Down