LCOV - code coverage report
Current view: top level - builds/barbot/Cosmos/src/Simulator - timeGen.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 50 199 25.1 %
Date: 2021-06-16 15:43:28 Functions: 11 19 57.9 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *                                                                             *
       3             :  * Cosmos:(C)oncept et (O)utils (S)tatistique pour les (Mo)deles               *
       4             :  * (S)tochastiques                                                             *
       5             :  *                                                                             *
       6             :  * Copyright (C) 2009-2012 LSV & LACL                                          *
       7             :  * Authors: Paolo Ballarini BenoƮt Barbot & Hilal Djafri                       *
       8             :  * Website: http://www.lsv.ens-cachan.fr/Software/cosmos                       *
       9             :  *                                                                             *
      10             :  * This program is free software; you can redistribute it and/or modify        *
      11             :  * it under the terms of the GNU General Public License as published by        *
      12             :  * the Free Software Foundation; either version 3 of the License, or           *
      13             :  * (at your option) any later version.                                         *
      14             :  *                                                                             *
      15             :  * This program is distributed in the hope that it will be useful,             *
      16             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of              *
      17             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
      18             :  * GNU General Public License for more details.                                *
      19             :  *                                                                             *
      20             :  * You should have received a copy of the GNU General Public License along     *
      21             :  * with this program; if not, write to the Free Software Foundation, Inc.,     *
      22             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                 *
      23             :  * file timeGen.cpp created by Benoit Barbot on 25/01/12.                      *
      24             :  *******************************************************************************
      25             :  */
      26             : 
      27             : 
      28             : 
      29             : #include <boost/math/special_functions/prime.hpp>
      30             : #include <float.h>
      31             : #include <array>
      32             : #include <tuple>
      33             : #include <limits>
      34             : #include <math.h>
      35             : #include <random>
      36             : 
      37             : #include "timeGen.hpp"
      38             : 
      39             : /*
      40             :   Copy from Boost: boost/math/tools/roots.hpp
      41             :   //  (C) Copyright John Maddock 2006.
      42             :   //  Use, modification and distribution are subject to the
      43             :   //  Boost Software License, Version 1.0. (See accompanying file
      44             :   //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
      45             :   */
      46             : template <class F>
      47           0 : double boost_newton_raphson_iterate(F f, double guess, double min, double max, int max_iter)
      48             : {
      49           0 :   int digits = 16;
      50           0 :   double f0(0), f1;
      51           0 :   double result = guess;
      52             : 
      53           0 :   double factor = static_cast<double>(ldexp(1.0, 1 - digits));
      54           0 :   double delta = 1;
      55           0 :   double delta1 = std::numeric_limits<double>::max();
      56           0 :   double delta2 = std::numeric_limits<double>::max();
      57             : 
      58           0 :   unsigned int count(max_iter);
      59             : 
      60           0 :   do{
      61           0 :     delta2 = delta1;
      62           0 :     delta1 = delta;
      63           0 :     std::tie(f0, f1) = f(result);
      64           0 :     if(0 == f0)
      65           0 :       break;
      66           0 :     if(f1 == 0)
      67             :       {
      68           0 :         std::cerr << "Zero derivative in newton raphson iterate" << std::endl;
      69           0 :         exit(EXIT_FAILURE);
      70             :       }
      71             :     else
      72             :       {
      73           0 :         delta = f0 / f1;
      74             :       }
      75           0 :     if(fabs(delta * 2) > fabs(delta2))
      76             :       {
      77             :         // last two steps haven't converged, try bisection:
      78           0 :         delta = (delta > 0) ? (result - min) / 2 : (result - max) / 2;
      79             :       }
      80           0 :     guess = result;
      81           0 :     result -= delta;
      82           0 :     if(result <= min)
      83             :       {
      84           0 :         delta = 0.5F * (guess - min);
      85           0 :         result = guess - delta;
      86           0 :         if((result == min) || (result == max))
      87             :           break;
      88             :       }
      89           0 :     else if(result >= max)
      90             :       {
      91           0 :         delta = 0.5F * (guess - max);
      92           0 :         result = guess - delta;
      93           0 :         if((result == min) || (result == max))
      94             :           break;
      95             :       }
      96             :     // update brackets:
      97           0 :     if(delta > 0)
      98           0 :       max = guess;
      99             :     else
     100           0 :       min = guess;
     101           0 :   }while(--count && (fabs(result * factor) < fabs(delta)));
     102             : 
     103           0 :   max_iter -= count;
     104             : 
     105           0 :   return result;
     106             : }
     107             : 
     108             : 
     109             : using namespace std;
     110             : 
     111          26 : VanDerCorputState::VanDerCorputState(){
     112          26 :   it = 20;
     113          26 :   baseId = 0;
     114          26 : }
     115             : 
     116          26 : void VanDerCorputState::seed(unsigned long seed){
     117          26 :   it = seed % 10000223 /* large prime */ ;
     118          26 : }
     119             : 
     120           0 : double VanDerCorputState::sample(){
     121           0 :   long n = it;
     122           0 :   unsigned int base = boost::math::prime(baseId);
     123           0 :   baseId = baseId+1;
     124             :   //(5437*baseId + 3671) % 9973; // A simple random (I choose the parameters randomly) linear congruantial pseudo random number generator.
     125           0 :   double vdc = 0, denom = 1;
     126           0 :   while (n){
     127           0 :     vdc += fmod(n, base) / (denom *= base);
     128           0 :     n /= base; // note: conversion from 'double' to 'int'
     129             :   }
     130           0 :   if(P.verbose > 4)cerr << "Sampling VDC; base: "<< base << "\tn: "<< it << " ->" << vdc << endl;
     131             : 
     132           0 :   return vdc;
     133             : }
     134             : 
     135           0 : void VanDerCorputState::newTrajectory(){
     136           0 :   baseId = 0;
     137           0 :   it++;
     138           0 : }
     139             : 
     140             : 
     141          26 : void timeGen::initRandomGenerator(unsigned int seed){
     142          26 :   RandomNumber.seed(seed);
     143          26 :   vanDerCorputSampler.seed(RandomNumber.operator()());
     144          26 :   kroneckerSampler.seed(seed);
     145          26 : }
     146             : 
     147             : 
     148          26 : KroneckerState::KroneckerState(){
     149          26 :   it = 0;
     150          26 :   baseId = 1;
     151          26 :   phi_d = 1.0 / compute_gamma(P.horizon);
     152          26 :   if(P.verbose > 4)cerr << "Initializing Kronecker Sampler: horizon: "<< P.horizon << "\tgamma: "<< 1.0/phi_d << endl;
     153             : 
     154          26 : }
     155             : 
     156          26 : double unif01ofint(unsigned long v){
     157          26 :     double res= 1.0;
     158        1660 :     while( v != 0){
     159         817 :       if(v %2){
     160         428 :         res /= 2.0;
     161             :       }else{
     162         389 :         res = (0.5 + res)/2.0;
     163             :       }
     164         817 :       v /=2;
     165             :     }
     166          26 :     return res;
     167             :   }
     168             : 
     169          26 : void KroneckerState::seed(unsigned long seed){
     170             :   //double * tmp =  reinterpret_cast<double*>(&seed);
     171          26 :   seed_val = unif01ofint(seed);
     172             : 
     173          26 : }
     174             : 
     175           0 : double KroneckerState::sample(){
     176           0 :   double alpha = pow(phi_d,baseId);
     177             :   //double alpha = sqrt(boost::math::prime(baseId-1));
     178             : 
     179             :   double d;
     180           0 :   double sample = modf( seed_val + (double)it * alpha, &d);
     181           0 :   if(P.verbose > 4)cerr << "Sampling Kronecker; seed: "<< seed_val << " base: "<< baseId << "\tn: "<< it << "\tbasealpha: " << phi_d << "\talpha: "<< alpha << "->" << sample << endl;
     182           0 :   baseId = baseId+1;
     183           0 :   return sample;
     184             : }
     185             : 
     186          26 : double KroneckerState::compute_gamma(unsigned long d){
     187          26 :   double x=1.0;
     188         806 :   for(int i=0; i< 30; i++){
     189         780 :     x = x-(pow(x,d+1)-x-1)/((d+1)*pow(x,d)-1);
     190             :   }
     191          26 :   return x;
     192             : }
     193             : 
     194           0 : void KroneckerState::newTrajectory(){
     195           0 :   baseId = 1;
     196           0 :   it++;
     197           0 : }
     198             : 
     199           0 : double timeGen::sampleQuasiRandom(size_t){
     200           0 :     double gentime =0.0;
     201           0 :     switch (P.randomGen) {
     202             :         case VDC:
     203           0 :             gentime = vanDerCorputSampler.sample();
     204           0 :             break;
     205             :         case Kronecker:
     206           0 :             gentime = vanDerCorputSampler.sample();
     207           0 :             break;
     208             :         case MT19937:
     209           0 :             gentime=0.0;
     210             :     }
     211             :   //cerr << gentime << "\t";
     212           0 :   return gentime;
     213             : }
     214             : 
     215      216102 : void timeGen::reset(){
     216      216102 :     switch (P.randomGen) {
     217             :         case VDC:
     218           0 :             vanDerCorputSampler.newTrajectory();
     219           0 :             break;
     220             :         case Kronecker:
     221           0 :             kroneckerSampler.newTrajectory();
     222             :         default: {}
     223             :     }
     224             :   //cerr << endl;
     225      216102 : }
     226             : 
     227           0 : string timeGen::string_of_dist(DistributionType d,const array<double,PARAM_TBL_SIZE> &param, const CustomDistr &cd)const{
     228             :   //use for debuging
     229           0 :   switch (d) {
     230             :   case   NORMAL:
     231           0 :     return "Normal("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     232             :   case   GAMMA:
     233           0 :     return "Gamma("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     234             :   case   UNIFORM:
     235           0 :     return "Uniform("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     236             :   case   EXPONENTIAL:
     237           0 :     return "Exponential("+ to_string(get<0>(param))+")";
     238             :   case   DETERMINISTIC:
     239           0 :     return "Deterministic("+ to_string(get<0>(param))+")";
     240             :   case   LOGNORMAL:
     241           0 :     return "LogNormal";
     242             :   case   TRIANGLE:
     243           0 :     return "Triangle";
     244             :   case   GEOMETRIC:
     245           0 :     return "Geometric";
     246             :   case   ERLANG:
     247           0 :     return "Erlang("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     248             :   case   PARETO:
     249           0 :     return "Pareto("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     250             :   case WEIBULL:
     251           0 :     return "Weibull("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     252             :   case   DISCRETEUNIF:
     253           0 :     return "DiscreteUnif("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     254             :   case   MASSACTION:
     255           0 :     return "MassAction("+ to_string(get<0>(param))+")";
     256             :   case IMMEDIATE:
     257             :   case IMDT:
     258           0 :     return "Immediate";
     259             : 
     260             :     //    return "Userdefine("+ to_string(param[0]) +","+ to_string(param[1])+","+ to_string(param[2])+","+ to_string(param[3])+")";
     261             :   case   DISCRETEUSERDEFINE:
     262           0 :     return "DiscreteUserDefine("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+")";
     263             :   case   USERDEFINE:
     264             :   case   USERDEFINEPOLYNOMIAL: {
     265           0 :     string s = "UserdefinePolynomial("+ to_string(get<0>(param)) +","+ to_string(get<1>(param))+","+ to_string(get<2>(param))+","+ to_string(get<3>(param))+")";
     266           0 :     return s + cd.print_poly( (int)get<0>(param));
     267             :   }
     268             :   case     SYNC:
     269           0 :     return "Synchronisation";
     270             :   default:
     271           0 :     return "Unknown distribution";
     272             :   }
     273             : 
     274             : }
     275             : 
     276             : 
     277             : /**
     278             :  * Call the random generator to generate fire time.
     279             :  * @param distribution is the type of distribution
     280             :  * @param param is a vector of parameters of the distribution.
     281             :  */
     282    83919280 : double timeGen::GenerateTime(DistributionType distribution,size_t trid, const array<double,PARAM_TBL_SIZE> &param, const CustomDistr &cd) {
     283             :   //cerr << "sampling " << string_of_dist(distribution,param) << endl;;
     284    83919280 :   switch (distribution) {
     285             :   case UNIFORM:
     286             :     {//UNIF
     287      811745 :       if(get<0>(param) == get<1>(param)) return get<0>(param);
     288      811745 :       if(P.randomGen != MT19937){
     289           0 :         return get<0>(param) + (get<1>(param)-get<0>(param))*sampleQuasiRandom(trid);
     290             :       }else{
     291      811745 :         std::uniform_real_distribution<> unif(get<0>(param), get<1>(param));
     292      811745 :         return unif(RandomNumber);
     293             :       }
     294             :     }
     295             : 
     296             :   case MASSACTION:
     297             :   case EXPONENTIAL:
     298             :     {//EXP
     299             :       //Exponential distribution is the only marking dependent parameters. Check of validity is required
     300             : 
     301             :       //-------------- Rare Event -----------------
     302             :       //cout << "rate:" << param[0] << endl;
     303    71161694 :       if(get<0>(param) <= 0) { return 1e200; };
     304             :       //------------- /Rare Event -----------------
     305             : 
     306    70854499 :       if (get<0>(param) <= 0) {
     307           0 :         cout << "Exponential ditribution should be with rate > 0 not "
     308           0 :              << get<0>(param) << "\n End of Simulation" << endl;
     309           0 :         exit(1);
     310             :       }
     311             : 
     312    70854499 :       if(P.randomGen != MT19937){
     313           0 :         return -log(
     314           0 :                     1.0 - sampleQuasiRandom(trid)) / get<0>(param);
     315             :       }else{
     316    70854499 :         std::exponential_distribution<> exp(get<0>(param));
     317    70854499 :         return exp(RandomNumber);
     318             :       }
     319             : 
     320             :     }
     321             : 
     322             :   case SYNC:
     323             :   case PLAYER1:
     324             :   case IMDT:
     325             :   case IMMEDIATE:
     326           0 :     return 0;
     327             : 
     328             :   case DETERMINISTIC:
     329             :     {//DETERMINISTIC
     330    11945841 :       return get<0>(param);
     331             :     }
     332             : 
     333             :   case NORMAL:
     334             :     {
     335           0 :       std::normal_distribution<> normal(get<0>(param), get<1>(param));
     336           0 :       return fmax(0.0,normal(RandomNumber));
     337             :     }
     338             : 
     339             :   case LOGNORMAL:
     340             :     {//LogNormal
     341           0 :       double m = get<0>(param); //location
     342           0 :       double s = get<1>(param); //scale
     343             :       //double mean = exp(m + s*s/2);
     344             :       //double variance = (exp(s*s)-1)*exp(2*m+s*s);
     345           0 :       std::lognormal_distribution<> lognormal(m, s);
     346           0 :       return lognormal(RandomNumber);
     347             :     }
     348             : 
     349             :   case TRIANGLE:
     350             :     {//Triangle
     351           0 :       std::uniform_real_distribution<> unif(0, 1);
     352           0 :       double p = unif(RandomNumber);
     353           0 :       double a = get<0>(param); //lower
     354           0 :       double b = get<2>(param); //upper
     355           0 :       double c = get<1>(param); //mode
     356           0 :       double p0 = (c-a)/(b-a); //inflection point
     357           0 :       if(p < p0)return sqrt((b-a)*(c-a)*p)+a;
     358           0 :       if(p > p0)return b-sqrt((b-a)*(b-c)*(1.0-p));
     359           0 :       if(p ==p0)return c; //Prob of this case is zero
     360             :     }
     361             :   case GEOMETRIC:
     362             :     {//GEOMETRIC
     363           0 :       std::uniform_real_distribution<> unif(0, 1);
     364           0 :       double p = unif(RandomNumber);
     365           0 :       return (get<1>(param) * (ceil(log(p) / log(1 - get<0>(param)))));
     366             :       /*if (p >= param[0]){
     367             :         return param[1];
     368             :         } else {
     369             :         return param[1] * ceil(log(p / param[0]) / log(1 - param[0]) + 1);
     370             :         }*/
     371             :     }
     372             :   case ERLANG:
     373             :     {//ERLANG
     374           0 :       std::uniform_real_distribution<> unif(0, 1);
     375           0 :       double sum = 0.0;
     376           0 :       for (int i = 0; i < get<0>(param); i++)
     377           0 :         sum -= log(unif(RandomNumber));
     378           0 :       return sum / get<1>(param);
     379             :     }
     380             :   case PARETO:
     381             :     {
     382           0 :       std::uniform_real_distribution<> unif(0, 1);
     383           0 :       return get<0>(param) / pow(unif(RandomNumber),1/get<1>(param)) ;
     384             :     }
     385             : 
     386             :   case WEIBULL:
     387             :     {
     388           0 :       std::weibull_distribution<> weibull(get<0>(param), get<1>(param));
     389           0 :       return weibull(RandomNumber);
     390             :     }
     391             : 
     392             : 
     393             :   case GAMMA:
     394             :     {//GAMMA
     395           0 :       std::gamma_distribution<> gamma(get<0>(param));
     396           0 :       return get<1>(param) * gamma(RandomNumber);
     397             :     }
     398             :   case DISCRETEUNIF:
     399             :     {//DISCRETEUNIF
     400           0 :       std::uniform_int_distribution<> unif((int)get<0>(param), (int)get<1>(param));
     401           0 :       return unif(RandomNumber);
     402             :       break;
     403             :     }
     404             : 
     405             :   case DISCRETEUSERDEFINE:
     406             :     {
     407           0 :       std::uniform_int_distribution<> unif(0, 100000);
     408           0 :       unsigned int i= unif(RandomNumber);
     409           0 :       return cd.userDefineDiscreteDistr(param,i);
     410             :     }
     411             : 
     412             :   case USERDEFINEPOLYNOMIAL:
     413             :   case USERDEFINE:
     414             :     {
     415             :       double gentime;
     416           0 :       if(P.randomGen != MT19937){
     417           0 :         gentime = sampleQuasiRandom(trid);
     418             :       }else{
     419           0 :         std::uniform_real_distribution<> unif(0.0, 1.0);
     420           0 :         gentime = unif(RandomNumber);
     421             :       }
     422             : 
     423             :         //cerr << gentime << "\t";
     424             : 
     425           0 :       double lower = cd.userDefineLowerBound(param);
     426           0 :       double upper = cd.userDefineUpperBound(param);
     427             : 
     428             : 
     429             :       /*//Isotrop Hack
     430             :       std::uniform_real_distribution<> unif(lower, upper);
     431             :       gentime = unif(RandomNumber);
     432             :       //gentime = sampleQuasiRandom(trid);
     433             :       //gentime = (upper-lower)*gentime + lower;
     434             :         cerr << gentime << "\t";
     435             :       //cerr << cd.userDefineCDF(param,gentime) << "\t";
     436             :       return gentime;
     437             :       //Isotrop Hack
     438             :       */
     439             : 
     440             :       //cerr << "sample(" << gentime << ",[" << lower << "," << upper << "]):" <<endl;
     441           0 :       double initialpt = (lower+upper)/ 2.0;
     442           0 :       double pt = boost_newton_raphson_iterate([&](double x){
     443           0 :           const auto cdf = cd.userDefineCDF(param,x);
     444           0 :           const auto pdf = cd.userDefinePDF(param,x);
     445             :           //      cerr << "it:" << x << endl;
     446           0 :           return make_tuple(cdf-gentime, pdf);
     447           0 :         }, initialpt, lower, upper, 100);
     448             :         //cerr << pt << "\t";
     449           0 :       return pt;
     450             :       break;
     451             : 
     452             :     }
     453             : 
     454             : 
     455             :   }
     456           0 :   return DBL_MIN;
     457          87 : }

Generated by: LCOV version 1.13