From 9f5de7652738b2cdcaf15b8f73c0932ca4fc3291 Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Wed, 1 Mar 2023 09:35:27 -0500 Subject: [PATCH 1/5] add BrownianGenerator --- quantlib/models/marketmodels/__init__.py | 0 .../marketmodels/_brownian_generator.pxd | 14 +++++++++++ .../marketmodels/brownian_generator.pxd | 8 +++++++ .../marketmodels/brownian_generator.pyx | 24 +++++++++++++++++++ .../browniangenerators/__init__.py | 0 .../_mt_brownian_generator.pxd | 14 +++++++++++ .../mt_brownian_generator.pxd | 7 ++++++ .../mt_brownian_generator.pyx | 11 +++++++++ 8 files changed, 78 insertions(+) create mode 100644 quantlib/models/marketmodels/__init__.py create mode 100644 quantlib/models/marketmodels/_brownian_generator.pxd create mode 100644 quantlib/models/marketmodels/brownian_generator.pxd create mode 100644 quantlib/models/marketmodels/brownian_generator.pyx create mode 100644 quantlib/models/marketmodels/browniangenerators/__init__.py create mode 100644 quantlib/models/marketmodels/browniangenerators/_mt_brownian_generator.pxd create mode 100644 quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pxd create mode 100644 quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pyx diff --git a/quantlib/models/marketmodels/__init__.py b/quantlib/models/marketmodels/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/quantlib/models/marketmodels/_brownian_generator.pxd b/quantlib/models/marketmodels/_brownian_generator.pxd new file mode 100644 index 000000000..399bc6309 --- /dev/null +++ b/quantlib/models/marketmodels/_brownian_generator.pxd @@ -0,0 +1,14 @@ +from libcpp.vector cimport vector +from quantlib.types cimport Real, Size +from quantlib.handle cimport shared_ptr + +cdef extern from 'ql/models/marketmodels/browniangenerator.hpp' namespace 'QuantLib' nogil: + cdef cppclass BrownianGenerator: + Real nextStep(vector[Real]&) except + + Real nextPath() + + Size numberOfFactors() const + Size numberOfSteps() const + + cdef cppclass BrownianGeneratorFactory: + shared_ptr[BrownianGenerator] create(Size factor, Size steps) const diff --git a/quantlib/models/marketmodels/brownian_generator.pxd b/quantlib/models/marketmodels/brownian_generator.pxd new file mode 100644 index 000000000..f2a76e864 --- /dev/null +++ b/quantlib/models/marketmodels/brownian_generator.pxd @@ -0,0 +1,8 @@ +from quantlib.handle cimport shared_ptr +from . cimport _brownian_generator as _bg + +cdef class BrownianGenerator: + cdef shared_ptr[_bg.BrownianGenerator] gen + +cdef class BrownianGeneratorFactory: + cdef _bg.BrownianGeneratorFactory* factory diff --git a/quantlib/models/marketmodels/brownian_generator.pyx b/quantlib/models/marketmodels/brownian_generator.pyx new file mode 100644 index 000000000..684cc43c0 --- /dev/null +++ b/quantlib/models/marketmodels/brownian_generator.pyx @@ -0,0 +1,24 @@ +from libcpp.vector cimport vector +from quantlib.types cimport Real, Size + +cdef class BrownianGenerator: + def next_step(self, vector[Real] v): + """ len(v) needs to be equal to number of factors """ + return self.gen.get().nextStep(v) + + def next_path(self): + return self.gen.get().nextPath() + + @property + def number_of_factors(self): + return self.gen.get().numberOfFactors() + + @property + def number_of_steps(self): + return self.gen.get().numberOfSteps() + +cdef class BrownianGeneratorFactory: + def create(self, Size factors, Size steps): + cdef BrownianGenerator r = BrownianGenerator.__new__(BrownianGenerator) + r.gen = self.factory.create(factors, steps) + return r diff --git a/quantlib/models/marketmodels/browniangenerators/__init__.py b/quantlib/models/marketmodels/browniangenerators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/quantlib/models/marketmodels/browniangenerators/_mt_brownian_generator.pxd b/quantlib/models/marketmodels/browniangenerators/_mt_brownian_generator.pxd new file mode 100644 index 000000000..143700265 --- /dev/null +++ b/quantlib/models/marketmodels/browniangenerators/_mt_brownian_generator.pxd @@ -0,0 +1,14 @@ +from quantlib.types cimport Size +from quantlib.types cimport Real, Size +from quantlib.handle cimport shared_ptr +from .._brownian_generator cimport BrownianGenerator, BrownianGeneratorFactory + +cdef extern from 'ql/models/marketmodels/browniangenerators/mtbrowniangenerator.hpp' namespace 'QuantLib' nogil: + cdef cppclass MTBrownianGenerator(BrownianGenerator): + MTBrownianGenerator(Size factors, + Size steps, + unsigned long seed) # = 0 + + cdef cppclass MTBrownianGeneratorFactory(BrownianGeneratorFactory): + MTBrownianGeneratorFactory(unsigned long seed) # = 0 + shared_ptr[BrownianGenerator] create(Size factor, Size steps) const diff --git a/quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pxd b/quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pxd new file mode 100644 index 000000000..132f1c914 --- /dev/null +++ b/quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pxd @@ -0,0 +1,7 @@ +from ..brownian_generator cimport BrownianGenerator, BrownianGeneratorFactory + +cdef class MTBrownianGenerator(BrownianGenerator): + pass + +cdef class MTBrownianGeneratorFactory(BrownianGeneratorFactory): + pass diff --git a/quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pyx b/quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pyx new file mode 100644 index 000000000..43dd1477b --- /dev/null +++ b/quantlib/models/marketmodels/browniangenerators/mt_brownian_generator.pyx @@ -0,0 +1,11 @@ +from quantlib.types cimport Size +from . cimport _mt_brownian_generator as _mtbg + +cdef class MTBrownianGenerator(BrownianGenerator): + def __init__(self, Size factors, Size steps, unsigned long seed=0): + self.gen.reset(new _mtbg.MTBrownianGenerator(factors, steps, seed)) + + +cdef class MTBrownianGeneratorFactory(BrownianGeneratorFactory): + def __init__(self, unsigned long seed=0): + self.factory = new _mtbg.MTBrownianGeneratorFactory(seed) From 6e1242ded6403f3dddf5b3ce9eec2b59f235a998 Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Tue, 3 Dec 2024 15:46:28 -0500 Subject: [PATCH 2/5] add Ziggurat Gaussian random sequence generator --- .../math/randomnumbers/_mt19937uniformrng.pxd | 3 + .../_randomsequencegenerator.pxd | 13 +++ quantlib/math/randomnumbers/_rngtraits.pxd | 15 ++++ .../_xoshiro256starstaruniformrng.pxd | 11 +++ .../randomnumbers/_zigguratgaussianrng.pxd | 9 +++ quantlib/math/randomnumbers/rngtraits.pxd | 11 ++- quantlib/math/randomnumbers/rngtraits.pyx | 79 +++++++++++++++++-- test/test_cms_spread.py | 2 +- test/test_randomnumbers.py | 2 +- 9 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 quantlib/math/randomnumbers/_mt19937uniformrng.pxd create mode 100644 quantlib/math/randomnumbers/_randomsequencegenerator.pxd create mode 100644 quantlib/math/randomnumbers/_xoshiro256starstaruniformrng.pxd create mode 100644 quantlib/math/randomnumbers/_zigguratgaussianrng.pxd diff --git a/quantlib/math/randomnumbers/_mt19937uniformrng.pxd b/quantlib/math/randomnumbers/_mt19937uniformrng.pxd new file mode 100644 index 000000000..7e8d8910e --- /dev/null +++ b/quantlib/math/randomnumbers/_mt19937uniformrng.pxd @@ -0,0 +1,3 @@ +cdef extern from 'ql/math/randomnumbers/mt19937uniformrng.hpp' namespace 'QuantLib' nogil: + cdef cppclass MersenneTwisterUniformRng: + pass diff --git a/quantlib/math/randomnumbers/_randomsequencegenerator.pxd b/quantlib/math/randomnumbers/_randomsequencegenerator.pxd new file mode 100644 index 000000000..42a41336e --- /dev/null +++ b/quantlib/math/randomnumbers/_randomsequencegenerator.pxd @@ -0,0 +1,13 @@ +from libcpp.vector cimport vector +from quantlib.types cimport Real, Size +from quantlib._stochastic_process cimport StochasticProcess +from quantlib.methods.montecarlo._sample cimport Sample + +cdef extern from 'ql/math/randomnumbers/randomsequencegenerator.hpp' namespace 'QuantLib' nogil: + cdef cppclass RandomSequenceGenerator[RNG]: + ctypedef Sample[vector[Real]] sample_type + RandomSequenceGenerator(Size dimensionality, + const RNG& rgn) + const sample_type& nextSequence() const + const sample_type& lastSequence() const + Size dimension() const diff --git a/quantlib/math/randomnumbers/_rngtraits.pxd b/quantlib/math/randomnumbers/_rngtraits.pxd index 3f83451d5..2d8fb07ac 100644 --- a/quantlib/math/randomnumbers/_rngtraits.pxd +++ b/quantlib/math/randomnumbers/_rngtraits.pxd @@ -1,12 +1,25 @@ from quantlib.types cimport BigNatural, Size from ._inverse_cumulative_rsg cimport InverseCumulativeRsg from ._sobol_rsg cimport SobolRsg +from ._randomsequencegenerator cimport RandomSequenceGenerator +from ._mt19937uniformrng cimport MersenneTwisterUniformRng +from ._zigguratgaussianrng cimport ZigguratGaussianRng +from ._xoshiro256starstaruniformrng cimport Xoshiro256StarStarUniformRng cdef extern from 'ql/math/distributions/normaldistribution.hpp' namespace 'QuantLib' nogil: cdef cppclass InverseCumulativeNormal: pass cdef extern from 'ql/math/randomnumbers/rngtraits.hpp' namespace 'QuantLib' nogil: + cdef cppclass GenericPseudoRandom[URNG, IC]: + ctypedef RandomSequenceGenerator[URNG] ursg_type + ctypedef InverseCumulativeRsg[ursg_type, IC] rsg_type + + @staticmethod + rsg_type make_sequence_generator(Size dimension, + BigNatural seed) + ctypedef GenericPseudoRandom[MersenneTwisterUniformRng, InverseCumulativeNormal] PseudoRandom + cdef cppclass GenericLowDiscrepancy[URSG, IC]: ctypedef InverseCumulativeRsg[URSG, IC] rsg_type @@ -15,3 +28,5 @@ cdef extern from 'ql/math/randomnumbers/rngtraits.hpp' namespace 'QuantLib' nogi BigNatural seed) ctypedef GenericLowDiscrepancy[SobolRsg, InverseCumulativeNormal] LowDiscrepancy + +ctypedef RandomSequenceGenerator[ZigguratGaussianRng[Xoshiro256StarStarUniformRng]] ZigguratPseudoRandom diff --git a/quantlib/math/randomnumbers/_xoshiro256starstaruniformrng.pxd b/quantlib/math/randomnumbers/_xoshiro256starstaruniformrng.pxd new file mode 100644 index 000000000..c0b839e57 --- /dev/null +++ b/quantlib/math/randomnumbers/_xoshiro256starstaruniformrng.pxd @@ -0,0 +1,11 @@ +from libc.stdint cimport uint64_t +from quantlib.types cimport Real +from quantlib.methods.montecarlo._sample cimport Sample + +cdef extern from 'ql/math/randomnumbers/xoshiro256starstaruniformrng.hpp' namespace 'QuantLib' nogil: + cdef cppclass Xoshiro256StarStarUniformRng: + ctypedef Sample[Real] sample_type + Xoshiro256StarStarUniformRng(uint64_t seed) #=0 + sample_type next() const + Real nextReal() const + uint64_t nextInt64() diff --git a/quantlib/math/randomnumbers/_zigguratgaussianrng.pxd b/quantlib/math/randomnumbers/_zigguratgaussianrng.pxd new file mode 100644 index 000000000..30e8882ca --- /dev/null +++ b/quantlib/math/randomnumbers/_zigguratgaussianrng.pxd @@ -0,0 +1,9 @@ +from quantlib.types cimport Real +from quantlib.methods.montecarlo._sample cimport Sample + +cdef extern from 'ql/math/randomnumbers/zigguratgaussianrng.hpp' namespace 'QuantLib' nogil: + cdef cppclass ZigguratGaussianRng[RNG]: + ctypedef Sample[Real] sample_type + ZigguratGaussianRng(const RNG& uint64Generator) + sample_type next() const + Real nextReal() const diff --git a/quantlib/math/randomnumbers/rngtraits.pxd b/quantlib/math/randomnumbers/rngtraits.pxd index e4f92296a..00b031dfc 100644 --- a/quantlib/math/randomnumbers/rngtraits.pxd +++ b/quantlib/math/randomnumbers/rngtraits.pxd @@ -1,7 +1,16 @@ -from . cimport _rngtraits from ._inverse_cumulative_rsg cimport InverseCumulativeRsg +from ._randomsequencegenerator cimport RandomSequenceGenerator from ._sobol_rsg cimport SobolRsg from ._rngtraits cimport InverseCumulativeNormal +from ._mt19937uniformrng cimport MersenneTwisterUniformRng +from ._zigguratgaussianrng cimport ZigguratGaussianRng +from ._xoshiro256starstaruniformrng cimport Xoshiro256StarStarUniformRng cdef class LowDiscrepancy: cdef InverseCumulativeRsg[SobolRsg, InverseCumulativeNormal]* _thisptr + +cdef class PseudoRandom: + cdef InverseCumulativeRsg[RandomSequenceGenerator[MersenneTwisterUniformRng], InverseCumulativeNormal]* _thisptr + +cdef class FastPseudoRandom: + cdef RandomSequenceGenerator[ZigguratGaussianRng[Xoshiro256StarStarUniformRng]]* _thisptr diff --git a/quantlib/math/randomnumbers/rngtraits.pyx b/quantlib/math/randomnumbers/rngtraits.pyx index d3bdd6cae..7e0306412 100644 --- a/quantlib/math/randomnumbers/rngtraits.pyx +++ b/quantlib/math/randomnumbers/rngtraits.pyx @@ -1,15 +1,51 @@ -from quantlib.types cimport BigNatural, Size +from libc.string cimport memcpy +from quantlib.types cimport BigNatural, Real, Size +from cython.operator cimport dereference as deref +from . cimport _rngtraits cimport cython cimport numpy as np from cython.operator cimport dereference as deref from libcpp.vector cimport vector +from quantlib.methods.montecarlo._sample cimport Sample np.import_array() cdef class LowDiscrepancy: def __init__(self, Size dimension, BigNatural seed=0): self._thisptr = new InverseCumulativeRsg[SobolRsg, InverseCumulativeNormal]( - _rngtraits.LowDiscrepancy.make_sequence_generator(dimension, seed)) + _rngtraits.LowDiscrepancy.make_sequence_generator(dimension, seed) + ) + + def __dealloc__(self): + if self._thisptr is not NULL: + del self._thisptr + + def __iter__(self): + return self + + @cython.boundscheck(False) + def __next__(self): + cdef np.npy_intp[1] dims + dims[0] = self._thisptr.dimension() + cdef arr = np.PyArray_SimpleNew(1, &dims[0], np.NPY_DOUBLE) + cdef Sample[vector[Real]]* s = &self._thisptr.nextSequence() + memcpy(np.PyArray_DATA(arr), s.value.data(), self._thisptr.dimension() * sizeof(Real)) + return (s.weight, arr) + + @property + def dimension(self): + return self._thisptr.dimension() + + def last_sequence(self): + return self._thisptr.lastSequence().value + + +cdef class PseudoRandom: + + def __init__(self, Size dimension, BigNatural seed=0): + self._thisptr = new _rngtraits.PseudoRandom.rsg_type( + _rngtraits.PseudoRandom.make_sequence_generator(dimension, seed) + ) def __dealloc__(self): if self._thisptr is not NULL: @@ -23,12 +59,9 @@ cdef class LowDiscrepancy: cdef np.npy_intp[1] dims dims[0] = self._thisptr.dimension() cdef arr = np.PyArray_SimpleNew(1, &dims[0], np.NPY_DOUBLE) - cdef double[:] r = arr - cdef size_t i - cdef vector[double] s = self._thisptr.nextSequence().value - for i in range(dims[0]): - r[i] = s[i] - return arr + cdef Sample[vector[Real]]* s = &self._thisptr.nextSequence() + memcpy(np.PyArray_DATA(arr), s.value.data(), self._thisptr.dimension() * sizeof(Real)) + return (s.weight, arr) @property def dimension(self): @@ -36,3 +69,33 @@ cdef class LowDiscrepancy: def last_sequence(self): return self._thisptr.lastSequence().value + + +cdef class FastPseudoRandom: + + def __init__(self, Size dimension, BigNatural seed=0): + cdef Xoshiro256StarStarUniformRng* uniform_random = new Xoshiro256StarStarUniformRng(seed) + cdef ZigguratGaussianRng[Xoshiro256StarStarUniformRng]* rng = new ZigguratGaussianRng[Xoshiro256StarStarUniformRng](deref(uniform_random)) + self._thisptr = new RandomSequenceGenerator[ZigguratGaussianRng[Xoshiro256StarStarUniformRng]](dimension, deref(rng)) + del uniform_random + del rng + + def __dealloc__(self): + if self._thisptr is not NULL: + del self._thisptr + + def __iter__(self): + return self + + @cython.boundscheck(False) + def __next__(self): + cdef np.npy_intp[1] dims + dims[0] = self._thisptr.dimension() + cdef arr = np.PyArray_SimpleNew(1, &dims[0], np.NPY_DOUBLE) + cdef Sample[vector[Real]]* s = &self._thisptr.nextSequence() + memcpy(np.PyArray_DATA(arr), s.value.data(), self._thisptr.dimension() * sizeof(Real)) + return (s.weight, arr) + + @property + def dimension(self): + return self._thisptr.dimension() diff --git a/test/test_cms_spread.py b/test/test_cms_spread.py index cc728d719..24336a8bc 100644 --- a/test/test_cms_spread.py +++ b/test/test_cms_spread.py @@ -168,7 +168,7 @@ def mc_reference_value(cpn1, cpn2, vol, correlation): it = 0 z = np.empty((samples, 2)) for i in range(samples): - z[i] = next(g) + _, z[i] = next(g) z = z.dot(C.T) + avg if vol.volatility_type == VolatilityType.ShiftedLognormal: z = (atm_rate + vol_shift) * np.exp(z) - vol_shift diff --git a/test/test_randomnumbers.py b/test/test_randomnumbers.py index 1febbca03..d9954c316 100644 --- a/test/test_randomnumbers.py +++ b/test/test_randomnumbers.py @@ -39,7 +39,7 @@ def test_mean_variance(self): samples = 100000 X = np.empty((samples, self.g.dimension)) for i in range(samples): - X[i] = next(self.g) + _, X[i] = next(self.g) C = np.cov(X.T) self.assertAlmostEqual(np.linalg.norm(X.mean(axis=0)), 0., 3) self.assertAlmostEqual(np.linalg.norm(C - np.eye(10)), 0., 2) From bcc2754280f670f2a127e2d34ef503baf3ceea7f Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Tue, 3 Dec 2024 15:47:36 -0500 Subject: [PATCH 3/5] cleanup types --- quantlib/_time_grid.pxd | 4 ++-- quantlib/math/_array.pxd | 2 +- quantlib/math/randomnumbers/_inverse_cumulative_rsg.pxd | 2 +- quantlib/methods/montecarlo/_sample.pxd | 2 +- quantlib/time_grid.pyx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/quantlib/_time_grid.pxd b/quantlib/_time_grid.pxd index d0abcdcd7..ab4524ed2 100644 --- a/quantlib/_time_grid.pxd +++ b/quantlib/_time_grid.pxd @@ -1,7 +1,7 @@ -include 'types.pxi' +from quantlib.types cimport Size, Time from libcpp.vector cimport vector -cdef extern from 'ql/timegrid.hpp' namespace 'QuantLib': +cdef extern from 'ql/timegrid.hpp' namespace 'QuantLib' nogil: cdef cppclass TimeGrid: TimeGrid() TimeGrid(Time end, Size steps) diff --git a/quantlib/math/_array.pxd b/quantlib/math/_array.pxd index f8e29871d..1d6167a5f 100644 --- a/quantlib/math/_array.pxd +++ b/quantlib/math/_array.pxd @@ -7,7 +7,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ -include '../types.pxi' +from quantlib.types cimport Real, Size cdef extern from 'ql/math/array.hpp' namespace 'QuantLib': cdef cppclass Array: diff --git a/quantlib/math/randomnumbers/_inverse_cumulative_rsg.pxd b/quantlib/math/randomnumbers/_inverse_cumulative_rsg.pxd index e188dd9de..fbe9c4c93 100644 --- a/quantlib/math/randomnumbers/_inverse_cumulative_rsg.pxd +++ b/quantlib/math/randomnumbers/_inverse_cumulative_rsg.pxd @@ -1,4 +1,4 @@ -include '../../types.pxi' +from quantlib.types cimport Real, Size from quantlib.methods.montecarlo._sample cimport Sample from libcpp.vector cimport vector diff --git a/quantlib/methods/montecarlo/_sample.pxd b/quantlib/methods/montecarlo/_sample.pxd index ad94fe0bf..2eb2a4362 100644 --- a/quantlib/methods/montecarlo/_sample.pxd +++ b/quantlib/methods/montecarlo/_sample.pxd @@ -1,4 +1,4 @@ -include '../../types.pxi' +from quantlib.types cimport Real cdef extern from 'ql/methods/montecarlo/sample.hpp' namespace 'QuantLib' nogil: cdef cppclass Sample[T]: T value diff --git a/quantlib/time_grid.pyx b/quantlib/time_grid.pyx index fc2e3fed1..a6c77bfbc 100644 --- a/quantlib/time_grid.pyx +++ b/quantlib/time_grid.pyx @@ -1,4 +1,4 @@ -include 'types.pxi' +from quantlib.types cimport Size, Time from libcpp.vector cimport vector from . cimport _time_grid as _tg From 79208a71268383f21396dce5a8cf321b2a2a6c1e Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Tue, 3 Dec 2024 15:53:34 -0500 Subject: [PATCH 4/5] add to_ndarray method to QuantLib Array --- quantlib/math/_array.pxd | 1 + quantlib/math/array.pyx | 21 ++++++++++++++++----- test/test_math.py | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/quantlib/math/_array.pxd b/quantlib/math/_array.pxd index 1d6167a5f..766089474 100644 --- a/quantlib/math/_array.pxd +++ b/quantlib/math/_array.pxd @@ -17,3 +17,4 @@ cdef extern from 'ql/math/array.hpp' namespace 'QuantLib': Real& at(Size i) except +IndexError Size size() Real& operator[](Size) + const Real* begin() diff --git a/quantlib/math/array.pyx b/quantlib/math/array.pyx index 8c9a46e2e..4783b3191 100644 --- a/quantlib/math/array.pyx +++ b/quantlib/math/array.pyx @@ -7,15 +7,19 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ -include '../types.pxi' +from quantlib.types cimport Real, Size +from cpython.ref cimport Py_INCREF from libcpp.utility cimport move +cimport numpy as np + +np.import_array() cdef class Array: """ 1D array for linear algebra """ - def __init__(self, Size size, value=None): + def __init__(self, Size size=0, value=None): if value is None: self._thisptr = move[_arr.Array](_arr.Array(size)) else: @@ -31,9 +35,16 @@ cdef class Array: raise IndexError("index {} is larger than the size of the array {}". format(key, self._thisptr.size())) - property size: - def __get__(self): - return self._thisptr.size() + def __len__(self): + return self._thisptr.size() + + def to_ndarray(self): + cdef np.npy_intp[1] dims + dims[0] = self._thisptr.size() + cdef arr = np.PyArray_SimpleNewFromData(1, &dims[0], np.NPY_DOUBLE, (self._thisptr.begin())) + Py_INCREF(self) + np.PyArray_SetBaseObject(arr, self) + return arr cpdef qlarray_from_pyarray(p): cdef Array x = Array(len(p)) diff --git a/test/test_math.py b/test/test_math.py index 1173ac635..aeb1b9350 100644 --- a/test/test_math.py +++ b/test/test_math.py @@ -10,7 +10,7 @@ def test_array_1(self): x = Array(10, v) self.assertEqual(x[4], v) - self.assertEqual(x.size, 10) + self.assertEqual(len(x), 10) def test_array_2(self): v = 3.14 From d15522d89d9589edd22898e1f2f7b4333d32b45e Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Thu, 5 Dec 2024 16:17:48 -0500 Subject: [PATCH 5/5] add multipathgenerator --- .../montecarlo/_multipathgenerator.pxd | 18 +++++++ .../methods/montecarlo/multipathgenerator.pxd | 13 +++++ .../methods/montecarlo/multipathgenerator.pyx | 50 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 quantlib/methods/montecarlo/_multipathgenerator.pxd create mode 100644 quantlib/methods/montecarlo/multipathgenerator.pxd create mode 100644 quantlib/methods/montecarlo/multipathgenerator.pyx diff --git a/quantlib/methods/montecarlo/_multipathgenerator.pxd b/quantlib/methods/montecarlo/_multipathgenerator.pxd new file mode 100644 index 000000000..0580a4a3c --- /dev/null +++ b/quantlib/methods/montecarlo/_multipathgenerator.pxd @@ -0,0 +1,18 @@ +from libcpp cimport bool +from quantlib.handle cimport shared_ptr + +from quantlib._time_grid cimport TimeGrid +from libcpp.vector cimport vector +from ._multipath cimport MultiPath +from ._sample cimport Sample +from quantlib._stochastic_process cimport StochasticProcess + +cdef extern from 'ql/methods/montecarlo/multipathgenerator.hpp' namespace 'QuantLib' nogil: + cdef cppclass MultiPathGenerator[GSG]: + ctypedef Sample[MultiPath] sample_type + MultiPathGenerator(const shared_ptr[StochasticProcess]&, + const TimeGrid&, + GSG generator, + bool brownianBridge) # = false) + const sample_type& next() + const sample_type& antithetic() diff --git a/quantlib/methods/montecarlo/multipathgenerator.pxd b/quantlib/methods/montecarlo/multipathgenerator.pxd new file mode 100644 index 000000000..aa343cec9 --- /dev/null +++ b/quantlib/methods/montecarlo/multipathgenerator.pxd @@ -0,0 +1,13 @@ +from . cimport _multipathgenerator as _mpg +from quantlib.math.randomnumbers._randomsequencegenerator cimport RandomSequenceGenerator +from quantlib.math.randomnumbers._rngtraits cimport PseudoRandom as QlPseudoRandom, LowDiscrepancy as QlLowDiscrepancy, ZigguratPseudoRandom + + +cdef class PseudoRandomMultiPathGenerator: + cdef _mpg.MultiPathGenerator[QlPseudoRandom.rsg_type]* _thisptr + +cdef class LowDiscrepancyMultiPathGenerator: + cdef _mpg.MultiPathGenerator[QlLowDiscrepancy.rsg_type]* _thisptr + +cdef class ZigguratMultiPathGenerator: + cdef _mpg.MultiPathGenerator[ZigguratPseudoRandom]* _thisptr diff --git a/quantlib/methods/montecarlo/multipathgenerator.pyx b/quantlib/methods/montecarlo/multipathgenerator.pyx new file mode 100644 index 000000000..bd642f880 --- /dev/null +++ b/quantlib/methods/montecarlo/multipathgenerator.pyx @@ -0,0 +1,50 @@ +from cython.operator cimport dereference as deref +from libcpp cimport bool +from quantlib.stochastic_process cimport StochasticProcess +from quantlib.time_grid cimport TimeGrid +from quantlib.math.randomnumbers.rngtraits cimport PseudoRandom, LowDiscrepancy, FastPseudoRandom +from ._sample cimport Sample +from .multipath cimport MultiPath +from . cimport _multipath as _mp + +cdef class PseudoRandomMultiPathGenerator: + def __init__(self, StochasticProcess process, TimeGrid time_grid, PseudoRandom gen, bool brownian_bridge): + self._thisptr = new _mpg.MultiPathGenerator[QlPseudoRandom.rsg_type](process._thisptr, time_grid._thisptr, deref(gen._thisptr), brownian_bridge) + + def __next__(self): + cdef Sample[_mp.MultiPath]* r = &self._thisptr.next() + cdef MultiPath mp = MultiPath.__new__(MultiPath) + mp._thisptr = new _mp.MultiPath(r.value) + return (r.weight, mp) + + def __dealloc__(self): + del self._thisptr + + + +cdef class LowDiscrepancyMultiPathGenerator: + def __init__(self, StochasticProcess process, TimeGrid time_grid, LowDiscrepancy gen, bool brownian_bridge): + self._thisptr = new _mpg.MultiPathGenerator[QlLowDiscrepancy.rsg_type](process._thisptr, time_grid._thisptr, deref(gen._thisptr), brownian_bridge) + + def __next__(self): + cdef Sample[_mp.MultiPath]* r = &self._thisptr.next() + cdef MultiPath mp = MultiPath.__new__(MultiPath) + mp._thisptr = new _mp.MultiPath(r.value) + return (r.weight, mp) + + def __dealloc__(self): + del self._thisptr + + +cdef class ZigguratMultiPathGenerator: + def __init__(self, StochasticProcess process, TimeGrid time_grid, FastPseudoRandom gen, bool brownian_bridge): + self._thisptr = new _mpg.MultiPathGenerator[ZigguratPseudoRandom](process._thisptr, time_grid._thisptr, deref(gen._thisptr), brownian_bridge) + + def __next__(self): + cdef Sample[_mp.MultiPath]* r = &self._thisptr.next() + cdef MultiPath mp = MultiPath.__new__(MultiPath) + mp._thisptr = new _mp.MultiPath(r.value) + return (r.weight, mp) + + def __dealloc__(self): + del self._thisptr