From c80efa2c2ab116108dd55ae861052971982189c2 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 30 Oct 2023 11:05:54 +1300 Subject: [PATCH 1/3] Add SkipModelConvertScalarSetWrapper --- docs/src/developers/extensions.md | 3 +++ src/constraints.jl | 39 +++++++++++++++++++++++++++---- test/test_constraint.jl | 9 +++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/docs/src/developers/extensions.md b/docs/src/developers/extensions.md index e2fbcd3fc26..9fe5f64be86 100644 --- a/docs/src/developers/extensions.md +++ b/docs/src/developers/extensions.md @@ -253,6 +253,9 @@ Rewriting my_equal_to to == When parsing a constraint you can recurse into sub-constraint (for example, the `{expr}` in `z => {x <= 1}`) by calling [`parse_constraint`](@ref). +To prevent JuMP from promoting the set to the same value type as the model, use +[`SkipModelConvertScalarSetWrapper`](@ref). + ### Build To extend the [`@constraint`](@ref) macro at build time, implement a new diff --git a/src/constraints.jl b/src/constraints.jl index dcb76d0d1c1..64c6e74655d 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -1543,16 +1543,47 @@ function relax_with_penalty!( return relax_with_penalty!(model, Dict(); default = default) end -struct _DoNotConvertSet{S} <: MOI.AbstractScalarSet +""" + SkipModelConvertScalarSetWrapper(set::MOI.AbstractScalarSet) + +JuMP uses [`model_convert`](@ref) to automatically promote [`MOI.AbstractScalarSet`](@ref) +sets to the same [`value_type`](@ref) as the model. + +In cases there this is undesirable, wrap the set in `SkipModelConvertScalarSetWrapper` +to pass the set un-changed to the solver. + +## Examples + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> @constraint(model, x in MOI.EqualTo(1 // 2)) +x = 0.5 + +julia> @constraint(model, x in SkipModelConvertScalarSetWrapper(MOI.EqualTo(1 // 2))) +x = 1//2 +``` +""" +struct SkipModelConvertScalarSetWrapper{S<:MOI.AbstractScalarSet} <: + MOI.AbstractScalarSet set::S end -model_convert(::AbstractModel, set::_DoNotConvertSet) = set +model_convert(::AbstractModel, set::SkipModelConvertScalarSetWrapper) = set -moi_set(c::ScalarConstraint{F,<:_DoNotConvertSet}) where {F} = c.set.set +function moi_set( + c::ScalarConstraint{F,<:SkipModelConvertScalarSetWrapper}, +) where {F} + return c.set.set +end function _build_boolean_equal_to(::Function, lhs::AbstractJuMPScalar, rhs::Bool) - return ScalarConstraint(lhs, _DoNotConvertSet(MOI.EqualTo(rhs))) + return ScalarConstraint( + lhs, + SkipModelConvertScalarSetWrapper(MOI.EqualTo(rhs)), + ) end function _build_boolean_equal_to(error_fn::Function, ::AbstractJuMPScalar, rhs) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index de98fd8b331..318bc64de56 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1763,4 +1763,13 @@ function test_def_equal_to_operator_bool() return end +function test_SkipModelConvertScalarSetWrapper() + model = Model() + @variable(model, x) + set = MOI.EqualTo(1 // 2) + c1 = @constraint(model, x in set) + c2 = @constraint(model, x in SkipModelConvertScalarSetWrapper(set)) + @test constraint_object(c1).set === MOI.EqualTo(0.5) + @test constraint_object(c2).set === MOI.EqualTo(1 // 2) + return end From 106bccb7387a76d1681a6de36c344c5074be4706 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 11:57:50 +1300 Subject: [PATCH 2/3] Update test_constraint.jl --- test/test_constraint.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 318bc64de56..de30fc97c54 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1773,3 +1773,6 @@ function test_SkipModelConvertScalarSetWrapper() @test constraint_object(c2).set === MOI.EqualTo(1 // 2) return end + +end # module + From 60865dc111aee2f1356aa43bb343da9a58ad75d2 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 11:58:15 +1300 Subject: [PATCH 3/3] Update test_constraint.jl --- test/test_constraint.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index de30fc97c54..59c734d4865 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1775,4 +1775,3 @@ function test_SkipModelConvertScalarSetWrapper() end end # module -