From 1d7b5b755e04804ca0667d38d7a70e00773c6032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 1 Jul 2020 14:54:16 +0200 Subject: [PATCH 1/2] Fix order ListOfConstraintIndices with constraint bridges on variable function --- src/Bridges/Constraint/Constraint.jl | 2 ++ src/Bridges/Constraint/map.jl | 14 +++++---- test/Bridges/bridge_optimizer.jl | 43 +++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 3490fa9e9b..d3419160fa 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -1,5 +1,7 @@ module Constraint +using OrderedCollections # for OrderedDict in Map + using MathOptInterface const MOI = MathOptInterface const MOIU = MOI.Utilities diff --git a/src/Bridges/Constraint/map.jl b/src/Bridges/Constraint/map.jl index 0850ea5073..646366c0ba 100644 --- a/src/Bridges/Constraint/map.jl +++ b/src/Bridges/Constraint/map.jl @@ -9,16 +9,19 @@ struct Map <: AbstractDict{MOI.ConstraintIndex, AbstractBridge} bridges::Vector{Union{Nothing, AbstractBridge}} # Constraint Index of bridged constraint -> Constraint type. constraint_types::Vector{Tuple{DataType, DataType}} + # The order of the keys is used in `keys_of_type` which is used by + # `ListOfConstraintIndices`. Therefore they need to be in the order + # of creation so we need `OrderedDict` and not `Dict`. # For `SingleVariable` constraints: (variable, set type) -> bridge - single_variable_constraints::Dict{Tuple{Int64, DataType}, AbstractBridge} + single_variable_constraints::OrderedDict{Tuple{Int64, DataType}, AbstractBridge} # For `VectorVariable` constraints: (variable, set type) -> bridge - vector_of_variables_constraints::Dict{Tuple{Int64, DataType}, AbstractBridge} + vector_of_variables_constraints::OrderedDict{Tuple{Int64, DataType}, AbstractBridge} end function Map() return Map(Union{Nothing, AbstractBridge}[], Tuple{DataType, DataType}[], - Dict{Tuple{Int64, DataType}, AbstractBridge}(), - Dict{Tuple{Int64, DataType}, AbstractBridge}()) + OrderedDict{Tuple{Int64, DataType}, AbstractBridge}(), + OrderedDict{Tuple{Int64, DataType}, AbstractBridge}()) end # Implementation of `AbstractDict` interface. @@ -141,7 +144,8 @@ function number_of_type end """ keys_of_type(map::Map, C::Type{<:MOI.ConstraintIndex}) -Return a list of all the keys of type `C` in `map`. +Return a list of all the keys of type `C` in `map` in order order in which they +were created with `add_key_for_bridge`. """ function keys_of_type end diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index 81f40a21d2..6cd997d3bf 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -212,13 +212,54 @@ end @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.Interval{Int}}())) == [c1] @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}}())) == [c2] + n = 4 + z = MOI.add_variables(model, n) + scon_indices = MOI.ConstraintIndex{MOI.SingleVariable, MOI.Interval{Int}}[] + for (i, v) in enumerate([x; y; z]) + f = MOI.SingleVariable(v) + c = MOI.add_constraint(model, f, MOI.Interval(i, 2i)) + push!(scon_indices, c) + + @test MOI.get(model, MOI.ListOfConstraints()) == [(MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}), (MOI.ScalarAffineFunction{Int},MOI.Interval{Int}), (MOI.SingleVariable, MOI.Interval{Int})] + test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.GreaterThan{Int}, 1) + test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.Interval{Int}, 1) + test_num_constraints(model, MOI.SingleVariable, MOI.Interval{Int}, i) + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.Interval{Int}}())) == [c1] + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}}())) == [c2] + # The indices should be returned in order of creation + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.Interval{Int}}())) == scon_indices + end + + vcon_indices = MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Nonnegatives}[] + for (i, v) in enumerate(z) + f = MOI.VectorOfVariables([v]) + c = MOI.add_constraint(model, f, MOI.Nonnegatives(1)) + push!(vcon_indices, c) + + @test Set(MOI.get(model, MOI.ListOfConstraints())) == Set([(MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}), (MOI.ScalarAffineFunction{Int},MOI.Interval{Int}), (MOI.SingleVariable, MOI.Interval{Int}), (MOI.VectorOfVariables, MOI.Nonnegatives)]) + test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.GreaterThan{Int}, 1) + test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.Interval{Int}, 1) + test_num_constraints(model, MOI.SingleVariable, MOI.Interval{Int}, n + 2) + test_num_constraints(model, MOI.VectorOfVariables, MOI.Nonnegatives, i) + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.Interval{Int}}())) == [c1] + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}}())) == [c2] + # The indices should be returned in order of creation + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.Interval{Int}}())) == scon_indices + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Nonnegatives}())) == vcon_indices + end + @test MOI.is_valid(model, c2) MOI.delete(model, c2) - @test MOI.get(model, MOI.ListOfConstraints()) == [(MOI.ScalarAffineFunction{Int},MOI.Interval{Int})] + @test Set(MOI.get(model, MOI.ListOfConstraints())) == Set([(MOI.ScalarAffineFunction{Int},MOI.Interval{Int}), (MOI.SingleVariable, MOI.Interval{Int}), (MOI.VectorOfVariables, MOI.Nonnegatives)]) test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.GreaterThan{Int}, 0) test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.Interval{Int}, 1) + test_num_constraints(model, MOI.SingleVariable, MOI.Interval{Int}, n + 2) + test_num_constraints(model, MOI.VectorOfVariables, MOI.Nonnegatives, n) @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Int},MOI.Interval{Int}}())) == [c1] + # The indices should be returned in order of creation + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.Interval{Int}}())) == scon_indices + @test (@inferred MOI.get(model, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Nonnegatives}())) == vcon_indices end @testset "Continuous Linear" begin From 58cdead2131c1939865abfed2884f4334a777f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 2 Jul 2020 17:08:52 +0200 Subject: [PATCH 2/2] More robust comparison of ListOfConstraints --- test/Bridges/bridge_optimizer.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index 6cd997d3bf..42da6e72f4 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -220,7 +220,7 @@ end c = MOI.add_constraint(model, f, MOI.Interval(i, 2i)) push!(scon_indices, c) - @test MOI.get(model, MOI.ListOfConstraints()) == [(MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}), (MOI.ScalarAffineFunction{Int},MOI.Interval{Int}), (MOI.SingleVariable, MOI.Interval{Int})] + @test Set(MOI.get(model, MOI.ListOfConstraints())) == Set([(MOI.ScalarAffineFunction{Int},MOI.GreaterThan{Int}), (MOI.ScalarAffineFunction{Int},MOI.Interval{Int}), (MOI.SingleVariable, MOI.Interval{Int})]) test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.GreaterThan{Int}, 1) test_num_constraints(model, MOI.ScalarAffineFunction{Int}, MOI.Interval{Int}, 1) test_num_constraints(model, MOI.SingleVariable, MOI.Interval{Int}, i)