core: refactor EmpricialRandomVariable and provide example
This commit is contained in:
committed by
Peter Barnes
parent
6ec2093986
commit
b7deb42a65
215
src/core/examples/empirical-random-variable-example.cc
Normal file
215
src/core/examples/empirical-random-variable-example.cc
Normal file
@@ -0,0 +1,215 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2020 Lawrence Livermore National Laboratory
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation;
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
|
||||
*/
|
||||
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/nstime.h"
|
||||
#include "ns3/command-line.h"
|
||||
#include "ns3/random-variable-stream.h"
|
||||
#include "ns3/histogram.h"
|
||||
#include "ns3/ptr.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \ingroup core-examples
|
||||
* \ingroup randomvariable
|
||||
* Example program illustrating use of ns3::EmpiricalRandomVariable
|
||||
*
|
||||
* This example illustrates
|
||||
*
|
||||
* * Creating an EmpiricalRandomVariable instance.
|
||||
* * Switching the mode.
|
||||
* * Using the sampling mode
|
||||
* * Switching modes
|
||||
* * Using the interpolating mode
|
||||
*
|
||||
* Consult the ns-3 manual for more information about the use of the
|
||||
* random number generator
|
||||
*/
|
||||
|
||||
using namespace ns3;
|
||||
|
||||
void
|
||||
RunSingleSample (std::string mode, Ptr<EmpiricalRandomVariable> erv)
|
||||
{
|
||||
std::cout << "------------------------------" << std::endl;
|
||||
std::cout << "Sampling " << mode << std::endl;
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Binned sample" << std::endl;
|
||||
double value = erv->GetValue ();
|
||||
std::cout << "Binned sample: " << value << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "Interpolated sample" << std::endl;
|
||||
erv->SetInterpolate (true);
|
||||
value = erv->GetValue ();
|
||||
std::cout << "Interpolated sample:" << value << std::endl;
|
||||
erv->SetInterpolate (false);
|
||||
}
|
||||
|
||||
void
|
||||
PrintStatsLine (const double value, const long count, const long n)
|
||||
{
|
||||
std::cout << std::fixed << std::setprecision (3)
|
||||
<< std::setw (10) << std::right << value
|
||||
<< std::setw (10) << std::right << count
|
||||
<< std::setw (10) << std::right
|
||||
<< count / static_cast<double> (n) * 100.0
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
PrintSummary (long sum, long n, double weighted, double expected)
|
||||
{
|
||||
std::cout << std::endl;
|
||||
std::cout << " --------" << std::endl;
|
||||
std::cout << " Total "
|
||||
<< std::setprecision (3) << std::fixed
|
||||
<< std::setw (10) << std::right
|
||||
<< sum / static_cast<double> (n) * 100.0
|
||||
<< std::endl;
|
||||
std::cout << " Average "
|
||||
<< std::setprecision (3) << std::fixed
|
||||
<< std::setw (6) << std::right << weighted / n
|
||||
<< std::endl;
|
||||
std::cout << " Expected "
|
||||
<< std::setprecision (3) << std::fixed
|
||||
<< std::setw (6) << std::right << expected
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
RunBothModes (std::string mode, Ptr<EmpiricalRandomVariable> erv, long n)
|
||||
{
|
||||
std::cout << std::endl;
|
||||
std::cout << "Sampling " << mode << std::endl;
|
||||
std::map <double, int> counts;
|
||||
counts[0] = 0;
|
||||
for (long i = 0; i < n; ++i)
|
||||
{
|
||||
++counts[erv->GetValue ()];
|
||||
}
|
||||
long sum = 0;
|
||||
double weighted = 0;
|
||||
std::cout << std::endl;
|
||||
std::cout << " Value Counts %" << std::endl;
|
||||
std::cout << "---------- -------- --------" << std::endl;
|
||||
for (auto c : counts)
|
||||
{
|
||||
long count = c.second;
|
||||
double value = c.first;
|
||||
sum += count;
|
||||
weighted += value * count;
|
||||
PrintStatsLine (value, count, n);
|
||||
}
|
||||
PrintSummary (sum, n, weighted, 8.75);
|
||||
|
||||
std::cout << "Interpolating " << mode << std::endl;
|
||||
erv->SetInterpolate (true);
|
||||
Histogram h (0.5);
|
||||
for (long i = 0; i < n; ++i)
|
||||
{
|
||||
h.AddValue (erv->GetValue ());
|
||||
// This could also be expressed as
|
||||
// h.AddValue (erv->Interpolate ());
|
||||
}
|
||||
erv->SetInterpolate (false);
|
||||
sum = 0;
|
||||
weighted = 0;
|
||||
std::cout << std::endl;
|
||||
std::cout << " Bin Start Counts %" << std::endl;
|
||||
std::cout << "---------- -------- --------" << std::endl;
|
||||
for (uint32_t i = 0; i < h.GetNBins (); ++i)
|
||||
{
|
||||
long count = h.GetBinCount (i);
|
||||
double start = h.GetBinStart (i);
|
||||
double value = start + h.GetBinWidth (i) / 2.;
|
||||
sum += count;
|
||||
weighted += count * value;
|
||||
PrintStatsLine (start, count, n);
|
||||
}
|
||||
PrintSummary (sum, n, weighted, 6.25);
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
long n = 1000000;
|
||||
bool disableAnti = false;
|
||||
bool single = false;
|
||||
CommandLine cmd;
|
||||
cmd.AddValue ("count", "how many draws to make from the rng", n);
|
||||
cmd.AddValue ("antithetic", "disable antithetic sampling", disableAnti);
|
||||
cmd.AddValue ("single", "sample a single time", single);
|
||||
cmd.Parse (argc, argv);
|
||||
std::cout << std::endl;
|
||||
std::cout << cmd.GetName () << std::endl;
|
||||
if (!single)
|
||||
{
|
||||
std::cout << "Sample count: " << n << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Sampling a single time" << std::endl;
|
||||
}
|
||||
if (disableAnti)
|
||||
{
|
||||
std::cout << "Antithetic sampling disabled" << std::endl;
|
||||
}
|
||||
|
||||
// Create the ERV in sampling mode
|
||||
Ptr<EmpiricalRandomVariable> erv = CreateObject<EmpiricalRandomVariable> ();
|
||||
erv->SetInterpolate (false);
|
||||
erv->CDF ( 0.0, 0.0);
|
||||
erv->CDF ( 5.0, 0.25);
|
||||
erv->CDF (10.0, 1.0);
|
||||
|
||||
if (single)
|
||||
{
|
||||
RunSingleSample ("normal", erv);
|
||||
if (!disableAnti)
|
||||
{
|
||||
std::cout << std::endl;
|
||||
std::cout << "Antithetic" << std::endl;
|
||||
erv->SetAntithetic (true);
|
||||
RunSingleSample ("antithetic", erv);
|
||||
erv->SetAntithetic (false);
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RunBothModes ("normal", erv, n);
|
||||
|
||||
if (!disableAnti)
|
||||
{
|
||||
erv->SetAntithetic (true);
|
||||
RunBothModes ("antithetic", erv, n);
|
||||
erv->SetAntithetic (false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -44,6 +44,9 @@ def build(bld):
|
||||
['core'])
|
||||
obj.source = 'sample-show-progress.cc'
|
||||
|
||||
obj = bld.create_ns3_program('empirical-random-variable-example', ['core', 'flow-monitor'])
|
||||
obj.source = 'empirical-random-variable-example.cc'
|
||||
|
||||
if bld.env['ENABLE_THREADING'] and bld.env["ENABLE_REAL_TIME"]:
|
||||
obj = bld.create_ns3_program('main-test-sync', ['network'])
|
||||
obj.source = 'main-test-sync.cc'
|
||||
|
||||
@@ -1518,12 +1518,13 @@ DeterministicRandomVariable::GetInteger (void)
|
||||
NS_OBJECT_ENSURE_REGISTERED (EmpiricalRandomVariable);
|
||||
|
||||
// ValueCDF methods
|
||||
EmpiricalRandomVariable::ValueCDF::ValueCDF ()
|
||||
EmpiricalRandomVariable::ValueCDF::ValueCDF (void)
|
||||
: value (0.0),
|
||||
cdf (0.0)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
EmpiricalRandomVariable::ValueCDF::ValueCDF (double v, double c)
|
||||
: value (v),
|
||||
cdf (c)
|
||||
@@ -1531,11 +1532,12 @@ EmpiricalRandomVariable::ValueCDF::ValueCDF (double v, double c)
|
||||
NS_LOG_FUNCTION (this << v << c);
|
||||
NS_ASSERT (c >= 0.0 && c <= 1.0);
|
||||
}
|
||||
EmpiricalRandomVariable::ValueCDF::ValueCDF (const ValueCDF& c)
|
||||
: value (c.value),
|
||||
cdf (c.cdf)
|
||||
|
||||
bool
|
||||
operator < (EmpiricalRandomVariable::ValueCDF a,
|
||||
EmpiricalRandomVariable::ValueCDF b)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << &c);
|
||||
return a.cdf < b.cdf;
|
||||
}
|
||||
|
||||
TypeId
|
||||
@@ -1545,18 +1547,39 @@ EmpiricalRandomVariable::GetTypeId (void)
|
||||
.SetParent<RandomVariableStream>()
|
||||
.SetGroupName ("Core")
|
||||
.AddConstructor<EmpiricalRandomVariable> ()
|
||||
.AddAttribute ("Interpolate",
|
||||
"Treat the CDF as a smooth distribution and interpolate, "
|
||||
"default is to treat the CDF as a histogram and sample.",
|
||||
BooleanValue (false),
|
||||
MakeBooleanAccessor (&EmpiricalRandomVariable::m_interpolate),
|
||||
MakeBooleanChecker ())
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
EmpiricalRandomVariable::EmpiricalRandomVariable ()
|
||||
:
|
||||
m_validated (false)
|
||||
EmpiricalRandomVariable::EmpiricalRandomVariable (void)
|
||||
: m_validated (false)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
double
|
||||
EmpiricalRandomVariable::GetValue (void)
|
||||
bool
|
||||
EmpiricalRandomVariable::SetInterpolate (bool interpolate)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << interpolate);
|
||||
bool prev = m_interpolate;
|
||||
m_interpolate = interpolate;
|
||||
return prev;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
EmpiricalRandomVariable::GetInteger (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return static_cast<uint32_t> (GetValue ());
|
||||
}
|
||||
|
||||
bool
|
||||
EmpiricalRandomVariable::PreSample (double & value)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
@@ -1564,29 +1587,61 @@ EmpiricalRandomVariable::GetValue (void)
|
||||
{
|
||||
Validate ();
|
||||
}
|
||||
|
||||
// Get a uniform random variable in [0,1].
|
||||
|
||||
// Get a uniform random variable in [0, 1].
|
||||
double r = Peek ()->RandU01 ();
|
||||
if (IsAntithetic ())
|
||||
{
|
||||
r = (1 - r);
|
||||
}
|
||||
|
||||
|
||||
value = r;
|
||||
bool valid = false;
|
||||
// check extrema
|
||||
if (r <= m_emp.front ().cdf)
|
||||
{
|
||||
return m_emp.front ().value; // Less than first
|
||||
value = m_emp.front ().value; // Less than first
|
||||
valid = true;
|
||||
}
|
||||
if (r >= m_emp.back ().cdf)
|
||||
else if (r >= m_emp.back ().cdf)
|
||||
{
|
||||
return m_emp.back ().value; // Greater than last
|
||||
value = m_emp.back ().value; // Greater than last
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
double
|
||||
EmpiricalRandomVariable::GetValue (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
double value;
|
||||
if (PreSample (value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
// Binary search
|
||||
auto bound = std::upper_bound (m_emp.begin (), m_emp.end (), r,
|
||||
[] (double p, ValueCDF &it)
|
||||
{
|
||||
return p < it.cdf;
|
||||
});
|
||||
// value now has the (unused) URNG selector
|
||||
if (m_interpolate)
|
||||
{
|
||||
value = DoInterpolate (value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = DoSampleCDF (value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
double
|
||||
EmpiricalRandomVariable::DoSampleCDF (double r)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << r);
|
||||
|
||||
ValueCDF selector (0, r);
|
||||
auto bound = std::upper_bound (m_emp.begin (), m_emp.end (), selector);
|
||||
|
||||
return bound->value;
|
||||
}
|
||||
|
||||
@@ -1595,52 +1650,55 @@ EmpiricalRandomVariable::Interpolate (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
if (!m_validated)
|
||||
double value;
|
||||
if (PreSample (value))
|
||||
{
|
||||
Validate ();
|
||||
}
|
||||
|
||||
// Get a uniform random variable in [0, 1].
|
||||
double r = Peek ()->RandU01 ();
|
||||
if (IsAntithetic ())
|
||||
{
|
||||
r = (1 - r);
|
||||
return value;
|
||||
}
|
||||
|
||||
if (r <= m_emp.front ().cdf)
|
||||
{
|
||||
return m_emp.front ().value; // Less than first
|
||||
}
|
||||
if (r >= m_emp.back ().cdf)
|
||||
{
|
||||
return m_emp.back ().value; // Greater than last
|
||||
}
|
||||
|
||||
// Binary search
|
||||
auto bound = std::upper_bound (m_emp.begin (), m_emp.end (), r,
|
||||
[] (double p, ValueCDF &it)
|
||||
{
|
||||
return p < it.cdf;
|
||||
});
|
||||
auto next = std::next(bound, 1);
|
||||
return DoInterpolate (bound->cdf, next->cdf, bound->value, next->value, r);
|
||||
// value now has the (unused) URNG selector
|
||||
value = DoInterpolate (value);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
EmpiricalRandomVariable::GetInteger (void)
|
||||
double
|
||||
EmpiricalRandomVariable::DoInterpolate (double r)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return (uint32_t)GetValue ();
|
||||
NS_LOG_FUNCTION (this << r);
|
||||
|
||||
// Return a value from the empirical distribution
|
||||
// This code based (loosely) on code by Bruce Mah (Thanks Bruce!)
|
||||
|
||||
// search
|
||||
ValueCDF selector (0, r);
|
||||
auto upper = std::upper_bound (m_emp.begin (), m_emp.end (), selector);
|
||||
auto lower = std::prev (upper, 1);
|
||||
if (upper == m_emp.begin ())
|
||||
{
|
||||
lower = upper;
|
||||
}
|
||||
|
||||
// Interpolate random value in range [v1..v2) based on [c1 .. r .. c2)
|
||||
double c1 = lower->cdf;
|
||||
double c2 = upper->cdf;
|
||||
double v1 = lower->value;
|
||||
double v2 = upper->value;
|
||||
|
||||
double value = (v1 + ((v2 - v1) / (c2 - c1)) * (r - c1));
|
||||
return value;
|
||||
}
|
||||
|
||||
void EmpiricalRandomVariable::CDF (double v, double c)
|
||||
{ // Add a new empirical datapoint to the empirical cdf
|
||||
void
|
||||
EmpiricalRandomVariable::CDF (double v, double c)
|
||||
{
|
||||
// Add a new empirical datapoint to the empirical cdf
|
||||
// NOTE. These MUST be inserted in non-decreasing order
|
||||
NS_LOG_FUNCTION (this << v << c);
|
||||
m_emp.push_back (ValueCDF (v, c));
|
||||
}
|
||||
|
||||
void EmpiricalRandomVariable::Validate ()
|
||||
void
|
||||
EmpiricalRandomVariable::Validate (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
if (m_emp.empty ())
|
||||
@@ -1648,9 +1706,8 @@ void EmpiricalRandomVariable::Validate ()
|
||||
NS_FATAL_ERROR ("CDF is not initialized");
|
||||
}
|
||||
ValueCDF prior = m_emp[0];
|
||||
for (std::vector<ValueCDF>::size_type i = 0; i < m_emp.size (); ++i)
|
||||
for (auto current : m_emp)
|
||||
{
|
||||
ValueCDF& current = m_emp[i];
|
||||
if (current.value < prior.value || current.cdf < prior.cdf)
|
||||
{ // Error
|
||||
std::cerr << "Empirical Dist error,"
|
||||
@@ -1669,11 +1726,4 @@ void EmpiricalRandomVariable::Validate ()
|
||||
m_validated = true;
|
||||
}
|
||||
|
||||
double EmpiricalRandomVariable::DoInterpolate (double c1, double c2,
|
||||
double v1, double v2, double r)
|
||||
{ // Interpolate random value in range [v1..v2) based on [c1 .. r .. c2)
|
||||
NS_LOG_FUNCTION (this << c1 << c2 << v1 << v2 << r);
|
||||
return (v1 + ((v2 - v1) / (c2 - c1)) * (r - c1));
|
||||
}
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
@@ -2403,34 +2403,70 @@ private:
|
||||
|
||||
/**
|
||||
* \ingroup randomvariable
|
||||
* \brief The Random Number Generator (RNG) that has a specified empirical distribution.
|
||||
* \brief The Random Number Generator (RNG) that has a specified
|
||||
* empirical distribution.
|
||||
*
|
||||
* Defines a random variable that has a specified, empirical
|
||||
* distribution. The distribution is specified by a
|
||||
* series of calls to the CDF member function, specifying a
|
||||
* series of calls to the CDF() member function, specifying a
|
||||
* value and the probability that the function value is less than
|
||||
* the specified value. When values are requested,
|
||||
* the specified value. When random values are requested,
|
||||
* a uniform random variable is used to select a probability,
|
||||
* and the return value is interpreted linearly between the
|
||||
* two appropriate points in the CDF. The method is known
|
||||
* and the return value is chosen from the largest input value with CDF
|
||||
* less than the random value. The method is known
|
||||
* as inverse transform sampling:
|
||||
* (http://en.wikipedia.org/wiki/Inverse_transform_sampling).
|
||||
*
|
||||
* Here is an example of how to use this class:
|
||||
* \code
|
||||
* // Create the RNG with a uniform distribution between 0 and 10.
|
||||
* Ptr<EmpiricalRandomVariable> x = CreateObject<EmpiricalRandomVariable> ();
|
||||
* x->CDF ( 0.0, 0.0);
|
||||
* x->CDF ( 5.0, 0.5);
|
||||
* x->CDF (10.0, 1.0);
|
||||
* This generator has two modes: *sampling* and *interpolating*.
|
||||
* In *sampling* mode this random variable generator
|
||||
* treats the CDF as an exact histogram and returns
|
||||
* one of the histogram inputs exactly. This is appropriate
|
||||
* when the configured CDF represents the exact probability
|
||||
* distribution, for a categorical variable, for example.
|
||||
*
|
||||
* // The expected value for the mean of the values returned by this
|
||||
* // empirical distribution is the midpoint of the distribution
|
||||
* //
|
||||
* // E[value] = 5 .
|
||||
* //
|
||||
* double value = x->GetValue ();
|
||||
* \endcode
|
||||
* In *interpolating* mode this random variable generator linearly
|
||||
* interpolates between the CDF values defining the histogram bins.
|
||||
* This is appropriate when the configured CDF is an approximation
|
||||
* to a continuous underlying probability distribution.
|
||||
*
|
||||
* For historical reasons the default is interpolating.
|
||||
* To switch modes use the \c Interpolate Attribute, or call SetInterpolate().
|
||||
* You can change modes at any time.
|
||||
*
|
||||
* If you find yourself switching frequently it could be simpler to
|
||||
* set the mode to sampling, then use the GetValue() function for
|
||||
* sampled values, and Interpolate() function for interpolated values.
|
||||
*
|
||||
* Here is an example of how to use this class:
|
||||
*
|
||||
* // Create the RNG with a non-uniform distribution between 0 and 10.
|
||||
* // in sampling mode.
|
||||
* Ptr<EmpiricalRandomVariable> x = CreateObject<EmpiricalRandomVariable> ();
|
||||
* x->SetInterpolate (false);
|
||||
* x->CDF ( 0.0, 0.0);
|
||||
* x->CDF ( 5.0, 0.25);
|
||||
* x->CDF (10.0, 1.0);
|
||||
*
|
||||
* double value = x->GetValue ();
|
||||
*
|
||||
* The expected values and probabilities returned by GetValue are
|
||||
*
|
||||
* Value | Probability
|
||||
* ----: | ----------:
|
||||
* 0.0 | 0
|
||||
* 5.0 | 25%
|
||||
* 10.0 | 75%
|
||||
*
|
||||
* The only two values ever returned are 5 and 10, with unequal probability.
|
||||
*
|
||||
* If instead you want linear interpolation between the points of the CDF
|
||||
* use the Interpolate() function:
|
||||
*
|
||||
* double interp = x->Interpolate ();
|
||||
*
|
||||
* This will return continuous values on the range [0,1).
|
||||
*
|
||||
* See empirical-random-variable-example.cc for an example.
|
||||
*/
|
||||
class EmpiricalRandomVariable : public RandomVariableStream
|
||||
{
|
||||
@@ -2443,14 +2479,16 @@ public:
|
||||
|
||||
/**
|
||||
* \brief Creates an empirical RNG that has a specified, empirical
|
||||
* distribution.
|
||||
* distribution, and configured for interpolating mode.
|
||||
*/
|
||||
EmpiricalRandomVariable ();
|
||||
EmpiricalRandomVariable (void);
|
||||
|
||||
/**
|
||||
* \brief Specifies a point in the empirical distribution
|
||||
* \note These *MUST* be inserted in ascending order of \p c
|
||||
*
|
||||
* \param [in] v The function value for this point
|
||||
* \param [in] c Probability that the function is less than or equal to v
|
||||
* \param [in] c Probability that the function is less than or equal to \p v
|
||||
*/
|
||||
void CDF (double v, double c); // Value, prob <= Value
|
||||
|
||||
@@ -2460,6 +2498,7 @@ public:
|
||||
*
|
||||
* Note that this does not interpolate the CDF, but treats it as a
|
||||
* stepwise continuous function.
|
||||
*
|
||||
* Also note that antithetic values are being generated if m_isAntithetic
|
||||
* is equal to true. If \f$u\f$ is a uniform variable over [0,1]
|
||||
* and \f$x\f$ is a value that would be returned normally, then
|
||||
@@ -2485,39 +2524,44 @@ public:
|
||||
virtual uint32_t GetInteger (void);
|
||||
|
||||
/**
|
||||
* \brief Returns the next value in the empirical distribution using linear interpolation.
|
||||
* \return The floating point next value in the empirical distribution using linear interpolation.
|
||||
* \brief Returns the next value in the empirical distribution using
|
||||
* linear interpolation.
|
||||
* \return The floating point next value in the empirical distribution
|
||||
* using linear interpolation.
|
||||
*/
|
||||
virtual double Interpolate (void);
|
||||
|
||||
/**
|
||||
* \brief Switch the mode between sampling the CDF and interpolating.
|
||||
* The default mode is sampling.
|
||||
* \param [in] interpolate If \c true set to interpolation, otherwise sampling.
|
||||
* \returns The previous interpolate flag value.
|
||||
*/
|
||||
bool SetInterpolate (bool interpolate);
|
||||
|
||||
private:
|
||||
/** Helper to hold one point of the CDF. */
|
||||
/** \brief Helper to hold one point of the CDF. */
|
||||
class ValueCDF
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
ValueCDF ();
|
||||
/** \brief Constructor. */
|
||||
ValueCDF (void);
|
||||
/**
|
||||
* Construct from values.
|
||||
* \brief Construct from values.
|
||||
*
|
||||
* \param [in] v The argument value.
|
||||
* \param [in] c The CDF at the argument value \pname{v}
|
||||
*/
|
||||
ValueCDF (double v, double c);
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
* \param [in] c The other ValueCDF.
|
||||
*/
|
||||
ValueCDF (const ValueCDF& c);
|
||||
|
||||
/** The argument value. */
|
||||
double value;
|
||||
/** The CDF at \pname{value} */
|
||||
double cdf;
|
||||
};
|
||||
}; // class ValueCDF
|
||||
|
||||
/**
|
||||
* Check that the CDF is valid.
|
||||
* \brief Check that the CDF is valid.
|
||||
*
|
||||
* A valid CDF has
|
||||
*
|
||||
@@ -2526,25 +2570,54 @@ private:
|
||||
*
|
||||
* It is a fatal error to fail validation.
|
||||
*/
|
||||
virtual void Validate ();
|
||||
void Validate (void);
|
||||
/**
|
||||
* \brief Do the initial rng draw and check against the extrema.
|
||||
*
|
||||
* If the extrema apply, \c value will have the extremal value
|
||||
* and the return will be \c true.
|
||||
*
|
||||
* If the extrema do not apply \c value will have the URNG value
|
||||
* and the return will be \c false.
|
||||
*
|
||||
* \param [out] value The extremal value, or the URNG.
|
||||
* \returns \c true if \p value is the extremal result,
|
||||
* or \c false if \p value is the URNG value.
|
||||
*/
|
||||
bool PreSample (double & value);
|
||||
/**
|
||||
* Linear interpolation between two points on the CDF to estimate
|
||||
* \brief Sample the CDF as a histogram (without interpolation).
|
||||
* \param [in] r The CDF value at which to sample the CDF.
|
||||
* \return The bin value corresponding to \c r.
|
||||
*/
|
||||
double DoSampleCDF (double r);
|
||||
/**
|
||||
* \brief Linear interpolation between two points on the CDF to estimate
|
||||
* the value at \p r.
|
||||
*
|
||||
* \param [in] c1 The first argument value.
|
||||
* \param [in] c2 The second argument value.
|
||||
* \param [in] v1 The first CDF value.
|
||||
* \param [in] v2 The second CDF value.
|
||||
* \param [in] r The argument value to interpolate to.
|
||||
* \returns The interpolated CDF at \pname{r}
|
||||
*/
|
||||
virtual double DoInterpolate (double c1, double c2,
|
||||
double v1, double v2, double r);
|
||||
double DoInterpolate (double r);
|
||||
|
||||
/**
|
||||
* \brief Comparison operator, for use by std::upper_bound
|
||||
* \param a [in] the first value
|
||||
* \param b [in] the second value
|
||||
* \returns \c true if \c a.cdf < \c b.cdf
|
||||
*/
|
||||
friend
|
||||
bool operator < (ValueCDF a, ValueCDF b);
|
||||
|
||||
/** \c true once the CDF has been validated. */
|
||||
bool m_validated;
|
||||
/** The vector of CDF points. */
|
||||
std::vector<ValueCDF> m_emp;
|
||||
/**
|
||||
* If \c true GetValue will interpolate,
|
||||
* otherwise treat CDF as normal histogram.
|
||||
*/
|
||||
bool m_interpolate;
|
||||
|
||||
}; // class EmpiricalRandomVariable
|
||||
|
||||
|
||||
@@ -2741,26 +2741,39 @@ RandomVariableStreamEmpiricalTestCase::DoRun (void)
|
||||
|
||||
// Create the RNG with a uniform distribution between 0 and 10.
|
||||
Ptr<EmpiricalRandomVariable> x = CreateObject<EmpiricalRandomVariable> ();
|
||||
x->SetInterpolate (false);
|
||||
x->CDF ( 0.0, 0.0);
|
||||
x->CDF ( 5.0, 0.5);
|
||||
x->CDF ( 5.0, 0.25);
|
||||
x->CDF (10.0, 1.0);
|
||||
|
||||
// Calculate the mean of these values.
|
||||
double sum = 0.0;
|
||||
double value;
|
||||
// Check that only the correct values are returned
|
||||
for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
|
||||
{
|
||||
value = x->GetValue ();
|
||||
double value = x->GetValue ();
|
||||
NS_TEST_EXPECT_MSG_EQ ( (value == 5) || (value == 10), true,
|
||||
"Incorrect value returned, expected only 5 or 10.");
|
||||
}
|
||||
|
||||
// Calculate the mean of the interpolated values.
|
||||
double sum = 0.0;
|
||||
for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
|
||||
{
|
||||
double value = x->Interpolate ();
|
||||
sum += value;
|
||||
}
|
||||
double valueMean = sum / N_MEASUREMENTS;
|
||||
|
||||
// The expected distribution (with interpolation) is
|
||||
// Domain Probability
|
||||
// [0, 5) 25%
|
||||
// [5, 10) 75%
|
||||
//
|
||||
// The expected value for the mean of the values returned by this
|
||||
// empirical distribution is the midpoint of the distribution
|
||||
// empirical distribution is
|
||||
//
|
||||
// E[value] = 5 .
|
||||
// E[value] = 2.5 * 25% + 7.5 * 75% = 6.25
|
||||
//
|
||||
double expectedMean = 5.0;
|
||||
double expectedMean = 6.25;
|
||||
|
||||
// Test that values have approximately the right mean value.
|
||||
double TOLERANCE = expectedMean * 1e-2;
|
||||
@@ -2768,6 +2781,7 @@ RandomVariableStreamEmpiricalTestCase::DoRun (void)
|
||||
|
||||
// Bug 2082: Create the RNG with a uniform distribution between -1 and 1.
|
||||
Ptr<EmpiricalRandomVariable> y = CreateObject<EmpiricalRandomVariable> ();
|
||||
y->SetInterpolate (false);
|
||||
y->CDF (-1.0, 0.0);
|
||||
y->CDF (0.0, 0.5);
|
||||
y->CDF (1.0, 1.0);
|
||||
@@ -2803,29 +2817,38 @@ RandomVariableStreamEmpiricalAntitheticTestCase::DoRun (void)
|
||||
|
||||
// Create the RNG with a uniform distribution between 0 and 10.
|
||||
Ptr<EmpiricalRandomVariable> x = CreateObject<EmpiricalRandomVariable> ();
|
||||
x->SetInterpolate (false);
|
||||
x->CDF ( 0.0, 0.0);
|
||||
x->CDF ( 5.0, 0.5);
|
||||
x->CDF ( 5.0, 0.25);
|
||||
x->CDF (10.0, 1.0);
|
||||
|
||||
// Make this generate antithetic values.
|
||||
x->SetAttribute ("Antithetic", BooleanValue (true));
|
||||
|
||||
// Check that only the correct values are returned
|
||||
for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
|
||||
{
|
||||
double value = x->GetValue ();
|
||||
NS_TEST_EXPECT_MSG_EQ ( (value == 5) || (value == 10), true,
|
||||
"Incorrect value returned, expected only 5 or 10.");
|
||||
}
|
||||
|
||||
// Calculate the mean of these values.
|
||||
double sum = 0.0;
|
||||
double value;
|
||||
for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
|
||||
{
|
||||
value = x->GetValue ();
|
||||
value = x->Interpolate ();
|
||||
sum += value;
|
||||
}
|
||||
double valueMean = sum / N_MEASUREMENTS;
|
||||
|
||||
// The expected value for the mean of the values returned by this
|
||||
// empirical distribution is the midpoint of the distribution
|
||||
// empirical distribution is
|
||||
//
|
||||
// E[value] = 5 .
|
||||
// E[value] = 2.5 * 25% + 7.5 * 75% = 6.25
|
||||
//
|
||||
double expectedMean = 5.0;
|
||||
double expectedMean = 6.25;
|
||||
|
||||
// Test that values have approximately the right mean value.
|
||||
double TOLERANCE = expectedMean * 1e-2;
|
||||
@@ -2841,41 +2864,41 @@ public:
|
||||
RandomVariableStreamTestSuite::RandomVariableStreamTestSuite ()
|
||||
: TestSuite ("random-variable-stream-generators", UNIT)
|
||||
{
|
||||
AddTestCase (new RandomVariableStreamUniformTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamUniformAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamConstantTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamSequentialTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamNormalTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamNormalAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamExponentialTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamExponentialAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamParetoTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamParetoAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamWeibullTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamWeibullAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamLogNormalTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamUniformTestCase);
|
||||
AddTestCase (new RandomVariableStreamUniformAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamConstantTestCase);
|
||||
AddTestCase (new RandomVariableStreamSequentialTestCase);
|
||||
AddTestCase (new RandomVariableStreamNormalTestCase);
|
||||
AddTestCase (new RandomVariableStreamNormalAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamExponentialTestCase);
|
||||
AddTestCase (new RandomVariableStreamExponentialAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamParetoTestCase);
|
||||
AddTestCase (new RandomVariableStreamParetoAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamWeibullTestCase);
|
||||
AddTestCase (new RandomVariableStreamWeibullAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamLogNormalTestCase);
|
||||
/// \todo This test is currently disabled because it fails sometimes.
|
||||
/// A possible reason for the failure is that the antithetic code is
|
||||
/// not implemented properly for this log-normal case.
|
||||
/*
|
||||
AddTestCase (new RandomVariableStreamLogNormalAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamLogNormalAntitheticTestCase);
|
||||
*/
|
||||
AddTestCase (new RandomVariableStreamGammaTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamGammaTestCase);
|
||||
/// \todo This test is currently disabled because it fails sometimes.
|
||||
/// A possible reason for the failure is that the antithetic code is
|
||||
/// not implemented properly for this gamma case.
|
||||
/*
|
||||
AddTestCase (new RandomVariableStreamGammaAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamGammaAntitheticTestCase);
|
||||
*/
|
||||
AddTestCase (new RandomVariableStreamErlangTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamErlangAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamZipfTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamZipfAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamZetaTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamZetaAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamDeterministicTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamEmpiricalTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamEmpiricalAntitheticTestCase, TestCase::QUICK);
|
||||
AddTestCase (new RandomVariableStreamErlangTestCase);
|
||||
AddTestCase (new RandomVariableStreamErlangAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamZipfTestCase);
|
||||
AddTestCase (new RandomVariableStreamZipfAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamZetaTestCase);
|
||||
AddTestCase (new RandomVariableStreamZetaAntitheticTestCase);
|
||||
AddTestCase (new RandomVariableStreamDeterministicTestCase);
|
||||
AddTestCase (new RandomVariableStreamEmpiricalTestCase);
|
||||
AddTestCase (new RandomVariableStreamEmpiricalAntitheticTestCase);
|
||||
}
|
||||
|
||||
static RandomVariableStreamTestSuite randomVariableStreamTestSuite;
|
||||
|
||||
Reference in New Issue
Block a user