Generate next random value based on a given statistical distribution in julia?

mutable struct DistrRng{T<:Sampleable}
	d::T
	rng::MersenneTwister

	function DistrRng(d::T, rng::MersenneTwister) where T <: Sampleable
		return new{T}(d, deepcopy(rng))
	end
	function DistrRng(d::T; seed::Int = nullIndex) where T <: Sampleable
		rng = (seed >= 0 ? MersenneTwister(seed) : MersenneTwister(rand(UInt32)))
		return new{T}(d, rng)
	end
end

global GlobalRngBackup = MersenneTwister(0); 

function copyRng!(dest::MersenneTwister, src::MersenneTwister)
	
	dest.seed = src.seed
	dest.state = src.state
	dest.vals = src.vals
	dest.ints = src.ints
	dest.idxF = src.idxF
	dest.idxI = src.idxI
	
end


function Base.rand(distrRng::DistrRng, n::Int)
	rng = distrRng.rng # shorthand
	copyRng!(GlobalRngBackup, GLOBAL_RNG)
	copyRng!(GLOBAL_RNG, rng)
	value = rand(distrRng.d, n)
	copyRng!(rng, GLOBAL_RNG)
	copyRng!(GLOBAL_RNG, GlobalRngBackup) 
	return value
end
function Base.rand(distrRng::DistrRng)
	return rand(distrRng, 1)[1]
end

The above is a very interesting piece of code to randomly generate a number following a given probability distribution sampler and random number generator, with the help of rewriting the default Base.rand functions. I only have a few confusing points as the following.
1: In the first inner constructor inside of the mutable struct DistrRng, deepcopy is used. Then, in the function copyRng!, apparently, a simple reassignment is used. I was wondering, why is this?
2: Is there a better way to accomplish this, besides rewriting this Base.rand() function?

You should probably ask the author of the algorithm.

But this code looks weird, with all that copying back and forth. It is unclear why that is needed, or what it is supposed to accomplish. It is generally best to just provide rngs to functions that use randomness explicitly, with the understanding that they may be mutated in an unpredictable manner, and not rely on the specifics.

1 Like

@Tamas_Papp, thanks for replying. Here is my understanding. The key function, based on my understanding, is the Base.rand(distrRng::DistrRng, n::Int) function. The intended goal is to generate next random value using the d attribute (which is for the statistical distribution) of the defined DistrRng type, and the random number generator, which is rng attribute of the defined DistrRng type. The author is trying to use the provided rand function in the Base module/library. Since Base.rand(::AbstractRNG, ::Distribution) does not exist for all distributions, the author uses the provided Base.rand(::Distribution), indeed, it’s rand(distrRng.d,n) inside of the Base.rand(distrRng::DistrRng, n::Int) to generate the next random number with distribution in distrRng, and with RNG in “GLOBAL_RNG”. That’s why the copying back and forth, before and after calling the rand(distrRng.d,n) function.

FWIW, I am still not getting what is going on here, but as I said, you should probably talk to the author.

Also, it may be better to just formulate a question about what you want to do, instead of trying to figure out what someone’s code does. Doing that without context is particularly difficult.

If you are trying to implement another RNG, there is some documentation, even though the API is not finalized yet. In particular, an RNG should define a few particular rand methods for bits types, the rest is optimization.

1 Like