A library for working with phylogenetic and population genetic data.
v0.32.0
random.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2024 Lucas Czech
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
34 
35 #include <cassert>
36 #include <random>
37 #include <stdexcept>
38 #include <string>
39 
40 namespace genesis {
41 namespace utils {
42 
43 // =================================================================================================
44 // Fast Randomness
45 // =================================================================================================
46 
47 // Implementation from https://en.wikipedia.org/wiki/Permuted_congruential_generator#Example_code
48 
49 static uint64_t pcg32_state = 0x4d595df4d0f33173; // Or something seed-dependent
50 static uint64_t const pcg32_multiplier = 6364136223846793005u;
51 static uint64_t const pcg32_increment = 1442695040888963407u; // Or an arbitrary odd constant
52 
53 static inline uint32_t pcg32_rotr32( uint32_t x, unsigned int r )
54 {
55  return x >> r | x << (-r & 31);
56 }
57 
59 {
60  uint64_t x = pcg32_state;
61  auto count = static_cast<unsigned int>( x >> 59 ); // 59 = 64 - 5
62 
64  x ^= x >> 18; // 18 = (64 - 27)/2
65  return pcg32_rotr32( static_cast<uint32_t>( x >> 27 ), count ); // 27 = 32 - 5
66 }
67 
68 uint32_t permuted_congruential_generator( uint32_t max )
69 {
70  return permuted_congruential_generator() % ( max + 1 );
71 }
72 
73 uint32_t permuted_congruential_generator( uint32_t min, uint32_t max )
74 {
75  if( min > max ) {
76  throw std::invalid_argument(
77  "Invalid call to permuted_congruential_generator( " + std::to_string(min) + ", " +
78  std::to_string(max) + " )"
79  );
80  }
81  return min + permuted_congruential_generator() % ( max - min + 1 );
82 }
83 
85 {
86  return permuted_congruential_generator() % 2 == 0;
87 }
88 
89 bool permuted_congruential_generator_bool( uint32_t chance_one_in )
90 {
91  if( chance_one_in == 0 ) {
92  throw std::invalid_argument(
93  "Invalid call to permuted_congruential_generator_bool( 0 )"
94  );
95  }
96  return permuted_congruential_generator() % chance_one_in == 0;
97 }
98 
100 {
101  pcg32_state = seed + pcg32_increment;
102 }
103 
104 // ================================================================================================
105 // Sampling
106 // ================================================================================================
107 
108 std::vector<size_t> select_without_replacement( size_t const k, size_t const n )
109 {
110  // Following http://stackoverflow.com/a/311716/4184258
111  // Knuth's variable names: k=n, n=N
112 
113  std::vector<size_t> result;
114  result.reserve( k );
115 
116  if( k > n ) {
117  throw std::invalid_argument(
118  "Cannot select more unique elements than there are elements: k == " +
119  std::to_string(k) + " > n == " + std::to_string(n) + "."
120  );
121  }
122 
123  auto& engine = Options::get().random_engine();
124  std::uniform_real_distribution<double> distribution( 0.0, 1.0 );
125 
126  size_t t = 0; // total input records dealt with
127  size_t m = 0; // number of items selected so far
128 
129  while( m < k ) {
130  // call a uniform( 0, 1 ) random number generator
131  double const u = distribution( engine );
132 
133  if( (n - t) * u >= k - m ) {
134  t++;
135  } else {
136  assert( t < n );
137  result.push_back( t );
138  t++;
139  m++;
140  }
141  }
142  assert( m == k );
143  assert( result.size() == k );
144 
145  return result;
146 }
147 
148 } // namespace utils
149 } // namespace genesis
genesis::utils::permuted_congruential_generator_init
void permuted_congruential_generator_init(uint64_t seed)
Set the seed for permuted_congruential_generator().
Definition: random.cpp:99
genesis::utils::pcg32_rotr32
static uint32_t pcg32_rotr32(uint32_t x, unsigned int r)
Definition: random.cpp:53
random.hpp
genesis::population::to_string
std::string to_string(GenomeLocus const &locus)
Definition: function/genome_locus.hpp:52
genesis::utils::pcg32_state
static uint64_t pcg32_state
Definition: random.cpp:49
genesis::utils::permuted_congruential_generator_bool
bool permuted_congruential_generator_bool()
Fast random number generator for 32bit integers, for bool with 0.5 probability.
Definition: random.cpp:84
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::Options::random_engine
std::default_random_engine & random_engine()
Return a thread-local engine for random number generation.
Definition: options.hpp:168
genesis::utils::pcg32_increment
static const uint64_t pcg32_increment
Definition: random.cpp:51
options.hpp
genesis::utils::select_without_replacement
std::vector< size_t > select_without_replacement(size_t const k, size_t const n)
Select k many unique numbers out of the range [ 0, n ).
Definition: random.cpp:108
genesis::utils::Options::get
static Options & get()
Returns a single instance of this class.
Definition: options.hpp:68
genesis::utils::permuted_congruential_generator
uint32_t permuted_congruential_generator()
Fast random number generator for 32bit integers.
Definition: random.cpp:58
genesis::utils::pcg32_multiplier
static const uint64_t pcg32_multiplier
Definition: random.cpp:50