From cb8cf623053a67098c3f50bca69db6c0893656fc Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Fri, 22 Sep 2023 15:30:42 +0200 Subject: [PATCH] global bounds for representatives of different subproblems (#1075) --- src/decomposition.jl | 15 +++- test/unit/MathProg/dw_decomposition.jl | 95 +++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/src/decomposition.jl b/src/decomposition.jl index 14c39f5fe..d35c09016 100644 --- a/src/decomposition.jl +++ b/src/decomposition.jl @@ -192,6 +192,17 @@ function instantiate_orig_vars!( end if !haskey(masterform, varid) + lb, ub = if is_representative(annotations, varid) + ( + getperenlb(origform, var) * sum(BD.getlowermultiplicity.(annotations.ann_per_repr_var[varid])), + getperenub(origform, var) * sum(BD.getuppermultiplicity.(annotations.ann_per_repr_var[varid])) + ) + else + ( + getperenlb(origform, var) * BD.getlowermultiplicity(sp_ann), + getperenub(origform, var) * BD.getuppermultiplicity(sp_ann) + ) + end clonevar!( origform, masterform, @@ -199,8 +210,8 @@ function instantiate_orig_vars!( var, MasterRepPricingVar, is_explicit = false, - lb = getperenlb(origform, var) * BD.getlowermultiplicity(sp_ann), - ub = getperenub(origform, var) * BD.getuppermultiplicity(sp_ann) + lb = lb, + ub = ub ) end end diff --git a/test/unit/MathProg/dw_decomposition.jl b/test/unit/MathProg/dw_decomposition.jl index a7d89168f..b641f6a5b 100644 --- a/test/unit/MathProg/dw_decomposition.jl +++ b/test/unit/MathProg/dw_decomposition.jl @@ -209,4 +209,97 @@ function dw_decomposition_with_identical_subproblems() @test Coluna.MathProg.getcurlb(sp1, sp1_vars["x2"]) == 2.0 @test Coluna.MathProg.getcurub(sp1, sp1_vars["x2"]) == 3.0 end -register!(unit_tests, "dw_decomposition", dw_decomposition_with_identical_subproblems) \ No newline at end of file +register!(unit_tests, "dw_decomposition", dw_decomposition_with_identical_subproblems) + +function dw_decomposition_repr() + """ + min e1 + s.t. e1 >= 4 + + sp1 : 1 <= e1 <= 2 with lm = 0, lm= 2 + sp2 : 1 <= e2 <= 3 with lm = 1, lm= 3 + """ + + env = Coluna.Env{Coluna.MathProg.VarId}( + Coluna.Params( + global_art_var_cost = 1000.0, + local_art_var_cost = 100.0 + ) + ) + + origform = Coluna.MathProg.create_formulation!( + env, Coluna.MathProg.Original() + ) + + # Variables + vars = Dict{String, Coluna.MathProg.VarId}() + e1 = Coluna.MathProg.getid( + Coluna.MathProg.setvar!( + origform, "e1", Coluna.MathProg.OriginalVar; + cost = 1.0, lb = 1.0, ub = 2.0, kind = Integ + ) + ) + + # Constraints + constrs = Dict{String, Coluna.MathProg.ConstrId}() + c1 = Coluna.MathProg.getid( + Coluna.MathProg.setconstr!( + origform, "c1", Coluna.MathProg.OriginalConstr; rhs = 4.0, sense = Coluna.MathProg.Greater, members = Dict(e1 => 1.0) + ) + ) + + # Decomposition tree + m = JuMP.Model() + BlockDecomposition.@axis(axis, [1, 2]) + tree = BlockDecomposition.Tree(m, BlockDecomposition.DantzigWolfe, axis) + mast_ann = tree.root.master + sp_ann1 = BlockDecomposition.Annotation(tree, BlockDecomposition.DwPricingSp, BlockDecomposition.DantzigWolfe, []) + BlockDecomposition.setlowermultiplicity!(sp_ann1, 0) + BlockDecomposition.setuppermultiplicity!(sp_ann1, 2) + BlockDecomposition.create_leaf!(BlockDecomposition.getroot(tree), axis[1], sp_ann1) + sp_ann2 = BlockDecomposition.Annotation(tree, BlockDecomposition.DwPricingSp, BlockDecomposition.DantzigWolfe, []) + BlockDecomposition.setlowermultiplicity!(sp_ann2, 1) + BlockDecomposition.setuppermultiplicity!(sp_ann2, 3) + BlockDecomposition.create_leaf!(BlockDecomposition.getroot(tree), axis[2], sp_ann2) + + # Dantzig-Wolfe annotations + ann = Coluna.Annotations() + ann.tree = tree + Coluna.store!(ann, mast_ann, Coluna.MathProg.getconstr(origform, c1)) + Coluna.store_repr!(ann, [sp_ann1, sp_ann2], Coluna.MathProg.getvar(origform, e1)) + + problem = Coluna.MathProg.Problem(env) + Coluna.MathProg.set_original_formulation!(problem, origform) + + Coluna.reformulate!(problem, ann, env) + reform = Coluna.MathProg.get_reformulation(problem) + + # Test master + master = Coluna.MathProg.getmaster(reform) + master_vars = Dict(getname(master, varid) => var for (varid, var) in Coluna.MathProg.getvars(master)) + master_constrs = Dict(getname(master, constrid) => constr for (constrid, constr) in Coluna.MathProg.getconstrs(master)) + + @show length(master_vars) + @test Coluna.MathProg.getcurlb(master, master_vars["e1"]) == 1.0 * (0 + 1) + @test Coluna.MathProg.getcurub(master, master_vars["e1"]) == 2.0 * (2 + 3) + @test Coluna.MathProg.getcurrhs(master, master_constrs["c1"]) == 4.0 + + # Test subproblem 1 + sp1 = first(values(Coluna.MathProg.get_dw_pricing_sps(reform))) + sp1_vars = Dict(getname(sp1, varid) => var for (varid, var) in Coluna.MathProg.getvars(sp1)) + sp1_constrs = Dict(getname(sp1, constrid) => constr for (constrid, constr) in Coluna.MathProg.getconstrs(sp1)) + + @test length(sp1_vars) == 2 + @test Coluna.MathProg.getcurlb(sp1, sp1_vars["e1"]) == 1.0 + @test Coluna.MathProg.getcurub(sp1, sp1_vars["e1"]) == 2.0 + + # Test subproblem 2 + sp2 = collect(values(Coluna.MathProg.get_dw_pricing_sps(reform)))[2] + sp2_vars = Dict(getname(sp2, varid) => var for (varid, var) in Coluna.MathProg.getvars(sp2)) + sp2_constrs = Dict(getname(sp2, constrid) => constr for (constrid, constr) in Coluna.MathProg.getconstrs(sp2)) + + @test length(sp2_vars) == 2 + @test Coluna.MathProg.getcurlb(sp1, sp2_vars["e1"]) == 1.0 + @test Coluna.MathProg.getcurub(sp1, sp2_vars["e1"]) == 2.0 +end +register!(unit_tests, "dw_decomposition", dw_decomposition_repr) \ No newline at end of file