From 56011d125fb7f253fd710c67a3c4c5cfdd5b826f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 26 Feb 2022 12:11:48 -0500 Subject: [PATCH 1/3] Add UniversalFallback utility to throw unsupported attributes and constraints --- src/Utilities/universalfallback.jl | 65 +++++++++++++++++++++++++++++ test/Utilities/universalfallback.jl | 61 +++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index 142d8ea221..7664cad08a 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -129,6 +129,71 @@ function final_touch(uf::UniversalFallback, index_map) return final_touch(uf.model, index_map) end +""" + throw_unsupported(uf::UniversalFallback; excluded_attributes = MOI.AnyAttribute[]) + +Throws [`MathOptInterface.UnsupportedAttribute`](@ref) if there are any model, +variable or constraint attribute such that 1) `is_copyable(attr)` returns +`true`, 2) the attribute was set to `uf` but not to `uf.model` and 3) the +attribute is not in `excluded_attributes`. + +Suppose `Optimizer` supports only the constraints and attributes +that `OptimizerCache` supports in addition to +[`MathOptInterface.VariablePrimalStart`](@ref). +Then, this function can be used in the implementation of the following method: +```julia +function MOI.copy_to( + dest::Optimizer, + src::MOI.Utilities.UniversalFallback{OptimizerCache}, +) + attr = MOI.VariablePrimalStart() + MOI.Utilities.throw_unsupported( + src, + excluded_attributes = MOI.AnyAttribute[attr], + ) + index_map = MOI.copy_to(dest, src.model) + if attr in MOI.get(src, MOI.ListOfModelAttributesSet()) + for vi_src in MOI.get(src, MOI.ListOfVariableIndices()) + vi_dest = index_map[vi_src] + value = MOI.get(src, attr, vi_src) + MOI.set(dest, attr, vi_dest, value) + end + end + return index_map +end +``` +""" +function throw_unsupported( + uf::UniversalFallback; + excluded_attributes = MOI.AnyAttribute[], +) + for attr in keys(uf.modattr) + if !(attr in excluded_attributes) + MOI.throw(MOI.UnsupportedAttribute(attr)) + end + end + for attr in keys(uf.varattr) + if !isempty(uf.varattr[attr]) && !(attr in excluded_attributes) + MOI.throw(MOI.UnsupportedAttribute(attr)) + end + end + for attr in keys(uf.conattr) + if !isempty(uf.conattr[attr]) && !(attr in excluded_attributes) + MOI.throw(MOI.UnsupportedAttribute(attr)) + end + end + for S in keys(uf.single_variable_constraints) + if !isempty(uf.single_variable_constraints[S]) + throw(MOI.UnsupportedConstraint{MOI.VariableIndex,S}()) + end + end + for (F, S) in keys(uf.constraints) + if !MOI.is_empty(uf.constraints[(F, S)]) + throw(MOI.UnsupportedConstraint{F,S}()) + end + end +end + # References function MOI.is_valid(uf::UniversalFallback, vi::MOI.VariableIndex) return MOI.is_valid(uf.model, vi) diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index d3908caed4..b168489785 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -380,6 +380,67 @@ function test_missing_attribute() return end +function test_throw_unsupported_model_attribute() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + attr = MOI.Test.UnknownModelAttribute() + MOI.set(model, attr, 1) + err = MOI.UnsupportedAttribute(attr) + @test_throws err MOI.Utilities.throw_unsupported(model) + MOI.Utilities.throw_unsupported( + model, + excluded_attributes = MOI.AnyAttribute[attr], + ) + return +end + +function test_throw_unsupported_variable_attribute() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + attr = MOI.Test.UnknownVariableAttribute() + MOI.set(model, attr, x, 1) + err = MOI.UnsupportedAttribute(attr) + @test_throws err MOI.Utilities.throw_unsupported(model) + MOI.Utilities.throw_unsupported( + model, + excluded_attributes = MOI.AnyAttribute[attr], + ) + return +end + +function test_throw_unsupported_constraint_attribute() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + c = MOI.add_constraint(model, x, MOI.EqualTo(1.0)) + attr = MOI.Test.UnknownConstraintAttribute() + MOI.set(model, attr, c, 1) + err = MOI.UnsupportedAttribute(attr) + @test_throws err MOI.Utilities.throw_unsupported(model) + MOI.Utilities.throw_unsupported( + model, + excluded_attributes = MOI.AnyAttribute[attr], + ) + return +end + +function test_throw_unsupported_variable_constraint() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + c = MOI.add_constraint(model, x, MOI.Test.UnknownScalarSet(1.0)) + err = MOI.UnsupportedConstraint{typeof(x),MOI.Test.UnknownScalarSet{Float64}}() + @test_throws err MOI.Utilities.throw_unsupported(model) + return +end + +function test_throw_unsupported_affine_constraint() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + func = 2.0x + c = MOI.add_constraint(model, func, MOI.Test.UnknownScalarSet(1.0)) + err = MOI.UnsupportedConstraint{typeof(func),MOI.Test.UnknownScalarSet{Float64}}() + @test_throws err MOI.Utilities.throw_unsupported(model) + return +end + end # module TestUniversalFallback.runtests() From 06421d5edd0d02dd63d263cc73cc0cc51eb138df Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 28 Feb 2022 08:36:22 +1300 Subject: [PATCH 2/3] Update universalfallback.jl --- src/Utilities/universalfallback.jl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index 7664cad08a..c23702f30f 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -172,26 +172,27 @@ function throw_unsupported( MOI.throw(MOI.UnsupportedAttribute(attr)) end end - for attr in keys(uf.varattr) - if !isempty(uf.varattr[attr]) && !(attr in excluded_attributes) + for (attr, val) in uf.varattr + if !isempty(val) && !(attr in excluded_attributes) MOI.throw(MOI.UnsupportedAttribute(attr)) end end - for attr in keys(uf.conattr) - if !isempty(uf.conattr[attr]) && !(attr in excluded_attributes) + for (attr, val) in uf.conattr + if !isempty(val) && !(attr in excluded_attributes) MOI.throw(MOI.UnsupportedAttribute(attr)) end end - for S in keys(uf.single_variable_constraints) - if !isempty(uf.single_variable_constraints[S]) + for (S, val) in uf.single_variable_constraints + if !isempty(val) throw(MOI.UnsupportedConstraint{MOI.VariableIndex,S}()) end end - for (F, S) in keys(uf.constraints) - if !MOI.is_empty(uf.constraints[(F, S)]) + for ((F, S), val) in uf.constraints + if !MOI.is_empty(val) throw(MOI.UnsupportedConstraint{F,S}()) end end + return end # References From ea0be6129bb80b3ffdfed97c44e809d6c70e8688 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 28 Feb 2022 08:38:50 +1300 Subject: [PATCH 3/3] Update universalfallback.jl --- test/Utilities/universalfallback.jl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index b168489785..40dde1458d 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -425,9 +425,12 @@ end function test_throw_unsupported_variable_constraint() model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) x = MOI.add_variable(model) - c = MOI.add_constraint(model, x, MOI.Test.UnknownScalarSet(1.0)) - err = MOI.UnsupportedConstraint{typeof(x),MOI.Test.UnknownScalarSet{Float64}}() - @test_throws err MOI.Utilities.throw_unsupported(model) + set = MOI.Test.UnknownScalarSet(1.0) + c = MOI.add_constraint(model, x, set) + @test_throws( + MOI.UnsupportedConstraint{typeof(x),typeof(set)}(), + MOI.Utilities.throw_unsupported(model), + ) return end @@ -435,9 +438,12 @@ function test_throw_unsupported_affine_constraint() model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) x = MOI.add_variable(model) func = 2.0x - c = MOI.add_constraint(model, func, MOI.Test.UnknownScalarSet(1.0)) - err = MOI.UnsupportedConstraint{typeof(func),MOI.Test.UnknownScalarSet{Float64}}() - @test_throws err MOI.Utilities.throw_unsupported(model) + set = MOI.Test.UnknownScalarSet(1.0) + c = MOI.add_constraint(model, func, set) + @test_throws( + MOI.UnsupportedConstraint{typeof(func),typeof(set)}(), + MOI.Utilities.throw_unsupported(model), + ) return end