diff --git a/src/Algorithm/branching/branchingalgo.jl b/src/Algorithm/branching/branchingalgo.jl index 06281402d..6f27ef2a9 100644 --- a/src/Algorithm/branching/branchingalgo.jl +++ b/src/Algorithm/branching/branchingalgo.jl @@ -279,6 +279,10 @@ function Branching.eval_child_of_candidate!(child, phase::Branching.AbstractStro restore_from_records!(units_to_restore, child.records) conquer_input = ConquerInputFromSb(global_primal_handler, child, units_to_restore) child.conquer_output = run!(Branching.get_conquer(phase), env, reform, conquer_input) + child.ip_dual_bound = get_lp_dual_bound(child.conquer_output) + for sol in get_ip_primal_sols(child.conquer_output) + store_ip_primal_sol!(global_primal_handler, sol) + end TreeSearch.set_records!(child, create_records(reform)) end diff --git a/src/Algorithm/branching/printer.jl b/src/Algorithm/branching/printer.jl index e36fa9448..0d41c7d8f 100644 --- a/src/Algorithm/branching/printer.jl +++ b/src/Algorithm/branching/printer.jl @@ -30,9 +30,9 @@ function new_phase_context( return PhasePrinter(inner_ctx, phase_index) end -function Branching.perform_branching_phase!(candidates, cand_children, phase::PhasePrinter, sb_state, env, reform, input) +function Branching.perform_branching_phase!(candidates, cand_children, phase::PhasePrinter, env, reform, input) println("**** Strong branching phase ", phase.phase_index, " is started *****"); - scores = Branching.perform_branching_phase_inner!(cand_children, phase, sb_state, env, reform, input) + scores = Branching.perform_branching_phase_inner!(cand_children, phase, env, reform, input) for (candidate, children, score) in Iterators.zip(candidates, cand_children, scores) @printf "SB phase %i branch on %+10s" phase.phase_index Branching.getdescription(candidate) @printf " (lhs=%.4f) : [" Branching.get_lhs(candidate) diff --git a/src/Algorithm/colgen/printer.jl b/src/Algorithm/colgen/printer.jl index c2cbf5f4f..014ad7dcd 100644 --- a/src/Algorithm/colgen/printer.jl +++ b/src/Algorithm/colgen/printer.jl @@ -55,7 +55,10 @@ function ColGen.update_master_constrs_dual_vals!(ctx::ColGenPrinterContext, mast end 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, new_ip_primal_sol) = ColGen.update_inc_primal_sol!(ctx.inner, ip_primal_sol, new_ip_primal_sol) +function ColGen.update_inc_primal_sol!(ctx::ColGenPrinterContext, ip_primal_sol, new_ip_primal_sol) + @info "Improving primal solution with value $(ColunaBase.getvalue(new_ip_primal_sol)) is found during column generation" + ColGen.update_inc_primal_sol!(ctx.inner, ip_primal_sol, new_ip_primal_sol) +end ColGen.get_subprob_var_orig_costs(ctx::ColGenPrinterContext) = ColGen.get_subprob_var_orig_costs(ctx.inner) ColGen.get_subprob_var_coef_matrix(ctx::ColGenPrinterContext) = ColGen.get_subprob_var_coef_matrix(ctx.inner) diff --git a/src/Algorithm/conquer.jl b/src/Algorithm/conquer.jl index f4601f8a9..bf0dec42c 100644 --- a/src/Algorithm/conquer.jl +++ b/src/Algorithm/conquer.jl @@ -92,6 +92,7 @@ struct ColCutGenConquer <: AbstractConquerAlgorithm max_nb_cut_rounds::Int # TODO : tailing-off ? opt_atol::Float64# TODO : force this value in an init() method opt_rtol::Float64 # TODO : force this value in an init() method + verbose::Bool end ColCutGenConquer(; @@ -103,7 +104,8 @@ ColCutGenConquer(; cutgen = CutCallbacks(), max_nb_cut_rounds = 3, opt_atol = AlgoAPI.default_opt_atol(), - opt_rtol = AlgoAPI.default_opt_rtol() + opt_rtol = AlgoAPI.default_opt_rtol(), + verbose = true ) = ColCutGenConquer( colgen, primal_heuristics, @@ -113,7 +115,8 @@ ColCutGenConquer(; cutgen, max_nb_cut_rounds, opt_atol, - opt_rtol + opt_rtol, + verbose ) # ColCutGenConquer does not use any storage unit for the moment, therefore @@ -261,11 +264,18 @@ function run_heuristics!(ctx::ColCutGenContext, heuristics, env, reform, input, records = create_records(reform) end + prev_primal_bound = get_ip_primal_bound(conquer_output) + heuristic_output = run!(heuristic.algorithm, env, reform, conquer_output) - update!(conquer_output, heuristic_output) - # for sol in Heuristic.get_primal_sols(output) - # store_ip_primal_sol!(get_global_primal_handler(input), sol) - # end + add_ip_primal_sols!(conquer_output, get_ip_primal_sols(heuristic_output)...) + update_ip_dual_bound!(conquer_output, get_ip_dual_bound(heuristic_output)) + + if ctx.params.verbose + curr_primal_bound = get_ip_primal_bound(conquer_output) + if curr_primal_bound != prev_primal_bound + @info "Heuristic $(heuristic.name) found improving primal solution with value $(curr_primal_bound.value)" + end + end if ismanager(heuristic.algorithm) restore_from_records!(input.units_to_restore, records) diff --git a/src/Algorithm/heuristic/restricted_master.jl b/src/Algorithm/heuristic/restricted_master.jl index 07eb5b9b5..09c10321e 100644 --- a/src/Algorithm/heuristic/restricted_master.jl +++ b/src/Algorithm/heuristic/restricted_master.jl @@ -21,12 +21,6 @@ function get_child_algorithms(algo::RestrictedMasterHeuristic, reform::Reformula return child_algs end -# struct RestrictedMasterHeuristicOutput <: Heuristic.AbstractHeuristicOutput -# ip_primal_sols::Vector{PrimalSolution} -# end - -# Heuristic.get_primal_sols(o::RestrictedMasterHeuristicOutput) = o.ip_primal_sols - function run!(algo::RestrictedMasterHeuristic, env, reform, input::OptimizationState) master = getmaster(reform) ip_form_output = run!(algo.solve_ip_form_alg, env, master, input) diff --git a/src/ColGen/ColGen.jl b/src/ColGen/ColGen.jl index 6c1f7fe6b..0ce41d1c8 100644 --- a/src/ColGen/ColGen.jl +++ b/src/ColGen/ColGen.jl @@ -137,8 +137,7 @@ 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") - # TODO: user friendly error message. + error("Column generation interrupted: LP solver did not return an optimal dual solution") end # Stores dual solution in the constraint. This is used when the pricing solver supports