diff --git a/src/reductions.jl b/src/reductions.jl index 3930d243..4a80375f 100644 --- a/src/reductions.jl +++ b/src/reductions.jl @@ -93,10 +93,15 @@ _mapreducezeros(f::Base.ExtremaMap, op::typeof(Base._extrema_rf), ::Type{T}, nze nzeros == 0 ? v0 : op(v0, f(zero(T))) # Specialized mapreduce for any and all +# Each value of the predicate is checked to be Boolean, as the dense reductions check it: +# `|` and `&` would absorb another value next to a `missing`, or combine two of them. +_boolean(v::Union{Bool,Missing}) = v +_boolean(v) = v::Bool +_booleanpredicate(f::F) where {F} = _boolean ∘ f Base._any(f, A::SparseMatrixCSCOrColumnSubset, ::Colon) = - iszero(widelength(A)) ? false : Base._mapreduce(f, |, IndexCartesian(), A) + iszero(widelength(A)) ? false : Base._mapreduce(_booleanpredicate(f), |, IndexCartesian(), A) Base._all(f, A::SparseMatrixCSCOrColumnSubset, ::Colon) = - iszero(widelength(A)) ? true : Base._mapreduce(f, &, IndexCartesian(), A) + iszero(widelength(A)) ? true : Base._mapreduce(_booleanpredicate(f), &, IndexCartesian(), A) function Base._mapreduce(f::F, op::Union{typeof(Base.mul_prod),typeof(*)}, ::Base.IndexCartesian, A::SparseMatrixCSCOrColumnSubset{T}) where {F,T} nnzA = nnz(A) diff --git a/src/sparsevector.jl b/src/sparsevector.jl index b56a3d0f..ad424ebb 100644 --- a/src/sparsevector.jl +++ b/src/sparsevector.jl @@ -1525,9 +1525,9 @@ function Base._mapreduce(f::F, op::G, ::IndexCartesian, A::SparseVectorOrView) w end Base._any(f, A::SparseVectorOrView, ::Colon) = - iszero(length(A)) ? false : Base._mapreduce(f, |, IndexCartesian(), A) + iszero(length(A)) ? false : Base._mapreduce(_booleanpredicate(f), |, IndexCartesian(), A) Base._all(f, A::SparseVectorOrView, ::Colon) = - iszero(length(A)) ? true : Base._mapreduce(f, &, IndexCartesian(), A) + iszero(length(A)) ? true : Base._mapreduce(_booleanpredicate(f), &, IndexCartesian(), A) function Base.mapreducedim!(f::F, op::G, R::AbstractVector, A::SparseVectorOrView) where {F,G} isempty(A) && return R diff --git a/test/reductions.jl b/test/reductions.jl index 24f310da..122fdf0e 100644 --- a/test/reductions.jl +++ b/test/reductions.jl @@ -127,6 +127,17 @@ along(f::F, X, dims) where {F} = f(X; dims) @test count(v) == length(v) @test all(!iszero, spzeros(0, 0)) @test !any(iszero, spzeros(0, 0)) + @static if COMPREHENSIVE + # a predicate that is not Boolean is rejected, as for dense + for S in (sparse([1 0; 0 0]), sparsevec([1, 0])) + @test_throws TypeError any(S) + @test_throws TypeError all(S) + end + # also next to a `missing`, which `|` and `&` would let absorb the other value + S = sparse(reshape(Union{Missing,Int}[missing, 1], 2, 1)) + @test_throws TypeError any(S) + @test_throws TypeError all(S) + end end @testset "seed without copying the first slice" begin