Primal and dual warm-starts

Some conic solvers have the ability to set warm-starts for the primal and dual solution. This can improve performance, particularly if you are repeatedly solving a sequence of related problems.

In this tutorial, we demonstrate how to write a function that sets the primal and dual starts as the optimal solution stored in a model. It is intended to be a starting point for which you can modify if you want to do something similar in your own code.

This tutorial uses the following packages:

using JuMP
import SCS

The main component of this tutorial is the following function. The most important observation is that we cache all of the solution values first, and then we modify the model second. (Alternating between querying a value and modifying the model is not allowed in JuMP.)

function set_optimal_start_values(model::Model)
# Store a mapping of the variable primal solution
variable_primal = Dict(x => value(x) for x in all_variables(model))
# In the following, we loop through every constraint and store a mapping
# from the constraint index to a tuple containing the primal and dual
# solutions.
constraint_solution = Dict()
for (F, S) in list_of_constraint_types(model)
# We add a try-catch here because some constraint types might not
# support getting the primal or dual solution.
try
for ci in all_constraints(model, F, S)
constraint_solution[ci] = (value(ci), dual(ci))
end
catch
@info("Something went wrong getting $F-in-$S. Skipping")
end
end
# Now we can loop through our cached solutions and set the starting values.
for (x, primal_start) in variable_primal
set_start_value(x, primal_start)
end
for (ci, (primal_start, dual_start)) in constraint_solution
set_start_value(ci, primal_start)
set_dual_start_value(ci, dual_start)
end
return
end
set_optimal_start_values (generic function with 1 method)

To test our function, we use the following linear program:

model = Model(SCS.Optimizer)
@variable(model, x[1:3] >= 0)
@constraint(model, sum(x) <= 1)
@objective(model, Max, sum(i * x[i] for i in 1:3))
optimize!(model)

By looking at the log (not shown in Documenter due to a bug), we can see that SCS took 100 iterations to find the optimal solution. Now we set the optimal solution as our starting point:

set_optimal_start_values(model)

and we re-optimize:

optimize!(model)

Now the optimization terminates after 0 iterations because our starting point is already optimal.

Tip