|
| 1 | +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | + |
| 3 | +/* |
| 4 | + Copyright (C) 2023 Peter Caspers |
| 5 | +
|
| 6 | + This file is part of QuantLib, a free-software/open-source library |
| 7 | + for financial quantitative analysts and developers - http://quantlib.org/ |
| 8 | +
|
| 9 | + QuantLib is free software: you can redistribute it and/or modify it |
| 10 | + under the terms of the QuantLib license. You should have received a |
| 11 | + copy of the license along with this program; if not, please email |
| 12 | + <quantlib-dev@lists.sf.net>. The license is also available online at |
| 13 | + <http://quantlib.org/license.shtml>. |
| 14 | +
|
| 15 | + This program is distributed in the hope that it will be useful, but WITHOUT |
| 16 | + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 17 | + FOR A PARTICULAR PURPOSE. See the license for more details. |
| 18 | +*/ |
| 19 | + |
| 20 | +#include <ql/math/randomnumbers/burley2020sobolrsg.hpp> |
| 21 | +#include <ql/math/randomnumbers/mt19937uniformrng.hpp> |
| 22 | + |
| 23 | +namespace QuantLib { |
| 24 | + |
| 25 | + Burley2020SobolRsg::Burley2020SobolRsg(Size dimensionality, |
| 26 | + unsigned long seed, |
| 27 | + SobolRsg::DirectionIntegers directionIntegers, |
| 28 | + unsigned long scrambleSeed) |
| 29 | + : dimensionality_(dimensionality), seed_(seed), directionIntegers_(directionIntegers), |
| 30 | + integerSequence_(dimensionality), sequence_(std::vector<Real>(dimensionality), 1.0) { |
| 31 | + reset(); |
| 32 | + group4Seeds_.resize((dimensionality_ - 1) / 4 + 1); |
| 33 | + MersenneTwisterUniformRng mt(scrambleSeed); |
| 34 | + for (auto& s : group4Seeds_) { |
| 35 | + s = static_cast<std::uint32_t>(mt.nextInt32()); |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + void Burley2020SobolRsg::reset() const { |
| 40 | + sobolRsg_ = ext::make_shared<SobolRsg>(dimensionality_, seed_, directionIntegers_, false); |
| 41 | + nextSequenceCounter_ = 0; |
| 42 | + } |
| 43 | + |
| 44 | + const std::vector<std::uint32_t>& Burley2020SobolRsg::skipTo(std::uint32_t n) const { |
| 45 | + reset(); |
| 46 | + for (Size k = 0; k < n + 1; ++k) { |
| 47 | + nextInt32Sequence(); |
| 48 | + } |
| 49 | + return integerSequence_; |
| 50 | + } |
| 51 | + |
| 52 | + namespace { |
| 53 | + |
| 54 | + // for reverseBits() see http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable |
| 55 | + |
| 56 | + static const std::uint8_t bitReverseTable[] = { |
| 57 | + 0u, 128u, 64u, 192u, 32u, 160u, 96u, 224u, 16u, 144u, 80u, 208u, 48u, 176u, |
| 58 | + 112u, 240u, 8u, 136u, 72u, 200u, 40u, 168u, 104u, 232u, 24u, 152u, 88u, 216u, |
| 59 | + 56u, 184u, 120u, 248u, 4u, 132u, 68u, 196u, 36u, 164u, 100u, 228u, 20u, 148u, |
| 60 | + 84u, 212u, 52u, 180u, 116u, 244u, 12u, 140u, 76u, 204u, 44u, 172u, 108u, 236u, |
| 61 | + 28u, 156u, 92u, 220u, 60u, 188u, 124u, 252u, 2u, 130u, 66u, 194u, 34u, 162u, |
| 62 | + 98u, 226u, 18u, 146u, 82u, 210u, 50u, 178u, 114u, 242u, 10u, 138u, 74u, 202u, |
| 63 | + 42u, 170u, 106u, 234u, 26u, 154u, 90u, 218u, 58u, 186u, 122u, 250u, 6u, 134u, |
| 64 | + 70u, 198u, 38u, 166u, 102u, 230u, 22u, 150u, 86u, 214u, 54u, 182u, 118u, 246u, |
| 65 | + 14u, 142u, 78u, 206u, 46u, 174u, 110u, 238u, 30u, 158u, 94u, 222u, 62u, 190u, |
| 66 | + 126u, 254u, 1u, 129u, 65u, 193u, 33u, 161u, 97u, 225u, 17u, 145u, 81u, 209u, |
| 67 | + 49u, 177u, 113u, 241u, 9u, 137u, 73u, 201u, 41u, 169u, 105u, 233u, 25u, 153u, |
| 68 | + 89u, 217u, 57u, 185u, 121u, 249u, 5u, 133u, 69u, 197u, 37u, 165u, 101u, 229u, |
| 69 | + 21u, 149u, 85u, 213u, 53u, 181u, 117u, 245u, 13u, 141u, 77u, 205u, 45u, 173u, |
| 70 | + 109u, 237u, 29u, 157u, 93u, 221u, 61u, 189u, 125u, 253u, 3u, 131u, 67u, 195u, |
| 71 | + 35u, 163u, 99u, 227u, 19u, 147u, 83u, 211u, 51u, 179u, 115u, 243u, 11u, 139u, |
| 72 | + 75u, 203u, 43u, 171u, 107u, 235u, 27u, 155u, 91u, 219u, 59u, 187u, 123u, 251u, |
| 73 | + 7u, 135u, 71u, 199u, 39u, 167u, 103u, 231u, 23u, 151u, 87u, 215u, 55u, 183u, |
| 74 | + 119u, 247u, 15u, 143u, 79u, 207u, 47u, 175u, 111u, 239u, 31u, 159u, 95u, 223u, |
| 75 | + 63u, 191u, 127u, 255u}; |
| 76 | + |
| 77 | + inline std::uint32_t reverseBits(std::uint32_t x) { |
| 78 | + return (bitReverseTable[x & 0xff] << 24) | (bitReverseTable[(x >> 8) & 0xff] << 16) | |
| 79 | + (bitReverseTable[(x >> 16) & 0xff] << 8) | (bitReverseTable[(x >> 24) & 0xff]); |
| 80 | + } |
| 81 | + |
| 82 | + inline std::uint32_t laine_karras_permutation(std::uint32_t x, std::uint32_t seed) { |
| 83 | + x += seed; |
| 84 | + x ^= x * 0x6c50b47cu; |
| 85 | + x ^= x * 0xb82f1e52u; |
| 86 | + x ^= x * 0xc7afe638u; |
| 87 | + x ^= x * 0x8d22f6e6u; |
| 88 | + return x; |
| 89 | + } |
| 90 | + |
| 91 | + inline std::uint32_t nested_uniform_scramble(std::uint32_t x, std::uint32_t seed) { |
| 92 | + x = reverseBits(x); |
| 93 | + x = laine_karras_permutation(x, seed); |
| 94 | + x = reverseBits(x); |
| 95 | + return x; |
| 96 | + } |
| 97 | + |
| 98 | + // the results depend a lot on the details of the hash_combine() function that is used |
| 99 | + // we use hash_combine() calling hash(), hash_mix() as implemented here: |
| 100 | + // https://github.com/boostorg/container_hash/blob/boost-1.83.0/include/boost/container_hash/hash.hpp#L560 |
| 101 | + // https://github.com/boostorg/container_hash/blob/boost-1.83.0/include/boost/container_hash/hash.hpp#L115 |
| 102 | + // https://github.com/boostorg/container_hash/blob/boost-1.83.0/include/boost/container_hash/detail/hash_mix.hpp#L67 |
| 103 | + |
| 104 | + inline std::uint64_t local_hash_mix(std::uint64_t x) { |
| 105 | + const std::uint64_t m = 0xe9846af9b1a615d; |
| 106 | + x ^= x >> 32; |
| 107 | + x *= m; |
| 108 | + x ^= x >> 32; |
| 109 | + x *= m; |
| 110 | + x ^= x >> 28; |
| 111 | + return x; |
| 112 | + } |
| 113 | + |
| 114 | + inline std::uint64_t local_hash(const std::uint64_t v) { |
| 115 | + std::uint64_t seed = 0; |
| 116 | + seed = (v >> 32) + local_hash_mix(seed); |
| 117 | + seed = (v & 0xFFFFFFFF) + local_hash_mix(seed); |
| 118 | + return seed; |
| 119 | + } |
| 120 | + |
| 121 | + inline std::uint64_t local_hash_combine(std::uint64_t x, const uint64_t v) { |
| 122 | + return local_hash_mix(x + 0x9e3779b9 + local_hash(v)); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + const std::vector<std::uint32_t>& Burley2020SobolRsg::nextInt32Sequence() const { |
| 127 | + auto n = nested_uniform_scramble(nextSequenceCounter_, group4Seeds_[0]); |
| 128 | + const auto& seq = sobolRsg_->skipTo(n); |
| 129 | + std::copy(seq.begin(), seq.end(), integerSequence_.begin()); |
| 130 | + Size i = 0, group = 0; |
| 131 | + do { |
| 132 | + std::uint64_t seed = group4Seeds_[group++]; |
| 133 | + for (Size g = 0; g < 4 && i < dimensionality_; ++g, ++i) { |
| 134 | + seed = local_hash_combine(seed, g); |
| 135 | + integerSequence_[i] = |
| 136 | + nested_uniform_scramble(integerSequence_[i], static_cast<std::uint32_t>(seed)); |
| 137 | + } |
| 138 | + } while (i < dimensionality_); |
| 139 | + ++nextSequenceCounter_; |
| 140 | + return integerSequence_; |
| 141 | + } |
| 142 | + |
| 143 | + const SobolRsg::sample_type& Burley2020SobolRsg::nextSequence() const { |
| 144 | + const std::vector<std::uint32_t>& v = nextInt32Sequence(); |
| 145 | + // normalize to get a double in (0,1) |
| 146 | + for (Size k = 0; k < dimensionality_; ++k) { |
| 147 | + sequence_.value[k] = static_cast<double>(v[k]) / 4294967296.0; |
| 148 | + } |
| 149 | + return sequence_; |
| 150 | + } |
| 151 | +} |
0 commit comments