# Custom Sampler with multiple parameters

**URL:** <https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446>\
**Category:** General Usage\
**Tags:** question, random\
**Created:** [November 19, 2023, 10:44pm UTC](https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446 "2023-11-19T22:44:53Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![oliviermilla](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oliviermilla/32/47200_2.png) [@oliviermilla](https://discourse.julialang.org/u/oliviermilla)\
**Post date:** [November 19, 2023, 10:44pm UTC](https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446/1 "2023-11-19T22:44:53Z")

</div>

Hello, with the following structures I’d like to do this:

```julia
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

```julia
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 ❤

---

<div class="post-metadata">

**Author:** ![filchristou](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/filchristou/32/26760_2.png) [@filchristou](https://discourse.julialang.org/u/filchristou)\
**Post date:** [November 20, 2023, 4:53pm UTC](https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446/2 "2023-11-20T16:53:54Z")

</div>

I would probably do something like this

```julia
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
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)

```

---

<div class="post-metadata">

**Author:** ![oliviermilla](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oliviermilla/32/47200_2.png) [@oliviermilla](https://discourse.julialang.org/u/oliviermilla)\
**Post date:** [November 21, 2023, 8:33am UTC](https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446/3 "2023-11-21T08:33:05Z")

</div>

Awesome.

Didn’t think of not using the Sampler interface from the documentation!

Thank you!

---

<div class="post-metadata">

**Author:** ![rfourquet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rfourquet/32/3610_2.png) [@rfourquet](https://discourse.julialang.org/u/rfourquet)\
**Post date:** [November 21, 2023, 9:23am UTC](https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446/4 "2023-11-21T09:23:02Z")

</div>

The [RandomExtension.jl](https://github.com/JuliaRandom/RandomExtensions.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:

```julia
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.

```julia
Random.rand(rng::AbstractRNG, ::Type{A}, b::B, k::Integer) =
    rand(rng, make(A, b), k)

```

---

<div class="post-metadata">

**Author:** ![oliviermilla](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oliviermilla/32/47200_2.png) [@oliviermilla](https://discourse.julialang.org/u/oliviermilla)\
**Post date:** [November 23, 2023, 1:37pm UTC](https://discourse.julialang.org/t/custom-sampler-with-multiple-parameters/106446/5 "2023-11-23T13:37:15Z")

</div>

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.
