LdsGen 1.2.6
Loading...
Searching...
No Matches
lds.hpp
Go to the documentation of this file.
1#pragma once
2
7#include <array>
8#include <atomic>
9#include <cmath>
10#include <concepts>
11#include <cstddef>
12#include <iterator>
13#include <limits>
14
15#ifndef M_PI
16# define M_PI 3.14159265358979323846264338327950288
17#endif
18
19namespace ldsgen {
20
21 constexpr const auto TWO_PI = 2.0 * M_PI;
22
23 // Constants for magic numbers
24 constexpr unsigned int MAX_REVERSE_BITS = 64;
25 constexpr double MAPPING_FACTOR = 2.0;
26
27 namespace detail {
28
49 template <typename T, typename Table>
50 constexpr auto vdc_digit_sum(unsigned long n, unsigned long base, const Table& weights)
51 -> T {
52 T reslt{};
53 std::size_t idx = 0;
54 while (n != 0) {
55 const auto remainder = n % base;
56 n /= base;
57 reslt += static_cast<T>(remainder) * weights[idx];
58 ++idx;
59 }
60 return reslt;
61 }
62
63 } // namespace detail
64
79 template <typename Generator, typename Value> class GeneratorIterator {
80 Generator* gen;
81 unsigned long index;
82
83 public:
84 using iterator_category = std::input_iterator_tag;
85 using value_type = Value;
86 using difference_type = std::ptrdiff_t;
87 using pointer = const value_type*;
89
90 explicit GeneratorIterator(Generator* g = nullptr, unsigned long idx = 0)
91 : gen{g}, index{idx} {}
92
96 auto operator*() const -> Value {
97 if (gen) {
98 auto temp_idx = gen->get_index();
99 gen->reseed(index);
100 auto value = gen->pop();
101 gen->reseed(temp_idx);
102 return value;
103 }
104 return Value{};
105 }
106
111 ++index;
112 return *this;
113 }
114
119 auto temp = *this;
120 ++index;
121 return temp;
122 }
123
127 auto operator==(const GeneratorIterator& other) const -> bool {
128 return index == other.index;
129 }
130
134 auto operator!=(const GeneratorIterator& other) const -> bool {
135 return index != other.index;
136 }
137
141 [[nodiscard]] auto get_index() const -> unsigned long { return index; }
142 };
143
154 template <typename G, typename V>
155 concept SequenceGenerator = requires(G g, unsigned long n) {
156 { g.pop() } -> std::convertible_to<V>;
157 { g.peek() } -> std::convertible_to<V>;
158 g.skip(static_cast<unsigned int>(n));
159 g.reseed(n);
160 { g.get_index() } -> std::convertible_to<unsigned long>;
161 };
162
182 template <typename Derived, typename Value> class GeneratorBase {
183 public:
191 auto pop() -> Value {
192 auto count_value
193 = this->count_.fetch_add(1, std::memory_order_relaxed) + 1; // ignore 0
194 return derived().value_at(count_value);
195 }
196
202 [[nodiscard]] auto peek() -> Value {
203 auto count_value = this->count_.load(std::memory_order_relaxed) + 1;
204 return derived().value_at(count_value);
205 }
206
212 auto skip(unsigned int n) -> void { this->count_.fetch_add(n, std::memory_order_relaxed); }
213
219 auto reseed(const unsigned long& seed) -> void {
220 this->count_.store(seed, std::memory_order_relaxed);
221 }
222
228 [[nodiscard]] auto get_index() const -> unsigned long {
229 return this->count_.load(std::memory_order_relaxed);
230 }
231
232 protected:
233 std::atomic<unsigned long> count_{0};
234
235 private:
236 friend Derived; // for classes deriving directly from GeneratorBase (ilds.hpp, lds_n.hpp)
237 template <typename, typename> friend class GeneratorIterable;
238 GeneratorBase() = default;
239
240 auto derived() -> Derived& { return static_cast<Derived&>(*this); }
241 };
242
253 template <typename Derived, typename Value> class GeneratorIterable
254 : public GeneratorBase<Derived, Value> {
255
256 private:
257 friend Derived;
258 GeneratorIterable() = default;
259
260 public:
267 return GeneratorIterator<Derived, Value>(static_cast<Derived*>(this));
268 }
269
277 [[nodiscard]] auto end() const -> GeneratorIterator<Derived, Value> {
279 std::numeric_limits<unsigned long>::max());
280 }
281 };
282
301 constexpr auto vdc(unsigned long count, unsigned long base) -> double {
302 auto reslt = 0.0;
303 auto denom = 1.0;
304 while (count != 0) {
305 const auto remainder = count % base;
306 count /= base;
307 denom *= static_cast<double>(base);
308 reslt += static_cast<double>(remainder) / denom;
309 }
310 return reslt;
311 }
312
346 class VdCorput : public GeneratorIterable<VdCorput, double> {
347 unsigned long base;
348 std::array<double, MAX_REVERSE_BITS> rev_lst{};
349 static_assert(MAX_REVERSE_BITS >= sizeof(unsigned long) * 8,
350 "MAX_REVERSE_BITS must be at least the number of bits in unsigned long");
351
352 public:
362 explicit VdCorput(const unsigned long base) : base{base} {
363 double reverse = 1.0;
364 for (unsigned int i = 0; i < MAX_REVERSE_BITS; ++i) {
365 reverse /= static_cast<double>(base);
366 this->rev_lst[i] = reverse;
367 }
368 }
369
380 auto value_at(unsigned long n) const -> double {
381 return detail::vdc_digit_sum<double>(n, this->base, this->rev_lst);
382 }
383 };
384
417 class Halton : public GeneratorIterable<Halton, std::array<double, 2>> {
418 VdCorput vdc0;
419 VdCorput vdc1;
420
421 public:
431 Halton(const unsigned long base0, const unsigned long base1) : vdc0(base0), vdc1(base1) {}
432
441 auto value_at(unsigned long n) const -> std::array<double, 2> {
442 return {this->vdc0.value_at(n), this->vdc1.value_at(n)};
443 }
444 };
445
481 class Circle : public GeneratorIterable<Circle, std::array<double, 2>> {
482 VdCorput vdc;
483
484 public:
493 explicit Circle(const unsigned long base) : vdc(base) {}
494
503 auto value_at(unsigned long n) const -> std::array<double, 2> {
504 auto theta = this->vdc.value_at(n) * TWO_PI; // map to [0, 2*pi];
505 return {std::cos(theta), std::sin(theta)};
506 }
507 };
508
546 class Disk : public GeneratorIterable<Disk, std::array<double, 2>> {
547 VdCorput vdc0;
548 VdCorput vdc1;
549
550 public:
559 Disk(const unsigned long base0, const unsigned long base1) : vdc0(base0), vdc1(base1) {}
560
570 auto value_at(unsigned long n) const -> std::array<double, 2> {
571 auto theta = this->vdc0.value_at(n) * TWO_PI; // map to [0, 2*pi];
572 auto radius = std::sqrt(this->vdc1.value_at(n));
573 return {radius * std::cos(theta), radius * std::sin(theta)};
574 }
575 };
576
615 class Sphere : public GeneratorIterable<Sphere, std::array<double, 3>> {
616 VdCorput vdcgen;
617 Circle cirgen;
618
619 public:
629 Sphere(const unsigned long base0, const unsigned long base1)
630 : vdcgen(base0), cirgen(base1) {}
631
641 auto value_at(unsigned long n) const -> std::array<double, 3> {
642 auto cosphi = (MAPPING_FACTOR * this->vdcgen.value_at(n)) - 1.0; // map to [-1, 1];
643 auto sinphi = std::sqrt(1.0 - (cosphi * cosphi));
644 auto arr = this->cirgen.value_at(n);
645 return {sinphi * arr[0], sinphi * arr[1], cosphi};
646 }
647 };
648
677 class Sphere3Hopf : public GeneratorIterable<Sphere3Hopf, std::array<double, 4>> {
678 VdCorput vdc0;
679 VdCorput vdc1;
680 VdCorput vdc2;
681
682 public:
693 Sphere3Hopf(const unsigned long base0, const unsigned long base1, const unsigned long base2)
694 : vdc0(base0), vdc1(base1), vdc2(base2) {}
695
714 auto value_at(unsigned long n) const -> std::array<double, 4> {
715 auto phi = this->vdc0.value_at(n) * TWO_PI; // map to [0, 2*pi];
716 auto psy = this->vdc1.value_at(n) * TWO_PI; // map to [0, 2*pi];
717 auto vdc = this->vdc2.value_at(n);
718 auto cos_eta = std::sqrt(vdc);
719 auto sin_eta = std::sqrt(1.0 - vdc);
720 return {
721 cos_eta * std::cos(psy),
722 cos_eta * std::sin(psy),
723 sin_eta * std::cos(phi + psy),
724 sin_eta * std::sin(phi + psy),
725 };
726 }
727 };
728
729 // Compile-time contract checks: every generator satisfies the protocol concept.
730 static_assert(SequenceGenerator<VdCorput, double>);
731 static_assert(SequenceGenerator<Halton, std::array<double, 2>>);
732 static_assert(SequenceGenerator<Circle, std::array<double, 2>>);
733 static_assert(SequenceGenerator<Disk, std::array<double, 2>>);
734 static_assert(SequenceGenerator<Sphere, std::array<double, 3>>);
735 static_assert(SequenceGenerator<Sphere3Hopf, std::array<double, 4>>);
736
742 extern unsigned long dummy(unsigned int index);
743} // namespace ldsgen
Circle sequence generator.
Definition lds.hpp:481
Circle(const unsigned long base)
Construct a new Circle object.
Definition lds.hpp:493
auto value_at(unsigned long n) const -> std::array< double, 2 >
Evaluate the point on the unit circle at a given index (pure, no state change)
Definition lds.hpp:503
Disk sequence generator.
Definition lds.hpp:546
Disk(const unsigned long base0, const unsigned long base1)
Construct a new Disk object.
Definition lds.hpp:559
auto value_at(unsigned long n) const -> std::array< double, 2 >
Evaluate the point in the unit disk at a given index (pure, no state change)
Definition lds.hpp:570
CRTP base implementing the sequence generator protocol.
Definition lds.hpp:182
auto get_index() const -> unsigned long
Get current index in the sequence.
Definition lds.hpp:228
auto pop() -> Value
Generate the next value in the sequence (advances state).
Definition lds.hpp:191
auto skip(unsigned int n) -> void
Skip n values in the sequence.
Definition lds.hpp:212
auto reseed(const unsigned long &seed) -> void
Reset the generator to a specific seed value.
Definition lds.hpp:219
std::atomic< unsigned long > count_
Current sequence index (single source of state)
Definition lds.hpp:233
auto peek() -> Value
Peek at the next value without advancing state.
Definition lds.hpp:202
CRTP mixin adding STL iterator support to a GeneratorBase.
Definition lds.hpp:254
auto end() const -> GeneratorIterator< Derived, Value >
Get iterator to end (infinite sequence)
Definition lds.hpp:277
auto begin() -> GeneratorIterator< Derived, Value >
Get iterator to beginning.
Definition lds.hpp:266
Forward iterator for sequence generators.
Definition lds.hpp:79
auto get_index() const -> unsigned long
Get current index.
Definition lds.hpp:141
const value_type * pointer
Definition lds.hpp:87
value_type reference
Definition lds.hpp:88
auto operator!=(const GeneratorIterator &other) const -> bool
Inequality comparison.
Definition lds.hpp:134
std::input_iterator_tag iterator_category
Definition lds.hpp:84
auto operator==(const GeneratorIterator &other) const -> bool
Equality comparison.
Definition lds.hpp:127
auto operator++(int) -> GeneratorIterator
Post-increment operator.
Definition lds.hpp:118
Value value_type
Definition lds.hpp:85
auto operator++() -> GeneratorIterator &
Pre-increment operator.
Definition lds.hpp:110
GeneratorIterator(Generator *g=nullptr, unsigned long idx=0)
Definition lds.hpp:90
auto operator*() const -> Value
Dereference operator.
Definition lds.hpp:96
std::ptrdiff_t difference_type
Definition lds.hpp:86
Halton sequence generator.
Definition lds.hpp:417
Halton(const unsigned long base0, const unsigned long base1)
Construct a new Halton object.
Definition lds.hpp:431
auto value_at(unsigned long n) const -> std::array< double, 2 >
Evaluate the 2D Halton point at a given index (pure, no state change)
Definition lds.hpp:441
S(3) sequence generator by Hopf fibration.
Definition lds.hpp:677
auto value_at(unsigned long n) const -> std::array< double, 4 >
Evaluate the point on the 3-sphere at a given index (pure, no state change)
Definition lds.hpp:714
Sphere3Hopf(const unsigned long base0, const unsigned long base1, const unsigned long base2)
Construct a new Sphere 3 Hopf object.
Definition lds.hpp:693
Sphere sequence generator.
Definition lds.hpp:615
Sphere(const unsigned long base0, const unsigned long base1)
Construct a new Sphere object.
Definition lds.hpp:629
auto value_at(unsigned long n) const -> std::array< double, 3 >
Evaluate the point on the unit sphere at a given index (pure, no state change)
Definition lds.hpp:641
Van der Corput sequence generator.
Definition lds.hpp:346
auto value_at(unsigned long n) const -> double
Evaluate the sequence value at a given index (pure, no state change)
Definition lds.hpp:380
VdCorput(const unsigned long base)
Construct a new VdCorput object.
Definition lds.hpp:362
Concept for the sequence generator protocol.
Definition lds.hpp:155
#define M_PI
Definition lds.hpp:16
constexpr auto vdc_digit_sum(unsigned long n, unsigned long base, const Table &weights) -> T
Core base-b digit/weight summation shared by all van der Corput generators.
Definition lds.hpp:50
Definition lds.hpp:19
constexpr const auto TWO_PI
Definition lds.hpp:21
constexpr double MAPPING_FACTOR
Definition lds.hpp:25
unsigned long dummy(unsigned int index)
Dummy function (placeholder, not yet implemented).
constexpr unsigned int MAX_REVERSE_BITS
Definition lds.hpp:24
constexpr auto vdc(unsigned long count, unsigned long base) -> double
Van der Corput sequence.
Definition lds.hpp:301