CMP++: Uncertainty Quantification & Bayesian Calibration
Loading...
Searching...
No Matches
grid.h
Go to the documentation of this file.
1#ifndef GRID_H
2#define GRID_H
3
4#include "cmp_defines.h"
5#include <boost/math/special_functions/prime.hpp>
6#include <boost/random/sobol.hpp>
7#include <boost/random/niederreiter_base2.hpp>
8#include <boost/random/faure.hpp>
9#include <poly.h>
10
11namespace cmp::grid {
12
22class Grid {
23 public:
24 virtual Eigen::MatrixXd construct(const size_t &nPoints) = 0;
25 virtual size_t dim() const = 0;
26 virtual ~Grid() = default;
27};
28
44class SobolGrid : public Grid {
45 private:
46 boost::random::sobol gen_;
47 size_t dimension_;
48
49 Eigen::VectorXd lowerBound_;
50 Eigen::VectorXd upperBound_;
51 public:
52 SobolGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound, const Eigen::Ref<const Eigen::VectorXd> &upperBound) : gen_(lowerBound.size()), dimension_(lowerBound.size()), lowerBound_(lowerBound), upperBound_(upperBound) {}
53
54 Eigen::MatrixXd construct(const size_t &nPoints) override {
55 Eigen::MatrixXd points(nPoints, dimension_);
56
57 std::vector<boost::uint_least64_t> seq(dimension_);
58
59 for(size_t i = 0; i < nPoints; ++i) {
60 gen_.generate(seq.begin(), seq.end());
61
62 for(size_t d = 0; d < dimension_; ++d) {
63 points(i, d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * static_cast<double>(seq[d]) / static_cast<double>(gen_.max());
64 }
65 }
66
67 // Make a list of indices and shuffle them
68 Eigen::VectorXs indices(nPoints);
69 std::iota(indices.data(), indices.data() + indices.size(), 0);
70 std::shuffle(indices.data(), indices.data() + indices.size(), gen_);
71
72 points = cmp::slice(points, indices);
73
74 gen_.discard(nPoints * dimension_); // Advance the generator state
75
76 return points;
77 }
78
79 // Pure lazy evaluation
80 Eigen::VectorXd operator()() {
81 Eigen::VectorXd point(dimension_);
82 std::vector<boost::uint_least64_t> seq(dimension_);
83
84 gen_.generate(seq.begin(), seq.end());
85
86 for(size_t d = 0; d < dimension_; ++d) {
87 point(d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * static_cast<double>(seq[d]) / static_cast<double>(gen_.max());
88 }
89 return point;
90 }
91
92 size_t dim() const override {
93 return dimension_;
94 }
95};
96
113class ScrambledSobolGrid : public Grid {
114 private:
115 boost::random::sobol gen_;
116 size_t dimension_;
117
118 Eigen::VectorXd lowerBound_;
119 Eigen::VectorXd upperBound_;
120
121 // We need a unique seed for each dimension's permutation tree
122 std::vector<uint64_t> dim_seeds_;
123
124 // Fast bit-mixer to generate deterministic pseudo-randomness based on bit history
125 static inline uint64_t hash_prefix(uint64_t prefix, uint64_t seed) {
126 uint64_t v = prefix ^ seed;
127 v ^= v >> 30;
128 v *= 0xbf58476d1ce4e5b9ULL;
129 v ^= v >> 27;
130 v *= 0x94d049bb133111ebULL;
131 v ^= v >> 31;
132 return v;
133 }
134
135 // True stateless nested Owen Scrambling
136 static inline uint64_t owen_scramble(uint64_t val, uint64_t seed) {
137 uint64_t result = 0;
138 for(int i = 0; i < 64; ++i) {
139 int shift = 63 - i;
140
141 // The permutation of this bit depends ONLY on the preceding bits (the prefix)
142 uint64_t prefix = (i == 0) ? 0 : (val >> (shift + 1));
143
144 // Hash the prefix and seed to decide whether to flip the current bit
145 uint64_t flip = hash_prefix(prefix, seed) & 1;
146 uint64_t bit = ((val >> shift) & 1) ^ flip;
147
148 result = (result << 1) | bit;
149 }
150 return result;
151 }
152
153 public:
154 ScrambledSobolGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound,
155 const Eigen::Ref<const Eigen::VectorXd> &upperBound,
156 unsigned int seed = 42)
157 : gen_(lowerBound.size()), dimension_(lowerBound.size()),
158 lowerBound_(lowerBound), upperBound_(upperBound) {
159
160 // Generate a unique scrambling seed for each dimension
161 std::default_random_engine rng(seed);
162 dim_seeds_.resize(dimension_);
163 for(size_t i = 0; i < dimension_; ++i) {
164 dim_seeds_[i] = rng();
165 }
166 }
167
168 Eigen::MatrixXd construct(const size_t &nPoints) override {
169 Eigen::MatrixXd points(nPoints, dimension_);
170 for(size_t i = 0; i < nPoints; ++i) {
171 points.row(i) = operator()();
172 }
173 return points;
174 }
175
176 // Pure lazy evaluation
177 Eigen::VectorXd operator()() {
178 Eigen::VectorXd point(dimension_);
179 std::vector<boost::uint_least64_t> seq(dimension_);
180
181 gen_.generate(seq.begin(), seq.end());
182 double max_val = static_cast<double>(gen_.max());
183
184 for(size_t d = 0; d < dimension_; ++d) {
185 // 1. Normalize the boost integer to [0, 1)
186 double u = static_cast<double>(seq[d]) / max_val;
187
188 // 2. Map into a full 64-bit integer representing the fractional bits
189 // (Bit 63 is the 0.5 space cut, bit 62 is 0.25, etc.)
190 // Using 18446744073709551615.0 (which is 2^64 - 1)
191 uint64_t fractional_bits = static_cast<uint64_t>(u * 18446744073709551615.0);
192
193 // 3. Apply the true Owen scrambling algorithm
194 uint64_t scrambled_bits = owen_scramble(fractional_bits, dim_seeds_[d]);
195
196 // 4. Map back to [0, 1] float space
197 double scrambled_u = static_cast<double>(scrambled_bits) / 18446744073709551615.0;
198
199 // 5. Scale to final parameter bounds
200 point(d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * scrambled_u;
201 }
202 return point;
203 }
204
205 size_t dim() const override {
206 return dimension_;
207 }
208};
209
219class MonteCarloGrid : public Grid {
220 private:
221 size_t dimension_;
222 std::default_random_engine rng_;
223 std::uniform_real_distribution<double> dist_;
224
225 Eigen::VectorXd lowerBound_;
226 Eigen::VectorXd upperBound_;
227 public:
234 MonteCarloGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound, const Eigen::Ref<const Eigen::VectorXd> &upperBound, unsigned int seed = 42)
235 : dimension_(lowerBound.size()), lowerBound_(lowerBound), upperBound_(upperBound), rng_(seed), dist_(0.0, 1.0) {}
236
241 Eigen::MatrixXd construct(const size_t &nPoints) override {
242 Eigen::MatrixXd points(nPoints, dimension_);
243 for(size_t i = 0; i < nPoints; ++i) {
244 for(size_t j = 0; j < dimension_; ++j) {
245 points(i, j) = lowerBound_(j) + (upperBound_(j) - lowerBound_(j)) * dist_(rng_);
246 }
247 }
248 return points;
249 }
250
254 Eigen::VectorXd operator()() {
255 Eigen::VectorXd point(dimension_);
256 for(size_t j = 0; j < dimension_; ++j) {
257 point(j) = lowerBound_(j) + (upperBound_(j) - lowerBound_(j)) * dist_(rng_);
258 }
259 return point;
260 }
261
265 size_t dim() const override {
266 return dimension_;
267 }
268};
269
285class LatinHypercubeGrid : public Grid {
286 private:
287 size_t dimension_;
288 std::default_random_engine rng_;
289
290 Eigen::VectorXd lowerBound_;
291 Eigen::VectorXd upperBound_;
292 public:
299 LatinHypercubeGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound, const Eigen::Ref<const Eigen::VectorXd> &upperBound, unsigned int seed = 42)
300 : lowerBound_(lowerBound), upperBound_(upperBound), rng_(seed), dimension_(lowerBound.size()) {}
301
306 Eigen::MatrixXd construct(const size_t &nPoints) override {
307 Eigen::MatrixXd grid_points(nPoints, dimension_);
308
309 std::uniform_real_distribution<double> dist(0.0, 1.0);
310
311 for(size_t j = 0; j < dimension_; j++) {
312 // bins 0..nPoints-1
313 std::vector<size_t> bins(nPoints);
314 std::iota(bins.begin(), bins.end(), 0);
315
316 // shuffle the bin order for this dimension
317 std::shuffle(bins.begin(), bins.end(), rng_);
318
319 // assign a random location inside each bin
320 for(size_t i = 0; i < nPoints; i++) {
321 double u = dist(rng_); // random offset
322 grid_points(i, j) = lowerBound_(j) + (upperBound_(j) - lowerBound_(j)) * (bins[i] + u) / double(nPoints);
323 }
324 }
325
326 return grid_points;
327 }
328
332 size_t dim() const override {
333 return dimension_;
334 }
335};
336
347class LinspacedGrid : public Grid {
348 private:
349 size_t dimension_;
350 Eigen::VectorXd lowerBound_;
351 Eigen::VectorXd upperBound_;
352
356 Eigen::VectorXs gridElement(const size_t &index, const size_t &n_pts, const size_t &dim);
357
358 public:
364 LinspacedGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound, const Eigen::Ref<const Eigen::VectorXd> &upperBound)
365 : lowerBound_(lowerBound), upperBound_(upperBound), dimension_(lowerBound.size()) {}
366
371 Eigen::MatrixXd construct(const size_t &nPoints) override {
372
373 // Compute the number of points per dimension
374 size_t n_pts_per_dim = static_cast<size_t>(std::ceil(std::pow(nPoints, 1.0 / dimension_)));
375 size_t total_points = static_cast<size_t>(std::pow(n_pts_per_dim, dimension_));
376
377 Eigen::MatrixXd grid_points(total_points, dimension_);
378 for(size_t i = 0; i < total_points; i++) {
379
380 //Index will be updated to contain numbers from 0 to n-1
382
383 //Generate current indices for the parameters
384 index = gridElement(i, n_pts_per_dim, dimension_);
385
386 for(size_t j = 0; j < dimension_; j++) {
387
388 // perform a linear mapping to [0,1]
389 grid_points(i, j) = lowerBound_(j) + (upperBound_(j) - lowerBound_(j)) * (index[j] + 0.5) / double(n_pts_per_dim);
390 }
391 }
392
393 return grid_points;
394 }
395
399 size_t dim() const override {
400 return dimension_;
401 }
402};
403
404
411class NiederreiterGrid : public Grid {
412 private:
413 boost::random::niederreiter_base2 gen_;
414 size_t dimension_;
415
416 Eigen::VectorXd lowerBound_;
417 Eigen::VectorXd upperBound_;
418 public:
424 NiederreiterGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound, const Eigen::Ref<const Eigen::VectorXd> &upperBound)
425 : gen_(lowerBound.size()), dimension_(lowerBound.size()), lowerBound_(lowerBound), upperBound_(upperBound) {}
426
431 Eigen::MatrixXd construct(const size_t &nPoints) override {
432 Eigen::MatrixXd points(nPoints, dimension_);
433 std::vector<boost::uint_least64_t> seq(dimension_);
434
435 for(size_t i = 0; i < nPoints; ++i) {
436 gen_.generate(seq.begin(), seq.end());
437
438 for(size_t d = 0; d < dimension_; ++d) {
439 points(i, d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * static_cast<double>(seq[d]) / static_cast<double>(gen_.max());
440 }
441 }
442
443 // Make a list of indices and shuffle them
444 Eigen::VectorXs indices(nPoints);
445 std::iota(indices.data(), indices.data() + indices.size(), 0);
446 std::shuffle(indices.data(), indices.data() + indices.size(), gen_);
447
448 points = cmp::slice(points, indices);
449
450 gen_.discard(nPoints * dimension_); // Advance the generator state
451
452 return points;
453 }
454
458 Eigen::VectorXd operator()() {
459 Eigen::VectorXd point(dimension_);
460 std::vector<boost::uint_least64_t> seq(dimension_);
461
462 gen_.generate(seq.begin(), seq.end());
463
464 for(size_t d = 0; d < dimension_; ++d) {
465 point(d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * static_cast<double>(seq[d]) / static_cast<double>(gen_.max());
466 }
467 return point;
468 }
469
473 size_t dim() const override {
474 return dimension_;
475 }
476};
477
484class FaureGrid : public Grid {
485 private:
486 boost::random::faure gen_;
487 std::default_random_engine rng_;
488 size_t dimension_;
489
490 Eigen::VectorXd lowerBound_;
491 Eigen::VectorXd upperBound_;
492 public:
499 FaureGrid(const Eigen::Ref<const Eigen::VectorXd> &lowerBound, const Eigen::Ref<const Eigen::VectorXd> &upperBound, unsigned int seed = 42)
500 : gen_(lowerBound.size()), rng_(seed), dimension_(lowerBound.size()), lowerBound_(lowerBound), upperBound_(upperBound) {}
501
506 Eigen::MatrixXd construct(const size_t &nPoints) override {
507 Eigen::MatrixXd points(nPoints, dimension_);
508 std::vector<boost::uint_least64_t> seq(dimension_);
509
510 for(size_t i = 0; i < nPoints; ++i) {
511 gen_.generate(seq.begin(), seq.end());
512
513 for(size_t d = 0; d < dimension_; ++d) {
514 points(i, d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * static_cast<double>(seq[d]) / static_cast<double>(gen_.max());
515 }
516 }
517
518 // Make a list of indices and shuffle them
519 Eigen::VectorXs indices(nPoints);
520 std::iota(indices.data(), indices.data() + indices.size(), 0);
521 std::shuffle(indices.data(), indices.data() + indices.size(), rng_);
522
523 points = cmp::slice(points, indices);
524
525 gen_.discard(nPoints * dimension_); // Advance the generator state
526
527 return points;
528 }
529
533 Eigen::VectorXd operator()() {
534 Eigen::VectorXd point(dimension_);
535 std::vector<boost::uint_least64_t> seq(dimension_);
536
537 gen_.generate(seq.begin(), seq.end());
538
539 for(size_t d = 0; d < dimension_; ++d) {
540 point(d) = lowerBound_(d) + (upperBound_(d) - lowerBound_(d)) * static_cast<double>(seq[d]) / static_cast<double>(gen_.max());
541 }
542 return point;
543 }
544
548 size_t dim() const override {
549 return dimension_;
550 }
551};
552
553}
554
555
556#endif
Faure low-discrepancy sequence grid generator.
Definition grid.h:484
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:490
std::default_random_engine rng_
Random engine for shuffling.
Definition grid.h:487
Eigen::VectorXd operator()()
Evaluates a single Faure sample.
Definition grid.h:533
FaureGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound, unsigned int seed=42)
Constructs a FaureGrid generator.
Definition grid.h:499
size_t dim() const override
Returns the dimension of the domain.
Definition grid.h:548
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:491
Eigen::MatrixXd construct(const size_t &nPoints) override
Constructs a matrix of Faure points.
Definition grid.h:506
size_t dimension_
Number of dimensions.
Definition grid.h:488
boost::random::faure gen_
Faure sequence generator.
Definition grid.h:486
Abstract base class for numerical integration grids and space-filling designs.
Definition grid.h:22
virtual Eigen::MatrixXd construct(const size_t &nPoints)=0
virtual ~Grid()=default
virtual size_t dim() const =0
Latin Hypercube Sampling (LHS) grid generator.
Definition grid.h:285
LatinHypercubeGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound, unsigned int seed=42)
Constructs a LatinHypercubeGrid generator.
Definition grid.h:299
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:290
size_t dimension_
Number of dimensions.
Definition grid.h:287
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:291
size_t dim() const override
Returns the dimension of the domain.
Definition grid.h:332
Eigen::MatrixXd construct(const size_t &nPoints) override
Constructs a Latin Hypercube matrix of samples.
Definition grid.h:306
std::default_random_engine rng_
Pseudo-random number generator.
Definition grid.h:288
Linearly spaced Cartesian tensor product grid generator.
Definition grid.h:347
Eigen::VectorXs gridElement(const size_t &index, const size_t &n_pts, const size_t &dim)
Helper to retrieve the multi-dimensional grid index matching a flat index.
Definition grid.cpp:5
LinspacedGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound)
Constructs a LinspacedGrid.
Definition grid.h:364
size_t dimension_
Number of dimensions.
Definition grid.h:349
size_t dim() const override
Returns the dimension of the domain.
Definition grid.h:399
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:350
Eigen::MatrixXd construct(const size_t &nPoints) override
Constructs the linearly spaced grid matrix.
Definition grid.h:371
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:351
Standard Monte Carlo random grid generator.
Definition grid.h:219
Eigen::MatrixXd construct(const size_t &nPoints) override
Constructs a matrix of random Monte Carlo samples.
Definition grid.h:241
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:226
std::default_random_engine rng_
Pseudo-random number generator.
Definition grid.h:222
size_t dimension_
Number of dimensions.
Definition grid.h:221
std::uniform_real_distribution< double > dist_
Uniform distribution range [0, 1].
Definition grid.h:223
MonteCarloGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound, unsigned int seed=42)
Constructs a MonteCarloGrid generator.
Definition grid.h:234
size_t dim() const override
Returns the dimension of the domain.
Definition grid.h:265
Eigen::VectorXd operator()()
Evaluates a single random Monte Carlo sample.
Definition grid.h:254
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:225
Niederreiter (base 2) low-discrepancy sequence grid generator.
Definition grid.h:411
size_t dimension_
Number of dimensions.
Definition grid.h:414
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:416
Eigen::VectorXd operator()()
Evaluates a single Niederreiter sample.
Definition grid.h:458
Eigen::MatrixXd construct(const size_t &nPoints) override
Constructs a matrix of Niederreiter points.
Definition grid.h:431
size_t dim() const override
Returns the dimension of the domain.
Definition grid.h:473
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:417
boost::random::niederreiter_base2 gen_
Niederreiter sequence generator.
Definition grid.h:413
NiederreiterGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound)
Constructs a NiederreiterGrid generator.
Definition grid.h:424
Owen-scrambled Sobol low-discrepancy sequence grid generator.
Definition grid.h:113
static uint64_t owen_scramble(uint64_t val, uint64_t seed)
Definition grid.h:136
static uint64_t hash_prefix(uint64_t prefix, uint64_t seed)
Definition grid.h:125
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:118
Eigen::MatrixXd construct(const size_t &nPoints) override
Definition grid.h:168
boost::random::sobol gen_
Boost Sobol sequence generator.
Definition grid.h:115
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:119
size_t dimension_
Number of dimensions.
Definition grid.h:116
Eigen::VectorXd operator()()
Definition grid.h:177
ScrambledSobolGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound, unsigned int seed=42)
Definition grid.h:154
size_t dim() const override
Definition grid.h:205
std::vector< uint64_t > dim_seeds_
Seeds used to initialize scrambling for each coordinate dimension.
Definition grid.h:122
Sobol low-discrepancy sequence grid generator.
Definition grid.h:44
Eigen::VectorXd operator()()
Definition grid.h:80
Eigen::VectorXd lowerBound_
Lower bounds vector.
Definition grid.h:49
Eigen::MatrixXd construct(const size_t &nPoints) override
Definition grid.h:54
boost::random::sobol gen_
Boost Sobol sequence generator.
Definition grid.h:46
SobolGrid(const Eigen::Ref< const Eigen::VectorXd > &lowerBound, const Eigen::Ref< const Eigen::VectorXd > &upperBound)
Definition grid.h:52
size_t dimension_
Number of dimensions.
Definition grid.h:47
size_t dim() const override
Definition grid.h:92
Eigen::VectorXd upperBound_
Upper bounds vector.
Definition grid.h:50
Matrix< size_t, Eigen::Dynamic, 1 > VectorXs
Definition cmp_defines.h:20
Definition grid.h:11
Derived::PlainObject slice(const Eigen::MatrixBase< Derived > &mat, const Eigen::VectorXs &indices)
Slices a matrix along its rows based on a set of indices.
Definition cmp_defines.h:178