use crate::{
FloatExt, SimulationError, XResult,
simulation::{
continuous::{AsymmetricLevy, Levy, simulate_asymmetric_levy, simulate_levy},
prelude::*,
},
};
use rand_distr::{Distribution, Exp1, StandardNormal, uniform::SampleUniform};
#[derive(Debug, Clone)]
pub struct AsymmetricCauchy<T: FloatExt = f64> {
start_position: T,
beta: T,
}
impl<T: FloatExt> AsymmetricCauchy<T> {
pub fn new(start_position: T, beta: T) -> XResult<Self> {
if !(-T::one()..=T::one()).contains(&beta) {
return Err(SimulationError::InvalidParameters(format!(
"The `beta` must be in the range [-1, 1], got {beta:?}",
))
.into());
}
Ok(Self {
start_position,
beta,
})
}
pub fn get_start_position(&self) -> T {
self.start_position
}
pub fn get_asymmetry(&self) -> T {
self.beta
}
}
impl<T: FloatExt + SampleUniform> ContinuousProcess<T> for AsymmetricCauchy<T>
where
Exp1: Distribution<T>,
StandardNormal: Distribution<T>,
{
fn start(&self) -> T {
self.start_position
}
fn simulate(&self, duration: T, time_step: T) -> XResult<(Vec<T>, Vec<T>)> {
simulate_asymmetric_levy(
self.start_position,
T::one(),
self.beta,
duration,
time_step,
)
}
fn displacement(&self, duration: T, time_step: T) -> XResult<T> {
let sp = AsymmetricLevy::new(self.start_position, T::one(), self.beta)?;
sp.displacement(duration, time_step)
}
}
#[derive(Debug, Clone)]
pub struct Cauchy<T: FloatExt = f64> {
start_position: T,
}
impl<T: FloatExt> Default for Cauchy<T> {
fn default() -> Self {
Self {
start_position: T::zero(),
}
}
}
impl<T: FloatExt> Cauchy<T> {
pub fn new(start_position: T) -> Self {
Self { start_position }
}
pub fn get_start_position(&self) -> T {
self.start_position
}
}
impl<T: FloatExt + SampleUniform> ContinuousProcess<T> for Cauchy<T>
where
Exp1: Distribution<T>,
StandardNormal: Distribution<T>,
{
fn start(&self) -> T {
self.start_position
}
fn simulate(&self, duration: T, time_step: T) -> XResult<(Vec<T>, Vec<T>)> {
simulate_levy(self.start_position, T::one(), duration, time_step)
}
fn displacement(&self, duration: T, time_step: T) -> XResult<T> {
let sp = Levy::new(self.start_position, T::one())?;
sp.displacement(duration, time_step)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simulate_cauchy() {
let cauchy = Cauchy::default();
let asymmetric_cauchy = AsymmetricCauchy::new(0.0, 0.4).unwrap();
let time_step = 0.1;
let duration = 1.0;
let (t, x) = cauchy.simulate(duration, time_step).unwrap();
println!("t: {t:?}");
println!("x: {x:?}");
let (t, x) = asymmetric_cauchy.simulate(duration, time_step).unwrap();
println!("t: {t:?}");
println!("x: {x:?}");
}
#[test]
fn test_fpt() {
let cauchy = Cauchy::default();
let asymmetric_cauchy = AsymmetricCauchy::new(0.0, 0.4).unwrap();
let time_step = 0.1;
let fpt = cauchy.fpt((-1.0, 1.0), 1000.0, time_step).unwrap();
println!("fpt: {fpt:?}");
let fpt = asymmetric_cauchy
.fpt((-1.0, 1.0), 1000.0, time_step)
.unwrap();
println!("fpt: {fpt:?}");
}
#[test]
fn test_occupation_time() {
let cauchy = Cauchy::default();
let asymmetric_cauchy = AsymmetricCauchy::new(0.0, 0.4).unwrap();
let time_step = 0.1;
let ot = cauchy
.occupation_time((-1.0, 1.0), 10.0, time_step)
.unwrap();
println!("ot: {ot:?}");
let ot = asymmetric_cauchy
.occupation_time((-1.0, 1.0), 10.0, time_step)
.unwrap();
println!("ot: {ot:?}");
}
#[test]
fn test_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Cauchy>();
assert_send_sync::<AsymmetricCauchy>();
}
}