This quick start guide will introduce the main concepts of JuMP. If you are familiar with another modeling language embedded in a high-level language such as PuLP (Python) or a solver-specific interface you will find most of this familiar. If you are coming from an AMPL or similar background, you may find some of the concepts novel but the general appearance will still be familiar.
The example in this guide is deliberately kept simple. There are more complex
examples in the JuMP/examples/
folder.
Once JuMP is installed, to use JuMP in your programs, you just need to say:
julia> using JuMP
You also need to include a Julia package which provides an appropriate solver.
One such solver is GLPK.Optimizer
, which is provided by the
GLPK.jl package.
julia> using GLPK
See Installation Guide for a list of other solvers you can use.
Models are created with the Model()
function. The with_optimizer
syntax is
used to specify the optimizer to be used:
julia> model = Model(with_optimizer(GLPK.Optimizer))
A JuMP Model
DocTestSetup = quote
# Using a caching optimizer removes the need to # load a solver such as GLPK
# for building the documentation.
const MOI = JuMP.MathOptInterface
model = Model(with_optimizer(MOI.Utilities.MockOptimizer,
JuMP.JuMPMOIModel{Float64}(),
eval_objective_value = false,
eval_variable_constraint_dual = false))
end
!!! note
Your model doesn't have to be called model
- it's just a name.
There are a few options for defining a variable, depending on whether you want
to have lower bounds, upper bounds, both bounds, or even no bounds. The
following commands will create two variables, x
and y
, with both lower and
upper bounds. Note the first argument is our model variable model
. These
variables are associated with this model and cannot be used in another model.
julia> @variable(model, 0 <= x <= 2)
x
julia> @variable(model, 0 <= y <= 30)
y
See the Variables section for more information on creating variables.
DocTestSetup = nothing
Next we'll set our objective. Note again the model
, so we know which model's
objective we are setting! The objective sense, Max
or Min
, should be
provided as the second argument. Note also that we don't have a multiplication
*
symbol between 5
and our variable x
- Julia is smart enough to not need
it! Feel free to stick with *
if it makes you feel more comfortable, as we
have done with 3 * y
. (We have been intentionally inconsistent here to demonstrate different syntax; however, it is good practice to pick one way or the other consistently in your code.)
julia> @objective(model, Max, 5x + 3 * y)
5 x + 3 y
Adding constraints is a lot like setting the objective. Here we create a
less-than-or-equal-to constraint using <=
, but we can also create equality
constraints using ==
and greater-than-or-equal-to constraints with >=
:
julia> @constraint(model, con, 1x + 5y <= 3)
con : x + 5 y <= 3.0
Note that in a similar manner to the @variable
macro, we have named the
constraint con
. This will bind the constraint to the Julia variable con
for
later analysis.
Models are solved with the JuMP.optimize!
function:
julia> JuMP.optimize!(model)
DocTestSetup = quote
# Now we load in the solution. Using a caching optimizer removes the need to
# load a solver such as GLPK for building the documentation.
mock = JuMP.caching_optimizer(model).optimizer.model
MOI.set(mock, MOI.TerminationStatus(), MOI.Success)
MOI.set(mock, MOI.PrimalStatus(), MOI.FeasiblePoint)
MOI.set(mock, MOI.DualStatus(), MOI.FeasiblePoint)
MOI.set(mock, MOI.ResultCount(), 1)
MOI.set(mock, MOI.ObjectiveValue(), 10.6)
MOI.set(mock, MOI.VariablePrimal(), JuMP.optimizer_index(x), 2.0)
MOI.set(mock, MOI.VariablePrimal(), JuMP.optimizer_index(y), 0.2)
MOI.set(mock, MOI.ConstraintDual(), JuMP.optimizer_index(con), -0.6)
MOI.set(mock, MOI.ConstraintDual(), JuMP.optimizer_index(JuMP.UpperBoundRef(x)), -4.4)
MOI.set(mock, MOI.ConstraintDual(), JuMP.optimizer_index(JuMP.LowerBoundRef(y)), 0.0)
end
After the call to JuMP.optimize!
has finished, we need to understand why the
optimizer stopped. This can be for a number of reasons. First, the solver might
have found the optimal solution, or proved that the problem is infeasible.
However, it might also have run into numerical difficulties, or terminated due
to a setting such as a time limit. We can ask the solver why it stopped using
the JuMP.termination_status
function:
julia> JuMP.termination_status(model)
Success::TerminationStatusCode = 0
In this case, GLPK
returned Success
. This does not mean that it has found
the optimal solution. Instead, it indicates that GLPK has finished running and
did not encounter any errors or user-provided termination limits.
DocTestSetup = nothing
To understand the reason for termination in more detail, we need to query
JuMP.primalstatus
:
julia> JuMP.primal_status(model)
FeasiblePoint::ResultStatusCode = 1
This indicates that GLPK has found a FeasiblePoint
to the primal problem.
Coupled with the Success
from JuMP.termination_status
, we can infer that GLPK
has indeed found the optimal solution. We can also query JuMP.dual_status
:
julia> JuMP.dual_status(model)
FeasiblePoint::ResultStatusCode = 1
Like the primal_status
, GLPK indicates that it has found a FeasiblePoint
to
the dual problem.
Finally, we can query the result of the optimization. First, we can query the objective value:
julia> JuMP.objective_value(model)
10.6
We can also query the primal result values of the x
and y
variables:
julia> JuMP.value(x)
2.0
julia> JuMP.value(y)
0.2
We can also query the value of the dual variable associated with the constraint
con
(which we bound to a Julia variable when defining the constraint):
julia> JuMP.dual(con)
-0.6
To query the dual variables associated with the variable bounds, things are a little trickier as we first need to obtain a reference to the constraint:
julia> x_upper = JuMP.UpperBoundRef(x)
x <= 2.0
julia> JuMP.dual(x_upper)
-4.4
A similar process can be followed to obtain the dual of the lower bound
constraint on y
:
julia> y_lower = JuMP.LowerBoundRef(y)
y >= 0.0
julia> JuMP.dual(y_lower)
0.0