From 1277ff9c2954a8fccc9c0f765b3e3dcd9cf8bfb7 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Sat, 25 Sep 2021 13:20:29 +0200 Subject: [PATCH] Cleanup weighted sum (no longer imported from Base) --- src/Statistics.jl | 4 +- src/wsum.jl | 438 ++++++++++++++++++++++------------------------ test/wsum.jl | 53 +++--- 3 files changed, 233 insertions(+), 262 deletions(-) diff --git a/src/Statistics.jl b/src/Statistics.jl index 505d4d2d..fca88fed 100644 --- a/src/Statistics.jl +++ b/src/Statistics.jl @@ -151,7 +151,7 @@ function _mean!(R::AbstractArray, A::AbstractArray, weights::Nothing) end _mean!(R::AbstractArray, A::AbstractArray, w::AbstractArray) = - rmul!(_sum!(R, A, weights=w), inv(sum(w))) + rmul!(wsum!(R, A, weights=w), inv(sum(w))) """ mean(A::AbstractArray; [dims], [weights::AbstractArray]) @@ -223,7 +223,7 @@ end # Note: weighted mean currently does not use _mean_promote to avoid overflow _mean(::typeof(identity), A::AbstractArray, dims::Colon, w::AbstractArray) = - _sum(A, weights=w) / sum(w) + wsum(A, weights=w) / sum(w) _mean(::typeof(identity), A::AbstractArray, dims, w::AbstractArray) = _mean!(Base.reducedim_init(t -> (t*zero(eltype(w)))/2, Base.add_sum, A, dims), A, w) diff --git a/src/wsum.jl b/src/wsum.jl index 417b1c4b..c2067a95 100644 --- a/src/wsum.jl +++ b/src/wsum.jl @@ -1,256 +1,234 @@ -# Weighted sum is only supported in Julia 1.4 and above -if VERSION >= v"1.4.0" - _sum = sum - _sum! = sum! -else - using Base: add_sum, reducedim_init, check_reducedims, safe_tail, reducedim1, axes1 - using LinearAlgebra: BlasReal +using Base: add_sum, reducedim_init, check_reducedims, safe_tail, reducedim1, axes1 +using LinearAlgebra: BlasReal - _sum(a::AbstractArray; dims=:, weights::Union{AbstractArray,Nothing}=nothing) = - _sum(a, dims, weights) - _sum(f, a::AbstractArray; dims=:, weights::Union{AbstractArray,Nothing}=nothing) = - _sum(f, a, dims, weights) - _sum(a::AbstractArray, ::Colon, weights) = _sum(identity, a, :, weights) - _sum(a::AbstractArray, ::Colon, ::Nothing) = sum(identity, a) - _sum(f, a, ::Colon, ::Nothing) = mapreduce(f, add_sum, a) +wsum(A::AbstractArray; dims=:, weights::AbstractArray) = + wsum(A, dims, weights) - _sum!(r::AbstractArray, A::AbstractArray; - init::Bool=true, weights::Union{AbstractArray,Nothing}=nothing) = - _sum!(identity, r, A; init=init, weights=weights) - _sum!(f::Function, r::AbstractArray, A::AbstractArray; - init::Bool=true, weights::Union{AbstractArray,Nothing}=nothing) = - _sum!(f, r, A, weights; init=init) - _sum!(f, r::AbstractArray, A::AbstractArray, ::Nothing; init::Bool=true) = - mapreducedim!(f, add_sum, initarray!(r, add_sum, init, A), A) - _sum(A::AbstractArray, dims, weights) = _sum(identity, A, dims, weights) - _sum(f, A::AbstractArray, dims, ::Nothing) = mapreduce(f, add_sum, A, dims=dims) - _sum(::typeof(identity), A::AbstractArray, dims, w::AbstractArray) = - _sum!(identity, reducedim_init(t -> t*zero(eltype(w)), add_sum, A, dims), A, w) - _sum(f, A::AbstractArray, dims, w::AbstractArray) = - throw(ArgumentError("Passing a function is not supported with `weights`")) +wsum(A::AbstractArray, dims, weights::AbstractArray) = + wsum!(reducedim_init(t -> t*zero(eltype(weights)), add_sum, A, dims), A, weights) +wsum!(r::AbstractArray, A::AbstractArray; + init::Bool=true, weights::AbstractArray) = + wsum!(r, A, weights; init=init) - # Weighted sum - function _sum(A::AbstractArray, dims::Colon, w::AbstractArray{<:Real}) - sw = size(w) - sA = size(A) - if sw != sA - throw(DimensionMismatch("weights must have the same dimension as data (got $sw and $sA).")) - end - s0 = zero(eltype(A)) * zero(eltype(w)) - s = add_sum(s0, s0) - @inbounds @simd for i in eachindex(A, w) - s += A[i] * w[i] - end - s +function wsum(A::AbstractArray, dims::Colon, w::AbstractArray{<:Real}) + sw = size(w) + sA = size(A) + if sw != sA + throw(DimensionMismatch("weights must have the same dimension as data (got $sw and $sA).")) end - - # Weighted sum over dimensions - # - # Brief explanation of the algorithm: - # ------------------------------------ - # - # 1. _wsum! provides the core implementation, which assumes that - # the dimensions of all input arguments are consistent, and no - # dimension checking is performed therein. - # - # wsum and wsum! perform argument checking and call _wsum! - # internally. - # - # 2. _wsum! adopt a Cartesian based implementation for general - # sub types of AbstractArray. Particularly, a faster routine - # that keeps a local accumulator will be used when dim = 1. - # - # The internal function that implements this is _wsum_general! - # - # 3. _wsum! is specialized for following cases: - # (a) A is a vector: we invoke the vector version wsum above. - # The internal function that implements this is _wsum1! - # - # (b) A is a dense matrix with eltype <: BlasReal: we call gemv! - # The internal function that implements this is _wsum2_blas! - # (in LinearAlgebra/src/wsum.jl) - # - # (c) A is a contiguous array with eltype <: BlasReal: - # dim == 1: treat A like a matrix of size (d1, d2 x ... x dN) - # dim == N: treat A like a matrix of size (d1 x ... x d(N-1), dN) - # otherwise: decompose A into multiple pages, and apply _wsum2_blas! - # for each - # The internal function that implements this is _wsumN! - # (in LinearAlgebra/src/wsum.jl) - # - # (d) A is a general dense array with eltype <: BlasReal: - # dim <= 2: delegate to (a) and (b) - # otherwise, decompose A into multiple pages - # The internal function that implements this is _wsumN! - # (in LinearAlgebra/src/wsum.jl) - - function _wsum1!(R::AbstractArray, A::AbstractVector, w::AbstractVector, init::Bool) - r = _sum(A, :, w) - if init - R[1] = r - else - R[1] += r - end - return R + s0 = zero(eltype(A)) * zero(eltype(w)) + s = add_sum(s0, s0) + @inbounds @simd for i in eachindex(A, w) + s = add_sum(s, A[i] * w[i]) end + s +end - function _wsum_general!(R::AbstractArray{S}, A::AbstractArray, w::AbstractVector, - dim::Int, init::Bool) where {S} - # following the implementation of _mapreducedim! - lsiz = check_reducedims(R,A) - !isempty(R) && init && fill!(R, zero(S)) - isempty(A) && return R +# Weighted sum over dimensions +# +# Brief explanation of the algorithm: +# ------------------------------------ +# +# 1. _wsum! provides the core implementation, which assumes that +# the dimensions of all input arguments are consistent, and no +# dimension checking is performed therein. +# +# wsum and wsum! perform argument checking and call _wsum! +# internally. +# +# 2. _wsum! adopt a Cartesian based implementation for general +# sub types of AbstractArray. Particularly, a faster routine +# that keeps a local accumulator will be used when dim = 1. +# +# The internal function that implements this is _wsum_general! +# +# 3. _wsum! is specialized for following cases: +# (a) A is a vector: we invoke the vector version wsum above. +# The internal function that implements this is _wsum1! +# +# (b) A is a dense matrix with eltype <: BlasReal: we call gemv! +# The internal function that implements this is _wsum2_blas! +# (in LinearAlgebra/src/wsum.jl) +# +# (c) A is a contiguous array with eltype <: BlasReal: +# dim == 1: treat A like a matrix of size (d1, d2 x ... x dN) +# dim == N: treat A like a matrix of size (d1 x ... x d(N-1), dN) +# otherwise: decompose A into multiple pages, and apply _wsum2_blas! +# for each +# The internal function that implements this is _wsumN! +# (in LinearAlgebra/src/wsum.jl) +# +# (d) A is a general dense array with eltype <: BlasReal: +# dim <= 2: delegate to (a) and (b) +# otherwise, decompose A into multiple pages +# The internal function that implements this is _wsumN! +# (in LinearAlgebra/src/wsum.jl) + +function _wsum1!(R::AbstractArray, A::AbstractVector, w::AbstractVector, init::Bool) + r = wsum(A, :, w) + if init + R[1] = r + else + R[1] += r + end + return R +end - indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(R)) # handle d=1 manually - keep, Idefault = Broadcast.shapeindexer(indsRt) - if reducedim1(R, A) - i1 = first(axes1(R)) - for IA in CartesianIndices(indsAt) - IR = Broadcast.newindex(IA, keep, Idefault) - r = R[i1,IR] - @inbounds @simd for i in axes(A, 1) - r += A[i,IA] * w[dim > 1 ? IA[dim-1] : i] - end - R[i1,IR] = r +function _wsum_general!(R::AbstractArray{S}, A::AbstractArray, w::AbstractVector, + dim::Int, init::Bool) where {S} + # following the implementation of _mapreducedim! + lsiz = check_reducedims(R,A) + !isempty(R) && init && fill!(R, zero(S)) + isempty(A) && return R + + indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(R)) # handle d=1 manually + keep, Idefault = Broadcast.shapeindexer(indsRt) + if reducedim1(R, A) + i1 = first(axes1(R)) + for IA in CartesianIndices(indsAt) + IR = Broadcast.newindex(IA, keep, Idefault) + r = R[i1,IR] + @inbounds @simd for i in axes(A, 1) + r += A[i,IA] * w[dim > 1 ? IA[dim-1] : i] end - else - for IA in CartesianIndices(indsAt) - IR = Broadcast.newindex(IA, keep, Idefault) - @inbounds @simd for i in axes(A, 1) - R[i,IR] += A[i,IA] * w[dim > 1 ? IA[dim-1] : i] - end + R[i1,IR] = r + end + else + for IA in CartesianIndices(indsAt) + IR = Broadcast.newindex(IA, keep, Idefault) + @inbounds @simd for i in axes(A, 1) + R[i,IR] += A[i,IA] * w[dim > 1 ? IA[dim-1] : i] end end - return R end + return R +end - _wsum!(R::AbstractArray, A::AbstractVector, w::AbstractVector, - dim::Int, init::Bool) = - _wsum1!(R, A, w, init) - - _wsum!(R::AbstractArray, A::AbstractArray, w::AbstractVector, - dim::Int, init::Bool) = - _wsum_general!(R, A, w, dim, init) +_wsum!(R::AbstractArray, A::AbstractVector, w::AbstractVector, + dim::Int, init::Bool) = + _wsum1!(R, A, w, init) + +_wsum!(R::AbstractArray, A::AbstractArray, w::AbstractVector, + dim::Int, init::Bool) = + _wsum_general!(R, A, w, dim, init) + +function wsum!(R::AbstractArray, A::AbstractArray{T,N}, w::AbstractArray; + init::Bool=true) where {T,N} + w isa AbstractVector || throw(ArgumentError("Only vector `weights` are supported")) + + check_reducedims(R,A) + reddims = size(R) .!= size(A) + dim = something(findfirst(reddims), ndims(R)+1) + if dim > N + dim1 = findfirst(==(1), size(A)) + if dim1 !== nothing + dim = dim1 + end + end + if findnext(reddims, dim+1) !== nothing + throw(ArgumentError("reducing over more than one dimension is not supported with weights")) + end + lw = length(w) + ldim = size(A, dim) + if lw != ldim + throw(DimensionMismatch("weights must have the same length as the dimension " * + "over which reduction is performed (got $lw and $ldim).")) + end + _wsum!(R, A, w, dim, init) +end - function _sum!(f, R::AbstractArray, A::AbstractArray{T,N}, w::AbstractArray; - init::Bool=true) where {T,N} - f === identity || throw(ArgumentError("Passing a function is not supported with `weights`")) - w isa AbstractVector || throw(ArgumentError("Only vector `weights` are supported")) +# Optimized method for weighted sum with BlasReal +# dot cannot be used for other types as it uses + rather than add_sum for accumulation, +# and therefore does not return the correct type +wsum(A::AbstractArray{T}, dims::Colon, w::AbstractArray{T}) where {T<:BlasReal} = + dot(vec(A), vec(w)) + +# Optimized methods for weighted sum over dimensions with BlasReal +# (generic method is defined in base/reducedim.jl) +# +# _wsum! is specialized for following cases: +# (a) A is a dense matrix with eltype <: BlasReal: we call gemv! +# The internal function that implements this is _wsum2_blas! +# +# (b) A is a contiguous array with eltype <: BlasReal: +# dim == 1: treat A like a matrix of size (d1, d2 x ... x dN) +# dim == N: treat A like a matrix of size (d1 x ... x d(N-1), dN) +# otherwise: decompose A into multiple pages, and apply _wsum2_blas! +# for each +# The internal function that implements this is _wsumN! +# +# (c) A is a general dense array with eltype <: BlasReal: +# dim <= 2: delegate to (a) and (b) +# otherwise, decompose A into multiple pages +# The internal function that implements this is _wsumN! + +function _wsum2_blas!(R::StridedVector{T}, A::StridedMatrix{T}, w::StridedVector{T}, + dim::Int, init::Bool) where T<:BlasReal + beta = ifelse(init, zero(T), one(T)) + trans = dim == 1 ? 'T' : 'N' + BLAS.gemv!(trans, one(T), A, w, beta, R) + return R +end - check_reducedims(R,A) - reddims = size(R) .!= size(A) - dim = something(findfirst(reddims), ndims(R)+1) - if dim > N - dim1 = findfirst(==(1), size(A)) - if dim1 !== nothing - dim = dim1 - end +function _wsumN!(R::StridedArray{T}, A::StridedArray{T,N}, w::StridedVector{T}, + dim::Int, init::Bool) where {T<:BlasReal,N} + if dim == 1 + m = size(A, 1) + n = div(length(A), m) + _wsum2_blas!(view(R,:), reshape(A, (m, n)), w, 1, init) + elseif dim == N + n = size(A, N) + m = div(length(A), n) + _wsum2_blas!(view(R,:), reshape(A, (m, n)), w, 2, init) + else # 1 < dim < N + m = 1 + for i = 1:dim-1 + m *= size(A, i) end - if findnext(reddims, dim+1) !== nothing - throw(ArgumentError("reducing over more than one dimension is not supported with weights")) + n = size(A, dim) + k = 1 + for i = dim+1:N + k *= size(A, i) end - lw = length(w) - ldim = size(A, dim) - if lw != ldim - throw(DimensionMismatch("weights must have the same length as the dimension " * - "over which reduction is performed (got $lw and $ldim).")) + Av = reshape(A, (m, n, k)) + Rv = reshape(R, (m, k)) + for i = 1:k + _wsum2_blas!(view(Rv,:,i), view(Av,:,:,i), w, 2, init) end - _wsum!(R, A, w, dim, init) - end - - # Optimized method for weighted sum with BlasReal - # dot cannot be used for other types as it uses + rather than add_sum for accumulation, - # and therefore does not return the correct type - _sum(A::AbstractArray{T}, dims::Colon, w::AbstractArray{T}) where {T<:BlasReal} = - dot(vec(A), vec(w)) - - # Optimized methods for weighted sum over dimensions with BlasReal - # (generic method is defined in base/reducedim.jl) - # - # _wsum! is specialized for following cases: - # (a) A is a dense matrix with eltype <: BlasReal: we call gemv! - # The internal function that implements this is _wsum2_blas! - # - # (b) A is a contiguous array with eltype <: BlasReal: - # dim == 1: treat A like a matrix of size (d1, d2 x ... x dN) - # dim == N: treat A like a matrix of size (d1 x ... x d(N-1), dN) - # otherwise: decompose A into multiple pages, and apply _wsum2_blas! - # for each - # The internal function that implements this is _wsumN! - # - # (c) A is a general dense array with eltype <: BlasReal: - # dim <= 2: delegate to (a) and (b) - # otherwise, decompose A into multiple pages - # The internal function that implements this is _wsumN! - - function _wsum2_blas!(R::StridedVector{T}, A::StridedMatrix{T}, w::StridedVector{T}, - dim::Int, init::Bool) where T<:BlasReal - beta = ifelse(init, zero(T), one(T)) - trans = dim == 1 ? 'T' : 'N' - BLAS.gemv!(trans, one(T), A, w, beta, R) - return R end + return R +end - function _wsumN!(R::StridedArray{T}, A::StridedArray{T,N}, w::StridedVector{T}, - dim::Int, init::Bool) where {T<:BlasReal,N} - if dim == 1 - m = size(A, 1) - n = div(length(A), m) - _wsum2_blas!(view(R,:), reshape(A, (m, n)), w, 1, init) - elseif dim == N - n = size(A, N) - m = div(length(A), n) - _wsum2_blas!(view(R,:), reshape(A, (m, n)), w, 2, init) - else # 1 < dim < N - m = 1 - for i = 1:dim-1 - m *= size(A, i) - end - n = size(A, dim) - k = 1 - for i = dim+1:N - k *= size(A, i) - end - Av = reshape(A, (m, n, k)) - Rv = reshape(R, (m, k)) - for i = 1:k - _wsum2_blas!(view(Rv,:,i), view(Av,:,:,i), w, 2, init) - end +function _wsumN!(R::StridedArray{T}, A::DenseArray{T,N}, w::StridedVector{T}, + dim::Int, init::Bool) where {T<:BlasReal,N} + @assert N >= 3 + if dim <= 2 + m = size(A, 1) + n = size(A, 2) + npages = 1 + for i = 3:N + npages *= size(A, i) end - return R - end - - function _wsumN!(R::StridedArray{T}, A::DenseArray{T,N}, w::StridedVector{T}, - dim::Int, init::Bool) where {T<:BlasReal,N} - @assert N >= 3 - if dim <= 2 - m = size(A, 1) - n = size(A, 2) - npages = 1 - for i = 3:N - npages *= size(A, i) - end - rlen = ifelse(dim == 1, n, m) - Rv = reshape(R, (rlen, npages)) - for i = 1:npages - _wsum2_blas!(view(Rv,:,i), view(A,:,:,i), w, dim, init) - end - else - _wsum_general!(R, A, w, dim, init) + rlen = ifelse(dim == 1, n, m) + Rv = reshape(R, (rlen, npages)) + for i = 1:npages + _wsum2_blas!(view(Rv,:,i), view(A,:,:,i), w, dim, init) end - return R + else + _wsum_general!(R, A, w, dim, init) end + return R +end - _wsum!(R::StridedArray{T}, A::DenseMatrix{T}, w::StridedVector{T}, - dim::Int, init::Bool) where {T<:BlasReal} = - _wsum2_blas!(view(R,:), A, w, dim, init) +_wsum!(R::StridedArray{T}, A::DenseMatrix{T}, w::StridedVector{T}, + dim::Int, init::Bool) where {T<:BlasReal} = + _wsum2_blas!(view(R,:), A, w, dim, init) - _wsum!(R::StridedArray{T}, A::DenseArray{T}, w::StridedVector{T}, - dim::Int, init::Bool) where {T<:BlasReal} = - _wsumN!(R, A, w, dim, init) +_wsum!(R::StridedArray{T}, A::DenseArray{T}, w::StridedVector{T}, + dim::Int, init::Bool) where {T<:BlasReal} = + _wsumN!(R, A, w, dim, init) - _wsum!(R::StridedVector{T}, A::DenseArray{T}, w::StridedVector{T}, - dim::Int, init::Bool) where {T<:BlasReal} = - _wsum1!(R, A, w, init) -end +_wsum!(R::StridedVector{T}, A::DenseArray{T}, w::StridedVector{T}, + dim::Int, init::Bool) where {T<:BlasReal} = + _wsum1!(R, A, w, init) \ No newline at end of file diff --git a/test/wsum.jl b/test/wsum.jl index 39ad8570..2fda0ce7 100644 --- a/test/wsum.jl +++ b/test/wsum.jl @@ -1,5 +1,5 @@ using Random -using Statistics: _sum, _sum! +using Statistics: wsum, wsum! @testset "weighted sum" begin wts = ([1.4, 2.5, 10.1], [1.4f0, 2.5f0, 10.1f0], [0.0, 2.3, 5.6], @@ -7,8 +7,8 @@ using Statistics: _sum, _sum! [2, 1, 3], Int8[1, 2, 3], [1, 1, 1]) for a in (rand(3), rand(Int, 3), rand(Int8, 3)) for w in wts - res = @inferred _sum(a, weights=w) - expected = _sum(a.*w) + res = @inferred wsum(a, weights=w) + expected = sum(a.*w) if isfinite(res) @test res ≈ expected else @@ -20,8 +20,8 @@ using Statistics: _sum, _sum! for a in (rand(3, 5), rand(Float32, 3, 5), rand(Int, 3, 5), rand(Int8, 3, 5)) for w in wts wr = repeat(w, outer=(1, 5)) - res = @inferred _sum(a, weights=wr) - expected = _sum(a.*wr) + res = @inferred wsum(a, weights=wr) + expected = sum(a.*wr) if isfinite(res) @test res ≈ expected else @@ -30,10 +30,6 @@ using Statistics: _sum, _sum! @test typeof(res) == typeof(expected) end end - - @test_throws ArgumentError _sum(exp, [1], weights=[1]) - @test_throws ArgumentError _sum!(exp, [0 0], [1 2], weights=[1, 10]) - @test_throws ArgumentError _sum!([0 0], [1 2], weights=[1 10]) end @testset "weighted sum over dimensions" begin @@ -50,28 +46,28 @@ end view(rand(5), 2:4)) for w in wts if all(isfinite, a) && all(isfinite, w) - expected = _sum(a.*w, dims=1) - res = @inferred _sum(a, weights=w, dims=1) + expected = sum(a.*w, dims=1) + res = @inferred wsum(a, weights=w, dims=1) @test res ≈ expected @test typeof(res) == typeof(expected) x = rand!(similar(expected)) y = copy(x) - @inferred _sum!(y, a, weights=w) + @inferred wsum!(y, a, weights=w) @test y ≈ expected y = copy(x) - @inferred _sum!(y, a, weights=w, init=false) + @inferred wsum!(y, a, weights=w, init=false) @test y ≈ x + expected else - expected = _sum(a.*w, dims=1) - res = @inferred _sum(a, weights=w, dims=1) + expected = sum(a.*w, dims=1) + res = @inferred wsum(a, weights=w, dims=1) @test isfinite.(res) == isfinite.(expected) @test typeof(res) == typeof(expected) x = rand!(similar(expected)) y = copy(x) - @inferred _sum!(y, a, weights=w) + @inferred wsum!(y, a, weights=w) @test isfinite.(y) == isfinite.(expected) y = copy(x) - @inferred _sum!(y, a, weights=w, init=false) + @inferred wsum!(y, a, weights=w, init=false) @test isfinite.(y) == isfinite.(expected) end end @@ -89,39 +85,36 @@ end (2, reshape(w, 1, :, 1)), (3, reshape(w, 1, 1, :))) if all(isfinite, a) && all(isfinite, w) - expected = _sum(a.*rw, dims=d) - res = @inferred _sum(a, weights=w, dims=d) + expected = sum(a.*rw, dims=d) + res = @inferred wsum(a, weights=w, dims=d) @test res ≈ expected @test typeof(res) == typeof(expected) x = rand!(similar(expected)) y = copy(x) - @inferred _sum!(y, a, weights=w) + @inferred wsum!(y, a, weights=w) @test y ≈ expected y = copy(x) - @inferred _sum!(y, a, weights=w, init=false) + @inferred wsum!(y, a, weights=w, init=false) @test y ≈ x + expected else - expected = _sum(a.*rw, dims=d) - res = @inferred _sum(a, weights=w, dims=d) + expected = sum(a.*rw, dims=d) + res = @inferred wsum(a, weights=w, dims=d) @test isfinite.(res) == isfinite.(expected) @test typeof(res) == typeof(expected) x = rand!(similar(expected)) y = copy(x) - @inferred _sum!(y, a, weights=w) + @inferred wsum!(y, a, weights=w) @test isfinite.(y) == isfinite.(expected) y = copy(x) - @inferred _sum!(y, a, weights=w, init=false) + @inferred wsum!(y, a, weights=w, init=false) @test isfinite.(y) == isfinite.(expected) end end - @test_throws DimensionMismatch _sum(a, weights=w, dims=4) + @test_throws DimensionMismatch wsum(a, weights=w, dims=4) end end # Corner case with a single row - @test _sum([1 2], weights=[2], dims=1) == [2 4] - - @test_throws ArgumentError _sum(exp, [1 2], weights=[1, 10], dims=1) - @test_throws ArgumentError _sum([1 2], weights=[1 10], dims=1) + @test wsum([1 2], weights=[2], dims=1) == [2 4] end