Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "SparseArraysBase"
uuid = "0d5efcca-f356-4864-8770-e1ed8d78f208"
authors = ["ITensor developers <[email protected]> and contributors"]
version = "0.2.0"
version = "0.2.1"

[deps]
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
Expand All @@ -14,7 +14,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Aqua = "0.8.9"
ArrayLayouts = "1.11.0"
BroadcastMapConversion = "0.1.0"
Derive = "0.3.0"
Derive = "0.3.6"
Dictionaries = "0.4.3"
LinearAlgebra = "1.10"
SafeTestsets = "0.1"
Expand Down
83 changes: 75 additions & 8 deletions src/abstractsparsearrayinterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,27 @@
# type instead so fallback functions can use abstract types.
abstract type AbstractSparseArrayInterface <: AbstractArrayInterface end

function Derive.combine_interface_rule(

Check warning on line 107 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L107

Added line #L107 was not covered by tests
interface1::AbstractSparseArrayInterface, interface2::AbstractSparseArrayInterface
)
return error("Rule not defined.")

Check warning on line 110 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L110

Added line #L110 was not covered by tests
end
function Derive.combine_interface_rule(

Check warning on line 112 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L112

Added line #L112 was not covered by tests
interface1::Interface, interface2::Interface
) where {Interface<:AbstractSparseArrayInterface}
return interface1

Check warning on line 115 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L115

Added line #L115 was not covered by tests
end
function Derive.combine_interface_rule(

Check warning on line 117 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L117

Added line #L117 was not covered by tests
interface1::AbstractSparseArrayInterface, interface2::AbstractArrayInterface
)
return interface1

Check warning on line 120 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L120

Added line #L120 was not covered by tests
end
function Derive.combine_interface_rule(

Check warning on line 122 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L122

Added line #L122 was not covered by tests
interface1::AbstractArrayInterface, interface2::AbstractSparseArrayInterface
)
return interface2

Check warning on line 125 in src/abstractsparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractsparsearrayinterface.jl#L125

Added line #L125 was not covered by tests
end

to_vec(x) = vec(collect(x))
to_vec(x::AbstractArray) = vec(x)

Expand Down Expand Up @@ -178,7 +199,46 @@
return SparseArrayDOK{T}(size...)
end

@interface ::AbstractSparseArrayInterface function Base.map!(
# Only map the stored values of the inputs.
function map_stored! end

@interface interface::AbstractArrayInterface function map_stored!(
f, a_dest::AbstractArray, as::AbstractArray...
)
for I in eachstoredindex(as...)
a_dest[I] = f(map(a -> a[I], as)...)
end
return a_dest
end

# Only map all values, not just the stored ones.
function map_all! end

@interface interface::AbstractArrayInterface function map_all!(
f, a_dest::AbstractArray, as::AbstractArray...
)
for I in eachindex(as...)
a_dest[I] = map(f, map(a -> a[I], as)...)
end
return a_dest
end

using ArrayLayouts: ArrayLayouts, zero!

# `zero!` isn't defined in `Base`, but it is defined in `ArrayLayouts`
# and is useful for sparse array logic, since it can be used to empty
# the sparse array storage.
# We use a single function definition to minimize method ambiguities.
@interface interface::AbstractSparseArrayInterface function ArrayLayouts.zero!(
a::AbstractArray
)
# More generally, this codepath could be taking if `zero(eltype(a))`
# is defined and the elements are immutable.
f = eltype(a) <: Number ? Returns(zero(eltype(a))) : zero!
return @interface interface map_stored!(f, a, a)
end

@interface interface::AbstractSparseArrayInterface function Base.map!(
f, a_dest::AbstractArray, as::AbstractArray...
)
# TODO: Define a function `preserves_unstored(a_dest, f, as...)`
Expand All @@ -194,15 +254,22 @@
preserves_unstored = iszero(f(map(a -> getunstoredindex(a, I), as)...))
if !preserves_unstored
# Doesn't preserve unstored values, loop over all elements.
for I in eachindex(as...)
a_dest[I] = map(f, map(a -> a[I], as)...)
end
@interface interface map_all!(f, a_dest, as...)
return a_dest
end
# Define `eachstoredindex` promotion.
for I in eachstoredindex(as...)
a_dest[I] = f(map(a -> a[I], as)...)
end
# First zero out the destination.
# TODO: Make this more nuanced, skip when possible, for
# example if the sparsity of the destination is a subset of
# the sparsity of the sources, i.e.:
# ```julia
# if eachstoredindex(as...) ∉ eachstoredindex(a_dest)
# zero!(a_dest)
# end
# ```
# This is the safest thing to do in general, for example
# if the destination is dense but the sources are sparse.
@interface interface zero!(a_dest)
@interface interface map_stored!(f, a_dest, as...)
return a_dest
end

Expand Down
15 changes: 15 additions & 0 deletions src/sparsearrayinterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

struct SparseArrayInterface <: AbstractSparseArrayInterface end

# Fix ambiguity error.
function Derive.combine_interface_rule(::SparseArrayInterface, ::SparseArrayInterface)
return SparseArrayInterface()
end
function Derive.combine_interface_rule(

Check warning on line 9 in src/sparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/sparsearrayinterface.jl#L9

Added line #L9 was not covered by tests
interface1::SparseArrayInterface, interface2::AbstractSparseArrayInterface
)
return interface1

Check warning on line 12 in src/sparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/sparsearrayinterface.jl#L12

Added line #L12 was not covered by tests
end
function Derive.combine_interface_rule(

Check warning on line 14 in src/sparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/sparsearrayinterface.jl#L14

Added line #L14 was not covered by tests
interface1::AbstractSparseArrayInterface, interface2::SparseArrayInterface
)
return interface2

Check warning on line 17 in src/sparsearrayinterface.jl

View check run for this annotation

Codecov / codecov/patch

src/sparsearrayinterface.jl#L17

Added line #L17 was not covered by tests
end

# Convenient shorthand to refer to the sparse interface.
# Can turn a function into a sparse function with the syntax `sparse(f)`,
# i.e. `sparse(map)(x -> 2x, randn(2, 2))` while use the sparse
Expand Down
44 changes: 32 additions & 12 deletions src/wrappers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@
value_to_parentvalue(a::AbstractArray, value) = value
eachstoredparentindex(a::AbstractArray) = eachstoredindex(parent(a))
storedparentvalues(a::AbstractArray) = storedvalues(parent(a))
parentindex_to_index(a::AbstractArray, I::CartesianIndex) = error()
function parentindex_to_index(a::AbstractArray, I::Int...)

function parentindex_to_index(a::AbstractArray{<:Any,N}, I::CartesianIndex{N}) where {N}
return throw(MethodError(parentindex_to_index, Tuple{typeof(a),typeof(I)}))

Check warning on line 7 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L6-L7

Added lines #L6 - L7 were not covered by tests
end
function parentindex_to_index(a::AbstractArray{<:Any,N}, I::Vararg{Int,N}) where {N}

Check warning on line 9 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L9

Added line #L9 was not covered by tests
return Tuple(parentindex_to_index(a, CartesianIndex(I)))
end
index_to_parentindex(a::AbstractArray, I::CartesianIndex) = error()
function index_to_parentindex(a::AbstractArray, I::Int...)
# Handle linear indexing.
function parentindex_to_index(a::AbstractArray, I::Int)
return parentindex_to_index(a, CartesianIndices(parent(a))[I])

Check warning on line 14 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L13-L14

Added lines #L13 - L14 were not covered by tests
end

function index_to_parentindex(a::AbstractArray{<:Any,N}, I::CartesianIndex{N}) where {N}
return throw(MethodError(index_to_parentindex, Tuple{typeof(a),typeof(I)}))

Check warning on line 18 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L17-L18

Added lines #L17 - L18 were not covered by tests
end
function index_to_parentindex(a::AbstractArray{<:Any,N}, I::Vararg{Int,N}) where {N}
return Tuple(index_to_parentindex(a, CartesianIndex(I)))
end
# Handle linear indexing.
function index_to_parentindex(a::AbstractArray, I::Int)
return index_to_parentindex(a, CartesianIndices(a)[I])

Check warning on line 25 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L24-L25

Added lines #L24 - L25 were not covered by tests
end

function cartesianindex_reverse(I::CartesianIndex)
return CartesianIndex(reverse(Tuple(I)))
Expand All @@ -21,10 +35,10 @@
genperm(v, perm) = map(j -> v[j], perm)

using LinearAlgebra: Adjoint
function parentindex_to_index(a::Adjoint, I::CartesianIndex)
function parentindex_to_index(a::Adjoint, I::CartesianIndex{2})
return cartesianindex_reverse(I)
end
function index_to_parentindex(a::Adjoint, I::CartesianIndex)
function index_to_parentindex(a::Adjoint, I::CartesianIndex{2})
return cartesianindex_reverse(I)
end
function parentvalue_to_value(a::Adjoint, value)
Expand All @@ -36,18 +50,18 @@

perm(::PermutedDimsArray{<:Any,<:Any,p}) where {p} = p
iperm(::PermutedDimsArray{<:Any,<:Any,<:Any,ip}) where {ip} = ip
function index_to_parentindex(a::PermutedDimsArray, I::CartesianIndex)
function index_to_parentindex(a::PermutedDimsArray{<:Any,N}, I::CartesianIndex{N}) where {N}
return CartesianIndex(genperm(I, iperm(a)))
end
function parentindex_to_index(a::PermutedDimsArray, I::CartesianIndex)
function parentindex_to_index(a::PermutedDimsArray{<:Any,N}, I::CartesianIndex{N}) where {N}
return CartesianIndex(genperm(I, perm(a)))
end

using Base: ReshapedArray
function parentindex_to_index(a::ReshapedArray, I::CartesianIndex)
function parentindex_to_index(a::ReshapedArray{<:Any,N}, I::CartesianIndex{N}) where {N}

Check warning on line 61 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L61

Added line #L61 was not covered by tests
return CartesianIndices(size(a))[LinearIndices(parent(a))[I]]
end
function index_to_parentindex(a::ReshapedArray, I::CartesianIndex)
function index_to_parentindex(a::ReshapedArray{<:Any,N}, I::CartesianIndex{N}) where {N}

Check warning on line 64 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L64

Added line #L64 was not covered by tests
return CartesianIndices(parent(a))[LinearIndices(size(a))[I]]
end

Expand All @@ -56,9 +70,15 @@
return all(d -> I[d] ∈ parentindices(a)[d], 1:ndims(parent(a)))
end
end
# Don't constrain the number of dimensions of the array
# and index since the parent array can have a different
# number of dimensions than the `SubArray`.
function index_to_parentindex(a::SubArray, I::CartesianIndex)
return CartesianIndex(Base.reindex(parentindices(a), Tuple(I)))
end
# Don't constrain the number of dimensions of the array
# and index since the parent array can have a different
# number of dimensions than the `SubArray`.
function parentindex_to_index(a::SubArray, I::CartesianIndex)
nonscalardims = filter(tuple_oneto(ndims(parent(a)))) do d
return !(parentindices(a)[d] isa Real)
Expand All @@ -81,10 +101,10 @@
end

using LinearAlgebra: Transpose
function parentindex_to_index(a::Transpose, I::CartesianIndex)
function parentindex_to_index(a::Transpose, I::CartesianIndex{2})

Check warning on line 104 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L104

Added line #L104 was not covered by tests
return cartesianindex_reverse(I)
end
function index_to_parentindex(a::Transpose, I::CartesianIndex)
function index_to_parentindex(a::Transpose, I::CartesianIndex{2})

Check warning on line 107 in src/wrappers.jl

View check run for this annotation

Codecov / codecov/patch

src/wrappers.jl#L107

Added line #L107 was not covered by tests
return cartesianindex_reverse(I)
end
function parentvalue_to_value(a::Transpose, value)
Expand Down
Loading