Skip to content

Commit 1e54b85

Browse files
authored
Constructors refactor: undef, sparsezeros, sparserand and sparserand! (#33)
* Add `sprand` and `sprand!` * Be a bit more mindful with constructors * Add `spzeros` * update `sprand` to take in `rfn` function * update docstrings * update README * Bump v0.3.0 * Bump skeleton * move functions * export new functions * slightly re-organize constructors * update registrator action * enable (some of) Aqua * fix undefined export * remove tested exports (included in aqua testset) * update testfunction * change `rfn` to `rand_function` * Add `SparseArrayDOK` docstrings * Revert "remove tested exports (included in aqua testset)" This reverts commit e7053a8. * update tested exports * merge export lists * change `rand_function` to `randfun` * change `spzeros`, `sprand` and `sprand!` to `sparsezeros`, `sparserand` and `sparserand!` * sort exports alphabetically
1 parent dd5554f commit 1e54b85

16 files changed

+233
-82
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: "Integration Test Request"
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
jobs:
8+
integrationrequest:
9+
if: |
10+
github.event.issue.pull_request &&
11+
contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association)
12+
uses: ITensor/ITensorActions/.github/workflows/IntegrationTestRequest.yml@main
13+
with:
14+
localregistry: https://github.com/ITensor/ITensorRegistry.git

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2024 ITensor developers
3+
Copyright (c) 2025 ITensor developers
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SparseArraysBase"
22
uuid = "0d5efcca-f356-4864-8770-e1ed8d78f208"
33
authors = ["ITensor developers <[email protected]> and contributors"]
4-
version = "0.2.11"
4+
version = "0.3.0"
55

66
[deps]
77
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
@@ -11,6 +11,7 @@ Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
1111
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
1212
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1313
MapBroadcast = "ebd9b9da-f48d-417c-9660-449667d60261"
14+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1415

1516
[compat]
1617
Accessors = "0.1.41"
@@ -21,6 +22,7 @@ Dictionaries = "0.4.3"
2122
FillArrays = "1.13.0"
2223
LinearAlgebra = "1.10"
2324
MapBroadcast = "0.1.5"
25+
Random = "1.10.0"
2426
SafeTestsets = "0.1"
2527
Suppressor = "0.2"
2628
Test = "1.10"

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ using SparseArraysBase:
4848
zero!
4949
using Test: @test, @test_throws
5050

51-
a = SparseArrayDOK{Float64}(2, 2)
51+
a = SparseArrayDOK{Float64}(undef, 2, 2)
5252
````
5353

5454
AbstractArray interface:
@@ -122,32 +122,32 @@ b = a[1:2, 2]
122122
@test b == [12, 0]
123123
@test storedlength(b) == 1
124124

125-
a = SparseArrayDOK{Float64}(2, 2)
125+
a = SparseArrayDOK{Float64}(undef, 2, 2)
126126
a .= 2
127127
for I in eachindex(a)
128128
@test a[I] == 2
129129
end
130130
@test storedlength(a) == length(a)
131131

132-
a = SparseArrayDOK{Float64}(2, 2)
132+
a = SparseArrayDOK{Float64}(undef, 2, 2)
133133
fill!(a, 2)
134134
for I in eachindex(a)
135135
@test a[I] == 2
136136
end
137137
@test storedlength(a) == length(a)
138138

139-
a = SparseArrayDOK{Float64}(2, 2)
139+
a = SparseArrayDOK{Float64}(undef, 2, 2)
140140
fill!(a, 0)
141141
@test iszero(a)
142142
@test iszero(storedlength(a))
143143

144-
a = SparseArrayDOK{Float64}(2, 2)
144+
a = SparseArrayDOK{Float64}(undef, 2, 2)
145145
a[1, 2] = 12
146146
zero!(a)
147147
@test iszero(a)
148148
@test iszero(storedlength(a))
149149

150-
a = SparseArrayDOK{Float64}(2, 2)
150+
a = SparseArrayDOK{Float64}(undef, 2, 2)
151151
a[1, 2] = 12
152152
b = zero(a)
153153
@test iszero(b)

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ makedocs(;
1616
edit_link="main",
1717
assets=String[],
1818
),
19-
pages=["Home" => "index.md"],
19+
pages=["Home" => "index.md", "Reference" => "reference.md"],
2020
)
2121

2222
deploydocs(;

docs/src/reference.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Reference
2+
3+
```@autodocs
4+
Modules = [SparseArraysBase]
5+
```

examples/README.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ using SparseArraysBase:
5353
zero!
5454
using Test: @test, @test_throws
5555

56-
a = SparseArrayDOK{Float64}(2, 2)
56+
a = SparseArrayDOK{Float64}(undef, 2, 2)
5757

5858
# AbstractArray interface:
5959

@@ -118,32 +118,32 @@ b = a[1:2, 2]
118118
@test b == [12, 0]
119119
@test storedlength(b) == 1
120120

121-
a = SparseArrayDOK{Float64}(2, 2)
121+
a = SparseArrayDOK{Float64}(undef, 2, 2)
122122
a .= 2
123123
for I in eachindex(a)
124124
@test a[I] == 2
125125
end
126126
@test storedlength(a) == length(a)
127127

128-
a = SparseArrayDOK{Float64}(2, 2)
128+
a = SparseArrayDOK{Float64}(undef, 2, 2)
129129
fill!(a, 2)
130130
for I in eachindex(a)
131131
@test a[I] == 2
132132
end
133133
@test storedlength(a) == length(a)
134134

135-
a = SparseArrayDOK{Float64}(2, 2)
135+
a = SparseArrayDOK{Float64}(undef, 2, 2)
136136
fill!(a, 0)
137137
@test iszero(a)
138138
@test iszero(storedlength(a))
139139

140-
a = SparseArrayDOK{Float64}(2, 2)
140+
a = SparseArrayDOK{Float64}(undef, 2, 2)
141141
a[1, 2] = 12
142142
zero!(a)
143143
@test iszero(a)
144144
@test iszero(storedlength(a))
145145

146-
a = SparseArrayDOK{Float64}(2, 2)
146+
a = SparseArrayDOK{Float64}(undef, 2, 2)
147147
a[1, 2] = 12
148148
b = zero(a)
149149
@test iszero(b)

src/SparseArraysBase.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ export SparseArrayDOK,
88
OneElementVector,
99
eachstoredindex,
1010
isstored,
11-
oneelementarray,
11+
oneelement,
12+
sparserand,
13+
sparserand!,
14+
sparsezeros,
1215
storedlength,
1316
storedpairs,
1417
storedvalues

src/abstractsparsearray.jl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,66 @@ function getunstoredindex(a::ReplacedUnstoredSparseArray, I::Int...)
5353
end
5454
eachstoredindex(a::ReplacedUnstoredSparseArray) = eachstoredindex(parent(a))
5555
@derive ReplacedUnstoredSparseArray AbstractArrayOps
56+
57+
# Special-purpose constructors
58+
# ----------------------------
59+
using Random: Random, AbstractRNG, default_rng
60+
61+
@doc """
62+
sparsezeros([T::Type], dims) -> A::SparseArrayDOK{T}
63+
64+
Create an empty size `dims` sparse array.
65+
The optional `T` argument specifies the element type, which defaults to `Float64`.
66+
""" sparsezeros
67+
68+
sparsezeros(dims::Dims) = sparsezeros(Float64, dims)
69+
sparsezeros(::Type{T}, dims::Dims) where {T} = SparseArrayDOK{T}(undef, dims)
70+
71+
@doc """
72+
sparserand([rng], [T::Type], dims; density::Real=0.5, randfun::Function=rand) -> A::SparseArrayDOK{T}
73+
74+
Create a random size `dims` sparse array in which the probability of any element being stored is independently given by `density`.
75+
The optional `rng` argument specifies a random number generator, see also `Random`.
76+
The optional `T` argument specifies the element type, which defaults to `Float64`.
77+
The optional `randfun` argument can be used to control the type of random elements, and should support
78+
the signature `randfun(rng, T, N)` to generate `N` entries of type `T`.
79+
80+
81+
See also [`sparserand!`](@ref).
82+
""" sparserand
83+
84+
function sparserand(::Type{T}, dims::Dims; kwargs...) where {T}
85+
return sparserand(default_rng(), T, dims; kwargs...)
86+
end
87+
sparserand(dims::Dims; kwargs...) = sparserand(default_rng(), Float64, dims; kwargs...)
88+
function sparserand(rng::AbstractRNG, dims::Dims; kwargs...)
89+
return sparserand(rng, Float64, dims; kwargs...)
90+
end
91+
function sparserand(rng::AbstractRNG, ::Type{T}, dims::Dims; kwargs...) where {T}
92+
A = SparseArrayDOK{T}(undef, dims)
93+
sparserand!(rng, A; kwargs...)
94+
return A
95+
end
96+
97+
@doc """
98+
sparserand!([rng], A::AbstractArray; density::Real=0.5, randfun::Function=rand) -> A
99+
100+
Overwrite part of an array with random entries, where the probability of overwriting is independently given by `density`.
101+
The optional `rng` argument specifies a random number generator, see also `Random`.
102+
The optional `randfun` argument can be used to control the type of random elements, and should support
103+
the signature `randfun(rng, T, N)` to generate `N` entries of type `T`.
104+
105+
See also [`sparserand`](@ref).
106+
""" sparserand!
107+
108+
sparserand!(A::AbstractArray; kwargs...) = sparserand!(default_rng(), A; kwargs...)
109+
function sparserand!(
110+
rng::AbstractRNG, A::AbstractArray; density::Real=0.5, randfun::Function=Random.rand
111+
)
112+
ArrayLayouts.zero!(A)
113+
rand_inds = Random.randsubseq(rng, eachindex(A), density)
114+
rand_entries = randfun(rng, eltype(A), length(rand_inds))
115+
@inbounds for (I, v) in zip(rand_inds, rand_entries)
116+
A[I] = v
117+
end
118+
end

src/abstractsparsearrayinterface.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ end
200200
a::AbstractArray, T::Type, size::Tuple{Vararg{Int}}
201201
)
202202
# TODO: Define `default_similartype` or something like that?
203-
return SparseArrayDOK{T}(size...)
203+
return SparseArrayDOK{T}(undef, size)
204204
end
205205

206206
# map over a specified subset of indices of the inputs.

src/sparsearraydok.jl

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,111 @@
11
using Accessors: @set
2-
using Dictionaries: Dictionary, IndexError, set!
2+
using Dictionaries: AbstractDictionary, Dictionary, IndexError, set!
33

44
function default_getunstoredindex(a::AbstractArray, I::Int...)
55
return zero(eltype(a))
66
end
77

8+
const DOKStorage{T,N} = Dictionary{CartesianIndex{N},T}
9+
10+
"""
11+
SparseArrayDOK{T,N,F} <: AbstractSparseArray{T,N}
12+
13+
`N`-dimensional sparse Dictionary-of-keys (DOK) array with elements of type `T`,
14+
optionally with a function of type `F` to instantiate non-stored elements.
15+
"""
816
struct SparseArrayDOK{T,N,F} <: AbstractSparseArray{T,N}
9-
storage::Dictionary{CartesianIndex{N},T}
17+
storage::DOKStorage{T,N}
1018
size::NTuple{N,Int}
1119
getunstoredindex::F
12-
end
1320

14-
function set_getunstoredindex(a::SparseArrayDOK, f)
15-
@set a.getunstoredindex = f
16-
return a
21+
# bare constructor
22+
function SparseArrayDOK{T,N,F}(
23+
::UndefInitializer, size::Dims{N}, getunstoredindex::F
24+
) where {T,N,F}
25+
storage = DOKStorage{T,N}()
26+
return new{T,N,F}(storage, size, getunstoredindex)
27+
end
28+
29+
# unchecked constructor from data
30+
function SparseArrayDOK{T,N,F}(
31+
storage::DOKStorage{T,N}, size::Dims{N}, getunstoredindex::F
32+
) where {T,N,F}
33+
return new{T,N,F}(storage, size, getunstoredindex)
34+
end
1735
end
1836

19-
using DerivableInterfaces: DerivableInterfaces
20-
# This defines the destination type of various operations in DerivableInterfaces.jl.
21-
DerivableInterfaces.arraytype(::AbstractSparseArrayInterface, T::Type) = SparseArrayDOK{T}
37+
# Constructors
38+
# ------------
39+
"""
40+
SparseArrayDOK{T}(undef, dims, unstored...)
41+
SparseArrayDOK{T}(undef, dims...)
42+
SparseArrayDOK{T,N}(undef, dims, unstored...)
43+
SparseArrayDOK{T,N}(undef, dims...)
2244
23-
function SparseArrayDOK{T,N}(size::Vararg{Int,N}) where {T,N}
24-
getunstoredindex = default_getunstoredindex
45+
Construct an uninitialized `N`-dimensional [`SparseArrayDOK`](@ref) containing
46+
elements of type `T`. `N` can either be supplied explicitly, or be determined by
47+
the length or number of `dims`.
48+
"""
49+
SparseArrayDOK{T,N}(::UndefInitializer, dims, unstored...)
50+
51+
function SparseArrayDOK{T,N}(
52+
::UndefInitializer, dims::Dims, getunstoredindex=default_getunstoredindex
53+
) where {T,N}
54+
(length(dims) == N && all((0), dims)) ||
55+
throw(ArgumentError("Invalid dimensions: $dims"))
2556
F = typeof(getunstoredindex)
26-
return SparseArrayDOK{T,N,F}(Dictionary{CartesianIndex{N},T}(), size, getunstoredindex)
57+
return SparseArrayDOK{T,N,F}(undef, dims, getunstoredindex)
58+
end
59+
function SparseArrayDOK{T}(::UndefInitializer, dims::Dims{N}, unstored...) where {T,N}
60+
return SparseArrayDOK{T,N}(undef, dims, unstored...)
61+
end
62+
function SparseArrayDOK{T}(::UndefInitializer, dims::Vararg{Int,N}) where {T,N}
63+
return SparseArrayDOK{T,N}(undef, dims)
2764
end
2865

29-
function SparseArrayDOK{T}(::UndefInitializer, size::Tuple{Vararg{Int}}) where {T}
30-
return SparseArrayDOK{T,length(size)}(size...)
66+
"""
67+
SparseArrayDOK(storage::Union{AbstractDict,AbstractDictionary}, dims, unstored...)
68+
SparseArrayDOK{T}(storage::Union{AbstractDict,AbstractDictionary}, dims, unstored...)
69+
SparseArrayDOK{T,N}(storage::Union{AbstractDict,AbstractDictionary}, dims, unstored...)
70+
71+
Construct an `N`-dimensional [`SparseArrayDOK`](@ref) containing elements of type `T`. Both
72+
`T` and `N` can either be supplied explicitly or be determined by the `storage` and the
73+
length or number of `dims`.
74+
75+
This constructor does not take ownership of the supplied storage, and will result in an
76+
independent container.
77+
"""
78+
SparseArrayDOK{T,N}(::Union{AbstractDict,AbstractDictionary}, dims, unstored...)
79+
80+
const AbstractDictOrDictionary = Union{AbstractDict,AbstractDictionary}
81+
# checked constructor from data: use `setindex!` to validate/convert input
82+
function SparseArrayDOK{T,N}(
83+
storage::AbstractDictOrDictionary, dims::Dims, unstored...
84+
) where {T,N}
85+
A = SparseArrayDOK{T,N}(undef, dims, unstored...)
86+
for (i, v) in pairs(storage)
87+
A[i] = v
88+
end
89+
return A
90+
end
91+
function SparseArrayDOK{T}(
92+
storage::AbstractDictOrDictionary, dims::Dims, unstored...
93+
) where {T}
94+
return SparseArrayDOK{T,length(dims)}(storage, dims, unstored...)
95+
end
96+
function SparseArrayDOK(storage::AbstractDictOrDictionary, dims::Dims, unstored...)
97+
return SparseArrayDOK{valtype(storage)}(storage, dims, unstored...)
3198
end
3299

33-
function SparseArrayDOK{T}(size::Int...) where {T}
34-
return SparseArrayDOK{T,length(size)}(size...)
100+
function set_getunstoredindex(a::SparseArrayDOK, f)
101+
@set a.getunstoredindex = f
102+
return a
35103
end
36104

105+
using DerivableInterfaces: DerivableInterfaces
106+
# This defines the destination type of various operations in DerivableInterfaces.jl.
107+
DerivableInterfaces.arraytype(::AbstractSparseArrayInterface, T::Type) = SparseArrayDOK{T}
108+
37109
using DerivableInterfaces: @array_aliases
38110
# Define `SparseMatrixDOK`, `AnySparseArrayDOK`, etc.
39111
@array_aliases SparseArrayDOK

test/runtests.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ isexamplefile(fn) =
2424
# tests in groups based on folder structure
2525
for testgroup in filter(isdir, readdir(@__DIR__))
2626
if GROUP == "ALL" || GROUP == uppercase(testgroup)
27-
for file in filter(istestfile, readdir(joinpath(@__DIR__, testgroup); join=true))
27+
groupdir = joinpath(@__DIR__, testgroup)
28+
for file in filter(istestfile, readdir(groupdir))
29+
filename = joinpath(groupdir, file)
2830
@eval @safetestset $file begin
29-
include($file)
31+
include($filename)
3032
end
3133
end
3234
end

test/test_aqua.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ using Aqua: Aqua
33
using Test: @testset
44

55
@testset "Code quality (Aqua.jl)" begin
6-
# TODO: Fix Aqua issues and add this back.
7-
# Aqua.test_all(SparseArraysBase)
6+
Aqua.test_all(SparseArraysBase; ambiguities=false)
87
end

0 commit comments

Comments
 (0)