Skip to content

Commit

Permalink
Test colgen stab flow (#940)
Browse files Browse the repository at this point in the history
  • Loading branch information
guimarqu authored Jun 15, 2023
1 parent d2cc545 commit 3a0be85
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/ColGen/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab)
# will add the violated cut to the master formulation.
# If the formulation changes, one needs to restart the column generation to update
# memoization to calculate reduced costs and stabilization.
# TODO: the user can get the reformulation from the context.
new_ip_primal_sol, new_cut_in_master = check_primal_ip_feasibility!(mast_primal_sol, context, phase, get_reform(context), env)
if new_cut_in_master
return new_iteration_output(O, is_min_sense, nothing, nothing, 0, true, false, false, false, false, false, nothing, nothing, nothing)
Expand All @@ -307,6 +308,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab)

# Stores dual solution in the constraint. This is used when the pricing solver supports
# non-robust cuts.
# TODO: the user can get the reformulation from the context.
update_master_constrs_dual_vals!(context, phase, get_reform(context), mast_dual_sol)

# Compute reduced cost (generic operation) by you must support math operations.
Expand Down Expand Up @@ -427,9 +429,11 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab)

# Insert columns into the master.
# The implementation is responsible for checking if the column is "valid".
# TODO: the user can get the reformulation from the context.
col_ids = insert_columns!(get_reform(context), context, phase, generated_columns)
nb_cols_inserted = length(col_ids)

# TODO: remove the context from the arguments.
update_stabilization_after_iter!(stab, context, master, generated_columns, mast_dual_sol)

return new_iteration_output(O, is_min_sense, get_obj_val(mast_result), valid_db, nb_cols_inserted, false, false, false, false, false, false, mast_primal_sol, ip_primal_sol, mast_dual_sol)
Expand Down
117 changes: 116 additions & 1 deletion test/unit/ColGen/colgen_stabilization.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
################################################################################
# Test the implementation of the stabilization procedure.
################################################################################

# Make sure the value of α is updated correctly after each misprice.
# The goal is to tend to 0.0 after a given number of misprices.
Expand Down Expand Up @@ -281,7 +283,6 @@ function test_angle_3()
end
register!(unit_tests, "colgen_stabilization", test_angle_3)


function test_dynamic_alpha_schedule()
for α in 0.1:0.1:0.9
@test Coluna.Algorithm.f_incr(α) > α
Expand Down Expand Up @@ -322,5 +323,119 @@ function test_dynamic_alpha_schedule()
end
register!(unit_tests, "colgen_stabilization", test_dynamic_alpha_schedule)


################################################################################
# Test to make sure the generic code works
################################################################################
# Mock implementation of the column generation to make sure the stabilization logic works
# as expected.

mutable struct ColGenStabFlowStab
nb_misprice::Int64
nb_update_stab_after_master_done::Int64
nb_update_stab_after_pricing_done::Int64
nb_check_misprice::Int64
nb_misprices_done::Int64
nb_update_stab_after_iter_done::Int64
ColGenStabFlowStab(nb_misprice) = new(nb_misprice, 0, 0, 0, 0, 0)
end

struct ColGenStabFlowRes end
struct ColGenStabFlowOutput end
struct ColGenStabFlowDualSol end
struct ColGenStabFlowPrimalSol end
struct ColGenStabFlowPricingStrategy end

mutable struct ColGenStabFlowCtx <: Coluna.ColGen.AbstractColGenContext
nb_compute_dual_bound::Int64
ColGenStabFlowCtx() = new(0)
end

ColGen.get_master(::ColGenStabFlowCtx) = nothing
ColGen.is_minimization(::ColGenStabFlowCtx) = true
ColGen.optimize_master_lp_problem!(master, ctx::ColGenStabFlowCtx, env) = ColGenStabFlowRes()
ColGen.colgen_iteration_output_type(::ColGenStabFlowCtx) = ColGenStabFlowOutput
ColGen.is_infeasible(::ColGenStabFlowRes) = false
ColGen.is_unbounded(::ColGenStabFlowRes) = false
ColGen.get_dual_sol(::ColGenStabFlowRes) = ones(Float64, 3)
ColGen.get_primal_sol(::ColGenStabFlowRes) = ColGenStabFlowPrimalSol()
ColGen.get_obj_val(::ColGenStabFlowRes) = 0.0
ColGen.isbetter(::ColGenStabFlowPrimalSol, p) = false
ColGen.get_reform(::ColGenStabFlowCtx) = nothing
ColGen.update_master_constrs_dual_vals!(::ColGenStabFlowCtx, phase, reform, dual_sol) = nothing
ColGen.get_subprob_var_orig_costs(::ColGenStabFlowCtx) = ones(Float64, 3)
ColGen.get_subprob_var_coef_matrix(::ColGenStabFlowCtx) = ones(Float64, 3, 3)
ColGen.update_reduced_costs!(::ColGenStabFlowCtx, phase, red_costs) = nothing

function ColGen.update_stabilization_after_master_optim!(stab::ColGenStabFlowStab, phase, mast_dual_sol)
stab.nb_update_stab_after_master_done += 1
return true
end

ColGen.get_master_dual_sol(stab::ColGenStabFlowStab, phase, mast_dual) = [0.5, 0.5, 0.5]
ColGen.set_of_columns(::ColGenStabFlowCtx) = []
ColGen.get_pricing_subprobs(::ColGenStabFlowCtx) = []
ColGen.get_pricing_strategy(::ColGenStabFlowCtx, phase) = ColGenStabFlowPricingStrategy()
ColGen.pricing_strategy_iterate(::ColGenStabFlowPricingStrategy) = nothing
ColGen.compute_dual_bound(ctx::ColGenStabFlowCtx, phase, bounds, mast_dual_sol) = ctx.nb_compute_dual_bound += 1

function ColGen.update_stabilization_after_pricing_optim!(stab::ColGenStabFlowStab, master, valid_db, pseudo_db, mast_dual_sol)
@test mast_dual_sol == [1.0, 1.0, 1.0] # we need the out point in this method.
stab.nb_update_stab_after_pricing_done += 1
return true
end

function ColGen.check_misprice(stab::ColGenStabFlowStab, cols, mast_dual_sol)
@test mast_dual_sol == [1.0, 1.0, 1.0] # we need the out point in this method.
stab.nb_check_misprice += 1
return stab.nb_check_misprice <= stab.nb_misprice
end

function ColGen.update_stabilization_after_misprice!(stab::ColGenStabFlowStab, mast_dual_sol)
@test mast_dual_sol == [1.0, 1.0, 1.0] # we need the out point in this method.
stab.nb_misprices_done += 1
end

function ColGen.insert_columns!(reform, context::ColGenStabFlowCtx, phase, generated_columns)
return []
end

function ColGen.update_stabilization_after_iter!(stab::ColGenStabFlowStab, ctx, master, generated_columns, mast_dual_sol)
@test mast_dual_sol == [1.0, 1.0, 1.0] # we need the out point in this method.
stab.nb_update_stab_after_iter_done += 1
return true
end

ColGen.new_iteration_output(::Type{<:ColGenStabFlowOutput}, args...) = nothing

function test_stabilization_flow_no_misprice()
ctx = ColGenStabFlowCtx()
phase = nothing
stage = nothing
env = nothing
ip_primal_sol = nothing
stab = ColGenStabFlowStab(0)
res = Coluna.ColGen.run_colgen_iteration!(ctx, phase, stage, env, ip_primal_sol, stab)
@test stab.nb_check_misprice == 1
@test stab.nb_misprices_done == 0
@test stab.nb_update_stab_after_iter_done == 1
@test stab.nb_update_stab_after_master_done == 1
@test stab.nb_update_stab_after_pricing_done == 1
end
register!(unit_tests, "colgen_stabilization", test_stabilization_flow_no_misprice)

function test_stabilization_flow_with_misprice()
ctx = ColGenStabFlowCtx()
phase = nothing
stage = nothing
env = nothing
ip_primal_sol = nothing
stab = ColGenStabFlowStab(10)
res = Coluna.ColGen.run_colgen_iteration!(ctx, phase, stage, env, ip_primal_sol, stab)
@test stab.nb_check_misprice == 10 + 1
@test stab.nb_misprices_done == 10
@test stab.nb_update_stab_after_iter_done == 1
@test stab.nb_update_stab_after_master_done == 1
@test stab.nb_update_stab_after_pricing_done == 10 + 1
end
register!(unit_tests, "colgen_stabilization", test_stabilization_flow_with_misprice)

0 comments on commit 3a0be85

Please sign in to comment.