From da867a296af985320fc25a19d47d5bb0ff5d1b25 Mon Sep 17 00:00:00 2001 From: Luis Benet Date: Mon, 9 Dec 2024 11:20:18 -0600 Subject: [PATCH] Add tests provided by Aqua.jl (#687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add tests provided by Aqua.jl * Additions to Project.toml and test/Project.toml * Fix _interval_infsup ambiguities * Add ambiguity solutions proposed by @OlivierHnt, and revert 47605eb 47605eb solved some ambiguities, but induced (construction) errors Co-authored-by: Olivier Hénot <38465572+OlivierHnt@users.noreply.github.com> * Solve Base.union! ambiguities * Remove LinearAlgebra from [deps] * Try to solve some ambiguities related to ForwardDiff * Return Dual's for promote_rule * Add convert method to ForwardDiff extension * Add Dual(x::ExactReal) * More methods Dual(::ExactReal) * Fix Project.toml * Define similar to avoid many ambiguities * Fix rebase issues * Fix tests * Fix compat * Fix similar breaking Documenter --------- Co-authored-by: Olivier Hénot <38465572+OlivierHnt@users.noreply.github.com> Co-authored-by: Benoît Richard --- Project.toml | 5 +-- ext/IntervalArithmeticForwardDiffExt.jl | 26 +++++++++++++--- src/intervals/construction.jl | 6 ++++ src/intervals/exact_literals.jl | 2 ++ src/intervals/real_interface.jl | 5 +++ src/matmul.jl | 41 +++++-------------------- test/Project.toml | 1 + test/aqua.jl | 29 +++++++++++++++++ test/runtests.jl | 3 ++ 9 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 test/aqua.jl diff --git a/Project.toml b/Project.toml index 273be455f..82126ee2d 100644 --- a/Project.toml +++ b/Project.toml @@ -12,20 +12,21 @@ RoundingEmulator = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" [weakdeps] DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" -RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" +RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" [extensions] IntervalArithmeticDiffRulesExt = "DiffRules" IntervalArithmeticForwardDiffExt = "ForwardDiff" -IntervalArithmeticRecipesBaseExt = "RecipesBase" IntervalArithmeticIntervalSetsExt = "IntervalSets" +IntervalArithmeticRecipesBaseExt = "RecipesBase" [compat] CRlibm_jll = "1" DiffRules = "1" ForwardDiff = "0.10" IntervalSets = "0.7" +LinearAlgebra = "1.9" MacroTools = "0.5" RecipesBase = "1" RoundingEmulator = "0.2" diff --git a/ext/IntervalArithmeticForwardDiffExt.jl b/ext/IntervalArithmeticForwardDiffExt.jl index daf9bfbd5..d2b8d512b 100644 --- a/ext/IntervalArithmeticForwardDiffExt.jl +++ b/ext/IntervalArithmeticForwardDiffExt.jl @@ -3,7 +3,27 @@ module IntervalArithmeticForwardDiffExt using IntervalArithmetic, ForwardDiff using ForwardDiff: Dual, Partials, ≺, value, partials -# +# Needed to avoid method ambiguities: +ForwardDiff.can_dual(::Type{ExactReal}) = true +Dual(x::ExactReal) = Dual{Nothing, typeof(x), 0}(x.value) +Dual{T}(x::ExactReal) where {T} = Dual{T, typeof(x), 0}(x.value) +Dual{T, V}(x::ExactReal) where {T, V<:Real} = convert(Dual{T, V, 0}, x) +Dual{T, V, N}(x::ExactReal) where {T, V<:Real, N} = convert(Dual{T, V, N}, x) + +Base.convert(::Type{Dual{T,V,N}}, x::ExactReal) where {T,V,N} = Dual{T}(V(x), zero(Partials{N,V})) + +Base.promote_rule(::Type{Dual{T, V, N}}, ::Type{Interval{S}}) where {T, V, N, S<:Union{AbstractFloat, Rational}} = + Dual{T,Interval{IntervalArithmetic.promote_numtype(V, S)},N} +Base.promote_rule(::Type{Interval{S}}, ::Type{Dual{T, V, N}}) where {S<:Union{AbstractFloat, Rational}, T, V, N} = + Dual{T,Interval{IntervalArithmetic.promote_numtype(V, S)},N} +Base.promote_rule(::Type{ExactReal{S}}, ::Type{Dual{T, V, N}}) where {S<:Real, T, V, N} = + Dual{T,ExactReal{IntervalArithmetic.promote_numtype(V, S)},N} +Base.promote_rule(::Type{Dual{T, V, N}}, ::Type{ExactReal{S}}) where {S<:Real, T, V, N} = + Dual{T,ExactReal{IntervalArithmetic.promote_numtype(V, S)},N} + +Base.:(==)(x::Union{BareInterval,Interval}, y::Dual) = isthin(x, y) +Base.:(==)(x::Dual, y::Union{BareInterval,Interval}) = y == x + function Base.:(^)(x::Dual{Txy,<:Interval}, y::Dual{Txy,<:Interval}) where {Txy} vx, vy = value(x), value(y) @@ -70,8 +90,4 @@ function Base.:(^)(x::ExactReal, y::Dual{<:Ty}) where {Ty} end end -# resolve ambiguity - -Base.convert(::Type{Dual{T,V,N}}, x::ExactReal) where {T,V,N} = Dual{T}(V(x), zero(Partials{N,V})) - end diff --git a/src/intervals/construction.jl b/src/intervals/construction.jl index 7f19cff24..8a5a25dba 100644 --- a/src/intervals/construction.jl +++ b/src/intervals/construction.jl @@ -426,6 +426,7 @@ interval(::Type{T}, A::AbstractArray, d::AbstractArray{Decoration}; format::Symb interval(A::AbstractArray, d::AbstractArray{Decoration}; format::Symbol = :infsup) = interval.(A, d; format = format) interval(::Type{T}, A::AbstractArray, B::AbstractArray, d::AbstractArray{Decoration}; format::Symbol = :infsup) where {T} = interval.(T, A, B, d; format = format) interval(A::AbstractArray, B::AbstractArray, d::AbstractArray{Decoration}; format::Symbol = :infsup) = interval.(A, B, d; format = format) +interval(T::Type, d::Decoration; format::Symbol = :infsup) = throw(MethodError(interval, (T, d))) # standard format @@ -488,6 +489,11 @@ function _interval_infsup(::Type{T}, x, y::Union{BareInterval,Interval}, d::Deco end end +_interval_infsup(::Type{T}, a::Complex, b::Union{BareInterval,Interval}, d::Decoration = com) where {T<:NumTypes} = + complex(_interval_infsup(T, real(a), real(b), d), _interval_infsup(T, imag(a), imag(b), d)) +_interval_infsup(::Type{T}, a::Union{BareInterval,Interval}, b::Complex, d::Decoration = com) where {T<:NumTypes} = + complex(_interval_infsup(T, real(a), real(b), d), _interval_infsup(T, imag(a), imag(b), d)) + _interval_infsup(::Type{T}, a::Complex, b::Complex, d::Decoration = com) where {T<:NumTypes} = complex(_interval_infsup(T, real(a), real(b), d), _interval_infsup(T, imag(a), imag(b), d)) _interval_infsup(::Type{T}, a::Complex, b, d::Decoration = com) where {T<:NumTypes} = diff --git a/src/intervals/exact_literals.jl b/src/intervals/exact_literals.jl index 5f7d939db..25d7367a5 100644 --- a/src/intervals/exact_literals.jl +++ b/src/intervals/exact_literals.jl @@ -54,6 +54,8 @@ struct ExactReal{T<:Real} <: Real ExactReal(value::T) where {T<:Real} = new{T}(value) end +ExactReal(x::ExactReal) = x + _value(x::ExactReal) = x.value # hook for interval constructor # utilities diff --git a/src/intervals/real_interface.jl b/src/intervals/real_interface.jl index c2d4d8142..01824b6cd 100644 --- a/src/intervals/real_interface.jl +++ b/src/intervals/real_interface.jl @@ -130,6 +130,11 @@ for T ∈ (:BareInterval, :Interval) throw(ArgumentError("`setdiff!` is purposely not supported for intervals. See instead `interiordiff`")) end end +Base.union!(::AbstractVector{S}, ::BareInterval, ::Interval, ::Any...) where {S} = + throw(ArgumentError("`union!` is purposely not supported for intervals. See instead `hull`")) +Base.union!(::AbstractVector{S}, ::Interval, ::BareInterval, ::Any...) where {S} = + throw(ArgumentError("`union!` is purposely not supported for intervals. See instead `hull`")) + # allow pointwise equality diff --git a/src/matmul.jl b/src/matmul.jl index 6e30a1ebc..89a5818ca 100644 --- a/src/matmul.jl +++ b/src/matmul.jl @@ -86,39 +86,14 @@ struct MatMulMode{T} end matmul_mode() = MatMulMode{:slow}() -# - -function Base.:*(A::AbstractMatrix{<:RealOrComplexI}, B::AbstractMatrix{<:RealOrComplexI}) - T = promote_type(eltype(A), eltype(B)) - C = zeros(T, size(A, 1), size(B, 2)) - return LinearAlgebra.mul!(C, A, B, one(T), zero(T)) -end -function Base.:*(A::AbstractMatrix{<:RealOrComplexI}, B::AbstractVector{<:RealOrComplexI}) - T = promote_type(eltype(A), eltype(B)) - C = zeros(T, size(A, 1)) - return LinearAlgebra.mul!(C, A, B, one(T), zero(T)) -end - -function Base.:*(A::AbstractMatrix{<:RealOrComplexI}, B::AbstractMatrix) - T = promote_type(eltype(A), eltype(B)) - C = zeros(T, size(A, 1), size(B, 2)) - return LinearAlgebra.mul!(C, A, B, one(T), zero(T)) -end -function Base.:*(A::AbstractMatrix{<:RealOrComplexI}, B::AbstractVector) - T = promote_type(eltype(A), eltype(B)) - C = zeros(T, size(A, 1)) - return LinearAlgebra.mul!(C, A, B, one(T), zero(T)) -end - -function Base.:*(A::AbstractMatrix, B::AbstractMatrix{<:RealOrComplexI}) - T = promote_type(eltype(A), eltype(B)) - C = zeros(T, size(A, 1), size(B, 2)) - return LinearAlgebra.mul!(C, A, B, one(T), zero(T)) -end -function Base.:*(A::AbstractMatrix, B::AbstractVector{<:RealOrComplexI}) - T = promote_type(eltype(A), eltype(B)) - C = zeros(T, size(A, 1)) - return LinearAlgebra.mul!(C, A, B, one(T), zero(T)) +Base.similar(a::Array{T}) where {T <: RealOrComplexI} = zeros(T, size(a)) +Base.similar(a::Array{T}, S::Type) where {T <: RealOrComplexI} = zeros(S, size(a)) +Base.similar(::Array{T}, m::Int) where {T <: RealOrComplexI} = zeros(T, m) +Base.similar(::Array{T}, S::Type, dims::Dims{N}) where {N, T <: RealOrComplexI} = zeros(S, dims) +Base.similar(::Array{T}, dims::Dims{N}) where {T <: RealOrComplexI, N} = zeros(T, dims) + +function LinearAlgebra.mul!(C::AbstractVecOrMat{<:RealOrComplexI}, A::AbstractMatrix{<:RealOrComplexI}, B::AbstractVecOrMat{<:RealOrComplexI}) + return LinearAlgebra.mul!(C, A, B, interval(true), interval(false)) end function LinearAlgebra.mul!(C::AbstractVecOrMat{<:RealOrComplexI}, A::AbstractMatrix{<:RealOrComplexI}, B::AbstractVecOrMat{<:RealOrComplexI}, α::Number, β::Number) diff --git a/test/Project.toml b/test/Project.toml index 5ddc09e4e..2dd97b198 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,4 +1,5 @@ [deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" diff --git a/test/aqua.jl b/test/aqua.jl new file mode 100644 index 000000000..7c600dee5 --- /dev/null +++ b/test/aqua.jl @@ -0,0 +1,29 @@ +using Test +using IntervalArithmetic +using Aqua + +@testset "Aqua tests (performance)" begin + # This tests that we don't accidentally run into + # https://github.com/JuliaLang/julia/issues/29393 + # Aqua.test_unbound_args(IntervalArithmetic) + ua = Aqua.detect_unbound_args_recursively(IntervalArithmetic) + @test length(ua) == 0 + + # See: https://github.com/SciML/OrdinaryDiffEq.jl/issues/1750 + # Test that we're not introducing method ambiguities across deps + ambs = Aqua.detect_ambiguities(IntervalArithmetic; recursive = true) + pkg_match(pkgname, pkdir::Nothing) = false + pkg_match(pkgname, pkdir::AbstractString) = occursin(pkgname, pkdir) + filter!(x -> pkg_match("IntervalArithmetic", pkgdir(last(x).module)), ambs) + @test_broken length(ambs) == 0 +end + +@testset "Aqua tests (additional)" begin + Aqua.test_undefined_exports(IntervalArithmetic) + # Aqua.test_deps_compat(IntervalArithmetic) + Aqua.test_stale_deps(IntervalArithmetic) + Aqua.test_piracies(IntervalArithmetic) + Aqua.test_unbound_args(IntervalArithmetic) + Aqua.test_project_extras(IntervalArithmetic) + Aqua.test_persistent_tasks(IntervalArithmetic) +end diff --git a/test/runtests.jl b/test/runtests.jl index cebbfb63a..27befbd6b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -26,3 +26,6 @@ for f ∈ readdir("ITF1788_tests"; join = true) include(f) end end + +# Aqua tests +include("aqua.jl")