Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix conditions to restart colgen when using lazy cut gen #502

Merged
merged 5 commits into from
Apr 15, 2021
Merged
Changes from 2 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
81 changes: 41 additions & 40 deletions src/Algorithm/colgen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ Column generation algorithm. It applies `restr_master_solve_alg` to solve the li
restricted master and `pricing_prob_solve_alg` to solve the subproblems.
"""
@with_kw struct ColumnGeneration <: AbstractOptimizationAlgorithm
restr_master_solve_alg = SolveLpForm(get_dual_solution = true)
#TODO : pricing problem solver may be different depending on the
restr_master_solve_alg = SolveLpForm(get_dual_solution=true)
# TODO : pricing problem solver may be different depending on the
# pricing subproblem
pricing_prob_solve_alg = SolveIpForm(
deactivate_artificial_vars = false,
enforce_integrality = false,
log_level = 2
deactivate_artificial_vars=false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you remove spaces around =? This is a new convention? I think with spaces it looks better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the formatter and it looks like to be the convention : https://github.com/invenia/BlueStyle#keyword-arguments

enforce_integrality=false,
log_level=2
)
essential_cut_gen_alg = CutCallbacks(call_robust_facultative = false)
essential_cut_gen_alg = CutCallbacks(call_robust_facultative=false)
max_nb_iterations::Int64 = 1000
log_print_frequency::Int64 = 1
store_all_ip_primal_sols::Bool = false
Expand All @@ -46,7 +46,7 @@ end
stabilization_is_used(algo::ColumnGeneration) = !iszero(algo.smoothing_stabilization)

function get_child_algorithms(algo::ColumnGeneration, reform::Reformulation)
child_algs = Tuple{AbstractAlgorithm, AbstractModel}[]
child_algs = Tuple{AbstractAlgorithm,AbstractModel}[]
push!(child_algs, (algo.restr_master_solve_alg, getmaster(reform)))
push!(child_algs, (algo.essential_cut_gen_alg, getmaster(reform)))
for (id, spform) in get_dw_pricing_sps(reform)
Expand All @@ -56,7 +56,7 @@ function get_child_algorithms(algo::ColumnGeneration, reform::Reformulation)
end

function get_units_usage(algo::ColumnGeneration, reform::Reformulation)
units_usage = Tuple{AbstractModel, UnitTypePair, UnitAccessMode}[]
units_usage = Tuple{AbstractModel,UnitTypePair,UnitAccessMode}[]
master = getmaster(reform)
push!(units_usage, (master, MasterColumnsUnitPair, READ_AND_WRITE))
push!(units_usage, (master, PartialSolutionUnitPair, READ_ONLY))
Expand Down Expand Up @@ -90,18 +90,15 @@ function run!(algo::ColumnGeneration, env::Env, data::ReformData, input::Optimiz
master = getmaster(reform)
optstate = OptimizationState(master, getoptstate(input), false, false)

restart = true
stop = false
while restart && !stop
set_ph3!(master) # mixed ph1 & ph2
stop, restart = cg_main_loop!(algo, env, 3, optstate, data)
end

set_ph3!(master) # mixed ph1 & ph2
stop, _ = cg_main_loop!(algo, env, 3, optstate, data)

restart = true
while should_do_ph_1(optstate) && restart && !stop
set_ph1!(master, optstate)
stop, restart = cg_main_loop!(algo, env, 1, optstate, data)
restart && break
stop, _ = cg_main_loop!(algo, env, 1, optstate, data)
if !stop
set_ph2!(master, optstate) # pure ph2
stop, restart = cg_main_loop!(algo, env, 2, optstate, data)
guimarqu marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -172,7 +169,7 @@ mutable struct SubprobInfo
ub::Float64
lb_dual::Float64
ub_dual::Float64
bestsol::Union{Nothing, PrimalSolution}
bestsol::Union{Nothing,PrimalSolution}
valid_dual_bound_contrib::Float64
pseudo_dual_bound_contrib::Float64
recorded_sol_ids::Vector{VarId}
Expand Down Expand Up @@ -220,7 +217,7 @@ function insert_cols_in_master!(
kind = Continuous
duty = MasterCol
mc = setcol_from_sp_primalsol!(
masterform, spform, sol_id, name, duty; lb = lb, ub = ub, kind = kind
masterform, spform, sol_id, name, duty; lb=lb, ub=ub, kind=kind
)
if phase == 1
setcurcost!(masterform, mc, 0.0)
Expand Down Expand Up @@ -267,17 +264,17 @@ function compute_red_cost(
else
red_cost = getvalue(spsol)
end
#red_cost -= (spinfo.lb * spinfo.lb_dual + spinfo.ub * spinfo.ub_dual)
red_cost -= (spinfo.lb_dual + spinfo.ub_dual)
# red_cost -= (spinfo.lb * spinfo.lb_dual + spinfo.ub * spinfo.ub_dual)
guimarqu marked this conversation as resolved.
Show resolved Hide resolved
red_cost -= spinfo.lb_dual + spinfo.ub_dual
return red_cost
end

function improving_red_cost(redcost::Float64, algo::ColumnGeneration, ::Type{MinSense})
return (redcost < 0.0 - algo.redcost_tol)
return redcost < 0.0 - algo.redcost_tol
end

function improving_red_cost(redcost::Float64, algo::ColumnGeneration, ::Type{MaxSense})
return (redcost > 0.0 + algo.redcost_tol)
return redcost > 0.0 + algo.redcost_tol
end

function solve_sp_to_gencol!(
Expand Down Expand Up @@ -339,7 +336,7 @@ function updatereducedcosts!(reform::Reformulation, redcostsvec::ReducedCostsVec
row_end = 0
row_pos = 0

terms = Dict{VarId, Float64}(id => 0.0 for id in redcostsvec.varids)
terms = Dict{VarId,Float64}(id => 0.0 for id in redcostsvec.varids)

for dual_pos in 1:length(dualsol.sol.array)
entry = dualsol.sol.array[dual_pos]
Expand Down Expand Up @@ -380,7 +377,7 @@ function updatereducedcosts!(reform::Reformulation, redcostsvec::ReducedCostsVec
end

function solve_sps_to_gencols!(
spinfos::Dict{FormId, SubprobInfo}, algo::ColumnGeneration, env::Env, phase::Int64,
spinfos::Dict{FormId,SubprobInfo}, algo::ColumnGeneration, env::Env, phase::Int64,
data::ReformData, redcostsvec::ReducedCostsVector, lp_dual_sol::DualSolution,
smooth_dual_sol::DualSolution,
)
Expand Down Expand Up @@ -447,7 +444,7 @@ function cleanup_columns(algo::ColumnGeneration, iteration::Int64, data::ReformD
# to quickly check the number of active master columns
iteration % 10 != 0 && return

cols_with_redcost = Vector{Pair{Variable, Float64}}()
cols_with_redcost = Vector{Pair{Variable,Float64}}()
master = getmodel(getmasterdata(data))
for (id, var) in getvars(master)
if getduty(id) <= MasterCol && iscuractive(master, var) && isexplicit(master, var)
Expand All @@ -460,7 +457,7 @@ function cleanup_columns(algo::ColumnGeneration, iteration::Int64, data::ReformD

# sort active master columns by reduced cost
reverse_order = getobjsense(master) == MinSense ? true : false
sort!(cols_with_redcost, by = x -> x.second, rev=reverse_order)
sort!(cols_with_redcost, by=x -> x.second, rev=reverse_order)

num_cols_to_keep = floor(Int64, num_active_cols * algo.cleanup_ratio)

Expand All @@ -483,10 +480,10 @@ ph_one_infeasible_db(algo, db::DualBound{MinSense}) = getvalue(db) > algo.opt_at
ph_one_infeasible_db(algo, db::DualBound{MaxSense}) = getvalue(db) < - algo.opt_atol

function update_lagrangian_dual_bound!(
stabunit::ColGenStabilizationUnit, optstate::OptimizationState{F, S}, algo::ColumnGeneration,
stabunit::ColGenStabilizationUnit, optstate::OptimizationState{F,S}, algo::ColumnGeneration,
master::Formulation, puremastervars::Vector{Pair{VarId,Float64}}, dualsol::DualSolution,
partialsol::PrimalSolution, spinfos::Dict{FormId, SubprobInfo}
) where {F, S}
partialsol::PrimalSolution, spinfos::Dict{FormId,SubprobInfo}
) where {F,S}

sense = getobjsense(master)

Expand All @@ -505,10 +502,10 @@ function update_lagrangian_dual_bound!(
puremastvars_contrib += redcost * mult
end
end

valid_lagr_bound = DualBound{S}(puremastvars_contrib + dualsol.bound)
for (spuid, spinfo) in spinfos
valid_lagr_bound += spinfo.valid_dual_bound_contrib
valid_lagr_bound += spinfo.valid_dual_bound_contrib
guimarqu marked this conversation as resolved.
Show resolved Hide resolved
end

update_ip_dual_bound!(optstate, valid_lagr_bound)
Expand All @@ -517,7 +514,7 @@ function update_lagrangian_dual_bound!(
if stabilization_is_used(algo)
pseudo_lagr_bound = DualBound{S}(puremastvars_contrib + dualsol.bound)
for (spuid, spinfo) in spinfos
pseudo_lagr_bound += spinfo.pseudo_dual_bound_contrib
pseudo_lagr_bound += spinfo.pseudo_dual_bound_contrib
guimarqu marked this conversation as resolved.
Show resolved Hide resolved
end
update_stability_center!(stabunit, dualsol, valid_lagr_bound, pseudo_lagr_bound)
end
Expand All @@ -526,7 +523,7 @@ end

function compute_subgradient_contribution(
algo::ColumnGeneration, stabunit::ColGenStabilizationUnit, master::Formulation,
puremastervars::Vector{Pair{VarId,Float64}}, spinfos::Dict{FormId, SubprobInfo}
puremastervars::Vector{Pair{VarId,Float64}}, spinfos::Dict{FormId,SubprobInfo}
)
sense = getobjsense(master)
constrids = ConstrId[]
Expand Down Expand Up @@ -560,7 +557,7 @@ function compute_subgradient_contribution(
end

function move_convexity_constrs_dual_values!(
spinfos::Dict{FormId, SubprobInfo}, dualsol::DualSolution
spinfos::Dict{FormId,SubprobInfo}, dualsol::DualSolution
)
newbound = dualsol.bound
for (spuid, spinfo) in spinfos
Expand Down Expand Up @@ -613,7 +610,7 @@ function cg_main_loop!(
# termination by bound does not apply
reform = getreform(data)
masterform = getmaster(reform)
spinfos = Dict{FormId, SubprobInfo}()
spinfos = Dict{FormId,SubprobInfo}()

# collect multiplicity current bounds for each sp
dwspvars = Vector{VarId}()
Expand All @@ -633,6 +630,7 @@ function cg_main_loop!(

redcostsvec = ReducedCostsVector(dwspvars, dwspforms)
iteration = 0
essential_cuts_separated = false
guimarqu marked this conversation as resolved.
Show resolved Hide resolved

stabunit = (stabilization_is_used(algo) ? getunit(getmasterdata(data), ColGenStabilizationUnitPair)
: ColGenStabilizationUnit(masterform) )
Expand All @@ -649,7 +647,7 @@ function cg_main_loop!(

rm_time = @elapsed begin
rm_input = OptimizationInput(
OptimizationState(masterform, ip_primal_bound = get_ip_primal_bound(cg_optstate))
OptimizationState(masterform, ip_primal_bound=get_ip_primal_bound(cg_optstate))
)
rm_output = run!(algo.restr_master_solve_alg, env, getmasterdata(data), rm_input)
end
Expand Down Expand Up @@ -696,8 +694,10 @@ function cg_main_loop!(
if cutcb_output.nb_cuts_added == 0
update_ip_primal_sol!(cg_optstate, new_primal_sol)
else
# because the new cuts may make the master infeasible
return false, true
essential_cuts_separated = true
if phase == 2 # because the new cuts may make the master infeasible
return false, true
end
guimarqu marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Expand Down Expand Up @@ -767,7 +767,7 @@ function cg_main_loop!(
primal_bound = get_lp_primal_bound(cg_optstate)
ip_primal_bound = get_ip_primal_bound(cg_optstate)

if ip_gap_closed(cg_optstate, atol = algo.opt_atol, rtol = algo.opt_rtol)
if ip_gap_closed(cg_optstate, atol=algo.opt_atol, rtol=algo.opt_rtol)
setterminationstatus!(cg_optstate, OPTIMAL)
@logmsg LogLevel(0) "Dual bound reached primal bound."
return true, false
Expand All @@ -781,12 +781,12 @@ function cg_main_loop!(
@logmsg LogLevel(0) "Phase one determines infeasibility."
return true, false
end
if lp_gap_closed(cg_optstate, atol = algo.opt_atol, rtol = algo.opt_rtol)
if lp_gap_closed(cg_optstate, atol=algo.opt_atol, rtol=algo.opt_rtol) && !essential_cuts_separated
@logmsg LogLevel(0) "Column generation algorithm has converged."
setterminationstatus!(cg_optstate, OPTIMAL)
return false, false
end
if nb_new_columns == 0
if nb_new_columns == 0 && !essential_cuts_separated
@logmsg LogLevel(0) "No new column generated by the pricing problems."
setterminationstatus!(cg_optstate, OTHER_LIMIT)
return false, false
Expand All @@ -796,6 +796,7 @@ function cg_main_loop!(
@warn "Maximum number of column generation iteration is reached."
guimarqu marked this conversation as resolved.
Show resolved Hide resolved
return true, false
end
essential_cuts_separated = false
end

return false, false
Expand Down