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. TheCachingOptimizer
has two different modes:AUTOMATIC
andMANUAL
corresponding to the two JuMP modes with the same names.LazyBridgeOptimizer
(this can be disabled using thebridge_constraints
keyword argument toModel
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 theLazyBridgeOptimizer
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 be able to create a new empty optimizer instance. For this reason, we provide JuMP with a function that creates a new optimizer (i.e., an optimizer factory), instead of a concrete optimizer object.
The factory can be provided either at model construction time by calling set_optimizer
. An optimizer must be set before a call to optimize!
. The optimizer can be grouped with attributes to be set before optimization with optimizer_with_attributes
.
JuMP.set_optimizer
— Function.set_optimizer(model::Model, optimizer_factory;
bridge_constraints::Bool=true)
Creates an empty MathOptInterface.AbstractOptimizer
instance by calling optimizer_factory()
and sets it as the optimizer of model
. Specifically, optimizer_factory
must be callable with zero arguments and return an empty MathOptInterface.AbstractOptimizer
.
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 explicitly added.
See set_optimizer_attributes
and set_optimizer_attribute
for setting solver-specific parameters of the optimizer.
Examples
model = Model()
set_optimizer(model, GLPK.Optimizer)
JuMP.optimizer_with_attributes
— Function.optimizer_with_attributes(optimizer_constructor, attrs::Pair...)
Groups an optimizer constructor with the list of attributes attrs
. Note that it is equivalent to MOI.OptimizerWithAttributes
.
When provided to the Model
constructor or to set_optimizer
, it creates an optimizer by calling optimizer_constructor()
, and then sets the attributes using set_optimizer_attribute
.
Example
model = Model(
optimizer_with_attributes(
Gurobi.Optimizer, "Presolve" => 0, "OutputFlag" => 1
)
)
is equivalent to:
model = Model(Gurobi.Optimizer)
set_optimizer_attribute(model, "Presolve", 0)
set_optimizer_attribute(model, "OutputFlag", 1)
Note
The string names of the attributes are specific to each solver. One should consult the solver's documentation to find the attributes of interest.
See also: set_optimizer_attribute
, set_optimizer_attributes
, get_optimizer_attribute
.
JuMP.NoOptimizer
— Type.struct NoOptimizer <: Exception end
No optimizer is set. The optimizer can be provided to the Model
constructor or by calling set_optimizer
.
JuMP.optimize!
— Function.optimize!(model::Model;
ignore_optimize_hook=(model.optimize_hook === nothing),
kwargs...)
Optimize the model. If an optimizer has not been set yet (see set_optimizer
), 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.
New JuMP models are created using the Model
constructor:
JuMP.Model
— Method.Model(; caching_mode::MOIU.CachingOptimizerMode=MOIU.AUTOMATIC)
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
. Use set_optimizer
to set the optimizer before calling optimize!
.
JuMP.Model
— Method.Model(optimizer_factory;
caching_mode::MOIU.CachingOptimizerMode=MOIU.AUTOMATIC,
bridge_constraints::Bool=true)
Return a new JuMP model with the provided optimizer and bridge settings. This function is equivalent to:
model = Model()
set_optimizer(model, optimizer_factory,
bridge_constraints=bridge_constraints)
return model
See set_optimizer
for the description of the optimizer_factory
and bridge_constraints
arguments.
Examples
The following creates a model with the optimizer set to Ipopt
:
model = Model(Ipopt.Optimizer)
A JuMP model may be reused by emptying it first:
Base.empty!
— Method.empty!(model::Model) -> model
Empty the model, that is, remove all variables, constraints and model attributes but not optimizer attributes. Always return the argument.
Note: removes extensions data.
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 theModel
constructor, such situations can be dealt with by storing the modifications in a cache and loading them into the optimizer whenoptimize!
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.get_optimizer_attribute
— Function.get_optimizer_attribute(model, name::String)
Return the value associated with the solver-specific attribute named name
.
Note that this is equivalent to get_optimizer_attribute(model, MOI.RawParameter(name))
.
Example
get_optimizer_attribute(model, "SolverSpecificAttributeName")
See also: set_optimizer_attribute
, set_optimizer_attributes
.
get_optimizer_attribute(
model::Model, attr::MOI.AbstractOptimizerAttribute
)
Return the value of the solver-specific attribute attr
in model
.
Example
get_optimizer_attribute(model, MOI.Silent())
See also: set_optimizer_attribute
, set_optimizer_attributes
.
JuMP.set_optimizer_attribute
— Function.set_optimizer_attribute(model::Model, name::String, value)
Sets solver-specific attribute identified by name
to value
.
Note that this is equivalent to set_optimizer_attribute(model, MOI.RawParameter(name), value)
.
Example
set_optimizer_attribute(model, "SolverSpecificAttributeName", true)
See also: set_optimizer_attributes
, get_optimizer_attribute
.
set_optimizer_attribute(
model::Model, attr::MOI.AbstractOptimizerAttribute, value
)
Set the solver-specific attribute attr
in model
to value
.
Example
set_optimizer_attribute(model, MOI.Silent(), true)
See also: set_optimizer_attributes
, get_optimizer_attribute
.
JuMP.set_optimizer_attributes
— Function.set_optimizer_attributes(model::Model, pairs::Pair...)
Given a list of attribute => value
pairs, calls set_optimizer_attribute(model, attribute, value)
for each pair.
Example
model = Model(Ipopt.Optimizer)
set_optimizer_attributes(model, "tol" => 1e-4, "max_iter" => 100)
is equivalent to:
model = Model(Ipopt.Optimizer)
set_optimizer_attribute(model, "tol", 1e-4)
set_optimizer_attribute(model, "max_iter", 100)
See also: set_optimizer_attribute
, get_optimizer_attribute
.
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_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
.
File formats
JuMP can write models to a variety of file-formats using write_to_file
and Base.write
.
The file formats are defined within MathOptInterface in the FileFormats enumeration.
JuMP.write_to_file
— Function.write_to_file(
model::Model,
filename::String;
format::MOI.FileFormats.FileFormat = MOI.FileFormats.FORMAT_AUTOMATIC
)
Write the JuMP model model
to filename
in the format format
.
If the filename ends in .gz
, it will be compressed using Gzip. If the filename ends in .bz2
, it will be compressed using BZip2.
Base.write
— Method.Base.write(
io::IO,
model::Model;
format::MOI.FileFormats.FileFormat = MOI.FileFormats.FORMAT_MOF
)
Write the JuMP model model
to io
in the format format
.
JuMP models can be created from file formats using read_from_file
and Base.read
.
JuMP.read_from_file
— Function.read_from_file(
filename::String;
format::MOI.FileFormats.FileFormat = MOI.FileFormats.FORMAT_AUTOMATIC
)
Return a JuMP model read from filename
in the format format
.
If the filename ends in .gz
, it will be uncompressed using Gzip. If the filename ends in .bz2
, it will be uncompressed using BZip2.
Base.read
— Method.Base.read(io::IO, ::Type{Model}; format::MOI.FileFormats.FileFormat)
Return a JuMP model read from io
in the format format
.