# Counting number of times a RNG is called

**URL:** <https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903>\
**Category:** General Usage\
**Created:** [June 4, 2019, 1:40am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903 "2019-06-04T01:40:36Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![MFairley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mfairley/32/8599_2.png) [@MFairley](https://discourse.julialang.org/u/MFairley)\
**Post date:** [June 4, 2019, 1:40am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/1 "2019-06-04T01:40:36Z")

</div>

Hi,

I’m trying to measure the number of random numbers generated by a Monte Carlo estimator for a given level of precision. Is there an easy way I could do this?

I would like to do something like:

```julia
rng = MersenneTwister(seed)
estimator(rng) # generates many random numbers
rng.count # returns how many random numbers were generated since rng was created

```

Best,  
Michael

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [June 4, 2019, 2:56am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/2 "2019-06-04T02:56:31Z")

</div>

Unfortunately, I found nothing in [the manual](https://docs.julialang.org/en/v1/stdlib/Random/index.html).

If you know which methods your estimator make use, and it does not restrict the type of its argument, I suppose you could create a new type wrapping MersenneTwister and define methods for it that just pass the arguments to MersenneTwister and increment a global (or a captured reference) counter by the number of generations made. However, this is a terrible solution (you can forget some method used for generating random numbers and count less, it depends in many assumptions, it takes considerable effort, etc…).

The other almost as bad solution is to look at the [MersenneTwister source](https://github.com/JuliaLang/julia/blob/55e36cc308b66d3472990a06b2797f9f9154ea0a/stdlib/Random/src/RNGs.jl#L58-L64) and check if one of the fields (probably idxF, or idxI, or some combination of them) gives what you want.

---

<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:** [June 4, 2019, 8:26am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/3 "2019-06-04T08:26:38Z")

</div>

There is no direct way to do that, and more information is needed to find a solution. For example if you call only the scalar `rand` for `Float64`, you could overwrite the `Random.set_mt_setfull!(r::MersenneTwister)` method (which is called only when re-filling the cache of `Float64` in a MT object) to also update a global counter, and at the end of your simulation multiply this counter with the size of the cache (plus adjusting with `r.idxF`).  
But if you use also a `rand` method which produces arrays, it’s quite difficult to do what you want. You may need to overwrite the `rand` methods themselves (to update a counter), or create a wrapper of `MersenneTwister` which has a counter as a field (the cleanest solution).

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 4, 2019, 11:18am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/4 "2019-06-04T11:18:33Z")

</div>

> [@rfourquet](#):
>
> create a wrapper of `MersenneTwister` which has a counter as a field (the cleanest solution).

This is the best solution IMO.

This would also allow making various details about the counting more precise, eg does `rand(Float64, 10)` count as 1 or 10 calls?

---

<div class="post-metadata">

**Author:** ![MFairley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mfairley/32/8599_2.png) [@MFairley](https://discourse.julialang.org/u/MFairley)\
**Post date:** [June 4, 2019, 4:24pm UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/5 "2019-06-04T16:24:15Z")

</div>

Thanks so much everyone for your suggestions. I would count rand(Float64, 10) as 10 calls in my case. I am passing rng to various samplers in the Distributions.jl package and I would ideally not want to look through the Distributions.jl source code to see how many times rng is used or what rand methods are used.

What would be the skeleton code for creating a wrapper of MersenneTwister so that it works with any methods that currently take a MersenneTwister rng?

---

<div class="post-metadata">

**Author:** ![carstenbauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/carstenbauer/32/4981_2.png) [@carstenbauer](https://discourse.julialang.org/u/carstenbauer)\
**Post date:** [June 4, 2019, 5:18pm UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/6 "2019-06-04T17:18:55Z")

</div>

For example, something along the lines of

```julia
mutable struct TweakedMersenneTwister
    count::Int
    rng::MersenneTwister
end

function Base.rand(t::TweakedMersenneTwister, i::Integer)
    t.count += i
    return rand(t.rng, i)
end

```

which gives

```julia
julia> rng = TweakedMersenneTwister(0, MersenneTwister());

julia> rng.count
0

julia> rand(rng, 3)
3-element Array{Float64,1}:
 0.6887358336000433
 0.2909818216770961
 0.23089019888929863

julia> rand(rng, 4)
4-element Array{Float64,1}:
 0.8570114504250239
 0.9896625712375964
 0.0658943236725813
 0.33129437214959623

julia> rng.count
7

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 5, 2019, 3:51am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/7 "2019-06-05T03:51:25Z")

</div>

Also, unless there is a specific reason to restrict to `MersenneTwister`, something like

```julia
mutable struct CountingRNG{T <: AbstractRNG}
    count::Int
    rng::T
end

```

should be more generic.

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [June 5, 2019, 5:33am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/8 "2019-06-05T05:33:49Z")

</div>

For fun, here’s a version that uses Cassette to count the number of calls to a particular method of `rand`

```julia
using Cassette, Random
import Cassette: overdub, recurse, @overdub
Cassette.@context CountCtx;

ctx = CountCtx(metadata=Ref(0))
const myrng = Random.GLOBAL_RNG # Change this for your RNG

"This method will be called at the very bottom"
function overdub(ctx::CountCtx, f::typeof(rand), rng::AbstractRNG, i::Random.UInt52)
    rng == myrng && (ctx.metadata[] += 1)
    recurse(ctx, f, rng, i)
end

overdub(ctx, randn, myrng)
julia> ctx.metadata[]
1

julia> overdub(ctx, randn, myrng, 5)

julia> ctx.metadata[]
6

```

This method just counts the number of times `rand` is invoked with a `UInt52` as the second argument, since this is the method that is called at the very bottom to produce a random number.

This can be used in a nicer way like this

```julia
function testfun(N)
    for n = 1:N
        randn(myrng, n)
    end
end

julia> @overdub ctx testfun(5)

julia> ctx.metadata[]
21

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 5, 2019, 5:54am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/9 "2019-06-05T05:54:55Z")

</div>

> [@baggepinnen](#):
>
> counts the number of times `rand` is invoked with a `UInt52` as the second argument, since this is the method that is called at the very bottom to produce a random number

For the MT, yes; in general, it need not be.

---

<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:** [June 5, 2019, 8:21am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/10 "2019-06-05T08:21:16Z")

</div>

Here is a robust solution:

```julia
using Random
import Random: Sampler, Repetition, rand, rand!, CloseOpen01, SamplerSimple

mutable struct CountingRNG{T<:AbstractRNG} <: AbstractRNG
    count::Int
    rng::T
end

Sampler(::Type{CountingRNG{T}}, X, n::Repetition) where {T} = Sampler(T, X, n)
# disambiguate
Sampler(::Type{CountingRNG{T}}, ::Type{X}, n::Repetition) where {T,X} = Sampler(T, X, n)
Sampler(::Type{CountingRNG{T}}, ::Type{X}, n::Repetition) where {T,X<:AbstractFloat} = Sampler(T, X, n)

# intercept Float64 generation
Sampler(::Type{CountingRNG{T}}, ::Type{Float64}, n::Repetition) where {T} =
    SamplerSimple(CloseOpen01(Float64), Sampler(T, Float64, n))

rand(rng::CountingRNG, sp::Sampler) = rand(rng.rng, sp)
# this is to benefit from possible optimizations implemented for rng.rng,
# instead of using the default rand! (simple loop)
rand!(rng::CountingRNG, A::AbstractArray, sp::Sampler) = rand!(rng.rng, A, sp)

function rand(rng::CountingRNG, sp::SamplerSimple{CloseOpen01{Float64}})
    rng.count += 1
    rand(rng.rng, sp.data)
end

function rand!(rng::CountingRNG, A::AbstractArray{Float64}, sp::SamplerSimple{CloseOpen01{Float64}})
    rng.count += length(A)
    rand!(rng.rng, A, sp.data)
end

```

I didn’t test much, so there may be more method ambiguities to resolve.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 5, 2019, 9:49am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/11 "2019-06-05T09:49:49Z")

</div>

Nice. I am wondering if we could make this simpler when this part of the API is finalized (my understanding is that it is unofficial at the moment).

---

<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:** [June 5, 2019, 10:49am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/12 "2019-06-05T10:49:25Z")

</div>

I don’t see what is unofficial at the moment (except maybe that `typeof(SamplerSimple(x, data) <: SamplerSimple{typeof(x)}`), but I agree that reading the docs once may not be enough to come up with this solution.  
What could be simplified is deleting the `rand!` methods, but you may loose a bit of performance.

What I’m annoyed with comes from the ambiguities, which forces here to write redundant methods. For the next major version of `Random` (which can be breaking), let’s see how this can be improved (also, check if `CloseOpen01` can be made un-official, in favor of `Float64`, to simplify things for the user).

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 5, 2019, 10:59am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/13 "2019-06-05T10:59:23Z")

</div>

> [@rfourquet](#):
>
> I don’t see what is unofficial at the moment

I meant

> The API is not clearly defined yet

[in the docs](https://docs.julialang.org/en/v1.3-dev/stdlib/Random/#Creating-new-generators-1).

---

<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:** [June 5, 2019, 11:06am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/14 "2019-06-05T11:06:01Z")

</div>

Ah OK, so the docs are maybe a bit defensive to stay on the safe side. What they mean basically is that there is no official API allowing you to define `rand` on your custom RNG for only one type or two (e.g. `UInt64`) and get the definition for other types for free. But what is in my post above is officially supported (`Sampler` and `rand` are defined for all the types), and the docs should be updated to reflect that.

---

<div class="post-metadata">

**Author:** ![MFairley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mfairley/32/8599_2.png) [@MFairley](https://discourse.julialang.org/u/MFairley)\
**Post date:** [June 5, 2019, 8:08pm UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/15 "2019-06-05T20:08:29Z")

</div>

Thanks so much for the code! I tried @rfourquet robust version but there is still some ambiguity when running `randn(rng)`

ERROR: MethodError: rand(::CountingRNG{MersenneTwister}, ::Random.SamplerTrivial{Random.UInt52{UInt64},UInt64}) is ambiguous. Candidates:  
rand(r::AbstractRNG, ::Random.SamplerTrivial{Random.UInt52{UInt64},E} where E) in Random at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/generation.jl:119  
rand(r::AbstractRNG, sp::Random.SamplerTrivial{#s623,E} where E where #s623\<:Random.UniformBits{T}) where T in Random at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/generation.jl:122  
rand(rng::CountingRNG, sp::Sampler) in Main at /Users/michaelfairley/Git-Projects/EVSI/src/counting\_rng.jl:18  
Possible fix, define  
rand(::CountingRNG, ::Random.SamplerTrivial{Random.UInt52{UInt64},E} where E)  
Stacktrace:  
[1] rand(::CountingRNG{MersenneTwister}, ::Random.UInt52{UInt64}) at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/Random.jl:219  
[2] randn at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/normal.jl:38 [inlined]  
[3] randn(::CountingRNG{MersenneTwister}, ::Type{Float64}) at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/normal.jl:165  
[4] randn! at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/normal.jl:171 [inlined]  
[5] randn at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/normal.jl:182 [inlined]  
[6] randn(::CountingRNG{MersenneTwister}, ::Int64) at /Users/julia/buildbot/worker/package\_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/normal.jl:186  
[7] top-level scope at none:0

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [June 6, 2019, 12:58am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/16 "2019-06-06T00:58:19Z")

</div>

Did you remember to initialize `rng`?

```julia
julia> rng = CountingRNG(0, MersenneTwister(1234));

julia> rand(rng)
0.5908446386657102

julia> rand(rng)
0.7667970365022592

julia> rng.count
2

```

```julia

```

---

<div class="post-metadata">

**Author:** ![MFairley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mfairley/32/8599_2.png) [@MFairley](https://discourse.julialang.org/u/MFairley)\
**Post date:** [June 6, 2019, 1:27am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/17 "2019-06-06T01:27:58Z")

</div>

Yes, the error is with `rand **n** `. Here’s all the input from a fresh REPL.

```julia
julia> using Random

julia> import Random: Sampler, Repetition, rand, rand!, CloseOpen01, SamplerSimple

julia> mutable struct CountingRNG{T<:AbstractRNG} <: AbstractRNG
           count::Int
           rng::T
       end

julia> Sampler(::Type{CountingRNG{T}}, X, n::Repetition) where {T} = Sampler(T, X, n)
Sampler

julia> # disambiguate
       Sampler(::Type{CountingRNG{T}}, ::Type{X}, n::Repetition) where {T,X} = Sampler(T, X, n)
Sampler

julia> Sampler(::Type{CountingRNG{T}}, ::Type{X}, n::Repetition) where {T,X<:AbstractFloat} = Sampler(T, X, n)
Sampler

julia> # intercept Float64 generation
       Sampler(::Type{CountingRNG{T}}, ::Type{Float64}, n::Repetition) where {T} =
           SamplerSimple(CloseOpen01(Float64), Sampler(T, Float64, n))
Sampler

julia> rand(rng::CountingRNG, sp::Sampler) = rand(rng.rng, sp)
rand (generic function with 62 methods)

julia> # this is to benefit from possible optimizations implemented for rng.rng,
       # instead of using the default rand! (simple loop)
       rand!(rng::CountingRNG, A::AbstractArray, sp::Sampler) = rand!(rng.rng, A, sp)
rand! (generic function with 48 methods)

julia> function rand(rng::CountingRNG, sp::SamplerSimple{CloseOpen01{Float64}})
           rng.count += 1
           rand(rng.rng, sp.data)
       end
rand (generic function with 63 methods)

julia> function rand!(rng::CountingRNG, A::AbstractArray{Float64}, sp::SamplerSimple{CloseOpen01{Float64}})
           rng.count += length(A)
           rand!(rng.rng, A, sp.data)
       end
rand! (generic function with 49 methods)

julia> rng = CountingRNG(0, MersenneTwister(1234));

julia> randn(rng)
ERROR: MethodError: rand(::CountingRNG{MersenneTwister}, ::Random.SamplerTrivial{Random.UInt52{UInt64},UInt64}) is ambiguous. Candidates:
  rand(r::AbstractRNG, ::Random.SamplerTrivial{Random.UInt52{UInt64},E} where E) in Random at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/generation.jl:119
  rand(r::AbstractRNG, sp::Random.SamplerTrivial{#s623,E} where E where #s623<:Random.UniformBits{T}) where T in Random at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/generation.jl:122
  rand(rng::CountingRNG, sp::Sampler) in Main at REPL[8]:1
Possible fix, define
  rand(::CountingRNG, ::Random.SamplerTrivial{Random.UInt52{UInt64},E} where E)
Stacktrace:
 [1] rand(::CountingRNG{MersenneTwister}, ::Random.UInt52{UInt64}) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/Random.jl:219
 [2] randn(::CountingRNG{MersenneTwister}) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/normal.jl:38
 [3] top-level scope at none:0

julia>

```

---

<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:** [June 6, 2019, 9:06am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/18 "2019-06-06T09:06:18Z")

</div>

You can define the method suggested in the error message:

```julia
function rand(rng::CountingRNG, sp::Random.SamplerTrivial{Random.UInt52{UInt64}})
    # rng.count += 1
    rand(rng.rng, sp)
end

```

You have to decide what exactly you want to count: does `randn(rng)` should count as 1, or as whatever number of calls to `rand(rng)` this call consumes? Uncomment the line `rng.count += 1` above for the latter (`rand(UInt52())` actually doesn’t consume a `Float64`, but still the same number of bits as a `Float64`).

---

<div class="post-metadata">

**Author:** ![MFairley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mfairley/32/8599_2.png) [@MFairley](https://discourse.julialang.org/u/MFairley)\
**Post date:** [June 6, 2019, 11:47pm UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/19 "2019-06-06T23:47:47Z")

</div>

Thanks! I count calls to `rand(rng)` that `randn(rng)` consumes. I don’t think I fully understand how the `Random` API works but it seems like I need to redefine all the `Sampler` and `rand` methods to work with CountingRNG. Is this approach robust in that if I forget to define a method then the call will produce an error and I won’t end up silently not counting some random number generation?

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [June 7, 2019, 5:32am UTC](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903/20 "2019-06-07T05:32:34Z")

</div>

If you are only ever going to use the MersenneTwister, why not consider the Cassette approach? It did not require you to implement much, only find the lowest method that generates an actual random number and define a contextual dispatch for this. Cassette itself does not have any dependencies so it’s a rather lightweight solution.

[Next page](https://discourse.julialang.org/t/counting-number-of-times-a-rng-is-called/24903.md?page=2)
