diff --git a/src/SparseArrays.jl b/src/SparseArrays.jl index 4f319880..20110d14 100644 --- a/src/SparseArrays.jl +++ b/src/SparseArrays.jl @@ -382,7 +382,7 @@ julia> collect(SparseArrays.iternz(x)) """ function iternz end -widelength(x::AbstractSparseArray) = prod(widen, size(x)) +widelength(x::AbstractSparseArray) = *(map(widen, size(x))...) # Linear indexing compares an index with `length(A)`, which wraps for 2^63 entries or more _checklinearindexing(A::AbstractSparseMatrixCSC) = widelength(A) <= typemax(Int) || throw(OverflowError(string("a ", size(A, 1), "×", size(A, 2), " sparse matrix has more ", diff --git a/src/constructors.jl b/src/constructors.jl index bf3beb05..9eebafe4 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -714,43 +714,23 @@ julia> sprand(Float64, 3, 0.75) """ function sprand(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat, rfn::Function, ::Type{T}) where T m, n = Int(m), Int(n) - if _sprandoverflows(m, n, density) - colptr, rowval, V = _sprand_bycolumn(r, m, n, density, rfn) - return SparseMatrixCSC(m, n, colptr, rowval, convert(Vector{T}, V)) - end I, V = _sprandvalues(r, m, n, density, rfn) return sparse_sortedlinearindices!(I, convert(Vector{T}, V), m, n) end function sprand(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat, rfn::Function) m, n = Int(m), Int(n) - if _sprandoverflows(m, n, density) - colptr, rowval, V = _sprand_bycolumn(r, m, n, density, rfn) - return SparseMatrixCSC(m, n, colptr, rowval, convert(Vector{eltype(V)}, V)) - end I, V = _sprandvalues(r, m, n, density, rfn) return sparse_sortedlinearindices!(I, convert(Vector{eltype(V)}, V), m, n) end -# the arguments are checked first, since negative dimensions can overflow too -function _sprandoverflows(m::Int, n::Int, density::AbstractFloat) +function _sprandvalues(r::AbstractRNG, m::Int, n::Int, density::AbstractFloat, rfn::F) where {F} (m < 0 || n < 0) && throw(ArgumentError("invalid Array dimensions")) 0 <= density <= 1 || throw(ArgumentError("$density not in [0,1]")) - return Base.mul_with_overflow(m, n)[2] -end -function _sprandvalues(r::AbstractRNG, m::Int, n::Int, density::AbstractFloat, rfn::F) where {F} + # the entries are drawn as linear indices, which wrap for 2^63 entries or more + Base.mul_with_overflow(m, n)[2] && throw(OverflowError(string("a ", m, "×", n, + " sparse matrix has more entries than `sprand` can draw from"))) I = randsubseq(r, 1:(m*n), density) return I, rfn(r, length(I)) end -# an `Int` cannot index the `m*n` entries, so the stored rows are drawn column by column -function _sprand_bycolumn(r::AbstractRNG, m::Int, n::Int, density::AbstractFloat, rfn::F) where {F} - colptr = Vector{Int}(undef, n + 1) - colptr[1] = 1 - rowval, rows = Int[], Int[] - for col in 1:n - append!(rowval, randsubseq!(r, rows, 1:m, density)) - colptr[col + 1] = length(rowval) + 1 - end - return colptr, rowval, rfn(r, length(rowval)) -end sprand(m::Integer, n::Integer, density::AbstractFloat, rfn::Function) = sprand(default_rng(), m, n, density, (r, i) -> rfn(i)) diff --git a/src/sparsematrix.jl b/src/sparsematrix.jl index 010e0dee..02a782af 100644 --- a/src/sparsematrix.jl +++ b/src/sparsematrix.jl @@ -319,7 +319,7 @@ function _storedinds(S::SparseMatrixCSCColumnSubset) end return inds end -widelength(S::SparseMatrixCSCColumnSubset) = prod(widen, size(S)) +widelength(S::SparseMatrixCSCColumnSubset) = *(map(widen, size(S))...) widelength(x::Union{SparseColumnView,SparseVectorView}) = length(x) # `A` itself where it exposes compressed storage through `nzrange`, `rowvals` and `nonzeros`, # otherwise its sparse copy @@ -984,7 +984,7 @@ similar(S::AbstractSparseMatrixCSC, ::Type{TvNew}, ::Type{TiNew}, m::Integer, n: _sparsesimilar(S, TvNew, TiNew, (m, n)) function Base.sizehint!(S::SparseMatrixCSC, n::Integer) - nhint = min(n, widelength(S)) + nhint = Int(min(n, widelength(S))) sizehint!(getrowval(S), nhint) sizehint!(getnzval(S), nhint) return S diff --git a/test/issues.jl b/test/issues.jl index 647e16e6..b765ce0f 100644 --- a/test/issues.jl +++ b/test/issues.jl @@ -979,7 +979,7 @@ end @test_throws OverflowError B[[1]] = [1.0] @test_throws OverflowError B[1] = 1.0 @test nnz(B) == 0 - @test nnz(sprand(Xoshiro(1), 2^62, 4, 1e-17)) > 0 + @test_throws OverflowError sprand(Xoshiro(1), 2^62, 4, 1e-17) end if isdefined(Docs, :undocumented_names) # new in Julia 1.11