/* * Copyright (c) 2006 Georgia Tech Research Corporation * Copyright (c) 2011 Mathieu Lacage * * SPDX-License-Identifier: GPL-2.0-only * * Authors: Rajib Bhattacharjea * Hadi Arbabi * Mathieu Lacage * * Modified by Mitch Watrous * */ #include "random-variable-stream.h" #include "assert.h" #include "boolean.h" #include "double.h" #include "integer.h" #include "log.h" #include "pointer.h" #include "rng-seed-manager.h" #include "rng-stream.h" #include "string.h" #include "uinteger.h" #include // upper_bound #include #include #include /** * @file * @ingroup randomvariable * ns3::RandomVariableStream and related implementations */ namespace ns3 { NS_LOG_COMPONENT_DEFINE("RandomVariableStream"); NS_OBJECT_ENSURE_REGISTERED(RandomVariableStream); TypeId RandomVariableStream::GetTypeId() { static TypeId tid = TypeId("ns3::RandomVariableStream") .SetParent() .SetGroupName("Core") .AddAttribute("Stream", "The stream number for this RNG stream. -1 means " "\"allocate a stream automatically\". " "Note that if -1 is set, Get will return -1 so that it " "is not possible to know which " "value was automatically allocated.", IntegerValue(-1), MakeIntegerAccessor(&RandomVariableStream::SetStream, &RandomVariableStream::GetStream), MakeIntegerChecker()) .AddAttribute("Antithetic", "Set this RNG stream to generate antithetic values", BooleanValue(false), MakeBooleanAccessor(&RandomVariableStream::SetAntithetic, &RandomVariableStream::IsAntithetic), MakeBooleanChecker()); return tid; } RandomVariableStream::RandomVariableStream() : m_rng(nullptr) { NS_LOG_FUNCTION(this); } RandomVariableStream::~RandomVariableStream() { delete m_rng; } void RandomVariableStream::SetAntithetic(bool isAntithetic) { NS_LOG_FUNCTION(this << isAntithetic); m_isAntithetic = isAntithetic; } bool RandomVariableStream::IsAntithetic() const { return m_isAntithetic; } uint32_t RandomVariableStream::GetInteger() { auto value = static_cast(GetValue()); NS_LOG_DEBUG(GetInstanceTypeId().GetName() << " integer value: " << value << " stream: " << GetStream()); return value; } void RandomVariableStream::SetStream(int64_t stream) { NS_LOG_FUNCTION(this << stream); // negative values are not legal. NS_ASSERT(stream >= -1); delete m_rng; if (stream == -1) { // The first 2^63 streams are reserved for automatic stream // number assignment. uint64_t nextStream = RngSeedManager::GetNextStreamIndex(); NS_ASSERT(nextStream <= ((1ULL) << 63)); NS_LOG_INFO(GetInstanceTypeId().GetName() << " automatic stream: " << nextStream); m_rng = new RngStream(RngSeedManager::GetSeed(), nextStream, RngSeedManager::GetRun()); } else { // The last 2^63 streams are reserved for deterministic stream // number assignment. uint64_t base = ((1ULL) << 63); uint64_t target = base + stream; NS_LOG_INFO(GetInstanceTypeId().GetName() << " configured stream: " << stream); m_rng = new RngStream(RngSeedManager::GetSeed(), target, RngSeedManager::GetRun()); } m_stream = stream; } int64_t RandomVariableStream::GetStream() const { return m_stream; } RngStream* RandomVariableStream::Peek() const { return m_rng; } NS_OBJECT_ENSURE_REGISTERED(UniformRandomVariable); TypeId UniformRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::UniformRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Min", "The lower bound on the values returned by this RNG stream.", DoubleValue(0), MakeDoubleAccessor(&UniformRandomVariable::m_min), MakeDoubleChecker()) .AddAttribute("Max", "The upper bound on the values returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&UniformRandomVariable::m_max), MakeDoubleChecker()); return tid; } UniformRandomVariable::UniformRandomVariable() { // m_min and m_max are initialized after constructor by attributes NS_LOG_FUNCTION(this); } double UniformRandomVariable::GetMin() const { return m_min; } double UniformRandomVariable::GetMax() const { return m_max; } double UniformRandomVariable::GetValue(double min, double max) { double v = min + Peek()->RandU01() * (max - min); if (IsAntithetic()) { v = min + (max - v); } NS_LOG_DEBUG("value: " << v << " stream: " << GetStream() << " min: " << min << " max: " << max); return v; } uint32_t UniformRandomVariable::GetInteger(uint32_t min, uint32_t max) { NS_ASSERT(min <= max); auto v = static_cast(GetValue((double)(min), (double)(max) + 1.0)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " min: " << min << " max " << max); return v; } double UniformRandomVariable::GetValue() { return GetValue(m_min, m_max); } uint32_t UniformRandomVariable::GetInteger() { auto v = static_cast(GetValue(m_min, m_max + 1)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream()); return v; } NS_OBJECT_ENSURE_REGISTERED(ConstantRandomVariable); TypeId ConstantRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::ConstantRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Constant", "The constant value returned by this RNG stream.", DoubleValue(0), MakeDoubleAccessor(&ConstantRandomVariable::m_constant), MakeDoubleChecker()); return tid; } ConstantRandomVariable::ConstantRandomVariable() { // m_constant is initialized after constructor by attributes NS_LOG_FUNCTION(this); } double ConstantRandomVariable::GetConstant() const { NS_LOG_FUNCTION(this); return m_constant; } double ConstantRandomVariable::GetValue(double constant) { NS_LOG_DEBUG("value: " << constant << " stream: " << GetStream()); return constant; } uint32_t ConstantRandomVariable::GetInteger(uint32_t constant) { NS_LOG_DEBUG("integer value: " << constant << " stream: " << GetStream()); return constant; } double ConstantRandomVariable::GetValue() { return GetValue(m_constant); } NS_OBJECT_ENSURE_REGISTERED(SequentialRandomVariable); TypeId SequentialRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::SequentialRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Min", "The first value of the sequence.", DoubleValue(0), MakeDoubleAccessor(&SequentialRandomVariable::m_min), MakeDoubleChecker()) .AddAttribute("Max", "One more than the last value of the sequence.", DoubleValue(0), MakeDoubleAccessor(&SequentialRandomVariable::m_max), MakeDoubleChecker()) .AddAttribute("Increment", "The sequence random variable increment.", StringValue("ns3::ConstantRandomVariable[Constant=1]"), MakePointerAccessor(&SequentialRandomVariable::m_increment), MakePointerChecker()) .AddAttribute("Consecutive", "The number of times each member of the sequence is repeated.", IntegerValue(1), MakeIntegerAccessor(&SequentialRandomVariable::m_consecutive), MakeIntegerChecker()); return tid; } SequentialRandomVariable::SequentialRandomVariable() : m_current(0), m_currentConsecutive(0), m_isCurrentSet(false) { // m_min, m_max, m_increment, and m_consecutive are initialized // after constructor by attributes. NS_LOG_FUNCTION(this); } double SequentialRandomVariable::GetMin() const { return m_min; } double SequentialRandomVariable::GetMax() const { return m_max; } Ptr SequentialRandomVariable::GetIncrement() const { return m_increment; } uint32_t SequentialRandomVariable::GetConsecutive() const { return m_consecutive; } double SequentialRandomVariable::GetValue() { // Set the current sequence value if it hasn't been set. if (!m_isCurrentSet) { // Start the sequence at its minimum value. m_current = m_min; m_isCurrentSet = true; } // Return a sequential series of values double r = m_current; if (++m_currentConsecutive == m_consecutive) { // Time to advance to next m_currentConsecutive = 0; m_current += m_increment->GetValue(); if (m_current >= m_max) { m_current = m_min + (m_current - m_max); } } NS_LOG_DEBUG("value: " << r << " stream: " << GetStream()); return r; } NS_OBJECT_ENSURE_REGISTERED(ExponentialRandomVariable); TypeId ExponentialRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::ExponentialRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Mean", "The mean of the values returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&ExponentialRandomVariable::m_mean), MakeDoubleChecker()) .AddAttribute("Bound", "The upper bound on the values returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&ExponentialRandomVariable::m_bound), MakeDoubleChecker()); return tid; } ExponentialRandomVariable::ExponentialRandomVariable() { // m_mean and m_bound are initialized after constructor by attributes NS_LOG_FUNCTION(this); } double ExponentialRandomVariable::GetMean() const { return m_mean; } double ExponentialRandomVariable::GetBound() const { return m_bound; } double ExponentialRandomVariable::GetValue(double mean, double bound) { while (true) { // Get a uniform random variable in [0,1]. double v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } // Calculate the exponential random variable. double r = -mean * std::log(v); // Use this value if it's acceptable. if (bound == 0 || r <= bound) { NS_LOG_DEBUG("value: " << r << " stream: " << GetStream() << " mean: " << mean << " bound: " << bound); return r; } } } uint32_t ExponentialRandomVariable::GetInteger(uint32_t mean, uint32_t bound) { NS_LOG_FUNCTION(this << mean << bound); auto v = static_cast(GetValue(mean, bound)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " mean: " << mean << " bound: " << bound); return v; } double ExponentialRandomVariable::GetValue() { return GetValue(m_mean, m_bound); } NS_OBJECT_ENSURE_REGISTERED(ParetoRandomVariable); TypeId ParetoRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::ParetoRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute( "Scale", "The scale parameter for the Pareto distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&ParetoRandomVariable::m_scale), MakeDoubleChecker()) .AddAttribute( "Shape", "The shape parameter for the Pareto distribution returned by this RNG stream.", DoubleValue(2.0), MakeDoubleAccessor(&ParetoRandomVariable::m_shape), MakeDoubleChecker()) .AddAttribute( "Bound", "The upper bound on the values returned by this RNG stream (if non-zero).", DoubleValue(0.0), MakeDoubleAccessor(&ParetoRandomVariable::m_bound), MakeDoubleChecker()); return tid; } ParetoRandomVariable::ParetoRandomVariable() { // m_shape, m_shape, and m_bound are initialized after constructor // by attributes NS_LOG_FUNCTION(this); } double ParetoRandomVariable::GetScale() const { return m_scale; } double ParetoRandomVariable::GetShape() const { return m_shape; } double ParetoRandomVariable::GetBound() const { return m_bound; } double ParetoRandomVariable::GetValue(double scale, double shape, double bound) { while (true) { // Get a uniform random variable in [0,1]. double v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } // Calculate the Pareto random variable. double r = (scale * (1.0 / std::pow(v, 1.0 / shape))); // Use this value if it's acceptable. if (bound == 0 || r <= bound) { NS_LOG_DEBUG("value: " << r << " stream: " << GetStream() << " scale: " << scale << " shape: " << shape << " bound: " << bound); return r; } } } uint32_t ParetoRandomVariable::GetInteger(uint32_t scale, uint32_t shape, uint32_t bound) { auto v = static_cast(GetValue(scale, shape, bound)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " scale: " << scale << " shape: " << shape << " bound: " << bound); return v; } double ParetoRandomVariable::GetValue() { return GetValue(m_scale, m_shape, m_bound); } NS_OBJECT_ENSURE_REGISTERED(WeibullRandomVariable); TypeId WeibullRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::WeibullRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute( "Scale", "The scale parameter for the Weibull distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&WeibullRandomVariable::m_scale), MakeDoubleChecker()) .AddAttribute( "Shape", "The shape parameter for the Weibull distribution returned by this RNG stream.", DoubleValue(1), MakeDoubleAccessor(&WeibullRandomVariable::m_shape), MakeDoubleChecker()) .AddAttribute("Bound", "The upper bound on the values returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&WeibullRandomVariable::m_bound), MakeDoubleChecker()); return tid; } WeibullRandomVariable::WeibullRandomVariable() { // m_scale, m_shape, and m_bound are initialized after constructor // by attributes NS_LOG_FUNCTION(this); } double WeibullRandomVariable::GetScale() const { return m_scale; } double WeibullRandomVariable::GetShape() const { return m_shape; } double WeibullRandomVariable::GetBound() const { return m_bound; } double WeibullRandomVariable::GetMean(double scale, double shape) { NS_LOG_FUNCTION(scale << shape); return scale * std::tgamma(1 + (1 / shape)); } double WeibullRandomVariable::GetMean() const { NS_LOG_FUNCTION(this); return GetMean(m_scale, m_shape); } double WeibullRandomVariable::GetValue(double scale, double shape, double bound) { double exponent = 1.0 / shape; while (true) { // Get a uniform random variable in [0,1]. double v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } // Calculate the Weibull random variable. double r = scale * std::pow(-std::log(v), exponent); // Use this value if it's acceptable. if (bound == 0 || r <= bound) { NS_LOG_DEBUG("value: " << r << " stream: " << GetStream() << " scale: " << scale << " shape: " << shape << " bound: " << bound); return r; } } } uint32_t WeibullRandomVariable::GetInteger(uint32_t scale, uint32_t shape, uint32_t bound) { auto v = static_cast(GetValue(scale, shape, bound)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " scale: " << scale << " shape: " << shape << " bound: " << bound); return v; } double WeibullRandomVariable::GetValue() { NS_LOG_FUNCTION(this); return GetValue(m_scale, m_shape, m_bound); } NS_OBJECT_ENSURE_REGISTERED(NormalRandomVariable); const double NormalRandomVariable::INFINITE_VALUE = 1e307; TypeId NormalRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::NormalRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Mean", "The mean value for the normal distribution returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&NormalRandomVariable::m_mean), MakeDoubleChecker()) .AddAttribute( "Variance", "The variance value for the normal distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&NormalRandomVariable::m_variance), MakeDoubleChecker()) .AddAttribute("Bound", "The bound on the values returned by this RNG stream.", DoubleValue(INFINITE_VALUE), MakeDoubleAccessor(&NormalRandomVariable::m_bound), MakeDoubleChecker()); return tid; } NormalRandomVariable::NormalRandomVariable() : m_nextValid(false) { // m_mean, m_variance, and m_bound are initialized after constructor // by attributes NS_LOG_FUNCTION(this); } double NormalRandomVariable::GetMean() const { return m_mean; } double NormalRandomVariable::GetVariance() const { return m_variance; } double NormalRandomVariable::GetBound() const { return m_bound; } double NormalRandomVariable::GetValue(double mean, double variance, double bound) { if (m_nextValid) { // use previously generated m_nextValid = false; double x2 = mean + m_v2 * m_y * std::sqrt(variance); if (std::fabs(x2 - mean) <= bound) { NS_LOG_DEBUG("value: " << x2 << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return x2; } } while (true) { // See Simulation Modeling and Analysis p. 466 (Averill Law) // for algorithm; basically a Box-Muller transform: // http://en.wikipedia.org/wiki/Box-Muller_transform double u1 = Peek()->RandU01(); double u2 = Peek()->RandU01(); if (IsAntithetic()) { u1 = (1 - u1); u2 = (1 - u2); } double v1 = 2 * u1 - 1; double v2 = 2 * u2 - 1; double w = v1 * v1 + v2 * v2; if (w <= 1.0) { // Got good pair double y = std::sqrt((-2 * std::log(w)) / w); double x1 = mean + v1 * y * std::sqrt(variance); // if x1 is in bounds, return it, cache v2 and y if (std::fabs(x1 - mean) <= bound) { m_nextValid = true; m_y = y; m_v2 = v2; NS_LOG_DEBUG("value: " << x1 << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return x1; } // otherwise try and return the other if it is valid double x2 = mean + v2 * y * std::sqrt(variance); if (std::fabs(x2 - mean) <= bound) { m_nextValid = false; NS_LOG_DEBUG("value: " << x2 << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return x2; } // otherwise, just run this loop again } } } uint32_t NormalRandomVariable::GetInteger(uint32_t mean, uint32_t variance, uint32_t bound) { auto v = static_cast(GetValue(mean, variance, bound)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return v; } double NormalRandomVariable::GetValue() { return GetValue(m_mean, m_variance, m_bound); } NS_OBJECT_ENSURE_REGISTERED(LogNormalRandomVariable); TypeId LogNormalRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::LogNormalRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute( "Mu", "The mu value for the log-normal distribution returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&LogNormalRandomVariable::m_mu), MakeDoubleChecker()) .AddAttribute( "Sigma", "The sigma value for the log-normal distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&LogNormalRandomVariable::m_sigma), MakeDoubleChecker()); return tid; } LogNormalRandomVariable::LogNormalRandomVariable() : m_nextValid(false) { // m_mu and m_sigma are initialized after constructor by // attributes NS_LOG_FUNCTION(this); } double LogNormalRandomVariable::GetMu() const { return m_mu; } double LogNormalRandomVariable::GetSigma() const { return m_sigma; } // The code from this function was adapted from the GNU Scientific // Library 1.8: /* randist/lognormal.c * * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough * * SPDX-License-Identifier: GPL-2.0-or-later */ /* The lognormal distribution has the form p(x) dx = 1/(x * sqrt(2 pi sigma^2)) exp(-(ln(x) - zeta)^2/2 sigma^2) dx for x > 0. Lognormal random numbers are the exponentials of gaussian random numbers */ double LogNormalRandomVariable::GetValue(double mu, double sigma) { if (m_nextValid) { // use previously generated m_nextValid = false; double v = std::exp(sigma * m_v2 * m_normal + mu); NS_LOG_DEBUG("value: " << v << " stream: " << GetStream() << " mu: " << mu << " sigma: " << sigma); return v; } double v1; double v2; double r2; double normal; double x; do { /* choose x,y in uniform square (-1,-1) to (+1,+1) */ double u1 = Peek()->RandU01(); double u2 = Peek()->RandU01(); if (IsAntithetic()) { u1 = (1 - u1); u2 = (1 - u2); } v1 = -1 + 2 * u1; v2 = -1 + 2 * u2; /* see if it is in the unit circle */ r2 = v1 * v1 + v2 * v2; } while (r2 > 1.0 || r2 == 0); m_normal = std::sqrt(-2.0 * std::log(r2) / r2); normal = v1 * m_normal; m_nextValid = true; m_v2 = v2; x = std::exp(sigma * normal + mu); NS_LOG_DEBUG("value: " << x << " stream: " << GetStream() << " mu: " << mu << " sigma: " << sigma); return x; } uint32_t LogNormalRandomVariable::GetInteger(uint32_t mu, uint32_t sigma) { auto v = static_cast(GetValue(mu, sigma)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " mu: " << mu << " sigma: " << sigma); return v; } double LogNormalRandomVariable::GetValue() { return GetValue(m_mu, m_sigma); } NS_OBJECT_ENSURE_REGISTERED(GammaRandomVariable); TypeId GammaRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::GammaRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Alpha", "The alpha value for the gamma distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&GammaRandomVariable::m_alpha), MakeDoubleChecker()) .AddAttribute("Beta", "The beta value for the gamma distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&GammaRandomVariable::m_beta), MakeDoubleChecker()); return tid; } GammaRandomVariable::GammaRandomVariable() : m_nextValid(false) { // m_alpha and m_beta are initialized after constructor by // attributes NS_LOG_FUNCTION(this); } double GammaRandomVariable::GetAlpha() const { return m_alpha; } double GammaRandomVariable::GetBeta() const { return m_beta; } /* The code for the following generator functions was adapted from ns-2 tools/ranvar.cc Originally the algorithm was devised by Marsaglia in 2000: G. Marsaglia, W. W. Tsang: A simple method for generating Gamma variables ACM Transactions on mathematical software, Vol. 26, No. 3, Sept. 2000 The Gamma distribution density function has the form x^(alpha-1) * exp(-x/beta) p(x; alpha, beta) = ---------------------------- beta^alpha * Gamma(alpha) for x > 0. */ double GammaRandomVariable::GetValue(double alpha, double beta) { if (alpha < 1) { double u = Peek()->RandU01(); if (IsAntithetic()) { u = (1 - u); } double v = GetValue(1.0 + alpha, beta) * std::pow(u, 1.0 / alpha); NS_LOG_DEBUG("value: " << v << " stream: " << GetStream() << " alpha: " << alpha << " beta: " << beta); return GetValue(1.0 + alpha, beta) * std::pow(u, 1.0 / alpha); } double x; double v; double u; double d = alpha - 1.0 / 3.0; double c = (1.0 / 3.0) / std::sqrt(d); while (true) { do { // Get a value from a normal distribution that has mean // zero, variance 1, and no bound. double mean = 0.0; double variance = 1.0; double bound = NormalRandomVariable::INFINITE_VALUE; x = GetNormalValue(mean, variance, bound); v = 1.0 + c * x; } while (v <= 0); v = v * v * v; u = Peek()->RandU01(); if (IsAntithetic()) { u = (1 - u); } if (u < 1 - 0.0331 * x * x * x * x) { break; } if (std::log(u) < 0.5 * x * x + d * (1 - v + std::log(v))) { break; } } double value = beta * d * v; NS_LOG_DEBUG("value: " << value << " stream: " << GetStream() << " alpha: " << alpha << " beta: " << beta); return value; } double GammaRandomVariable::GetValue() { return GetValue(m_alpha, m_beta); } double GammaRandomVariable::GetNormalValue(double mean, double variance, double bound) { if (m_nextValid) { // use previously generated m_nextValid = false; double x2 = mean + m_v2 * m_y * std::sqrt(variance); if (std::fabs(x2 - mean) <= bound) { NS_LOG_DEBUG("value: " << x2 << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return x2; } } while (true) { // See Simulation Modeling and Analysis p. 466 (Averill Law) // for algorithm; basically a Box-Muller transform: // http://en.wikipedia.org/wiki/Box-Muller_transform double u1 = Peek()->RandU01(); double u2 = Peek()->RandU01(); if (IsAntithetic()) { u1 = (1 - u1); u2 = (1 - u2); } double v1 = 2 * u1 - 1; double v2 = 2 * u2 - 1; double w = v1 * v1 + v2 * v2; if (w <= 1.0) { // Got good pair double y = std::sqrt((-2 * std::log(w)) / w); double x1 = mean + v1 * y * std::sqrt(variance); // if x1 is in bounds, return it, cache v2 an y if (std::fabs(x1 - mean) <= bound) { m_nextValid = true; m_y = y; m_v2 = v2; NS_LOG_DEBUG("value: " << x1 << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return x1; } // otherwise try and return the other if it is valid double x2 = mean + v2 * y * std::sqrt(variance); if (std::fabs(x2 - mean) <= bound) { m_nextValid = false; NS_LOG_DEBUG("value: " << x2 << " stream: " << GetStream() << " mean: " << mean << " variance: " << variance << " bound: " << bound); return x2; } // otherwise, just run this loop again } } } NS_OBJECT_ENSURE_REGISTERED(ErlangRandomVariable); TypeId ErlangRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::ErlangRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("K", "The k value for the Erlang distribution returned by this RNG stream.", IntegerValue(1), MakeIntegerAccessor(&ErlangRandomVariable::m_k), MakeIntegerChecker()) .AddAttribute( "Lambda", "The lambda value for the Erlang distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&ErlangRandomVariable::m_lambda), MakeDoubleChecker()); return tid; } ErlangRandomVariable::ErlangRandomVariable() { // m_k and m_lambda are initialized after constructor by attributes NS_LOG_FUNCTION(this); } uint32_t ErlangRandomVariable::GetK() const { return m_k; } double ErlangRandomVariable::GetLambda() const { return m_lambda; } /* The code for the following generator functions was adapted from ns-2 tools/ranvar.cc The Erlang distribution density function has the form x^(k-1) * exp(-x/lambda) p(x; k, lambda) = --------------------------- lambda^k * (k-1)! for x > 0. */ double ErlangRandomVariable::GetValue(uint32_t k, double lambda) { double mean = lambda; double bound = 0.0; double result = 0; for (unsigned int i = 0; i < k; ++i) { result += GetExponentialValue(mean, bound); } NS_LOG_DEBUG("value: " << result << " stream: " << GetStream() << " k: " << k << " lambda: " << lambda); return result; } uint32_t ErlangRandomVariable::GetInteger(uint32_t k, uint32_t lambda) { auto v = static_cast(GetValue(k, lambda)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " k: " << k << " lambda: " << lambda); return v; } double ErlangRandomVariable::GetValue() { return GetValue(m_k, m_lambda); } double ErlangRandomVariable::GetExponentialValue(double mean, double bound) { while (true) { // Get a uniform random variable in [0,1]. double v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } // Calculate the exponential random variable. double r = -mean * std::log(v); // Use this value if it's acceptable. if (bound == 0 || r <= bound) { NS_LOG_DEBUG("value: " << r << " stream: " << GetStream() << " mean:: " << mean << " bound: " << bound); return r; } } } NS_OBJECT_ENSURE_REGISTERED(TriangularRandomVariable); TypeId TriangularRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::TriangularRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute( "Mean", "The mean value for the triangular distribution returned by this RNG stream.", DoubleValue(0.5), MakeDoubleAccessor(&TriangularRandomVariable::m_mean), MakeDoubleChecker()) .AddAttribute("Min", "The lower bound on the values returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&TriangularRandomVariable::m_min), MakeDoubleChecker()) .AddAttribute("Max", "The upper bound on the values returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&TriangularRandomVariable::m_max), MakeDoubleChecker()); return tid; } TriangularRandomVariable::TriangularRandomVariable() { // m_mean, m_min, and m_max are initialized after constructor by // attributes NS_LOG_FUNCTION(this); } double TriangularRandomVariable::GetMean() const { return m_mean; } double TriangularRandomVariable::GetMin() const { return m_min; } double TriangularRandomVariable::GetMax() const { return m_max; } double TriangularRandomVariable::GetValue(double mean, double min, double max) { // Calculate the mode. double mode = 3.0 * mean - min - max; // Get a uniform random variable in [0,1]. double u = Peek()->RandU01(); if (IsAntithetic()) { u = (1 - u); } // Calculate the triangular random variable. if (u <= (mode - min) / (max - min)) { double v = min + std::sqrt(u * (max - min) * (mode - min)); NS_LOG_DEBUG("value: " << v << " stream: " << GetStream() << " mean: " << mean << " min: " << min << " max: " << max); return v; } else { double v = max - std::sqrt((1 - u) * (max - min) * (max - mode)); NS_LOG_DEBUG("value: " << v << " stream: " << GetStream() << " mean: " << mean << " min: " << min << " max: " << max); return v; } } uint32_t TriangularRandomVariable::GetInteger(uint32_t mean, uint32_t min, uint32_t max) { auto v = static_cast(GetValue(mean, min, max)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " mean: " << mean << " min: " << min << " max: " << max); return v; } double TriangularRandomVariable::GetValue() { return GetValue(m_mean, m_min, m_max); } NS_OBJECT_ENSURE_REGISTERED(ZipfRandomVariable); TypeId ZipfRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::ZipfRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("N", "The n value for the Zipf distribution returned by this RNG stream.", IntegerValue(1), MakeIntegerAccessor(&ZipfRandomVariable::m_n), MakeIntegerChecker()) .AddAttribute("Alpha", "The alpha value for the Zipf distribution returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&ZipfRandomVariable::m_alpha), MakeDoubleChecker()); return tid; } ZipfRandomVariable::ZipfRandomVariable() { // m_n and m_alpha are initialized after constructor by attributes NS_LOG_FUNCTION(this); } uint32_t ZipfRandomVariable::GetN() const { return m_n; } double ZipfRandomVariable::GetAlpha() const { return m_alpha; } double ZipfRandomVariable::GetValue(uint32_t n, double alpha) { // Calculate the normalization constant c. m_c = 0.0; for (uint32_t i = 1; i <= n; i++) { m_c += (1.0 / std::pow((double)i, alpha)); } m_c = 1.0 / m_c; // Get a uniform random variable in [0,1]. double u = Peek()->RandU01(); if (IsAntithetic()) { u = (1 - u); } double sum_prob = 0; double zipf_value = 0; for (uint32_t i = 1; i <= n; i++) { sum_prob += m_c / std::pow((double)i, alpha); if (sum_prob > u) { zipf_value = i; break; } } NS_LOG_DEBUG("value: " << zipf_value << " stream: " << GetStream() << " n: " << n << " alpha: " << alpha); return zipf_value; } uint32_t ZipfRandomVariable::GetInteger(uint32_t n, uint32_t alpha) { NS_LOG_FUNCTION(this << n << alpha); auto v = static_cast(GetValue(n, alpha)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " n: " << n << " alpha: " << alpha); return v; } double ZipfRandomVariable::GetValue() { return GetValue(m_n, m_alpha); } NS_OBJECT_ENSURE_REGISTERED(ZetaRandomVariable); TypeId ZetaRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::ZetaRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Alpha", "The alpha value for the zeta distribution returned by this RNG stream.", DoubleValue(3.14), MakeDoubleAccessor(&ZetaRandomVariable::m_alpha), MakeDoubleChecker()); return tid; } ZetaRandomVariable::ZetaRandomVariable() { // m_alpha is initialized after constructor by attributes NS_LOG_FUNCTION(this); } double ZetaRandomVariable::GetAlpha() const { return m_alpha; } double ZetaRandomVariable::GetValue(double alpha) { m_b = std::pow(2.0, alpha - 1.0); double u; double v; double X; double T; double test; do { // Get a uniform random variable in [0,1]. u = Peek()->RandU01(); if (IsAntithetic()) { u = (1 - u); } // Get a uniform random variable in [0,1]. v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } X = std::floor(std::pow(u, -1.0 / (alpha - 1.0))); T = std::pow(1.0 + 1.0 / X, alpha - 1.0); test = v * X * (T - 1.0) / (m_b - 1.0); } while (test > (T / m_b)); NS_LOG_DEBUG("value: " << X << " stream: " << GetStream() << " alpha: " << alpha); return X; } uint32_t ZetaRandomVariable::GetInteger(uint32_t alpha) { auto v = static_cast(GetValue(alpha)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " alpha: " << alpha); return v; } double ZetaRandomVariable::GetValue() { return GetValue(m_alpha); } NS_OBJECT_ENSURE_REGISTERED(DeterministicRandomVariable); TypeId DeterministicRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::DeterministicRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor(); return tid; } DeterministicRandomVariable::DeterministicRandomVariable() : m_count(0), m_next(0), m_data(nullptr) { NS_LOG_FUNCTION(this); } DeterministicRandomVariable::~DeterministicRandomVariable() { // Delete any values currently set. NS_LOG_FUNCTION(this); if (m_data != nullptr) { delete[] m_data; } } void DeterministicRandomVariable::SetValueArray(const std::vector& values) { SetValueArray(values.data(), values.size()); } void DeterministicRandomVariable::SetValueArray(const double* values, std::size_t length) { NS_LOG_FUNCTION(this << values << length); // Delete any values currently set. if (m_data != nullptr) { delete[] m_data; } // Make room for the values being set. m_data = new double[length]; m_count = length; m_next = length; // Copy the values. for (std::size_t i = 0; i < m_count; i++) { m_data[i] = values[i]; } } double DeterministicRandomVariable::GetValue() { // Make sure the array has been set. NS_ASSERT(m_count > 0); if (m_next == m_count) { m_next = 0; } double v = m_data[m_next++]; NS_LOG_DEBUG("value: " << v << " stream: " << GetStream()); return v; } NS_OBJECT_ENSURE_REGISTERED(EmpiricalRandomVariable); TypeId EmpiricalRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::EmpiricalRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .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) { NS_LOG_FUNCTION(this); } bool EmpiricalRandomVariable::SetInterpolate(bool interpolate) { NS_LOG_FUNCTION(this << interpolate); bool prev = m_interpolate; m_interpolate = interpolate; return prev; } bool EmpiricalRandomVariable::PreSample(double& value) { NS_LOG_FUNCTION(this << value); if (!m_validated) { Validate(); } // 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_empCdf.begin()->first) { value = m_empCdf.begin()->second; // Less than first valid = true; } else if (r >= m_empCdf.rbegin()->first) { value = m_empCdf.rbegin()->second; // Greater than last valid = true; } return valid; } double EmpiricalRandomVariable::GetValue() { double value; if (PreSample(value)) { return value; } // value now has the (unused) URNG selector if (m_interpolate) { value = DoInterpolate(value); } else { value = DoSampleCDF(value); } NS_LOG_DEBUG("value: " << value << " stream: " << GetStream()); return value; } double EmpiricalRandomVariable::DoSampleCDF(double r) { NS_LOG_FUNCTION(this << r); // Find first CDF that is greater than r auto bound = m_empCdf.upper_bound(r); return bound->second; } double EmpiricalRandomVariable::Interpolate() { NS_LOG_FUNCTION(this); double value; if (PreSample(value)) { return value; } // value now has the (unused) URNG selector value = DoInterpolate(value); return value; } double EmpiricalRandomVariable::DoInterpolate(double r) { NS_LOG_FUNCTION(this << r); // Return a value from the empirical distribution // This code based (loosely) on code by Bruce Mah (Thanks Bruce!) // search auto upper = m_empCdf.upper_bound(r); auto lower = std::prev(upper, 1); if (upper == m_empCdf.begin()) { lower = upper; } // Interpolate random value in range [v1..v2) based on [c1 .. r .. c2) double c1 = lower->first; double c2 = upper->first; double v1 = lower->second; double v2 = upper->second; double value = (v1 + ((v2 - v1) / (c2 - c1)) * (r - c1)); return value; } void EmpiricalRandomVariable::CDF(double v, double c) { NS_LOG_FUNCTION(this << v << c); auto vPrevious = m_empCdf.find(c); if (vPrevious != m_empCdf.end()) { NS_LOG_WARN("Empirical CDF already has a value " << vPrevious->second << " for CDF " << c << ". Overwriting it with value " << v << "."); } m_empCdf[c] = v; } void EmpiricalRandomVariable::Validate() { NS_LOG_FUNCTION(this); if (m_empCdf.empty()) { NS_FATAL_ERROR("CDF is not initialized"); } double vPrev = m_empCdf.begin()->second; // Check if values are non-decreasing for (const auto& cdfPair : m_empCdf) { const auto& vCurr = cdfPair.second; if (vCurr < vPrev) { NS_FATAL_ERROR("Empirical distribution has decreasing CDF values. Current CDF: " << vCurr << ", prior CDF: " << vPrev); } vPrev = vCurr; } // Bounds check on CDF endpoints auto firstCdfPair = m_empCdf.begin(); auto lastCdfPair = m_empCdf.rbegin(); if (firstCdfPair->first < 0.0) { NS_FATAL_ERROR("Empirical distribution has invalid first CDF value. CDF: " << firstCdfPair->first << ", Value: " << firstCdfPair->second); } if (lastCdfPair->first > 1.0) { NS_FATAL_ERROR("Empirical distribution has invalid last CDF value. CDF: " << lastCdfPair->first << ", Value: " << lastCdfPair->second); } m_validated = true; } NS_OBJECT_ENSURE_REGISTERED(BinomialRandomVariable); TypeId BinomialRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::BinomialRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Trials", "The number of trials.", IntegerValue(10), MakeIntegerAccessor(&BinomialRandomVariable::m_trials), MakeIntegerChecker(0)) .AddAttribute("Probability", "The probability of success in each trial.", DoubleValue(0.5), MakeDoubleAccessor(&BinomialRandomVariable::m_probability), MakeDoubleChecker(0)); return tid; } BinomialRandomVariable::BinomialRandomVariable() { // m_trials and m_probability are initialized after constructor by attributes NS_LOG_FUNCTION(this); } double BinomialRandomVariable::GetValue(uint32_t trials, double probability) { double successes = 0; for (uint32_t i = 0; i < trials; ++i) { double v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } if (v <= probability) { successes += 1; } } NS_LOG_DEBUG("value: " << successes << " stream: " << GetStream() << " trials: " << trials << " probability: " << probability); return successes; } uint32_t BinomialRandomVariable::GetInteger(uint32_t trials, uint32_t probability) { auto v = static_cast(GetValue(trials, probability)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " trials: " << trials << " probability: " << probability); return v; } double BinomialRandomVariable::GetValue() { return GetValue(m_trials, m_probability); } NS_OBJECT_ENSURE_REGISTERED(BernoulliRandomVariable); TypeId BernoulliRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::BernoulliRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Probability", "The probability of the random variable returning a value of 1.", DoubleValue(0.5), MakeDoubleAccessor(&BernoulliRandomVariable::m_probability), MakeDoubleChecker(0)); return tid; } BernoulliRandomVariable::BernoulliRandomVariable() { // m_probability is initialized after constructor by attributes NS_LOG_FUNCTION(this); } double BernoulliRandomVariable::GetValue(double probability) { double v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } double value = (v <= probability) ? 1.0 : 0.0; NS_LOG_DEBUG("value: " << value << " stream: " << GetStream() << " probability: " << probability); return value; } uint32_t BernoulliRandomVariable::GetInteger(uint32_t probability) { auto v = static_cast(GetValue(probability)); NS_LOG_DEBUG("integer value: " << v << " stream: " << GetStream() << " probability: " << probability); return v; } double BernoulliRandomVariable::GetValue() { return GetValue(m_probability); } NS_OBJECT_ENSURE_REGISTERED(LaplacianRandomVariable); TypeId LaplacianRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::LaplacianRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Location", "The location parameter for the Laplacian distribution returned by this " "RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&LaplacianRandomVariable::m_location), MakeDoubleChecker()) .AddAttribute( "Scale", "The scale parameter for the Laplacian distribution returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&LaplacianRandomVariable::m_scale), MakeDoubleChecker()) .AddAttribute("Bound", "The bound on the values returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&LaplacianRandomVariable::m_bound), MakeDoubleChecker()); return tid; } LaplacianRandomVariable::LaplacianRandomVariable() { NS_LOG_FUNCTION(this); } double LaplacianRandomVariable::GetLocation() const { return m_location; } double LaplacianRandomVariable::GetScale() const { return m_scale; } double LaplacianRandomVariable::GetBound() const { return m_bound; } double LaplacianRandomVariable::GetValue(double location, double scale, double bound) { NS_LOG_FUNCTION(this << location << scale << bound); NS_ABORT_MSG_IF(scale <= 0, "Scale parameter should be larger than 0"); while (true) { // Get a uniform random variable in [-0.5,0.5]. auto v = (Peek()->RandU01() - 0.5); if (IsAntithetic()) { v = (1 - v); } // Calculate the laplacian random variable. const auto sgn = (v > 0) ? 1 : ((v < 0) ? -1 : 0); const auto r = location - (scale * sgn * std::log(1.0 - (2.0 * std::abs(v)))); // Use this value if it's acceptable. if (bound == 0.0 || std::fabs(r - location) <= bound) { return r; } } } uint32_t LaplacianRandomVariable::GetInteger(uint32_t location, uint32_t scale, uint32_t bound) { NS_LOG_FUNCTION(this << location << scale << bound); return static_cast(GetValue(location, scale, bound)); } double LaplacianRandomVariable::GetValue() { NS_LOG_FUNCTION(this); return GetValue(m_location, m_scale, m_bound); } double LaplacianRandomVariable::GetVariance(double scale) { NS_LOG_FUNCTION(scale); return 2.0 * std::pow(scale, 2.0); } double LaplacianRandomVariable::GetVariance() const { return GetVariance(m_scale); } NS_OBJECT_ENSURE_REGISTERED(LargestExtremeValueRandomVariable); TypeId LargestExtremeValueRandomVariable::GetTypeId() { static TypeId tid = TypeId("ns3::LargestExtremeValueRandomVariable") .SetParent() .SetGroupName("Core") .AddConstructor() .AddAttribute("Location", "The location parameter for the Largest Extreme Value distribution " "returned by this RNG stream.", DoubleValue(0.0), MakeDoubleAccessor(&LargestExtremeValueRandomVariable::m_location), MakeDoubleChecker()) .AddAttribute("Scale", "The scale parameter for the Largest Extreme Value distribution " "returned by this RNG stream.", DoubleValue(1.0), MakeDoubleAccessor(&LargestExtremeValueRandomVariable::m_scale), MakeDoubleChecker()); return tid; } LargestExtremeValueRandomVariable::LargestExtremeValueRandomVariable() { NS_LOG_FUNCTION(this); } double LargestExtremeValueRandomVariable::GetLocation() const { return m_location; } double LargestExtremeValueRandomVariable::GetScale() const { return m_scale; } double LargestExtremeValueRandomVariable::GetValue(double location, double scale) { NS_LOG_FUNCTION(this << location << scale); NS_ABORT_MSG_IF(scale <= 0, "Scale parameter should be larger than 0"); // Get a uniform random variable in [0,1]. auto v = Peek()->RandU01(); if (IsAntithetic()) { v = (1 - v); } // Calculate the largest extreme value random variable. const auto t = std::log(v) * (-1.0); const auto r = location - (scale * std::log(t)); return r; } uint32_t LargestExtremeValueRandomVariable::GetInteger(uint32_t location, uint32_t scale) { NS_LOG_FUNCTION(this << location << scale); return static_cast(GetValue(location, scale)); } double LargestExtremeValueRandomVariable::GetValue() { NS_LOG_FUNCTION(this); return GetValue(m_location, m_scale); } double LargestExtremeValueRandomVariable::GetMean(double location, double scale) { NS_LOG_FUNCTION(location << scale); return (location + (scale * std::numbers::egamma)); } double LargestExtremeValueRandomVariable::GetMean() const { return GetMean(m_location, m_scale); } double LargestExtremeValueRandomVariable::GetVariance(double scale) { NS_LOG_FUNCTION(scale); return std::pow((scale * std::numbers::pi), 2) / 6.0; } double LargestExtremeValueRandomVariable::GetVariance() const { return GetVariance(m_scale); } } // namespace ns3