From 9fbeaf5caffc07d71a2e2e6c5837e67f12dcd5e8 Mon Sep 17 00:00:00 2001 From: mtfishman Date: Fri, 6 Dec 2024 19:14:31 -0500 Subject: [PATCH 1/2] Update for latest Derive --- examples/README.jl | 10 +++++++++ src/SparseArraysBase.jl | 1 + src/abstractsparsearray.jl | 24 +++++++++++++++++++++ src/sparsearraydok.jl | 22 +++++++++---------- src/sparsearrayinterface.jl | 43 ++++++++++++++++++++++++------------- 5 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 src/abstractsparsearray.jl diff --git a/examples/README.jl b/examples/README.jl index ee84078..f0e827c 100644 --- a/examples/README.jl +++ b/examples/README.jl @@ -55,6 +55,7 @@ a = SparseArrayDOK{Float64}(2, 2) # AbstractArray interface: a[1, 2] = 12 +@test a == [0 12; 0 0] @test a[1, 1] == 0 @test a[2, 1] == 0 @test a[1, 2] == 12 @@ -75,3 +76,12 @@ a[1, 2] = 12 @test storedlength(a) == 1 @test issetequal(storedpairs(a), [CartesianIndex(1, 2) => 12]) @test issetequal(storedvalues(a), [12]) + +# AbstractArray functionality: + +b = a .+ 2 .* a' +@test b == [0 12; 24 0] +@test storedlength(b) == 2 +@test b isa SparseArrayDOK{Float64} + +a * a' diff --git a/src/SparseArraysBase.jl b/src/SparseArraysBase.jl index 60b1cf0..0bf4764 100644 --- a/src/SparseArraysBase.jl +++ b/src/SparseArraysBase.jl @@ -2,6 +2,7 @@ module SparseArraysBase include("sparsearrayinterface.jl") include("wrappers.jl") +include("abstractsparsearray.jl") include("sparsearraydok.jl") end diff --git a/src/abstractsparsearray.jl b/src/abstractsparsearray.jl new file mode 100644 index 0000000..23eb087 --- /dev/null +++ b/src/abstractsparsearray.jl @@ -0,0 +1,24 @@ +abstract type AbstractSparseArray{T,N} <: AbstractArray{T,N} end + +using Derive: @array_aliases +# Define AbstractSparseVector, AnyAbstractSparseArray, etc. +@array_aliases AbstractSparseArray + +using Derive: Derive +function Derive.interface(::Type{<:AbstractSparseArray}) + return SparseArrayInterface() +end + +using Derive: @derive +# Derive `Base.getindex`, `Base.setindex!`, etc. +@derive AnyAbstractSparseArray AbstractArrayOps + +using LinearAlgebra: LinearAlgebra +@derive (T=AnyAbstractSparseVecOrMat,) begin + LinearAlgebra.mul!(::AbstractMatrix, ::T, ::T, ::Number, ::Number) +end + +using ArrayLayouts: ArrayLayouts +@derive (T=AnyAbstractSparseArray,) begin + ArrayLayouts.MemoryLayout(::Type{<:T}) +end diff --git a/src/sparsearraydok.jl b/src/sparsearraydok.jl index 5ba80b9..748e709 100644 --- a/src/sparsearraydok.jl +++ b/src/sparsearraydok.jl @@ -1,25 +1,23 @@ # TODO: Define `AbstractSparseArray`, make this a subtype. -struct SparseArrayDOK{T,N} <: AbstractArray{T,N} +struct SparseArrayDOK{T,N} <: AbstractSparseArray{T,N} storage::Dict{CartesianIndex{N},T} size::NTuple{N,Int} end -function SparseArrayDOK{T}(size::Int...) where {T} - N = length(size) +const SparseMatrixDOK{T} = SparseArrayDOK{T,2} +const SparseVectorDOK{T} = SparseArrayDOK{T,1} + +function SparseArrayDOK{T,N}(size::Vararg{Int,N}) where {T,N} return SparseArrayDOK{T,N}(Dict{CartesianIndex{N},T}(), size) end -using Derive: @wrappedtype -# Define `WrappedSparseArrayDOK` and `AnySparseArrayDOK`. -@wrappedtype SparseArrayDOK - -using Derive: Derive -function Derive.interface(::Type{<:SparseArrayDOK}) - return SparseArrayInterface() +function SparseArrayDOK{T}(size::Int...) where {T} + return SparseArrayDOK{T,length(size)}(size...) end -using Derive: @derive -@derive AnySparseArrayDOK AbstractArrayOps +using Derive: @array_aliases +# Define `SparseMatrixDOK`, `AnySparseArrayDOK`, etc. +@array_aliases SparseArrayDOK storage(a::SparseArrayDOK) = a.storage Base.size(a::SparseArrayDOK) = a.size diff --git a/src/sparsearrayinterface.jl b/src/sparsearrayinterface.jl index 17d88d0..fd72d55 100644 --- a/src/sparsearrayinterface.jl +++ b/src/sparsearrayinterface.jl @@ -5,10 +5,14 @@ storedvalues(a) = error() isstored(a, I::Int...) = error() eachstoredindex(a) = error() getstoredindex(a, I::Int...) = error() -getunstoredindex(a, I::Int...) = error() setstoredindex!(a, value, I::Int...) = error() setunstoredindex!(a, value, I::Int...) = error() +# Interface defaults. +# TODO: Have a fallback that handles element types +# that don't define `zero(::Type)`. +getunstoredindex(a, I::Int...) = zero(eltype(a)) + # Derived interface. storedlength(a) = length(storedvalues(a)) storedpairs(a) = map(I -> I => getstoredindex(a, I), eachstoredindex(a)) @@ -19,22 +23,28 @@ function eachstoredindex(a1, a2, a_rest...) return union(eachstoredindex.((a1, a2, a_rest...))...) end -# TODO: Add `ndims` type parameter. -# TODO: Define `AbstractSparseArrayInterface`, make this a subtype. using Derive: Derive, @interface, AbstractArrayInterface -struct SparseArrayInterface <: AbstractArrayInterface end + +# TODO: Add `ndims` type parameter. +# 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 <: AbstractArrayInterface end + +struct SparseArrayInterface <: AbstractSparseArrayInterface end # Convenient shorthand to refer to the sparse interface. +# TODO: Define this as `InterfaceFunction(AbstractSparseArrayInterface)` const sparse = SparseArrayInterface() # TODO: Use `ArrayLayouts.layout_getindex`, `ArrayLayouts.sub_materialize` # to handle slicing (implemented by copying SubArray). -@interface sparse function Base.getindex(a, I::Int...) +@interface AbstractSparseArrayInterface function Base.getindex(a, I::Int...) !isstored(a, I...) && return getunstoredindex(a, I...) return getstoredindex(a, I...) end -@interface sparse function Base.setindex!(a, value, I::Int...) +@interface AbstractSparseArrayInterface function Base.setindex!(a, value, I::Int...) iszero(value) && return a if !isstored(a, I...) setunstoredindex!(a, value, I...) @@ -46,23 +56,25 @@ end # TODO: This may need to be defined in `sparsearraydok.jl`, after `SparseArrayDOK` # is defined. And/or define `default_type(::SparseArrayStyle, T::Type) = SparseArrayDOK{T}`. -@interface sparse function Base.similar(a, T::Type, size::Tuple{Vararg{Int}}) +@interface AbstractSparseArrayInterface function Base.similar( + a, T::Type, size::Tuple{Vararg{Int}} +) return SparseArrayDOK{T}(size...) end ## TODO: Make this more general, handle mixtures of integers and ranges. ## TODO: Make this logic generic to all `similar(::AbstractInterface, ...)`. -## @interface sparse function Base.similar(a, T::Type, dims::Tuple{Vararg{Base.OneTo}}) +## @interface AbstractSparseArrayInterface function Base.similar(a, T::Type, dims::Tuple{Vararg{Base.OneTo}}) ## return sparse(similar)(interface, a, T, Base.to_shape(dims)) ## end -@interface sparse function Base.map(f, as...) +@interface AbstractSparseArrayInterface function Base.map(f, as...) # This is defined in this way so we can rely on the Broadcast logic # for determining the destination of the operation (element type, shape, etc.). return f.(as...) end -@interface sparse function Base.map!(f, dest, as...) +@interface AbstractSparseArrayInterface function Base.map!(f, dest, as...) # Check `f` preserves zeros. # Define as `map_stored!`. # Define `eachstoredindex` promotion. @@ -72,12 +84,13 @@ end return dest end -# TODO: Define `AbstractSparseArrayStyle`, make this a subtype. -struct SparseArrayStyle{N} <: Broadcast.AbstractArrayStyle{N} end +abstract type AbstractSparseArrayStyle{N} <: Broadcast.AbstractArrayStyle{N} end + +struct SparseArrayStyle{N} <: AbstractSparseArrayStyle{N} end SparseArrayStyle{M}(::Val{N}) where {M,N} = SparseArrayStyle{N}() -@interface sparse function Broadcast.BroadcastStyle(type::Type) +@interface AbstractSparseArrayInterface function Broadcast.BroadcastStyle(type::Type) return SparseArrayStyle{ndims(type)}() end @@ -100,12 +113,12 @@ abstract type AbstractSparseLayout <: ArrayLayouts.MemoryLayout end struct SparseLayout <: AbstractSparseLayout end -@interface sparse function ArrayLayouts.MemoryLayout(type::Type) +@interface AbstractSparseArrayInterface function ArrayLayouts.MemoryLayout(type::Type) return SparseLayout() end using LinearAlgebra: LinearAlgebra -@interface sparse function LinearAlgebra.mul!(a_dest, a1, a2, α, β) +@interface AbstractSparseArrayInterface function LinearAlgebra.mul!(a_dest, a1, a2, α, β) return ArrayLayouts.mul!(a_dest, a1, a2, α, β) end From 9436feb3eeb1dd328bf2f83555dd320eeb49f9a0 Mon Sep 17 00:00:00 2001 From: mtfishman Date: Fri, 6 Dec 2024 19:20:12 -0500 Subject: [PATCH 2/2] Regenerate README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 43b33d6..09487ed 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ AbstractArray interface: ````julia a[1, 2] = 12 +@test a == [0 12; 0 0] @test a[1, 1] == 0 @test a[2, 1] == 0 @test a[1, 2] == 12 @@ -76,6 +77,17 @@ SparseArraysBase interface: @test issetequal(storedvalues(a), [12]) ```` +AbstractArray functionality: + +````julia +b = a .+ 2 .* a' +@test b == [0 12; 24 0] +@test storedlength(b) == 2 +@test b isa SparseArrayDOK{Float64} + +a * a' +```` + --- *This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*