From 131ac00b23bda4a7d652cf53f54359986f5fbb8d Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 30 Oct 2023 15:01:49 +1300 Subject: [PATCH 1/5] Add ListOfVariablesWithAttributeSet and ListOfConstraintsWithAttributeSet --- docs/src/manual/models.md | 2 ++ docs/src/reference/models.md | 2 ++ docs/src/tutorials/implementing.md | 2 ++ src/Test/test_model.jl | 26 ++++++++++++++ src/Utilities/universalfallback.jl | 22 ++++++++++++ src/attributes.jl | 55 ++++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+) diff --git a/docs/src/manual/models.md b/docs/src/manual/models.md index f31ba77f1e..7863b45ff8 100644 --- a/docs/src/manual/models.md +++ b/docs/src/manual/models.md @@ -67,9 +67,11 @@ The following attributes are available: * [`ListOfConstraintAttributesSet`](@ref) * [`ListOfConstraintIndices`](@ref) * [`ListOfConstraintTypesPresent`](@ref) + * [`ListOfConstraintsWithAttributeSet`](@ref) * [`ListOfModelAttributesSet`](@ref) * [`ListOfVariableAttributesSet`](@ref) * [`ListOfVariableIndices`](@ref) + * [`ListOfVariablesWithAttributeSet`](@ref) * [`NumberOfConstraints`](@ref) * [`NumberOfVariables`](@ref) * [`Name`](@ref) diff --git a/docs/src/reference/models.md b/docs/src/reference/models.md index 1429c51a65..253c1d6063 100644 --- a/docs/src/reference/models.md +++ b/docs/src/reference/models.md @@ -53,7 +53,9 @@ ListOfConstraintIndices ListOfOptimizerAttributesSet ListOfModelAttributesSet ListOfVariableAttributesSet +ListOfVariablesWithAttributeSet ListOfConstraintAttributesSet +ListOfConstraintsWithAttributeSet UserDefinedFunction ListOfSupportedNonlinearOperators ``` diff --git a/docs/src/tutorials/implementing.md b/docs/src/tutorials/implementing.md index ebb60253f6..9c931d40a2 100644 --- a/docs/src/tutorials/implementing.md +++ b/docs/src/tutorials/implementing.md @@ -479,6 +479,7 @@ Variable-related attributes: | Attribute | [`get`](@ref) | [`set`](@ref) | [`supports`](@ref) | | ---------------------- | --------------| ------------- | ------------------ | | [`ListOfVariableAttributesSet`](@ref) | Yes | No | No | +| [`ListOfVariablesWithAttributeSet`](@ref) | Yes | No | No | | [`NumberOfVariables`](@ref) | Yes | No | No | | [`ListOfVariableIndices`](@ref) | Yes | No | No | @@ -487,6 +488,7 @@ Constraint-related attributes: | Attribute | [`get`](@ref) | [`set`](@ref) | [`supports`](@ref) | | ---------------------- | --------------| ------------- | ------------------ | | [`ListOfConstraintAttributesSet`](@ref) | Yes | No | No | +| [`ListOfConstraintsWithAttributeSet`](@ref) | Yes | No | No | | [`NumberOfConstraints`](@ref) | Yes | No | No | | [`ListOfConstraintTypesPresent`](@ref) | Yes | No | No | | [`ConstraintFunction`](@ref) | Yes | Yes | No | diff --git a/src/Test/test_model.jl b/src/Test/test_model.jl index 3bd190e7a6..7b5dca333c 100644 --- a/src/Test/test_model.jl +++ b/src/Test/test_model.jl @@ -1194,3 +1194,29 @@ function test_model_ModelFilter_ListOfConstraintTypesPresent( @test MOI.get(dest, attr) == 4 return end + +function test_model_ListOfVariablesWithAttributeSet( + model::MOI.ModelLike, + ::Config, +) + attr = MOI.VariableName() + @requires MOI.supports(model, attr, MOI.VariableIndex) + x = MOI.add_variables(model, 2) + MOI.set(model, attr, x[2], "y") + @test x[2] in MOI.get(model, MOI.ListOfVariableAttributesSet(attr)) + return +end + +function test_model_ListOfConstraintsWithAttributeSet( + model::MOI.ModelLike, + ::Config{T}, +) where {T} + F, S = MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T} + attr = MOI.ConstraintName() + @requires MOI.supports(model, attr, MOI.ConstraintIndex{F,S}) + x = MOI.add_variables(model, 2) + c = MOI.add_constraint.(model, one(T) .* x, MOI.GreaterThan(zero(T))) + MOI.set(model, attr, c[2], "y") + @test c[2] in MOI.get(model, MOI.ListOfVariableAttributesSet{F,S}(attr)) + return +end diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index 0eb3f409cd..df34be04ff 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -939,3 +939,25 @@ end MOI.add_variable(uf::UniversalFallback) = MOI.add_variable(uf.model) MOI.add_variables(uf::UniversalFallback, n) = MOI.add_variables(uf.model, n) + +function MOI.get( + uf::UniversalFallback, + attr::MOI.ListOfVariablesWithAttributeSet, +) + dict = get(uf.varattr, attr.attr, nothing) + if dict === nothing + return MOI.get(uf.model, attr) + end + return collect(keys(dict)) +end + +function MOI.get( + uf::UniversalFallback, + attr::MOI.ListOfConstraintsWithAttributeSet{F,S}, +) where {F,S} + dict = get(uf.conattr, attr.attr, nothing) + if dict === nothing + return MOI.get(uf.model, attr) + end + return filter(Base.Fix2(isa, MOI.ConstraintIndex{F,S}), keys(dict)) +end diff --git a/src/attributes.jl b/src/attributes.jl index 7331d0f363..6fe2291692 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -1455,6 +1455,31 @@ attribute was set to variables. """ struct ListOfVariableAttributesSet <: AbstractModelAttribute end +""" + ListOfVariablesWithAttributeSet(attr::AbstractVariableAttribute) + +A model attribute for the `Vector{VariableIndex}` of all variables with the +attribute `attr` set. + +The returned list may not be minimal, so some elements may have their default +value set. + +## Note + +This is an optional attribute to implement. The default fallback is to get +[`ListOfVariableIndices`](@ref). +""" +struct ListOfVariablesWithAttributeSet{A} <: AbstractModelAttribute + attr::A + function ListOfVariablesWithAttributeSet(attr::AbstractVariableAttribute) + return new{typeof(attr)}(attr) + end +end + +function get_fallback(model::ModelLike, ::ListOfVariablesWithAttributeSet) + return get(model, ListOfVariableIndices()) +end + """ VariableName() @@ -1581,6 +1606,36 @@ not be included in the list even if then have been set with [`set`](@ref). """ struct ListOfConstraintAttributesSet{F,S} <: AbstractModelAttribute end +""" + ListOfConstraintsWithAttributeSet{F,S}(attr:AbstractConstraintAttribute) + +A model attribute for the `Vector{ConstraintIndex{F,S}}` of all constraints with +the attribute `attr` set. + +The returned list may not be minimal, so some elements may have their default +value set. + +## Note + +This is an optional attribute to implement. The default fallback is to get +[`ListOfConstraintIndices`](@ref). +""" +struct ListOfConstraintsWithAttributeSet{F,S,A} <: AbstractModelAttribute + attr::A + function ListOfConstraintsWithAttributeSet{F,S}( + attr::AbstractConstraintAttribute, + ) where {F,S} + return new{F,S,typeof(attr)}(attr) + end +end + +function get_fallback( + model::ModelLike, + ::ListOfConstraintsWithAttributeSet{F,S}, +) where {F,S} + return get(model, ListOfConstraintIndices{F,S}()) +end + """ ConstraintName() From 843661ea2285e09cd4965a1bacdee43fbeebb671 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 15:34:31 +1300 Subject: [PATCH 2/5] Update test_model.jl --- src/Test/test_model.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Test/test_model.jl b/src/Test/test_model.jl index 7b5dca333c..effd9a56c8 100644 --- a/src/Test/test_model.jl +++ b/src/Test/test_model.jl @@ -1203,7 +1203,7 @@ function test_model_ListOfVariablesWithAttributeSet( @requires MOI.supports(model, attr, MOI.VariableIndex) x = MOI.add_variables(model, 2) MOI.set(model, attr, x[2], "y") - @test x[2] in MOI.get(model, MOI.ListOfVariableAttributesSet(attr)) + @test x[2] in MOI.get(model, MOI.ListOfVariablesWithAttributeSet(attr)) return end @@ -1217,6 +1217,7 @@ function test_model_ListOfConstraintsWithAttributeSet( x = MOI.add_variables(model, 2) c = MOI.add_constraint.(model, one(T) .* x, MOI.GreaterThan(zero(T))) MOI.set(model, attr, c[2], "y") - @test c[2] in MOI.get(model, MOI.ListOfVariableAttributesSet{F,S}(attr)) + ret = MOI.get(model, MOI.ListOfConstraintsWithAttributeSet{F,S}(attr)) + @test c[2] in ret return end From 7b610b4798751bdca222eb667971bc508fb87b80 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 30 Oct 2023 17:08:37 +1300 Subject: [PATCH 3/5] Update --- src/Utilities/universalfallback.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index df34be04ff..f337b339f2 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -953,9 +953,13 @@ end function MOI.get( uf::UniversalFallback, - attr::MOI.ListOfConstraintsWithAttributeSet{F,S}, -) where {F,S} - dict = get(uf.conattr, attr.attr, nothing) + attr::MOI.ListOfConstraintsWithAttributeSet{F,S,A}, +) where {F,S,A} + dict = if A === MOI.ConstraintName + uf.con_to_name + else + get(uf.conattr, attr.attr, nothing) + end if dict === nothing return MOI.get(uf.model, attr) end From 55f947a945099975dc3292aba7c1dcdf873fbb04 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 30 Oct 2023 18:23:12 +1300 Subject: [PATCH 4/5] Update --- src/Utilities/universalfallback.jl | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index f337b339f2..80c60c860f 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -953,15 +953,24 @@ end function MOI.get( uf::UniversalFallback, - attr::MOI.ListOfConstraintsWithAttributeSet{F,S,A}, -) where {F,S,A} - dict = if A === MOI.ConstraintName - uf.con_to_name - else - get(uf.conattr, attr.attr, nothing) - end + attr::MOI.ListOfConstraintsWithAttributeSet{F,S}, +) where {F,S} + dict = get(uf.conattr, attr.attr, nothing) if dict === nothing return MOI.get(uf.model, attr) end return filter(Base.Fix2(isa, MOI.ConstraintIndex{F,S}), keys(dict)) end + +function MOI.get( + uf::UniversalFallback, + attr::MOI.ListOfConstraintsWithAttributeSet{F,S,MOI.ConstraintName}, +) where {F,S} + if MOI.supports_constraint(uf.model, F, S) + return MOI.get(uf.model, attr) + end + return filter( + Base.Fix2(isa, MOI.ConstraintIndex{F,S}), + keys(uf.con_to_name), + ) +end From 0161efcd4f5d94a3dd223adca4729c80e605da23 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 31 Oct 2023 13:36:47 +1300 Subject: [PATCH 5/5] Update --- src/Utilities/universalfallback.jl | 51 ++++++++++++++++++----------- test/Utilities/universalfallback.jl | 37 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/Utilities/universalfallback.jl b/src/Utilities/universalfallback.jl index 80c60c860f..e167fcd184 100644 --- a/src/Utilities/universalfallback.jl +++ b/src/Utilities/universalfallback.jl @@ -373,11 +373,26 @@ function _get( return MOI.get_fallback(uf, attr, ci) end +function _model_supports_attribute(model, attr, index...) + return !MOI.is_copyable(attr) || MOI.supports(model, attr, index...) +end + +function _model_supports_attribute( + model, + attr, + index::Type{MOI.ConstraintIndex{F,S}}, +) where {F,S} + if !MOI.supports_constraint(model, F, S) + return false + end + return !MOI.is_copyable(attr) || MOI.supports(model, attr, index) +end + function MOI.get( uf::UniversalFallback, attr::Union{MOI.AbstractOptimizerAttribute,MOI.AbstractModelAttribute}, ) - if !MOI.is_copyable(attr) || MOI.supports(uf.model, attr) + if _model_supports_attribute(uf.model, attr) return MOI.get(uf.model, attr) end return _get(uf, attr) @@ -388,8 +403,7 @@ function MOI.get( attr::MOI.AbstractConstraintAttribute, ci::MOI.ConstraintIndex{F,S}, ) where {F,S} - if MOI.supports_constraint(uf.model, F, S) && - (!MOI.is_copyable(attr) || MOI.supports(uf.model, attr, typeof(ci))) + if _model_supports_attribute(uf.model, attr, MOI.ConstraintIndex{F,S}) return MOI.get(uf.model, attr, ci) end return _get(uf, attr, ci) @@ -400,7 +414,7 @@ function MOI.get( attr::MOI.AbstractVariableAttribute, vi::MOI.VariableIndex, ) - if !MOI.is_copyable(attr) || MOI.supports(uf.model, attr, MOI.VariableIndex) + if _model_supports_attribute(uf.model, attr, MOI.VariableIndex) return MOI.get(uf.model, attr, vi) end return _get(uf, attr, vi) @@ -944,33 +958,30 @@ function MOI.get( uf::UniversalFallback, attr::MOI.ListOfVariablesWithAttributeSet, ) + if _model_supports_attribute(uf.model, attr.attr, MOI.VariableIndex) + return MOI.get(uf.model, attr) + end dict = get(uf.varattr, attr.attr, nothing) if dict === nothing - return MOI.get(uf.model, attr) + return MOI.VariableIndex[] end return collect(keys(dict)) end +_constraint_attribute_dict(uf, ::MOI.ConstraintName) = uf.con_to_name +_constraint_attribute_dict(uf, attr) = get(uf.conattr, attr, nothing) + function MOI.get( uf::UniversalFallback, attr::MOI.ListOfConstraintsWithAttributeSet{F,S}, ) where {F,S} - dict = get(uf.conattr, attr.attr, nothing) - if dict === nothing + if _model_supports_attribute(uf.model, attr.attr, MOI.ConstraintIndex{F,S}) return MOI.get(uf.model, attr) end - return filter(Base.Fix2(isa, MOI.ConstraintIndex{F,S}), keys(dict)) -end - -function MOI.get( - uf::UniversalFallback, - attr::MOI.ListOfConstraintsWithAttributeSet{F,S,MOI.ConstraintName}, -) where {F,S} - if MOI.supports_constraint(uf.model, F, S) - return MOI.get(uf.model, attr) + dict = _constraint_attribute_dict(uf, attr.attr) + if dict === nothing + return MOI.ConstraintIndex{F,S}[] end - return filter( - Base.Fix2(isa, MOI.ConstraintIndex{F,S}), - keys(uf.con_to_name), - ) + indices = collect(keys(dict)) + return filter(Base.Fix2(isa, MOI.ConstraintIndex{F,S}), indices) end diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index ea57a1771f..ebbc5bacf8 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -453,6 +453,43 @@ function test_throw_unsupported_affine_constraint() return end +function test_ListOfVariablesWithAttributeSet() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x = MOI.add_variables(model, 2) + MOI.set(model, MOI.VariableName(), x[1], "x") + # Passed through to Model with no special support + attr = MOI.ListOfVariablesWithAttributeSet(MOI.VariableName()) + @test MOI.get(model, attr) == x + # Handled by UniversalFallback + attr = MOI.ListOfVariablesWithAttributeSet(MOI.VariablePrimalStart()) + # ... no attributes set + @test MOI.get(model, attr) == MOI.VariableIndex[] + # ... one attribute set + MOI.set(model, MOI.VariablePrimalStart(), x[2], 1.0) + @test MOI.get(model, attr) == [x[2]] + return +end + +function test_ListOfConstraintsWithAttributeSet() + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x = MOI.add_variables(model, 2) + c = MOI.add_constraint.(model, 1.0 * x, MOI.EqualTo(1.0)) + F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64} + MOI.set(model, MOI.ConstraintName(), c[1], "c") + # Passed through to Model with no special support + attr = MOI.ListOfConstraintsWithAttributeSet{F,S}(MOI.ConstraintName()) + @test MOI.get(model, attr) == c + # Handled by UniversalFallback + attr = + MOI.ListOfConstraintsWithAttributeSet{F,S}(MOI.ConstraintPrimalStart()) + # ... no attributes set + @test MOI.get(model, attr) == MOI.ConstraintIndex{F,S}[] + # ... one attribute set + MOI.set(model, MOI.ConstraintPrimalStart(), c[2], 1.0) + @test MOI.get(model, attr) == [c[2]] + return +end + end # module TestUniversalFallback.runtests()