From 6e56a1ee92d5c7b9d5f7c9adf93fee49e1150982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Sep 2019 22:05:55 +0200 Subject: [PATCH 1/2] Fix deletion of constrained variables for CachingOptimizer --- src/Test/UnitTests/variables.jl | 68 +++++++++++++++++++++++++++++++ src/Utilities/cachingoptimizer.jl | 33 +++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/src/Test/UnitTests/variables.jl b/src/Test/UnitTests/variables.jl index 34a6090de0..e07ec5ed02 100644 --- a/src/Test/UnitTests/variables.jl +++ b/src/Test/UnitTests/variables.jl @@ -88,6 +88,74 @@ function delete_variables(model::MOI.ModelLike, config::TestConfig) end unittests["delete_variables"] = delete_variable +""" + delete_nonnegative_variables(model::MOI.ModelLike, config::TestConfig) + +Test adding, and then deleting, nonnegative variables. +""" +function delete_nonnegative_variables(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + @test MOI.is_empty(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + v, cv = MOI.add_constrained_variables(model, MOI.Nonnegatives(2)) + @test MOI.get(model, MOI.NumberOfVariables()) == 2 + MOI.delete(model, v) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + @test !MOI.is_valid(model, v[1]) + @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) + @test !MOI.is_valid(model, v[2]) + @test_throws MOI.InvalidIndex(v[2]) MOI.delete(model, v[2]) + @test !MOI.is_valid(model, cv) + v, cv = MOI.add_constrained_variables(model, MOI.Nonnegatives(1)) + @test MOI.get(model, MOI.NumberOfVariables()) == 1 + MOI.delete(model, v[1]) + @test !MOI.is_valid(model, v[1]) + @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) + @test !MOI.is_valid(model, cv) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + v, cv = MOI.add_constrained_variables(model, MOI.Nonnegatives(2)) + @test MOI.get(model, MOI.NumberOfVariables()) == 2 + MOI.delete(model, v[1]) + @test !MOI.is_valid(model, v[1]) + @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) + @test MOI.is_valid(model, cv) + @test MOI.is_valid(model, v[2]) + @test MOI.get(model, MOI.ConstraintFunction(), cv) == MOI.VectorOfVariables([v[2]]) + @test MOI.get(model, MOI.ConstraintSet(), cv) == MOI.Nonnegatives(1) + MOI.delete(model, v[2]) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + @test !MOI.is_valid(model, v[1]) + @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) + @test !MOI.is_valid(model, v[2]) + @test_throws MOI.InvalidIndex(v[2]) MOI.delete(model, v[2]) + @test !MOI.is_valid(model, cv) +end +unittests["delete_nonnegative_variables"] = delete_nonnegative_variables + +""" + delete_soc_variables(model::MOI.ModelLike, config::TestConfig) + +Test adding, and then deleting, nonnegative variables. +""" +function delete_soc_variables(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + @test MOI.is_empty(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + v, cv = MOI.add_constrained_variables(model, MOI.SecondOrderCone(3)) + @test MOI.get(model, MOI.NumberOfVariables()) == 3 + MOI.delete(model, v) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + @test !MOI.is_valid(model, v[1]) + @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) + @test !MOI.is_valid(model, v[2]) + @test_throws MOI.InvalidIndex(v[2]) MOI.delete(model, v[2]) + @test !MOI.is_valid(model, cv) + v, cv = MOI.add_constrained_variables(model, MOI.SecondOrderCone(3)) + @test MOI.get(model, MOI.NumberOfVariables()) == 3 + @test_throws MOI.DeleteNotAllowed MOI.delete(model, v[1]) +end +unittests["delete_soc_variables"] = delete_soc_variables + """ getvariable(model::MOI.ModelLike, config::TestConfig) diff --git a/src/Utilities/cachingoptimizer.jl b/src/Utilities/cachingoptimizer.jl index 207c63f049..078daaa252 100644 --- a/src/Utilities/cachingoptimizer.jl +++ b/src/Utilities/cachingoptimizer.jl @@ -375,6 +375,39 @@ function MOI.delete(m::CachingOptimizer, index::MOI.Index) MOI.delete(m.model_cache, index) end +function MOI.delete(m::CachingOptimizer, indices::Vector{<:MOI.Index}) + if m.state == ATTACHED_OPTIMIZER + for index in indices + if !MOI.is_valid(m, index) + # The index thrown by m.model_cache would be xored + throw(MOI.InvalidIndex(index)) + end + end + indices_optimizer = [m.model_to_optimizer_map[index] for index in indices] + if m.mode == AUTOMATIC + try + MOI.delete(m.optimizer, indices_optimizer) + catch err + if err isa MOI.NotAllowedError + reset_optimizer(m) + else + rethrow(err) + end + end + else + MOI.delete(m.optimizer, indices_optimizer) + end + end + # The state may have changed in AUTOMATIC mode since reset_optimizer is + # called in case the deletion is not supported + if m.state == ATTACHED_OPTIMIZER + for index in indices + delete!(m.optimizer_to_model_map, m.model_to_optimizer_map[index]) + delete!(m.model_to_optimizer_map, index) + end + end + MOI.delete(m.model_cache, indices) +end # TODO: add_constraints, transform From 0060461f363aae4cad9ef494e7ea530bf640f1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 28 Sep 2019 12:16:42 +0200 Subject: [PATCH 2/2] Split test in two --- src/Test/UnitTests/variables.jl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Test/UnitTests/variables.jl b/src/Test/UnitTests/variables.jl index e07ec5ed02..cfff7e2554 100644 --- a/src/Test/UnitTests/variables.jl +++ b/src/Test/UnitTests/variables.jl @@ -113,6 +113,18 @@ function delete_nonnegative_variables(model::MOI.ModelLike, config::TestConfig) @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) @test !MOI.is_valid(model, cv) @test MOI.get(model, MOI.NumberOfVariables()) == 0 +end +unittests["delete_nonnegative_variables"] = delete_nonnegative_variables + +""" + update_dimension_nonnegative_variables(model::MOI.ModelLike, config::TestConfig) + +Test adding, and then deleting one by one, nonnegative variables. +""" +function update_dimension_nonnegative_variables(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + @test MOI.is_empty(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 0 v, cv = MOI.add_constrained_variables(model, MOI.Nonnegatives(2)) @test MOI.get(model, MOI.NumberOfVariables()) == 2 MOI.delete(model, v[1]) @@ -130,12 +142,12 @@ function delete_nonnegative_variables(model::MOI.ModelLike, config::TestConfig) @test_throws MOI.InvalidIndex(v[2]) MOI.delete(model, v[2]) @test !MOI.is_valid(model, cv) end -unittests["delete_nonnegative_variables"] = delete_nonnegative_variables +unittests["update_dimension_nonnegative_variables"] = update_dimension_nonnegative_variables """ delete_soc_variables(model::MOI.ModelLike, config::TestConfig) -Test adding, and then deleting, nonnegative variables. +Test adding, and then deleting, second-order cone variables. """ function delete_soc_variables(model::MOI.ModelLike, config::TestConfig) MOI.empty!(model)