diff --git a/Project.toml b/Project.toml index e533e14..f7130fe 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.3.2" +version = "0.4.0" [deps] Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" diff --git a/docs/Project.toml b/docs/Project.toml index 80df8fd..d3db650 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.3.0" +SparseArraysBase = "0.4.0" diff --git a/examples/Project.toml b/examples/Project.toml index de9c838..c115b65 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.3.0" +SparseArraysBase = "0.4.0" Test = "<0.0.1, 1" diff --git a/src/SparseArraysBase.jl b/src/SparseArraysBase.jl index 02b5970..9d0ea77 100644 --- a/src/SparseArraysBase.jl +++ b/src/SparseArraysBase.jl @@ -9,6 +9,7 @@ export SparseArrayDOK, eachstoredindex, isstored, oneelement, + sparse, sparserand, sparserand!, sparsezeros, diff --git a/src/abstractsparsearray.jl b/src/abstractsparsearray.jl index 08e8af4..fcf79a2 100644 --- a/src/abstractsparsearray.jl +++ b/src/abstractsparsearray.jl @@ -1,3 +1,5 @@ +using Dictionaries: AbstractDictionary + abstract type AbstractSparseArray{T,N} <: AbstractArray{T,N} end using DerivableInterfaces: @array_aliases @@ -33,17 +35,56 @@ end # Special-purpose constructors # ---------------------------- + +""" + sparse(storage::Union{AbstractDict,AbstractDictionary}, dims...[; getunstored]) + +Construct an `N`-dimensional [`SparseArrayDOK`](@ref) containing elements of type `T`. Both +`T` and `N` can either be supplied explicitly or be determined by the `storage` and the +length or number of `dims`. If `dims` aren't specified, the size will be determined automatically +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...) + +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...) + 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...) +end +# Determine the size automatically. +function sparse(storage::AbstractDictOrDictionary; kwargs...) + dims = ntuple(Returns(0), length(keytype(storage))) + for I in keys(storage) + dims = map(max, dims, Tuple(I)) + end + return sparse(storage, dims; kwargs...) +end + using Random: Random, AbstractRNG, default_rng @doc """ - sparsezeros([T::Type], dims) -> A::SparseArrayDOK{T} + sparsezeros([T::Type], dims[; getunstored]) -> A::SparseArrayDOK{T} Create an empty size `dims` sparse array. The optional `T` argument specifies the element type, which defaults to `Float64`. """ sparsezeros -sparsezeros(dims::Dims) = sparsezeros(Float64, dims) -sparsezeros(::Type{T}, dims::Dims) where {T} = SparseArrayDOK{T}(undef, dims) +function sparsezeros(::Type{T}, dims::Dims; kwargs...) where {T} + return SparseArrayDOK{T}(undef, dims; kwargs...) +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...) @doc """ sparserand([rng], [T::Type], dims; density::Real=0.5, randfun::Function=rand) -> A::SparseArrayDOK{T} @@ -61,15 +102,25 @@ See also [`sparserand!`](@ref). function sparserand(::Type{T}, dims::Dims; kwargs...) where {T} return sparserand(default_rng(), T, dims; kwargs...) end +function sparserand(::Type{T}, dims::Int...; kwargs...) where {T} + return sparserand(T, dims; kwargs...) +end sparserand(dims::Dims; kwargs...) = sparserand(default_rng(), Float64, dims; kwargs...) +sparserand(dims::Int...; kwargs...) = sparserand(dims; kwargs...) function sparserand(rng::AbstractRNG, dims::Dims; kwargs...) return sparserand(rng, Float64, dims; kwargs...) end +function sparserand(rng::AbstractRNG, dims::Int...; kwargs...) + return sparserand(rng, dims; kwargs...) +end function sparserand(rng::AbstractRNG, ::Type{T}, dims::Dims; kwargs...) where {T} A = SparseArrayDOK{T}(undef, dims) sparserand!(rng, A; kwargs...) return A end +function sparserand(rng::AbstractRNG, ::Type{T}, dims::Int...; kwargs...) where {T} + return sparserand(rng, T, dims; kwargs...) +end @doc """ sparserand!([rng], A::AbstractArray; density::Real=0.5, randfun::Function=rand) -> A diff --git a/src/oneelementarray.jl b/src/oneelementarray.jl index 122d65c..7936c6c 100644 --- a/src/oneelementarray.jl +++ b/src/oneelementarray.jl @@ -1,12 +1,21 @@ using FillArrays: Fill +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} value::T index::I axes::A - getunstoredindex::F + getunstored::F + global @inline function _OneElementArray( + value::T, index::I, axes::A, getunstored::F + ) where {T,I,A,F} + N = length(axes) + @assert N == length(index) + return new{T,N,I,A,F}(value, index, axes, getunstored) + end end using DerivableInterfaces: @array_aliases @@ -14,238 +23,259 @@ using DerivableInterfaces: @array_aliases @array_aliases OneElementArray function OneElementArray{T,N}( - value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}, getunstoredindex + value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; getunstored=getzero ) where {T,N} - return OneElementArray{T,N,typeof(index),typeof(axes),typeof(getunstoredindex)}( - value, index, axes, getunstoredindex - ) + return _OneElementArray(convert(T, value), index, axes, getunstored) end -function OneElementArray{T,N}( - value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} -) where {T,N} - return OneElementArray{T,N}(value, index, axes, default_getunstoredindex) -end function OneElementArray{<:Any,N}( - value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} + value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, index, axes) + return OneElementArray{T,N}(value, index, axes; kwargs...) end function OneElementArray( - value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} + value::T, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, index, axes) + return OneElementArray{T,N}(value, index, axes; kwargs...) end function OneElementArray{T,N}( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {T,N} - return OneElementArray{T,N}(one(T), index, axes) + return OneElementArray{T,N}(one(T), index, axes; kwargs...) end function OneElementArray{<:Any,N}( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {N} - return OneElementArray{Bool,N}(index, axes) + return OneElementArray{Bool,N}(index, axes; kwargs...) end function OneElementArray{T}( - index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {T,N} - return OneElementArray{T,N}(index, axes) + return OneElementArray{T,N}(index, axes; kwargs...) end -function OneElementArray(index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}) where {N} - return OneElementArray{Bool,N}(index, axes) +function OneElementArray( + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... +) where {N} + return OneElementArray{Bool,N}(index, axes; kwargs...) end function OneElementArray{T,N}( - value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} + value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, last.(ax_ind), first.(ax_ind)) + return OneElementArray{T,N}(value, last.(ax_ind), first.(ax_ind); kwargs...) end function OneElementArray{<:Any,N}( - value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} + value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, ax_ind...) + return OneElementArray{T,N}(value, ax_ind...; kwargs...) end function OneElementArray{T}( - value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} + value, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, ax_ind...) + return OneElementArray{T,N}(value, ax_ind...; kwargs...) end function OneElementArray( - value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N} + value::T, ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, ax_ind...) + return OneElementArray{T,N}(value, ax_ind...; kwargs...) end -function OneElementArray{T,N}(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {T,N} - return OneElementArray{T,N}(last.(ax_ind), first.(ax_ind)) +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...) end -function OneElementArray{<:Any,N}(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {N} - return OneElementArray{Bool,N}(ax_ind...) +function OneElementArray{<:Any,N}( + ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... +) where {N} + return OneElementArray{Bool,N}(ax_ind...; kwargs...) end -function OneElementArray{T}(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {T,N} +function OneElementArray{T}( + ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... +) where {T,N} return OneElementArray{T,N}(ax_ind...) end -function OneElementArray(ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}) where {N} - return OneElementArray{Bool,N}(ax_ind...) +function OneElementArray( + ax_ind::Vararg{Pair{<:AbstractUnitRange,Int},N}; kwargs... +) where {N} + return OneElementArray{Bool,N}(ax_ind...; kwargs...) end # Fix ambiguity errors. -function OneElementArray{T,0}(value, index::Tuple{}, axes::Tuple{}) where {T} - return OneElementArray{T,0}(value, index, axes, default_getunstoredindex) +function OneElementArray{T,0}( + value, index::Tuple{}, axes::Tuple{}; getunstored=getzero +) where {T} + return _OneElementArray(convert(T, value), index, axes, getunstored) end -function OneElementArray{<:Any,0}(value::T, index::Tuple{}, axes::Tuple{}) where {T} - return OneElementArray{T,0}(value, index, axes) +function OneElementArray{<:Any,0}( + value::T, index::Tuple{}, axes::Tuple{}; kwargs... +) where {T} + return OneElementArray{T,0}(value, index, axes; kwargs...) end -function OneElementArray{T}(value, index::Tuple{}, axes::Tuple{}) where {T} - return OneElementArray{T,0}(value, index, axes) +function OneElementArray{T}(value, index::Tuple{}, axes::Tuple{}; kwargs...) where {T} + return OneElementArray{T,0}(value, index, axes; kwargs...) end -function OneElementArray(value::T, index::Tuple{}, axes::Tuple{}) where {T} - return OneElementArray{T,0}(value, index, axes) +function OneElementArray(value::T, index::Tuple{}, axes::Tuple{}; kwargs...) where {T} + return OneElementArray{T,0}(value, index, axes; kwargs...) end # Fix ambiguity errors. -function OneElementArray{T,0}(index::Tuple{}, axes::Tuple{}) where {T} - return OneElementArray{T,0}(one(T), index, axes) +function OneElementArray{T,0}(index::Tuple{}, axes::Tuple{}; kwargs...) where {T} + return OneElementArray{T,0}(one(T), index, axes; kwargs...) end -function OneElementArray{<:Any,0}(index::Tuple{}, axes::Tuple{}) - return OneElementArray{Bool,0}(index, axes) +function OneElementArray{<:Any,0}(index::Tuple{}, axes::Tuple{}; kwargs...) + return OneElementArray{Bool,0}(index, axes; kwargs...) end -function OneElementArray{T}(index::Tuple{}, axes::Tuple{}) where {T} - return OneElementArray{T,0}(index, axes) +function OneElementArray{T}(index::Tuple{}, axes::Tuple{}; kwargs...) where {T} + return OneElementArray{T,0}(index, axes; kwargs...) end -function OneElementArray(index::Tuple{}, axes::Tuple{}) - return OneElementArray{Bool,0}(value, index, axes) +function OneElementArray(index::Tuple{}, axes::Tuple{}; kwargs...) + return OneElementArray{Bool,0}(value, index, axes; kwargs...) end -function OneElementArray{T,0}(value) where {T} - return OneElementArray{T,0}(value, (), ()) +function OneElementArray{T,0}(value; kwargs...) where {T} + return OneElementArray{T,0}(value, (), (); kwargs...) end -function OneElementArray{<:Any,0}(value::T) where {T} - return OneElementArray{T,0}(value) +function OneElementArray{<:Any,0}(value::T; kwargs...) where {T} + return OneElementArray{T,0}(value; kwargs...) end -function OneElementArray{T}(value) where {T} - return OneElementArray{T,0}(value) +function OneElementArray{T}(value; kwargs...) where {T} + return OneElementArray{T,0}(value; kwargs...) end -function OneElementArray(value::T) where {T} - return OneElementArray{T}(value) +function OneElementArray(value::T; kwargs...) where {T} + return OneElementArray{T}(value; kwargs...) end -function OneElementArray{T,0}() where {T} - return OneElementArray{T,0}((), ()) +function OneElementArray{T,0}(; kwargs...) where {T} + return OneElementArray{T,0}((), (); kwargs...) end -function OneElementArray{<:Any,0}() - return OneElementArray{Bool,0}(value) +function OneElementArray{<:Any,0}(; kwargs...) + return OneElementArray{Bool,0}(value; kwargs...) end -function OneElementArray{T}() where {T} - return OneElementArray{T,0}() +function OneElementArray{T}(; kwargs...) where {T} + return OneElementArray{T,0}(; kwargs...) end -function OneElementArray() - return OneElementArray{Bool}() +function OneElementArray(; kwargs...) + return OneElementArray{Bool}(; kwargs...) end function OneElementArray{T,N}( - value, index::NTuple{N,Int}, size::NTuple{N,Integer} + value, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, index, Base.oneto.(size)) + return OneElementArray{T,N}(value, index, Base.oneto.(size); kwargs...) end function OneElementArray{<:Any,N}( - value::T, index::NTuple{N,Int}, size::NTuple{N,Integer} + value::T, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, index, size) + return OneElementArray{T,N}(value, index, size; kwargs...) end function OneElementArray{T}( - value, index::NTuple{N,Int}, size::NTuple{N,Integer} + value, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, index, size) + return OneElementArray{T,N}(value, index, size; kwargs...) end function OneElementArray( - value::T, index::NTuple{N,Int}, size::NTuple{N,Integer} + value::T, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... ) where {T,N} - return OneElementArray{T,N}(value, index, Base.oneto.(size)) + return OneElementArray{T,N}(value, index, Base.oneto.(size); kwargs...) end -function OneElementArray{T,N}(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {T,N} - return OneElementArray{T,N}(one(T), index, size) +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...) end -function OneElementArray{<:Any,N}(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} - return OneElementArray{Bool,N}(index, size) +function OneElementArray{<:Any,N}( + index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... +) where {N} + return OneElementArray{Bool,N}(index, size; kwargs...) end -function OneElementArray{T}(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {T,N} - return OneElementArray{T,N}(index, size) +function OneElementArray{T}( + index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... +) where {T,N} + return OneElementArray{T,N}(index, size; kwargs...) end -function OneElementArray(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} - return OneElementArray{Bool,N}(index, size) +function OneElementArray(index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs...) where {N} + return OneElementArray{Bool,N}(index, size; kwargs...) end -function OneElementVector{T}(value, index::Int, length::Integer) where {T} - return OneElementVector{T}(value, (index,), (length,)) +function OneElementVector{T}(value, index::Int, length::Integer; kwargs...) where {T} + return OneElementVector{T}(value, (index,), (length,); kwargs...) end -function OneElementVector(value::T, index::Int, length::Integer) where {T} - return OneElementVector{T}(value, index, length) +function OneElementVector(value::T, index::Int, length::Integer; kwargs...) where {T} + return OneElementVector{T}(value, index, length; kwargs...) end -function OneElementArray{T}(value, index::Int, length::Integer) where {T} - return OneElementVector{T}(value, index, length) +function OneElementArray{T}(value, index::Int, length::Integer; kwargs...) where {T} + return OneElementVector{T}(value, index, length; kwargs...) end -function OneElementArray(value::T, index::Int, length::Integer) where {T} - return OneElementVector{T}(value, index, length) +function OneElementArray(value::T, index::Int, length::Integer; kwargs...) where {T} + return OneElementVector{T}(value, index, length; kwargs...) end -function OneElementVector{T}(index::Int, size::Integer) where {T} - return OneElementVector{T}((index,), (size,)) +function OneElementVector{T}(index::Int, size::Integer; kwargs...) where {T} + return OneElementVector{T}((index,), (size,); kwargs...) +end +function OneElementVector(index::Int, length::Integer; kwargs...) + return OneElementVector{Bool}(index, length; kwargs...) end -function OneElementVector(index::Int, length::Integer) - return OneElementVector{Bool}(index, length) +function OneElementArray{T}(index::Int, size::Integer; kwargs...) where {T} + return OneElementVector{T}(index, size; kwargs...) end -function OneElementArray{T}(index::Int, size::Integer) where {T} - return OneElementVector{T}(index, size) +function OneElementArray(index::Int, size::Integer; kwargs...) + return OneElementVector{Bool}(index, size; kwargs...) end -OneElementArray(index::Int, size::Integer) = OneElementVector{Bool}(index, size) # 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} + value, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {N} - return OneElementArray(value, index, axes) + return OneElementArray(value, index, axes; kwargs...) end function oneelement( - eltype::Type, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange} + eltype::Type, index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... ) where {N} - return oneelement(one(eltype), index, axes) + return oneelement(one(eltype), index, axes; kwargs...) end -function oneelement(index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}) where {N} - return oneelement(Bool, index, axes) +function oneelement( + index::NTuple{N,Int}, axes::NTuple{N,AbstractUnitRange}; kwargs... +) where {N} + return oneelement(Bool, index, axes; kwargs...) end -function oneelement(value, index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} - return oneelement(value, index, Base.oneto.(size)) +function oneelement( + value, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... +) where {N} + return oneelement(value, index, Base.oneto.(size); kwargs...) end -function oneelement(eltype::Type, index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} - return oneelement(one(eltype), index, size) +function oneelement( + eltype::Type, index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs... +) where {N} + return oneelement(one(eltype), index, size; kwargs...) end -function oneelement(index::NTuple{N,Int}, size::NTuple{N,Integer}) where {N} - return oneelement(Bool, index, size) +function oneelement(index::NTuple{N,Int}, size::NTuple{N,Integer}; kwargs...) where {N} + return oneelement(Bool, index, size; kwargs...) end -function oneelement(value, ax_ind::Pair{<:AbstractUnitRange,Int}...) - return oneelement(value, last.(ax_ind), first.(ax_ind)) +function oneelement(value, ax_ind::Pair{<:AbstractUnitRange,Int}...; kwargs...) + return oneelement(value, last.(ax_ind), first.(ax_ind); kwargs...) end -function oneelement(eltype::Type, ax_ind::Pair{<:AbstractUnitRange,Int}...) - return oneelement(one(eltype), ax_ind...) +function oneelement(eltype::Type, ax_ind::Pair{<:AbstractUnitRange,Int}...; kwargs...) + return oneelement(one(eltype), ax_ind...; kwargs...) end -function oneelement(ax_ind::Pair{<:AbstractUnitRange,Int}...) - return oneelement(Bool, ax_ind...) +function oneelement(ax_ind::Pair{<:AbstractUnitRange,Int}...; kwargs...) + return oneelement(Bool, ax_ind...; kwargs...) end -function oneelement(value) - return oneelement(value, (), ()) +function oneelement(value; kwargs...) + return oneelement(value, (), (); kwargs...) end -function oneelement(eltype::Type) - return oneelement(one(eltype)) +function oneelement(eltype::Type; kwargs...) + return oneelement(one(eltype); kwargs...) end -function oneelement() - return oneelement(Bool) +function oneelement(; kwargs...) + return oneelement(Bool; kwargs...) end Base.axes(a::OneElementArray) = getfield(a, :axes) @@ -265,7 +295,7 @@ function getstoredindex(a::OneElementArray, I::Int...) return storedvalue(a) end function getunstoredindex(a::OneElementArray, I::Int...) - return a.getunstoredindex(a, I...) + return a.getunstored(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 12f5112..2fcb138 100644 --- a/src/sparsearraydok.jl +++ b/src/sparsearraydok.jl @@ -1,12 +1,14 @@ using Accessors: @set -using Dictionaries: AbstractDictionary, Dictionary, IndexError, set! +using Dictionaries: Dictionary, IndexError, set! -function default_getunstoredindex(a::AbstractArray, I::Int...) +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 + """ SparseArrayDOK{T,N,F} <: AbstractSparseArray{T,N} @@ -16,89 +18,46 @@ optionally with a function of type `F` to instantiate non-stored elements. struct SparseArrayDOK{T,N,F} <: AbstractSparseArray{T,N} storage::DOKStorage{T,N} size::NTuple{N,Int} - getunstoredindex::F - - # bare constructor - function SparseArrayDOK{T,N,F}( - ::UndefInitializer, size::Dims{N}, getunstoredindex::F - ) where {T,N,F} - storage = DOKStorage{T,N}() - return new{T,N,F}(storage, size, getunstoredindex) - end - - # unchecked constructor from data - function SparseArrayDOK{T,N,F}( - storage::DOKStorage{T,N}, size::Dims{N}, getunstoredindex::F + getunstored::F + global @inline function _SparseArrayDOK( + storage::DOKStorage{T,N}, size::Dims{N}, getunstored::F ) where {T,N,F} - return new{T,N,F}(storage, size, getunstoredindex) + return new{T,N,F}(storage, size, getunstored) end end # Constructors # ------------ """ - SparseArrayDOK{T}(undef, dims, unstored...) - SparseArrayDOK{T}(undef, dims...) - SparseArrayDOK{T,N}(undef, dims, unstored...) - SparseArrayDOK{T,N}(undef, dims...) + SparseArrayDOK{T}(undef, dims...[; getunstored]) + SparseArrayDOK{T,N}(undef, dims...[; getunstored]) 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, unstored...) +SparseArrayDOK{T,N}(::UndefInitializer, dims; kwargs...) function SparseArrayDOK{T,N}( - ::UndefInitializer, dims::Dims, getunstoredindex=default_getunstoredindex + ::UndefInitializer, dims::Dims; getunstored=getzero ) where {T,N} (length(dims) == N && all(≥(0), dims)) || throw(ArgumentError("Invalid dimensions: $dims")) - F = typeof(getunstoredindex) - return SparseArrayDOK{T,N,F}(undef, dims, getunstoredindex) -end -function SparseArrayDOK{T}(::UndefInitializer, dims::Dims{N}, unstored...) where {T,N} - return SparseArrayDOK{T,N}(undef, dims, unstored...) + storage = DOKStorage{T,N}() + return _SparseArrayDOK(storage, dims, getunstored) end -function SparseArrayDOK{T}(::UndefInitializer, dims::Vararg{Int,N}) where {T,N} - return SparseArrayDOK{T,N}(undef, dims) -end - -""" - SparseArrayDOK(storage::Union{AbstractDict,AbstractDictionary}, dims, unstored...) - SparseArrayDOK{T}(storage::Union{AbstractDict,AbstractDictionary}, dims, unstored...) - SparseArrayDOK{T,N}(storage::Union{AbstractDict,AbstractDictionary}, dims, unstored...) - -Construct an `N`-dimensional [`SparseArrayDOK`](@ref) containing elements of type `T`. Both -`T` and `N` can either be supplied explicitly or be determined by the `storage` and the -length or number of `dims`. - -This constructor does not take ownership of the supplied storage, and will result in an -independent container. -""" -SparseArrayDOK{T,N}(::Union{AbstractDict,AbstractDictionary}, dims, unstored...) - -const AbstractDictOrDictionary = Union{AbstractDict,AbstractDictionary} -# checked constructor from data: use `setindex!` to validate/convert input -function SparseArrayDOK{T,N}( - storage::AbstractDictOrDictionary, dims::Dims, unstored... -) where {T,N} - A = SparseArrayDOK{T,N}(undef, dims, unstored...) - for (i, v) in pairs(storage) - A[i] = v - end - return A +function SparseArrayDOK{T,N}(::UndefInitializer, dims::Vararg{Int,N}; kwargs...) where {T,N} + return SparseArrayDOK{T,N}(undef, dims; kwargs...) end -function SparseArrayDOK{T}( - storage::AbstractDictOrDictionary, dims::Dims, unstored... -) where {T} - return SparseArrayDOK{T,length(dims)}(storage, dims, unstored...) +function SparseArrayDOK{T}(::UndefInitializer, dims::Dims{N}; kwargs...) where {T,N} + return SparseArrayDOK{T,N}(undef, dims; kwargs...) end -function SparseArrayDOK(storage::AbstractDictOrDictionary, dims::Dims, unstored...) - return SparseArrayDOK{valtype(storage)}(storage, dims, unstored...) +function SparseArrayDOK{T}(::UndefInitializer, dims::Vararg{Int,N}; kwargs...) where {T,N} + return SparseArrayDOK{T,N}(undef, dims; kwargs...) end -function set_getunstoredindex(a::SparseArrayDOK, f) - @set a.getunstoredindex = f +function set_getunstored(a::SparseArrayDOK, f) + @set a.getunstored = f return a end @@ -124,7 +83,7 @@ function getstoredindex(a::SparseArrayDOK, I::Int...) return storage(a)[CartesianIndex(I)] end function getunstoredindex(a::SparseArrayDOK, I::Int...) - return a.getunstoredindex(a, I...) + return a.getunstored(a, I...) end function setstoredindex!(a::SparseArrayDOK, value, I::Int...) # TODO: Have a way to disable this check, analogous to `checkbounds`, diff --git a/test/Project.toml b/test/Project.toml index ccff132..cccbcc1 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -23,7 +23,7 @@ JLArrays = "0.2.0" LinearAlgebra = "<0.0.1, 1" Random = "<0.0.1, 1" SafeTestsets = "0.1.0" -SparseArraysBase = "0.3.0" +SparseArraysBase = "0.4.0" StableRNGs = "1.0.2" Suppressor = "0.2.8" Test = "<0.0.1, 1" diff --git a/test/test_exports.jl b/test/test_exports.jl index 952e478..15dce45 100644 --- a/test/test_exports.jl +++ b/test/test_exports.jl @@ -12,6 +12,7 @@ using Test: @test, @testset :eachstoredindex, :isstored, :oneelement, + :sparse, :sparserand, :sparserand!, :sparsezeros, diff --git a/test/test_linalg.jl b/test/test_linalg.jl index 7c67354..950ab77 100644 --- a/test/test_linalg.jl +++ b/test/test_linalg.jl @@ -1,6 +1,7 @@ using SparseArraysBase: sparserand using LinearAlgebra: mul! using StableRNGs: StableRNG +using Test: @test, @testset const rng = StableRNG(123) diff --git a/test/test_sparsearraydok.jl b/test/test_sparsearraydok.jl index 8409958..4e50982 100644 --- a/test/test_sparsearraydok.jl +++ b/test/test_sparsearraydok.jl @@ -1,18 +1,24 @@ using Adapt: adapt using ArrayLayouts: zero! +using Dictionaries: Dictionary using JLArrays: JLArray, @allowscalar using SparseArraysBase: SparseArraysBase, SparseArrayDOK, + SparseMatrixDOK, eachstoredindex, getstoredindex, getunstoredindex, isstored, setstoredindex!, setunstoredindex!, + sparse, + sparserand, + sparsezeros, storedlength, storedpairs, storedvalues +using StableRNGs: StableRNG using Test: @test, @testset elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @@ -243,4 +249,60 @@ arrayts = (Array,) @test storedlength(r1) == 2 @test a == [11 12; 12 22] @test storedlength(a) == 4 + + for d in ( + Dict([CartesianIndex(1, 2) => elt(12), CartesianIndex(2, 1) => elt(21)]), + Dictionary([CartesianIndex(1, 2), CartesianIndex(2, 1)], [elt(12), elt(21)]), + ) + for a in ( + sparse(d, 2, 2), + sparse(d, 2, 2; getunstored=Returns(zero(elt))), + 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]) + @test a[2, 1] == elt(21) + @test a[1, 2] == elt(12) + @test iszero(a[2, 2]) + @test size(a) == (2, 2) + @test storedlength(a) == 2 + @test eltype(a) === elt + @test a isa SparseMatrixDOK{elt} + end + end + + for (a, elt′) in ( + (sparsezeros(elt, 2, 2), elt), + (sparsezeros(elt, 2, 2; getunstored=Returns(zero(elt))), 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((2, 2)), Float64), + (sparsezeros((2, 2); getunstored=Returns(zero(Float64))), Float64), + ) + @test iszero(a) + @test size(a) == (2, 2) + @test storedlength(a) == 0 + @test eltype(a) === elt′ + @test a isa SparseMatrixDOK{elt′} + end + + rng = StableRNG(123) + for (a, elt′) in ( + (sparserand(rng, elt, 20, 20; density=0.5), elt), + (sparserand(rng, elt, (20, 20); density=0.5), elt), + (sparserand(rng, 20, 20; density=0.5), Float64), + (sparserand(rng, (20, 20); density=0.5), Float64), + ) + @test !iszero(a) + @test size(a) == (20, 20) + @test !iszero(storedlength(a)) + @test eltype(a) === elt′ + @test a isa SparseMatrixDOK{elt′} + end end