diff --git a/Project.toml b/Project.toml index ef6906d..6aa242a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SparseArraysBase" uuid = "0d5efcca-f356-4864-8770-e1ed8d78f208" authors = ["ITensor developers and contributors"] -version = "0.6.0" +version = "0.7.0" [deps] Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" diff --git a/docs/Project.toml b/docs/Project.toml index ad4f567..700efd6 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -8,4 +8,4 @@ SparseArraysBase = "0d5efcca-f356-4864-8770-e1ed8d78f208" Dictionaries = "0.4.4" Documenter = "1.8.1" Literate = "2.20.1" -SparseArraysBase = "0.6.0" +SparseArraysBase = "0.7.0" diff --git a/examples/Project.toml b/examples/Project.toml index 5c0e154..76d70b2 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -5,5 +5,5 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] Dictionaries = "0.4.4" -SparseArraysBase = "0.6.0" +SparseArraysBase = "0.7.0" Test = "<0.0.1, 1" diff --git a/src/abstractsparsearray.jl b/src/abstractsparsearray.jl index 9dfe012..a7d684f 100644 --- a/src/abstractsparsearray.jl +++ b/src/abstractsparsearray.jl @@ -11,6 +11,54 @@ function DerivableInterfaces.interface(::Type{<:AbstractSparseArray}) return SparseArrayInterface() end +function Base.copy(a::AnyAbstractSparseArray) + return copyto!(similar(a), a) +end + +function similar_sparsearray(a::AnyAbstractSparseArray, unstored::Unstored) + return SparseArrayDOK(unstored) +end +function similar_sparsearray(a::AnyAbstractSparseArray, T::Type, ax::Tuple) + return similar_sparsearray(a, Unstored(unstoredsimilar(unstored(a), T, ax))) +end +function similar_sparsearray(a::AnyAbstractSparseArray, T::Type) + return similar_sparsearray(a, Unstored(unstoredsimilar(unstored(a), T))) +end +function similar_sparsearray(a::AnyAbstractSparseArray, ax::Tuple) + return similar_sparsearray(a, Unstored(unstoredsimilar(unstored(a), ax))) +end +function similar_sparsearray(a::AnyAbstractSparseArray) + return similar_sparsearray(a, Unstored(unstored(a))) +end + +function Base.similar(a::AnyAbstractSparseArray, unstored::Unstored) + return similar_sparsearray(a, unstored) +end +function Base.similar(a::AnyAbstractSparseArray) + return similar_sparsearray(a) +end +function Base.similar(a::AnyAbstractSparseArray, T::Type) + return similar_sparsearray(a, T) +end +function Base.similar(a::AnyAbstractSparseArray, ax::Tuple) + return similar_sparsearray(a, ax) +end +function Base.similar(a::AnyAbstractSparseArray, T::Type, ax::Tuple) + return similar_sparsearray(a, T, ax) +end +# Fix ambiguity error. +function Base.similar(a::AnyAbstractSparseArray, T::Type, ax::Tuple{Int,Vararg{Int}}) + return similar_sparsearray(a, T, ax) +end +# Fix ambiguity error. +function Base.similar( + a::AnyAbstractSparseArray, + T::Type, + ax::Tuple{Union{Integer,Base.OneTo},Vararg{Union{Integer,Base.OneTo}}}, +) + return similar_sparsearray(a, T, ax) +end + using DerivableInterfaces: @derive # TODO: These need to be loaded since `AbstractArrayOps` @@ -20,12 +68,30 @@ using DerivableInterfaces: @derive using ArrayLayouts: ArrayLayouts using LinearAlgebra: LinearAlgebra -# DerivableInterfaces `Base.getindex`, `Base.setindex!`, etc. -# TODO: Define `AbstractMatrixOps` and overload for -# `AnyAbstractSparseMatrix` and `AnyAbstractSparseVector`, -# which is where matrix multiplication and factorizations -# should go. -@derive AnyAbstractSparseArray AbstractArrayOps +@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 @@ -35,7 +101,12 @@ function Base._cat(dims, a::AnyAbstractSparseArray...) return concatenate(dims, a...) end +# 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 +# values are not well defined. function map_stored(f, a::AnyAbstractSparseArray) + iszero(storedlength(a)) && return a kvs = storedpairs(a) # `collect` to convert to `Vector`, since otherwise # if it stays as `Dictionary` we might hit issues like @@ -52,6 +123,10 @@ end using Adapt: adapt function Base.print_array(io::IO, a::AnyAbstractSparseArray) + # TODO: Use `map(WeakPreserving(adapt(Array)), a)` instead. + # Currently that has trouble with type unstable maps, since + # the element type becomes abstract and therefore the zero/unstored + # values are not well defined. a′ = map_stored(adapt(Array), a) return @invoke Base.print_array(io::typeof(io), a′::AbstractArray{<:Any,ndims(a)}) end @@ -75,27 +150,30 @@ from the input indices. This constructor does not take ownership of the supplied storage, and will result in an independent container. """ -sparse(::Union{AbstractDict,AbstractDictionary}, dims...; kwargs...) +sparse(::Union{AbstractDict,AbstractDictionary}, dims...) const AbstractDictOrDictionary = Union{AbstractDict,AbstractDictionary} # checked constructor from data: use `setindex!` to validate/convert input -function sparse(storage::AbstractDictOrDictionary, dims::Dims; kwargs...) - A = SparseArrayDOK{valtype(storage)}(undef, dims; kwargs...) +function sparse(storage::AbstractDictOrDictionary, unstored::AbstractArray) + A = SparseArrayDOK(Unstored(unstored)) for (i, v) in pairs(storage) A[i] = v end return A end -function sparse(storage::AbstractDictOrDictionary, dims::Int...; kwargs...) - return sparse(storage, dims; kwargs...) +function sparse(storage::AbstractDictOrDictionary, ax::Tuple) + return sparse(storage, Zeros{valtype(storage)}(ax)) +end +function sparse(storage::AbstractDictOrDictionary, dims::Int...) + return sparse(storage, dims) end # Determine the size automatically. -function sparse(storage::AbstractDictOrDictionary; kwargs...) +function sparse(storage::AbstractDictOrDictionary) dims = ntuple(Returns(0), length(keytype(storage))) for I in keys(storage) dims = map(max, dims, Tuple(I)) end - return sparse(storage, dims; kwargs...) + return sparse(storage, dims) end using Random: Random, AbstractRNG, default_rng @@ -107,12 +185,18 @@ Create an empty size `dims` sparse array. The optional `T` argument specifies the element type, which defaults to `Float64`. """ sparsezeros -function sparsezeros(::Type{T}, dims::Dims; kwargs...) where {T} - return SparseArrayDOK{T}(undef, dims; kwargs...) +function sparsezeros(::Type{T}, unstored::AbstractArray{<:Any,N}) where {T,N} + return SparseArrayDOK{T,N}(Unstored(unstored)) +end +function sparsezeros(unstored::AbstractArray{T,N}) where {T,N} + return SparseArrayDOK{T,N}(Unstored(unstored)) +end +function sparsezeros(::Type{T}, dims::Dims) where {T} + return sparsezeros(T, Zeros{T}(dims)) end -sparsezeros(::Type{T}, dims::Int...; kwargs...) where {T} = sparsezeros(T, dims; kwargs...) -sparsezeros(dims::Dims; kwargs...) = sparsezeros(Float64, dims; kwargs...) -sparsezeros(dims::Int...; kwargs...) = sparsezeros(Float64, dims; kwargs...) +sparsezeros(::Type{T}, dims::Int...) where {T} = sparsezeros(T, dims) +sparsezeros(dims::Dims) = sparsezeros(Float64, dims) +sparsezeros(dims::Int...) = sparsezeros(Float64, dims) @doc """ sparserand([rng], [T::Type], dims; density::Real=0.5, randfun::Function=rand) -> A::SparseArrayDOK{T} diff --git a/src/abstractsparsearrayinterface.jl b/src/abstractsparsearrayinterface.jl index 719c94e..ff6e3e4 100644 --- a/src/abstractsparsearrayinterface.jl +++ b/src/abstractsparsearrayinterface.jl @@ -1,7 +1,9 @@ using Base: @_propagate_inbounds_meta using DerivableInterfaces: DerivableInterfaces, @derive, @interface, AbstractArrayInterface, zero! +using FillArrays: Zeros +function unstored end function eachstoredindex end function getstoredindex end function getunstoredindex end @@ -12,9 +14,25 @@ function storedlength end function storedpairs end function storedvalues end -# Replace the function for accessing -# unstored values. -function set_getunstoredindex end +# Indicates that the array should be interpreted +# as the unstored values of a sparse array. +struct Unstored{T,N,P<:AbstractArray{T,N}} <: AbstractArray{T,N} + parent::P +end +Base.parent(a::Unstored) = a.parent + +unstored(a::AbstractArray) = Zeros{eltype(a)}(axes(a)) + +function unstoredsimilar(a::AbstractArray, T::Type, ax::Tuple) + return Zeros{T}(ax) +end +function unstoredsimilar(a::AbstractArray, ax::Tuple) + return unstoredsimilar(a, eltype(a), ax) +end +function unstoredsimilar(a::AbstractArray, T::Type) + return AbstractArray{T}(a) +end +unstoredsimilar(a::AbstractArray) = a # Generic functionality for converting to a # dense array, trying to preserve information @@ -84,14 +102,6 @@ Base.size(a::StoredValues) = size(a.storedindices) return setindex!(a.array, value, a.storedindices[I]) 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 ::AbstractSparseArrayInterface function Base.similar( - a::AbstractArray, T::Type, size::Tuple{Vararg{Int}} -) - # TODO: Define `default_similartype` or something like that? - return SparseArrayDOK{T}(undef, size) -end using DerivableInterfaces: DerivableInterfaces, zero! # `zero!` isn't defined in `Base`, but it is defined in `ArrayLayouts` @@ -136,7 +146,10 @@ end abstract type AbstractSparseArrayStyle{N} <: Broadcast.AbstractArrayStyle{N} end -@derive AbstractSparseArrayStyle AbstractArrayStyleOps +@derive (T=AbstractSparseArrayStyle,) begin + Base.similar(::Broadcast.Broadcasted{<:T}, ::Type, ::Tuple) + Base.copyto!(::AbstractArray, ::Broadcast.Broadcasted{<:T}) +end struct SparseArrayStyle{N} <: AbstractSparseArrayStyle{N} end diff --git a/src/map.jl b/src/map.jl index f35426e..fbaad34 100644 --- a/src/map.jl +++ b/src/map.jl @@ -48,20 +48,20 @@ function ZeroPreserving(f, T::Type, Ts::Type...) return NonPreserving(f) end end +ZeroPreserving(f::ZeroPreserving, T::Type, Ts::Type...) = f -const _WEAK_FUNCTIONS = (:+, :-) -for f in _WEAK_FUNCTIONS +for F in (:(typeof(+)), :(typeof(-)), :(typeof(identity))) @eval begin - ZeroPreserving(::typeof($f), ::Type{<:Number}, ::Type{<:Number}...) = WeakPreserving($f) + ZeroPreserving(f::$F, ::Type, ::Type...) = WeakPreserving(f) end end -const _STRONG_FUNCTIONS = (:*,) -for f in _STRONG_FUNCTIONS +using MapBroadcast: MapFunction +for F in (:(typeof(*)), :(MapFunction{typeof(*)})) @eval begin - ZeroPreserving(::typeof($f), ::Type{<:Number}, ::Type{<:Number}...) = StrongPreserving( - $f - ) + function ZeroPreserving(f::$F, ::Type, ::Type...) + return StrongPreserving(f) + end end end @@ -71,29 +71,35 @@ end f, A::AbstractArray, Bs::AbstractArray... ) f_pres = ZeroPreserving(f, A, Bs...) - return @interface I map(f_pres, A, Bs...) + return map_sparsearray(f_pres, A, Bs...) end -@interface I::AbstractSparseArrayInterface function Base.map( - f::ZeroPreserving, A::AbstractArray, Bs::AbstractArray... -) + +# This isn't an overload of `Base.map` since that leads to ambiguity errors. +function map_sparsearray(f::ZeroPreserving, A::AbstractArray, Bs::AbstractArray...) T = Base.Broadcast.combine_eltypes(f.f, (A, Bs...)) - C = similar(I, T, size(A)) - return @interface I map!(f, C, A, Bs...) + C = similar(A, T) + # TODO: Instead use: + # ```julia + # U = map(f.f, map(unstored, (A, Bs...))...) + # C = similar(A, Unstored(U)) + # ``` + # though right now `map` doesn't preserve `Zeros` or `BlockZeros`. + return map_sparsearray!(f, C, A, Bs...) end @interface I::AbstractSparseArrayInterface function Base.map!( f, C::AbstractArray, A::AbstractArray, Bs::AbstractArray... ) f_pres = ZeroPreserving(f, A, Bs...) - return @interface I map!(f_pres, C, A, Bs...) + return map_sparsearray!(f_pres, C, A, Bs...) end -@interface ::AbstractSparseArrayInterface function Base.map!( +# This isn't an overload of `Base.map!` since that leads to ambiguity errors. +function map_sparsearray!( f::ZeroPreserving, C::AbstractArray, A::AbstractArray, Bs::AbstractArray... ) checkshape(C, A, Bs...) unaliased = map(Base.Fix1(Base.unalias, C), (A, Bs...)) - if f isa StrongPreserving style = IndexStyle(C, unaliased...) inds = intersect(eachstoredindex.(Ref(style), unaliased)...) @@ -107,19 +113,20 @@ end else error(lazy"unknown zero-preserving type $(typeof(f))") end - @inbounds for I in inds C[I] = f.f(ith_all(I, unaliased)...) end - return C end # Derived functions # ----------------- -@interface I::AbstractSparseArrayInterface Base.copyto!(C::AbstractArray, A::AbstractArray) = @interface I map!( - identity, C, A +@interface I::AbstractSparseArrayInterface function Base.copyto!( + dest::AbstractArray, src::AbstractArray ) + @interface I map!(identity, dest, src) + return dest +end # Only map the stored values of the inputs. function map_stored! end diff --git a/src/oneelementarray.jl b/src/oneelementarray.jl index 251acfa..d5b361e 100644 --- a/src/oneelementarray.jl +++ b/src/oneelementarray.jl @@ -4,17 +4,15 @@ function _OneElementArray end # Like [`FillArrays.OneElement`](https://github.com/JuliaArrays/FillArrays.jl) # and [`OneHotArrays.OneHotArray`](https://github.com/FluxML/OneHotArrays.jl). -struct OneElementArray{T,N,I,A,F} <: AbstractSparseArray{T,N} +struct OneElementArray{T,N,I,Unstored<:AbstractArray{T,N}} <: AbstractSparseArray{T,N} value::T index::I - axes::A - getunstored::F + unstored::Unstored global @inline function _OneElementArray( - value::T, index::I, axes::A, getunstored::F - ) where {T,I,A,F} - N = length(axes) + value::T, index::I, unstored::Unstored + ) where {T,N,I,Unstored<:AbstractArray{T,N}} @assert N == length(index) - return new{T,N,I,A,F}(value, index, axes, getunstored) + return new{T,N,I,Unstored}(value, index, unstored) end end @@ -23,263 +21,238 @@ using DerivableInterfaces: @array_aliases @array_aliases OneElementArray function OneElementArray{T,N}( - value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; getunstored=getzero + value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {T,N} - return _OneElementArray(convert(T, value), index, axes, getunstored) + return _OneElementArray(convert(T, value), index, Zeros{T}(axes)) end function OneElementArray{<:Any,N}( - value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {T,N} - return OneElementArray{T,N}(value, index, axes; kwargs...) + return OneElementArray{T,N}(value, index, axes) end function OneElementArray( - value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {T,N} - return OneElementArray{T,N}(value, index, axes; kwargs...) + return OneElementArray{T,N}(value, index, axes) end function OneElementArray{T,N}( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {T,N} - return OneElementArray{T,N}(one(T), index, axes; kwargs...) + return OneElementArray{T,N}(one(T), index, axes) end function OneElementArray{<:Any,N}( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {N} - return OneElementArray{Bool,N}(index, axes; kwargs...) + return OneElementArray{Bool,N}(index, axes) end function OneElementArray{T}( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {T,N} - return OneElementArray{T,N}(index, axes; kwargs...) + return OneElementArray{T,N}(index, axes) end -function OneElementArray( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... -) where {N} - return OneElementArray{Bool,N}(index, axes; kwargs...) +function OneElementArray(index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}) where {N} + return OneElementArray{Bool,N}(index, axes) end function OneElementArray{T,N}( - value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... + value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} ) where {T,N} - return OneElementArray{T,N}(value, last.(ax_ind), first.(ax_ind); kwargs...) + return OneElementArray{T,N}(value, last.(ax_ind), first.(ax_ind)) end function OneElementArray{<:Any,N}( - value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... + value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} ) where {T,N} - return OneElementArray{T,N}(value, ax_ind...; kwargs...) + return OneElementArray{T,N}(value, ax_ind...) end function OneElementArray{T}( - value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... + value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} ) where {T,N} - return OneElementArray{T,N}(value, ax_ind...; kwargs...) + return OneElementArray{T,N}(value, ax_ind...) end function OneElementArray( - value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... + value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} ) where {T,N} - return OneElementArray{T,N}(value, ax_ind...; kwargs...) + return OneElementArray{T,N}(value, ax_ind...) end -function OneElementArray{T,N}( - ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... -) where {T,N} - return OneElementArray{T,N}(last.(ax_ind), first.(ax_ind); kwargs...) +function OneElementArray{T,N}(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {T,N} + return OneElementArray{T,N}(last.(ax_ind), first.(ax_ind)) end -function OneElementArray{<:Any,N}( - ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... -) where {N} - return OneElementArray{Bool,N}(ax_ind...; kwargs...) +function OneElementArray{<:Any,N}(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {N} + return OneElementArray{Bool,N}(ax_ind...) end -function OneElementArray{T}( - ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... -) where {T,N} +function OneElementArray{T}(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {T,N} return OneElementArray{T,N}(ax_ind...) end -function OneElementArray( - ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... -) where {N} - return OneElementArray{Bool,N}(ax_ind...; kwargs...) +function OneElementArray(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {N} + return OneElementArray{Bool,N}(ax_ind...) end # Fix ambiguity errors. -function OneElementArray{T,0}( - value, index::Tuple{}, axes::Tuple{}; getunstored=getzero -) where {T} - return _OneElementArray(convert(T, value), index, axes, getunstored) +function OneElementArray{T,0}(value, index::Tuple{}, axes::Tuple{}) where {T} + return _OneElementArray(convert(T, value), index, Zeros{T}(axes)) end -function OneElementArray{<:Any,0}( - value::T, index::Tuple{}, axes::Tuple{}; kwargs... -) where {T} - return OneElementArray{T,0}(value, index, axes; kwargs...) +function OneElementArray{<:Any,0}(value::T, index::Tuple{}, axes::Tuple{}) where {T} + return OneElementArray{T,0}(value, index, axes) end -function OneElementArray{T}(value, index::Tuple{}, axes::Tuple{}; kwargs...) where {T} - return OneElementArray{T,0}(value, index, axes; kwargs...) +function OneElementArray{T}(value, index::Tuple{}, axes::Tuple{}) where {T} + return OneElementArray{T,0}(value, index, axes) end -function OneElementArray(value::T, index::Tuple{}, axes::Tuple{}; kwargs...) where {T} - return OneElementArray{T,0}(value, index, axes; kwargs...) +function OneElementArray(value::T, index::Tuple{}, axes::Tuple{}) where {T} + return OneElementArray{T,0}(value, index, axes) end # Fix ambiguity errors. -function OneElementArray{T,0}(index::Tuple{}, axes::Tuple{}; kwargs...) where {T} - return OneElementArray{T,0}(one(T), index, axes; kwargs...) +function OneElementArray{T,0}(index::Tuple{}, axes::Tuple{}) where {T} + return OneElementArray{T,0}(one(T), index, axes) end -function OneElementArray{<:Any,0}(index::Tuple{}, axes::Tuple{}; kwargs...) - return OneElementArray{Bool,0}(index, axes; kwargs...) +function OneElementArray{<:Any,0}(index::Tuple{}, axes::Tuple{}) + return OneElementArray{Bool,0}(index, axes) end -function OneElementArray{T}(index::Tuple{}, axes::Tuple{}; kwargs...) where {T} - return OneElementArray{T,0}(index, axes; kwargs...) +function OneElementArray{T}(index::Tuple{}, axes::Tuple{}) where {T} + return OneElementArray{T,0}(index, axes) end -function OneElementArray(index::Tuple{}, axes::Tuple{}; kwargs...) - return OneElementArray{Bool,0}(value, index, axes; kwargs...) +function OneElementArray(index::Tuple{}, axes::Tuple{}) + return OneElementArray{Bool,0}(value, index, axes) end -function OneElementArray{T,0}(value; kwargs...) where {T} - return OneElementArray{T,0}(value, (), (); kwargs...) +function OneElementArray{T,0}(value) where {T} + return OneElementArray{T,0}(value, (), ()) end -function OneElementArray{<:Any,0}(value::T; kwargs...) where {T} - return OneElementArray{T,0}(value; kwargs...) +function OneElementArray{<:Any,0}(value::T) where {T} + return OneElementArray{T,0}(value) end -function OneElementArray{T}(value; kwargs...) where {T} - return OneElementArray{T,0}(value; kwargs...) +function OneElementArray{T}(value) where {T} + return OneElementArray{T,0}(value) end -function OneElementArray(value::T; kwargs...) where {T} - return OneElementArray{T}(value; kwargs...) +function OneElementArray(value::T) where {T} + return OneElementArray{T}(value) end -function OneElementArray{T,0}(; kwargs...) where {T} - return OneElementArray{T,0}((), (); kwargs...) +function OneElementArray{T,0}() where {T} + return OneElementArray{T,0}((), ()) end -function OneElementArray{<:Any,0}(; kwargs...) - return OneElementArray{Bool,0}(value; kwargs...) +function OneElementArray{<:Any,0}() + return OneElementArray{Bool,0}(value) end -function OneElementArray{T}(; kwargs...) where {T} - return OneElementArray{T,0}(; kwargs...) +function OneElementArray{T}() where {T} + return OneElementArray{T,0}() end -function OneElementArray(; kwargs...) - return OneElementArray{Bool}(; kwargs...) +function OneElementArray() + return OneElementArray{Bool}() end function OneElementArray{T,N}( - value, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... + value, index::NTuple{N,Int}, size::NTuple{N,Integer} ) where {T,N} - return OneElementArray{T,N}(value, index, Base.oneto.(size); kwargs...) + return OneElementArray{T,N}(value, index, Base.oneto.(size)) end function OneElementArray{<:Any,N}( - value::T, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... + value::T, index::NTuple{N,Int}, size::NTuple{N,Integer} ) where {T,N} - return OneElementArray{T,N}(value, index, size; kwargs...) + return OneElementArray{T,N}(value, index, size) end function OneElementArray{T}( - value, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... + value, index::NTuple{N,Int}, size::NTuple{N,Integer} ) where {T,N} - return OneElementArray{T,N}(value, index, size; kwargs...) + return OneElementArray{T,N}(value, index, size) end function OneElementArray( - value::T, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... + value::T, index::NTuple{N,Int}, size::NTuple{N,Integer} ) where {T,N} - return OneElementArray{T,N}(value, index, Base.oneto.(size); kwargs...) + return OneElementArray{T,N}(value, index, Base.oneto.(size)) end -function OneElementArray{T,N}( - index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... -) where {T,N} - return OneElementArray{T,N}(one(T), index, size; kwargs...) +function OneElementArray{T,N}(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {T,N} + return OneElementArray{T,N}(one(T), index, size) end -function OneElementArray{<:Any,N}( - index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... -) where {N} - return OneElementArray{Bool,N}(index, size; kwargs...) +function OneElementArray{<:Any,N}(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} + return OneElementArray{Bool,N}(index, size) end -function OneElementArray{T}( - index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... -) where {T,N} - return OneElementArray{T,N}(index, size; kwargs...) +function OneElementArray{T}(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {T,N} + return OneElementArray{T,N}(index, size) end -function OneElementArray(index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs...) where {N} - return OneElementArray{Bool,N}(index, size; kwargs...) +function OneElementArray(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} + return OneElementArray{Bool,N}(index, size) end -function OneElementVector{T}(value, index::Int, length::Integer; kwargs...) where {T} - return OneElementVector{T}(value, (index,), (length,); kwargs...) +function OneElementVector{T}(value, index::Int, length::Integer) where {T} + return OneElementVector{T}(value, (index,), (length,)) end -function OneElementVector(value::T, index::Int, length::Integer; kwargs...) where {T} - return OneElementVector{T}(value, index, length; kwargs...) +function OneElementVector(value::T, index::Int, length::Integer) where {T} + return OneElementVector{T}(value, index, length) end -function OneElementArray{T}(value, index::Int, length::Integer; kwargs...) where {T} - return OneElementVector{T}(value, index, length; kwargs...) +function OneElementArray{T}(value, index::Int, length::Integer) where {T} + return OneElementVector{T}(value, index, length) end -function OneElementArray(value::T, index::Int, length::Integer; kwargs...) where {T} - return OneElementVector{T}(value, index, length; kwargs...) +function OneElementArray(value::T, index::Int, length::Integer) where {T} + return OneElementVector{T}(value, index, length) end -function OneElementVector{T}(index::Int, size::Integer; kwargs...) where {T} - return OneElementVector{T}((index,), (size,); kwargs...) +function OneElementVector{T}(index::Int, size::Integer) where {T} + return OneElementVector{T}((index,), (size,)) end -function OneElementVector(index::Int, length::Integer; kwargs...) - return OneElementVector{Bool}(index, length; kwargs...) +function OneElementVector(index::Int, length::Integer) + return OneElementVector{Bool}(index, length) end -function OneElementArray{T}(index::Int, size::Integer; kwargs...) where {T} - return OneElementVector{T}(index, size; kwargs...) +function OneElementArray{T}(index::Int, size::Integer) where {T} + return OneElementVector{T}(index, size) end -function OneElementArray(index::Int, size::Integer; kwargs...) - return OneElementVector{Bool}(index, size; kwargs...) +function OneElementArray(index::Int, size::Integer) + return OneElementVector{Bool}(index, size) end # Interface to overload for constructing arrays like `OneElementArray`, # that may not be `OneElementArray` (i.e. wrapped versions). function oneelement( - value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {N} - return OneElementArray(value, index, axes; kwargs...) + return OneElementArray(value, index, axes) end function oneelement( - eltype::Type, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... + eltype::Type, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} ) where {N} - return oneelement(one(eltype), index, axes; kwargs...) + return oneelement(one(eltype), index, axes) end -function oneelement( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... -) where {N} - return oneelement(Bool, index, axes; kwargs...) +function oneelement(index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}) where {N} + return oneelement(Bool, index, axes) end -function oneelement( - value, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... -) where {N} - return oneelement(value, index, Base.oneto.(size); kwargs...) +function oneelement(value, index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} + return oneelement(value, index, Base.oneto.(size)) end -function oneelement( - eltype::Type, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... -) where {N} - return oneelement(one(eltype), index, size; kwargs...) +function oneelement(eltype::Type, index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} + return oneelement(one(eltype), index, size) end -function oneelement(index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs...) where {N} - return oneelement(Bool, index, size; kwargs...) +function oneelement(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} + return oneelement(Bool, index, size) end -function oneelement(value, ax_ind::Pair{<:AbstractUnitRange,Int}...; kwargs...) - return oneelement(value, last.(ax_ind), first.(ax_ind); kwargs...) +function oneelement(value, ax_ind::Pair{<:AbstractUnitRange,Int}...) + return oneelement(value, last.(ax_ind), first.(ax_ind)) end -function oneelement(eltype::Type, ax_ind::Pair{<:AbstractUnitRange,Int}...; kwargs...) - return oneelement(one(eltype), ax_ind...; kwargs...) +function oneelement(eltype::Type, ax_ind::Pair{<:AbstractUnitRange,Int}...) + return oneelement(one(eltype), ax_ind...) end -function oneelement(ax_ind::Pair{<:AbstractUnitRange,Int}...; kwargs...) - return oneelement(Bool, ax_ind...; kwargs...) +function oneelement(ax_ind::Pair{<:AbstractUnitRange,Int}...) + return oneelement(Bool, ax_ind...) end -function oneelement(value; kwargs...) - return oneelement(value, (), (); kwargs...) +function oneelement(value) + return oneelement(value, (), ()) end -function oneelement(eltype::Type; kwargs...) - return oneelement(one(eltype); kwargs...) +function oneelement(eltype::Type) + return oneelement(one(eltype)) end -function oneelement(; kwargs...) - return oneelement(Bool; kwargs...) +function oneelement() + return oneelement(Bool) end -Base.axes(a::OneElementArray) = getfield(a, :axes) -Base.size(a::OneElementArray) = length.(axes(a)) +unstored(a::OneElementArray) = getfield(a, :unstored) +Base.axes(a::OneElementArray) = axes(unstored(a)) +Base.size(a::OneElementArray) = size(unstored(a)) storedvalue(a::OneElementArray) = getfield(a, :value) storedvalues(a::OneElementArray) = Fill(storedvalue(a), 1) @@ -295,7 +268,7 @@ function getstoredindex(a::OneElementArray, I::Int...) return storedvalue(a) end function getunstoredindex(a::OneElementArray, I::Int...) - return a.getunstored(a, I...) + return unstored(a)[I...] end function setstoredindex!(a::OneElementArray, value, I::Int...) return error("`OneElementArray` is immutable, you can't set elements.") diff --git a/src/sparsearraydok.jl b/src/sparsearraydok.jl index 8d123dc..949667b 100644 --- a/src/sparsearraydok.jl +++ b/src/sparsearraydok.jl @@ -2,10 +2,6 @@ using Accessors: @set using DerivableInterfaces: DerivableInterfaces, @interface, interface, zero! using Dictionaries: Dictionary, IndexError, set! -function getzero(a::AbstractArray{<:Any,N}, I::Vararg{Int,N}) where {N} - return zero(eltype(a)) -end - const DOKStorage{T,N} = Dictionary{CartesianIndex{N},T} function _SparseArrayDOK end @@ -13,53 +9,60 @@ function _SparseArrayDOK end """ SparseArrayDOK{T,N,F} <: AbstractSparseArray{T,N} -`N`-dimensional sparse Dictionary-of-keys (DOK) array with elements of type `T`, -optionally with a function of type `F` to instantiate non-stored elements. +`N`-dimensional sparse dictionary-of-keys (DOK) array with elements of type `T`, +with a specified background of unstored values `unstored` of the size of the array. """ -struct SparseArrayDOK{T,N,F} <: AbstractSparseArray{T,N} +struct SparseArrayDOK{T,N,Unstored<:AbstractArray{T,N}} <: AbstractSparseArray{T,N} storage::DOKStorage{T,N} - size::NTuple{N,Int} - getunstored::F + unstored::Unstored global @inline function _SparseArrayDOK( - storage::DOKStorage{T,N}, size::Dims{N}, getunstored::F - ) where {T,N,F} - return new{T,N,F}(storage, size, getunstored) + storage::DOKStorage{T,N}, unstored::Unstored + ) where {T,N,Unstored<:AbstractArray{T,N}} + return new{T,N,Unstored}(storage, unstored) end end +unstored(a::SparseArrayDOK) = a.unstored +Base.size(a::SparseArrayDOK) = size(unstored(a)) +Base.axes(a::SparseArrayDOK) = axes(unstored(a)) + +function SparseArrayDOK{T,N}(a::Unstored) where {T,N} + storage = DOKStorage{T,N}() + return _SparseArrayDOK(storage, parent(a)) +end +function SparseArrayDOK{T}(a::Unstored) where {T} + return SparseArrayDOK{T,ndims(a)}(a) +end +function SparseArrayDOK{<:Any,N}(a::Unstored) where {N} + return SparseArrayDOK{eltype(a),N}(a) +end +function SparseArrayDOK(a::Unstored) + return SparseArrayDOK{eltype(a),ndims(a)}(a) +end + # Constructors # ------------ """ - SparseArrayDOK{T}(undef, dims...[; getunstored]) - SparseArrayDOK{T,N}(undef, dims...[; getunstored]) + SparseArrayDOK{T}(undef, dims...) + SparseArrayDOK{T,N}(undef, dims...) Construct an uninitialized `N`-dimensional [`SparseArrayDOK`](@ref) containing elements of type `T`. `N` can either be supplied explicitly, or be determined by the length or number of `dims`. """ -SparseArrayDOK{T,N}(::UndefInitializer, dims; kwargs...) +SparseArrayDOK{T,N}(::UndefInitializer, dims...) -function SparseArrayDOK{T,N}( - ::UndefInitializer, dims::Dims; getunstored=getzero -) where {T,N} - (length(dims) == N && all(≥(0), dims)) || - throw(ArgumentError("Invalid dimensions: $dims")) - storage = DOKStorage{T,N}() - return _SparseArrayDOK(storage, dims, getunstored) +function SparseArrayDOK{T,N}(::UndefInitializer, ax::Tuple{Vararg{Any,N}}) where {T,N} + return SparseArrayDOK{T,N}(Unstored(Zeros{T}(ax))) end -function SparseArrayDOK{T,N}(::UndefInitializer, dims::Vararg{Int,N}; kwargs...) where {T,N} - return SparseArrayDOK{T,N}(undef, dims; kwargs...) +function SparseArrayDOK{T}(::UndefInitializer, ax::Tuple{Vararg{Any,N}}) where {T,N} + return SparseArrayDOK{T,N}(undef, ax) end -function SparseArrayDOK{T}(::UndefInitializer, dims::Dims{N}; kwargs...) where {T,N} - return SparseArrayDOK{T,N}(undef, dims; kwargs...) +function SparseArrayDOK{T,N}(::UndefInitializer, ax::Vararg{Int,N}) where {T,N} + return SparseArrayDOK{T,N}(undef, ax) end -function SparseArrayDOK{T}(::UndefInitializer, dims::Vararg{Int,N}; kwargs...) where {T,N} - return SparseArrayDOK{T,N}(undef, dims; kwargs...) -end - -function set_getunstored(a::SparseArrayDOK, f) - @set a.getunstored = f - return a +function SparseArrayDOK{T}(::UndefInitializer, ax::Vararg{Any,N}) where {T,N} + return SparseArrayDOK{T,N}(undef, ax) end using DerivableInterfaces: DerivableInterfaces @@ -73,7 +76,6 @@ using DerivableInterfaces: @array_aliases @array_aliases SparseArrayDOK storage(a::SparseArrayDOK) = a.storage -Base.size(a::SparseArrayDOK) = a.size storedvalues(a::SparseArrayDOK) = values(storage(a)) @inline function isstored(a::SparseArrayDOK{<:Any,N}, I::Vararg{Int,N}) where {N} @@ -89,7 +91,7 @@ end end @inline function getunstoredindex(a::SparseArrayDOK{<:Any,N}, I::Vararg{Int,N}) where {N} @boundscheck checkbounds(a, I...) - return a.getunstored(a, I...) + return unstored(a)[I...] end @inline function setstoredindex!( a::SparseArrayDOK{<:Any,N}, value, I::Vararg{Int,N} diff --git a/test/Project.toml b/test/Project.toml index d7c7bb6..8a7449d 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -3,6 +3,7 @@ Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -17,11 +18,12 @@ Adapt = "4.2.0" Aqua = "0.8.11" ArrayLayouts = "1.11.1" Dictionaries = "0.4.4" +FillArrays = "1.13.0" JLArrays = "0.2.0" LinearAlgebra = "<0.0.1, 1" Random = "<0.0.1, 1" SafeTestsets = "0.1.0" -SparseArraysBase = "0.6.0" +SparseArraysBase = "0.7.0" StableRNGs = "1.0.2" Suppressor = "0.2.8" Test = "<0.0.1, 1" diff --git a/test/test_sparsearraydok.jl b/test/test_sparsearraydok.jl index 4e2b229..ec50f23 100644 --- a/test/test_sparsearraydok.jl +++ b/test/test_sparsearraydok.jl @@ -1,6 +1,7 @@ using Adapt: adapt using ArrayLayouts: zero! using Dictionaries: Dictionary +using FillArrays: Zeros using JLArrays: JLArray, @allowscalar using SparseArraysBase: SparseArraysBase, @@ -275,12 +276,10 @@ arrayts = (Array,) ) for a in ( sparse(d, 2, 2), - sparse(d, 2, 2; getunstored=Returns(zero(elt))), + sparse(d, Zeros{elt}(2, 2)), sparse(d, (2, 2)), - sparse(d, (2, 2); getunstored=Returns(zero(elt))), # Determine the size automatically. sparse(d), - sparse(d; getunstored=Returns(zero(elt))), ) @test !iszero(a) @test iszero(a[1, 1]) @@ -296,13 +295,11 @@ arrayts = (Array,) for (a, elt′) in ( (sparsezeros(elt, 2, 2), elt), - (sparsezeros(elt, 2, 2; getunstored=Returns(zero(elt))), elt), + (sparsezeros(elt, Zeros{elt}(2, 2)), elt), (sparsezeros(elt, (2, 2)), elt), - (sparsezeros(elt, (2, 2); getunstored=Returns(zero(elt))), elt), (sparsezeros(2, 2), Float64), - (sparsezeros(2, 2; getunstored=Returns(zero(Float64))), Float64), + (sparsezeros(Zeros{Float64}(2, 2)), Float64), (sparsezeros((2, 2)), Float64), - (sparsezeros((2, 2); getunstored=Returns(zero(Float64))), Float64), ) @test iszero(a) @test size(a) == (2, 2)