From 35eb30246ee8218298402d4802d2e67872c8f532 Mon Sep 17 00:00:00 2001 From: Martin Kunz Date: Sat, 22 Feb 2025 09:23:32 +0100 Subject: [PATCH 1/3] add: [CartesianIndex...] sparse method A method for specifying the indexes to fill with values directly as CartesianIndices. --- src/sparsematrix.jl | 5 +++++ test/sparsematrix_constructors_indexing.jl | 1 + 2 files changed, 6 insertions(+) diff --git a/src/sparsematrix.jl b/src/sparsematrix.jl index 38cc21d5..d76f8dbd 100644 --- a/src/sparsematrix.jl +++ b/src/sparsematrix.jl @@ -1111,6 +1111,11 @@ end sparse(I::AbstractVector, J::AbstractVector, V::AbstractVector, m::Integer, n::Integer, combine) = sparse(AbstractVector{Int}(I), AbstractVector{Int}(J), V, m, n, combine) +function sparse(IJ::AbstractVector{Ci}, V::AbstractVector, m::Integer, n::Integer) where {Ci<:CartesianIndex} + IJ′ = reinterpret(Int, reshape(IJ, 1, :)) + return sparse(view(IJ′, 1, :), view(IJ′, 2, :), V, m, n) +end + """ sparse!(I::AbstractVector{Ti}, J::AbstractVector{Ti}, V::AbstractVector{Tv}, m::Integer, n::Integer, combine, klasttouch::Vector{Ti}, diff --git a/test/sparsematrix_constructors_indexing.jl b/test/sparsematrix_constructors_indexing.jl index a0396f40..517d57a3 100644 --- a/test/sparsematrix_constructors_indexing.jl +++ b/test/sparsematrix_constructors_indexing.jl @@ -52,6 +52,7 @@ end SparseMatrixCSC(6, 6, big.([1,2,4,7,8,9,9]), big.([1,1,2,1,2,3,4,5]), big.([1,2,3,4,5,6,7,8]))) @test sparse(Any[1,2,3], Any[1,2,3], Any[1,1,1]) == sparse([1,2,3], [1,2,3], [1,1,1]) @test sparse(Any[1,2,3], Any[1,2,3], Any[1,1,1], 5, 4) == sparse([1,2,3], [1,2,3], [1,1,1], 5, 4) + @test isequal(sparse([CartesianIndex(1, 1), CartesianIndex(2, 2), CartesianIndex(3, 3)], [1.0, 2.0, 3.0], 3, 3), sparse([1, 2, 3], [1, 2, 3], [1.0, 2.0, 3.0], 3, 3)) # with combine @test sparse([1, 1, 2, 2, 2], [1, 2, 1, 2, 2], 1.0, 2, 2, +) == sparse([1, 1, 2, 2], [1, 2, 1, 2], [1.0, 1.0, 1.0, 2.0], 2, 2) @test sparse([1, 1, 2, 2, 2], [1, 2, 1, 2, 2], -1.0, 2, 2, *) == sparse([1, 1, 2, 2], [1, 2, 1, 2], [-1.0, -1.0, -1.0, 1.0], 2, 2) From 0021a713b85c3e1b009f06c10435bd5cb0ed160d Mon Sep 17 00:00:00 2001 From: Martin Kunz Date: Tue, 25 Feb 2025 18:47:05 +0100 Subject: [PATCH 2/3] fix: Only allow 2D Cartesian indices in constructor NOTE: `CartesianIndex{2}` is no longer an abstract type so the function no longer needs to be parametric. --- src/sparsematrix.jl | 2 +- test/sparsematrix_constructors_indexing.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sparsematrix.jl b/src/sparsematrix.jl index d76f8dbd..817a74eb 100644 --- a/src/sparsematrix.jl +++ b/src/sparsematrix.jl @@ -1111,7 +1111,7 @@ end sparse(I::AbstractVector, J::AbstractVector, V::AbstractVector, m::Integer, n::Integer, combine) = sparse(AbstractVector{Int}(I), AbstractVector{Int}(J), V, m, n, combine) -function sparse(IJ::AbstractVector{Ci}, V::AbstractVector, m::Integer, n::Integer) where {Ci<:CartesianIndex} +function sparse(IJ::AbstractVector{CartesianIndex{2}}, V::AbstractVector, m::Integer, n::Integer) IJ′ = reinterpret(Int, reshape(IJ, 1, :)) return sparse(view(IJ′, 1, :), view(IJ′, 2, :), V, m, n) end diff --git a/test/sparsematrix_constructors_indexing.jl b/test/sparsematrix_constructors_indexing.jl index 517d57a3..2b7007bb 100644 --- a/test/sparsematrix_constructors_indexing.jl +++ b/test/sparsematrix_constructors_indexing.jl @@ -52,6 +52,7 @@ end SparseMatrixCSC(6, 6, big.([1,2,4,7,8,9,9]), big.([1,1,2,1,2,3,4,5]), big.([1,2,3,4,5,6,7,8]))) @test sparse(Any[1,2,3], Any[1,2,3], Any[1,1,1]) == sparse([1,2,3], [1,2,3], [1,1,1]) @test sparse(Any[1,2,3], Any[1,2,3], Any[1,1,1], 5, 4) == sparse([1,2,3], [1,2,3], [1,1,1], 5, 4) + @test_throws MethodError sparse([CartesianIndex(1, 1, 1), CartesianIndex(2, 2, 2), CartesianIndex(3, 3, 3)], [1, 2, 4], [1, 2, 3], 3, 3) # Only 2D indices should work @test isequal(sparse([CartesianIndex(1, 1), CartesianIndex(2, 2), CartesianIndex(3, 3)], [1.0, 2.0, 3.0], 3, 3), sparse([1, 2, 3], [1, 2, 3], [1.0, 2.0, 3.0], 3, 3)) # with combine @test sparse([1, 1, 2, 2, 2], [1, 2, 1, 2, 2], 1.0, 2, 2, +) == sparse([1, 1, 2, 2], [1, 2, 1, 2], [1.0, 1.0, 1.0, 2.0], 2, 2) From 42e29b6b2dac9454d8a350779063c4ed9cc9a578 Mon Sep 17 00:00:00 2001 From: Martin Kunz Date: Thu, 8 Jan 2026 22:03:16 +0100 Subject: [PATCH 3/3] docs: Document the CartesianIndex method of `sparse` Also fixed the methods to include all of the shortcuts (defaults) that the multiple vector method supports. --- src/sparsematrix.jl | 22 +++++++++++++++++----- test/sparsematrix_constructors_indexing.jl | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sparsematrix.jl b/src/sparsematrix.jl index 817a74eb..8d4c8ad3 100644 --- a/src/sparsematrix.jl +++ b/src/sparsematrix.jl @@ -1041,6 +1041,7 @@ sparse(D::Diagonal) = SparseMatrixCSC(D) """ sparse(I, J, V,[ m, n, combine]) + sparse(IJ, V,[ m, n, combine]) Create a sparse matrix `S` of dimensions `m x n` such that `S[I[k], J[k]] = V[k]`. The `combine` function is used to combine duplicates. If `m` and `n` are not specified, they @@ -1061,6 +1062,12 @@ julia> Js = [1; 2; 3]; julia> Vs = [1; 2; 3]; julia> sparse(Is, Js, Vs) +3×3 SparseMatrixCSC{Int64, Int64} with 3 stored entries: + 1 ⋅ ⋅ + ⋅ 2 ⋅ + ⋅ ⋅ 3 + +julia> sparse([CartesianIndex(i,i) for i in 1:3], Vs) 3×3 SparseMatrixCSC{Int64, Int64} with 3 stored entries: 1 ⋅ ⋅ ⋅ 2 ⋅ @@ -1111,11 +1118,6 @@ end sparse(I::AbstractVector, J::AbstractVector, V::AbstractVector, m::Integer, n::Integer, combine) = sparse(AbstractVector{Int}(I), AbstractVector{Int}(J), V, m, n, combine) -function sparse(IJ::AbstractVector{CartesianIndex{2}}, V::AbstractVector, m::Integer, n::Integer) - IJ′ = reinterpret(Int, reshape(IJ, 1, :)) - return sparse(view(IJ′, 1, :), view(IJ′, 2, :), V, m, n) -end - """ sparse!(I::AbstractVector{Ti}, J::AbstractVector{Ti}, V::AbstractVector{Tv}, m::Integer, n::Integer, combine, klasttouch::Vector{Ti}, @@ -1336,17 +1338,27 @@ end dimlub(I) = isempty(I) ? 0 : Int(maximum(I)) #least upper bound on required sparse matrix dimension +function reinterpret_cartesian(IJ::AbstractVector{CartesianIndex{2}}) + IJ′ = reinterpret(Int, reshape(IJ, 1, :)) + return (view(IJ′, 1, :), view(IJ′, 2, :)) +end + sparse(I,J,v::Number) = sparse(I, J, fill(v,length(I))) +sparse(IJ::AbstractVector{<:CartesianIndex{2}},v::Number) = sparse(reinterpret_cartesian(IJ)..., v) sparse(I,J,V::AbstractVector) = sparse(I, J, V, dimlub(I), dimlub(J)) +sparse(IJ::AbstractVector{<:CartesianIndex{2}},V::AbstractVector) = sparse(reinterpret_cartesian(IJ)..., V) sparse(I,J,v::Number,m,n) = sparse(I, J, fill(v,length(I)), Int(m), Int(n)) +sparse(IJ::AbstractVector{<:CartesianIndex{2}},v::Number,m,n) = sparse(reinterpret_cartesian(IJ)..., v, m, n) sparse(I,J,V::AbstractVector,m,n) = sparse(I, J, V, Int(m), Int(n), +) +sparse(IJ::AbstractVector{<:CartesianIndex{2}},V::AbstractVector,m,n) = sparse(reinterpret_cartesian(IJ)..., V, m, n) sparse(I,J,V::AbstractVector{Bool},m,n) = sparse(I, J, V, Int(m), Int(n), |) sparse(I,J,v::Number,m,n,combine::Function) = sparse(I, J, fill(v,length(I)), Int(m), Int(n), combine) +sparse(IJ::AbstractVector{<:CartesianIndex{2}},v::Number,m,n,combine::Function) = sparse(reinterpret_cartesian(IJ)..., v, m, n, combine) ## Transposition and permutation methods diff --git a/test/sparsematrix_constructors_indexing.jl b/test/sparsematrix_constructors_indexing.jl index 2b7007bb..589a6590 100644 --- a/test/sparsematrix_constructors_indexing.jl +++ b/test/sparsematrix_constructors_indexing.jl @@ -54,6 +54,7 @@ end @test sparse(Any[1,2,3], Any[1,2,3], Any[1,1,1], 5, 4) == sparse([1,2,3], [1,2,3], [1,1,1], 5, 4) @test_throws MethodError sparse([CartesianIndex(1, 1, 1), CartesianIndex(2, 2, 2), CartesianIndex(3, 3, 3)], [1, 2, 4], [1, 2, 3], 3, 3) # Only 2D indices should work @test isequal(sparse([CartesianIndex(1, 1), CartesianIndex(2, 2), CartesianIndex(3, 3)], [1.0, 2.0, 3.0], 3, 3), sparse([1, 2, 3], [1, 2, 3], [1.0, 2.0, 3.0], 3, 3)) + @test sparse([CartesianIndex(1, 1), CartesianIndex(2, 2), CartesianIndex(3, 3)], [1, 2, 3]) == sparse([1, 2, 3], [1, 2, 3], [1, 2, 3]) # with combine @test sparse([1, 1, 2, 2, 2], [1, 2, 1, 2, 2], 1.0, 2, 2, +) == sparse([1, 1, 2, 2], [1, 2, 1, 2], [1.0, 1.0, 1.0, 2.0], 2, 2) @test sparse([1, 1, 2, 2, 2], [1, 2, 1, 2, 2], -1.0, 2, 2, *) == sparse([1, 1, 2, 2], [1, 2, 1, 2], [-1.0, -1.0, -1.0, 1.0], 2, 2)