Random numbers in litebird_sim

The LiteBIRD Simulation Framework can be used to generate random numbers, used for example for producing noise timelines. In order to do so, a seed and a Random Number Generator (RNG) are used.

Seed

The random_seed is used to control the behaviour of the RNG. The seed can be None or an integer number. If you are not interested in the reproducibility of your results, you can set random_seed to None. However, this is not recommended. In fact, this means that if you run multiple times a function or method where a RNG is used, e.g. for producing noise timelines, then the outputs will be different and you will not be able to re-obtain these results again. If instead you are interested in the reproducibility of your results, you can set random_seed to an integer number. With this choice, if you run multiple times a function or method where a RNG is used, then the outputs will be the same and you will re-obtain these results by re-running your script, as the random numbers produced by the generator will be exactly the same.

How should you set the random_seed? This parameter must be passed to the constructor of a Simulation class. If a Simulation instance is created without passing the seed, an error will be raised and your script will fail. This feature has been introduced on purpose in order to avoid automatic settings of the seed and unclear behaviour of the generation of random numbers. If you are running a parallel script using MPI, you do not have to worry about setting a different seed for different MPI ranks: by passing the same seed, the random numbers generated by the different ranks will be different. This is ensured by the random number generator. We want this behaviour as we do not want repetitions of, e.g., the same noise TOD if we split their computation on different MPI ranks. For example, in this way, if you split the TOD matrix of an Observation class by the time, you will not encounter the same noise after the samples generated by a certain rank; if you split the TOD matrix of an Observation class by the detectors, each one will have a different noise timestream.

Regarding the reproducibility of the results in a parallel code, there is an important thing to bear in mind: if you set the seed to an integer number but run your script with a different number of MPI ranks, the outputs will be different! Think about a noise time stream of 4 samples. If you use 2 MPI ranks, then the first 2 samples will be generated by one RNG (the one of rank 0), while the last 2 samples will be generated by a different RNG (the one of rank 1). If you then run the same script with the same seed but with 4 MPI ranks, each of the samples will be generated by a different RNG and only the first sample will be the same for the two runs, as it is always the first one generated by rank 0’s RNG.

The setting of the random_seed is as simple as this:

sim = lbs.Simulation(
    base_path="/storage/output",
    start_time=astropy.time.Time("2020-02-01T10:30:00"),
    duration_s=3600.0,
    name="My noise simulation",
    description="I want to generate some noise and be able to reproduce my results",
    random_seed=12345, # Here the seed for the random number generator is set
)

During the execution of the Simulation constructor, the Simulation.init_random() method is run. There, the RNG is built and seeded with random_seed. You can then use this RNG by calling sim.random. There is also the possibility to change the random seed after creatinga Simulation instance. This is achieved by calling Simulation.init_random():

sim = lbs.Simulation(random_seed=12345)
[...]
sim.init_random(random_seed=42)

Random number generators

The random number generator used by the Simulation.init_random() method of the Simulation class is PCG64. After creating this RNG by calling Simulation.init_random() (directly or from the Simulation constructor), you can use it via sim.random:

sim = lbs.Simulation(random_seed=12345)
[...]
sim.add_noise(noise_type='white', random=sim.random)

You can also use your own RNG with the functions and methods of litebird_sim:

sim = lbs.Simulation(random_seed=12345)
[...]
my_rng = ... # New RNG definition
sim.add_noise(noise_type='white', random=my_rng)

You should just make sure that your custom RNG implements the normal method, so it can be used for white noise generation.