Callback Example
An example of creating and adding a callback to a ProgressiveHedging.jl run. This example is also available as the script callback_example.jl in the example directory.
In this example, we will use the same setup as in the Basic Example.
using ProgressiveHedging
import JuMP
import Ipopt
function two_stage_model(scenario_id::ScenarioID)
model = JuMP.Model(()->Ipopt.Optimizer())
JuMP.set_optimizer_attribute(model, "print_level", 0)
JuMP.set_optimizer_attribute(model, "tol", 1e-12)
JuMP.set_optimizer_attribute(model, "acceptable_tol", 1e-12)
scen = value(scenario_id)
ref = JuMP.@variable(model, x >= 0.0)
stage1 = [ref]
ref = JuMP.@variable(model, y >= 0.0)
stage2 = [ref]
b_s = scen == 0 ? 11.0 : 4.0
c_s = scen == 0 ? 0.5 : 10.0
JuMP.@constraint(model, x + y == b_s)
JuMP.@objective(model, Min, 1.0*x + c_s*y)
return JuMPSubproblem(model,
scenario_id,
Dict(stid(1) => stage1,
stid(2) => stage2)
)
end
scen_tree = two_stage_tree(2)
We now write a function to get called as PH executes and wrap it in the Callback
type.
function my_callback(ext::Dict{Symbol,Any},
phd::PHData,
winf::ProgressiveHedging.WorkerInf,
niter::Int)
# The `ext` dictionary can be used to store things between PH iterations
if niter == 2
ext[:message] = "This is from iteration 2!"
elseif niter == 5
println("Iteration 5 found the message: " * ext[:message])
elseif niter == 10
println("This is iteration 10!")
# We can access the current consensus variable values
for (xhid, xhat) in pairs(consensus_variables(phd))
println("The value of $(name(phd,xhid)) is $(value(xhat)).")
end
end
# Returning false from the callback will terminate PH.
# Here we stop after 20 iterations.
return niter < 20
end
my_cb = Callback(my_callback)
Several things are worth noting here:
- All callback functions must have the signature given here
- The
ext
dictionary is unique to each callback and can be used to pass information from one iteration to the next - Returning false from the callback will terminate PH
Now we call the solve
function as before but giving it the callback object we created.
(niter, abs_res, rel_res, obj, soln_df, phd) = solve(scen_tree,
two_stage_model,
ScalarPenaltyParameter(1.0),
callbacks=[my_cb]
)
@show niter
@show abs_res
@show rel_res
@show obj
@show soln_df
Iteration 5 found the message: This is from iteration 2! This is iteration 10! The value of x is 4.04687501015623. niter = 20 abs_res = 0.002441406256835066 rel_res = 0.0006100536849070564 obj = 5.750973778188833 soln_df = 3×4 DataFrame Row │ variable value stage scenarios │ String Float64 Int64 String ─────┼───────────────────────────────────── 1 │ x 4.00195 1 0,1 2 │ y 6.99609 2 0 3 │ y 0.0 2 1
The callbacks can be used to implement solution heuristics and alternative termination criteria. There are two callbacks are included with ProgressiveHedging.jl that do this. variable_fixing
is a heuristic that fixes variables whose values remain (approximately) the same over a set number of iterations. It is hoped that this speeds the convergence of PH. mean_deviation
is an alternative termination criteria. It is a form of mean relative absolute deviation from the consensus variable value. These are both implmentations of ideas found in (Watson & Woodruff 2010).