Hello, with the following structures I’d like to do this:
struct A
a::Float64
b::Float64
end
struct B
x::Float64
end
b = B(32.5)
res = rand(A, b, 3) # Vector of 3 A
with the computation of the random 3 A
factoring in the parameter b
.
Is there any way to do this using a custom Sampler
without having to write a wrapper
struct AB
a::A
b::B
end
Random.Sampler(RNG::Type{<:AbstractRNG}, ab::AB, r::Random.Repetition) = #code returning A
That would be neat as I don’t want to write a wrapper for every type that I’d like to generate that uses B parameters!
Thank you
I would probably do something like this
using Random
Random.rand(T::Type, b::B, k) = Random.rand(Random.default_rng(), T, b, k)
function Random.rand(rng::AbstractRNG, T::Type, b::B, k)
# random operations
a = rand(rng) * b.x + k
T(a, k)
end
Modify the code inside the function as you wish, e.g. to get your vector.
Then
julia> rand(A, B(3), 5) # uses default random generator
A(5.48452407660821, 5.0)
julia> rand(A, B(3), 5)
A(5.93748059591882, 5.0)
julia> rand(MersenneTwister(0), A, B(3), 5) # custom
A(7.470942523932237, 5.0)
julia> rand(MersenneTwister(0), A, B(3), 5) # same result
A(7.470942523932237, 5.0)
1 Like
Awesome.
Didn’t think of not using the Sampler interface from the documentation!
Thank you!
1 Like
The RandomExtension.jl is specifically designed for this use-case. One limitation of the Random
module is that it’s designed to accept only one object to specify the sampling space. As you mentioned in the OP, the obvious way to specify multiple parameters is to define a struct wrapping these parameters (like e.g. Normal
from the Distribution
package), but it can quickly become tedious in some situations.
In RandomExtensions
, there is one generic such struct, called Make
(think of it as a Tuple
), and you construct these objects by calling the helper function make
. Your problem could be solved for example as follows:
struct A
a::Float64
b::Float64
end
struct B
x::Float64
end
b = B(32.5)
using Random
using RandomExtensions: make, Make
# use the Sampler interface from Random:
Random.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{<:Make{<:A}}) =
A(rand(rng), sp[][1].x)
# now call `rand` via `make`:
julia> rand(make(A, b))
A(0.935066169840552, 32.5)
julia> rand(Xoshiro(0), make(A, b), 3)
3-element Vector{A}:
A(0.4056994708920292, 32.5)
A(0.06854582438651502, 32.5)
A(0.8621408571954849, 32.5)
Of course, you can also extend rand
to accept directly more parameters, but this would be “sugar”; the make approach is more composable. E.g.
Random.rand(rng::AbstractRNG, ::Type{A}, b::B, k::Integer) =
rand(rng, make(A, b), k)
1 Like
Bonjour, @rfourquet! This is so awesome.
The extension looks only natural to get merged into the base Random API!
Thank you! This could be marked as a solution to my issue.
1 Like