pathwise_core/scheme/
milstein.rs1use super::Scheme;
2use crate::state::{Diffusion, Increment};
3use crate::process::markov::Drift;
4
5pub struct Milstein {
6 h: f64,
7}
8
9impl Milstein {
10 pub fn new(h: f64) -> Self { Self { h } }
11}
12
13impl Scheme<f64> for Milstein {
17 type Noise = f64;
18
19 fn step<D, G>(&self, drift: &D, diffusion: &G, x: &f64, t: f64, dt: f64, inc: &Increment<f64>) -> f64
20 where
21 D: Drift<f64>,
22 G: Diffusion<f64, f64>,
23 {
24 let dw = inc.dw;
25 let f = drift(x, t);
26 let g = diffusion.apply(x, t, &1.0_f64);
28 let g_plus = diffusion.apply(&(x + self.h), t, &1.0_f64);
29 let g_minus = diffusion.apply(&(x - self.h), t, &1.0_f64);
30 let dg_dx = (g_plus - g_minus) / (2.0 * self.h);
31 x + f * dt + g * dw + 0.5 * g * dg_dx * (dw * dw - dt)
32 }
33}
34
35pub fn milstein() -> Milstein { Milstein::new(1e-5) }
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::process::markov::bm;
41 use crate::state::Increment;
42
43 #[test]
44 fn milstein_equals_euler_for_constant_diffusion() {
45 let b = bm();
46 let m = milstein();
47 let e = crate::scheme::euler::euler();
48 let x = 1.0_f64;
49 let inc = Increment { dw: 0.3_f64, dz: 0.0 };
50 let x_euler = e.step(&b.drift, &b.diffusion, &x, 0.0, 0.01, &inc);
51 let x_milstein = m.step(&b.drift, &b.diffusion, &x, 0.0, 0.01, &inc);
52 assert!((x_euler - x_milstein).abs() < 1e-8,
53 "euler={} milstein={}", x_euler, x_milstein);
54 }
55
56 #[test]
57 fn milstein_differs_from_euler_for_state_dependent_diffusion() {
58 let gbm = crate::process::markov::gbm(0.0, 0.2);
59 let m = milstein();
60 let e = crate::scheme::euler();
61 let x = 1.0_f64;
62 let inc = Increment { dw: 0.5_f64, dz: 0.0 };
63 let dt = 0.01;
64 let x_euler = e.step(&gbm.drift, &gbm.diffusion, &x, 0.0, dt, &inc);
65 let x_milstein = m.step(&gbm.drift, &gbm.diffusion, &x, 0.0, dt, &inc);
66 assert!((x_euler - x_milstein).abs() > 1e-4, "should differ by correction term");
67 }
68}