Skip to content

Commit 6e1242d

Browse files
committed
add Ziggurat Gaussian random sequence generator
1 parent 9f5de76 commit 6e1242d

File tree

9 files changed

+134
-11
lines changed

9 files changed

+134
-11
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cdef extern from 'ql/math/randomnumbers/mt19937uniformrng.hpp' namespace 'QuantLib' nogil:
2+
cdef cppclass MersenneTwisterUniformRng:
3+
pass
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from libcpp.vector cimport vector
2+
from quantlib.types cimport Real, Size
3+
from quantlib._stochastic_process cimport StochasticProcess
4+
from quantlib.methods.montecarlo._sample cimport Sample
5+
6+
cdef extern from 'ql/math/randomnumbers/randomsequencegenerator.hpp' namespace 'QuantLib' nogil:
7+
cdef cppclass RandomSequenceGenerator[RNG]:
8+
ctypedef Sample[vector[Real]] sample_type
9+
RandomSequenceGenerator(Size dimensionality,
10+
const RNG& rgn)
11+
const sample_type& nextSequence() const
12+
const sample_type& lastSequence() const
13+
Size dimension() const

quantlib/math/randomnumbers/_rngtraits.pxd

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
from quantlib.types cimport BigNatural, Size
22
from ._inverse_cumulative_rsg cimport InverseCumulativeRsg
33
from ._sobol_rsg cimport SobolRsg
4+
from ._randomsequencegenerator cimport RandomSequenceGenerator
5+
from ._mt19937uniformrng cimport MersenneTwisterUniformRng
6+
from ._zigguratgaussianrng cimport ZigguratGaussianRng
7+
from ._xoshiro256starstaruniformrng cimport Xoshiro256StarStarUniformRng
48

59
cdef extern from 'ql/math/distributions/normaldistribution.hpp' namespace 'QuantLib' nogil:
610
cdef cppclass InverseCumulativeNormal:
711
pass
812

913
cdef extern from 'ql/math/randomnumbers/rngtraits.hpp' namespace 'QuantLib' nogil:
14+
cdef cppclass GenericPseudoRandom[URNG, IC]:
15+
ctypedef RandomSequenceGenerator[URNG] ursg_type
16+
ctypedef InverseCumulativeRsg[ursg_type, IC] rsg_type
17+
18+
@staticmethod
19+
rsg_type make_sequence_generator(Size dimension,
20+
BigNatural seed)
21+
ctypedef GenericPseudoRandom[MersenneTwisterUniformRng, InverseCumulativeNormal] PseudoRandom
22+
1023
cdef cppclass GenericLowDiscrepancy[URSG, IC]:
1124
ctypedef InverseCumulativeRsg[URSG, IC] rsg_type
1225

@@ -15,3 +28,5 @@ cdef extern from 'ql/math/randomnumbers/rngtraits.hpp' namespace 'QuantLib' nogi
1528
BigNatural seed)
1629

1730
ctypedef GenericLowDiscrepancy[SobolRsg, InverseCumulativeNormal] LowDiscrepancy
31+
32+
ctypedef RandomSequenceGenerator[ZigguratGaussianRng[Xoshiro256StarStarUniformRng]] ZigguratPseudoRandom
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from libc.stdint cimport uint64_t
2+
from quantlib.types cimport Real
3+
from quantlib.methods.montecarlo._sample cimport Sample
4+
5+
cdef extern from 'ql/math/randomnumbers/xoshiro256starstaruniformrng.hpp' namespace 'QuantLib' nogil:
6+
cdef cppclass Xoshiro256StarStarUniformRng:
7+
ctypedef Sample[Real] sample_type
8+
Xoshiro256StarStarUniformRng(uint64_t seed) #=0
9+
sample_type next() const
10+
Real nextReal() const
11+
uint64_t nextInt64()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from quantlib.types cimport Real
2+
from quantlib.methods.montecarlo._sample cimport Sample
3+
4+
cdef extern from 'ql/math/randomnumbers/zigguratgaussianrng.hpp' namespace 'QuantLib' nogil:
5+
cdef cppclass ZigguratGaussianRng[RNG]:
6+
ctypedef Sample[Real] sample_type
7+
ZigguratGaussianRng(const RNG& uint64Generator)
8+
sample_type next() const
9+
Real nextReal() const
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1-
from . cimport _rngtraits
21
from ._inverse_cumulative_rsg cimport InverseCumulativeRsg
2+
from ._randomsequencegenerator cimport RandomSequenceGenerator
33
from ._sobol_rsg cimport SobolRsg
44
from ._rngtraits cimport InverseCumulativeNormal
5+
from ._mt19937uniformrng cimport MersenneTwisterUniformRng
6+
from ._zigguratgaussianrng cimport ZigguratGaussianRng
7+
from ._xoshiro256starstaruniformrng cimport Xoshiro256StarStarUniformRng
58

69
cdef class LowDiscrepancy:
710
cdef InverseCumulativeRsg[SobolRsg, InverseCumulativeNormal]* _thisptr
11+
12+
cdef class PseudoRandom:
13+
cdef InverseCumulativeRsg[RandomSequenceGenerator[MersenneTwisterUniformRng], InverseCumulativeNormal]* _thisptr
14+
15+
cdef class FastPseudoRandom:
16+
cdef RandomSequenceGenerator[ZigguratGaussianRng[Xoshiro256StarStarUniformRng]]* _thisptr
Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,51 @@
1-
from quantlib.types cimport BigNatural, Size
1+
from libc.string cimport memcpy
2+
from quantlib.types cimport BigNatural, Real, Size
3+
from cython.operator cimport dereference as deref
4+
from . cimport _rngtraits
25
cimport cython
36
cimport numpy as np
47
from cython.operator cimport dereference as deref
58
from libcpp.vector cimport vector
9+
from quantlib.methods.montecarlo._sample cimport Sample
610
np.import_array()
711

812
cdef class LowDiscrepancy:
913

1014
def __init__(self, Size dimension, BigNatural seed=0):
1115
self._thisptr = new InverseCumulativeRsg[SobolRsg, InverseCumulativeNormal](
12-
_rngtraits.LowDiscrepancy.make_sequence_generator(dimension, seed))
16+
_rngtraits.LowDiscrepancy.make_sequence_generator(dimension, seed)
17+
)
18+
19+
def __dealloc__(self):
20+
if self._thisptr is not NULL:
21+
del self._thisptr
22+
23+
def __iter__(self):
24+
return self
25+
26+
@cython.boundscheck(False)
27+
def __next__(self):
28+
cdef np.npy_intp[1] dims
29+
dims[0] = self._thisptr.dimension()
30+
cdef arr = np.PyArray_SimpleNew(1, &dims[0], np.NPY_DOUBLE)
31+
cdef Sample[vector[Real]]* s = <Sample[vector[Real]]*>&self._thisptr.nextSequence()
32+
memcpy(np.PyArray_DATA(arr), s.value.data(), self._thisptr.dimension() * sizeof(Real))
33+
return (s.weight, arr)
34+
35+
@property
36+
def dimension(self):
37+
return self._thisptr.dimension()
38+
39+
def last_sequence(self):
40+
return self._thisptr.lastSequence().value
41+
42+
43+
cdef class PseudoRandom:
44+
45+
def __init__(self, Size dimension, BigNatural seed=0):
46+
self._thisptr = new _rngtraits.PseudoRandom.rsg_type(
47+
_rngtraits.PseudoRandom.make_sequence_generator(dimension, seed)
48+
)
1349

1450
def __dealloc__(self):
1551
if self._thisptr is not NULL:
@@ -23,16 +59,43 @@ cdef class LowDiscrepancy:
2359
cdef np.npy_intp[1] dims
2460
dims[0] = self._thisptr.dimension()
2561
cdef arr = np.PyArray_SimpleNew(1, &dims[0], np.NPY_DOUBLE)
26-
cdef double[:] r = arr
27-
cdef size_t i
28-
cdef vector[double] s = self._thisptr.nextSequence().value
29-
for i in range(dims[0]):
30-
r[i] = s[i]
31-
return arr
62+
cdef Sample[vector[Real]]* s = <Sample[vector[Real]]*>&self._thisptr.nextSequence()
63+
memcpy(np.PyArray_DATA(arr), s.value.data(), self._thisptr.dimension() * sizeof(Real))
64+
return (s.weight, arr)
3265

3366
@property
3467
def dimension(self):
3568
return self._thisptr.dimension()
3669

3770
def last_sequence(self):
3871
return self._thisptr.lastSequence().value
72+
73+
74+
cdef class FastPseudoRandom:
75+
76+
def __init__(self, Size dimension, BigNatural seed=0):
77+
cdef Xoshiro256StarStarUniformRng* uniform_random = new Xoshiro256StarStarUniformRng(seed)
78+
cdef ZigguratGaussianRng[Xoshiro256StarStarUniformRng]* rng = new ZigguratGaussianRng[Xoshiro256StarStarUniformRng](deref(uniform_random))
79+
self._thisptr = new RandomSequenceGenerator[ZigguratGaussianRng[Xoshiro256StarStarUniformRng]](dimension, deref(rng))
80+
del uniform_random
81+
del rng
82+
83+
def __dealloc__(self):
84+
if self._thisptr is not NULL:
85+
del self._thisptr
86+
87+
def __iter__(self):
88+
return self
89+
90+
@cython.boundscheck(False)
91+
def __next__(self):
92+
cdef np.npy_intp[1] dims
93+
dims[0] = self._thisptr.dimension()
94+
cdef arr = np.PyArray_SimpleNew(1, &dims[0], np.NPY_DOUBLE)
95+
cdef Sample[vector[Real]]* s = <Sample[vector[Real]]*>&self._thisptr.nextSequence()
96+
memcpy(np.PyArray_DATA(arr), s.value.data(), self._thisptr.dimension() * sizeof(Real))
97+
return (s.weight, arr)
98+
99+
@property
100+
def dimension(self):
101+
return self._thisptr.dimension()

test/test_cms_spread.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def mc_reference_value(cpn1, cpn2, vol, correlation):
168168
it = 0
169169
z = np.empty((samples, 2))
170170
for i in range(samples):
171-
z[i] = next(g)
171+
_, z[i] = next(g)
172172
z = z.dot(C.T) + avg
173173
if vol.volatility_type == VolatilityType.ShiftedLognormal:
174174
z = (atm_rate + vol_shift) * np.exp(z) - vol_shift

test/test_randomnumbers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def test_mean_variance(self):
3939
samples = 100000
4040
X = np.empty((samples, self.g.dimension))
4141
for i in range(samples):
42-
X[i] = next(self.g)
42+
_, X[i] = next(self.g)
4343
C = np.cov(X.T)
4444
self.assertAlmostEqual(np.linalg.norm(X.mean(axis=0)), 0., 3)
4545
self.assertAlmostEqual(np.linalg.norm(C - np.eye(10)), 0., 2)

0 commit comments

Comments
 (0)