From e1322118f2de030a053927464ecd696ea5bcf901 Mon Sep 17 00:00:00 2001 From: eduardovegas Date: Wed, 7 Dec 2022 16:14:30 -0300 Subject: [PATCH 1/3] Refactor reformulation parser --- test/parser.jl | 71 +++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/test/parser.jl b/test/parser.jl index e3d064758..d323be8cb 100644 --- a/test/parser.jl +++ b/test/parser.jl @@ -77,20 +77,15 @@ mutable struct ConstrCache rhs::Float64 end -mutable struct SubproblemCache - constraints::Vector{ConstrCache} - varids::Vector{String} -end - -mutable struct MasterCache +mutable struct ProblemCache sense::Type{<:ClB.AbstractSense} objective::ExprCache constraints::Vector{ConstrCache} end mutable struct ReadCache - master::MasterCache - subproblems::Dict{Int64,SubproblemCache} + master::ProblemCache + subproblems::Dict{Int64,ProblemCache} variables::Dict{String,VarCache} end @@ -106,14 +101,14 @@ end function ReadCache() return ReadCache( - MasterCache( + ProblemCache( CL.MinSense, ExprCache( Dict{String, Float64}() ), ConstrCache[] ), - Dict{Int64,SubproblemCache}(), + Dict{Int64,ProblemCache}(), Dict{String,VarCache}() ) end @@ -212,16 +207,21 @@ end read_master!(::Any, cache::ReadCache, line::AbstractString) = nothing -function read_subproblem!(cache::ReadCache, line::AbstractString, nb_sp::Int64) +function read_subproblem!(sense::Type{<:ClB.AbstractSense}, cache::ReadCache, line::AbstractString, nb_sp::Int64) + obj = _read_expression(line) + if haskey(cache.subproblems, nb_sp) + cache.subproblems[nb_sp].sense = sense + cache.subproblems[nb_sp].obj = obj + else + cache.subproblems[nb_sp] = ProblemCache(sense, obj, []) + end +end + +function read_subproblem!(::Val{:constraints}, cache::ReadCache, line::AbstractString, nb_sp::Int64) constr = _read_constraint(line) if !isnothing(constr) - varids = collect(keys(constr.lhs.vars)) if haskey(cache.subproblems, nb_sp) push!(cache.subproblems[nb_sp].constraints, constr) - push!(cache.subproblems[nb_sp].varids, varids...) - unique!(cache.subproblems[nb_sp].varids) - else - cache.subproblems[nb_sp] = SubproblemCache([constr], varids) end end end @@ -258,11 +258,13 @@ end read_variables!(::Any, ::Any, ::ReadCache, ::AbstractString) = nothing function create_subproblems!(env::Env{ClMP.VarId}, reform::ClMP.Reformulation, cache::ReadCache) + i = 1 + constraints = ClMP.Constraint[] subproblems = [] all_spvars = Dict{String, ClMP.Variable}() for (_, sp) in cache.subproblems spform = nothing - for varid in sp.varids + for (varid, cost) in sp.objective.vars if haskey(cache.variables, varid) var = cache.variables[varid] if var.duty <= ClMP.DwSpPricingVar || var.duty <= ClMP.MasterRepPricingVar @@ -270,25 +272,27 @@ function create_subproblems!(env::Env{ClMP.VarId}, reform::ClMP.Reformulation, c spform = ClMP.create_formulation!( env, ClMP.DwSp(nothing, nothing, nothing, var.kind); - obj_sense = cache.master.sense + obj_sense = sp.sense ) end v = ClMP.setvar!(spform, varid, ClMP.DwSpPricingVar; lb = var.lb, ub = var.ub, kind = var.kind) - if haskey(cache.master.objective.vars, varid) - ClMP.setperencost!(spform, v, cache.master.objective.vars[varid]) - else - throw(UndefVarParserError("Variable $varid not present in objective function")) - end + ClMP.setperencost!(spform, v, cost) all_spvars[varid] = v end else throw(UndefVarParserError("Variable $varid duty and/or kind not defined")) end end + for constr in sp.constraints + members = Dict(ClMP.getid(all_spvars[varid]) => coeff for (varid, coeff) in constr.lhs.vars) + c = ClMP.setconstr!(spform, "sp_c$i", ClMP.DwSpPureConstr; rhs = constr.rhs, sense = constr.sense, members = members) + push!(constraints, c) + i += 1 + end push!(subproblems, spform) ClMP.add_dw_pricing_sp!(reform, spform) end - return subproblems, all_spvars + return subproblems, all_spvars, constraints end function add_master_vars!(master::ClMP.Formulation, all_spvars::Dict{String, ClMP.Variable}, cache::ReadCache) @@ -314,10 +318,9 @@ function add_master_vars!(master::ClMP.Formulation, all_spvars::Dict{String, ClM return mastervars end -function add_constraints!(master::ClMP.Formulation, mastervars::Dict{String, ClMP.Variable}, cache::ReadCache) +function add_master_constraints!(master::ClMP.Formulation, mastervars::Dict{String, ClMP.Variable}, constraints::Vector{ClMP.Constraint}, cache::ReadCache) #create master constraints i = 1 - constraints = [] for constr in cache.master.constraints members = Dict{ClMP.VarId, Float64}() constr_duty = ClMP.MasterPureConstr @@ -340,16 +343,6 @@ function add_constraints!(master::ClMP.Formulation, mastervars::Dict{String, ClM push!(constraints, c) i += 1 end - #create subproblems constraints in master - for (_, sp) in cache.subproblems - for constr in sp.constraints - members = Dict(ClMP.getid(mastervars[varid]) => coeff for (varid, coeff) in constr.lhs.vars) - c = ClMP.setconstr!(master, "c$i", ClMP.MasterMixedConstr; rhs = constr.rhs, sense = constr.sense, members = members) - push!(constraints, c) - i += 1 - end - end - return constraints end function reformfromcache(cache::ReadCache) @@ -362,7 +355,7 @@ function reformfromcache(cache::ReadCache) env = Env{ClMP.VarId}(CL.Params()) reform = ClMP.Reformulation(env) - subproblems, all_spvars = create_subproblems!(env, reform, cache) + subproblems, all_spvars, constraints = create_subproblems!(env, reform, cache) master = ClMP.create_formulation!( env, @@ -372,7 +365,7 @@ function reformfromcache(cache::ReadCache) ) ClMP.setmaster!(reform, master) mastervars = add_master_vars!(master, all_spvars, cache) - constraints = add_constraints!(master, mastervars, cache) + add_master_constraints!(master, mastervars, constraints, cache) for sp in subproblems sp.parent_formulation = master @@ -409,7 +402,7 @@ function reformfromstring(s::String) continue end if section == _KW_SUBPROBLEM - read_subproblem!(cache, line, nb_subproblems) + read_subproblem!(sub_section, cache, line, nb_subproblems) continue end if section == _KW_BOUNDS From 7edb12c92092a4589f229c438325971c48411762 Mon Sep 17 00:00:00 2001 From: eduardovegas Date: Wed, 7 Dec 2022 16:17:13 -0300 Subject: [PATCH 2/3] Update parser tests --- test/integration/parser.jl | 207 ++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 95 deletions(-) diff --git a/test/integration/parser.jl b/test/integration/parser.jl index 00324fc0b..d94575584 100644 --- a/test/integration/parser.jl +++ b/test/integration/parser.jl @@ -43,7 +43,10 @@ end # master not defined s = """ SP - - 6.3y1 + 3y2 == 5.9 + Min + - 2y1 + y2 + S.t. + - 6.3y1 + 3y2 == 5.9 Continuous pricing y1, y2 @@ -58,14 +61,19 @@ end Master max such that - x + y1 + y2 <= 50.3 + x + y1 <= 50.3 SP - - 6.3y1 + 3y2 == 5.9 + max + - 2y1 + y2 + such that + - 6.3y1 + 3y2 == 5.9 Continuous pure x + representative + y1 pricing - y1, y2 + y2 bounds y1 >= 1 1 <= y2 @@ -103,7 +111,7 @@ end @test isempty(subproblems) - # sp variable present in master but no subproblem + # representative variable present in master but no subproblem s = """ master maximise @@ -116,7 +124,7 @@ end Int pure y - pricing + representative w bounds y <= 10 @@ -125,7 +133,7 @@ end end @testset "variables not defined" begin - # subproblem variables not present in OF + # representative variable not present in OF s = """ master maximum @@ -133,7 +141,10 @@ end such that x - y1 <= 25 dw_sp - y1 - y2 >= 25 + maximum + 6y2 - 2.0*y1 + such that + y1 - y2 >= 25 cont pure x @@ -165,16 +176,12 @@ end s = """ master min - 3*x + 7w - y + 3*x + 7w st x + w == 25 - dw_sp - y >= 25 int pure x - pricing - y bounds 2 <= x, w <= 10 """ @@ -184,16 +191,12 @@ end s = """ master minimise - 3*x + 7w - y + 3*x + 7w such that x + w - z == 25 - dw_sp - y >= 25 int pure x, w - pricing - y bounds 2 <= x, w <= 10 """ @@ -203,16 +206,19 @@ end s = """ master min - 3*x - y + 3*x - w such that - x + y == 25 + x + w == 25 dw_sp - y >= 25 + min + y + such that + y >= 25 integer pure - x + x, w bound - 2 <= x <= 10 + 2 <= x, w <= 10 """ @test_throws UndefVarParserError reformfromstring(s) @@ -223,86 +229,91 @@ end 3*x - y such that x + y == 25 - dw_sp - y >= 25 bounds - 2 <= x <= 10 + 2 <= x, y <= 10 """ @test_throws UndefVarParserError reformfromstring(s) end - @testset "minimize no bounds and representatives" begin + @testset "minimize no bounds" begin s = """ Master Minimize - 2*x + 4.5y1 + y2 + 2*x + 4.5*y1 Subject To x + y1 <= 10.5 - x + y2 >= 3 SP + Min + y1 + y2 + St - 6.3y1 + 3y2 == 5.9 Continuous pure x + representative + y1 pricing - y1, y2 + y2 """ env, master, subproblems, constraints = reformfromstring(s) @test CL.getobjsense(master) == CL.MinSense names, kinds, duties, costs, bounds = get_vars_info(master) - @test names == ["y2", "x", "y1"] - @test kinds == [ClMP.Continuous, ClMP.Continuous, ClMP.Continuous] - @test duties == [ClMP.MasterRepPricingVar, ClMP.MasterPureVar, ClMP.MasterRepPricingVar] - @test costs == [1.0, 2.0, 4.5] - @test bounds == [(-Inf, Inf), (-Inf, Inf), (-Inf, Inf)] + @test names == ["x", "y1"] + @test kinds == [ClMP.Continuous, ClMP.Continuous] + @test duties == [ClMP.MasterPureVar, ClMP.MasterRepPricingVar] + @test costs == [2.0, 4.5] + @test bounds == [(-Inf, Inf), (-Inf, Inf)] constrs = get_constrs_info(master) - c1 = constrs[1] # x + y2 >= 3 - @test c1.coeffs == [("y2", 1.0), ("x", 1.0)] - @test c1.duty == ClMP.MasterMixedConstr - @test c1.sense == CL.Greater - @test c1.rhs == 3.0 - - c1 = constrs[2] # - 6.3y1 + 3y2 == 5.9 - @test c1.coeffs == [("y1", -6.3), ("y2", 3.0)] - @test c1.duty == ClMP.MasterMixedConstr - @test c1.sense == CL.Equal - @test c1.rhs == 5.9 - - c1 = constrs[3] # x + y1 <= 10.5 + c1 = constrs[1] # x + y1 <= 10.5 @test c1.coeffs == [("y1", 1.0), ("x", 1.0)] @test c1.duty == ClMP.MasterMixedConstr @test c1.sense == CL.Less @test c1.rhs == 10.5 sp1 = subproblems[1] + @test CL.getobjsense(sp1) == CL.MinSense + names, kinds, duties, costs, bounds = get_vars_info(sp1) @test names == ["y2", "y1"] @test kinds == [ClMP.Continuous, ClMP.Continuous] @test duties == [ClMP.DwSpPricingVar, ClMP.DwSpPricingVar] - @test costs == [1.0, 4.5] + @test costs == [1.0, 1.0] @test bounds == [(-Inf, Inf), (-Inf, Inf)] + + constrs = get_constrs_info(sp1) + c1 = constrs[1] # - 6.3y1 + 3y2 == 5.9 + @test c1.coeffs == [("y1", -6.3), ("y2", 3.0)] + @test c1.duty == ClMP.DwSpPureConstr + @test c1.sense == CL.Equal + @test c1.rhs == 5.9 end @testset "minimize multiple kinds, duties, subproblems and bounds" begin s = """ master min - 2*x - 5w + 4.5y1 + 9*y2 - 3z_1 + z_2 + 2.2*z_3 + 2*x - 5w + y1 + y2 s.t. - x - 3y1 + 8*y2 + z_1 >= 20 + x - 3y1 + 8*y2 >= 20 x + w <= 9 dw_sp + min + 4.5*y1 - 3*z_1 + z_2 + s.t. 6.3y1 + z_1 == 5 - z_2 - 5*y2 >= 4.2 + z_1 - 5*z_2 >= 4.2 dw_sp - 2*z_3 + y1 - 3*y2 >= 3.8 + min + 9*y2 + 2.2*z_3 + s.t. + 2*z_3 - 3y2 >= 3.8 integers pures @@ -324,57 +335,63 @@ end @test CL.getobjsense(master) == CL.MinSense names, kinds, duties, costs, bounds = get_vars_info(master) - @test names == ["z_1", "y1", "y2", "z_2", "x", "w", "z_3"] - @test kinds == [ClMP.Continuous, ClMP.Binary, ClMP.Binary, ClMP.Continuous, ClMP.Integ, ClMP.Integ, ClMP.Continuous] - @test duties == [ClMP.MasterRepPricingVar, ClMP.MasterRepPricingVar, ClMP.MasterRepPricingVar, ClMP.MasterRepPricingVar, ClMP.MasterPureVar, ClMP.MasterPureVar, ClMP.MasterRepPricingVar] - @test costs == [-3.0, 4.5, 9.0, 1.0, 2.0, -5.0, 2.2] - @test bounds == [(6.2, Inf), (0.0, 1.0), (0.0, 1.0), (6.2, Inf), (0.0, 20.0), (-Inf, Inf), (-Inf, Inf)] + @test names == ["w", "x", "y2", "y1"] + @test kinds == [ClMP.Integ, ClMP.Integ, ClMP.Binary, ClMP.Binary] + @test duties == [ClMP.MasterPureVar, ClMP.MasterPureVar, ClMP.MasterRepPricingVar, ClMP.MasterRepPricingVar] + @test costs == [-5.0, 2.0, 1.0, 1.0] + @test bounds == [(-Inf, Inf), (0.0, 20.0), (0.0, 1.0), (0.0, 1.0)] constrs = get_constrs_info(master) - c1 = constrs[1] # z_2 - 5*y2 >= 4.2 - @test c1.coeffs == [("y2", -5.0), ("z_2", 1.0)] - @test c1.duty == ClMP.MasterMixedConstr - @test c1.sense == CL.Greater - @test c1.rhs == 4.2 + c1 = constrs[1] # x + w <= 9 + @test c1.coeffs == [("w", 1.0), ("x", 1.0)] + @test c1.duty == ClMP.MasterPureConstr + @test c1.sense == CL.Less + @test c1.rhs == 9.0 - c2 = constrs[2] # 6.3y1 + z_1 == 5 - @test c2.coeffs == [("y1", 6.3), ("z_1", 1.0)] + c2 = constrs[2] # x - 3y1 + 8*y2 >= 20 + @test c2.coeffs == [("y2", 8.0), ("y1", -3.0), ("x", 1.0)] @test c2.duty == ClMP.MasterMixedConstr - @test c2.sense == CL.Equal - @test c2.rhs == 5.0 - - c3 = constrs[3] # x + w <= 9 - @test c3.coeffs == [("w", 1.0), ("x", 1.0)] - @test c3.duty == ClMP.MasterPureConstr - @test c3.sense == CL.Less - @test c3.rhs == 9.0 - - c4 = constrs[4] # 2*z_3 + y1 - 3*y2 >= 3.8 - @test c4.coeffs == [("z_3", 2.0), ("y1", 1.0), ("y2", -3.0)] - @test c4.duty == ClMP.MasterMixedConstr - @test c4.sense == CL.Greater - @test c4.rhs == 3.8 - - c5 = constrs[5] # x - 3y1 + 8*y2 + z_1 >= 20 - @test c5.coeffs == [("y1", -3.0), ("z_1", 1.0), ("y2", 8.0), ("x", 1.0)] - @test c5.duty == ClMP.MasterMixedConstr - @test c5.sense == CL.Greater - @test c5.rhs == 20.0 + @test c2.sense == CL.Greater + @test c2.rhs == 20.0 sp1 = subproblems[1] + @test CL.getobjsense(sp1) == CL.MinSense + names, kinds, duties, costs, bounds = get_vars_info(sp1) - @test names == ["y1", "y2", "z_3"] - @test kinds == [ClMP.Binary, ClMP.Binary, ClMP.Continuous] - @test duties == [ClMP.DwSpPricingVar, ClMP.DwSpPricingVar, ClMP.DwSpPricingVar] - @test costs == [4.5, 9.0, 2.2] - @test bounds == [(0.0, 1.0), (0.0, 1.0), (-Inf, Inf)] + @test names == ["y2", "z_3"] + @test kinds == [ClMP.Binary, ClMP.Continuous] + @test duties == [ClMP.DwSpPricingVar, ClMP.DwSpPricingVar] + @test costs == [9.0, 2.2] + @test bounds == [(0.0, 1.0), (-Inf, Inf)] + + constrs = get_constrs_info(sp1) + c1 = constrs[1] # 2*z_3 - 3*y2 >= 3.8 + @test c1.coeffs == [("z_3", 2.0), ("y2", -3.0)] + @test c1.duty == ClMP.DwSpPureConstr + @test c1.sense == CL.Greater + @test c1.rhs == 3.8 sp2 = subproblems[2] + @test CL.getobjsense(sp2) == CL.MinSense + names, kinds, duties, costs, bounds = get_vars_info(sp2) - @test names == ["z_1", "y1", "y2", "z_2"] - @test kinds == [ClMP.Continuous, ClMP.Binary, ClMP.Binary, ClMP.Continuous] - @test duties == [ClMP.DwSpPricingVar, ClMP.DwSpPricingVar, ClMP.DwSpPricingVar, ClMP.DwSpPricingVar] - @test costs == [-3.0, 4.5, 9.0, 1.0] - @test bounds == [(6.2, Inf), (0.0, 1.0), (0.0, 1.0), (6.2, Inf)] + @test names == ["z_1", "z_2", "y1"] + @test kinds == [ClMP.Continuous, ClMP.Continuous, ClMP.Binary] + @test duties == [ClMP.DwSpPricingVar, ClMP.DwSpPricingVar, ClMP.DwSpPricingVar] + @test costs == [-3.0, 1.0, 4.5] + @test bounds == [(6.2, Inf), (6.2, Inf), (0.0, 1.0)] + + constrs = get_constrs_info(sp2) + c1 = constrs[1] # 6.3y1 + z_1 == 5 + @test c1.coeffs == [("y1", 6.3), ("z_1", 1.0)] + @test c1.duty == ClMP.DwSpPureConstr + @test c1.sense == CL.Equal + @test c1.rhs == 5.0 + + c2 = constrs[2] # z_1 - 5*z_2 >= 4.2 + @test c2.coeffs == [("z_2", -5.0), ("z_1", 1.0)] + @test c2.duty == ClMP.DwSpPureConstr + @test c2.sense == CL.Greater + @test c2.rhs == 4.2 end end From 22dcf0929681a4f796ee00e8304932125d405f02 Mon Sep 17 00:00:00 2001 From: eduardovegas Date: Wed, 7 Dec 2022 16:18:40 -0300 Subject: [PATCH 3/3] Use parser to build reformulations in the tests --- test/integration/initial_columns_callback.jl | 67 ++++++--------- test/unit/Algorithm/colgen.jl | 90 ++++++++------------ 2 files changed, 60 insertions(+), 97 deletions(-) diff --git a/test/integration/initial_columns_callback.jl b/test/integration/initial_columns_callback.jl index 1bd6be51b..99a6485b9 100644 --- a/test/integration/initial_columns_callback.jl +++ b/test/integration/initial_columns_callback.jl @@ -1,52 +1,33 @@ @testset "Integration - initial columns callback" begin - # DW subproblem formulation having a given nb of variables. - # Its parent is the master with representatives of sp vars & a constraint sum_i i*x_i <= 0. - # Cost of variable x[i] is i. - function build_reformulation(nb_variables) - env = CL.Env{ClMP.VarId}(CL.Params()) - - # Create the reformulation - reform = ClMP.Reformulation(env) - - # Create subproblem and variables - spform = ClMP.create_formulation!(env, ClMP.DwSp(nothing, nothing, nothing, ClMP.Continuous)) - spvars = Dict{String, ClMP.Variable}(); - for i in 1:nb_variables - x = ClMP.setvar!(spform, "x$i", ClMP.DwSpPricingVar) - ClMP.setperencost!(spform, x, i * 1.0) - spvars["x$i"] = x - end - ClMP.add_dw_pricing_sp!(reform, spform) - - # Create master and representatives - master = ClMP.create_formulation!(env, ClMP.DwMaster(); parent_formulation = reform) - spform.parent_formulation = master - mastervars = Dict{String, ClMP.Variable}(); - for i in 1:nb_variables - x = ClMP.setvar!( - master, "x$i", ClMP.MasterRepPricingVar, id = ClMP.getid(spvars["x$i"]) - ) - ClMP.setperencost!(master, x, i * 1.0) - mastervars["x$i"] = x - end - - constr = ClMP.setconstr!( - master, "constr", ClMP.MasterMixedConstr; - members = Dict(ClMP.getid(mastervars["x$i"]) => 1.0 * i for i in 1:nb_variables) - ) - ClMP.setmaster!(reform, master) - - closefillmode!(ClMP.getcoefmatrix(master)) - closefillmode!(ClMP.getcoefmatrix(spform)) + function build_reformulation() + nb_variables = 4 + form_string = """ + master + min + 1.0*x1 + 2.0*x2 + 3.0*x3 + 4.0*x4 + s.t. + 1.0*x1 + 2.0*x2 + 3.0*x3 + 4.0*x4 >= 0.0 + + dw_sp + min + 1.0*x1 + 2.0*x2 + 3.0*x3 + 4.0*x4 + + continuous + representatives + x1, x2, x3, x4 + """ + env, master, subproblems, constraints = reformfromstring(form_string) + spform = subproblems[1] + spvarids = Dict(CL.getname(spform, var) => varid for (varid, var) in CL.getvars(spform)) # Fake JuMP model to simulate the user interacting with it in the callback. fake_model = JuMP.Model() @variable(fake_model, x[i in 1:nb_variables]) - + for name in ["x$i" for i in 1:nb_variables] - CleverDicts.add_item(env.varids, ClMP.getid(spvars[name])) + CleverDicts.add_item(env.varids, spvarids[name]) end - return env, master, spform, spvars, x, constr + return env, master, spform, x, constraints[1] end # Create a formulation with 4 variables [x1 x2 x3 x4] and provide an initial column @@ -54,7 +35,7 @@ # Cost of the column in the master should be 7. # Coefficient of the column in the constraint should be 7. @testset "normal case" begin - env, master, spform, vars, x, constr = build_reformulation(4) + env, master, spform, x, constr = build_reformulation() function callback(cbdata) variables = [x[1].index, x[3].index] diff --git a/test/unit/Algorithm/colgen.jl b/test/unit/Algorithm/colgen.jl index 55849e347..831081eb1 100644 --- a/test/unit/Algorithm/colgen.jl +++ b/test/unit/Algorithm/colgen.jl @@ -1,49 +1,25 @@ -function reformulation_for_colgen(nb_variables = 5, obj_sense = Coluna.MathProg.MinSense) - env = Env{ClMP.VarId}(Coluna.Params()) - - spform = ClMP.create_formulation!(env, ClMP.DwSp(nothing, nothing, nothing, ClMP.Integ), obj_sense = obj_sense) - # Create subproblem variables - spvars = Dict{String, ClMP.Variable}() - for i in 1:nb_variables - x = ClMP.setvar!(spform, "x$i", ClMP.DwSpPricingVar) - ClMP.setperencost!(spform, x, i * 1.0) - spvars["x$i"] = x - end - - # Create the reformulation - reform = ClMP.Reformulation(env) - ClMP.add_dw_pricing_sp!(reform, spform) - - master = ClMP.create_formulation!(env, ClMP.DwMaster(); obj_sense = obj_sense) - ClMP.setmaster!(reform, master) - spform.parent_formulation = master - master.parent_formulation = reform - # Create sp representative variables in the master - mastervars = Dict{String, ClMP.Variable}() - for i in 1:nb_variables - x = ClMP.setvar!( - master, "x$i", ClMP.MasterRepPricingVar, id = ClMP.getid(spvars["x$i"]) - ) - ClMP.setperencost!(master, x, i * 1.0) - mastervars["x$i"] = x - end - - # Create a constraint in the master - constr = ClMP.setconstr!( - master, "constr", ClMP.MasterMixedConstr; - members = Dict(ClMP.getid(mastervars["x$i"]) => 1.0 * i for i in 1:nb_variables) - ) - - closefillmode!(ClMP.getcoefmatrix(master)) - closefillmode!(ClMP.getcoefmatrix(spform)) - return env, master, spform, spvars, constr -end - @testset "Algorithm - colgen" begin + form_string = """ + master + min + x1 + x2 + x3 + x4 + x5 + s.t. + x1 + x2 + x3 + x4 + x5 >= 0.0 + + dw_sp + min + x1 + x2 + x3 + x4 + x5 + + continuous + representatives + x1, x2, x3, x4, x5 + """ @testset "insert_columns!" begin @testset "Two identical columns at two iterations" begin # Expected: unexpected variable state error. - env, master, spform, spvars, constr = reformulation_for_colgen() + env, master, subproblems, constraints = reformfromstring(form_string) + spform = subproblems[1] + spvarids = Dict(CL.getname(spform, var) => varid for (varid, var) in CL.getvars(spform)) algo = ClA.ColumnGeneration( throw_column_already_inserted_warning = true ) @@ -55,14 +31,14 @@ end col1 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 1.0, ClB.FEASIBLE_SOL ) col2 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x2", "x3"]), + map(x -> spvarids[x], ["x2", "x3"]), [5.0, 2.0], 2.5, ClB.FEASIBLE_SOL @@ -78,7 +54,7 @@ end col3 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 3.0, ClB.FEASIBLE_SOL @@ -90,7 +66,9 @@ end @testset "Two identical columns at same iteration" begin # Expected: no error and two identical columns in the formulation - env, master, spform, spvars, constr = reformulation_for_colgen() + env, master, subproblems, constraints = reformfromstring(form_string) + spform = subproblems[1] + spvarids = Dict(CL.getname(spform, var) => varid for (varid, var) in CL.getvars(spform)) algo = ClA.ColumnGeneration( throw_column_already_inserted_warning = true ) @@ -101,21 +79,21 @@ end sp_optstate = ClA.OptimizationState(spform; max_length_ip_primal_sols = 5) col1 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 1.0, ClB.FEASIBLE_SOL ) col2 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 2.0, ClB.FEASIBLE_SOL ) col3 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x2", "x3"]), + map(x -> spvarids[x], ["x2", "x3"]), [5.0, 2.0], 3.5, ClB.FEASIBLE_SOL @@ -127,7 +105,9 @@ end end @testset "Deactivated column added twice at same iteration" begin - env, master, spform, spvars, constr = reformulation_for_colgen() + env, master, subproblems, constraints = reformfromstring(form_string) + spform = subproblems[1] + spvarids = Dict(CL.getname(spform, var) => varid for (varid, var) in CL.getvars(spform)) algo = ClA.ColumnGeneration( throw_column_already_inserted_warning = true ) @@ -135,7 +115,7 @@ end # Add column. col1 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 1.0, ClB.FEASIBLE_SOL @@ -152,14 +132,14 @@ end sp_optstate = ClA.OptimizationState(spform; max_length_ip_primal_sols = 5) col2 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 1.0, ClB.FEASIBLE_SOL ) col3 = ClMP.PrimalSolution( spform, - map(x -> ClMP.getid(spvars[x]), ["x1", "x3"]), + map(x -> spvarids[x], ["x1", "x3"]), [1.0, 2.0], 2.0, ClB.FEASIBLE_SOL @@ -171,7 +151,9 @@ end end @testset "Infeasible subproblem" begin - env, master, spform, spvars, constr = reformulation_for_colgen() + env, master, subproblems, constraints = reformfromstring(form_string) + spform = subproblems[1] + spvarids = Dict(CL.getname(spform, var) => varid for (varid, var) in CL.getvars(spform)) algo = ClA.ColumnGeneration( throw_column_already_inserted_warning = true )