Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix empty optimization result + test of infeasible ip restricted master #173

Merged
merged 4 commits into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/algorithms/masteripheur.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ function run!(::Type{MasterIpHeuristic}, form, node, strategy_rec, params)
@logmsg LogLevel(1) "Applying Master IP heuristic"
master = getmaster(form)
algorithm_data = MasterIpHeuristicData(getobjsense(master))
deactivate!(master, MasterArtVar)
enforce_integrality!(master)
opt_result = optimize!(master)
relax_integrality!(master)
activate!(master, MasterArtVar)
set_ip_primal_sol!(algorithm_data.incumbents, getbestprimalsol(opt_result))
@logmsg LogLevel(1) string("Found primal solution of ", get_ip_primal_bound(algorithm_data.incumbents))
@logmsg LogLevel(-3) get_ip_primal_sol(algorithm_data.incumbents)
Expand Down
34 changes: 32 additions & 2 deletions src/formulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,43 @@ function deactivate!(f::Formulation, varconstr::AbstractVarConstr)
end
deactivate!(f::Formulation, id::Id) = deactivate!(f, getelem(f, id))

function deactivate!(f::Formulation, Duty::Type{<:AbstractVarDuty})
vars = filter(id_v -> get_cur_is_active(id_v[2]) && getduty(id_v[2]) <: Duty, getvars(f))
for (id, var) in vars
deactivate!(f, var)
end
return
end

function deactivate!(f::Formulation, Duty::Type{<:AbstractConstrDuty})
constrs = filter(id_c -> get_cur_is_active(id_c[2]) && getduty(id_c[2]) <: Duty, getconstrs(f))
for (id, constr) in constrs
deactivate!(f, constr)
end
return
end

"Activates a variable in the formulation"
function activate!(f::Formulation, id::Id)
varconstr = getelem(f, id)
function activate!(f::Formulation, varconstr::AbstractVarConstr)
add!(f.buffer, varconstr)
set_cur_is_active(varconstr, true)
return
end
activate!(f::Formulation, id::Id) = activate!(f, getelem(f, id))

function activate!(f::Formulation, Duty::Type{<:AbstractVarDuty})
vars = filter(id_v -> !get_cur_is_active(id_v[2]) && getduty(id_v[2]) <: Duty, getvars(f))
for (id, var) in vars
activate!(f, var)
end
end

function activate!(f::Formulation, Duty::Type{<:AbstractConstrDuty})
constrs = filter(id_c -> !get_cur_is_active(id_c[2]) && getduty(id_c[2]) <: Duty, getconstrs(f))
for (id, constr) in constrs
activate!(f, constr)
end
end

function addprimalspsol!(f::Formulation, var::Variable)
return addprimalspsol!(f.manager, var)
Expand Down
3 changes: 3 additions & 0 deletions src/incumbents.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ function set_ip_primal_sol!(inc::Incumbents{S},
end
return false
end
set_ip_primal_sol!(inc::Incumbents, ::Nothing) = false

"""
Updates the best primal solution to the linear program if the new one is better
Expand All @@ -88,6 +89,7 @@ function set_lp_primal_sol!(inc::Incumbents{S},
end
return false
end
set_lp_primal_sol!(inc::Incumbents, ::Nothing) = false

"""
Updates the dual bound of the mixed-integer program if the new one is better than
Expand Down Expand Up @@ -123,6 +125,7 @@ function set_lp_dual_sol!(inc::Incumbents{S},
end
return false
end
set_lp_dual_sol!(inc::Incumbents, ::Nothing) = false

"Updates the fields of `dest` that are worse than those of `src`."
function set!(dest::Incumbents{S}, src::Incumbents{S}) where {S}
Expand Down
4 changes: 2 additions & 2 deletions src/optimizationresults.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ getprimalsols(res::OptimizationResult) = res.primal_sols
getdualsols(res::OptimizationResult) = res.dual_sols
nbprimalsols(res::OptimizationResult) = length(res.primal_sols)
nbdualsols(res::OptimizationResult) = length(res.dual_sols)
getbestprimalsol(res::OptimizationResult) = res.primal_sols[1]
getbestdualsol(res::OptimizationResult) = res.dual_sols[1]
getbestprimalsol(res::OptimizationResult) = get(res.primal_sols, 1, nothing)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should leave it to the user to ask if there is a solution available in case he is not sure.
We could add a function issolavailable
Anyway after calling getbestsol he will need to check if the return value was nothing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add nbprimalsol() and nbdualsol()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are already there..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what is the best behavior? In any case, the user should assure himself that there is at least one solution available, it he uses directly the result of the new getbestsol he might also get in trouble

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine for now if it returns nothing. I added new methods to handle this case e.g.
set_lp_primal_sol!(inc::Incumbents, ::Nothing) = false. If the user works on the solution, he will have to test that solution != nothing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means we will have to add methods for functions that expect a solution all over the place.

getbestdualsol(res::OptimizationResult) = get(res.dual_sols, 1, nothing)
setprimalbound!(res::OptimizationResult, b::PrimalBound) = res.primal_bound = b
setdualbound!(res::OptimizationResult, b::DualBound) = res.dual_bound = b
setterminationstatus!(res::OptimizationResult, status::TerminationStatus) = res.termination_status = status
Expand Down
5 changes: 5 additions & 0 deletions src/optimizerwrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ function optimize!(form::Formulation, optimizer::MoiOptimizer)
sync_solver!(getoptimizer(form), form)
@logmsg LogLevel(-3) "MOI formulation after synch: "
@logmsg LogLevel(-3) getoptimizer(form)
nbvars = MOI.get(form.optimizer.inner, MOI.NumberOfVariables())
if nbvars <= 0
@warn "No variable in the formulation. Coluna does not call the solver."
return retrieve_result(form, optimizer)
end
call_moi_optimize_with_silence(form.optimizer)
status = MOI.get(form.optimizer.inner, MOI.TerminationStatus())
@logmsg LogLevel(-2) string("Optimization finished with status: ", status)
Expand Down
3 changes: 3 additions & 0 deletions test/unit/algorithms/algorithm.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
include("masteripheur.jl")

function algorithm_unit_tests()
algorithm_fallbacks_tests()
masteripheur_tests()
end

function algorithm_fallbacks_tests()
Expand Down
32 changes: 32 additions & 0 deletions test/unit/algorithms/masteripheur.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
function masteripheur_tests()
infeasible_master_ip_heur_tests()
end

CL.to_be_pruned(n::CL.Node) = true # issue 166

struct InfeasibleMasterIpHeur <: CL.AbstractConquerStrategy end

function CL.apply!(::Type{InfeasibleMasterIpHeur}, reform, node, strategy_rec::CL.StrategyRecord, params)
# Apply directly master ip heuristic => infeasible
mip_rec = CL.apply!(CL.MasterIpHeuristic, reform, node, strategy_rec, params)
return
end

function infeasible_master_ip_heur_tests()
@testset "play gap" begin
data = CLD.GeneralizedAssignment.data("play2.txt")

coluna = JuMP.with_optimizer(
Coluna.Optimizer,
params = CL.Params(
global_strategy = CL.GlobalStrategy(InfeasibleMasterIpHeur, CL.NoBranching, CL.DepthFirst)
),
default_optimizer = with_optimizer(GLPK.Optimizer)
)

problem, x, dec = CLD.GeneralizedAssignment.model(data, coluna)

JuMP.optimize!(problem)
@test JuMP.objective_value(problem) == Inf
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also check the feasibility status?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns INFEASIBLE. I think it should be NODE_LIMIT. We should do that in another PR and tests all possible termination statuses.

end
end
11 changes: 11 additions & 0 deletions test/unit/optimizationresults.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function optimizationresults_unit_test()
emptyresults_tests()
end

function emptyresults_tests()
result = CL.OptimizationResult{CL.MinSense}()
@test CL.getprimalbound(result) == Inf
@test CL.getdualbound(result) == -Inf
@test CL.getbestprimalsol(result) == nothing
@test CL.getbestdualsol(result) == nothing
end
4 changes: 4 additions & 0 deletions test/unit/unit_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include("variable.jl")
include("constraint.jl")
include("varconstr.jl")
# include("manager.jl")
include("optimizationresults.jl")
include("filters.jl")
include("solsandbounds.jl")
include("incumbents.jl")
Expand Down Expand Up @@ -65,6 +66,9 @@ function unit_tests()
@testset "solsandbounds.jl" begin
solsandbounds_unit_tests()
end
@testset "optimizationresults.jl" begin
optimizationresults_unit_test()
end
@testset "incumbents.jl" begin
incumbents_unit_tests()
end
Expand Down