# Interacting with solvers

A JuMP model keeps a MathOptInterface (MOI) *backend* of type `MOI.ModelLike`

that stores the optimization problem and acts as the optimization solver. We call it an MOI *backend* and not optimizer as it can also be a wrapper around an optimization file format such as MPS that writes the JuMP model in a file. From JuMP, the MOI backend can be accessed using the `backend`

function. JuMP can be viewed as a lightweight, user-friendly layer on top of the MOI backend, in the sense that:

- JuMP does not maintain any copy of the model outside this MOI backend.
- JuMP variable (resp. constraint) references are simple structures containing both a reference to the JuMP model and the MOI index of the variable (resp. constraint).
- JuMP gives the constraints to the MOI backend in the form provided by the user without doing any automatic reformulation.
- variables additions, constraints additions/modifications and objective modifications are directly applied to the MOI backend thus expecting the backend to support such modifications.

While this allows JuMP to be a thin wrapper on top of the solver API, as mentioned in the last point above, this seems rather demanding on the solver. Indeed, while some solvers support incremental building of the model and modifications before and after solve, other solvers only support the model being copied at once before solve. Moreover, it seems to require all solvers to implement all possible reformulations independently which seems both very ambitious and might generate a lot of duplicated code.

These apparent limitations are addressed at level of MOI in a manner that is completely transparent to JuMP. While the MOI API may seem very demanding, it allows MOI models to be a succession of lightweight MOI layers that fill the gap between JuMP requirements and the solver capabilities. The remainder of this section describes how JuMP interacts with the MOI backend.

JuMP models can be created in three different modes: `AUTOMATIC`

, `MANUAL`

and `DIRECT`

.

## Automatic and Manual modes

In `AUTOMATIC`

and `MANUAL`

modes, two MOI layers are automatically applied to the optimizer:

`CachingOptimizer`

: maintains a cache of the model so that when the optimizer does not support an incremental change to the model, the optimizer's internal model can be discarded and restored from the cache just before optimization. The`CachingOptimizer`

has two different modes:`AUTOMATIC`

and`MANUAL`

corresponding to the two JuMP modes with the same names.`LazyBridgeOptimizer`

(this can be disabled using the`bridge_constraints`

keyword argument to`Model`

constructor): when a constraint added is not supported by the optimizer, it attempts to transform the constraint into an equivalent form, possibly adding new variables and constraints that are supported by the optimizer. The applied transformations are selected among known recipes which are called bridges. A few default bridges are defined in MOI but new ones can be defined and added to the`LazyBridgeOptimizer`

used by JuMP.

See the MOI documentation for more details on these two MOI layers.

To attach an optimizer to a JuMP model, JuMP needs to create a new empty optimizer instance. New optimizer instances can be obtained using an `OptimizerFactory`

that can be created using the `with_optimizer`

function:

`JuMP.with_optimizer`

— Function.`with_optimizer(constructor, args...; kwargs...)`

Return an `OptimizerFactory`

that creates optimizers using the constructor `constructor`

with positional arguments `args`

and keyword arguments `kwargs`

.

**Examples**

The following returns an optimizer factory that creates `Ipopt.Optimizer`

s using the constructor call `Ipopt.Optimizer(print_level=0)`

:

`with_optimizer(Ipopt.Optimizer, print_level=0)`

The factory can be provided either at model construction time or at `optimize!`

time:

`JuMP.NoOptimizer`

— Type.`struct NoOptimizer <: Exception end`

No optimizer is set. The optimizer can be provided at the `Model`

constructor or at the `optimize!`

call with `with_optimizer`

.

`JuMP.optimize!`

— Function.```
optimize!(model::Model,
optimizer_factory::Union{Nothing, OptimizerFactory}=nothing;
bridge_constraints::Bool=true,
ignore_optimize_hook=(model.optimize_hook === nothing),
kwargs...)
```

Optimize the model. If `optimizer_factory`

is not `nothing`

, it first sets the optimizer to a new one created using the optimizer factory. The factory can be created using the `with_optimizer`

function. If `optimizer_factory`

is `nothing`

and no optimizer was set to `model`

before calling this function, a `NoOptimizer`

error is thrown.

Keyword arguments `kwargs`

are passed to the `optimize_hook`

. An error is thrown if `optimize_hook`

is `nothing`

and keyword arguments are provided.

**Examples**

The optimizer factory can either be given in the `Model`

constructor as follows:

```
model = Model(with_optimizer(GLPK.Optimizer))
# ...fill model with variables, constraints and objectives...
# Solve the model with GLPK
optimize!(model)
```

or in the `optimize!`

call as follows:

```
model = Model()
# ...fill model with variables, constraints and objectives...
# Solve the model with GLPK
optimize!(model, with_optimizer(GLPK.Optimizer))
```

New JuMP models are created using the `Model`

constructor:

`JuMP.Model`

— Method.```
Model(; caching_mode::MOIU.CachingOptimizerMode=MOIU.AUTOMATIC,
bridge_constraints::Bool=true)
```

Return a new JuMP model without any optimizer; the model is stored the model in a cache. The mode of the `CachingOptimizer`

storing this cache is `caching_mode`

. The optimizer can be set later in the `optimize!`

call. If `bridge_constraints`

is true, constraints that are not supported by the optimizer are automatically bridged to equivalent supported constraints when an appropriate transformation is defined in the `MathOptInterface.Bridges`

module or is defined in another module and is explicitely added.

`JuMP.Model`

— Method.```
Model(optimizer_factory::OptimizerFactory;
caching_mode::MOIU.CachingOptimizerMode=MOIU.AUTOMATIC,
bridge_constraints::Bool=true)
```

Return a new JuMP model using the optimizer factory `optimizer_factory`

to create the optimizer. The optimizer factory can be created by the `with_optimizer`

function.

**Examples**

The following creates a model using the optimizer `Ipopt.Optimizer(print_level=0)`

:

`model = Model(with_optimizer(Ipopt.Optimizer, print_level=0))`

## Direct mode

JuMP models can be created in `DIRECT`

mode using the `JuMP.direct_model`

function.

`JuMP.direct_model`

— Function.`direct_model(backend::MOI.ModelLike)`

Return a new JuMP model using `backend`

to store the model and solve it. As opposed to the `Model`

constructor, no cache of the model is stored outside of `backend`

and no bridges are automatically applied to `backend`

. The absence of cache reduces the memory footprint but it is important to bear in mind the following implications of creating models using this *direct* mode:

- When
`backend`

does not support an operation, such as modifying constraints or adding variables/constraints after solving, an error is thrown. For models created using the`Model`

constructor, such situations can be dealt with by storing the modifications in a cache and loading them into the optimizer when`optimize!`

is called. - No constraint bridging is supported by default.
- The optimizer used cannot be changed the model is constructed.
- The model created cannot be copied.

`JuMP.backend`

— Function.`backend(model::Model)`

Return the lower-level MathOptInterface model that sits underneath JuMP. This model depends on which operating mode JuMP is in (manual, automatic, or direct), and whether there are any bridges in the model.

If JuMP is in direct mode (i.e., the model was created using `direct_model`

), the backend with be the optimizer passed to `direct_model`

. If JuMP is in manual or automatic mode, the backend is a `MOI.Utilities.CachingOptimizer`

.

This function should only be used by advanced users looking to access low-level MathOptInterface or solver-specific functionality.

## Solver attributes

Some solver attributes can be queried and set through JuMP models.

`JuMP.solver_name`

— Function.`solver_name(model::Model)`

If available, returns the `SolverName`

property of the underlying optimizer. Returns `"No optimizer attached"`

in `AUTOMATIC`

or `MANUAL`

modes when no optimizer is attached. Returns "SolverName() attribute not implemented by the optimizer." if the attribute is not implemented.

`JuMP.bridge_constraints`

— Function.`bridge_constraints(model::Model)`

When in direct mode, return `false`

. When in manual or automatic mode, return a `Bool`

indicating whether the optimizer is set and unsupported constraints are automatically bridged to equivalent supported constraints when an appropriate transformation is available.

`JuMP.set_silent`

— Function.`set_silent(model::Model)`

Takes precedence over any other attribute controlling verbosity and requires the solver to produce no output.

`JuMP.unset_silent`

— Function.`unset_silent(model::Model)`

Neutralize the effect of the `set_silent`

function and let the solver attributes control the verbosity.

`JuMP.set_parameter`

— Function.`set_parameter(model::Model, name, value)`

Sets solver-specific parameter identified by `name`

to `value`

.

`JuMP.set_time_limit_sec`

— Function.`set_time_limit_sec(model::Model, limit)`

Sets the time limit (in seconds) of the solver. Can be unset using `unset_time_limit_sec`

or with `limit`

set to `nothing`

.

`JuMP.unset_time_limit_sec`

— Function.`unset_time_limit_sec(model::Model)`

Unsets the time limit of the solver. Can be set using `set_time_limit_sec`

.

`JuMP.time_limit_sec`

— Function.`time_limit_sec(model::Model)`

Gets the time limit (in seconds) of the model (`nothing`

if unset). Can be set using `set_time_limit_sec`

.