jmejia8 / Metaheuristics.jl

High-performance metaheuristics for optimization coded purely in Julia.
https://jmejia8.github.io/Metaheuristics.jl/stable/
Other
253 stars 27 forks source link

Feature request: allow passing a random generator or create a fresh generator for `optimize` #111

Open RoyCCWang opened 1 month ago

RoyCCWang commented 1 month ago

Thanks for making this package!

There is a Metaheuristics.Option with keyword seed, but is there a way to pass in the random number generator to every call of random number generation in this package? For example:

using Random, LinearAlgebra
rng1 = Random.Xoshiro(0)
rng2 = Random.Xoshiro(0)

x = randn(rng1, 3)
x_next = randn(rng1, 3)

y = randn(rng2, 3)
@show norm(x-y)
@show norm(x_next - randn(rng2,3))

gets a print out of zeros.

The option for users to supply the random number generator is important for reproducible results in a multi-threaded setting, where the random number generators might be subjected to race conditions because every call to random number generation methods actually mutates the generator variable, rng. There are complicated strategies to get consistent results with a single random number generator shared across threads, i.e., this JuliaCon 2020 talk, but I think a good alternative is to just let users pass in one dedicated random number generators for every optimize call, or (*) generate a new random number generator at a certain seed value for every optimize call.

A possible solution is to add an additional method that takes in as its first parameter the random number generator, to the existing optimize method. The standard library's rand and randn! all have an additional method that does it this way, as does TrainState method from Lux.jl. With this potential solution, you'll need to handle the case when the user supplies both a random number generator, rng1, to optimize and a seed value, s, in Options. One solution for this case is to create a new random number generator of the same type as rng1, with the seed value, i.e. implenting (*). This can be done simply e.g.:

using Random, LinearAlgebra
rng1 = Random.Xoshiro(0)
rng2 = Random.Xoshiro(25)

s = 25
rng3 = typeof(rng1)(s) # creates new random number generator with seed value s

@show norm(rand(rng2, 3) - rand(rng3, 3))

gets a print out of zero.

You can also deepcopy a random number generator.