Main Content

Repeat Random Numbers in parfor-Loops

This example shows how to control random number generation in parfor-loops by assigning a particular substream for each iteration.

As described in Control Random Number Streams on Workers, each worker in a cluster working on the same job has an independent random number generator stream. By default, each worker in a pool, and each iteration in a parfor-loop has a unique, independent set of random numbers. Subsequent runs of the parfor-loop generate different numbers.

In a parfor-loop, you cannot control what sequence the iterations execute in, nor can you control which worker runs which iterations. So even if you reset the random number generators, the parfor-loop can generate the same values in a different sequence.

To reproduce the same set of random numbers in a parfor-loop each time the loop runs, you must control random number generation by assigning a particular substream for each iteration.

Create the stream you want to use, using a generator that supports substreams. Creating the stream as a parallel.pool.Constant allows all workers to access the stream. For a list of generators that support substreams, see Choosing a Random Number Generator.

sc = parallel.pool.Constant(RandStream("Threefry"))
sc = 
 Constant with properties: 

                Value: [1x1 RandStream]

Inside the parfor-loop, you can set the substream index based on the loop index. This ensures that each iteration uses a distinct and reproducible set of random numbers, regardless of which worker executes the iteration or the order in which iterations run.

There are two ways to use the stream to generate random numbers on the workers.

You can pass the modified stream as the first input argument to functions such as rand, randn, randi, and randperm. This method ensures that each function call uses the correct substream.

r1 = zeros(1,10);
parfor idx = 1:10
    % Extract the stream from the Constant
    stream = sc.Value;
    stream.Substream = idx;
    r1(idx) = rand(stream,1);
end
disp(r1)
    0.3640    0.8645    0.0440    0.7564    0.5323    0.8075    0.2145    0.9128    0.4057    0.0581

You can also set the global random number stream on the worker to the modified stream. This approach allows all subsequent random number generation calls to use the assigned stream automatically.

r2 = zeros(1,10);
parfor idx = 1:10
    % Extract the stream from the Constant
    stream = sc.Value;        
    stream.Substream = idx;

    % Save previous global stream
    oldGlobalStream = RandStream.setGlobalStream(stream);
    r2(idx) = rand(1);

    % Restore the previous global stream
    RandStream.setGlobalStream(oldGlobalStream);
end
disp(r2)
    0.3640    0.8645    0.0440    0.7564    0.5323    0.8075    0.2145    0.9128    0.4057    0.0581

Verify that the random numbers each option generates are the same for each iteration.

isequal(r1,r2)
ans = logical
   1

See Also

|

Topics