From 2d66d58888db6bc2c3a448957eaff9f7672fef4c Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Tue, 10 Mar 2020 11:21:15 +0100 Subject: [PATCH 1/4] masterIpHeur -> IpForm --- src/Algorithm/Algorithm.jl | 4 +- src/Algorithm/conquer.jl | 2 +- src/Algorithm/interface.jl | 2 +- src/Algorithm/ipform.jl | 58 ++++++++++++++++++++++++++++ src/Algorithm/masteripheur.jl | 39 ------------------- src/Containers/nestedenum.jl | 6 ++- src/MathProg/new_varconstr.jl | 23 ++++------- test/runtests.jl | 3 +- test/unit/algorithms/masteripheur.jl | 6 ++- 9 files changed, 82 insertions(+), 61 deletions(-) create mode 100644 src/Algorithm/ipform.jl delete mode 100644 src/Algorithm/masteripheur.jl diff --git a/src/Algorithm/Algorithm.jl b/src/Algorithm/Algorithm.jl index b3ad8be13..086da95c6 100644 --- a/src/Algorithm/Algorithm.jl +++ b/src/Algorithm/Algorithm.jl @@ -36,7 +36,7 @@ include("record.jl") # Here include slave algorithms used by conquer algorithms include("colgen.jl") include("benders.jl") -include("masteripheur.jl") +include("ipform.jl") include("masterlp.jl") include("preprocessing.jl") @@ -57,7 +57,7 @@ include("treesearch.jl") # Types export AbstractOptimizationAlgorithm, TreeSearchAlgorithm, ColGenConquer, ColumnGeneration, - BendersConquer, BendersCutGeneration, MasterIpHeuristic, ExactBranchingPhase, + BendersConquer, BendersCutGeneration, IpForm, ExactBranchingPhase, OnlyRestrictedMasterBranchingPhase end \ No newline at end of file diff --git a/src/Algorithm/conquer.jl b/src/Algorithm/conquer.jl index 7a25b228f..7f9cf7cfd 100644 --- a/src/Algorithm/conquer.jl +++ b/src/Algorithm/conquer.jl @@ -108,7 +108,7 @@ end Base.@kwdef struct ColGenConquer <: AbstractConquerAlgorithm colgen::ColumnGeneration = ColumnGeneration() - mastipheur::MasterIpHeuristic = MasterIpHeuristic() + mastipheur::IpForm = IpForm() preprocess::PreprocessAlgorithm = PreprocessAlgorithm() run_mastipheur::Bool = true run_preprocessing::Bool = false diff --git a/src/Algorithm/interface.jl b/src/Algorithm/interface.jl index 0170f73ec..3b6044a0d 100644 --- a/src/Algorithm/interface.jl +++ b/src/Algorithm/interface.jl @@ -95,7 +95,7 @@ abstract type AbstractOptimizationAlgorithm <: AbstractAlgorithm end function run!( algo::AbstractOptimizationAlgorithm, form::AbstractFormulation, input::OptimizationInput -)::OldOutput +)::OptimizationOutput algotype = typeof(algo) error("Method run! which takes formulation and Incumbents as input returns OldOutput is not implemented for algorithm $algotype.") diff --git a/src/Algorithm/ipform.jl b/src/Algorithm/ipform.jl new file mode 100644 index 000000000..86ebdbdfb --- /dev/null +++ b/src/Algorithm/ipform.jl @@ -0,0 +1,58 @@ +""" + IpForm + +todo +Deactivate artificial variables and solve formulation with optimizer +""" +Base.@kwdef struct IpForm <: AbstractOptimizationAlgorithm + time_limit::Int = 600 +end + +# TODO : content of OptimizationInput ? +# +# struct IpFormInput <: AbstractOptimizationInput +# initial_primal_bound +# end + +function run!(algo::IpForm, form::Formulation, input::OptimizationInput)::OptimizationOutput + @logmsg LogLevel(1) "Algorithm IpForm" + initincumb = getincumbents(input) + optresult = OptimizationResult(form, initincumb) + optimizer = getoptimizer(form).inner + + if MOI.supports_constraint(optimizer, MOI.SingleVariable, MOI.Integer) + MOI.set(optimizer, MOI.TimeLimitSec(), algo.time_limit) + # No way to enforce upper bound through MOI. + # Add a constraint c'x <= UB in form ? + + deactivate!(form, vcid -> isanArtificialDuty(getduty(vcid))) + enforce_integrality!(form) + moiresult = optimize!(form) + + relax_integrality!(form) + activate!(form, vcid -> isanArtificialDuty(getduty(vcid))) + + setfeasibilitystatus!(optresult, getfeasibilitystatus(moiresult)) + setterminationstatus!(optresult, getterminationstatus(moiresult)) + + bestprimalsol = getbestprimalsol(moiresult) + if bestprimalsol !== nothing + add_ip_primal_sol!(optresult, bestprimalsol) + + @logmsg LogLevel(1) string( + "Found primal solution of ", + @sprintf "%.4f" getvalue(get_ip_primal_bound(optresult)) + ) + @logmsg LogLevel(-3) get_best_ip_primal_sol(optresult) + else + @logmsg LogLevel(1) string( + "No primal solution found. Termination status is ", + getterminationstatus(optresult), ". Feasibility status is ", + getfeasibilitystatus(optresult), "." + ) + end + return OptimizationOutput(optresult) + end + @warn "Optimizer of formulation with id =", getuid(form) ," does not support integer variables. Skip IpForm algorithm." + return OptimizationOutput(optresult) +end \ No newline at end of file diff --git a/src/Algorithm/masteripheur.jl b/src/Algorithm/masteripheur.jl deleted file mode 100644 index de936b03e..000000000 --- a/src/Algorithm/masteripheur.jl +++ /dev/null @@ -1,39 +0,0 @@ -struct MasterIpHeuristic <: AbstractOptimizationAlgorithm end - -function run!(algo::MasterIpHeuristic, reform::Reformulation, input::OptimizationInput)::OptimizationOutput - @logmsg LogLevel(1) "Applying Master IP heuristic" - S = getobjsense(reform) - initincumb = getincumbents(input) - master = getmaster(reform) - - opt_result = OptimizationResult(master, initincumb) - if MOI.supports_constraint(getoptimizer(master).inner, MOI.SingleVariable, MOI.Integer) - # TO DO : enforce here the upper bound and maximum solution time - - deactivate!(master, MasterArtVar) - enforce_integrality!(master) - moi_result = optimize!(master) - - relax_integrality!(master) - activate!(master, MasterArtVar) - - setfeasibilitystatus!(opt_result, getfeasibilitystatus(moi_result)) - setterminationstatus!(opt_result, getterminationstatus(moi_result)) - - bestprimalsol = getbestprimalsol(moi_result) - if bestprimalsol !== nothing - add_ip_primal_sol!(opt_result, bestprimalsol) - end - - @logmsg LogLevel(1) string( - "Found primal solution of ", - @sprintf "%.4f" getvalue(get_ip_primal_bound(opt_result)) - ) - @logmsg LogLevel(-3) get_best_primal_sol(opt_result) - - return OptimizationOutput(opt_result) - end - - @warn "Master optimizer does not support integer variables. Skip Restricted IP Master Heuristic." - return OptimizationOutput(opt_result) -end diff --git a/src/Containers/nestedenum.jl b/src/Containers/nestedenum.jl index f0dc652a9..ddddb14a8 100644 --- a/src/Containers/nestedenum.jl +++ b/src/Containers/nestedenum.jl @@ -1,9 +1,13 @@ abstract type NestedEnum end -function Base.:(<=)(a::T, b::T) where {T <: NestedEnum} +function Base.:(<=)(a::T, b::T) where {T<:NestedEnum} return a.value % b.value == 0 end +function Base.:(<=)(a::T, b::U) where {T<:NestedEnum,U<:NestedEnum} + return false +end + # Store the item defined in expr at position i function _store!(expr::Symbol, i, names, parent_pos, depths) names[i] = expr diff --git a/src/MathProg/new_varconstr.jl b/src/MathProg/new_varconstr.jl index 33200f493..02a76fcfb 100644 --- a/src/MathProg/new_varconstr.jl +++ b/src/MathProg/new_varconstr.jl @@ -257,7 +257,7 @@ function _setiscuractive!(form::Formulation, constrid::ConstrId, is_active::Bool return end -"Activates a variable in the formulation" +"Activate a variable in the formulation" function activate!(form::Formulation, varconstrid::Id{VC}) where {VC<:AbstractVarConstr} if iscurexplicit(form, varconstrid) add!(form.buffer, varconstrid) @@ -267,20 +267,18 @@ function activate!(form::Formulation, varconstrid::Id{VC}) where {VC<:AbstractVa end activate!(form::Formulation, varconstr::AbstractVarConstr) = activate!(form, getid(varconstr)) -function activate!(form::Formulation, duty::Duty{Variable}) +function activate!(form::Formulation, f::Function) for (varid, _) in getvars(form) - if iscuractive(form, varid) && getduty(varid) <= duty + if iscuractive(form, varid) && f(varid) activate!(form, varid) end end -end - -function activate!(form::Formulation, duty::Duty{Constraint}) for (constrid, _) in getconstrs(form) - if iscuractive(form, constrid) && getduty(constrid) <= duty + if iscuractive(form, constrid) && f(constrid) activate!(form, constrid) end end + return end """ @@ -295,25 +293,20 @@ function deactivate!(form::Formulation, varconstrid::Id{VC}) where {VC<:Abstract end deactivate!(form::Formulation, varconstr::AbstractVarConstr) = deactivate!(form, getid(varconstr)) -function deactivate!(form::Formulation, duty::Duty{Variable}) +function deactivate!(form::Formulation, f::Function) for (varid, _) in getvars(form) - if iscuractive(form, varid) && getduty(varid) <= duty + if iscuractive(form, varid) && f(varid) deactivate!(form, varid) end end - return -end - -function deactivate!(form::Formulation, duty::Duty{Constraint}) for (constrid, _) in getconstrs(form) - if iscuractive(form, constrid) && getduty(constrid) <= duty + if iscuractive(form, constrid) && f(constrid) deactivate!(form, constrid) end end return end - ## explicit """ todo diff --git a/test/runtests.jl b/test/runtests.jl index f272bb177..206802e2d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,7 +15,8 @@ global const CL = Coluna global const CLD = ColunaDemos global const BD = BlockDecomposition -global const ClF = Coluna.MathProg +global const ClF = Coluna.MathProg # Must be deleted +global const ClMP = Coluna.MathProg global const ClA = Coluna.Algorithm include("unit/unit_tests.jl") diff --git a/test/unit/algorithms/masteripheur.jl b/test/unit/algorithms/masteripheur.jl index 525192a46..b7b43bb1d 100644 --- a/test/unit/algorithms/masteripheur.jl +++ b/test/unit/algorithms/masteripheur.jl @@ -12,6 +12,10 @@ end # return # end +function ClA.run!(alg::ClA.IpForm, reform::ClMP.Reformulation, input::ClA.OptimizationInput) + return ClA.run!(alg, ClMP.getmaster(reform), input) +end + function infeasible_master_ip_heur_tests() @testset "play gap" begin data = CLD.GeneralizedAssignment.data("play2.txt") @@ -20,7 +24,7 @@ function infeasible_master_ip_heur_tests() coluna = JuMP.optimizer_with_attributes( Coluna.Optimizer, "params" => CL.Params( - solver = ClA.MasterIpHeuristic() + solver = ClA.IpForm() ), "default_optimizer" => GLPK.Optimizer ) From c21d20c652df12ba011157afebca17707a24d92a Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Tue, 10 Mar 2020 11:28:04 +0100 Subject: [PATCH 2/4] future input in comment --- src/Algorithm/ipform.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Algorithm/ipform.jl b/src/Algorithm/ipform.jl index 86ebdbdfb..e3c4b9434 100644 --- a/src/Algorithm/ipform.jl +++ b/src/Algorithm/ipform.jl @@ -11,7 +11,7 @@ end # TODO : content of OptimizationInput ? # # struct IpFormInput <: AbstractOptimizationInput -# initial_primal_bound +# incumbents::ObjValues{S} # end function run!(algo::IpForm, form::Formulation, input::OptimizationInput)::OptimizationOutput From 258570ad6956160c983241665c7e2bfbc816bd24 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Tue, 10 Mar 2020 14:13:38 +0100 Subject: [PATCH 3/4] rm use of incumbents --- src/Algorithm/benders.jl | 4 +- src/Algorithm/colgen.jl | 4 +- src/Algorithm/ipform.jl | 16 ++++---- src/MathProg/MathProg.jl | 2 +- src/MathProg/optimizationresults.jl | 57 ++++++++++++++-------------- src/MathProg/solutions.jl | 47 ++++++++++++++++++++++- src/optimize.jl | 5 ++- test/unit/algorithms/masteripheur.jl | 14 ++----- 8 files changed, 92 insertions(+), 57 deletions(-) diff --git a/src/Algorithm/benders.jl b/src/Algorithm/benders.jl index 93ad7bcaa..b69976318 100644 --- a/src/Algorithm/benders.jl +++ b/src/Algorithm/benders.jl @@ -46,8 +46,8 @@ function run!(algo::BendersCutGeneration, reform::Reformulation, input::Optimiza result = OptimizationResult( getmaster(reform), - data.is_feasible ? FEASIBLE : INFEASIBLE, - data.has_converged ? OPTIMAL : OTHER_LIMIT, + feasibility_status = data.is_feasible ? FEASIBLE : INFEASIBLE, + termination_status = data.has_converged ? OPTIMAL : OTHER_LIMIT, ip_dual_bound = get_ip_dual_bound(data.incumbents), lp_dual_bound = get_lp_dual_bound(data.incumbents) ) diff --git a/src/Algorithm/colgen.jl b/src/Algorithm/colgen.jl index 539688b60..511ed190f 100644 --- a/src/Algorithm/colgen.jl +++ b/src/Algorithm/colgen.jl @@ -53,8 +53,8 @@ function run!(algo::ColumnGeneration, reform::Reformulation, input::Optimization result = OptimizationResult( masterform, - data.is_feasible ? FEASIBLE : INFEASIBLE, - data.has_converged ? OPTIMAL : OTHER_LIMIT, + feasibility_status = data.is_feasible ? FEASIBLE : INFEASIBLE, + termination_status = data.has_converged ? OPTIMAL : OTHER_LIMIT, ip_primal_bound = get_ip_primal_bound(data.incumbents), ip_dual_bound = get_lp_dual_bound(data.incumbents), # TODO : check if objective function is integer lp_dual_bound = get_lp_dual_bound(data.incumbents) diff --git a/src/Algorithm/ipform.jl b/src/Algorithm/ipform.jl index e3c4b9434..30b17e9bf 100644 --- a/src/Algorithm/ipform.jl +++ b/src/Algorithm/ipform.jl @@ -8,16 +8,16 @@ Base.@kwdef struct IpForm <: AbstractOptimizationAlgorithm time_limit::Int = 600 end -# TODO : content of OptimizationInput ? -# -# struct IpFormInput <: AbstractOptimizationInput -# incumbents::ObjValues{S} -# end +struct IpFormInput{S} <: AbstractInput + incumbents::ObjValues{S} +end -function run!(algo::IpForm, form::Formulation, input::OptimizationInput)::OptimizationOutput +function run!(algo::IpForm, form::Formulation, input::IpFormInput)::OptimizationOutput @logmsg LogLevel(1) "Algorithm IpForm" - initincumb = getincumbents(input) - optresult = OptimizationResult(form, initincumb) + + optresult = OptimizationResult( + form, ip_primal_bound = get_ip_primal_bound(input.incumbents) + ) optimizer = getoptimizer(form).inner if MOI.supports_constraint(optimizer, MOI.SingleVariable, MOI.Integer) diff --git a/src/MathProg/MathProg.jl b/src/MathProg/MathProg.jl index 8c31b3a9f..31e4f46cc 100644 --- a/src/MathProg/MathProg.jl +++ b/src/MathProg/MathProg.jl @@ -137,7 +137,7 @@ export getperenecost, reset! # methods related to solutions -export OptimizationResult +export OptimizationResult, ObjValues export getterminationstatus, getfeasibilitystatus, diff --git a/src/MathProg/optimizationresults.jl b/src/MathProg/optimizationresults.jl index a31f1d70d..47eafc458 100644 --- a/src/MathProg/optimizationresults.jl +++ b/src/MathProg/optimizationresults.jl @@ -118,13 +118,6 @@ end ### END : TO BE DELETED - -""" - OptimizationResult{M,S} - - Structure to be returned by all Coluna `optimize!` methods. -""" -# TO DO : Optimization result should include information about both IP and LP solutions mutable struct OptimizationResult{F<:AbstractFormulation,S<:Coluna.AbstractSense} termination_status::TerminationStatus feasibility_status::FeasibilityStatus @@ -136,8 +129,10 @@ mutable struct OptimizationResult{F<:AbstractFormulation,S<:Coluna.AbstractSense lp_dual_sols::Union{Nothing, Vector{DualSolution{F}}} end - """ + OptimizationResult{M,S} +Structure to be returned by all Coluna `optimize!` methods. + OptimizationResult(model) Builds an empty OptimizationResult. @@ -152,29 +147,25 @@ function OptimizationResult(model::M) where {M<:Coluna.Containers.AbstractModel} end function OptimizationResult( - form::F, fs::FeasibilityStatus, ts::TerminationStatus; - ip_primal_bound::Union{Nothing,PrimalBound} = nothing, - ip_dual_bound::Union{Nothing,DualBound} = nothing, - lp_primal_bound::Union{Nothing,PrimalBound} = nothing, - lp_dual_bound::Union{Nothing,DualBound} = nothing + form::F; + feasibility_status::FeasibilityStatus = UNKNOWN_FEASIBILITY, + termination_status::TerminationStatus = NOT_YET_DETERMINED, + ip_primal_bound = nothing, + ip_dual_bound = nothing, + lp_primal_bound = nothing, + lp_dual_bound = nothing ) where {F <: AbstractFormulation} - incumbents = ObjValues(form) - if ip_primal_bound !== nothing - set_ip_primal_bound!(incumbents, ip_primal_bound) - end - if ip_dual_bound !== nothing - set_ip_dual_bound!(incumbents, ip_dual_bound) - end - if lp_primal_bound !== nothing - set_lp_primal_bound!(incumbents, lp_primal_bound) - end - if lp_dual_bound !== nothing - set_lp_dual_bound!(incumbents, lp_dual_bound) - end + incumbents = ObjValues( + form; + ip_primal_bound = ip_primal_bound, + ip_dual_bound = ip_dual_bound, + lp_primal_bound = lp_primal_bound, + lp_dual_bound = lp_dual_bound + ) S = getobjsense(form) result = OptimizationResult{F,S}( - ts, fs, PrimalBound(form), DualBound(form), incumbents, - nothing, nothing, nothing + termination_status, feasibility_status, PrimalBound(form), DualBound(form), + incumbents, nothing, nothing, nothing ) if ip_primal_bound !== nothing setprimalbound!(result, ip_primal_bound) @@ -189,7 +180,9 @@ end function OptimizationResult( form::F, incumbents::Incumbents ) where {F} - ov = OptimizationResult(form, UNKNOWN_FEASIBILITY, NOT_YET_DETERMINED; + ov = OptimizationResult(form, + feasibility_status = UNKNOWN_FEASIBILITY, + termination_status = NOT_YET_DETERMINED, ip_primal_bound = get_ip_primal_bound(incumbents), ip_dual_bound = get_ip_dual_bound(incumbents)) setprimalbound!(ov, get_ip_primal_bound(incumbents)) @@ -320,6 +313,9 @@ function add_lp_dual_sol!(res::OptimizationResult{F,S}, solution::DualSolution{F end function add_primal_sol!(res::OptimizationResult{M,S}, solution::PrimalSolution{M}) where {M,S} + if res.lp_primal_sols === nothing + res.lp_primal_sols = PrimalSolution{M}[] + end push!(res.lp_primal_sols, solution) pb = PrimalBound{S}(getvalue(solution)) if isbetter(pb, getprimalbound(res)) @@ -330,6 +326,9 @@ function add_primal_sol!(res::OptimizationResult{M,S}, solution::PrimalSolution{ end function add_dual_sol!(res::OptimizationResult{M,S}, solution::DualSolution{M}) where {M,S} + if res.lp_dual_sols === nothing + res.lp_dual_sols = DualSolution{M}[] + end push!(res.lp_dual_sols, solution) db = DualBound{S}(getvalue(solution)) if isbetter(db, getdualbound(res)) diff --git a/src/MathProg/solutions.jl b/src/MathProg/solutions.jl index a532238cb..39f74128e 100644 --- a/src/MathProg/solutions.jl +++ b/src/MathProg/solutions.jl @@ -16,6 +16,18 @@ function PrimalBound(form::AbstractFormulation, val::Float64) return Coluna.Containers.Bound{Primal,Se}(val) end +function PrimalBound(form::AbstractFormulation, pb::PrimalBound{S}) where {S} + Se = getobjsense(form) + if Se != S + msg = """ + Cannot create primal bound. + Sense of the formulation is $Se and sense of the bound is $S. + """ + error(msg) + end + return Coluna.Containers.Bound{Primal,Se}(getvalue(pb)) +end + function PrimalSolution(form::M) where {M} return Coluna.Containers.Solution{M,VarId,Float64}(form) end @@ -36,6 +48,18 @@ function DualBound(form::AbstractFormulation, val::Float64) return Coluna.Containers.Bound{Dual,Se}(val) end +function DualBound(form::AbstractFormulation, db::DualBound{S}) where {S} + Se = getobjsense(form) + if Se != S + msg = """ + Cannot create primal bound. + Sense of the formulation is $Se and sense of the bound is $S. + """ + error(msg) + end + return Coluna.Containers.Bound{Dual,Se}(getvalue(db)) +end + function DualSolution(form::M) where {M} return Coluna.Containers.Solution{M,ConstrId,Float64}(form) end @@ -72,11 +96,30 @@ end todo """ -function ObjValues(form::M) where {M<:AbstractFormulation} +function ObjValues( + form::M; + ip_primal_bound = nothing, + ip_dual_bound = nothing, + lp_primal_bound = nothing, + lp_dual_bound = nothing +) where {M<:AbstractFormulation} S = getobjsense(form) - return ObjValues{S}( + ov = ObjValues{S}( PrimalBound(form), DualBound(form), PrimalBound(form), DualBound(form) ) + if ip_primal_bound !== nothing + set_ip_primal_bound!(ov, PrimalBound(form, ip_primal_bound)) + end + if ip_dual_bound !== nothing + set_ip_dual_bound!(ov, DualBound(ip_dual_bound)) + end + if lp_primal_bound !== nothing + set_lp_primal_bound!(ov, PrimalBound(lp_primal_bound)) + end + if lp_dual_bound !== nothing + set_lp_dual_bound!(ov, DualBound(lp_dual_bound)) + end + return ov end ## Getters bounds diff --git a/src/optimize.jl b/src/optimize.jl index d20575ded..433b96d86 100644 --- a/src/optimize.jl +++ b/src/optimize.jl @@ -99,8 +99,9 @@ function optimize!( opt_result = AL.getresult(output) result = OptimizationResult( - master, getfeasibilitystatus(opt_result), - getterminationstatus(opt_result), + master, + feasibility_status = getfeasibilitystatus(opt_result), + termination_status = getterminationstatus(opt_result), ip_primal_bound = get_ip_primal_bound(opt_result), ip_dual_bound = get_ip_dual_bound(opt_result), lp_primal_bound = get_lp_primal_bound(opt_result), diff --git a/test/unit/algorithms/masteripheur.jl b/test/unit/algorithms/masteripheur.jl index b7b43bb1d..0f9517f94 100644 --- a/test/unit/algorithms/masteripheur.jl +++ b/test/unit/algorithms/masteripheur.jl @@ -2,18 +2,10 @@ function masteripheur_tests() infeasible_master_ip_heur_tests() end -#CL.to_be_pruned(n::CL.Node) = true # issue 166 - -# struct InfeasibleMasterIpHeur <: ClA.AbstractConquerAlgorithm end - -# function ClA.run!(strategy::InfeasibleMasterIpHeur, reform, node) -# # Apply directly master ip heuristic => infeasible -# mip_rec = ClA.run!(ClA.MasterIpHeuristic(), reform, node) -# return -# end - function ClA.run!(alg::ClA.IpForm, reform::ClMP.Reformulation, input::ClA.OptimizationInput) - return ClA.run!(alg, ClMP.getmaster(reform), input) + master = ClMP.getmaster(reform) + ipforminput = ClA.IpFormInput(ClMP.ObjValues(master)) + return ClA.run!(alg, master, ipforminput) end function infeasible_master_ip_heur_tests() From b901787b0fe5419477146bbba96645e725b08324 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Tue, 10 Mar 2020 15:45:04 +0100 Subject: [PATCH 4/4] solve colgen sp with ipform + try things with Logging --- src/Algorithm/colgen.jl | 34 +++++++------ src/Algorithm/ipform.jl | 106 ++++++++++++++++++++++++++++------------ 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/src/Algorithm/colgen.jl b/src/Algorithm/colgen.jl index 511ed190f..a5d605b7b 100644 --- a/src/Algorithm/colgen.jl +++ b/src/Algorithm/colgen.jl @@ -185,32 +185,36 @@ function solve_sp_to_gencol!( # Solve sub-problem and insert generated columns in master # @logmsg LogLevel(-3) "optimizing pricing prob" + ipform = IpForm(deactivate_artificial_vars = false, enforce_integrality = false, log_level = 2) TO.@timeit Coluna._to "Pricing subproblem" begin - opt_result = optimize!(spform) + sp_output = run!(ipform, spform, IpFormInput(ObjValues(spform))) end + sp_result = getresult(sp_output) pricing_db_contrib = compute_pricing_db_contrib( - spform, getprimalbound(opt_result), sp_lb, sp_ub + spform, get_ip_primal_bound(sp_result), sp_lb, sp_ub ) - if !isfeasible(opt_result) + if !isfeasible(sp_result) sp_is_feasible = false # @logmsg LogLevel(-3) "pricing prob is infeasible" return sp_is_feasible, recorded_solution_ids, PrimalBound(spform) end - for sol in getprimalsols(opt_result) - if contrib_improves_mlp(getobjsense(spform), getvalue(sol)) # has negative reduced cost - insertion_status, col_id = setprimalsol!(spform, sol) - if insertion_status - push!(recorded_solution_ids, col_id) - elseif !insertion_status && !iscuractive(masterform, col_id) - push!(sp_solution_ids_to_activate, col_id) - else - msg = """ - Column already exists as $(getname(masterform, col_id)) and is already active. - """ - @warn string(msg) + if nb_ip_primal_sols(sp_result) > 0 + for sol in get_ip_primal_sols(sp_result) + if contrib_improves_mlp(getobjsense(spform), getvalue(sol)) # has negative reduced cost + insertion_status, col_id = setprimalsol!(spform, sol) + if insertion_status + push!(recorded_solution_ids, col_id) + elseif !insertion_status && !iscuractive(masterform, col_id) + push!(sp_solution_ids_to_activate, col_id) + else + msg = """ + Column already exists as $(getname(masterform, col_id)) and is already active. + """ + @warn string(msg) + end end end end diff --git a/src/Algorithm/ipform.jl b/src/Algorithm/ipform.jl index 30b17e9bf..d4a724bc5 100644 --- a/src/Algorithm/ipform.jl +++ b/src/Algorithm/ipform.jl @@ -2,57 +2,101 @@ IpForm todo -Deactivate artificial variables and solve formulation with optimizer +Solve ip formulation """ Base.@kwdef struct IpForm <: AbstractOptimizationAlgorithm time_limit::Int = 600 + deactivate_artificial_vars = true + enforce_integrality = true + log_level = 1 end struct IpFormInput{S} <: AbstractInput incumbents::ObjValues{S} end + +# TO DO : create an Algorithm Logger +# function Logging.shouldlog(logger::ConsoleLogger, level, _module, group, id) +# println("*******") +# @show level _module group id +# println("log = ", get(logger.message_limits, id, 1)) +# println("******") +# return get(logger.message_limits, id, 1) > 0 +# end + function run!(algo::IpForm, form::Formulation, input::IpFormInput)::OptimizationOutput + logger = ConsoleLogger(stderr, LogLevel(algo.log_level)) + with_logger(logger) do + return run_ipform!(algo, form, input) + end +end + +function run_ipform!(algo::IpForm, form::Formulation, input::IpFormInput)::OptimizationOutput @logmsg LogLevel(1) "Algorithm IpForm" - optresult = OptimizationResult( + algoresult = OptimizationResult( form, ip_primal_bound = get_ip_primal_bound(input.incumbents) ) - optimizer = getoptimizer(form).inner - if MOI.supports_constraint(optimizer, MOI.SingleVariable, MOI.Integer) - MOI.set(optimizer, MOI.TimeLimitSec(), algo.time_limit) - # No way to enforce upper bound through MOI. - # Add a constraint c'x <= UB in form ? + ip_supported = check_if_optimizer_supports_ip(getoptimizer(form)) + if !ip_supported + @warn "Optimizer of formulation with id =", getuid(form) ," does not support integer variables. Skip IpForm algorithm." + return OptimizationOutput(algoresult) + end + + optimizer_result = optimize_ip_form!(algo, getoptimizer(form), form) + + setfeasibilitystatus!(algoresult, getfeasibilitystatus(optimizer_result)) + setterminationstatus!(algoresult, getterminationstatus(optimizer_result)) + + bestprimalsol = getbestprimalsol(optimizer_result) + if bestprimalsol !== nothing + add_ip_primal_sol!(algoresult, bestprimalsol) + + @logmsg LogLevel(1) string( + "Found primal solution of ", + @sprintf "%.4f" getvalue(get_ip_primal_bound(algoresult)) + ) + @logmsg LogLevel(-3) get_best_ip_primal_sol(algoresult) + else + @logmsg LogLevel(1) string( + "No primal solution found. Termination status is ", + getterminationstatus(algoresult), ". Feasibility status is ", + getfeasibilitystatus(algoresult), "." + ) + end + return OptimizationOutput(algoresult) +end + +function check_if_optimizer_supports_ip(optimizer::MoiOptimizer) + return MOI.supports_constraint(optimizer.inner, MOI.SingleVariable, MOI.Integer) +end +check_if_optimizer_supports_ip(optimizer::UserOptimizer) = true + +function optimize_ip_form!(algo::IpForm, optimizer::MoiOptimizer, form::Formulation) + MOI.set(optimizer.inner, MOI.TimeLimitSec(), algo.time_limit) + # No way to enforce upper bound through MOI. + # Add a constraint c'x <= UB in form ? + if algo.deactivate_artificial_vars deactivate!(form, vcid -> isanArtificialDuty(getduty(vcid))) + end + if algo.enforce_integrality enforce_integrality!(form) - moiresult = optimize!(form) + end + optimizer_result = optimize!(form) + + if algo.enforce_integrality relax_integrality!(form) + end + if algo.deactivate_artificial_vars activate!(form, vcid -> isanArtificialDuty(getduty(vcid))) - - setfeasibilitystatus!(optresult, getfeasibilitystatus(moiresult)) - setterminationstatus!(optresult, getterminationstatus(moiresult)) - - bestprimalsol = getbestprimalsol(moiresult) - if bestprimalsol !== nothing - add_ip_primal_sol!(optresult, bestprimalsol) - - @logmsg LogLevel(1) string( - "Found primal solution of ", - @sprintf "%.4f" getvalue(get_ip_primal_bound(optresult)) - ) - @logmsg LogLevel(-3) get_best_ip_primal_sol(optresult) - else - @logmsg LogLevel(1) string( - "No primal solution found. Termination status is ", - getterminationstatus(optresult), ". Feasibility status is ", - getfeasibilitystatus(optresult), "." - ) - end - return OptimizationOutput(optresult) end - @warn "Optimizer of formulation with id =", getuid(form) ," does not support integer variables. Skip IpForm algorithm." - return OptimizationOutput(optresult) + return optimizer_result +end + +function optimize_ip_form!(algo::IpForm, optimizer::UserOptimizer, form::Formulation) + return optimize!(form) end \ No newline at end of file