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
8 changes: 4 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name = "SparseArraysBase"
uuid = "0d5efcca-f356-4864-8770-e1ed8d78f208"
authors = ["ITensor developers <support@itensor.org> and contributors"]
version = "0.7.11"
version = "0.8.0"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
DerivableInterfaces = "6c5e35bf-e59e-4898-b73c-732dcc4ba65f"
Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
FunctionImplementations = "7c7cc465-9c6a-495f-bdd1-f42428e86d0c"
GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MapBroadcast = "ebd9b9da-f48d-417c-9660-449667d60261"
Expand All @@ -29,13 +29,13 @@ SparseArraysBaseTensorAlgebraExt = ["TensorAlgebra", "SparseArrays"]
Accessors = "0.1.41"
Adapt = "4.3"
ArrayLayouts = "1.11"
DerivableInterfaces = "0.5"
Dictionaries = "0.4.3"
FillArrays = "1.13"
FunctionImplementations = "0.3.1"
GPUArraysCore = "0.2"
LinearAlgebra = "1.10"
MapBroadcast = "0.1.5"
NamedDimsArrays = "0.11"
NamedDimsArrays = "0.12"
Random = "1.10"
SparseArrays = "1.10"
TensorAlgebra = "0.6.2"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ SparseArraysBase = {path = ".."}
Dictionaries = "0.4.4"
Documenter = "1.8.1"
Literate = "2.20.1"
SparseArraysBase = "0.7.0"
SparseArraysBase = "0.8"
2 changes: 1 addition & 1 deletion examples/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ SparseArraysBase = {path = ".."}

[compat]
Dictionaries = "0.4.4"
SparseArraysBase = "0.7.0"
SparseArraysBase = "0.8"
Test = "<0.0.1, 1"
4 changes: 2 additions & 2 deletions src/SparseArraysBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export SparseArrayDOK,
storedpairs,
storedvalues

include("abstractsparsearrayinterface.jl")
include("sparsearrayinterface.jl")
include("abstractsparsearraystyle.jl")
include("sparsearraystyle.jl")
include("indexing.jl")
include("map.jl")
include("wrappers.jl")
Expand Down
112 changes: 63 additions & 49 deletions src/abstractsparsearray.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
using Dictionaries: AbstractDictionary

abstract type AbstractSparseArray{T, N} <: AbstractArray{T, N} end
const AbstractSparseVector{T} = AbstractSparseArray{T, 1}
const AbstractSparseMatrix{T} = AbstractSparseArray{T, 2}

using Adapt: WrappedArray
const WrappedAbstractSparseArray{T, N} =
WrappedArray{T, N, AbstractSparseArray, AbstractSparseArray{T, N}}
const AnyAbstractSparseArray{T, N} = Union{
AbstractSparseArray{T, N}, WrappedAbstractSparseArray{T, N},
}
const AnyAbstractSparseVector{T} = AnyAbstractSparseArray{T, 1}
const AnyAbstractSparseMatrix{T} = AnyAbstractSparseArray{T, 2}
const AnyAbstractSparseVecOrMat{T} = Union{
AnyAbstractSparseVector{T}, AnyAbstractSparseMatrix{T},
}

Base.convert(T::Type{<:AbstractSparseArray}, a::AbstractArray) = a isa T ? a : T(a)

using DerivableInterfaces: @array_aliases
# Define AbstractSparseVector, AnyAbstractSparseArray, etc.
@array_aliases AbstractSparseArray

using DerivableInterfaces: DerivableInterfaces
function DerivableInterfaces.interface(::Type{<:AbstractSparseArray})
return SparseArrayInterface()
end
using FunctionImplementations: FunctionImplementations
FunctionImplementations.Style(::Type{<:AnyAbstractSparseArray}) = SparseArrayStyle()

function Base.copy(a::AnyAbstractSparseArray)
return copyto!(similar(a), a)
Expand Down Expand Up @@ -49,48 +57,58 @@ function Base.similar(
return similar_sparsearray(a, T, ax)
end

using DerivableInterfaces: @derive

# TODO: These need to be loaded since `AbstractArrayOps`
# includes overloads of functions from these modules.
# Ideally that wouldn't be needed and can be circumvented
# with `GlobalRef`.
using ArrayLayouts: ArrayLayouts
using LinearAlgebra: LinearAlgebra

@derive (T = AnyAbstractSparseArray,) begin
Base.getindex(::T, ::Any...)
Base.getindex(::T, ::Int...)
Base.setindex!(::T, ::Any, ::Any...)
Base.setindex!(::T, ::Any, ::Int...)
Base.copy!(::AbstractArray, ::T)
Base.copyto!(::AbstractArray, ::T)
Base.map(::Any, ::T...)
Base.map!(::Any, ::AbstractArray, ::T...)
Base.mapreduce(::Any, ::Any, ::T...; kwargs...)
Base.reduce(::Any, ::T...; kwargs...)
Base.all(::Function, ::T)
Base.all(::T)
Base.iszero(::T)
Base.real(::T)
Base.fill!(::T, ::Any)
DerivableInterfaces.zero!(::T)
Base.zero(::T)
Base.permutedims!(::Any, ::T, ::Any)
Broadcast.BroadcastStyle(::Type{<:T})
Base.copyto!(::T, ::Broadcast.Broadcasted{Broadcast.DefaultArrayStyle{0}})
ArrayLayouts.MemoryLayout(::Type{<:T})
LinearAlgebra.mul!(::AbstractMatrix, ::T, ::T, ::Number, ::Number)
end

using DerivableInterfaces.Concatenate: concatenate
# We overload `Base._cat` instead of `Base.cat` since it
# is friendlier for invalidations/compile times, see
# https://github.com/ITensor/SparseArraysBase.jl/issues/25.
function Base._cat(dims, a::AnyAbstractSparseArray...)
return concatenate(dims, a...)
Base.getindex(a::AnyAbstractSparseArray, I::Any...) = style(a)(getindex)(a, I...)
Base.getindex(a::AnyAbstractSparseArray, I::Int...) = style(a)(getindex)(a, I...)
Base.setindex!(a::AnyAbstractSparseArray, x, I::Any...) = style(a)(setindex!)(a, x, I...)
Base.setindex!(a::AnyAbstractSparseArray, x, I::Int...) = style(a)(setindex!)(a, x, I...)
Base.copy!(dst::AbstractArray, src::AnyAbstractSparseArray) = style(src)(copy!)(dst, src)
function Base.copyto!(dst::AbstractArray, src::AnyAbstractSparseArray)
return style(src)(copyto!)(dst, src)
end
Base.map(f, as::AnyAbstractSparseArray...) = style(as...)(map)(f, as...)
function Base.map!(f, dst::AbstractArray, as::AnyAbstractSparseArray...)
return style(as...)(map!)(f, dst, as...)
end
function Base.mapreduce(f, op, as::AnyAbstractSparseArray...; kwargs...)
return style(as...)(mapreduce)(f, op, as...; kwargs...)
end
function Base.reduce(f, as::AnyAbstractSparseArray...; kwargs...)
return style(as...)(reduce)(f, as...; kwargs...)
end
Base.all(f::Function, a::AnyAbstractSparseArray) = style(a)(all)(f, a)
Base.all(a::AnyAbstractSparseArray) = style(a)(all)(a)
Base.iszero(a::AnyAbstractSparseArray) = style(a)(iszero)(a)
Base.isreal(a::AnyAbstractSparseArray) = style(a)(isreal)(a)
Base.real(a::AnyAbstractSparseArray) = style(a)(real)(a)
Base.fill!(a::AnyAbstractSparseArray, x) = style(a)(fill!)(a, x)
FunctionImplementations.zero!(a::AnyAbstractSparseArray) = style(a)(zero!)(a)
Base.zero(a::AnyAbstractSparseArray) = style(a)(zero)(a)
function Base.permutedims!(dst, a::AnyAbstractSparseArray, perm)
return style(a)(permutedims!)(dst, a, perm)
end
function LinearAlgebra.mul!(
dst::AbstractMatrix, a1::AnyAbstractSparseArray, a2::AnyAbstractSparseArray,
α::Number, β::Number,
)
return style(a1, a2)(mul!)(dst, a1, a2, α, β)
end

function Base.Broadcast.BroadcastStyle(type::Type{<:AnyAbstractSparseArray})
return Broadcast.SparseArrayStyle{ndims(type)}()
end

using ArrayLayouts: ArrayLayouts
ArrayLayouts.MemoryLayout(type::Type{<:AnyAbstractSparseArray}) = SparseLayout()

using FunctionImplementations.Concatenate: concatenate
# We overload `Base._cat` instead of `Base.cat` since it
# is friendlier for invalidations/compile times, see:
# https://github.com/ITensor/SparseArraysBase.jl/issues/25
Base._cat(dims, a::AnyAbstractSparseArray...) = concatenate(dims, a...)

# TODO: Use `map(WeakPreserving(f), a)` instead.
# Currently that has trouble with type unstable maps, since
# the element type becomes abstract and therefore the zero/unstored
Expand Down Expand Up @@ -247,10 +265,6 @@ function sparserand!(
end
end

# Catch some cases that aren't getting caught by the current
# DerivableInterfaces.jl logic.
# TODO: Make this more systematic once DerivableInterfaces.jl
# is rewritten.
using ArrayLayouts: ArrayLayouts, MemoryLayout
using LinearAlgebra: LinearAlgebra, Adjoint
function ArrayLayouts.MemoryLayout(::Type{Transpose{T, P}}) where {T, P <: AbstractSparseMatrix}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Base: @_propagate_inbounds_meta
using DerivableInterfaces:
DerivableInterfaces, @derive, @interface, AbstractArrayInterface, zero!
using FillArrays: Zeros
using FunctionImplementations: FunctionImplementations

function unstored end
function eachstoredindex end
Expand Down Expand Up @@ -52,34 +51,21 @@ function dense(a::AbstractArray)
return @allowscalar convert(densetype(a), a)
end

# Minimal interface for `SparseArrayInterface`.
# Minimal interface for `SparseArrayStyle`.
# Fallbacks for dense/non-sparse arrays.

# TODO: Add `ndims` type parameter, like `Base.Broadcast.AbstractArrayStyle`.
# TODO: This isn't used to define interface functions right now.
# Currently, `@interface` expects an instance, probably it should take a
# type instead so fallback functions can use abstract types.
abstract type AbstractSparseArrayInterface{N} <: AbstractArrayInterface{N} end
using FunctionImplementations: AbstractArrayStyle
abstract type AbstractSparseArrayStyle <: AbstractArrayStyle end

function DerivableInterfaces.combine_interface_rule(
interface1::AbstractSparseArrayInterface, interface2::AbstractSparseArrayInterface
function FunctionImplementations.Style(
style1::AbstractSparseArrayStyle, style2::AbstractSparseArrayStyle
)
return error("Rule not defined.")
end
function DerivableInterfaces.combine_interface_rule(
interface1::Interface, interface2::Interface
) where {Interface <: AbstractSparseArrayInterface}
return interface1
end
function DerivableInterfaces.combine_interface_rule(
interface1::AbstractSparseArrayInterface, interface2::AbstractArrayInterface
)
return interface1
end
function DerivableInterfaces.combine_interface_rule(
interface1::AbstractArrayInterface, interface2::AbstractSparseArrayInterface
function FunctionImplementations.Style(
style1::AbstractSparseArrayStyle, style2::AbstractArrayStyle
)
return interface2
return style1
end

to_vec(x) = vec(collect(x))
Expand All @@ -106,63 +92,6 @@ Base.size(a::StoredValues) = size(a.storedindices)
return setindex!(a.array, value, a.storedindices[I])
end

using DerivableInterfaces: DerivableInterfaces, 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 DerivableInterfaces.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!
@inbounds for I in eachstoredindex(a)
a[I] = f(a[I])
end
return a
end

# `f::typeof(norm)`, `op::typeof(max)` used by `norm`.
function reduce_init(f, op, as...)
# TODO: Generalize this.
@assert isone(length(as))
a = only(as)
## TODO: Make this more efficient for block sparse
## arrays, in that case it allocates a block. Maybe
## it can use `FillArrays.Zeros`.
return f(getunstoredindex(a, first(eachindex(a))))
end

@interface ::AbstractSparseArrayInterface function Base.mapreduce(
f, op, as::AbstractArray...; init = reduce_init(f, op, as...), kwargs...
)
# TODO: Generalize this.
@assert isone(length(as))
a = only(as)
output = mapreduce(f, op, storedvalues(a); init, kwargs...)
## TODO: Bring this check back, or make the function more general.
## f_notstored = apply_notstored(f, a)
## @assert isequal(op(output, eltype(output)(f_notstored)), output)
return output
end

abstract type AbstractSparseArrayStyle{N} <: Broadcast.AbstractArrayStyle{N} end

@derive (T = AbstractSparseArrayStyle,) begin
Base.similar(::Broadcast.Broadcasted{<:T}, ::Type, ::Tuple)
Base.copyto!(::AbstractArray, ::Broadcast.Broadcasted{<:T})
end

struct SparseArrayStyle{N} <: AbstractSparseArrayStyle{N} end

SparseArrayStyle{M}(::Val{N}) where {M, N} = SparseArrayStyle{N}()

@interface ::AbstractSparseArrayInterface function Broadcast.BroadcastStyle(type::Type)
return SparseArrayStyle{ndims(type)}()
end

using ArrayLayouts: ArrayLayouts, MatMulMatAdd

abstract type AbstractSparseLayout <: ArrayLayouts.MemoryLayout end
Expand All @@ -181,7 +110,7 @@ function mul_indices(I1::CartesianIndex{2}, I2::CartesianIndex{2})
end

using LinearAlgebra: mul!
function default_mul!!(
function mul!!(
a_dest::AbstractMatrix,
a1::AbstractMatrix,
a2::AbstractMatrix,
Expand All @@ -192,20 +121,20 @@ function default_mul!!(
return a_dest
end

function default_mul!!(
function mul!!(
a_dest::Number, a1::Number, a2::Number, α::Number = true, β::Number = false
)
return a1 * a2 * α + a_dest * β
end

# a1 * a2 * α + a_dest * β
function sparse_mul!(
function _mul!_sparse(
a_dest::AbstractArray,
a1::AbstractArray,
a2::AbstractArray,
α::Number = true,
β::Number = false;
(mul!!) = (default_mul!!),
(mul!!) = (mul!!),
)
a_dest .*= β
β′ = one(Bool)
Expand All @@ -227,12 +156,6 @@ end
function ArrayLayouts.materialize!(
m::MatMulMatAdd{<:AbstractSparseLayout, <:AbstractSparseLayout, <:AbstractSparseLayout}
)
sparse_mul!(m.C, m.A, m.B, m.α, m.β)
_mul!_sparse(m.C, m.A, m.B, m.α, m.β)
return m.C
end

struct SparseLayout <: AbstractSparseLayout end

@interface ::AbstractSparseArrayInterface function ArrayLayouts.MemoryLayout(type::Type)
return SparseLayout()
end
Loading
Loading