Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/PlasmoBenders/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "PlasmoBenders"
uuid = "491f1417-53b2-48aa-b9da-44cdd6c031b7"
authors = ["David Cole"]
version = "0.3.1"
version = "0.3.2"

[deps]
Plasmo = "d3f7391f-f14a-50cc-bbe4-76a32d1bad3c"
Expand Down
10 changes: 7 additions & 3 deletions lib/PlasmoBenders/src/Benders.jl
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ mutable struct BendersAlgorithm{T, V} <: AbstractPBAlgorithm{T, V}
primal_iters::Dict{T, Matrix{Float64}}
phis::Dict{T, Vector{Float64}}
phis_LR::Dict{T, Vector{Float64}}
subgraph_objectives::Dict{T, Float64}
Comment thread
dlcole3 marked this conversation as resolved.

time_forward_pass::Float64
time_backward_pass::Float64
Expand Down Expand Up @@ -228,7 +229,8 @@ mutable struct BendersAlgorithm{T, V} <: AbstractPBAlgorithm{T, V}
optimizer.primal_iters = Dict{T, Matrix{Float64}}()
optimizer.phis = Dict{T, Vector{Float64}}()
optimizer.phis_LR = Dict{T, Vector{Float64}}()

optimizer.subgraph_objectives = Dict{T, Float64}()

optimizer.time_forward_pass = 0.
optimizer.time_backward_pass = 0.
optimizer.time_init = 0.
Expand Down Expand Up @@ -720,9 +722,9 @@ function _forward_pass!(optimizer::BendersAlgorithm)
if get_regularize(optimizer)
get_regularize_lbs(optimizer)[root_object] = lb[1]

_regularize_pass!(optimizer, root_object, ub)
_regularize_pass!(optimizer, root_object)
else
_add_to_upper_bound!(optimizer, root_object, ub)
_save_subproblem_objective!(optimizer, root_object)
# Save primal information to upcoming objects
next_objects = optimizer.solve_order_dict[root_object]
for object in next_objects
Expand All @@ -737,6 +739,8 @@ function _forward_pass!(optimizer::BendersAlgorithm)
optimizer.last_solutions[root_object] = _get_object_last_solutions(root_object)
end

ub[1] = optimizer.subgraph_objectives[root_object]

############# Solve each successive object #################
if get_parallelize_benders(optimizer)
_optimize_in_forward_pass_multithread!(optimizer, ub)
Expand Down
7 changes: 3 additions & 4 deletions lib/PlasmoBenders/src/regularize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ end

function _regularize_pass!(
optimizer::BendersAlgorithm{T},
object,
ub
object::T
) where {T <: Union{Plasmo.OptiGraph, Plasmo.RemoteOptiGraph}}
next_objects = optimizer.solve_order_dict[object]
if length(next_objects) > 0
Expand All @@ -65,7 +64,7 @@ function _regularize_pass!(

if termination_status(object) != MOI.INFEASIBLE
obj_val_minus_theta = value(object, original_objective) - _theta_value(optimizer, object)
ub[1] += obj_val_minus_theta
optimizer.subgraph_objectives[object] = obj_val_minus_theta
get_regularize_ubs(optimizer)[object] = obj_val_minus_theta
for next_object in next_objects
comp_vars = optimizer.comp_vars[next_object]
Expand Down Expand Up @@ -102,7 +101,7 @@ function _regularize_pass!(
delete!(object_dictionary(object), :_reg_con)
end
else
_add_to_upper_bound!(optimizer, object, ub)
_save_subproblem_objective!(optimizer, object)
get_regularize_ubs(optimizer)[object] = JuMP.objective_value(object)

if !optimizer.is_MIP #TODO: This can probably be moved to _save_forward_pass_solutions function; doesn't need to be here
Expand Down
41 changes: 23 additions & 18 deletions lib/PlasmoBenders/src/solution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ end

function _optimize_in_forward_pass_multithread!(optimizer::BendersAlgorithm{OptiGraph}, ub) #TODO: Type these functions
Threads.@threads for i in 2:(length(optimizer.solve_order))
_forward_pass_iteration!(optimizer, i, ub)
_forward_pass_iteration!(optimizer, i)
end
# sum the objective values from each thread and add to upper bound
ub[1] += sum(optimizer.subgraph_objectives[optimizer.solve_order[i]] for i in 2:(length(optimizer.solve_order)))
end
Comment thread
dlcole3 marked this conversation as resolved.
Comment thread
dlcole3 marked this conversation as resolved.
Comment thread
dlcole3 marked this conversation as resolved.

function _optimize_in_forward_pass_multithread!(optimizer::BendersAlgorithm{RemoteOptiGraph}, ub)
Expand All @@ -111,9 +113,9 @@ function _optimize_in_forward_pass_multithread!(optimizer::BendersAlgorithm{Remo

optimizer.time_subproblem_solves += t_solve
if !is_feasible
ub[1] = Inf
optimizer.subgraph_objectives[next_object] = Inf
else
ub[1] += obj_val
optimizer.subgraph_objectives[next_object] = obj_val # TODO: if we parallelize beyond two stages, we need to subtract the value of theta here
end
Comment thread
dlcole3 marked this conversation as resolved.

optimizer.feasibility_map[next_object] = is_feasible
Expand All @@ -129,6 +131,8 @@ function _optimize_in_forward_pass_multithread!(optimizer::BendersAlgorithm{Remo
end
end
end
# sum the objective values from each thread and add to upper bound
ub[1] += sum(optimizer.subgraph_objectives[optimizer.solve_order[i]] for i in 2:(length(optimizer.solve_order)))
end

function _optimize_remote_graph_forward(optimizer::BendersAlgorithm, next_object::RemoteOptiGraph, i::Int)
Expand Down Expand Up @@ -184,11 +188,13 @@ end

function _optimize_in_forward_pass!(optimizer, ub)
for i in 2:(length(optimizer.solve_order))
_forward_pass_iteration!(optimizer, i, ub)
_forward_pass_iteration!(optimizer, i)
end
# sum the objective values from each thread and add to upper bound
ub[1] += sum(optimizer.subgraph_objectives[optimizer.solve_order[i]] for i in 2:(length(optimizer.solve_order)))
end

function _forward_pass_iteration!(optimizer, i, ub)
function _forward_pass_iteration!(optimizer, i)
next_object = optimizer.solve_order[i]

comp_vars = optimizer.comp_vars[next_object]
Expand All @@ -208,20 +214,21 @@ function _forward_pass_iteration!(optimizer, i, ub)
object_termination_status = _check_termination_status(next_object, i; add_slacks_bool=get_add_slacks(optimizer), feasibility_cuts_bool=get_feasibility_cuts(optimizer))

if object_termination_status
_save_forward_pass_solutions(optimizer, next_object, ub)
_save_forward_pass_solutions(optimizer, next_object)
optimizer.feasibility_map[next_object] = true
else
# Need to do feasibility cuts; the check termination status function already tested
# that feasibility_cuts was true
println("Subgraph $i in forward pass was infeasible; using feasibility_cuts")
_save_feasibility_cut_data(optimizer, next_object, ub)
_save_feasibility_cut_data(optimizer, next_object)
optimizer.subgraph_objectives[next_object] = Inf
optimizer.feasibility_map[next_object] = false
end

PlasmoBenders._unfix_variables(next_object, var_copies)
end

function _save_forward_pass_solutions(optimizer, next_object, ub)
function _save_forward_pass_solutions(optimizer, next_object)
# Add to the upper bound; if it's not the last object, subtract the cost-to-go from upper bound
obj_val = JuMP.objective_value(next_object)

Expand All @@ -235,10 +242,10 @@ function _save_forward_pass_solutions(optimizer, next_object, ub)
if get_regularize(optimizer)
get_regularize_lbs(optimizer)[next_object] = obj_val

_regularize_pass!(optimizer, next_object, ub)
_regularize_pass!(optimizer, next_object)
else
# Save primal information to upcoming objects
_add_to_upper_bound!(optimizer, next_object, ub)
_save_subproblem_objective!(optimizer, next_object)
next_objects = optimizer.solve_order_dict[next_object]
for object in next_objects
next_comp_vars = optimizer.comp_vars[object]
Expand All @@ -262,12 +269,10 @@ function _save_forward_pass_solutions(optimizer, next_object, ub)
end
end

function _save_feasibility_cut_data(optimizer, next_object, ub)
function _save_feasibility_cut_data(optimizer, next_object)
# See JuMP documentation for implementation of this method:
# https://jump.dev/JuMP.jl/stable/tutorials/algorithms/benders_decomposition/#Feasibility-cuts

ub[1] = Inf

# Save primal information to upcoming objects
if !optimizer.is_MIP
obj_val = JuMP.dual_objective_value(next_object)
Expand Down Expand Up @@ -694,15 +699,15 @@ function _add_initial_relaxed_cuts!(
optimizer.ext["link_var_mapping"] = link_vars_mapping
end

function _add_to_upper_bound!(
function _save_subproblem_objective!(
optimizer::BendersAlgorithm{T},
object::T,
ub
object::T
) where {T <: Plasmo.AbstractOptiGraph}
obj_val = JuMP.objective_value(object)
ub[1] += obj_val
if length(optimizer.solve_order_dict[object]) > 0
theta_val = _theta_value(optimizer, object)
ub[1] -= theta_val
optimizer.subgraph_objectives[object] = obj_val - theta_val
else
optimizer.subgraph_objectives[object] = obj_val
end
end
Loading