David M.
David M.

Reputation: 391

OMNeT++ RNG not converging to mean

I am using the OMNeT++ 5.1.1 simulator. I've been seeing some strange behavior from the bernoulli() function, so I built a MWE to see what's happening.

The MWE works by creating a network with a single node, and setting one timer (self message) at t=0. When the timer goes off, the simulation runs some number n Bernoulli trials with success probability p. The values n and p are specified via per-run configuration options that I defined using the Register_PerRunConfigOption() macro.

Here is my code:

#include <math.h>
#include <omnetpp.h>

using namespace omnetpp;

Register_PerRunConfigOption(CFGID_NUM_TRIALS, "num-trials", CFG_INT,
    "0", "The number of Bernoulli trials to run");
Register_PerRunConfigOption(CFGID_BERNOULLI_MEAN, "bernoulli-mean",
    CFG_DOUBLE, "0.0", "The mean of the Bernoulli experiments");

class Test : public cSimpleModule {
    private:
        int nTrials, nSuccess;
        double p;
        cMessage *timer;
    protected:
        virtual void initialize() override;
        virtual void handleMessage(cMessage *msg) override;
};

Define_Module(Test);

void Test::initialize()
{
    nTrials = getEnvir()->getConfig()->getAsInt(CFGID_NUM_TRIALS);
    p = getEnvir()->getConfig()->getAsDouble(CFGID_BERNOULLI_MEAN);

    timer = new cMessage("timer");
    scheduleAt(0.0, timer);
}

void Test::handleMessage(cMessage *msg)
{   
    int trial;

    printf("\n\n");

    for (int n = 0; n < nTrials; n++) {
        trial = bernoulli(p); 
        if (trial)
            nSuccess++;
    }

    double mean     = nTrials * p;
    double variance = mean * (1.0 - p);
    double stddev   = std::sqrt(variance);

    printf("nTrials:  %12d(%.3e)\n", nTrials, (double) nTrials);
    printf("nSuccess: %12d(%.3e)\n", nSuccess, (double) nSuccess);
    printf("Pct.:     %12.5f\n", 100.0 * (double) nSuccess / nTrials);
    printf("nStdDevs: %12.2f\n", (nSuccess - mean) / stddev);
    printf("\n\n");

    delete msg;
}

This code is as simple as I could think of (I'm new to OMNeT++). Here is the .ned file:

simple Test
{
    gates:
}

network Bernoulli
{
    submodules:
        node: Test;
}

Here is the omnetpp.ini file:

[General]
network = Bernoulli
bernoulli-mean = 0.05
num-trials = 10000000
rng-class = "cMersenneTwister"
seed-0-mt = ${seed=0,1,2,3,4,5,6,7,8,9}

I am running the code with the command: ./exe_file -u Cmdenv -r 3 (I am intentionally picking out the third run). When I do this with the omnetpp.ini file above, I get about 532,006 successes (though THIS NUMBER CHANGES SLIGHTLY ON EACH RUN??). For 10^7 runs, this is about 46 standard deviations from the mean (computed using mean and variance of the binomial distribution).

Furthermore, if I comment out the line rng-class="cMersenneTwister", the number jumps to about 531,793 successes, again changing slightly (but not radically) each time.

Moreover, if I comment out the seed-0-mt=... line, then suddenly the simulation starts producing values within 0.06 std. dev. of the mean! This despite the fact that the OMNeT++ manual assures that using the cMersenneTwister algorithm means you can choose seeds at random, since the period is so large.

Why is this happening?? I would expect that (1) since cMersenneTwister is the default, including it in the omnetpp.ini file shouldn't change anything, and (2) that since I'm choosing the same seed each time (i.e. the seed 3), that I should be getting the same results. But I'm not! This confuses me, because the OMNeT++ manual states:

For the cMersenneTwister random number generator, selecting seeds so that the generated sequences don't overlap is easy, due to the extremely long sequence of the RNG. The RNG is initialized from the 32-bit seed value seed = runNumber*numRngs + rngNumber.

Thanks!

Upvotes: 2

Views: 275

Answers (1)

Jerzy D.
Jerzy D.

Reputation: 7002

You should initialize nSuccess to zero before using it because in C++ the member of class which is fundamental type (int, float, etc.) is not initialized by default.
Moreover, I strongly encourage you to use the parameter mechanism in OMNeT++ - it is a standard way to control simulation. To use it you should:

  1. Add the definition of parameters in NED file of simple module, for example:

    simple Test
    {
        parameters: 
           double bernoulli_mean;
           int num_trials;
        gates:
    }
    
  2. Set values in omnetpp.ini:

    **.bernoulli_mean = 0.05
    **.num_trials = 10000000
    
  3. Read the parameters in your class:

    void Test::initialize()
    {
      nTrials = par("num_trials");
      p = par("bernoulli_mean").doubleValue();
      // ...
    

Notes:

  • Using "-" in the name of a parameter is forbidden.
  • In omnetpp.ini every instance of a simple module has own values of its parameters. However, to assign the same values to all module, one can use wildcard patterns, for example **

Upvotes: 2

Related Questions