2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2015,2016, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS 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 GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * \brief Tabulated normal distribution
39 * A very fast normal distribution, but with limited resolution.
41 * \author Erik Lindahl <erik.lindahl@gmail.com>
43 * \ingroup module_random
46 #ifndef GMX_RANDOM_TABULATEDNORMALDISTRIBUTION_H
47 #define GMX_RANDOM_TABULATEDNORMALDISTRIBUTION_H
54 #include "gromacs/math/utilities.h"
55 #include "gromacs/utility/basedefinitions.h"
56 #include "gromacs/utility/classhelpers.h"
57 #include "gromacs/utility/real.h"
62 /*! \brief Tabulated normal random distribution
64 * Random distribution compatible with C++11 distributions - it can be
65 * used with any C++11 random engine.
67 * \tparam RealType Type of the return value. Float or double. Note that
68 * GROMACS uses "real" type by default in contrast to the C++11
69 * standard library, to avoid double/float conversions.
70 * \tparam tableBits Size of the table, specified in bits. The storage
71 * space required is sizeof(RealType)*2^tableBits. To
72 * keep things sane this is limited to 24 bits.
74 * Some stochastic integrators depend on drawing a lot of normal
75 * distribution random numbers quickly, but in many cases the only
76 * important property is the distribution - given the noise in forces
77 * we do not need very high resolution.
78 * This distribution uses an internal table to return samples from a
79 * normal distribution with limited resolution. By default the table uses
80 * 14 bits, but this is specified with a template parameter.
82 * Since this distribution only uses tableBits bits per value generated,
83 * the values draw from the random engine are used for several results.
84 * To make sure you get a reproducible result when using counter-based
85 * random engines (such as ThreeFry2x64), remember to call the reset()
86 * method to cancel the internal memory of the distribution.
88 * \note For modern NUMA systems, you likely want to use separate
89 * distributions for each thread, and make sure they are initialized
90 * on the CPU where they will run, so the table is placed in that
92 * \note The finite table resolution means this distribution will NOT
93 * return arbitrarily small/large values, but with e.g. 14 bits
94 * the results are limited to roughly +/- 4 standard deviations.
96 template<class RealType = real, unsigned int tableBits = 14>
97 class TabulatedNormalDistribution
99 static_assert(tableBits <= 24, "Normal distribution table is limited to 24bits (64MB in single precision)");
102 /*! \brief Type of normal distribution results */
103 typedef RealType result_type;
105 /*! \brief Normal distribution parameter class (mean and stddev) */
109 /*! \brief The type of distribution the parameters describe */
110 typedef TabulatedNormalDistribution distribution_type;
112 /*! \brief Constructor. Default is classical distr. with mean 0, stddev 1.
114 * \param mean Expectation value.
115 * \param stddev Standard deviation.
118 explicit param_type(result_type mean = 0.0, result_type stddev = 1.0)
119 : mean_(mean), stddev_(stddev) {}
121 /*! \brief Return mean parameter of normal distribution */
123 mean() const { return mean_; }
125 /*! \brief Return standard deviation parameter of normal distribution */
127 stddev() const { return stddev_; }
129 /*! \brief True if two sets of normal distributions parameters are identical
131 * \param x Instance to compare with.
134 operator==(const param_type &x) const
136 return (mean_ == x.mean_ && stddev_ == x.stddev_);
139 /*! \brief True if two sets of normal distributions parameters are different.
141 * \param x Instance to compare with.
144 operator!=(const param_type &x) const { return !operator==(x); }
147 /*! \brief Internal storage for mean of normal distribution */
149 /*! \brief Internal storage for standard deviation of normal distribution */
155 /*! \brief Fill the table with values for the normal distribution
157 * This routine returns a new reference to a std::vector allocated on
158 * the heap. It will only be called to generate the static
159 * constant table data at initialization time. While it is technically
160 * returning memory on the heap, this does not use/leak more memory
161 * than what we would use by having the same table as a static member
165 std::vector<RealType> &
166 // cppcheck-suppress unusedPrivateFunction
169 /* Fill the table with the integral of a gaussian distribution:
171 std::size_t tableSize = 1 << tableBits;
172 std::size_t halfSize = (1 << tableBits)/2;
173 double invSize = 1.0/tableSize;
174 double factor = std::sqrt(2.0*M_PI);
175 double x = 0.5*factor*invSize;
177 std::vector<RealType>* table = new std::vector<RealType>(1ULL << tableBits);
179 for (std::size_t i = 0; i < halfSize; i++)
187 double invNormal = factor*std::exp(0.5*x*x);
188 /* det is larger than 0 for all x, except the last */
189 double det = 1.0 - 2.0*invSize*x*invNormal;
190 dx = (1.0 - std::sqrt(det))/x;
198 table->at(halfSize-1-i) = -x;
199 table->at(halfSize+i) = x;
206 /*! \brief Construct new normal distribution with specified mean & stdddev.
208 * \param mean Mean value of tabulated normal distribution
209 * \param stddev Standard deviation of tabulated normal distribution
211 explicit TabulatedNormalDistribution(result_type mean = 0.0, result_type stddev = 1.0 )
212 : param_(param_type(mean, stddev)), savedRandomBits_(0), savedRandomBitsLeft_(0)
216 /*! \brief Construct new normal distribution from parameter type.
218 * \param param Parameter class containing mean and standard deviation.
220 explicit TabulatedNormalDistribution( const param_type ¶m )
221 : param_(param), savedRandomBits_(0), savedRandomBitsLeft_(0)
225 /*! \brief Smallest value that can be generated in normal distrubiton.
227 * \note The smallest value is not -infinity with a table, but it
228 * depends on the table resolution. With 14 bits, this is roughly
229 * four standard deviations below the mean.
237 /*! \brief Largest value that can be generated in normal distribution.
239 * \note The largest value is not infinity with a table, but it
240 * depends on the table resolution. With 14 bits, this is roughly
241 * four standard deviations above the mean.
246 return c_table_[c_table_.size()-1];
249 /*! \brief Mean of the present normal distribution */
253 return param_.mean();
256 /*! \brief Standard deviation of the present normal distribution */
261 return param_.stddev();
264 /*! \brief The parameter class (mean & stddev) of the normal distribution */
271 /*! \brief Clear all internal saved random bits from the random engine */
275 savedRandomBitsLeft_ = 0;
278 /*! \brief Return normal distribution value specified by internal parameters.
280 * \tparam Rng Random engine type used to provide uniform random bits.
281 * \param g Random engine of class Rng. For normal GROMACS usage
282 * you likely want to use ThreeFry2x64.
288 return (*this)(g, param_);
291 /*! \brief Return normal distribution value specified by given parameters
293 * \tparam Rng Random engine type used to provide uniform random bits.
294 * \param g Random engine of class Rng. For normal GROMACS usage
295 * you likely want to use ThreeFry2x64.
296 * \param param Parameters used to specify normal distribution.
300 operator()(Rng &g, const param_type ¶m)
302 if (savedRandomBitsLeft_ < tableBits)
304 // We do not know whether the generator g returns 64 or 32 bits,
305 // since g is not known when we construct this class.
306 // To keep things simple, we always draw one random number,
307 // store it in our 64-bit value, and set the number of active bits.
308 // For tableBits up to 16 this will be as efficient both with 32
309 // and 64 bit random engines when drawing multiple numbers
310 // (our default value is 14), It also avoids drawing multiple
311 // 32-bit random numbres even if we just call this routine for a
313 savedRandomBits_ = static_cast<gmx_uint64_t>(g());
314 savedRandomBitsLeft_ = std::numeric_limits<typename Rng::result_type>::digits;
316 result_type value = c_table_[savedRandomBits_ & ( (1ULL << tableBits) - 1 ) ];
317 savedRandomBits_ >>= tableBits;
318 savedRandomBitsLeft_ -= tableBits;
319 return param.mean() + value * param.stddev();
322 /*!\brief Check if two tabulated normal distributions have identical states.
324 * \param x Instance to compare with.
327 operator==(const TabulatedNormalDistribution<RealType, tableBits> &x) const
329 return (param_ == x.param_ &&
330 savedRandomBits_ == x.savedRandomBits_ &&
331 savedRandomBitsLeft_ == x.savedRandomBitsLeft_);
334 /*!\brief Check if two tabulated normal distributions have different states.
336 * \param x Instance to compare with.
339 operator!=(const TabulatedNormalDistribution<RealType, tableBits> &x) const
341 return !operator==(x);
345 /*! \brief Parameters of normal distribution (mean and stddev) */
347 /*! \brief Array with tabluated values of normal distribution */
348 static const std::vector<RealType> c_table_;
349 /*! \brief Saved output from random engine, shifted tableBits right each time */
350 gmx_uint64_t savedRandomBits_;
351 /*! \brief Number of valid bits remaining i savedRandomBits_ */
352 unsigned int savedRandomBitsLeft_;
354 GMX_DISALLOW_COPY_AND_ASSIGN(TabulatedNormalDistribution);
357 // MSVC does not handle extern template class members correctly even in MSVC 2015,
358 // so in that case we have to instantiate in every object using it. In addition,
359 // doxygen is convinced this defines a function (which leads to crashes in our python
360 // scripts), so to avoid confusion we hide it from doxygen too.
361 #if (!defined(_MSC_VER) || defined(__INTEL_COMPILER)) && !defined(DOXYGEN)
362 // Declaration of template specialization
364 const std::vector<real> TabulatedNormalDistribution<real, 14>::c_table_;
367 const std::vector<real> TabulatedNormalDistribution<real, 14>::c_table_;
370 // Instantiation for all tables without specialization
371 template<class RealType, unsigned int tableBits>
372 const std::vector<RealType> TabulatedNormalDistribution<RealType, tableBits>::c_table_ = TabulatedNormalDistribution<RealType, tableBits>::makeTable();
376 #endif // GMX_RANDOM_TABULATEDNORMALDISTRIBUTION_H