LCOV - code coverage report
Current view: top level - src/utils/common - RandHelper.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 82.1 % 84 69
Test Date: 2025-12-06 15:35:27 Functions: 85.7 % 7 6

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2005-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    RandHelper.h
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Michael Behrisch
      17              : /// @author  Jakob Erdmann
      18              : /// @date    Fri, 29.04.2005
      19              : ///
      20              : //
      21              : /****************************************************************************/
      22              : #pragma once
      23              : #include <config.h>
      24              : 
      25              : #include <cassert>
      26              : #include <cstring>
      27              : #include <vector>
      28              : #include <map>
      29              : #include <random>
      30              : #include <sstream>
      31              : #include <iostream>
      32              : #include <algorithm>
      33              : 
      34              : // TODO make this configurable
      35              : #define SAVE_ONLY_COUNT 1000000
      36              : 
      37              : // ===========================================================================
      38              : // helper function
      39              : // ===========================================================================
      40              : 
      41              : #ifdef __clang__
      42              : __attribute__((no_sanitize("unsigned-integer-overflow")))
      43              : #endif
      44              : inline uint64_t splitmix64(const uint64_t seed) {
      45        65106 :     uint64_t z = (seed + 0x9e3779b97f4a7c15);
      46        65106 :     z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
      47        65106 :     z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
      48        65106 :     return z ^ (z >> 31);
      49              : }
      50              : 
      51              : 
      52              : // ===========================================================================
      53              : // class declaration
      54              : // ===========================================================================
      55              : 
      56              : class OptionsCont;
      57              : 
      58              : // ===========================================================================
      59              : // class definitions
      60              : // ===========================================================================
      61              : /**
      62              :  * @class XoShiRo256PlusPlus
      63              :  * @brief A random number generator as proposed here https://prng.di.unimi.it/xoshiro256plusplus.c
      64              :  */
      65              : class XoShiRo256PlusPlus {
      66              : public:
      67              :     inline void seed(const uint64_t seed)     {
      68              :         this->state[0] = splitmix64(splitmix64(seed));
      69              :         this->state[1] = splitmix64(this->state[0]);
      70              :         this->state[2] = splitmix64(this->state[1]);
      71              :         this->state[3] = splitmix64(this->state[2]);
      72              :     }
      73              : 
      74              :     inline uint64_t operator()()     {
      75              :         const uint64_t result = rotl64(this->state[0] + this->state[3], 23) + this->state[0];
      76              :         const uint64_t t = this->state[1] << 17;
      77              :         this->state[2] ^= this->state[0];
      78              :         this->state[3] ^= this->state[1];
      79              :         this->state[1] ^= this->state[2];
      80              :         this->state[0] ^= this->state[3];
      81              :         this->state[2] ^= t;
      82              :         this->state[3] = rotl64(this->state[3], 45);
      83              :         return result;
      84              :     }
      85              : 
      86              :     void discard(unsigned long long skip) {
      87              :         while (skip-- > 0) {
      88              :             (*this)();
      89              :         }
      90              :     }
      91              : 
      92              :     friend std::ostream& operator<<(std::ostream& os, const XoShiRo256PlusPlus& r) {
      93              :         os << r.state[0] << r.state[1] << r.state[2] << r.state[3];
      94              :         return os;
      95              :     }
      96              : 
      97              :     friend std::istream& operator>>(std::istream& is, XoShiRo256PlusPlus& r) {
      98              :         is >> r.state[0] >> r.state[1] >> r.state[2] >> r.state[3];
      99              :         return is;
     100              :     }
     101              : 
     102              : 
     103              : private:
     104              :     static inline uint64_t rotl64(const uint64_t x, const int k) {
     105              :         return (x << k) | (x >> (64 - k));
     106              :     }
     107              : 
     108              :     uint64_t state[4];
     109              : 
     110              : };
     111              : 
     112              : 
     113              : //class SumoRNG : public XoShiRo256PlusPlus {
     114      7564051 : class SumoRNG : public std::mt19937 {
     115              : public:
     116      5702460 :     SumoRNG(const std::string& _id) : id(_id) {}
     117              : 
     118              :     void setSeed(int _seed) {
     119      2785718 :         origSeed = _seed;
     120      2785718 :         seed(_seed);
     121      2785718 :     }
     122              : 
     123              :     unsigned long long int count = 0;
     124              :     int origSeed = default_seed;
     125              :     std::string id;
     126              : };
     127              : 
     128              : 
     129              : /**
     130              :  * @class RandHelper
     131              :  * @brief Utility functions for using a global, resetable random number generator
     132              :  */
     133              : class RandHelper {
     134              : 
     135              : public:
     136              :     /// @brief Initialises the given options container with random number options
     137              :     static void insertRandOptions(OptionsCont& oc);
     138              : 
     139              :     /// @brief Initialises the random number generator with hardware randomness or seed
     140              :     static void initRand(SumoRNG* which = nullptr, const bool random = false, const int seed = 23423);
     141              : 
     142              :     /// @brief Reads the given random number options and initialises the random number generator in accordance
     143              :     static void initRandGlobal(SumoRNG* which = nullptr);
     144              : 
     145              :     static int getSeed(SumoRNG* which = nullptr);
     146              : 
     147              :     /// @brief Returns a random real number in [0, 1)
     148              :     static double rand(SumoRNG* rng = nullptr);
     149              : 
     150              :     /// @brief Returns a random real number in [0, maxV)
     151              :     static inline double rand(double maxV, SumoRNG* rng = nullptr) {
     152     51472903 :         return maxV * rand(rng);
     153              :     }
     154              : 
     155              :     /// @brief Returns a random real number in [minV, maxV)
     156              :     static inline double rand(double minV, double maxV, SumoRNG* rng = nullptr) {
     157       194962 :         return minV + (maxV - minV) * rand(rng);
     158              :     }
     159              : 
     160              :     /// @brief Returns a random integer in [0, maxV-1]
     161     81857781 :     static inline int rand(int maxV, SumoRNG* rng = nullptr) {
     162     83219259 :         if (rng == nullptr) {
     163              :             rng = &myRandomNumberGenerator;
     164              :         }
     165     81857781 :         unsigned int usedBits = maxV - 1;
     166     81857781 :         usedBits |= usedBits >> 1;
     167     81857781 :         usedBits |= usedBits >> 2;
     168     81857781 :         usedBits |= usedBits >> 4;
     169     81857781 :         usedBits |= usedBits >> 8;
     170     81857781 :         usedBits |= usedBits >> 16;
     171              : 
     172              :         // Draw numbers until one is found in [0, maxV-1]
     173              :         int result;
     174              :         do {
     175     84220498 :             result = (*rng)() & usedBits;
     176     84220498 :             rng->count++;
     177     82798753 :         } while (result >= maxV);
     178     81857781 :         return result;
     179              :     }
     180              : 
     181              :     /// @brief Returns a random integer in [minV, maxV-1]
     182              :     static inline int rand(int minV, int maxV, SumoRNG* rng = nullptr) {
     183        92735 :         return minV + rand(maxV - minV, rng);
     184              :     }
     185              : 
     186              :     /// @brief Returns a random 64 bit integer in [0, maxV-1]
     187        14252 :     static inline long long int rand(long long int maxV, SumoRNG* rng = nullptr) {
     188        14252 :         if (maxV <= std::numeric_limits<int>::max()) {
     189        14252 :             return rand((int)maxV, rng);
     190              :         }
     191            0 :         if (rng == nullptr) {
     192              :             rng = &myRandomNumberGenerator;
     193              :         }
     194            0 :         unsigned long long int usedBits = maxV - 1;
     195            0 :         usedBits |= usedBits >> 1;
     196            0 :         usedBits |= usedBits >> 2;
     197            0 :         usedBits |= usedBits >> 4;
     198            0 :         usedBits |= usedBits >> 8;
     199            0 :         usedBits |= usedBits >> 16;
     200            0 :         usedBits |= usedBits >> 32;
     201              : 
     202              :         // Draw numbers until one is found in [0, maxV-1]
     203              :         long long int result;
     204              :         do {
     205            0 :             result = (((unsigned long long int)(*rng)() << 32) | (*rng)()) & usedBits;    // toss unused bits to shorten search
     206            0 :             rng->count += 2;
     207            0 :         } while (result >= maxV);
     208              :         return result;
     209              :     }
     210              : 
     211              :     /// @brief Returns a random 64 bit integer in [minV, maxV-1]
     212              :     static inline long long int rand(long long int minV, long long int maxV, SumoRNG* rng = nullptr) {
     213        14032 :         return minV + rand(maxV - minV, rng);
     214              :     }
     215              : 
     216              :     /// @brief Access to a random number from a normal distribution
     217              :     static double randNorm(double mean, double variance, SumoRNG* rng = nullptr);
     218              : 
     219              :     /// @brief Access to a random number from an exponential distribution
     220              :     static double randExp(double rate, SumoRNG* rng = nullptr);
     221              : 
     222              :     /// @brief Returns a random element from the given vector
     223              :     template<class T>
     224              :     static inline const T&
     225              :     getRandomFrom(const std::vector<T>& v, SumoRNG* rng = nullptr) {
     226              :         assert(v.size() > 0);
     227     81670460 :         return v[rand((int)v.size(), rng)];
     228              :     }
     229              : 
     230              :     /// @brief save rng state to string
     231          781 :     static std::string saveState(SumoRNG* rng = nullptr) {
     232          781 :         if (rng == nullptr) {
     233              :             rng = &myRandomNumberGenerator;
     234              :         }
     235          781 :         std::ostringstream oss;
     236          781 :         oss << rng->count;
     237          781 :         if (rng->count >= SAVE_ONLY_COUNT) {
     238            1 :             oss << " " << (*rng);
     239              :         }
     240          781 :         return oss.str();
     241          781 :     }
     242              : 
     243              :     /// @brief load rng state from string
     244          781 :     static void loadState(const std::string& state, SumoRNG* rng = nullptr) {
     245          781 :         if (rng == nullptr) {
     246              :             rng = &myRandomNumberGenerator;
     247              :         }
     248          781 :         std::istringstream iss(state);
     249          781 :         iss >> rng->count;
     250          781 :         if (rng->count < SAVE_ONLY_COUNT) {
     251          780 :             rng->discard(rng->count);
     252              :         } else {
     253            1 :             iss >> (*rng);
     254              :         }
     255          781 :     }
     256              : 
     257              :     template<class T>
     258            0 :     static void shuffle(std::vector<T>& v, SumoRNG* rng = nullptr) {
     259            0 :         for (int i = (int)(v.size() - 1); i > 0; --i) {
     260            0 :             std::swap(*(v.begin() + i), *(v.begin() + rand(i, rng)));
     261              :         }
     262            0 :     }
     263              : 
     264              :     static long long int count() {
     265              :         return myRandomNumberGenerator.count;
     266              :     }
     267              : 
     268              :     /// @brief return a value scrambled value from [0, 1]
     269              :     static double randHash(long long int x) {
     270        65106 :         const uint64_t h = splitmix64(x) >> 11;
     271        65106 :         return (double)h / (double(1ULL << 53));
     272              :     }
     273              : 
     274              :     /// @brief string hashing adapted from https://en.wikipedia.org/wiki/MurmurHash
     275              : #ifdef __clang__
     276              :     __attribute__((no_sanitize("integer"))) // left-shift and unsigned-integer-overflow
     277              : #endif
     278      6196637 :     static uint32_t murmur3_32(const std::string& key2, int seed) {
     279              :         const uint8_t* key = reinterpret_cast<const uint8_t*>(key2.data());
     280              :         const size_t len = key2.size();
     281      6196637 :         uint32_t h = seed;
     282              :         uint32_t k;
     283              :         /* Read in groups of 4. */
     284     15773889 :         for (size_t i = len >> 2; i; i--) {
     285              :             memcpy(&k, key, sizeof(uint32_t));
     286      9577252 :             key += sizeof(uint32_t);
     287      9577252 :             h ^= murmur_32_scramble(k);
     288      9577252 :             h = (h << 13) | (h >> 19);
     289      9577252 :             h = h * 5 + 0xe6546b64;
     290              :         }
     291              :         /* Read the rest. */
     292      6196637 :         k = 0;
     293     16043136 :         for (size_t i = len & 3; i; i--) {
     294      9846499 :             k <<= 8;
     295      9846499 :             k |= key[i - 1];
     296              :         }
     297      6196637 :         h ^= murmur_32_scramble(k);
     298              :         /* Finalize. */
     299      6196637 :         h ^= len;
     300      6196637 :         h ^= h >> 16;
     301      6196637 :         h *= 0x85ebca6b;
     302      6196637 :         h ^= h >> 13;
     303      6196637 :         h *= 0xc2b2ae35;
     304      6196637 :         h ^= h >> 16;
     305      6196637 :         return h;
     306              :     }
     307              : 
     308              : 
     309              : protected:
     310              :     /// @brief the default random number generator to use
     311              :     static SumoRNG myRandomNumberGenerator;
     312              : 
     313              :     /// @brief helper function for murmur_32_scramble from https://en.wikipedia.org/wiki/MurmurHash
     314              : #ifdef __clang__
     315              :     __attribute__((no_sanitize("integer"))) // left-shift and unsigned-integer-overflow
     316              : #endif
     317              :     static inline uint32_t murmur_32_scramble(uint32_t k) {
     318     15773889 :         k *= 0xcc9e2d51;
     319     15773889 :         k = (k << 15) | (k >> 17);
     320      9577252 :         k *= 0x1b873593;
     321              :         return k;
     322              :     }
     323              : 
     324              : };
        

Generated by: LCOV version 2.0-1