Source code for aleatory.processes.analytical.brownian_meander

"""
Brownian Meander
"""
import numpy as np

from aleatory.processes import BrownianBridge, BrownianMotion
from aleatory.utils.utils import check_positive_integer, draw_paths_with_end_point, draw_paths


[docs]class BrownianMeander(BrownianMotion): r""" Brownian Meander .. image:: _static/brownian_meander_drawn.png .. image:: _static/tied_brownian_meander_drawn.png Let :math:`\{W_t : t \geq 0\}` be a standard Brownian motion and .. math:: \tau = \sup\{ t \in [0,1] : W_t =0\}, i.e. the last time before t = 1 when :math:`W_t` visits zero. Then the Brownian Meander is defined as follows .. math:: W_t^{+} = \frac{1}{\sqrt{1-\tau}} |W(\tau + t (1-\tau))|, \ \ \ \ t\in (0,1]. Parameters """ def __init__(self, T=1.0, fixed_end=False, end=None, rng=None): super().__init__(T=T, rng=rng) self.name = "Tied Brownian Meander" if fixed_end else "Brownian Meander" self.fixed_end = fixed_end self.end = end self._BrownianBridge = BrownianBridge(T=self.T) self.n = None self.times = None def __str__(self): return f"Brownian Meander" def __repr__(self): return f"BrownianMeander" @property def end(self): return self._end @end.setter def end(self, value): if value is None: self.exponential = self.rng.exponential(1) self._end = np.sqrt(2.0 * self.T * self.rng.exponential(1)) elif value < 0: raise ValueError("end point cannot be negative") else: self._end = True self._end = value def _sample_brownian_meander(self, n): """ Generate a realization from Brownian Meander """ check_positive_integer(n) self.n = n self.times = np.linspace(0, 1, n) bridges = self._BrownianBridge.simulate(n=n, N=3) end = self.end if self.fixed_end else np.sqrt(2.0 * self.T * self.rng.exponential(1)) times_scaled = self.times / self.T meander = np.sqrt((end * times_scaled + bridges[0]) ** 2 + bridges[1] ** 2 + bridges[2] ** 2) return meander def _sample_brownian_meander_at(self, times): self.times = times bridges = self._BrownianBridge.sample_at(times) end = np.sqrt(2.0 * self.T * self.rng.exponential(1)) times_scaled = self.times / self.T meander = np.sqrt((end * times_scaled + bridges[0]) ** 2 + bridges[1] ** 2 + bridges[2] ** 2) return meander
[docs] def sample(self, n): return self._sample_brownian_meander(n)
[docs] def sample_at(self, times): return self._sample_brownian_meander_at(times)
def _draw_paths(self, n, N, title=None, **fig_kw): self.simulate(n, N) chart_title = title if title else self.name fig_kw['envelope'] = False if self.fixed_end: if 'marginal' in fig_kw: fig_kw.pop('marginal') if 'orientation' in fig_kw: fig_kw.pop('orientation') fig = draw_paths_with_end_point(times=self.times, paths=self.paths, title=chart_title, **fig_kw) else: fig = draw_paths(times=self.times, paths=self.paths, N=N, expectations=None, KDE=False, title=chart_title, **fig_kw) return fig
[docs] def draw(self, n, N, title=None, **fig_kw): """ Simulates and plots paths/trajectories from the instanced stochastic process. Produces different kind of visualisation illustrating the following elements: - times versus process values as lines - the expectation of the process across time - histogram showing the empirical marginal distribution :math:`X_T` (optional when ``marginal = True``) - probability density function of the marginal distribution :math:`X_T` (optional when ``marginal = True``) - envelope of confidence intervals across time (optional when ``envelope = True``) :param n: number of steps in each path :param N: number of paths to simulate :param title: string to customise plot title :return: """ return self._draw_paths(n, N, title, **fig_kw)