use crate::sde::ManifoldSDE;
use crate::simulate::GeoScheme;
use cartan_core::{Manifold, ParallelTransport};
use pathwise_core::state::Increment;
pub struct GeodesicSRI {
pub eps: f64,
}
impl GeodesicSRI {
pub fn new() -> Self {
Self { eps: 1e-4 }
}
pub fn step<M, D, G>(
&self,
sde: &ManifoldSDE<M, D, G>,
x: &M::Point,
t: f64,
dt: f64,
inc: &Increment<f64>,
) -> M::Point
where
M: Manifold + ParallelTransport,
D: Fn(&M::Point, f64) -> M::Tangent + Send + Sync,
G: Fn(&M::Point, f64) -> M::Tangent + Send + Sync,
M::Tangent: std::ops::Add<Output = M::Tangent>
+ std::ops::Mul<f64, Output = M::Tangent>
+ std::ops::Sub<Output = M::Tangent>
+ Clone,
{
let dw = inc.dw;
let dz = inc.dz;
let f = (sde.drift)(x, t);
let g = (sde.diffusion)(x, t);
let eps = self.eps;
let eps_g = g.clone() * eps;
let y = sde.manifold.exp(x, &eps_g);
let g_at_y = (sde.diffusion)(&y, t);
let tangent = match sde.manifold.transport(&y, x, &g_at_y) {
Ok(g_transported) => {
let nabla_g_g = (g_transported - g.clone()) * (1.0 / eps);
let milstein_correction = nabla_g_g.clone() * (0.5 * (dw * dw - dt));
let sri_correction = nabla_g_g * dz;
f * dt + g * dw + milstein_correction + sri_correction
}
Err(_) => {
f * dt + g * dw
}
};
sde.manifold.exp(x, &tangent)
}
}
impl Default for GeodesicSRI {
fn default() -> Self {
Self::new()
}
}
impl<M, D, G> GeoScheme<M, D, G> for GeodesicSRI
where
M: Manifold + ParallelTransport,
D: Fn(&M::Point, f64) -> M::Tangent + Send + Sync,
G: Fn(&M::Point, f64) -> M::Tangent + Send + Sync,
M::Tangent: std::ops::Add<Output = M::Tangent>
+ std::ops::Mul<f64, Output = M::Tangent>
+ std::ops::Sub<Output = M::Tangent>
+ Clone,
{
fn step_geo(
&self,
sde: &ManifoldSDE<M, D, G>,
x: &M::Point,
t: f64,
dt: f64,
inc: &Increment<f64>,
) -> M::Point {
self.step(sde, x, t, dt, inc)
}
}