stochastic_processes/processes/
mod.rs

1//! Module containing various stochastic processes.
2
3mod cir;
4pub use cir::*;
5
6mod gbm;
7pub use gbm::*;
8
9mod ornstein_uhlenbeck;
10pub use ornstein_uhlenbeck::*;
11
12/// Base trait for a stochastic process.
13pub trait StochasticProcess {}
14
15/// Non-autonomous stochastic process.
16///
17/// A non-autonomous stochastic process is a stochastic process in which the drift and
18/// diffusion are functions that depend on time.
19pub trait NonautonomousStochasticProcess: StochasticProcess {
20    fn drift(&self, t: f32, x: f32) -> f32;
21
22    fn diffusion(&self, t: f32, x: f32) -> f32;
23
24    fn run_euler_maruyama(&self, x_0: f32, t_0: f32, t_n: f32, n: usize) -> crate::SimulatedPath {
25        use rand::prelude::Distribution;
26
27        let dt: f32 = (t_n - t_0) / (n as f32);
28
29        // Set up rng.
30        let mut rng = rand::thread_rng();
31        let increments: Vec<f32> = match statrs::distribution::Normal::new(0.0, 1.0) {
32            Ok(dist) => dist,
33            Err(_) => panic!("Bad parameters."),
34        }
35        .sample_iter(&mut rng)
36        .take(n)
37        .map(|x| (x as f32) * dt.sqrt())
38        .collect();
39
40        // Construct empty path.
41        let mut path = crate::SimulatedPath::zeros_generic(
42            nalgebra::Dim::from_usize(n + 1),
43            nalgebra::Dim::from_usize(2),
44        );
45
46        // Fill in t.
47        path[(0, 0)] = t_0;
48        path[(n, 0)] = t_n;
49        for t in 1..(n + 1) {
50            path[(t, 0)] = t_0 + dt * (t as f32);
51        }
52
53        // Fill in x.
54        path[(0, 1)] = x_0;
55        for t in 0..n {
56            path[(t + 1, 1)] = path[(t, 1)]
57                + self.drift(path[(t, 0)], path[(t, 1)]) * (path[(t + 1, 0)] - path[(t, 0)])
58                + self.diffusion(path[(t, 0)], path[(t, 1)]) * increments[t];
59        }
60        path
61    }
62}
63
64/// Autonomous stochastic process.
65///
66/// An autonomous stochastic process is a stochastic process in which the drift and
67/// diffusion are time-invariant functions.
68pub trait AutonomousStochasticProcess: StochasticProcess {
69    fn drift(&self, x: f32) -> f32;
70
71    fn diffusion(&self, x: f32) -> f32;
72
73    fn run_euler_maruyama(&self, x_0: f32, t_0: f32, t_n: f32, n: usize) -> crate::SimulatedPath {
74        use rand::prelude::Distribution;
75
76        let dt: f32 = (t_n - t_0) / (n as f32);
77
78        // Set up rng.
79        let mut rng = rand::thread_rng();
80        let increments: Vec<f32> = match statrs::distribution::Normal::new(0.0, 1.0) {
81            Ok(dist) => dist,
82            Err(_) => panic!("Bad parameters."),
83        }
84        .sample_iter(&mut rng)
85        .take(n)
86        .map(|x| (x as f32) * dt.sqrt())
87        .collect();
88
89        // Construct empty path.
90        let mut path = crate::SimulatedPath::zeros_generic(
91            nalgebra::Dim::from_usize(n + 1),
92            nalgebra::Dim::from_usize(2),
93        );
94
95        // Fill in t.
96        path[(0, 0)] = t_0;
97        path[(n, 0)] = t_n;
98        for t in 1..(n + 1) {
99            path[(t, 0)] = t_0 + dt * (t as f32);
100        }
101
102        // Fill in x.
103        path[(0, 1)] = x_0;
104        for t in 0..n {
105            path[(t + 1, 1)] = path[(t, 1)]
106                + self.drift(path[(t, 1)]) * (path[(t + 1, 0)] - path[(t, 0)])
107                + self.diffusion(path[(t, 1)]) * increments[t];
108        }
109        path
110    }
111}