From b26a5f9964169c0a73b4ca366ed31770a8ab6fa7 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Thu, 22 Jun 2023 15:12:24 +0200 Subject: [PATCH 1/5] Fix alpha dynamic update --- src/Algorithm/colgen/printer.jl | 3 +++ src/Algorithm/colgen/stabilization.jl | 20 +++++++++++--------- src/ColGen/interface.jl | 22 ++++++++++++---------- src/ColGen/stabilization.jl | 4 ++-- test/unit/ColGen/colgen_stabilization.jl | 2 +- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Algorithm/colgen/printer.jl b/src/Algorithm/colgen/printer.jl index 912e28d0a..72f3b18b9 100644 --- a/src/Algorithm/colgen/printer.jl +++ b/src/Algorithm/colgen/printer.jl @@ -22,6 +22,9 @@ ColGen.is_minimization(ctx::ColGenPrinterContext) = ColGen.is_minimization(ctx.i ColGen.get_pricing_subprobs(ctx::ColGenPrinterContext) = ColGen.get_pricing_subprobs(ctx.inner) ColGen.setup_stabilization!(ctx::ColGenPrinterContext, master) = ColGen.setup_stabilization!(ctx.inner, master) +function ColGen.update_stabilization_after_pricing_optim!(stab, ctx::ColGenPrinterContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) + return ColGen.update_stabilization_after_pricing_optim!(stab, ctx.inner, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) +end ColGen.new_phase_iterator(ctx::ColGenPrinterContext) = ColGen.new_phase_iterator(ctx.inner) ColGen.new_stage_iterator(ctx::ColGenPrinterContext) = ColGen.new_stage_iterator(ctx.inner) diff --git a/src/Algorithm/colgen/stabilization.jl b/src/Algorithm/colgen/stabilization.jl index a2c48baea..f8364ffd4 100644 --- a/src/Algorithm/colgen/stabilization.jl +++ b/src/Algorithm/colgen/stabilization.jl @@ -3,7 +3,7 @@ struct NoColGenStab end ColGen.update_stabilization_after_master_optim!(::NoColGenStab, phase, mast_dual_sol) = false ColGen.get_master_dual_sol(::NoColGenStab, phase, mast_dual_sol) = mast_dual_sol ColGen.check_misprice(::NoColGenStab, generated_cols, mast_dual_sol) = false -ColGen.update_stabilization_after_pricing_optim!(::NoColGenStab, master, valid_db, pseudo_db, mast_dual_sol) = nothing +ColGen.update_stabilization_after_pricing_optim!(::NoColGenStab, ctx::ColGenContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) = nothing ColGen.update_stabilization_after_misprice!(::NoColGenStab, mast_dual_sol) = nothing ColGen.update_stabilization_after_iter!(::NoColGenStab, ctx, master, generated_columns, mast_dual_sol) = nothing ColGen.get_output_str(::NoColGenStab) = 0.0 @@ -55,7 +55,16 @@ function ColGen.get_master_dual_sol(stab::ColGenStab, phase, mast_dual_sol) return stab.cur_α * stab.cur_stab_center + (1 - stab.cur_α) * mast_dual_sol end -function ColGen.update_stabilization_after_pricing_optim!(stab::ColGenStab, master, valid_db, pseudo_db, mast_dual_sol) +function ColGen.update_stabilization_after_pricing_optim!(stab::ColGenStab, ctx::ColGenContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) + # At each iteration, we always update α after the first pricing optimization. + # We don't update α if we are in a misprice sequence. + if stab.automatic && stab.nb_misprices == 0 + is_min = ColGen.is_minimization(ctx) + primal_sol = _primal_solution(master, generated_columns, is_min) + α = _dynamic_alpha_schedule(stab.base_α, mast_dual_sol, stab.cur_stab_center, subgradient_helper(ctx), primal_sol, is_min) + stab.base_α = α + end + if isbetter(DualBound(master, valid_db), stab.valid_dual_bound) stab.cur_stab_center = mast_dual_sol stab.valid_dual_bound = DualBound(master, valid_db) @@ -160,13 +169,6 @@ function _dynamic_alpha_schedule( end function ColGen.update_stabilization_after_iter!(stab::ColGenStab, ctx, master, generated_columns, mast_dual_sol) - if stab.automatic - is_min = ColGen.is_minimization(ctx) - primal_sol = _primal_solution(master, generated_columns, is_min) - α = _dynamic_alpha_schedule(stab.base_α, mast_dual_sol, stab.cur_stab_center, subgradient_helper(ctx), primal_sol, is_min) - stab.base_α = α - end - if !isnothing(stab.stab_center_for_next_iteration) stab.cur_stab_center = stab.stab_center_for_next_iteration stab.stab_center_for_next_iteration = nothing diff --git a/src/ColGen/interface.jl b/src/ColGen/interface.jl index 11158f639..4aab39bc9 100644 --- a/src/ColGen/interface.jl +++ b/src/ColGen/interface.jl @@ -265,9 +265,10 @@ _inf(is_min_sense) = is_min_sense ? Inf : -Inf function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) master = get_master(context) is_min_sense = is_minimization(context) - mast_result = optimize_master_lp_problem!(master, context, env) O = colgen_iteration_output_type(context) + mast_result = optimize_master_lp_problem!(master, context, env) + # Iteration continues only if master is not infeasible nor unbounded and has dual # solution. if is_infeasible(mast_result) @@ -303,20 +304,20 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) mast_dual_sol = get_dual_sol(mast_result) if isnothing(mast_dual_sol) error("Cannot continue") - # error or stop? (depends on the context) + # TODO: user friendly error message. end # 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) + update_master_constrs_dual_vals!(context, phase, get_reform(context), mast_dual_sol) # TODO: rm phase # Compute reduced cost (generic operation) by you must support math operations. - # using the master dual solution. + # using the master dual solution. (remove and do this in misprice loop) c = get_subprob_var_orig_costs(context) A = get_subprob_var_coef_matrix(context) red_costs = c - transpose(A) * mast_dual_sol - update_reduced_costs!(context, phase, red_costs) + update_reduced_costs!(context, phase, red_costs) # buffer cout reduit variable sp # Stabilization stab_changes_mast_dual_sol = update_stabilization_after_master_optim!(stab, phase, mast_dual_sol) @@ -324,8 +325,9 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) # TODO: check the compatibility of the pricing strategy and the stabilization. - # All generated columns will be stored in the following container. We will insert them - # into the master after the optimization of the pricing subproblems. + # All generated columns during this iteration will be stored in the following container. + # We will insert them into the master after the optimization of the pricing subproblems. + # It is empty. generated_columns = set_of_columns(context) valid_db = nothing @@ -380,7 +382,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) throw(UnboundedProblemError("Unbounded subproblem.")) end - check_pricing_termination_status(pricing_result) + check_pricing_termination_status(pricing_result) # TODO: remove primal_sols = get_primal_sols(pricing_result) nb_cols_pushed = 0 @@ -412,7 +414,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) # pseudo dual bound is used for stabilization only. pseudo_db = compute_dual_bound(context, phase, sps_pb, cur_mast_dual_sol) - update_stabilization_after_pricing_optim!(stab, master, valid_db, pseudo_db, mast_dual_sol) + update_stabilization_after_pricing_optim!(stab, context, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) # We have finished to solve all pricing subproblems. # If we have stabilization, we need to check if we have misprice. @@ -430,7 +432,7 @@ 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) + col_ids = insert_columns!(get_reform(context), context, phase, generated_columns) # TODO: remove phase nb_cols_inserted = length(col_ids) # TODO: remove the context from the arguments. diff --git a/src/ColGen/stabilization.jl b/src/ColGen/stabilization.jl index e77523377..34d20a2d9 100644 --- a/src/ColGen/stabilization.jl +++ b/src/ColGen/stabilization.jl @@ -30,10 +30,10 @@ Updates stabilization after pricing optimization where: - `pseudo_db` is the pseudo dual bound of the problem after optimization of the pricing problems - `mast_dual_sol` is the dual solution to the master problem """ -@mustimplement "ColGenStab" update_stabilization_after_pricing_optim!(stab, master, valid_db, pseudo_db, mast_dual_sol) = nothing +@mustimplement "ColGenStab" update_stabilization_after_pricing_optim!(stab, ctx, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) = nothing """ -Updates stabilization after a misprice. +Updates stabilization after a misprice. Argument `mast_dual_sol` is the dual solution to the master problem. """ @mustimplement "ColGenStab" update_stabilization_after_misprice!(stab, mast_dual_sol) = nothing diff --git a/test/unit/ColGen/colgen_stabilization.jl b/test/unit/ColGen/colgen_stabilization.jl index 458549d0c..78eeae444 100644 --- a/test/unit/ColGen/colgen_stabilization.jl +++ b/test/unit/ColGen/colgen_stabilization.jl @@ -379,7 +379,7 @@ ColGen.get_pricing_strategy(::ColGenStabFlowCtx, phase) = ColGenStabFlowPricingS 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) +function ColGen.update_stabilization_after_pricing_optim!(stab::ColGenStabFlowStab, ctx, generated_columns, 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 From 4ff9f61bbf95fc2ddb8d0e7642d215c1883d9412 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Thu, 22 Jun 2023 15:29:01 +0200 Subject: [PATCH 2/5] =?UTF-8?q?ez=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Algorithm/colgen/default.jl | 22 ++++++++++++++++++++ src/Algorithm/colgen/stabilization.jl | 26 ++---------------------- src/ColGen/interface.jl | 2 +- src/ColGen/stabilization.jl | 2 +- test/unit/ColGen/colgen_iteration.jl | 5 +++++ test/unit/ColGen/colgen_stabilization.jl | 2 +- 6 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/Algorithm/colgen/default.jl b/src/Algorithm/colgen/default.jl index cb9c01733..34c00a077 100644 --- a/src/Algorithm/colgen/default.jl +++ b/src/Algorithm/colgen/default.jl @@ -846,3 +846,25 @@ ColGen.get_master_ip_primal_sol(output::ColGenPhaseOutput) = output.master_ip_pr ColGen.get_best_ip_primal_master_sol_found(output::ColGenPhaseOutput) = output.master_lp_primal_sol ColGen.get_final_lp_primal_master_sol_found(output::ColGenPhaseOutput) = output.master_ip_primal_sol ColGen.get_final_db(output::ColGenPhaseOutput) = output.db + +ColGen.update_stabilization_after_pricing_optim!(::NoColGenStab, ctx::ColGenContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) = nothing +function ColGen.update_stabilization_after_pricing_optim!(stab::ColGenStab, ctx::ColGenContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) + # At each iteration, we always update α after the first pricing optimization. + # We don't update α if we are in a misprice sequence. + if stab.automatic && stab.nb_misprices == 0 + is_min = ColGen.is_minimization(ctx) + primal_sol = _primal_solution(master, generated_columns, is_min) + α = _dynamic_alpha_schedule(stab.base_α, mast_dual_sol, stab.cur_stab_center, subgradient_helper(ctx), primal_sol, is_min) + stab.base_α = α + end + + if isbetter(DualBound(master, valid_db), stab.valid_dual_bound) + stab.cur_stab_center = mast_dual_sol + stab.valid_dual_bound = DualBound(master, valid_db) + end + if isbetter(DualBound(master, pseudo_db), stab.pseudo_dual_bound) + stab.stab_center_for_next_iteration = mast_dual_sol + stab.pseudo_dual_bound = DualBound(master, pseudo_db) + end + return +end \ No newline at end of file diff --git a/src/Algorithm/colgen/stabilization.jl b/src/Algorithm/colgen/stabilization.jl index f8364ffd4..b57819a52 100644 --- a/src/Algorithm/colgen/stabilization.jl +++ b/src/Algorithm/colgen/stabilization.jl @@ -3,9 +3,8 @@ struct NoColGenStab end ColGen.update_stabilization_after_master_optim!(::NoColGenStab, phase, mast_dual_sol) = false ColGen.get_master_dual_sol(::NoColGenStab, phase, mast_dual_sol) = mast_dual_sol ColGen.check_misprice(::NoColGenStab, generated_cols, mast_dual_sol) = false -ColGen.update_stabilization_after_pricing_optim!(::NoColGenStab, ctx::ColGenContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) = nothing ColGen.update_stabilization_after_misprice!(::NoColGenStab, mast_dual_sol) = nothing -ColGen.update_stabilization_after_iter!(::NoColGenStab, ctx, master, generated_columns, mast_dual_sol) = nothing +ColGen.update_stabilization_after_iter!(::NoColGenStab, mast_dual_sol) = nothing ColGen.get_output_str(::NoColGenStab) = 0.0 """ Implementation of the "Smoothing with a self adjusting parameter" described in the paper of @@ -55,27 +54,6 @@ function ColGen.get_master_dual_sol(stab::ColGenStab, phase, mast_dual_sol) return stab.cur_α * stab.cur_stab_center + (1 - stab.cur_α) * mast_dual_sol end -function ColGen.update_stabilization_after_pricing_optim!(stab::ColGenStab, ctx::ColGenContext, generated_columns, master, valid_db, pseudo_db, mast_dual_sol) - # At each iteration, we always update α after the first pricing optimization. - # We don't update α if we are in a misprice sequence. - if stab.automatic && stab.nb_misprices == 0 - is_min = ColGen.is_minimization(ctx) - primal_sol = _primal_solution(master, generated_columns, is_min) - α = _dynamic_alpha_schedule(stab.base_α, mast_dual_sol, stab.cur_stab_center, subgradient_helper(ctx), primal_sol, is_min) - stab.base_α = α - end - - if isbetter(DualBound(master, valid_db), stab.valid_dual_bound) - stab.cur_stab_center = mast_dual_sol - stab.valid_dual_bound = DualBound(master, valid_db) - end - if isbetter(DualBound(master, pseudo_db), stab.pseudo_dual_bound) - stab.stab_center_for_next_iteration = mast_dual_sol - stab.pseudo_dual_bound = DualBound(master, pseudo_db) - end - return -end - ColGen.check_misprice(stab::ColGenStab, generated_cols, mast_dual_sol) = length(generated_cols.columns) == 0 && stab.cur_α > 0.0 function _misprice_schedule(automatic, nb_misprices, base_α) @@ -168,7 +146,7 @@ function _dynamic_alpha_schedule( return increase ? f_incr(α) : f_decr(α) end -function ColGen.update_stabilization_after_iter!(stab::ColGenStab, ctx, master, generated_columns, mast_dual_sol) +function ColGen.update_stabilization_after_iter!(stab::ColGenStab, mast_dual_sol) if !isnothing(stab.stab_center_for_next_iteration) stab.cur_stab_center = stab.stab_center_for_next_iteration stab.stab_center_for_next_iteration = nothing diff --git a/src/ColGen/interface.jl b/src/ColGen/interface.jl index 4aab39bc9..c2087ed31 100644 --- a/src/ColGen/interface.jl +++ b/src/ColGen/interface.jl @@ -436,7 +436,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) 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) + update_stabilization_after_iter!(stab, 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) end \ No newline at end of file diff --git a/src/ColGen/stabilization.jl b/src/ColGen/stabilization.jl index 34d20a2d9..b603dd388 100644 --- a/src/ColGen/stabilization.jl +++ b/src/ColGen/stabilization.jl @@ -46,7 +46,7 @@ Updates stabilization after an iteration of the column generation algorithm. Arg - `generated_columns` is the set of generated columns - `mast_dual_sol` is the dual solution to the master problem """ -@mustimplement "ColGenStab" update_stabilization_after_iter!(stab, ctx, master, generated_columns, mast_dual_sol) = nothing +@mustimplement "ColGenStab" update_stabilization_after_iter!(stab, mast_dual_sol) = nothing "Returns a string with a short information about the stabilization." @mustimplement "ColGenStab" get_output_str(stab) = nothing \ No newline at end of file diff --git a/test/unit/ColGen/colgen_iteration.jl b/test/unit/ColGen/colgen_iteration.jl index 68381f358..d651bd1ed 100644 --- a/test/unit/ColGen/colgen_iteration.jl +++ b/test/unit/ColGen/colgen_iteration.jl @@ -216,6 +216,8 @@ function ColGen.compute_dual_bound(::ColGenIterationTestContext, ::ColGenIterati return 22.5 - 23/4 end +ColGen.update_stabilization_after_pricing_optim!(::Coluna.Algorithm.NoColGenStab, ::ColGenIterationTestContext, _, _, _, _, _) = nothing + struct TestColGenIterationOutput <: ColGen.AbstractColGenIterationOutput min_sense::Bool mlp::Union{Nothing, Float64} @@ -266,6 +268,9 @@ function ColGen.new_iteration_output(::Type{<:TestColGenIterationOutput}, ) end +ColGen.update_stabilization_after_pricing_optim!(::Coluna.Algorithm.NoColGenStab, ::TestColGenIterationContext, _, _, _, _, _) = nothing + + function colgen_iteration_master_ok_pricing_ok() ctx = ColGenIterationTestContext() output = ColGen.run_colgen_iteration!(ctx, ColGenIterationTestPhase(), ColGenIterationTestStage(), nothing, nothing, Coluna.Algorithm.NoColGenStab()) diff --git a/test/unit/ColGen/colgen_stabilization.jl b/test/unit/ColGen/colgen_stabilization.jl index 78eeae444..34b1f6b91 100644 --- a/test/unit/ColGen/colgen_stabilization.jl +++ b/test/unit/ColGen/colgen_stabilization.jl @@ -400,7 +400,7 @@ function ColGen.insert_columns!(reform, context::ColGenStabFlowCtx, phase, gener return [] end -function ColGen.update_stabilization_after_iter!(stab::ColGenStabFlowStab, ctx, master, generated_columns, mast_dual_sol) +function ColGen.update_stabilization_after_iter!(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_update_stab_after_iter_done += 1 return true From 7e5420b4edddf534784df513fac3dd4c58a99891 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Thu, 22 Jun 2023 15:33:47 +0200 Subject: [PATCH 3/5] insert_columns! ok --- src/Algorithm/colgen/default.jl | 3 ++- src/Algorithm/colgen/printer.jl | 4 ++-- src/ColGen/interface.jl | 4 +--- test/unit/ColGen/colgen_default.jl | 12 ++++++------ test/unit/ColGen/colgen_iteration.jl | 2 +- test/unit/ColGen/colgen_stabilization.jl | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Algorithm/colgen/default.jl b/src/Algorithm/colgen/default.jl index 34c00a077..5442a2efc 100644 --- a/src/Algorithm/colgen/default.jl +++ b/src/Algorithm/colgen/default.jl @@ -461,7 +461,8 @@ end _set_column_cost!(master, col_id, phase) = nothing _set_column_cost!(master, col_id, ::ColGenPhase1) = setcurcost!(master, col_id, 0.0) -function ColGen.insert_columns!(reform, ctx::ColGenContext, phase, columns) +function ColGen.insert_columns!(ctx::ColGenContext, phase, columns) + reform = ColGen.get_reform(ctx) primal_sols_to_insert = PrimalSolution{Formulation{DwSp}}[] col_ids_to_activate = Set{VarId}() master = ColGen.get_master(ctx) diff --git a/src/Algorithm/colgen/printer.jl b/src/Algorithm/colgen/printer.jl index 72f3b18b9..b72154b92 100644 --- a/src/Algorithm/colgen/printer.jl +++ b/src/Algorithm/colgen/printer.jl @@ -60,8 +60,8 @@ end ColGen.update_reduced_costs!(ctx::ColGenPrinterContext, phase, red_costs) = ColGen.update_reduced_costs!(ctx.inner, phase, red_costs) -function ColGen.insert_columns!(reform, ctx::ColGenPrinterContext, phase, columns) - col_ids = ColGen.insert_columns!(reform, ctx.inner, phase, columns) +function ColGen.insert_columns!(ctx::ColGenPrinterContext, phase, columns) + col_ids = ColGen.insert_columns!(ctx.inner, phase, columns) if ctx.print_column_reduced_cost _print_column_reduced_costs(ColGen.get_reform(ctx), col_ids) end diff --git a/src/ColGen/interface.jl b/src/ColGen/interface.jl index c2087ed31..9ac0b750a 100644 --- a/src/ColGen/interface.jl +++ b/src/ColGen/interface.jl @@ -431,11 +431,9 @@ 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) # TODO: remove phase + col_ids = insert_columns!(context, phase, generated_columns) nb_cols_inserted = length(col_ids) - # TODO: remove the context from the arguments. update_stabilization_after_iter!(stab, 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) diff --git a/test/unit/ColGen/colgen_default.jl b/test/unit/ColGen/colgen_default.jl index 79adad499..74a79d8c6 100644 --- a/test/unit/ColGen/colgen_default.jl +++ b/test/unit/ColGen/colgen_default.jl @@ -714,8 +714,8 @@ ColGen.set_of_columns(ctx::TestColGenIterationContext) = ColGen.set_of_columns(c ColGen.push_in_set!(ctx::TestColGenIterationContext, set, col) = ColGen.push_in_set!(ctx.context, set, col) # Columns insertion -function ColGen.insert_columns!(reform, ctx::TestColGenIterationContext, phase, columns) - return ColGen.insert_columns!(reform, ctx.context, phase, columns) +function ColGen.insert_columns!(ctx::TestColGenIterationContext, phase, columns) + return ColGen.insert_columns!(ctx.context, phase, columns) end function ColGen.optimize_pricing_problem!(ctx::TestColGenIterationContext, sp::Formulation{DwSp}, env, optimizer, master_dual_sol, stab_changes_mast_dual_sol) @@ -1051,7 +1051,7 @@ function test_two_identicals_cols_at_two_iterations_failure() ColGen.push_in_set!(ctx, columns, ClA.GeneratedColumn(sol, cost)) end - new_cols = ColGen.insert_columns!(reform, ctx, phase, columns) + new_cols = ColGen.insert_columns!(ctx, phase, columns) @test length(new_cols) == 1 ## Iteration 2 @@ -1068,7 +1068,7 @@ function test_two_identicals_cols_at_two_iterations_failure() for (cost, sol) in Iterators.zip(redcosts_spsols, [col3]) ColGen.push_in_set!(ctx, columns, ClA.GeneratedColumn(sol, cost)) end - @test_throws ClA.ColumnAlreadyInsertedColGenWarning ColGen.insert_columns!(reform, ctx, phase, columns) + @test_throws ClA.ColumnAlreadyInsertedColGenWarning ColGen.insert_columns!(ctx, phase, columns) end register!(unit_tests, "colgen_default", test_two_identicals_cols_at_two_iterations_failure) @@ -1110,7 +1110,7 @@ function test_two_identicals_cols_at_same_iteration_ok() ColGen.push_in_set!(ctx, columns, ClA.GeneratedColumn(sol, cost)) end - new_cols = ColGen.insert_columns!(reform, ctx, phase, columns) + new_cols = ColGen.insert_columns!(ctx, phase, columns) @test length(new_cols) == 2 end register!(unit_tests, "colgen_default", test_two_identicals_cols_at_same_iteration_ok) @@ -1159,7 +1159,7 @@ function test_deactivated_column_added_twice_at_same_iteration_ok() ColGen.push_in_set!(ctx, columns, ClA.GeneratedColumn(sol, cost)) end - new_cols = ColGen.insert_columns!(reform, ctx, phase, columns) + new_cols = ColGen.insert_columns!(ctx, phase, columns) @test length(new_cols) == 1 end register!(unit_tests, "colgen_default", test_deactivated_column_added_twice_at_same_iteration_ok) diff --git a/test/unit/ColGen/colgen_iteration.jl b/test/unit/ColGen/colgen_iteration.jl index d651bd1ed..c23e86cb4 100644 --- a/test/unit/ColGen/colgen_iteration.jl +++ b/test/unit/ColGen/colgen_iteration.jl @@ -206,7 +206,7 @@ end ColGen.update_master_constrs_dual_vals!(::ColGenIterationTestContext, ::ColGenIterationTestPhase, reform, dual_mast_sol) = nothing -function ColGen.insert_columns!(reform, ::ColGenIterationTestContext, phase, generated_columns) +function ColGen.insert_columns!(::ColGenIterationTestContext, phase, generated_columns) @test length(generated_columns) == 1 @test generated_columns[1] == [0, 1, 1, 0, 1, 1, 0, 0, 1, 0] return [1] diff --git a/test/unit/ColGen/colgen_stabilization.jl b/test/unit/ColGen/colgen_stabilization.jl index 34b1f6b91..1293bd817 100644 --- a/test/unit/ColGen/colgen_stabilization.jl +++ b/test/unit/ColGen/colgen_stabilization.jl @@ -396,7 +396,7 @@ function ColGen.update_stabilization_after_misprice!(stab::ColGenStabFlowStab, m stab.nb_misprices_done += 1 end -function ColGen.insert_columns!(reform, context::ColGenStabFlowCtx, phase, generated_columns) +function ColGen.insert_columns!(context::ColGenStabFlowCtx, phase, generated_columns) return [] end From abc9efc4e3dc9f31aec1e749ee74084b01fc3dfe Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Thu, 22 Jun 2023 15:39:10 +0200 Subject: [PATCH 4/5] ok --- src/Algorithm/colgen/default.jl | 2 +- src/Algorithm/colgen/printer.jl | 4 +-- src/ColGen/interface.jl | 32 ++++++++---------------- test/unit/ColGen/colgen_default.jl | 2 +- test/unit/ColGen/colgen_iteration.jl | 2 +- test/unit/ColGen/colgen_stabilization.jl | 2 +- 6 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/Algorithm/colgen/default.jl b/src/Algorithm/colgen/default.jl index 5442a2efc..73f90ea4b 100644 --- a/src/Algorithm/colgen/default.jl +++ b/src/Algorithm/colgen/default.jl @@ -388,7 +388,7 @@ ColGen.get_primal_sol(master_res::ColGenMasterResult) = get_best_lp_primal_sol(m ColGen.get_dual_sol(master_res::ColGenMasterResult) = get_best_lp_dual_sol(master_res.result) ColGen.get_obj_val(master_res::ColGenMasterResult) = get_lp_primal_bound(master_res.result) -function ColGen.update_master_constrs_dual_vals!(ctx::ColGenContext, phase, reform, master_lp_dual_sol) +function ColGen.update_master_constrs_dual_vals!(ctx::ColGenContext, master_lp_dual_sol) master = ColGen.get_master(ctx) # Set all dual value of all constraints to 0. for constr in Iterators.values(getconstrs(master)) diff --git a/src/Algorithm/colgen/printer.jl b/src/Algorithm/colgen/printer.jl index b72154b92..5ef8e43cc 100644 --- a/src/Algorithm/colgen/printer.jl +++ b/src/Algorithm/colgen/printer.jl @@ -44,8 +44,8 @@ function ColGen.optimize_master_lp_problem!(master, ctx::ColGenPrinterContext, e return output end -function ColGen.update_master_constrs_dual_vals!(ctx::ColGenPrinterContext, phase, reform, master_lp_dual_sol) - return ColGen.update_master_constrs_dual_vals!(ctx.inner, phase, reform, master_lp_dual_sol) +function ColGen.update_master_constrs_dual_vals!(ctx::ColGenPrinterContext, master_lp_dual_sol) + return ColGen.update_master_constrs_dual_vals!(ctx.inner, master_lp_dual_sol) end ColGen.check_primal_ip_feasibility!(mast_primal_sol, ctx::ColGenPrinterContext, phase, reform, env) = ColGen.check_primal_ip_feasibility!(mast_primal_sol, ctx.inner, phase, reform, env) diff --git a/src/ColGen/interface.jl b/src/ColGen/interface.jl index 9ac0b750a..f79748578 100644 --- a/src/ColGen/interface.jl +++ b/src/ColGen/interface.jl @@ -173,7 +173,7 @@ Updates the current master IP primal solution. Updates dual value of the master constraints. Dual values of the constraints can be used when the pricing solver supports non-robust cuts. """ -@mustimplement "ColGenReducedCosts" update_master_constrs_dual_vals!(ctx, phase, reform, mast_lp_dual_sol) = nothing +@mustimplement "ColGenReducedCosts" update_master_constrs_dual_vals!(ctx, mast_lp_dual_sol) = nothing """ Updates reduced costs of the master variables. @@ -207,16 +207,6 @@ if something unexpected happens. @mustimplement "ColGen" insert_columns!(reform, ctx, phase, columns) = nothing -function check_master_termination_status(mast_result) - if !is_infeasible(mast_result) && !is_unbounded(mast_result) - @assert !isnothing(get_dual_sol(mast_result)) - end -end - -function check_pricing_termination_status(pricing_result) - # TODO -end - """ compute_dual_bound(ctx, phase, master_lp_obj_val, master_dbs, mast_dual_sol) -> Float64 @@ -277,8 +267,6 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) throw(UnboundedProblemError("Unbounded master problem.")) end - check_master_termination_status(mast_result) - # Master primal solution mast_primal_sol = get_primal_sol(mast_result) if !isnothing(mast_primal_sol) && isbetter(mast_primal_sol, ip_primal_sol) @@ -297,7 +285,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) end if !isnothing(new_ip_primal_sol) ip_primal_sol = new_ip_primal_sol - update_inc_primal_sol!(context, ip_primal_sol) # TODO: change method name because the incumbent is maintained by colgen + update_inc_primal_sol!(context, ip_primal_sol) end end @@ -309,15 +297,20 @@ 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) # TODO: rm phase + update_master_constrs_dual_vals!(context, mast_dual_sol) # Compute reduced cost (generic operation) by you must support math operations. - # using the master dual solution. (remove and do this in misprice loop) + # We always compute the reduced costs of the subproblem variables against the real master + # dual solution because this is the cost of the subproblem variables in the pricing problems + # if we don't use stabilization, or because we use this cost to compute the real reduced cost + # of the columns when using stabilization. c = get_subprob_var_orig_costs(context) A = get_subprob_var_coef_matrix(context) red_costs = c - transpose(A) * mast_dual_sol - update_reduced_costs!(context, phase, red_costs) # buffer cout reduit variable sp + + # Buffer when using stabilization to compute the real reduced cost + # of the column once generated. + update_reduced_costs!(context, phase, red_costs) # Stabilization stab_changes_mast_dual_sol = update_stabilization_after_master_optim!(stab, phase, mast_dual_sol) @@ -382,8 +375,6 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) throw(UnboundedProblemError("Unbounded subproblem.")) end - check_pricing_termination_status(pricing_result) # TODO: remove - primal_sols = get_primal_sols(pricing_result) nb_cols_pushed = 0 for primal_sol in primal_sols # multi column generation support. @@ -425,7 +416,6 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) if misprice update_stabilization_after_misprice!(stab, mast_dual_sol) cur_mast_dual_sol = get_master_dual_sol(stab, phase, mast_dual_sol) - generated_columns = set_of_columns(context) # TODO: not sure because seems redoundant: no cols in set => misprice end end diff --git a/test/unit/ColGen/colgen_default.jl b/test/unit/ColGen/colgen_default.jl index 74a79d8c6..c796e30f0 100644 --- a/test/unit/ColGen/colgen_default.jl +++ b/test/unit/ColGen/colgen_default.jl @@ -694,7 +694,7 @@ end ColGen.check_primal_ip_feasibility!(master_lp_primal_sol, ::TestColGenIterationContext, phase, reform, env) = nothing, false ColGen.is_unbounded(ctx::TestColGenIterationContext) = ColGen.is_unbounded(ctx.context) ColGen.is_infeasible(ctx::TestColGenIterationContext) = ColGen.is_infeasible(ctx.context) -ColGen.update_master_constrs_dual_vals!(ctx::TestColGenIterationContext, phase, reform, master_lp_dual_sol) = ColGen.update_master_constrs_dual_vals!(ctx.context, phase, reform, master_lp_dual_sol) +ColGen.update_master_constrs_dual_vals!(ctx::TestColGenIterationContext, master_lp_dual_sol) = ColGen.update_master_constrs_dual_vals!(ctx.context, master_lp_dual_sol) ColGen.update_reduced_costs!(ctx::TestColGenIterationContext, phase, red_costs) = nothing ColGen.get_subprob_var_orig_costs(ctx::TestColGenIterationContext) = ColGen.get_subprob_var_orig_costs(ctx.context) ColGen.get_subprob_var_coef_matrix(ctx::TestColGenIterationContext) = ColGen.get_subprob_var_coef_matrix(ctx.context) diff --git a/test/unit/ColGen/colgen_iteration.jl b/test/unit/ColGen/colgen_iteration.jl index c23e86cb4..0329bff72 100644 --- a/test/unit/ColGen/colgen_iteration.jl +++ b/test/unit/ColGen/colgen_iteration.jl @@ -204,7 +204,7 @@ function ColGen.update_inc_primal_sol!(::ColGenIterationTestContext, sol::Vector @test sol == [7.0, 7.0, 7.0] end -ColGen.update_master_constrs_dual_vals!(::ColGenIterationTestContext, ::ColGenIterationTestPhase, reform, dual_mast_sol) = nothing +ColGen.update_master_constrs_dual_vals!(::ColGenIterationTestContext, dual_mast_sol) = nothing function ColGen.insert_columns!(::ColGenIterationTestContext, phase, generated_columns) @test length(generated_columns) == 1 diff --git a/test/unit/ColGen/colgen_stabilization.jl b/test/unit/ColGen/colgen_stabilization.jl index 1293bd817..06d9cee15 100644 --- a/test/unit/ColGen/colgen_stabilization.jl +++ b/test/unit/ColGen/colgen_stabilization.jl @@ -362,7 +362,7 @@ 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.update_master_constrs_dual_vals!(::ColGenStabFlowCtx, 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 From 8ce28a1c591b9bc8d9e186089dbf2254e1b1ebbf Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Thu, 22 Jun 2023 15:41:21 +0200 Subject: [PATCH 5/5] ok --- src/Algorithm/colgen/default.jl | 4 ++-- src/Algorithm/colgen/printer.jl | 2 +- src/ColGen/interface.jl | 4 ++-- test/unit/ColGen/colgen_default.jl | 2 +- test/unit/ColGen/colgen_iteration.jl | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Algorithm/colgen/default.jl b/src/Algorithm/colgen/default.jl index 73f90ea4b..1904325c2 100644 --- a/src/Algorithm/colgen/default.jl +++ b/src/Algorithm/colgen/default.jl @@ -415,9 +415,9 @@ function _violates_essential_cuts!(master, master_lp_primal_sol, env) return cutcb_output.nb_cuts_added > 0 end -ColGen.check_primal_ip_feasibility!(_, ctx::ColGenContext, ::ColGenPhase1, _, _) = nothing, false +ColGen.check_primal_ip_feasibility!(_, ctx::ColGenContext, ::ColGenPhase1, _) = nothing, false -function ColGen.check_primal_ip_feasibility!(master_lp_primal_sol, ctx::ColGenContext, phase, reform, env) +function ColGen.check_primal_ip_feasibility!(master_lp_primal_sol, ctx::ColGenContext, phase, env) # Check if feasible. if contains(master_lp_primal_sol, varid -> isanArtificialDuty(getduty(varid))) return nothing, false diff --git a/src/Algorithm/colgen/printer.jl b/src/Algorithm/colgen/printer.jl index 5ef8e43cc..e525a43c7 100644 --- a/src/Algorithm/colgen/printer.jl +++ b/src/Algorithm/colgen/printer.jl @@ -48,7 +48,7 @@ function ColGen.update_master_constrs_dual_vals!(ctx::ColGenPrinterContext, mast return ColGen.update_master_constrs_dual_vals!(ctx.inner, master_lp_dual_sol) end -ColGen.check_primal_ip_feasibility!(mast_primal_sol, ctx::ColGenPrinterContext, phase, reform, env) = ColGen.check_primal_ip_feasibility!(mast_primal_sol, ctx.inner, phase, reform, env) +ColGen.check_primal_ip_feasibility!(mast_primal_sol, ctx::ColGenPrinterContext, phase, env) = ColGen.check_primal_ip_feasibility!(mast_primal_sol, ctx.inner, phase, env) ColGen.update_inc_primal_sol!(ctx::ColGenPrinterContext, ip_primal_sol) = ColGen.update_inc_primal_sol!(ctx.inner, ip_primal_sol) ColGen.get_subprob_var_orig_costs(ctx::ColGenPrinterContext) = ColGen.get_subprob_var_orig_costs(ctx.inner) diff --git a/src/ColGen/interface.jl b/src/ColGen/interface.jl index f79748578..029ea38b8 100644 --- a/src/ColGen/interface.jl +++ b/src/ColGen/interface.jl @@ -154,7 +154,7 @@ See `optimize_master_lp_problem!`. Returns a primal solution expressed in the original problem variables if the current master LP solution is integer feasible; `nothing` otherwise. """ -@mustimplement "ColGenMaster" check_primal_ip_feasibility!(mast_lp_primal_sol, ::AbstractColGenContext, phase, reform, env) = nothing +@mustimplement "ColGenMaster" check_primal_ip_feasibility!(mast_lp_primal_sol, ::AbstractColGenContext, phase, env) = nothing """ Returns `true` if the new master IP primal solution is better than the current; `false` otherwise. @@ -279,7 +279,7 @@ function run_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) # 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) + new_ip_primal_sol, new_cut_in_master = check_primal_ip_feasibility!(mast_primal_sol, context, phase, 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) end diff --git a/test/unit/ColGen/colgen_default.jl b/test/unit/ColGen/colgen_default.jl index c796e30f0..52a1743ac 100644 --- a/test/unit/ColGen/colgen_default.jl +++ b/test/unit/ColGen/colgen_default.jl @@ -691,7 +691,7 @@ function ColGen.optimize_master_lp_problem!(master, ctx::TestColGenIterationCont return output end -ColGen.check_primal_ip_feasibility!(master_lp_primal_sol, ::TestColGenIterationContext, phase, reform, env) = nothing, false +ColGen.check_primal_ip_feasibility!(master_lp_primal_sol, ::TestColGenIterationContext, phase, env) = nothing, false ColGen.is_unbounded(ctx::TestColGenIterationContext) = ColGen.is_unbounded(ctx.context) ColGen.is_infeasible(ctx::TestColGenIterationContext) = ColGen.is_infeasible(ctx.context) ColGen.update_master_constrs_dual_vals!(ctx::TestColGenIterationContext, master_lp_dual_sol) = ColGen.update_master_constrs_dual_vals!(ctx.context, master_lp_dual_sol) diff --git a/test/unit/ColGen/colgen_iteration.jl b/test/unit/ColGen/colgen_iteration.jl index 0329bff72..597a579ad 100644 --- a/test/unit/ColGen/colgen_iteration.jl +++ b/test/unit/ColGen/colgen_iteration.jl @@ -185,7 +185,7 @@ end ColGen.update_reduced_costs!(::ColGenIterationTestContext, phase, red_costs) = nothing -function ColGen.check_primal_ip_feasibility!(sol, ctx::ColGenIterationTestContext, ::ColGenIterationTestPhase, reform, env) +function ColGen.check_primal_ip_feasibility!(sol, ctx::ColGenIterationTestContext, ::ColGenIterationTestPhase, env) if ctx.new_ip_primal_sol @assert !ctx.master_has_new_cuts return [7.0, 7.0, 7.0], false