Usage

Seeds, Streams and Generators

Generally, it is possible to define one random seed per model part (module), initialise one generator per model part and use that generator for every random number stream required within the model part. The other way is to also define one random seed per model part but use numerous generators (e.g. for every specific task within the model part) that are all initialised with that seed.

Comparison of the two versions
AdvantagesDisadvantages
One generatorMore efficient and ClearerNot possible to exclude specific tasks without influencing following tasks that depend on that generator
Numerous generatorsIndependent tasksMore effort to configure

When defining several random seeds and generators as parameters it is possible to define defaults where random seeds and generators relating to specific areas of the model may point to if they are not specified explicitly. Whereas this is recommended for random seeds it is not for random number generators: if one chooses to define a certain generator explicitly in certain cases that would also effect other random processes since random numbers of that specific area are no longer drawn from the default generator!

When a generator defaults to a super-generator (same String) an alternative name is required (set a new name for the generator) in case of customisation to indicate that it is now customised and may no longer point to the super-generator (otherwise, the customized generator would overwrite the super-generator).

Distributions

Whereas seeds and thus generators define the sequence of underlying pseudo-random numbers the distributions define the characteristic of resulting random numbers.

Therefore, code that uses random numbers and wants to enable the user to influence the random numbers should provide means to adapt both, the random seed used for random engine creation (or the random engine itself) and the distribution. One way is to use parameters for random seed, generators, and for the distribution name that is to be registered at the UranusRandomService.

Parameter Definitions for Random Seeds, Generators, and Distributions

In order to allow full flexibility for simulations the modeler should define parameters for random seed, generator, and distributions for both, the entire model and specific parts of the model. E.g.:

RANDOM_SEED(Integer.class, 1),
RANDOM_SEED_AGENTINIT(Integer.class, RANDOM_SEED),
RANDOM_SEED_DECISIONMAKING(Integer.class, RANDOM_SEED),

RANDOM_STREAM(String.class, "RANDOM_STREAM"),
RANDOM_STREAM_AGENTINIT(String.class, "RANDOM_STREAM_AGENTINIT"),
RANDOM_STREAM_DECISIONMAKING(String.class, "RANDOM_STREAM_DECISIONMAKING"),

RANDOM_DIST(String.class, "RANDOM_DIST"),
RANDOM_DIST_AGENTINIT(String.class, "RANDOM_DIST_AGENTINIT"),
RANDOM_DIST_DECISIONMAKING(String.class, "RANDOM_DIST_DECISIONMAKING"),

Full Flexibility

A quite rich behaviour is possible:

  1. If all parameters (seed, distributions, generators) are left to their default values, a random stream with the default random seed (1) is created and used by all processes.
  2. In case there is only a general random seed defined (RANDOM_SEED) this is used to instantiate a new random number generator (RANDOM_STREAM) used for all model parts in case there is no random generator defined as well.
  3. If there is a general random number generator defined (RANDOM_STREAM) this is used for all parts. Definitions of RANDOM_SEED are ignored in this case. However, the defined generator should be based on the defined random seed.
  4. In case there is a random seed defined for any specific part (e.g. RANDOM_SEED_AGENTINIT) this is used to instantiate a new random number generator (RANDOM_STREAM_AGENTINIT) used for this part in case there is no random generator defined as well.
  5. If there is a random number generator defined for any specific part (e.g. RANDOM_STREAM_AGENTINIT) this is used for this specific part. Definitions of e.g. RANDOM_SEED_AGENTINIT are ignored in this case. However, the defined generator should be based on the defined random seed for the specific part.
  6. If for any part a distribution is registered for the predefined distribution name (e.g. RANDOM_DIST_AGENTINIT) the registered distribution is used. However, also the distribution should be based on the defined random seed (RANDOM_SEED_AGENTINIT)

    NOTE: To achieve independent random number streams for different parts it is sufficient to set the random seeds accordingly, even if they are set to the same value.

Initialising Generators and Distributions

A typical initialisation following above considerations would be (assuming random number generators are initialised for each area of the model anyway - see Seeds, Streams and Generators:

UranusRandomService randomService = URandomService.getURandomService();

// init the general random stream (unless it is changed or registered by the user before):
if (!PmParameterManager.isCustomised(RANDOM_STREAM)
                && !getURandomService().isGeneratorRegistered(
                                (String) PmParameterManager.getParameter(RANDOM_STREAM))) {
        getURandomService().registerGenerator((String) PmParameterManager.getParameter(RANDOM_STREAM),
                        new MersenneTwister(((Integer) PmParameterManager.getParameter(RANDOM_SEED)).intValue()));
}

/*
 * The following specific distributions and generators are only initialised when the according parameter values
 * have changed. Otherwise, the default distribution and generator are used for all.
 */
if (PmParameterManager.isCustomised(RANDOM_SEED_AGENTINIT)
                && !(PmParameterManager.isCustomised(RANDOM_STREAM_AGENTINIT)
                && randomService.isGeneratorRegistered((String) PmParameterManager
                .getParameter(RANDOM_STREAM_AGENTINIT)))) {
        randomService.registerGenerator((String) PmParameterManager
                        .getParameter(RANDOM_STREAM_AGENTINIT), new MersenneTwister(
                        ((Integer) PmParameterManager.getParameter(RANDOM_SEED_AGENTINIT)).intValue()));
}

String distName = (String) PmParameterManager.getParameter(RANDOM_DIST_AGENTINIT);
if (randomService.isDistributionRegistered(distName)) {
        if (!(randomService.getDistribution(distName) instanceof Uniform)) {
                logger.warn("Distribution with assigned name in RANDOM_DIST_AGENTINIT ("
                                + distName + ") is not an Uniform distribution");
                randomService.registerDistribution(randomService.getNewUniformDistribution(randomService
                                .getGenerator((String) PmParameterManager.getParameter(RANDOM_STREAM_AGENTINIT))),
                                distName);
        }
} else {
        randomService.registerDistribution(randomService.getNewUniformDistribution(randomService
                        .getGenerator((String) PmParameterManager.getParameter(RANDOM_STREAM_AGENTINIT))), distName);
}

Registering Generators and Distributions

Some attention is required when registering random generators and distributions at URaNuS. If one uses the current parameter values ugly effects occur when one decides to assign the same value to all generator and/or distribution names: The last generator/distribution registered overwrites earlier registrations. URaNuS issues a warning in this case. To prevent such situations it is better to use the default parameter value or another constant as name for registration. However, if the default is another parameter anyway one should not register the specific generator/distribution at all.

URandomService.getURandomService().registerDistribution(new Uniform(
        URandomService.getURandomService().getGenerator((String) PmParameterManager.getParameter(
                RandomPa.RANDOM_STREAM_DECISIONMAKING))), (String)RandomPa.RANDOM_DIST_DECISIONMAKING.getDefaultValue());

Logging

It is certainly a good idea to use logging to control which random distribution/generator/seed is used in which model part. To activate logging configure your Log4J as follows:

log4j.logger.de.cesr.uranus = INFO, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p - (%C{1}:%L) %m%n

If the logging level is set to DEBUG instead of INFO the framework logs the origin of the random number request using UIdentifyCallerException.

NOTE: In order to enable the logging of origin of random number requests it is required to initialise the distributions by URandomService with methods getNewUniformDistribution() and createNormal(). Of course it is also possible to use own distributions that are adapted according to e.g. UNormalController.