Skip to content

Random generation

Simulations often use stochastic elements to model real world processes. A problem this brings is that results from a randomized process or state cannot be reproduced. In computer science this is solved through the use of a "seed". This is a number which describes the initial state of your random number generator (RNG), and from which each consequent future state (and thus your random numbers) are drawn.

Random number generators

ERS supports two high-quality seeded pseudorandom number generators:

Generator Description Use Case
Mersenne Twister Industry-standard, high-quality generator with very long period (2^19937-1) Default choice for most simulations
Wichmann-Hill Combines three linear congruent generators Legacy support, faster but shorter period

Submodel random generator

Each submodel has its own RNG which is automatically created when the submodel is initialized. It is required for each submodel to have their own instance as the non-deterministic run order of the submodels would cause the random numbers to be drawn in an unreproducible order between submodels. For that reason, you should never use your own random number generators when working with ERS.

Model container random seeding

The RNGs of the submodels are created using a seed generated by the model container, which itself has a RNG. The seed for this RNG can be set using the SetSeed function, although this is not required; when not set, this seeds is set automatically to a random number. Note that this will thus lead to unreproducible runs. If you have set a seed and wish to return to this behavior you can call GenerateRandomSeed.

Note

The seeds of the simulators are set when the model container starts. Therefore it is only possible to sample random numbers after the model container has started. See common pitfalls.

Ers::ModelContainer modelContainer = Ers::ModelContainer::Create();

// Set the master seed - this will seed the internal generator
// that is used to seed all SubModel generators
modelContainer.SetSeed(42);

// Add simulators - each will get a different seed from the ModelContainer's generator
Ers::Simulator sim1 = modelContainer.AddSimulator("Simulator 1", Ers::SimulatorType::DiscreteEvent);
Ers::Simulator sim2 = modelContainer.AddSimulator("Simulator 2", Ers::SimulatorType::DiscreteEvent);

modelContainer.Start();

// Random values can now be sampled.
// sim1 and sim2 will have different random streams even though the model structure is the same
ModelContainer modelContainer = ModelContainer.Create();

// Set the master seed - this will seed the internal generator
// that is used to seed all SubModel generators
modelContainer.Seed = 42;

// Add simulators - each will get a different seed from the ModelContainer's generator
Simulator sim1 = modelContainer.AddSimulator("Simulator 1", SimulatorType.DiscreteEvent);
Simulator sim2 = modelContainer.AddSimulator("Simulator 2", SimulatorType.DiscreteEvent);

modelContainer.Start();

// Random values can now be sampled.
// sim1 and sim2 will have different random streams even though the model structure is the same

Basic usage

Sampling random numbers

simulator.EnterSubModel();
Ers::SubModel subModel = Ers::SubModel::Get();

// Generate a random number between 0.0 and 1.0
double randomValue = subModel.SampleRandomGenerator();

// Generate a random number in a specific range
double min = 5.0;
double max = 10.0;
double randomInRange = min + (max - min) * subModel.SampleRandomGenerator();

simulator.ExitSubModel();
simulator.EnterSubModel();
SubModel subModel = SubModel.Get();

// Generate a random number between 0.0 and 1.0
double randomValue = subModel.SampleRandomGenerator();

// Generate a random number in a specific range
double min = 5.0;
double max = 10.0;
double randomInRange = min + (max - min) * subModel.SampleRandomGenerator();

simulator.ExitSubModel();

Resetting the generator

subModel.SetRandomGeneratorSeed(42);
double a = subModel.SampleRandomGenerator();

// Reset to go back to initial seed
subModel.ResetRandomGenerator();
double b = subModel.SampleRandomGenerator();

// a and b will be equal
assert(a == b);
subModel.SetRandomGeneratorSeed(42);
double a = subModel.SampleRandomGenerator();

// Reset to go back to initial seed
subModel.ResetRandomGenerator();
double b = subModel.SampleRandomGenerator();

// a and b will be equal
Debug.Assert(a == b);

Changing generator types

Ers::SubModelRandomProperties randomProps = subModel.GetRandomProperties();

// Switch to Wichmann-Hill generator
randomProps.SetRandomGenerator(Ers::Math::RandomGeneratorType::WichmannHill);

// Switch back to Mersenne Twister (default)
randomProps.SetRandomGenerator(Ers::Math::RandomGeneratorType::MersenneTwister);
using Ers;

SubModelRandomProperties randomProps = subModel.GetRandomProperties();

// Switch to Wichmann-Hill generator
randomProps.SetRandomGenerator(RandomGeneratorType.WichmannHill);

// Switch back to Mersenne Twister (default)
randomProps.SetRandomGenerator(RandomGeneratorType.MersenneTwister);

Direct generator access

Access the random generator directly for more control.

Ers::Math::RandomGenerator generator = subModel.GetRandomProperties().GetRandomNumberGenerator();

// Check generator type
Ers::Math::RandomGeneratorType type = generator.GetRandomGeneratorType();

// Sample multiple values
for (int i = 0; i < 10; i++)
{
    double value = generator.Sample();
}

// Reset the generator
generator.Reset();

// Set a new seed
generator.SetSeed(999);
using Ers;

RandomGenerator generator = subModel.GetRandomProperties().GetRandomNumberGenerator();

// Check generator type
RandomGeneratorType type = generator.GetRandomGeneratorType();

// Sample multiple values
for (int i = 0; i < 10; i++)
{
    double value = generator.Sample();
}

// Reset the generator
generator.Reset();

// Set a new seed
generator.Seed = 999;

Probability distributions

The random number generator provides uniform random values between 0.0 and 1.0. You can build any probability distribution using these uniform samples.

// Example: Uniform distribution in range [min, max]
double min = 5.0;
double max = 10.0;
double uniformValue = min + (max - min) * subModel.SampleRandomGenerator();

// Example: Exponential distribution with given mean
double mean = 10.0;
double exponentialValue = -mean * std::log(subModel.SampleRandomGenerator());
// Example: Uniform distribution in range [min, max]
double min = 5.0;
double max = 10.0;
double uniformValue = min + (max - min) * subModel.SampleRandomGenerator();

// Example: Exponential distribution with given mean
double mean = 10.0;
double exponentialValue = -mean * Math.Log(subModel.SampleRandomGenerator());

Best practices

Reproducibility

For testing and debugging, set explicit seeds at the ModelContainer level.

// Use fixed seed for reproducibility
modelContainer.SetSeed(42);
// Use fixed seed for reproducibility
modelContainer.Seed = 42;

Common pitfalls

Sampling random numbers before starting the model container

The simulator's seeds are only set when the model container starts. When sampling before starting the model container, the submodels' RNGs will not have been seeded yet. This results in all submodels's RNG having the same seed, and any manually set seed on a model container being ignored.

Ers::ModelContainer modelContainer = Ers::ModelContainer::Create();
modelContainer.SetSeed(42);

// Add simulators - each will get a different seed from the ModelContainer's generator
Ers::Simulator sim1 = modelContainer.AddSimulator("Simulator 1", Ers::SimulatorType::DiscreteEvent);
Ers::Simulator sim2 = modelContainer.AddSimulator("Simulator 2", Ers::SimulatorType::DiscreteEvent);

// BAD: Sampling before starting the model container
sim1.EnterSubModel();
double sample = Ers::SubModel::Get().SampleRandomGenerator();
sim1.ExitSubModel();

modelContainer.Start();

// GOOD: Sampling after starting the model container
sim1.EnterSubModel();
double sample = Ers::SubModel::Get().SampleRandomGenerator();
sim1.ExitSubModel();
ModelContainer modelContainer = ModelContainer.Create();
modelContainer.Seed = 42;

// Add simulators - each will get a different seed from the ModelContainer's generator
Simulator sim1 = modelContainer.AddSimulator("Simulator 1", SimulatorType.DiscreteEvent);
Simulator sim2 = modelContainer.AddSimulator("Simulator 2", SimulatorType.DiscreteEvent);

// BAD: Sampling before starting the model container
sim1.EnterSubModel();
double sample = SubModel.Get().SampleRandomGenerator();
sim1.EnterSubModel();

modelContainer.Start();

// GOOD: Sampling after starting the model container
sim1.EnterSubModel();
double sample = SubModel.Get().SampleRandomGenerator();
sim1.EnterSubModel();

Don't reset unnecessarily

Resetting the generator in a loop destroys randomness. Let the sequence continue naturally.

// BAD: Resetting destroys randomness
for (int i = 0; i < 100; i++)
{
    subModel.ResetRandomGenerator();  // Don't do this!
    double value = subModel.SampleRandomGenerator();
}

// GOOD: Let the sequence continue
for (int i = 0; i < 100; i++)
{
    double value = subModel.SampleRandomGenerator();
}
// BAD: Resetting destroys randomness
for (int i = 0; i < 100; i++)
{
    subModel.ResetRandomGenerator();  // Don't do this!
    double value = subModel.SampleRandomGenerator();
}

// GOOD: Let the sequence continue
for (int i = 0; i < 100; i++)
{
    double value = subModel.SampleRandomGenerator();
}

Set seed before model starts

Set the seed during model setup, before the simulation starts running.

// BAD: Setting seed after model has started
void OnStart()
{
    subModel.SetRandomGeneratorSeed(42);  // Don't do this!
}

// GOOD: Set seed before starting the model
Ers::ModelContainer modelContainer = Ers::ModelContainer::Create();
modelContainer.SetSeed(42);  // Set at ModelContainer level

// Or if you need to set per-submodel, do it during setup
simulator.EnterSubModel();
Ers::SubModel subModel = Ers::SubModel::Get();
subModel.SetRandomGeneratorSeed(42);  // Before any simulation logic runs
simulator.ExitSubModel();

// Now start the simulation
modelContainer.Update(1000);
// BAD: Setting seed after model has started
public override void OnStart()
{
    subModel.SetRandomGeneratorSeed(42);  // Don't do this!
}

// GOOD: Set seed before starting the model
ModelContainer modelContainer = ModelContainer.Create();
modelContainer.Seed = 42;  // Set at ModelContainer level

// Or if you need to set per-submodel, do it during setup
simulator.EnterSubModel();
SubModel subModel = SubModel.Get();
subModel.SetRandomGeneratorSeed(42);  // Before any simulation logic runs
simulator.ExitSubModel();

// Now start the simulation
modelContainer.Update(1000);

See Also

  • Simulator: Simulation execution and timing.
  • Submodel: SubModel structure and management.
  • Events: Event scheduling and handling.
Was this page helpful?