Skip to content

Commit 0da32ac

Browse files
committed
implement generation of random Set & Dict & Sparse & String & BitArray
* implement generation of random dictionaries: rand(Combine(Pair, Int, 1:3), Dict, 10) * implement generation of random sets: rand(1:3, Set, 10) * supersede sprand[n](m, [m], p, rfn) by rand(X, p::AbstractFloat, n, [m]) * supersede randstring(n, chars) by rand(chars, String, n) * supersede bitrand(dims) by rand(BitArray, dims)
1 parent 00b5824 commit 0da32ac

3 files changed

Lines changed: 239 additions & 3 deletions

File tree

src/RandomExtensions.jl

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,79 @@ module RandomExtensions
22

33
export Combine, Uniform, Normal, Exponential, CloseOpen
44

5-
import Random: Sampler, rand
5+
import Random: Sampler, rand, rand!
66

77
using Random
8-
using Random: SamplerTrivial, SamplerSimple, SamplerTag, Repetition
8+
using Random: GLOBAL_RNG, SamplerTrivial, SamplerSimple, SamplerTag, Repetition
9+
10+
using SparseArrays: sprand, sprandn
11+
912

1013
include("distributions.jl")
1114
include("sampling.jl")
15+
include("containers.jl")
16+
17+
18+
## updated rand docstring (TODO: replace Base's one)
19+
20+
"""
21+
rand([rng=GLOBAL_RNG], [S], [C...]) # RandomExtensions
22+
23+
Pick a random element or collection of random elements from the set of values specified by `S`;
24+
`S` can be
25+
26+
* an indexable collection (for example `1:n` or `['x','y','z']`),
27+
* an `AbstractDict` or `AbstractSet` object,
28+
* a string (considered as a collection of characters), or
29+
* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for
30+
integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating
31+
point numbers;
32+
* a `Distribution` object, e.g. `Normal()` for a normal distribution (like `randn()`),
33+
or `CloseOpen(10.0, 20.0)` for uniform `Float64` numbers in the range ``[10.0, 20.0)``;
34+
* a `Combine` object, which can be either `Combine(Pair, S1, S2)` or `Combine(Complex, S1, S2)`,
35+
where `S1` and `S2` are one of the specifications above; `Pair` or `Complex` can optionally be
36+
given as concrete types, e.g. `Combine(ComplexF64, 1:3, Int)` to generate `ComplexF64` instead
37+
of `Complex{Int}`.
38+
39+
`S` usually defaults to [`Float64`](@ref).
40+
41+
If `C...` is not specified, `rand` produces a scalar. Otherwise, `C...` can be:
42+
43+
* a set of integers, or a tuple of `Int`, which specify the dimensions of an `Array` to generate;
44+
* `(p::AbstractFloat, m::Integer, [n::Integer])`, which produces a sparse array of dimensions `(m, n)`,
45+
in which the probability of any element being nonzero is independently given by `p`
46+
* `(String, [n=8])`, which produces a random `String` of length `n`; the generated string consists of `Char`
47+
taken from a predefined set like `randstring`, and can be specified with the `S` parameter.
48+
* `(Dict, n)`, which produces a `Dict` of length `n`; `S` must then specify the type of its elements,
49+
e.g. `Combine(Pair, Int, 2:3)`;
50+
* `(Set, n)`, which produces a `Set` of length `n`;
51+
* `(BitArray, dims...)`, which produces a `BitArray` with the specified dimensions.
52+
53+
# Examples
54+
```julia-repl
55+
julia> rand(Int, 2)
56+
2-element Array{Int64,1}:
57+
1339893410598768192
58+
1575814717733606317
59+
60+
julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4))
61+
1=>2
62+
63+
julia> rand("abc", String, 12)
64+
"bccaacaabaac"
65+
66+
julia> rand(1:10, Set, 3)
67+
Set([3, 8, 6])
68+
```
69+
70+
!!! note
71+
The complexity of `rand(rng, s::Union{AbstractDict,AbstractSet})`
72+
is linear in the length of `s`, unless an optimized method with
73+
constant complexity is available, which is the case for `Dict`,
74+
`Set` and `BitSet`. For more than a few calls, use `rand(rng,
75+
collect(s))` instead, or either `rand(rng, Dict(s))` or `rand(rng,
76+
Set(s))` as appropriate.
77+
"""
78+
rand
1279

1380
end # module

src/containers.jl

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# generation of some containers filled with random values
2+
3+
4+
## dicts
5+
6+
rand!(A::AbstractDict{K,V}, dist::Distribution{<:Pair}=Combine(Pair, K, V)) where {K,V} =
7+
rand!(GLOBAL_RNG, A, dist)
8+
9+
rand!(rng::AbstractRNG, A::AbstractDict{K,V},
10+
dist::Distribution{<:Pair}=Combine(Pair, K, V)) where {K,V} =
11+
rand!(GLOBAL_RNG, A, Sampler(rng, dist))
12+
13+
function _rand!(rng::AbstractRNG, A::Union{AbstractDict,AbstractSet}, n::Integer, sp::Sampler)
14+
empty!(A)
15+
while length(A) < n
16+
push!(A, rand(rng, sp))
17+
end
18+
A
19+
end
20+
21+
rand!(rng::AbstractRNG, A::AbstractDict{K,V}, sp::Sampler) where {K,V} = _rand!(rng, A, length(A), sp)
22+
23+
rand(rng::AbstractRNG, dist::Distribution{<:Pair}, ::Type{T}, n::Integer) where {T<:AbstractDict} =
24+
_rand!(rng, deduce_type(T, eltype(dist).parameters...)(), n, Sampler(rng, dist))
25+
26+
rand(u::Distribution{<:Pair}, ::Type{T}, n::Integer) where {T<:AbstractDict} = rand(GLOBAL_RNG, u, T, n)
27+
28+
29+
## sets
30+
31+
rand!(A::AbstractSet{T}, X) where {T} = rand!(GLOBAL_RNG, A, X)
32+
rand!(A::AbstractSet{T}, ::Type{X}=T) where {T,X} = rand!(GLOBAL_RNG, A, X)
33+
34+
rand!(rng::AbstractRNG, A::AbstractSet, X) = rand!(rng, A, Sampler(rng, X))
35+
rand!(rng::AbstractRNG, A::AbstractSet{T}, ::Type{X}=T) where {T,X} = rand!(rng, A, Sampler(rng, X))
36+
37+
_rand0!(rng::AbstractRNG, A::AbstractSet, n::Integer, X) = _rand!(rng, A, n, Sampler(rng, X))
38+
_rand0!(rng::AbstractRNG, A::AbstractSet, n::Integer, ::Type{X}) where {X} = _rand!(rng, A, n, Sampler(rng, X))
39+
_rand0!(rng::AbstractRNG, A::AbstractSet, n::Integer, sp::Sampler) = _rand!(rng, A, n, sp)
40+
41+
rand!(rng::AbstractRNG, A::AbstractSet, sp::Sampler) = _rand!(rng, A, length(A), sp)
42+
43+
44+
rand(r::AbstractRNG, ::Type{T}, n::Integer) where {T<:AbstractSet} = rand(r, Float64, T, n)
45+
rand( ::Type{T}, n::Integer) where {T<:AbstractSet} = rand(GLOBAL_RNG, T, n)
46+
47+
rand(r::AbstractRNG, X, ::Type{T}, n::Integer) where {T<:AbstractSet} = _rand0!(r, deduce_type(T, eltype(X))(), n, X)
48+
rand( X, ::Type{T}, n::Integer) where {T<:AbstractSet} = rand(GLOBAL_RNG, X, T, n)
49+
50+
rand(r::AbstractRNG, ::Type{X}, ::Type{T}, n::Integer) where {X,T<:AbstractSet} = _rand0!(r, deduce_type(T, X)(), n, X)
51+
rand( ::Type{X}, ::Type{T}, n::Integer) where {X,T<:AbstractSet} = rand(GLOBAL_RNG, X, T, n)
52+
53+
54+
## sparse vectors & matrices
55+
56+
rand(r::AbstractRNG, p::AbstractFloat, m::Integer) = sprand(r, m, p)
57+
rand( p::AbstractFloat, m::Integer) = sprand(GLOBAL_RNG, m, p)
58+
rand(r::AbstractRNG, p::AbstractFloat, m::Integer, n::Integer) = sprand(r, m, n, p)
59+
rand( p::AbstractFloat, m::Integer, n::Integer) = sprand(GLOBAL_RNG, m, n, p)
60+
61+
rand(r::AbstractRNG, X::Sampler, p::AbstractFloat, m::Integer) =
62+
sprand(r, m, p, (r, n)->rand(r, X, n))
63+
64+
rand(r::AbstractRNG, X, p::AbstractFloat, m::Integer) =
65+
rand(r, Sampler(r, X), p, m)
66+
67+
rand(r::AbstractRNG, ::Type{X}, p::AbstractFloat, m::Integer) where {X} =
68+
rand(r, Sampler(r, X), p, m)
69+
70+
rand(X, p::AbstractFloat, m::Integer) = rand(GLOBAL_RNG, X, p, m)
71+
72+
rand(r::AbstractRNG, X::Sampler, p::AbstractFloat, m::Integer, n::Integer) =
73+
sprand(r, m, n, p, (r, n)->rand(r, X, n), eltype(X))
74+
75+
rand(r::AbstractRNG, X, p::AbstractFloat, m::Integer, n::Integer) =
76+
rand(r, Sampler(r, X), p, m, n)
77+
78+
rand(r::AbstractRNG, ::Type{X}, p::AbstractFloat, m::Integer, n::Integer) where {X} =
79+
rand(r, Sampler(r, X), p, m, n)
80+
81+
rand(X, p::AbstractFloat, m::Integer, n::Integer) = rand(GLOBAL_RNG, X, p, m, n)
82+
83+
84+
## String
85+
86+
let b = UInt8['0':'9';'A':'Z';'a':'z']
87+
global rand
88+
rand(rng::AbstractRNG, chars, ::Type{String}, n::Integer=8) = String(rand(rng, chars, n))
89+
rand( chars, ::Type{String}, n::Integer=8) = rand(GLOBAL_RNG, chars, String, n)
90+
rand(rng::AbstractRNG, ::Type{String}, n::Integer=8) = rand(rng, b, String, n)
91+
rand( ::Type{String}, n::Integer=8) = rand(GLOBAL_RNG, b, String, n)
92+
end
93+
94+
95+
## BitArray
96+
97+
const BitArrays = Union{BitArray,BitVector,BitMatrix}
98+
99+
rand(r::AbstractRNG, ::Type{T}, dims::Dims) where {T<:BitArrays} =
100+
rand!(r, T(uninitialized, dims))
101+
102+
rand(r::AbstractRNG, ::Type{T}, dims::Integer...) where {T<:BitArrays} =
103+
rand!(r, T(uninitialized, convert(Dims, dims)))
104+
105+
rand(::Type{T}, dims::Dims) where {T<:BitArrays} =
106+
rand!(T(uninitialized, dims))
107+
108+
rand(::Type{T}, dims::Integer...) where {T<:BitArrays} =
109+
rand!(T(uninitialized, convert(Dims, dims)))

test/runtests.jl

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using RandomExtensions
1+
using RandomExtensions, Random, SparseArrays
22
using Test
33

44
@testset "Distributions" begin
@@ -28,3 +28,63 @@ using Test
2828
@test rand(Uniform(1:10)) 1:10
2929
@test rand(Uniform(Int)) isa Int
3030
end
31+
32+
@testset "Containers" for rng in ([], [MersenneTwister(0)], [RandomDevice()])
33+
# Set
34+
for s = (rand(rng..., 1:99, Set{Int}, 10),
35+
rand(rng..., 1:99, Set, 10))
36+
@test s isa Set{Int}
37+
@test length(s) == 10
38+
@test rand(s) 1:99
39+
end
40+
s = Set([1, 2])
41+
@test s === rand!(s)
42+
@test first(s) (1, 2) # extremely unlikely
43+
@test length(s) == 2
44+
@test s === rand!(s, 3:9) <= Set(3:9)
45+
@test length(s) == 2
46+
47+
# Dict
48+
for s = (rand(rng..., Combine(Pair, 1:99, 1:99), Dict, 10),
49+
rand(rng..., Combine(Pair, 1:99, 1:99), Dict{Int,Int}, 10))
50+
@test s isa Dict{Int,Int}
51+
@test length(s) == 10
52+
p = rand(s)
53+
@test p.first 1:99
54+
@test p.second 1:99
55+
end
56+
s = Dict(1=>2, 2=>1)
57+
@test s === rand!(s)
58+
@test length(s) == 2
59+
@test first(s).first (1, 2) # extremely unlikely
60+
rand!(s, Combine(Pair, 3:9, Int))
61+
@test length(s) == 2
62+
@test first(s).first 3:9
63+
64+
# sparse
65+
@test rand(rng..., Float64, .5, 10) isa SparseVector{Float64}
66+
@test rand(rng..., .5, 10) isa SparseVector{Float64}
67+
@test rand(rng..., Int, .5, 10) isa SparseVector{Int}
68+
@test rand(rng..., Float64, .5, 10, 3) isa SparseMatrixCSC{Float64}
69+
@test rand(rng..., .5, 10, 3) isa SparseMatrixCSC{Float64}
70+
@test rand(rng..., Int, .5, 10, 3) isa SparseMatrixCSC{Int}
71+
72+
# BitArray
73+
@test rand(rng..., BitArray, 10) isa BitVector
74+
@test rand(rng..., BitVector, 10) isa BitVector
75+
@test_throws MethodError rand(rng..., BitVector, 10, 20) isa BitVector
76+
@test rand(rng..., BitArray, 10, 3) isa BitMatrix
77+
@test rand(rng..., BitMatrix, 10, 3) isa BitMatrix
78+
@test_throws MethodError rand(rng..., BitVector, 10, 3) isa BitMatrix
79+
80+
# String
81+
s = rand(rng..., String)
82+
@test s isa String
83+
@test length(s) == 8
84+
s = rand(rng..., String, 10)
85+
@test s isa String
86+
@test length(s) == 10
87+
s = rand(rng..., "asd", String)
88+
@test length(s) == 8
89+
@test Set(s) <= Set("asd")
90+
end

0 commit comments

Comments
 (0)