ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
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 <vector>
39
40/*
41 * @brief Salt for the RNGs
42 *
43 * This is to avoid correlations between the
44 * noise on the particle coupling and the fluid
45 * thermalization.
46 */
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);
115 Utils::VectorXd<N> noise{};
116 std::transform(integers.begin(), integers.begin() + N, noise.begin(),
117 [](std::size_t value) { return Utils::uniform(value) - 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::transform(integers.begin(), integers.begin() + 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
167 Utils::VectorXd<N> noise{};
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.
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:47
@ NPTISO0_HALF_STEP1
@ BROWNIAN_INC
@ LANGEVIN_ROT
@ NPTISO0_HALF_STEP2
@ BROWNIAN_ROT_WALK
@ BROWNIAN_WALK
@ BROWNIAN_ROT_INC
@ THERMALIZED_BOND