# Custom Sampler with multiple parameters

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