Spectra 1.1.0
Header-only C++ Library for Large Scale Eigenvalue Problems
Loading...
Searching...
No Matches
SimpleRandom.h
1// Copyright (C) 2016-2025 Yixuan Qiu <yixuan.qiu@cos.name>
2//
3// This Source Code Form is subject to the terms of the Mozilla
4// Public License v. 2.0. If a copy of the MPL was not distributed
5// with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7#ifndef SPECTRA_SIMPLE_RANDOM_H
8#define SPECTRA_SIMPLE_RANDOM_H
9
10#include <Eigen/Core>
11#include <complex>
12
14
15namespace Spectra {
16
17// We need a simple pseudo random number generator here:
18// 1. It is used to generate initial and restarted residual vector.
19// 2. It is not necessary to be so "random" and advanced. All we hope
20// is that the residual vector is not in the space spanned by the
21// current Krylov space. This should be met almost surely.
22// 3. We don't want to call RNG in C++, since we actually want the
23// algorithm to be deterministic. Also, calling RNG in C/C++ is not
24// allowed in R packages submitted to CRAN.
25// 4. The method should be as simple as possible, so an LCG is enough.
26// 5. Based on public domain code by Ray Gardner
27// http://stjarnhimlen.se/snippets/rg_rand.c
28
29// Given a 32-bit integer as the seed, generate a pseudo random 32-bit integer
30inline long next_long_rand(long seed)
31{
32 constexpr unsigned int m_a = 16807; // multiplier
33 constexpr unsigned long m_max = 2147483647L; // 2^31 - 1
34
35 unsigned long lo, hi;
36
37 lo = m_a * (long) (seed & 0xFFFF);
38 hi = m_a * (long) ((unsigned long) seed >> 16);
39 lo += (hi & 0x7FFF) << 16;
40 if (lo > m_max)
41 {
42 lo &= m_max;
43 ++lo;
44 }
45 lo += hi >> 15;
46 if (lo > m_max)
47 {
48 lo &= m_max;
49 ++lo;
50 }
51 return (long) lo;
52}
53
54// Generate a random scalar from the given random seed
55// Also overwrite seed with the new random integer
56template <typename Scalar>
57struct RandomScalar
58{
59 static Scalar run(long& seed)
60 {
61 constexpr unsigned long m_max = 2147483647L; // 2^31 - 1
62
63 seed = next_long_rand(seed);
64 return Scalar(seed) / Scalar(m_max) - Scalar(0.5);
65 }
66};
67// Specialization for complex values
68template <typename RealScalar>
69struct RandomScalar<std::complex<RealScalar>>
70{
71 static std::complex<RealScalar> run(long& seed)
72 {
73 RealScalar r = RandomScalar<RealScalar>::run(seed);
74 RealScalar i = RandomScalar<RealScalar>::run(seed);
75 return std::complex<RealScalar>(r, i);
76 }
77};
78
79// A simple random generator class
80template <typename Scalar = double>
81class SimpleRandom
82{
83private:
84 // The real part type of the matrix element
85 using RealScalar = typename Eigen::NumTraits<Scalar>::Real;
86 using Index = Eigen::Index;
87 using Vector = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
88
89 long m_rand; // RNG state
90
91public:
92 SimpleRandom(unsigned long init_seed)
93 {
94 constexpr unsigned long m_max = 2147483647L; // 2^31 - 1
95 m_rand = init_seed ? (init_seed & m_max) : 1;
96 }
97
98 // Return a single random number, ranging from -0.5 to 0.5
99 Scalar random()
100 {
101 return RandomScalar<Scalar>::run(m_rand);
102 }
103
104 // Fill the given vector with random numbers
105 // Ranging from -0.5 to 0.5
106 void random_vec(Vector& vec)
107 {
108 const Index len = vec.size();
109 for (Index i = 0; i < len; i++)
110 {
111 vec[i] = random();
112 }
113 }
114
115 // Return a vector of random numbers
116 // Ranging from -0.5 to 0.5
117 Vector random_vec(const Index len)
118 {
119 Vector res(len);
120 random_vec(res);
121 return res;
122 }
123};
124
125} // namespace Spectra
126
128
129#endif // SPECTRA_SIMPLE_RANDOM_H