diff --git a/NEWS.md b/NEWS.md index 775e486421b2b..a53f4ba64f35a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -52,6 +52,7 @@ Standard library changes * `Base.tail` now works on named tuples ([#29595]). * `randperm` and `randcycle` now use the type of their argument to determine the element type of the returned array ([#29670]). + * A new method `rand(::Tuple)` implements sampling from the values of a tuple ([#25278]). Compiler/Runtime improvements ----------------------------- diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index 1ac74ad5e64fd..54d01ceeddf90 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -217,6 +217,8 @@ rand(rng::AbstractRNG, ::UniformT{T}) where {T} = rand(rng, T) #### scalars rand(rng::AbstractRNG, X) = rand(rng, Sampler(rng, X, Val(1))) +# this is needed to disambiguate +rand(rng::AbstractRNG, X::Dims) = rand(rng, Sampler(rng, X, Val(1))) rand(rng::AbstractRNG=GLOBAL_RNG, ::Type{X}=Float64) where {X} = rand(rng, Sampler(rng, X, Val(1))) rand(X) = rand(GLOBAL_RNG, X) @@ -249,9 +251,6 @@ rand( X, d::Integer, dims::Integer...) = rand(X, Dims((d, dims... # rand(r, ()) would match both this method and rand(r, dims::Dims) # moreover, a call like rand(r, NotImplementedType()) would be an infinite loop -# this is needed to disambiguate -rand(r::AbstractRNG, dims::Dims) = error("rand(rng, dims) is discontinued; try rand(rng, Float64, dims)") - rand(r::AbstractRNG, ::Type{X}, dims::Dims) where {X} = rand!(r, Array{X}(undef, dims), X) rand( ::Type{X}, dims::Dims) where {X} = rand(GLOBAL_RNG, X, dims) @@ -283,7 +282,7 @@ include("misc.jl") Pick a random element or array of random elements from the set of values specified by `S`; `S` can be -* an indexable collection (for example `1:n` or `['x','y','z']`), +* an indexable collection (for example `1:9` or `('x', "y", :z)`), * an `AbstractDict` or `AbstractSet` object, * a string (considered as a collection of characters), or * a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for diff --git a/stdlib/Random/src/generation.jl b/stdlib/Random/src/generation.jl index 7451de367e89b..f85447715794b 100644 --- a/stdlib/Random/src/generation.jl +++ b/stdlib/Random/src/generation.jl @@ -430,3 +430,54 @@ function rand(rng::AbstractRNG, sp::SamplerSimple{<:AbstractString,<:Sampler}):: isvalid_unsafe(str, pos) && return str[pos] end end + + +## random elements from tuples + +### 1 + +Sampler(::Type{<:AbstractRNG}, t::Tuple{A}, ::Repetition) where {A} = + SamplerTrivial(t) + +rand(rng::AbstractRNG, sp::SamplerTrivial{Tuple{A}}) where {A} = + @inbounds return sp[][1] + +### 2 + +Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B}, n::Repetition) where {A,B} = + SamplerSimple(t, Sampler(RNG, Bool, n)) + +rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B}}) where {A,B} = + @inbounds return sp[][1 + rand(rng, sp.data)] + +### 3 + +Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B,C}, n::Repetition) where {A,B,C} = + SamplerSimple(t, Sampler(RNG, UInt52(), n)) + +function rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B,C}}) where {A,B,C} + local r + while true + r = rand(rng, sp.data) + r != 0x000fffffffffffff && break # _very_ likely + end + @inbounds return sp[][1 + r ÷ 0x0005555555555555] +end + +### 4 + +Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B,C,D}, n::Repetition) where {A,B,C,D} = + SamplerSimple(t, Sampler(RNG, UInt52Raw(Int), n)) + +function rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B,C,D}}) where {A,B,C,D} + r = rand(rng, sp.data) & 3 + @inbounds return sp[][1 + r] +end + +### n + +Sampler(RNG::Type{<:AbstractRNG}, t::Tuple, n::Repetition) = + SamplerSimple(t, Sampler(RNG, Base.OneTo(length(t)), n)) + +rand(rng::AbstractRNG, sp::SamplerSimple{<:Tuple}) = + @inbounds return sp[][rand(rng, sp.data)] diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index 56318d3e2c162..c1d68910289f2 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -702,3 +702,14 @@ end a = rand(Int, 1) @test shuffle(a) == a end + +@testset "rand(::Tuple)" begin + for x in (0x1, 1) + @test rand((x,)) == 0x1 + @test rand((x, 2)) ∈ 1:2 + @test rand((x, 2, 3)) ∈ 1:3 + @test rand((x, 2, 3, 4)) ∈ 1:4 + @test rand((x, 2, 3, 4, 5)) ∈ 1:5 + @test rand((x, 2, 3, 4, 6)) ∈ 1:6 + end +end diff --git a/stdlib/SparseArrays/src/linalg.jl b/stdlib/SparseArrays/src/linalg.jl index ee3ca211e5f67..4350a2d922fc9 100644 --- a/stdlib/SparseArrays/src/linalg.jl +++ b/stdlib/SparseArrays/src/linalg.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license import LinearAlgebra: checksquare +using Random: rand! ## sparse matrix multiplication @@ -929,12 +930,6 @@ function opnormestinv(A::SparseMatrixCSC{T}, t::Integer = min(2,maximum(size(A)) S = zeros(T <: Real ? Int : Ti, n, t) - function _rand_pm1!(v) - for i in eachindex(v) - v[i] = rand()<0.5 ? 1 : -1 - end - end - function _any_abs_eq(v,n::Int) for vv in v if abs(vv)==n @@ -949,7 +944,7 @@ function opnormestinv(A::SparseMatrixCSC{T}, t::Integer = min(2,maximum(size(A)) X[1:n,1] .= 1 for j = 2:t while true - _rand_pm1!(view(X,1:n,j)) + rand!(view(X,1:n,j), (-1, 1)) yaux = X[1:n,j]' * X[1:n,1:j-1] if !_any_abs_eq(yaux,n) break @@ -1010,7 +1005,7 @@ function opnormestinv(A::SparseMatrixCSC{T}, t::Integer = min(2,maximum(size(A)) end end if repeated - _rand_pm1!(view(S,1:n,j)) + rand!(view(S,1:n,j), (-1, 1)) else break end