Reputation: 303
I am currently attempting to run my code in parallel. Part of my code (genetic algorithm) relies on the selection of random numbers. Therefore, I need to make sure that each thread used, have their own random number generator. I must also be able to replicate the results. Therefore, I have "borrowed" a function, which has the ability to set a random seed or create specific seeds for each thread.
In order to test if this was working correctly, I wanted to set the seeds equal for each thread and test to see if the same random numbers were generated. However, this was not the case!
I believe that I am missing something fundamental about the generation of the numbers. When creating the random number generators, I have used the same UInt32
. I believe that they should therefore draw the same numbers, however the randjump
may interfere with this. Perhaps the random number generators do not "jump" forward to the same point/state to ensure that each is far enough from each other in the cycle. Perhaps they share a state when doing it this way? Is there a way to control if the implementation is accurate? Perhaps there is a bigger error at play here?
clearconsole()
using Test
using Random
using Future
using Test
function set_rngs(random_seed)
if random_seed == true
rng_seed = Random.make_seed()
rng = MersenneTwister(rng_seed)
return Future.randjump(rng, Threads.nthreads());
else
rng_seed = UInt32[0xbd8e7dc4,0xbd8e7dc4];
rng = Random.MersenneTwister(rng_seed)
return Future.randjump(rng, Threads.nthreads());
end
end
function random_test()
tmp = Vector{Float64}(undef, 100)
Threads.@threads for i in 1:100
tmp[i] = rand()
end
return tmp
end
Random.seed!(set_rngs(false))
tester = random_test()
function testing_rand(tester::Vector{Float64})
@testset "Testing if random numbers drawn by each processer are different" begin
for i in 1:50
@test tester[i] != tester[i+50]
end
end
end
testing_rand(tester)
Upvotes: 1
Views: 524
Reputation: 42234
You basically do not need to use randjump
because the MersenneTwister
streams are not correlated.
Hence, the standard way to initiate the random streams for threads is the following:
using Random
const mts = MersenneTwister.(1:Threads.nthreads())
When writing a code you always access an approppiate random state via:
rand(mts[Threads.threadid()])
On some rare occasion you might indeed want to have different parts of the same random stream and need to perform randjump
.
const mts2 = [Future.randjump(MersenneTwister(0), i) for i in 1:Threads.nthreads()]
The access pattern to the object is the same as above. Note that in each case you need to hold a separate random state for each thread. This approach is also recommended for performance reasons (rather than several threads competing for a single random state).
Upvotes: 2