Loading [MathJax]/extensions/TeX/AMSmath.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
random.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2022 The ESPResSo project
3 *
4 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
5 * Max-Planck-Institute for Polymer Research, Theory Group
6 *
7 * This file is part of ESPResSo.
8 *
9 * ESPResSo is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * ESPResSo is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22#ifndef RANDOM_H
23#define RANDOM_H
24
25/** \file
26 * Random number generation using Philox.
27 */
28
29#include <utils/Vector.hpp>
30#include <utils/u32_to_u64.hpp>
31#include <utils/uniform.hpp>
32
33#include <Random123/philox.h>
34
35#include <cstddef>
36#include <numbers>
37#include <random>
38#include <ranges>
39#include <vector>
40
41/*
42 * @brief Salt for the RNGs
43 *
44 * This is to avoid correlations between the
45 * noise on the particle coupling and the fluid
46 * thermalization.
47 */
62
63namespace Random {
64/**
65 * @brief get 4 random uint 64 from the Philox RNG
66 *
67 * This uses the Philox PRNG, the state is controlled
68 * by the counter, the salt and two keys.
69 * If any of the keys and salt differ, the noise is
70 * not correlated between two calls along the same counter
71 * sequence.
72 *
73 */
74template <RNGSalt salt>
75auto philox_4_uint64s(uint64_t counter, uint32_t seed, int key1, int key2 = 0) {
76
77 using rng_type = r123::Philox4x64;
78 using ctr_type = rng_type::ctr_type;
79 using key_type = rng_type::key_type;
80
81 const ctr_type c{{counter, 0u, 0u, 0u}};
82
83 auto const id1 = static_cast<uint32_t>(key1);
84 auto const id2 = static_cast<uint32_t>(key2);
85 const key_type k{{Utils::u32_to_u64(id1, id2),
86 Utils::u32_to_u64(static_cast<uint32_t>(salt), seed)}};
87
88 return rng_type{}(c, k);
89}
90
91/**
92 * @brief Generator for random uniform noise.
93 *
94 * Mean = 0, variance = 1 / 12.
95 * This uses the Philox PRNG, the state is controlled
96 * by the counter, the salt and two keys.
97 * If any of the keys and salt differ, the noise is
98 * not correlated between two calls along the same counter
99 * sequence.
100 *
101 * @tparam salt RNG salt
102 * @tparam N Size of the noise vector
103 * @param counter counter for random number generation
104 * @param seed seed for random number generation
105 * @param key1 key for random number generation
106 * @param key2 key for random number generation
107 *
108 * @return Vector of uniform random numbers.
109 */
110template <RNGSalt salt, std::size_t N = 3,
111 std::enable_if_t<(N > 1) and (N <= 4), int> = 0>
112auto noise_uniform(uint64_t counter, uint32_t seed, int key1, int key2 = 0) {
113
114 auto const integers = philox_4_uint64s<salt>(counter, seed, key1, key2);
116 std::ranges::transform(integers | std::ranges::views::take(N), noise.begin(),
117 [](std::size_t v) { return Utils::uniform(v) - 0.5; });
118 return noise;
119}
120
121template <RNGSalt salt, std::size_t N, std::enable_if_t<N == 1, int> = 0>
122auto noise_uniform(uint64_t counter, uint32_t seed, int key1, int key2 = 0) {
123
124 auto const integers = philox_4_uint64s<salt>(counter, seed, key1, key2);
125 return Utils::uniform(integers[0]) - 0.5;
126}
127
128/** @brief Generator for Gaussian noise.
129 *
130 * Mean = 0, standard deviation = 1.0.
131 * Based on the Philox RNG using 4x64 bits.
132 * The Box-Muller transform is used to convert from uniform to normal
133 * distribution. The transform is only valid, if the uniformly distributed
134 * random numbers are not zero (approx one in 2^64). To avoid this case,
135 * such numbers are replaced by std::numeric_limits<double>::min()
136 * This breaks statistics in rare cases but allows for consistent RNG
137 * counters across MPI ranks.
138 *
139 * @tparam salt decorrelates different thermostat types
140 * @param counter counter for random number generation
141 * @param seed seed for random number generation
142 * @param key1 key for random number generation
143 * @param key2 key for random number generation
144 *
145 * @return Vector of Gaussian random numbers.
146 *
147 */
148template <RNGSalt salt, std::size_t N = 3,
149 class = std::enable_if_t<(N >= 1) and (N <= 4)>>
150auto noise_gaussian(uint64_t counter, uint32_t seed, int key1, int key2 = 0) {
151
152 auto const integers = philox_4_uint64s<salt>(counter, seed, key1, key2);
153 static const double epsilon = std::numeric_limits<double>::min();
154
155 constexpr std::size_t M = (N <= 2) ? 2 : 4;
157 std::ranges::transform(integers | std::ranges::views::take(M), u.begin(),
158 [](std::size_t value) {
159 auto u = Utils::uniform(value);
160 return (u < epsilon) ? epsilon : u;
161 });
162
163 // Box-Muller transform code adapted from
164 // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
165 // optimizations: the modulo is cached (logarithms are expensive), the
166 // sin/cos are evaluated simultaneously by gcc or separately by Clang
168 {
169 auto const modulo = sqrt(-2. * log(u[0]));
170 auto const angle = 2. * std::numbers::pi * u[1];
171 noise[0] = modulo * cos(angle);
172 if (N > 1) {
173 noise[1] = modulo * sin(angle);
174 }
175 }
176 if (N > 2) {
177 auto const modulo = sqrt(-2. * log(u[2]));
178 auto const angle = 2. * std::numbers::pi * u[3];
179 noise[2] = modulo * cos(angle);
180 if (N > 3) {
181 noise[3] = modulo * sin(angle);
182 }
183 }
184 return noise;
185}
186
187/** Mersenne Twister with warmup.
188 * The first 100'000 values of Mersenne Twister generators are often heavily
189 * correlated @cite panneton06a. This utility function discards the first
190 * 1'000'000 values.
191 *
192 * @param seed RNG seed
193 */
194template <typename T> std::mt19937 mt19937(T &&seed) {
195 std::mt19937 generator(seed);
196 generator.discard(1'000'000);
197 return generator;
198}
199
200} // namespace Random
201
202#endif
Vector implementation and trait types for boost qvm interoperability.
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
auto philox_4_uint64s(uint64_t counter, uint32_t seed, int key1, int key2=0)
get 4 random uint 64 from the Philox RNG
Definition random.hpp:75
auto noise_uniform(uint64_t counter, uint32_t seed, int key1, int key2=0)
Generator for random uniform noise.
Definition random.hpp:112
auto noise_gaussian(uint64_t counter, uint32_t seed, int key1, int key2=0)
Generator for Gaussian noise.
Definition random.hpp:150
std::mt19937 mt19937(T &&seed)
Mersenne Twister with warmup.
Definition random.hpp:194
constexpr double uniform(uint64_t in)
Uniformly map unsigned integer to double.
Definition uniform.hpp:36
constexpr uint64_t u32_to_u64(uint32_t high, uint32_t low)
RNGSalt
Definition random.hpp:48
@ BROWNIAN_INC
@ LANGEVIN_ROT
@ BROWNIAN_ROT_WALK
@ BROWNIAN_WALK
@ BROWNIAN_ROT_INC
@ NPTISO_VOLUME
@ THERMALIZED_BOND
@ NPTISO_PARTICLE