From 4abc6fe2fd96c8b6b9c63ba4bb78705efcf4210c Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 4 Jun 2025 13:55:53 +0530 Subject: [PATCH 1/6] Iterator norm in `isapprox` for `Array`s --- src/generic.jl | 8 +++++++- test/generic.jl | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/generic.jl b/src/generic.jl index c8336f87..ef4b89f1 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -2004,7 +2004,7 @@ function isapprox(x::AbstractArray, y::AbstractArray; atol::Real=0, rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), nans::Bool=false, norm::Function=norm) - d = norm(x - y) + d = norm_x_minus_y(x, y) if isfinite(d) return iszero(rtol) ? d <= atol : d <= max(atol, rtol*max(norm(x), norm(y))) else @@ -2014,6 +2014,12 @@ function isapprox(x::AbstractArray, y::AbstractArray; end end +norm_x_minus_y(x, y) = norm(x - y) +function norm_x_minus_y(x::Array, y::Array) + Base.promote_shape(size(x), size(y)) # ensure same size + norm(Iterators.map(splat(-), zip(x,y))) +end + """ normalize!(a::AbstractArray, p::Real=2) diff --git a/test/generic.jl b/test/generic.jl index 4fd48a6f..1983166b 100644 --- a/test/generic.jl +++ b/test/generic.jl @@ -938,4 +938,11 @@ end @test B == A2 end +@testset "isapprox alloc" begin + A = rand(3,3) + isapprox(A, A) + n = @allocated isapprox(A, A) + @test n == 0 +end + end # module TestGeneric From abfb65d471ceb42654affd46ba063cb691b29e2d Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 4 Jun 2025 15:40:48 +0530 Subject: [PATCH 2/6] Handle empty arrays --- src/generic.jl | 7 ++++++- test/generic.jl | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index ef4b89f1..55a328eb 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -2004,7 +2004,12 @@ function isapprox(x::AbstractArray, y::AbstractArray; atol::Real=0, rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), nans::Bool=false, norm::Function=norm) - d = norm_x_minus_y(x, y) + d = if isempty(x) && isempty(y) + Base.promote_shape(size(x), size(y)) # ensure same size + norm(zero(eltype(x)) - zero(eltype(y))) + else + norm_x_minus_y(x, y) + end if isfinite(d) return iszero(rtol) ? d <= atol : d <= max(atol, rtol*max(norm(x), norm(y))) else diff --git a/test/generic.jl b/test/generic.jl index 1983166b..576781cc 100644 --- a/test/generic.jl +++ b/test/generic.jl @@ -938,11 +938,12 @@ end @test B == A2 end -@testset "isapprox alloc" begin +@testset "isapprox for Arrays" begin A = rand(3,3) isapprox(A, A) n = @allocated isapprox(A, A) @test n == 0 + @test Int[] ≈ Int[] end end # module TestGeneric From 4906add57fc76b752235259f8fb3bc84819a508f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 13 Jun 2025 20:33:15 +0530 Subject: [PATCH 3/6] Specialize for strided contiguous `SubArray`s --- src/generic.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 55a328eb..83185926 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -2004,8 +2004,8 @@ function isapprox(x::AbstractArray, y::AbstractArray; atol::Real=0, rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), nans::Bool=false, norm::Function=norm) + Base.promote_shape(size(x), size(y)) # ensure compatible size d = if isempty(x) && isempty(y) - Base.promote_shape(size(x), size(y)) # ensure same size norm(zero(eltype(x)) - zero(eltype(y))) else norm_x_minus_y(x, y) @@ -2020,8 +2020,8 @@ function isapprox(x::AbstractArray, y::AbstractArray; end norm_x_minus_y(x, y) = norm(x - y) -function norm_x_minus_y(x::Array, y::Array) - Base.promote_shape(size(x), size(y)) # ensure same size +const ArrayOrFastContiguousSubArrayStrided = Union{Array, FastContiguousSubArrayStrided} +function norm_x_minus_y(x::ArrayOrFastContiguousSubArrayStrided, y::ArrayOrFastContiguousSubArrayStrided) norm(Iterators.map(splat(-), zip(x,y))) end From 2a7518ea93b457d3b4d40988c19e17f5446ec3af Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 20 Jun 2025 15:59:40 +0530 Subject: [PATCH 4/6] Limit to Array views --- src/generic.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 83185926..c0090c10 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -2020,8 +2020,9 @@ function isapprox(x::AbstractArray, y::AbstractArray; end norm_x_minus_y(x, y) = norm(x - y) -const ArrayOrFastContiguousSubArrayStrided = Union{Array, FastContiguousSubArrayStrided} -function norm_x_minus_y(x::ArrayOrFastContiguousSubArrayStrided, y::ArrayOrFastContiguousSubArrayStrided) +FastContiguousArrayView{T,N,P<:Array,I<:Tuple{AbstractUnitRange, Vararg{Any}}} = Base.SubArray{T,N,P,I,true} +const ArrayOrFastContiguousArrayView = Union{Array, FastContiguousArrayView} +function norm_x_minus_y(x::ArrayOrFastContiguousArrayView, y::ArrayOrFastContiguousArrayView) norm(Iterators.map(splat(-), zip(x,y))) end From 35c5dd3353f7681233334c7c8dce537cb4a39fdd Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 20 Jun 2025 16:02:34 +0530 Subject: [PATCH 5/6] Remove initial call before `@allocated` test --- test/generic.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/generic.jl b/test/generic.jl index 576781cc..c6037315 100644 --- a/test/generic.jl +++ b/test/generic.jl @@ -940,7 +940,6 @@ end @testset "isapprox for Arrays" begin A = rand(3,3) - isapprox(A, A) n = @allocated isapprox(A, A) @test n == 0 @test Int[] ≈ Int[] From ef309a63aa372de06c228352900a826f8e792c92 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 2 Jul 2025 03:02:12 +0530 Subject: [PATCH 6/6] Move checks to within `norm_x_minus_y` method for `Array`s --- src/generic.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index c0090c10..85e029c7 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -2004,12 +2004,7 @@ function isapprox(x::AbstractArray, y::AbstractArray; atol::Real=0, rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), nans::Bool=false, norm::Function=norm) - Base.promote_shape(size(x), size(y)) # ensure compatible size - d = if isempty(x) && isempty(y) - norm(zero(eltype(x)) - zero(eltype(y))) - else - norm_x_minus_y(x, y) - end + d = norm_x_minus_y(x, y) if isfinite(d) return iszero(rtol) ? d <= atol : d <= max(atol, rtol*max(norm(x), norm(y))) else @@ -2023,7 +2018,12 @@ norm_x_minus_y(x, y) = norm(x - y) FastContiguousArrayView{T,N,P<:Array,I<:Tuple{AbstractUnitRange, Vararg{Any}}} = Base.SubArray{T,N,P,I,true} const ArrayOrFastContiguousArrayView = Union{Array, FastContiguousArrayView} function norm_x_minus_y(x::ArrayOrFastContiguousArrayView, y::ArrayOrFastContiguousArrayView) - norm(Iterators.map(splat(-), zip(x,y))) + Base.promote_shape(size(x), size(y)) # ensure compatible size + if isempty(x) && isempty(y) + norm(zero(eltype(x)) - zero(eltype(y))) + else + norm(Iterators.map(splat(-), zip(x,y))) + end end """