diff --git a/docs/src/tutorials/algorithms/benders_decomposition.jl b/docs/src/tutorials/algorithms/benders_decomposition.jl index 376a4fd19ff..7ca882bedee 100644 --- a/docs/src/tutorials/algorithms/benders_decomposition.jl +++ b/docs/src/tutorials/algorithms/benders_decomposition.jl @@ -239,34 +239,27 @@ y_optimal = optimal_ret.y lazy_model = Model(GLPK.Optimizer) @variable(lazy_model, x[1:dim_x] >= 0, Int) @variable(lazy_model, θ >= M) -@objective(lazy_model, Min, θ) +@objective(lazy_model, Min, c_1' * x + θ) print(lazy_model) # What differs is that we write a callback function instead of a loop: -k = 0 - -""" - my_callback(cb_data) - -A callback that implements Benders decomposition. Note how similar it is to the -inner loop of the iterative method. -""" +number_of_subproblem_solves = 0 function my_callback(cb_data) - global k += 1 + status = callback_node_status(cb_data, lazy_model) + if status != MOI.CALLBACK_NODE_STATUS_INTEGER + ## Only add the constraint if `x` is an integer feasible solution + return + end x_k = callback_value.(cb_data, x) θ_k = callback_value(cb_data, θ) - lower_bound = c_1' * x_k + θ_k + global number_of_subproblem_solves += 1 ret = solve_subproblem(x_k) - upper_bound = c_1' * x_k + c_2' * ret.y - gap = (upper_bound - lower_bound) / upper_bound - print_iteration(k, lower_bound, upper_bound, gap) - if gap < ABSOLUTE_OPTIMALITY_GAP - println("Terminating with the optimal solution") - return + if θ_k < (ret.obj - 1e-6) + ## Only add the constraint if θ_k violates the constraint + cut = @build_constraint(θ >= ret.obj + -ret.π' * A_1 * (x .- x_k)) + MOI.submit(lazy_model, MOI.LazyConstraint(cb_data), cut) end - cut = @build_constraint(θ >= ret.obj + -ret.π' * A_1 * (x .- x_k)) - MOI.submit(model, MOI.LazyConstraint(cb_data), cut) return end @@ -276,8 +269,12 @@ set_attribute(lazy_model, MOI.LazyConstraintCallback(), my_callback) optimize!(lazy_model) -# Note how this problem also takes 4 iterations to converge, but the sequence -# of bounds is different compared to the iterative method. +# For this model, the callback algorithm required more solves of the subproblem: + +number_of_subproblem_solves + +# But for larger problems, you can expect the callback algorithm to be more +# efficient than the iterative algorithm. # Finally, we can obtain the optimal solution: