Can rand generate random vectors with custom type entries?

I’m trying to understand how to implement rand for custom types. After looking at the documentation, I’ve come up with the following:

using Random

struct A{T}
    x::T
end

Random.rand(rng::AbstractRNG, ::Type{A{T}}) where T = A(rand(rng, T))

This generates single elements as expected:

julia> rand(A{Bool})
A{Bool}(false)

I thought that generating random vectors would work automatically, but it doesn’t:

julia> rand(A{Bool}, 3)
ERROR: MethodError: no method matching Random.Sampler(::Type{TaskLocalRNG}, ::Random.SamplerType{A{Bool}}, ::Val{1})

Closest candidates are:
  Random.Sampler(::Type{<:AbstractRNG}, ::Random.Sampler, ::Union{Val{1}, Val{Inf}})
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:147
  Random.Sampler(::Type{<:AbstractRNG}, ::Any, ::Union{Val{1}, Val{Inf}})
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:183
  Random.Sampler(::Type{<:AbstractRNG}, ::BitSet, ::Union{Val{1}, Val{Inf}})
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/generation.jl:450
  ...

Stacktrace:
 [1] Random.Sampler(T::Type{TaskLocalRNG}, sp::Random.SamplerType{A{Bool}}, r::Val{1})
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:147
 [2] Random.Sampler(rng::TaskLocalRNG, x::Random.SamplerType{A{Bool}}, r::Val{1})
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:139
 [3] rand(rng::TaskLocalRNG, X::Random.SamplerType{A{Bool}})
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:255
 [4] rand!
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:273 [inlined]
 [5] rand!
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:269 [inlined]
 [6] rand
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:290 [inlined]
 [7] rand
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:291 [inlined]
 [8] rand(::Type{A{Bool}}, ::Int64)
   @ Random /usr/local/julia-1.10.0-rc1/share/julia/stdlib/v1.10/Random/src/Random.jl:294
 [9] top-level scope
   @ REPL[5]:1

I find this particularly surprising because the first closest candidate displayed above should match in my opinion because

julia> TaskLocalRNG <: AbstractRNG, Random.SamplerType{A{Bool}} <: Random.Sampler
(true, true)

(The second should also match, but it’s less specific.) Am I missing anything?

(Note that you can always just do [rand(T) for _=1:n].)

Sure. But should rand(A{Bool}, 3) work? (So my question is rather about the API of Random. I’ve clarified the topic title.)

https://docs.julialang.org/en/v1/stdlib/Random/#Hooking-into-the-Random-API

1 Like

It does seem like there should be a default Sampler for SamplerType{T} that simply calls rand(rng, T)

cc @rfourquet

Just to make sure the solution is spelled out here: the one change you need is to replace Type with Random.SamplerType in your method definition.

using Random

struct A{T}
    x::T
end

Random.rand(rng::AbstractRNG, ::Random.SamplerType{A{T}}) where {T} = A(rand(rng, T))
julia> rand(A{Bool})
A{Bool}(false)

julia> rand(A{Bool}, 3)
3-element Vector{A{Bool}}:
 A{Bool}(true)
 A{Bool}(false)
 A{Bool}(false)
5 Likes