Counting number of times a RNG is called

Yes, but the example I gave does just that, with a catch-all method. The problem is that you will get ambiguities in some cases, it would be great to figure a way to have less of those in Random.

In the example I gave yes, at least when wrapping MersenneTwister (and most likely for all currently existing generators, unless they specialize generation of UInt52). You can’t predict how a function will use an RNG. In the case of randn, only by looking at the source code can you see that it uses rand(UInt52()) and decide to count that the same as if it was generating a Float64. Another routine could generate an Int and treat it as a Float64. Ultimately, an RNG just produces random bits which are put together into a value of a certain type. Another way to solve the problem is to count the number of generated bits, at the lower level of the generator. But then you don’t discriminate between e.g. integers and floats.

1 Like

Thanks. How would I count the number of generated bits as an alternative to defining all these rand methods? I think this approach would actually suit my use case better because I am generating different types of random numbers across different estimators. E.g. some estimators require a random integer. If generating different types uses different numbers of bits then it would be better to measure the number of generated bits to have a standardized measurement across estimators.

As for the original approach, I hit another ambiguous method error when using sample(rng, 1:N, m, replace=false) from StatsBase.jl.

MethodError: Random.Sampler(::Type{EVSI.CountingRNG{MersenneTwister}}, ::UnitRange{Int64}, ::Val{1}) is ambiguous. Candidates:
(::Type{Random.Sampler})(::Type{EVSI.CountingRNG{T}}, X, n::Union{Val{1}, Val{Inf}}) where T in EVSI at /Users/michaelfairley/Git-Projects/EVSI/src/counting_rng.jl:9
(::Type{Random.Sampler})(::Type{#s623} where #s623<:AbstractRNG, r::AbstractUnitRange{T}, ::Union{Val{1}, Val{Inf}}) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Random at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/Random/src/generation.jl:278
Possible fix, define
Random.Sampler(::Type{EVSI.CountingRNG{T}}, ::AbstractUnitRange{T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}}, ::Union{Val{1}, Val{Inf}})

I tried defining the following but it didn’t fix the error.

Sampler(::Type{CountingRNG{T}}, ::Type{X}, n::Repetition) where {T,X<:AbstractUnitRange{Integer}} = Sampler(T, X, n)

Counting the number of bits is again tricky: at the lowest level you can use the following ugly solution. It will count the total number of bits generated from the dSFMT library. But MersenneTwister “wastes” some bits, so the count will be overestimated. I think for your pupose, your best bet is to write a countingRNG wrapper like discussed above but without a catch-all method (i.e. you write the rand methods for everything that you want to count).
For your new ambiguous method, you didn’t implement exactly the one suggested by the error message, by using Integer instead of the Union of specific integers.

# counting all the bits generated from dSFMT
const count = Ref(0)

function Random.fill_array!(s::Random.DSFMT_state, A::Ptr{Float64}, n::Int, ::Random.CloseOpen01_64)
    count[] += n
    Random.dsfmt_fill_array_close_open!(s, A, n)
end

function Random.fill_array!(s::Random.DSFMT_state, A::Ptr{Float64}, n::Int, ::Random.CloseOpen12_64)
    count[] += n
    Random.dsfmt_fill_array_close1_open2!(s, A, n)
end


function Random.gen_rand(r::MersenneTwister)
    GC.@preserve r Random.fill_array!(r.state, pointer(r.vals), length(r.vals), Random.CloseOpen12())
    Random.mt_setfull!(r)
end

getcount(r::MersenneTwister) = 52*(count[] - Random.MT_CACHE_F + r.idxF) - 8*r.idxI

I have a PR which add counters to MersenneTwister, so this may become easier in the fututre.

2 Likes