Import Geant4 11.0.0 source tree

This commit is contained in:
Gabriele Cosmo
2021-12-10 14:46:44 +01:00
committed by Ben Morgan
parent 6399a014b6
commit 80e2389dd8
3932 changed files with 202519 additions and 246221 deletions
@@ -31,6 +31,7 @@
#include "CLHEP/Random/RanecuEngine.h"
#include "CLHEP/Random/RanluxEngine.h"
#include "CLHEP/Random/Ranlux64Engine.h"
#include "CLHEP/Random/RanluxppEngine.h"
#include "CLHEP/Random/RanshiEngine.h"
// Including distributions ...
@@ -0,0 +1,104 @@
//
// -*- C++ -*-
//
// -----------------------------------------------------------------------
// HEP Random
// --- RanluxppEngine ---
// class header file
// -----------------------------------------------------------------------
// Implementation of the RANLUX++ generator
//
// RANLUX++ is an LCG equivalent of RANLUX using 576 bit numbers.
//
// References:
// A. Sibidanov
// A revision of the subtract-with-borrow random numbergenerators
// Computer Physics Communications, 221(2017), 299-303
//
// J. Hahnfeld, L. Moneta
// A Portable Implementation of RANLUX++
// vCHEP2021
#ifndef RanluxppEngine_h
#define RanluxppEngine_h
#include "CLHEP/Random/RandomEngine.h"
namespace CLHEP {
/**
* @author Jonas Hahnfeld
* @ingroup random
*/
class RanluxppEngine final : public HepRandomEngine {
public:
RanluxppEngine();
RanluxppEngine(long seed);
RanluxppEngine(std::istream &is);
virtual ~RanluxppEngine();
// Constructors and destructor
double flat() override;
// It returns a pseudo random number between 0 and 1,
// excluding the end points.
void flatArray(const int size, double *vect) override;
// Fills the array "vect" of specified size with flat random values.
void setSeed(long seed, int dummy = 0) override;
// Sets the state of the algorithm according to seed.
void setSeeds(const long *seeds, int dummy = 0) override;
// Sets the state of the algorithm according to the zero terminated
// array of seeds. Only the first seed is used.
void skip(uint64_t n);
// Skip `n` random numbers without generating them.
void saveStatus(const char filename[] = "Ranluxpp.conf") const override;
// Saves in named file the current engine status.
void restoreStatus(const char filename[] = "Ranluxpp.conf") override;
// Reads from named file the last saved engine status and restores it.
void showStatus() const override;
// Dumps the engine status on the screen.
std::string name() const override;
// Optional methods to serialize the engine's state into vectors and streams.
static std::string engineName();
static std::string beginTag();
std::ostream &put(std::ostream &os) const override;
std::istream &get(std::istream &is) override;
std::istream &getState(std::istream &is) override;
std::vector<unsigned long> put() const override;
bool get(const std::vector<unsigned long> &v) override;
bool getState(const std::vector<unsigned long> &v) override;
// Save and restore to/from streams
operator double() override { return flat(); }
operator float() override { return float(flat()); }
operator unsigned int() override { return (unsigned int)nextRandomBits(); }
// 1 value for the engine ID, 2 * 9 values for the state, and 2 more values
// for the carry bit and the position.
static const unsigned int VECTOR_STATE_SIZE = 21;
private:
void advance();
uint64_t nextRandomBits();
uint64_t fState[9]; ///< RANLUX state of the generator
unsigned fCarry; ///< Carry bit of the RANLUX state
int fPosition = 0; ///< Current position in bits
}; // RanluxppEngine
} // namespace CLHEP
#endif
@@ -0,0 +1,151 @@
//
// -*- C++ -*-
//
// -----------------------------------------------------------------------
// HEP Random
// --- RanluxppEngine ---
// helper implementation file
// -----------------------------------------------------------------------
#ifndef RANLUXPP_HELPERS_H
#define RANLUXPP_HELPERS_H
#include <cstdint>
/// Compute `a + b` and set `overflow` accordingly.
static inline uint64_t add_overflow(uint64_t a, uint64_t b,
unsigned &overflow) {
uint64_t add = a + b;
overflow = (add < a);
return add;
}
/// Compute `a + b` and increment `carry` if there was an overflow
static inline uint64_t add_carry(uint64_t a, uint64_t b, unsigned &carry) {
unsigned overflow;
uint64_t add = add_overflow(a, b, overflow);
// Do NOT branch on overflow to avoid jumping code, just add 0 if there was
// no overflow.
carry += overflow;
return add;
}
/// Compute `a - b` and set `overflow` accordingly
static inline uint64_t sub_overflow(uint64_t a, uint64_t b,
unsigned &overflow) {
uint64_t sub = a - b;
overflow = (sub > a);
return sub;
}
/// Compute `a - b` and increment `carry` if there was an overflow
static inline uint64_t sub_carry(uint64_t a, uint64_t b, unsigned &carry) {
unsigned overflow;
uint64_t sub = sub_overflow(a, b, overflow);
// Do NOT branch on overflow to avoid jumping code, just add 0 if there was
// no overflow.
carry += overflow;
return sub;
}
/// Update r = r - (t1 + t2) + (t3 + t2) * b ** 10
///
/// This function also yields cbar = floor(r / m) as its return value (int64_t
/// because the value can be -1). With an initial value of r = t0, this can
/// be used for computing the remainder after division by m (see the function
/// mod_m in mulmod.h). The function to_ranlux passes r = 0 and uses only the
/// return value to obtain the decimal expansion after divison by m.
static inline int64_t compute_r(const uint64_t *upper, uint64_t *r) {
// Subtract t1 (24 * 24 = 576 bits)
unsigned carry = 0;
for (int i = 0; i < 9; i++) {
uint64_t r_i = r[i];
r_i = sub_overflow(r_i, carry, carry);
uint64_t t1_i = upper[i];
r_i = sub_carry(r_i, t1_i, carry);
r[i] = r_i;
}
int64_t c = -((int64_t)carry);
// Subtract t2 (only 240 bits, so need to extend)
carry = 0;
for (int i = 0; i < 9; i++) {
uint64_t r_i = r[i];
r_i = sub_overflow(r_i, carry, carry);
uint64_t t2_bits = 0;
if (i < 4) {
t2_bits += upper[i + 5] >> 16;
if (i < 3) {
t2_bits += upper[i + 6] << 48;
}
}
r_i = sub_carry(r_i, t2_bits, carry);
r[i] = r_i;
}
c -= carry;
// r += (t3 + t2) * 2 ** 240
carry = 0;
{
uint64_t r_3 = r[3];
// 16 upper bits
uint64_t t2_bits = (upper[5] >> 16) << 48;
uint64_t t3_bits = (upper[0] << 48);
r_3 = add_carry(r_3, t2_bits, carry);
r_3 = add_carry(r_3, t3_bits, carry);
r[3] = r_3;
}
for (int i = 0; i < 3; i++) {
uint64_t r_i = r[i + 4];
r_i = add_overflow(r_i, carry, carry);
uint64_t t2_bits = (upper[5 + i] >> 32) + (upper[6 + i] << 32);
uint64_t t3_bits = (upper[i] >> 16) + (upper[1 + i] << 48);
r_i = add_carry(r_i, t2_bits, carry);
r_i = add_carry(r_i, t3_bits, carry);
r[i + 4] = r_i;
}
{
uint64_t r_7 = r[7];
r_7 = add_overflow(r_7, carry, carry);
uint64_t t2_bits = (upper[8] >> 32);
uint64_t t3_bits = (upper[3] >> 16) + (upper[4] << 48);
r_7 = add_carry(r_7, t2_bits, carry);
r_7 = add_carry(r_7, t3_bits, carry);
r[7] = r_7;
}
{
uint64_t r_8 = r[8];
r_8 = add_overflow(r_8, carry, carry);
uint64_t t3_bits = (upper[4] >> 16) + (upper[5] << 48);
r_8 = add_carry(r_8, t3_bits, carry);
r[8] = r_8;
}
c += carry;
// c = floor(r / 2 ** 576) has been computed along the way via the carry
// flags. Now if c = 0 and the value currently stored in r is greater or
// equal to m, we need cbar = 1 and subtract m, otherwise cbar = c. The
// value currently in r is greater or equal to m, if and only if one of
// the last 240 bits is set and the upper bits are all set.
bool greater_m = r[0] | r[1] | r[2] | (r[3] & 0x0000ffffffffffff);
greater_m &= (r[3] >> 48) == 0xffff;
for (int i = 4; i < 9; i++) {
greater_m &= (r[i] == UINT64_MAX);
}
return c + (c == 0 && greater_m);
}
#endif
@@ -0,0 +1,255 @@
//
// -*- C++ -*-
//
// -----------------------------------------------------------------------
// HEP Random
// --- RanluxppEngine ---
// helper implementation file
// -----------------------------------------------------------------------
#ifndef RANLUXPP_MULMOD_H
#define RANLUXPP_MULMOD_H
#include "helpers.h"
#include <cstdint>
/// Multiply two 576 bit numbers, stored as 9 numbers of 64 bits each
///
/// \param[in] in1 first factor as 9 numbers of 64 bits each
/// \param[in] in2 second factor as 9 numbers of 64 bits each
/// \param[out] out result with 18 numbers of 64 bits each
static void multiply9x9(const uint64_t *in1, const uint64_t *in2,
uint64_t *out) {
uint64_t next = 0;
unsigned nextCarry = 0;
#if defined(__clang__) || defined(__INTEL_COMPILER)
#pragma unroll
#elif defined(__GNUC__) && __GNUC__ >= 8
// This pragma was introduced in GCC version 8.
#pragma GCC unroll 18
#endif
for (int i = 0; i < 18; i++) {
uint64_t current = next;
unsigned carry = nextCarry;
next = 0;
nextCarry = 0;
#if defined(__clang__) || defined(__INTEL_COMPILER)
#pragma unroll
#elif defined(__GNUC__) && __GNUC__ >= 8
// This pragma was introduced in GCC version 8.
#pragma GCC unroll 9
#endif
for (int j = 0; j < 9; j++) {
int k = i - j;
if (k < 0 || k >= 9)
continue;
uint64_t fac1 = in1[j];
uint64_t fac2 = in2[k];
#if defined(__SIZEOF_INT128__) && !defined(CLHEP_NO_INT128)
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
unsigned __int128 prod = fac1;
prod = prod * fac2;
uint64_t upper = prod >> 64;
uint64_t lower = static_cast<uint64_t>(prod);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#else
uint64_t upper1 = fac1 >> 32;
uint64_t lower1 = static_cast<uint32_t>(fac1);
uint64_t upper2 = fac2 >> 32;
uint64_t lower2 = static_cast<uint32_t>(fac2);
// Multiply 32-bit parts, each product has a maximum value of
// (2 ** 32 - 1) ** 2 = 2 ** 64 - 2 * 2 ** 32 + 1.
uint64_t upper = upper1 * upper2;
uint64_t middle1 = upper1 * lower2;
uint64_t middle2 = lower1 * upper2;
uint64_t lower = lower1 * lower2;
// When adding the two products, the maximum value for middle is
// 2 * 2 ** 64 - 4 * 2 ** 32 + 2, which exceeds a uint64_t.
unsigned overflow;
uint64_t middle = add_overflow(middle1, middle2, overflow);
// Handling the overflow by a multiplication with 0 or 1 is cheaper
// than branching with an if statement, which the compiler does not
// optimize to this equivalent code. Note that we could do entirely
// without this overflow handling when summing up the intermediate
// products differently as described in the following SO answer:
// https://stackoverflow.com/a/51587262
// However, this approach takes at least the same amount of thinking
// why a) the code gives the same results without b) overflowing due
// to the mixture of 32 bit arithmetic. Moreover, my tests show that
// the scheme implemented here is actually slightly more performant.
uint64_t overflow_add = overflow * (uint64_t(1) << 32);
// This addition can never overflow because the maximum value of upper
// is 2 ** 64 - 2 * 2 ** 32 + 1 (see above). When now adding another
// 2 ** 32, the result is 2 ** 64 - 2 ** 32 + 1 and still smaller than
// the maximum 2 ** 64 - 1 that can be stored in a uint64_t.
upper += overflow_add;
uint64_t middle_upper = middle >> 32;
uint64_t middle_lower = middle << 32;
lower = add_overflow(lower, middle_lower, overflow);
upper += overflow;
// This still can't overflow since the maximum of middle_upper is
// - 2 ** 32 - 4 if there was an overflow for middle above, bringing
// the maximum value of upper to 2 ** 64 - 2.
// - otherwise upper still has the initial maximum value given above
// and the addition of a value smaller than 2 ** 32 brings it to
// a maximum value of 2 ** 64 - 2 ** 32 + 2.
// (Both cases include the increment to handle the overflow in lower.)
//
// All the reasoning makes perfect sense given that the product of two
// 64 bit numbers is smaller than or equal to
// (2 ** 64 - 1) ** 2 = 2 ** 128 - 2 * 2 ** 64 + 1
// with the upper bits matching the 2 ** 64 - 2 of the first case.
upper += middle_upper;
#endif
// Add to current, remember carry.
current = add_carry(current, lower, carry);
// Add to next, remember nextCarry.
next = add_carry(next, upper, nextCarry);
}
next = add_carry(next, carry, nextCarry);
out[i] = current;
}
}
/// Compute a value congruent to mul modulo m less than 2 ** 576
///
/// \param[in] mul product from multiply9x9 with 18 numbers of 64 bits each
/// \param[out] out result with 9 numbers of 64 bits each
///
/// \f$ m = 2^{576} - 2^{240} + 1 \f$
///
/// The result in out is guaranteed to be smaller than the modulus.
static void mod_m(const uint64_t *mul, uint64_t *out) {
uint64_t r[9];
// Assign r = t0
for (int i = 0; i < 9; i++) {
r[i] = mul[i];
}
int64_t c = compute_r(mul + 9, r);
// To update r = r - c * m, it suffices to know c * (-2 ** 240 + 1)
// because the 2 ** 576 will cancel out. Also note that c may be zero, but
// the operation is still performed to avoid branching.
// c * (-2 ** 240 + 1) in 576 bits looks as follows, depending on c:
// - if c = 0, the number is zero.
// - if c = 1: bits 576 to 240 are set,
// bits 239 to 1 are zero, and
// the last one is set
// - if c = -1, which corresponds to all bits set (signed int64_t):
// bits 576 to 240 are zero and the rest is set.
// Note that all bits except the last are exactly complimentary (unless c = 0)
// and the last byte is conveniently represented by c already.
// Now construct the three bit patterns from c, their names correspond to the
// assembly implementation by Alexei Sibidanov.
// c = 0 -> t0 = 0; c = 1 -> t0 = 0; c = -1 -> all bits set (sign extension)
// (The assembly implementation shifts by 63, which gives the same result.)
int64_t t0 = c >> 1;
// Left shifting negative values is undefined behavior until C++20, cast to
// unsigned.
uint64_t c_unsigned = static_cast<uint64_t>(c);
// c = 0 -> t2 = 0; c = 1 -> upper 16 bits set; c = -1 -> lower 48 bits set
int64_t t2 = t0 - (c_unsigned << 48);
// c = 0 -> t1 = 0; c = 1 -> all bits set (sign extension); c = -1 -> t1 = 0
// (The assembly implementation shifts by 63, which gives the same result.)
int64_t t1 = t2 >> 48;
unsigned carry = 0;
{
uint64_t r_0 = r[0];
uint64_t out_0 = sub_carry(r_0, c, carry);
out[0] = out_0;
}
for (int i = 1; i < 3; i++) {
uint64_t r_i = r[i];
r_i = sub_overflow(r_i, carry, carry);
uint64_t out_i = sub_carry(r_i, t0, carry);
out[i] = out_i;
}
{
uint64_t r_3 = r[3];
r_3 = sub_overflow(r_3, carry, carry);
uint64_t out_3 = sub_carry(r_3, t2, carry);
out[3] = out_3;
}
for (int i = 4; i < 9; i++) {
uint64_t r_i = r[i];
r_i = sub_overflow(r_i, carry, carry);
uint64_t out_i = sub_carry(r_i, t1, carry);
out[i] = out_i;
}
}
/// Combine multiply9x9 and mod_m with internal temporary storage
///
/// \param[in] in1 first factor with 9 numbers of 64 bits each
/// \param[inout] inout second factor and also the output of the same size
///
/// The result in inout is guaranteed to be smaller than the modulus.
static void mulmod(const uint64_t *in1, uint64_t *inout) {
uint64_t mul[2 * 9] = {0};
multiply9x9(in1, inout, mul);
mod_m(mul, inout);
}
/// Compute base to the n modulo m
///
/// \param[in] base with 9 numbers of 64 bits each
/// \param[out] res output with 9 numbers of 64 bits each
/// \param[in] n exponent
///
/// The arguments base and res may point to the same location.
static void powermod(const uint64_t *base, uint64_t *res, uint64_t n) {
uint64_t fac[9] = {0};
fac[0] = base[0];
res[0] = 1;
for (int i = 1; i < 9; i++) {
fac[i] = base[i];
res[i] = 0;
}
uint64_t mul[18] = {0};
while (n) {
if (n & 1) {
multiply9x9(res, fac, mul);
mod_m(mul, res);
}
n >>= 1;
if (!n)
break;
multiply9x9(fac, fac, mul);
mod_m(mul, fac);
}
}
#endif
@@ -0,0 +1,88 @@
//
// -*- C++ -*-
//
// -----------------------------------------------------------------------
// HEP Random
// --- RanluxppEngine ---
// helper implementation file
// -----------------------------------------------------------------------
#ifndef RANLUXPP_RANLUX_LCG_H
#define RANLUXPP_RANLUX_LCG_H
#include "helpers.h"
#include <cstdint>
/// Convert RANLUX numbers to an LCG state
///
/// \param[in] ranlux the RANLUX numbers as 576 bits
/// \param[out] lcg the 576 bits of the LCG state, smaller than m
/// \param[in] c the carry bit of the RANLUX state
///
/// \f$ m = 2^{576} - 2^{240} + 1 \f$
static void to_lcg(const uint64_t *ranlux, unsigned c, uint64_t *lcg) {
unsigned carry = 0;
// Subtract the final 240 bits.
for (int i = 0; i < 9; i++) {
uint64_t ranlux_i = ranlux[i];
uint64_t lcg_i = sub_overflow(ranlux_i, carry, carry);
uint64_t bits = 0;
if (i < 4) {
bits += ranlux[i + 5] >> 16;
if (i < 3) {
bits += ranlux[i + 6] << 48;
}
}
lcg_i = sub_carry(lcg_i, bits, carry);
lcg[i] = lcg_i;
}
// Add and propagate the carry bit.
for (int i = 0; i < 9; i++) {
lcg[i] = add_overflow(lcg[i], c, c);
}
}
/// Convert an LCG state to RANLUX numbers
///
/// \param[in] lcg the 576 bits of the LCG state, must be smaller than m
/// \param[out] ranlux the RANLUX numbers as 576 bits
/// \param[out] c the carry bit of the RANLUX state
///
/// \f$ m = 2^{576} - 2^{240} + 1 \f$
static void to_ranlux(const uint64_t *lcg, uint64_t *ranlux, unsigned &c_out) {
uint64_t r[9] = {0};
int64_t c = compute_r(lcg, r);
// ranlux = t1 + t2 + c
unsigned carry = 0;
for (int i = 0; i < 9; i++) {
uint64_t in_i = lcg[i];
uint64_t tmp_i = add_overflow(in_i, carry, carry);
uint64_t bits = 0;
if (i < 4) {
bits += lcg[i + 5] >> 16;
if (i < 3) {
bits += lcg[i + 6] << 48;
}
}
tmp_i = add_carry(tmp_i, bits, carry);
ranlux[i] = tmp_i;
}
// If c = -1, we need to add it to all components.
int64_t c1 = c >> 1;
ranlux[0] = add_overflow(ranlux[0], c, carry);
for (int i = 1; i < 9; i++) {
uint64_t ranlux_i = ranlux[i];
ranlux_i = add_overflow(ranlux_i, carry, carry);
ranlux_i = add_carry(ranlux_i, c1, carry);
}
c_out = carry;
}
#endif