Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
correlated_rng.cpp
Go to the documentation of this file.
1#include <cmath>
2#include <cstddef>
3#include <iomanip>
4#include <iostream>
5#include <limits>
6#include <memory>
7#include <random>
8#include <stdexcept>
9#include <string>
10#include <vector>
11#include <algorithm>
12
13using Matrix = std::vector<std::vector<double>>;
14using Vector = std::vector<double>;
15
16static constexpr double EPS_SYM = 1e-10;
17static constexpr double EPS_DIAG = 1e-8;
18
20 int n;
21 if (!(std::cin >> n) || n <= 0) {
22 throw std::runtime_error("Impossible to read n (matrix size).");
23 }
24 Matrix A(static_cast<size_t>(n), std::vector<double>(static_cast<size_t>(n)));
25 for (int i = 0; i < n; ++i) {
26 for (int j = 0; j < n; ++j) {
27 if (!(std::cin >> A[i][j])) {
28 throw std::runtime_error("Matrix lecture has failed.");
29 }
30 }
31 }
32 return A;
33}
34
35void printVector(const Vector& v) {
36 std::cout << std::fixed << std::setprecision(6);
37 for (size_t i = 0; i < v.size(); ++i) {
38 if (i) std::cout << " ";
39 std::cout << v[i];
40 }
41 std::cout << "\n";
42}
43
45 virtual ~IMarginalDistribution() = default;
46
47 virtual Vector sample(std::size_t n) = 0;
48};
49
51 virtual ~IDecomposition() = default;
52
53 virtual Matrix factorize(const Matrix& R) = 0;
54};
55
57public:
58 void validate(const Matrix& R) const {
59 const size_t n = R.size();
60 if (n == 0) throw std::invalid_argument("Invalid matrix.");
61 for (const auto& row : R) {
62 if (row.size() != n) throw std::invalid_argument("Non squared matrix.");
63 }
64
65 //->check sym
66 for (size_t i = 0; i < n; ++i) {
67 for (size_t j = i + 1; j < n; ++j) {
68 if (std::fabs(R[i][j] - R[j][i]) > EPS_SYM) {
69 throw std::invalid_argument("Non symmetric matrix.");
70 }
71 }
72 }
73 //->check diag
74 for (size_t i = 0; i < n; ++i) {
75 if (std::fabs(R[i][i] - 1.0) > EPS_DIAG) {
76 throw std::invalid_argument("Diagonal elements of the matrix needs to be ones");
77 }
78 }
79 // positive-dev check with Cholesky
80 }
81};
82
83// Cholesky
85public:
86 Matrix factorize(const Matrix& R) override {
87 const size_t n = R.size();
88 Matrix L(n, std::vector<double>(n, 0.0));
89
90 for (size_t i = 0; i < n; ++i) {
91 for (size_t j = 0; j <= i; ++j) {
92 double sum = R[i][j];
93 for (size_t k = 0; k < j; ++k) {
94 sum -= L[i][k] * L[j][k];
95 }
96 if (i == j) {
97 if (sum <= 0.0) {
98 throw std::invalid_argument(
99 "Matrix is not positive-definite for Cholesky decomposition.");
100 }
101 L[i][j] = std::sqrt(sum);
102 } else {
103 L[i][j] = sum / L[j][j];
104 }
105 }
106 }
107 return L;
108 }
109};
110
112public:
113 explicit GaussianMarginal(unsigned int seed = std::random_device{}())
114 : eng_(seed), dist_(0.0, 1.0) {}
115
116 Vector sample(std::size_t n) override {
117 Vector z(n);
118 for (std::size_t i = 0; i < n; ++i) z[i] = dist_(eng_);
119 return z;
120 }
121
122private:
123 std::mt19937 eng_;
124 std::normal_distribution<double> dist_;
125};
126
128public:
129 static std::unique_ptr<IMarginalDistribution> create(const std::string& name,
130 unsigned int seed = std::random_device{}()) {
131 std::string lower = name;
132 std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) {
133 return static_cast<char>(std::tolower(c));
134 });
135
136 if (lower == "gaussian" || lower == "normal" || lower == "gauss") {
137 return std::make_unique<GaussianMarginal>(seed);
138 }
139
140 throw std::invalid_argument("Unkwown distribution: " + name +
141 " (try: gaussian|normal)");
142 }
143};
144
146public:
147 JointDistribution(std::unique_ptr<IMarginalDistribution> dist,
148 std::unique_ptr<IDecomposition> decomp)
149 : dist_(std::move(dist)), decomp_(std::move(decomp)) {}
150
151 // y = L * z, z ~ i.i.d. (E=0, Var=1). Cov(y) = L L^T = R.
152 Vector generate(const Matrix& correlation) const {
154 Matrix L = decomp_->factorize(correlation);
155 const std::size_t n = L.size();
156
157 Vector z = dist_->sample(n);
158 Vector y(n, 0.0);
159
160 for (std::size_t i = 0; i < n; ++i) {
161 double acc = 0.0;
162 for (std::size_t k = 0; k <= i; ++k) {
163 acc += L[i][k] * z[k];
164 }
165 y[i] = acc;
166 }
167 return y;
168 }
169
170private:
171 std::unique_ptr<IMarginalDistribution> dist_;
172 std::unique_ptr<IDecomposition> decomp_;
173};
174
175void printUsage(const char* prog) {
176 std::cerr
177 << "Usage: " << prog << " [distribution=gaussian] [seed (optionnel)] < matrice.txt\n"
178 << " - La matrice d'entree est lue sur stdin au format:\n"
179 << " n\\n\n"
180 << " r11 r12 ... r1n\\n\n"
181 << " ...\\n"
182 << " rn1 rn2 ... rnn\\n\n"
183 << " - Distribution supportee: gaussian|normal\n"
184 << "Exemple:\n"
185 << " " << prog << " gaussian 12345 < my_corr.txt\n";
186}
187
188int main(int argc, char** argv) {
189 try {
190 std::string distName = "gaussian";
191 unsigned int seed = std::random_device{}();
192
193 if (argc >= 2) {
194 std::string arg1 = argv[1];
195 if (arg1 == "-h" || arg1 == "--help") {
196 printUsage(argv[0]);
197 return 0;
198 }
199 distName = arg1;
200 }
201 if (argc >= 3) {
202 try {
203 seed = static_cast<unsigned int>(std::stoul(argv[2]));
204 } catch (...) {
205 std::cerr << "Avertissement: seed invalide, utilisation d'un seed aleatoire.\n";
206 seed = std::random_device{}();
207 }
208 }
209
211
212 auto dist = DistributionFactory::create(distName, seed);
213 auto decomp = std::make_unique<CholeskyDecomposition>();
214
215 JointDistribution generator(std::move(dist), std::move(decomp));
216 Vector y = generator.generate(R);
217
218 printVector(y);
219 return 0;
220 } catch (const std::exception& ex) {
221 std::cerr << "Erreur: " << ex.what() << "\n";
222 printUsage(argv[0]);
223 return 1;
224 }
225}
Matrix factorize(const Matrix &R) override
void validate(const Matrix &R) const
static std::unique_ptr< IMarginalDistribution > create(const std::string &name, unsigned int seed=std::random_device{}())
One-dimensional Gaussian marginal distribution.
Vector sample(std::size_t n) override
GaussianMarginal(unsigned int seed=std::random_device{}())
Vector generate(const Matrix &correlation) const
JointDistribution(std::unique_ptr< IMarginalDistribution > dist, std::unique_ptr< IDecomposition > decomp)
void printUsage(const char *prog)
std::vector< std::vector< double > > Matrix
std::vector< double > Vector
void printVector(const Vector &v)
Matrix readMatrixFromStdin()
Hash specialization for SymbolId<Tag>.
Definition BlockName.h:353
virtual ~IDecomposition()=default
virtual Matrix factorize(const Matrix &R)=0
Abstract interface for scalar marginal distributions.
virtual Vector sample(std::size_t n)=0
virtual ~IMarginalDistribution()=default