diff --git a/src/Algorithm/colgen.jl b/src/Algorithm/colgen.jl index ad1d045b0..3e4ea55e1 100644 --- a/src/Algorithm/colgen.jl +++ b/src/Algorithm/colgen.jl @@ -142,10 +142,14 @@ function run!(algo::ColumnGeneration, env::Env, reform::Reformulation, input::Op update_ip_primal_sol!(optstate, result.master_ip_primal_sol) end + if !isnothing(result.master_lp_dual_sol) + update_lp_dual_sol!(optstate, result.master_lp_dual_sol) + end + if !isnothing(result.db) set_lp_dual_bound!(optstate, DualBound(master, result.db)) set_ip_dual_bound!(optstate, DualBound(master, result.db)) - end + end return optstate end diff --git a/src/Algorithm/colgen/default.jl b/src/Algorithm/colgen/default.jl index e60cd57f2..cce45c132 100644 --- a/src/Algorithm/colgen/default.jl +++ b/src/Algorithm/colgen/default.jl @@ -53,6 +53,7 @@ ColGen.get_pricing_subprobs(ctx::ColGenContext) = get_dw_pricing_sps(ctx.reform) struct ColGenPhaseOutput <: ColGen.AbstractColGenPhaseOutput master_lp_primal_sol::Union{Nothing,PrimalSolution} master_ip_primal_sol::Union{Nothing,PrimalSolution} + master_lp_dual_sol::Union{Nothing,DualSolution} mlp::Union{Nothing, Float64} db::Union{Nothing, Float64} new_cut_in_master::Bool @@ -61,6 +62,7 @@ end struct ColGenOutput <: ColGen.AbstractColGenOutput master_lp_primal_sol::Union{Nothing,PrimalSolution} master_ip_primal_sol::Union{Nothing,PrimalSolution} + master_lp_dual_sol::Union{Nothing,DualSolution} mlp::Union{Nothing, Float64} db::Union{Nothing, Float64} end @@ -68,7 +70,8 @@ end function ColGen.new_output(::Type{<:ColGenOutput}, output::ColGenPhaseOutput) return ColGenOutput( output.master_lp_primal_sol, - output.master_ip_primal_sol, + output.master_ip_primal_sol, + output.master_lp_dual_sol, output.mlp, output.db ) @@ -486,6 +489,7 @@ struct ColGenIterationOutput <: ColGen.AbstractColGenIterationOutput time_limit_reached::Bool master_lp_primal_sol::Union{Nothing, PrimalSolution} master_ip_primal_sol::Union{Nothing, PrimalSolution} + master_lp_dual_sol::Union{Nothing, DualSolution} end ColGen.colgen_iteration_output_type(::ColGenContext) = ColGenIterationOutput @@ -502,7 +506,8 @@ function ColGen.new_iteration_output(::Type{<:ColGenIterationOutput}, unbounded_subproblem, time_limit_reached, master_lp_primal_sol, - master_ip_primal_sol + master_ip_primal_sol, + master_lp_dual_sol ) return ColGenIterationOutput( min_sense, @@ -516,7 +521,8 @@ function ColGen.new_iteration_output(::Type{<:ColGenIterationOutput}, unbounded_subproblem, time_limit_reached, master_lp_primal_sol, - master_ip_primal_sol + master_ip_primal_sol, + master_lp_dual_sol ) end @@ -556,6 +562,7 @@ function ColGen.new_phase_output(::Type{<:ColGenPhaseOutput}, colgen_iter_output return ColGenPhaseOutput( colgen_iter_output.master_lp_primal_sol, colgen_iter_output.master_ip_primal_sol, + colgen_iter_output.master_lp_dual_sol, colgen_iter_output.mlp, colgen_iter_output.db, colgen_iter_output.new_cut_in_master diff --git a/src/ColGen/interface.jl b/src/ColGen/interface.jl index 15d4a4a6c..a37ad3652 100644 --- a/src/ColGen/interface.jl +++ b/src/ColGen/interface.jl @@ -224,7 +224,8 @@ abstract type AbstractColGenIterationOutput end unbounded_subproblem, time_limit_reached, master_primal_sol, - ip_primal_sol + ip_primal_sol, + dual_sol ) = nothing @mustimplement "ColGenIterationOutput" get_nb_new_cols(::AbstractColGenIterationOutput) = nothing @@ -249,7 +250,7 @@ function run_colgen_iteration!(context, phase, env, ip_primal_sol) # Iteration continues only if master is not infeasible nor unbounded and has dual # solution. if is_infeasible(mast_result) - return new_iteration_output(O, is_min_sense, nothing, _inf(is_min_sense), 0, false, true, false, false, false, false, nothing, nothing) + return new_iteration_output(O, is_min_sense, nothing, _inf(is_min_sense), 0, false, true, false, false, false, false, nothing, nothing, nothing) elseif is_unbounded(mast_result) throw(UnboundedProblemError("Unbounded master problem.")) end @@ -269,7 +270,7 @@ function run_colgen_iteration!(context, phase, env, ip_primal_sol) # memoization to calculate reduced costs and stabilization. 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, ip_primal_sol) + return new_iteration_output(O, is_min_sense, nothing, nothing, 0, true, false, false, false, false, false, nothing, ip_primal_sol, nothing) end if !isnothing(new_ip_primal_sol) && isbetter(new_ip_primal_sol, ip_primal_sol) ip_primal_sol = new_ip_primal_sol @@ -332,7 +333,7 @@ function run_colgen_iteration!(context, phase, env, ip_primal_sol) # Iteration continues only if the pricing solution is not infeasible nor unbounded. if is_infeasible(pricing_result) # TODO: if the lower multiplicity of the subproblem is zero, we can continue. - return new_iteration_output(O, is_min_sense, nothing, _inf(is_min_sense), 0, false, false, false, true, false, false, mast_primal_sol, ip_primal_sol) + return new_iteration_output(O, is_min_sense, nothing, _inf(is_min_sense), 0, false, false, false, true, false, false, mast_primal_sol, ip_primal_sol, mast_dual_sol) elseif is_unbounded(pricing_result) # We do not support unbounded pricing (even if it's theorically possible). # We must stop Coluna here by throwing an exception because we can't claim @@ -376,6 +377,6 @@ function run_colgen_iteration!(context, phase, env, ip_primal_sol) # update_stab_after_gencols! - return new_iteration_output(O, is_min_sense, master_lp_obj_val, valid_db, nb_cols_inserted, false, false, false, false, false, false, mast_primal_sol, ip_primal_sol) + return new_iteration_output(O, is_min_sense, master_lp_obj_val, valid_db, nb_cols_inserted, false, false, false, false, false, false, mast_primal_sol, ip_primal_sol, mast_dual_sol) end diff --git a/test/.222-revise-exit-code b/test/.222-revise-exit-code new file mode 100644 index 000000000..6dd90d24d --- /dev/null +++ b/test/.222-revise-exit-code @@ -0,0 +1 @@ +222 \ No newline at end of file diff --git a/test/unit/ColGen/colgen_iteration.jl b/test/unit/ColGen/colgen_iteration.jl index da7378bed..44c38ff54 100644 --- a/test/unit/ColGen/colgen_iteration.jl +++ b/test/unit/ColGen/colgen_iteration.jl @@ -220,6 +220,7 @@ struct TestColGenIterationOutput <: ColGen.AbstractColGenIterationOutput time_limit_reached::Bool master_lp_primal_sol::Union{Nothing, Vector{Float64}} master_ip_primal_sol::Union{Nothing, Vector{Float64}} + master_lp_dual_sol::Union{Nothing, Vector{Float64}} end ColGen.colgen_iteration_output_type(::ColGenIterationTestContext) = TestColGenIterationOutput @@ -236,7 +237,8 @@ function ColGen.new_iteration_output(::Type{<:TestColGenIterationOutput}, unbounded_subproblem, time_limit_reached, master_lp_primal_sol, - master_ip_primal_sol + master_ip_primal_sol, + master_lp_dual_sol ) return TestColGenIterationOutput( min_sense, @@ -250,7 +252,8 @@ function ColGen.new_iteration_output(::Type{<:TestColGenIterationOutput}, unbounded_subproblem, time_limit_reached, master_lp_primal_sol, - master_ip_primal_sol + master_ip_primal_sol, + master_lp_dual_sol ) end diff --git a/test/unit/ColGen/colgen_phase.jl b/test/unit/ColGen/colgen_phase.jl index 577b27821..69c5d4a19 100644 --- a/test/unit/ColGen/colgen_phase.jl +++ b/test/unit/ColGen/colgen_phase.jl @@ -129,6 +129,7 @@ function stop_colgen_phase_if_colgen_converged_eq() false, false, nothing, + nothing, nothing ) @@ -155,6 +156,7 @@ function stop_colgen_phase_if_colgen_converged_min() false, false, nothing, + nothing, nothing ) @@ -181,6 +183,7 @@ function stop_colgen_phase_if_colgen_converged_max() false, false, nothing, + nothing, nothing ) @@ -207,6 +210,7 @@ function stop_colgen_phase_if_iterations_limit() false, false, nothing, + nothing, nothing ) @@ -233,6 +237,7 @@ function stop_colgen_phase_if_time_limit() false, true, nothing, + nothing, nothing ) @@ -259,6 +264,7 @@ function stop_colgen_phase_if_subproblem_infeasible() false, false, nothing, + nothing, nothing ) @@ -285,6 +291,7 @@ function stop_colgen_phase_if_subproblem_unbounded() true, false, nothing, + nothing, nothing ) @@ -311,6 +318,7 @@ function stop_colgen_phase_if_master_unbounded() false, false, nothing, + nothing, nothing ) @@ -337,6 +345,7 @@ function stop_colgen_phase_if_no_new_column() false, false, nothing, + nothing, nothing ) @test ColGen.stop_colgen_phase(ctx, ClA.ColGenPhase1(), env, colgen_iter_output, colgen_iteration, cutsep_iteration) @@ -362,6 +371,7 @@ function stop_colgen_phase_if_new_cut_in_master() false, false, nothing, + nothing, nothing ) @test ColGen.stop_colgen_phase(ctx, ClA.ColGenPhase3(), env, colgen_iter_output, colgen_iteration, cutsep_iteration) @@ -387,6 +397,7 @@ function continue_colgen_phase_otherwise() false, false, nothing, + nothing, nothing ) @test !ColGen.stop_colgen_phase(ctx, ClA.ColGenPhase1(), env, colgen_iter_output, colgen_iteration, cutsep_iteration) diff --git a/test/unit/ColGen/colgen_printer.jl b/test/unit/ColGen/colgen_printer.jl index b8bc13817..96e547791 100644 --- a/test/unit/ColGen/colgen_printer.jl +++ b/test/unit/ColGen/colgen_printer.jl @@ -12,6 +12,7 @@ function printer_colgen_iteration_master_ok_pricing_ok() false, false, nothing, + nothing, nothing ) expected_str = " " @@ -33,6 +34,7 @@ function printer_colgen_iteration_master_infeasible() false, false, nothing, + nothing, nothing ) expected_str = " - infeasible master" @@ -54,6 +56,7 @@ function printer_colgen_iteration_pricing_infeasible() false, false, nothing, + nothing, nothing ) expected_str = " - infeasible subproblem" @@ -75,6 +78,7 @@ function printer_colgen_iteration_master_unbounded() false, false, nothing, + nothing, nothing ) expected_str = "" @@ -96,6 +100,7 @@ function printer_colgen_iteration_pricing_unbounded() true, false, nothing, + nothing, nothing ) expected_str = " - unbounded subproblem" @@ -139,6 +144,7 @@ function printer_colgen_new_cuts_in_master() false, false, nothing, + nothing, nothing ) expected_str = " "