# -*- coding: utf-8 -*-
# timewave
# --------
# timewave, a stochastic process evolution simulation engine in python.
#
# Author: sonntagsgesicht, based on a fork of Deutsche Postbank [pbrisk]
# Version: 0.6, copyright Wednesday, 18 September 2019
# Website: https://github.com/sonntagsgesicht/timewave
# License: Apache License 2.0 (see LICENSE file)
"""
module containing stochastic process model producer
"""
from math import sqrt
from scipy.linalg import cholesky
from .engine import Producer, State
from .producers import MultiProducer
from .indexedmatrix import IndexMatrix
# producer
[docs]class GaussEvolutionFunctionProducer(Producer):
"""
class implementing general Gauss process between grid dates
"""
def __init__(self, func=None, initial_state=None, length=None):
"""
:param callable func: evolve function, e.g. `lambda x, s, e, q: x + sqrt(e - s) * q` by default
with `x` current state value, `s` current point in time, i.e. start point of next evolution step,
`e` next point in time, i.e. end point of evolution step, `q` standard normal random number to do step
:param initial_state: initial state (value) of evolution,
:param int or None length: length of `q` as a list of Gauss random numbers,
if `None` or `0` the evolution function `func` will be invoked with `q`
not as a list but a float random number.
class implementing general Gauss process between grid dates and provides state to any evolve style function
`foo(x, s, e, q)` with `x` last state, `s` last state time, `e` current point in time and
`q` current Gauss process state
"""
if func is None:
func = (lambda x, s, e, q: x + sqrt(e - s) * q)
self._len = length
super(GaussEvolutionFunctionProducer, self).__init__(func, initial_state)
def __len__(self):
return 0 if self._len is None else self._len
[docs] def evolve(self, new_date):
"""
evolve to the new process state at the next date
:param date new_date: date or point in time of the new state
:return State:
"""
if self.state.date == new_date and not self.initial_state.date == new_date:
return self.state
if self._len:
q = [self.random.gauss(0., 1.) for _ in range(int(self._len))]
else:
q = self.random.gauss(0., 1.)
self.state.value = self.func(self.state.value, self.state.date, new_date, q)
self.state.date = new_date
return self.state
[docs]class GaussEvolutionProducer(GaussEvolutionFunctionProducer):
"""
producer to bring diffusion process to life
"""
def __init__(self, process):
"""
:param StochasticProcess process: diffusion process to evolve
"""
self.process = process
self.diffusion_driver = process.diffusion_driver
length = len(process) if len(process) > 1 else None
super(GaussEvolutionProducer, self).__init__(process.evolve, State(process.start), length)
class _FakeGaussRandom(list):
def seed(self, *args):
pass
def gauss(self, *args):
return self.pop(0)
[docs]class MultiGaussEvolutionProducer(CorrelatedGaussEvolutionProducer):
"""
class implementing multi variant GaussEvolutionProducer
"""
def __init__(self, process_list, correlation=None, diffusion_driver=None):
producers = [GaussEvolutionProducer(p) for p in process_list]
super(MultiGaussEvolutionProducer, self).__init__(producers, correlation, diffusion_driver)