From 8272a5c39aef585d528622c62c1ddc9a7407726b Mon Sep 17 00:00:00 2001 From: Jerry Ling Date: Tue, 26 Apr 2022 14:16:45 -0400 Subject: [PATCH 1/6] fix findmin/findmax (#93) Co-authored-by: Steven G. Johnson --- src/sparsevector.jl | 7 ++++++- test/sparsevector.jl | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/sparsevector.jl b/src/sparsevector.jl index 1f3d0793..e17ae607 100644 --- a/src/sparsevector.jl +++ b/src/sparsevector.jl @@ -1434,7 +1434,12 @@ for (fun, comp, word) in ((:findmin, :(<), "minimum"), (:findmax, :(>), "maximum # we try to avoid findfirst(iszero, x) sindex = findfirst(iszero, nzvals) # first stored zero, if any zindex = findfirst(i -> i < nzinds[i], eachindex(nzinds)) # first non-stored zero - index = isnothing(sindex) ? zindex : min(sindex, zindex) + index = if isnothing(sindex) + # non-stored zero are contiguous and at the end + isnothing(zindex) && last(nzinds) < lastindex(x) ? last(nzinds) + 1 : zindex + else + min(sindex, zindex) + end return zeroval, index end end diff --git a/test/sparsevector.jl b/test/sparsevector.jl index 6eaaba98..53b8060d 100644 --- a/test/sparsevector.jl +++ b/test/sparsevector.jl @@ -898,6 +898,24 @@ end @test_throws ArgumentError findmin(x) @test_throws ArgumentError findmax(x) end + + let v = spzeros(3) #Julia #44978 + v[1] = 2 + @test argmin(v) == 2 + @test argmax(v) == 1 + v[2] = 2 + @test argmin(v) == 3 + v[1] = 0 + v[2] = 0 + v[3] = 2 + @test argmin(v) == 1 + @test argmax(v) == 3 + end + + let v = spzeros(3) #Julia #44978 + v[3] = 2 + @test argmax(v) == 3 + end end ### linalg From 2cae325bd30ce36f451fdded55aacf1ee18713b7 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 13 Sep 2022 19:54:43 +0200 Subject: [PATCH 2/6] On the backports-v1.8 branch, run CI on v1.8 (#256) --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87fd07a3..f185c6c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: fail-fast: false matrix: version: - # - '1.6' - - 'nightly' + - '1.8' + # - 'nightly' os: - ubuntu-latest - macOS-latest @@ -63,8 +63,8 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@latest with: - # version: '1.6' - version: 'nightly' + version: '1.8' + # version: 'nightly' - name: Generate docs run: | julia --color=yes -e 'write("Project.toml", replace(read("Project.toml", String), r"uuid = .*?\n" =>"uuid = \"3f01184e-e22b-5df5-ae63-d93ebab69eaf\"\n"));' From 48c37e7f673f2ec38141f15668d7fa583ecf1266 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 13 Sep 2022 21:48:14 +0200 Subject: [PATCH 3/6] Run CI on PRs into `backports-release-*` (#258) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f185c6c0..125e0b41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: branches: - 'main' - 'release-*' + - 'backports-release-*' push: branches: - 'main' From e946f6a21a55ececf2503d330237f92f6ccbbd40 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 14 Sep 2022 10:33:05 +0200 Subject: [PATCH 4/6] Backport: Fixes to `vcat` of vectors and numbers (#253) (#257) --- src/sparsevector.jl | 27 +++++++++++++++++---------- test/sparsevector.jl | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/sparsevector.jl b/src/sparsevector.jl index e17ae607..2b56fe17 100644 --- a/src/sparsevector.jl +++ b/src/sparsevector.jl @@ -1075,20 +1075,27 @@ const _Annotated_SparseConcatArrays = Union{_Triangular_SparseConcatArrays, _Sym const _SparseConcatGroup = Union{_DenseConcatGroup, _SparseConcatArrays, _Annotated_SparseConcatArrays} # Concatenations involving un/annotated sparse/special matrices/vectors should yield sparse arrays + +# the output array type is determined by the first element of the to be concatenated objects +# if this is a Number, the output would be dense by the fallback abstractarray.jl code (see cat_similar) +# so make sure that if that happens, the "array" is sparse (if more sparse arrays are involved, of course) +_sparse(x::Number) = sparsevec([1], [x], 1) +_sparse(A) = _makesparse(A) _makesparse(x::Number) = x -_makesparse(x::AbstractArray) = SparseMatrixCSC(issparse(x) ? x : sparse(x)) +_makesparse(x::AbstractVector) = convert(SparseVector, issparse(x) ? x : sparse(x))::SparseVector +_makesparse(x::AbstractMatrix) = convert(SparseMatrixCSC, issparse(x) ? x : sparse(x))::SparseMatrixCSC function Base._cat(dims, Xin::_SparseConcatGroup...) - X = map(_makesparse, Xin) + X = (_sparse(first(Xin)), map(_makesparse, Base.tail(Xin))...) T = promote_eltype(Xin...) Base.cat_t(T, X...; dims=dims) end function hcat(Xin::_SparseConcatGroup...) - X = map(_makesparse, Xin) + X = (_sparse(first(Xin)), map(_makesparse, Base.tail(Xin))...) return cat(X..., dims=Val(2)) end function vcat(Xin::_SparseConcatGroup...) - X = map(_makesparse, Xin) + X = (_sparse(first(Xin)), map(_makesparse, Base.tail(Xin))...) return cat(X..., dims=Val(1)) end hvcat(rows::Tuple{Vararg{Int}}, X::_SparseConcatGroup...) = @@ -1122,9 +1129,9 @@ Concatenate along dimension 2. Return a SparseMatrixCSC object. the concatenation with specialized "sparse" matrix types from LinearAlgebra.jl automatically yielded sparse output even in the absence of any SparseArray argument. """ -sparse_hcat(Xin::Union{AbstractVecOrMat,Number}...) = cat(map(_makesparse, Xin)..., dims=Val(2)) +sparse_hcat(Xin::Union{AbstractVecOrMat,Number}...) = cat(_sparse(first(Xin)), map(_makesparse, Base.tail(Xin))..., dims=Val(2)) function sparse_hcat(X::Union{AbstractVecOrMat,UniformScaling,Number}...) - LinearAlgebra._hcat(X...; array_type = SparseMatrixCSC) + LinearAlgebra._hcat(_sparse(first(X)), map(_makesparse, Base.tail(X))...; array_type = SparseMatrixCSC) end """ @@ -1137,9 +1144,9 @@ Concatenate along dimension 1. Return a SparseMatrixCSC object. the concatenation with specialized "sparse" matrix types from LinearAlgebra.jl automatically yielded sparse output even in the absence of any SparseArray argument. """ -sparse_vcat(Xin::Union{AbstractVecOrMat,Number}...) = cat(map(_makesparse, Xin)..., dims=Val(1)) +sparse_vcat(Xin::Union{AbstractVecOrMat,Number}...) = cat(_sparse(first(Xin)), map(_makesparse, Base.tail(Xin))..., dims=Val(1)) function sparse_vcat(X::Union{AbstractVecOrMat,UniformScaling,Number}...) - LinearAlgebra._vcat(X...; array_type = SparseMatrixCSC) + LinearAlgebra._vcat(_sparse(first(X)), map(_makesparse, Base.tail(X))...; array_type = SparseMatrixCSC) end """ @@ -1155,10 +1162,10 @@ arguments to concatenate in each block row. automatically yielded sparse output even in the absence of any SparseArray argument. """ function sparse_hvcat(rows::Tuple{Vararg{Int}}, Xin::Union{AbstractVecOrMat,Number}...) - hvcat(rows, map(_makesparse, Xin)...) + hvcat(rows, _sparse(first(Xin)), map(_makesparse, Base.tail(Xin))...) end function sparse_hvcat(rows::Tuple{Vararg{Int}}, X::Union{AbstractVecOrMat,UniformScaling,Number}...) - LinearAlgebra._hvcat(rows, X...; array_type = SparseMatrixCSC) + LinearAlgebra._hvcat(rows, _sparse(first(X)), map(_makesparse, Base.tail(X))...; array_type = SparseMatrixCSC) end ### math functions diff --git a/test/sparsevector.jl b/test/sparsevector.jl index 53b8060d..7d770ce9 100644 --- a/test/sparsevector.jl +++ b/test/sparsevector.jl @@ -537,6 +537,22 @@ end @test length(V) == m * n Vr = vec(Hr) @test Array(V) == Vr + Vnum = vcat(A..., zero(Float64)) + Vnum2 = sparse_vcat(map(Array, A)..., zero(Float64)) + @test Vnum isa SparseVector{Float64,Int} + @test Vnum2 isa SparseVector{Float64,Int} + @test length(Vnum) == length(Vnum2) == m*n + 1 + @test Array(Vnum) == Array(Vnum2) == [Vr; 0] + Vnum = vcat(zero(Float64), A...) + Vnum2 = sparse_vcat(zero(Float64), map(Array, A)...) + @test Vnum isa SparseVector{Float64,Int} + @test Vnum2 isa SparseVector{Float64,Int} + @test length(Vnum) == length(Vnum2) == m*n + 1 + @test Array(Vnum) == Array(Vnum2) == [0; Vr] + # case with rowwise a Number as first element, should still yield a sparse matrix + x = sparsevec([1], [3.0], 1) + X = [3.0 x; 3.0 x] + @test issparse(X) end @testset "concatenation of sparse vectors with other types" begin From d9a9e952f40d8acac3b3405e8e5c3f0bb31ed293 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 15 Sep 2022 14:16:18 +0200 Subject: [PATCH 5/6] Fix direction of circshift (#260) (cherry picked from commit 4fb8f0eb087ed5777f9d147e451dd6069cf28997) --- src/SparseArrays.jl | 5 +++++ src/sparsematrix.jl | 8 ++++---- src/sparsevector.jl | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/SparseArrays.jl b/src/SparseArrays.jl index 873a7fb3..3c609e54 100644 --- a/src/SparseArrays.jl +++ b/src/SparseArrays.jl @@ -11,6 +11,11 @@ using Base.Sort: Forward using LinearAlgebra using LinearAlgebra: AdjOrTrans, matprod +# Temporary workaround for simplifying SparseArrays.jl upgrade in JuliaLang/julia +# to workaround circshift! bug, see https://github.com/JuliaLang/julia/pull/46759 +const CIRCSHIFT_WRONG_DIRECTION = circshift!([1, 2, 3], 1) != circshift([1, 2, 3], 1) + + import Base: +, -, *, \, /, &, |, xor, ==, zero import LinearAlgebra: mul!, ldiv!, rdiv!, cholesky, adjoint!, diag, eigen, dot, issymmetric, istril, istriu, lu, tr, transpose!, tril!, triu!, isbanded, diff --git a/src/sparsematrix.jl b/src/sparsematrix.jl index e4547cc7..48c410dc 100644 --- a/src/sparsematrix.jl +++ b/src/sparsematrix.jl @@ -3938,16 +3938,16 @@ function Base.swaprows!(A::AbstractSparseMatrixCSC, i, j) rows[rr[iidx]] = j jidx == 0 && continue rotate_range = rr[iidx]:jrange[jidx] - circshift!(@view(vals[rotate_range]), -1) - circshift!(@view(rows[rotate_range]), -1) + circshift!(@view(vals[rotate_range]), CIRCSHIFT_WRONG_DIRECTION ? -1 : 1) + circshift!(@view(rows[rotate_range]), CIRCSHIFT_WRONG_DIRECTION ? -1 : 1) else # Same as i, but in the opposite direction @assert has_j rows[jrange[jidx]] = i iidx > length(rr) && continue rotate_range = rr[iidx]:jrange[jidx] - circshift!(@view(vals[rotate_range]), 1) - circshift!(@view(rows[rotate_range]), 1) + circshift!(@view(vals[rotate_range]), CIRCSHIFT_WRONG_DIRECTION ? 1 : -1) + circshift!(@view(rows[rotate_range]), CIRCSHIFT_WRONG_DIRECTION ? 1 : -1) end end return nothing diff --git a/src/sparsevector.jl b/src/sparsevector.jl index 2b56fe17..6e33b708 100644 --- a/src/sparsevector.jl +++ b/src/sparsevector.jl @@ -2132,8 +2132,8 @@ function subvector_shifter!(R::AbstractVector, V::AbstractVector, start::Integer end end # ...but rowval should be sorted within columns - circshift!(@view(R[start:fin]), split-start+1) - circshift!(@view(V[start:fin]), split-start+1) + circshift!(@view(R[start:fin]), (CIRCSHIFT_WRONG_DIRECTION ? (+) : (-))(split-start+1)) + circshift!(@view(V[start:fin]), (CIRCSHIFT_WRONG_DIRECTION ? (+) : (-))(split-start+1)) end function circshift!(O::SparseVector, X::SparseVector, (r,)::Base.DimsInteger{1}) From f7dc713e048cc127151b4de21c66b13418665370 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Fri, 16 Sep 2022 14:28:31 +0200 Subject: [PATCH 6/6] Let `length(::SparseVector{Tv,Ti})` return `Ti` (#176) (#262) --- src/sparsevector.jl | 1 + test/sparsevector.jl | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sparsevector.jl b/src/sparsevector.jl index 6e33b708..750096ef 100644 --- a/src/sparsevector.jl +++ b/src/sparsevector.jl @@ -40,6 +40,7 @@ const AdjOrTransSparseVectorUnion{Tv,Ti} = LinearAlgebra.AdjOrTrans{Tv, <:Sparse ### Basic properties +length(x::SparseVector) = getfield(x, :n) size(x::SparseVector) = (getfield(x, :n),) count(f, x::SparseVector) = count(f, nonzeros(x)) + f(zero(eltype(x)))*(length(x) - nnz(x)) diff --git a/test/sparsevector.jl b/test/sparsevector.jl index 7d770ce9..e53fc577 100644 --- a/test/sparsevector.jl +++ b/test/sparsevector.jl @@ -33,9 +33,12 @@ x1_full[SparseArrays.nonzeroinds(spv_x1)] = nonzeros(spv_x1) @test SparseArrays.nonzeroinds(x) == [2, 5, 6] @test nonzeros(x) == [1.25, -0.75, 3.5] @test count(SparseVector(8, [2, 5, 6], [true,false,true])) == 2 - y = SparseVector(typemax(Int128), Int128[4], [5]) + y = SparseVector(8, Int128[4], [5]) @test y isa SparseVector{Int,Int128} - @test @inferred size(y) == (@inferred(length(y)),) + @test @inferred size(y) == (@inferred(length(y))::Int128,) + y = SparseVector(8, Int8[4], [5.0]) + @test y isa SparseVector{Float64,Int8} + @test @inferred size(y) == (@inferred(length(y))::Int8,) end @testset "isstored" begin