From ffdb6a20b66853f1fb37af6f0359b2684ea4c929 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 28 Oct 2026 09:47:04 +1300 Subject: [PATCH] Fix TODO copying sets that are not isbitstype --- src/sets.jl | 38 ++++++++++++++++++++------------------ test/General/test_sets.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/sets.jl b/src/sets.jl index 3c5681ee8e..2bfb118445 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -162,6 +162,8 @@ struct GreaterThan{T<:Real} <: AbstractScalarSet lower::T end +Base.copy(set::GreaterThan) = GreaterThan(copy(set.lower)) + Base.:(==)(a::GreaterThan{T}, b::GreaterThan{T}) where {T} = a.lower == b.lower """ @@ -185,6 +187,8 @@ struct LessThan{T<:Real} <: AbstractScalarSet upper::T end +Base.copy(set::LessThan) = LessThan(copy(set.upper)) + Base.:(==)(a::LessThan{T}, b::LessThan{T}) where {T} = a.upper == b.upper """ @@ -208,6 +212,8 @@ struct EqualTo{T<:Number} <: AbstractScalarSet value::T end +Base.copy(set::EqualTo) = EqualTo(copy(set.value)) + Base.:(==)(a::EqualTo{T}, b::EqualTo{T}) where {T} = a.value == b.value """ @@ -243,6 +249,8 @@ struct Parameter{T<:Number} <: AbstractScalarSet value::T end +Base.copy(set::Parameter) = Parameter(copy(set.value)) + Base.:(==)(a::Parameter{T}, b::Parameter{T}) where {T} = a.value == b.value """ @@ -274,6 +282,8 @@ function Interval(lower::Real, upper::Real) return Interval(promote(lower, upper)...) end +Base.copy(set::Interval) = Interval(copy(set.lower), copy(set.upper)) + function Base.:(==)(a::Interval{T}, b::Interval{T}) where {T} return a.lower == b.lower && a.upper == b.upper end @@ -374,6 +384,10 @@ function Semicontinuous(lower::Real, upper::Real) return Semicontinuous(promote(lower, upper)...) end +function Base.copy(set::Semicontinuous) + return Semicontinuous(copy(set.lower), copy(set.upper)) +end + function Base.:(==)(a::Semicontinuous{T}, b::Semicontinuous{T}) where {T} return a.lower == b.lower && a.upper == b.upper end @@ -408,6 +422,8 @@ function Semiinteger(lower::Real, upper::Real) return Semiinteger(promote(lower, upper)...) end +Base.copy(set::Semiinteger) = Semiinteger(copy(set.lower), copy(set.upper)) + function Base.:(==)(a::Semiinteger{T}, b::Semiinteger{T}) where {T} return a.lower == b.lower && a.upper == b.upper end @@ -921,6 +937,8 @@ dual_set_type(::Type{PowerCone{T}}) where {T<:Real} = DualPowerCone{T} dimension(::PowerCone) = 3 +Base.copy(set::PowerCone) = PowerCone(copy(set.exponent)) + function Base.:(==)(x::PowerCone{T}, y::PowerCone{T}) where {T} return x.exponent == y.exponent end @@ -951,6 +969,8 @@ dual_set_type(::Type{DualPowerCone{T}}) where {T<:Real} = PowerCone{T} dimension(::DualPowerCone) = 3 +Base.copy(set::DualPowerCone) = DualPowerCone(copy(set.exponent)) + function Base.:(==)(x::DualPowerCone{T}, y::DualPowerCone{T}) where {T} return x.exponent == y.exponent end @@ -2946,24 +2966,6 @@ function Base.show(io::IO, s::VectorNonlinearOracle{T}) where {T} return end -# TODO(odow): these are not necessarily isbits. They may not be safe to return -# without copying if the number is BigFloat, for example. -function Base.copy( - set::Union{ - GreaterThan, - LessThan, - EqualTo, - Parameter, - Interval, - Semicontinuous, - Semiinteger, - PowerCone, - DualPowerCone, - }, -) - return set -end - """ supports_dimension_update(S::Type{<:MOI.AbstractVectorSet}) diff --git a/test/General/test_sets.jl b/test/General/test_sets.jl index 853dc97eda..174a18af5e 100644 --- a/test/General/test_sets.jl +++ b/test/General/test_sets.jl @@ -503,6 +503,35 @@ function test_VectorNonlinearOracle() return end +struct MyNum <: Real + x::Base.RefValue{Float64} +end + +Base.copy(x::MyNum) = MyNum(Ref(x.x[])) + +Base.:(==)(x::MyNum, y::MyNum) = x.x[] == y.x[] + +function test_set_copy_not_isbitstype() + # copy(::BigInt) is a shallow copy by explicit design in Julia. + set = MOI.GreaterThan(big"1") + @test copy(set) === set + for set in Any[ + MOI.GreaterThan(MyNum(Ref(1.0))), + MOI.LessThan(MyNum(Ref(1.0))), + MOI.EqualTo(MyNum(Ref(1.0))), + MOI.Interval(MyNum(Ref(1.0)), MyNum(Ref(3.0))), + MOI.Parameter(MyNum(Ref(1.0))), + MOI.Semicontinuous(MyNum(Ref(1.0)), MyNum(Ref(3.0))), + MOI.Semiinteger(MyNum(Ref(1.0)), MyNum(Ref(3.0))), + MOI.PowerCone(MyNum(Ref(1.0))), + MOI.DualPowerCone(MyNum(Ref(1.0))), + ] + @test copy(set) == set + @test copy(set) !== set + end + return +end + end # module TestSets.runtests()