use crate::errors::QlResult;
use crate::math::array::Array;
use crate::patterns::observable::{AsObservable, Observable};
use crate::require;
use crate::stochasticprocess::StochasticProcess1D;
use crate::types::{Real, Time};
pub struct MfStateProcess {
reversion: Real,
times: Array,
vols: Array,
observable: Observable,
}
impl MfStateProcess {
pub fn new(reversion: Real, times: Array, vols: Array) -> QlResult<Self> {
require!(reversion.is_finite(), "reversion must be finite");
require!(
vols.size().checked_sub(1) == Some(times.size()),
"number of volatilities must exceed number of times by one"
);
for &t in times.iter() {
validate_time(t)?;
}
require!(
times.windows(2).all(|pair| pair[0] < pair[1]),
"times must be strictly increasing"
);
require!(
vols.iter().all(|v| v.is_finite() && *v >= 0.0),
"volatilities must be finite and nonnegative"
);
Ok(Self {
reversion,
times,
vols,
observable: Observable::new(),
})
}
pub fn reversion(&self) -> Real {
self.reversion
}
pub fn times(&self) -> &[Time] {
&self.times
}
pub fn volatilities(&self) -> &[Real] {
&self.vols
}
fn bucket_variance(&self, start: Time, end: Time, vol: Real) -> QlResult<Real> {
if vol == 0.0 || end == start {
return Ok(0.0);
}
let length = end - start;
let a = if self.reversion.abs() < Real::EPSILON {
0.0
} else {
self.reversion
};
let anchor = if a > 0.0 { end } else { start };
let z = (a.abs() * length) * 2.0;
let ratio = if z == 0.0 { 1.0 } else { -(-z).exp_m1() / z };
let exponent = (a * anchor) * 2.0;
let squared_vol = vol * vol;
let scale = exponent.exp();
let scaled = squared_vol * scale;
let integrated = scaled * length;
let direct = integrated * ratio;
if [squared_vol, scale, scaled, integrated, direct]
.iter()
.all(|value| value.is_normal())
{
return Ok(direct);
}
let log_integral = if a == 0.0 || z == 0.0 {
length.ln()
} else {
(-(-z).exp_m1()).ln() - a.abs().ln() - 2.0_f64.ln()
};
let value = (2.0 * vol.ln() + exponent + log_integral).exp();
require!(value.is_finite(), "transition variance overflow");
Ok(value)
}
}
impl AsObservable for MfStateProcess {
fn observable(&self) -> &Observable {
&self.observable
}
}
impl StochasticProcess1D for MfStateProcess {
fn x0(&self) -> QlResult<Real> {
Ok(0.0)
}
fn drift(&self, t: Time, x: Real) -> QlResult<Real> {
validate_state(t, x)?;
Ok(0.0)
}
fn diffusion(&self, t: Time, x: Real) -> QlResult<Real> {
validate_state(t, x)?;
Ok(self.vols[self.times.partition_point(|boundary| *boundary <= t)])
}
fn expectation(&self, t0: Time, x0: Real, dt: Time) -> QlResult<Real> {
validate_interval(t0, x0, dt)?;
Ok(x0)
}
fn variance(&self, t0: Time, x0: Real, dt: Time) -> QlResult<Real> {
let end = validate_interval(t0, x0, dt)?;
if dt < Real::EPSILON {
return Ok(0.0);
}
if self.times.is_empty() {
if self.reversion.abs() < Real::EPSILON {
return Ok(dt);
}
return self.bucket_variance(t0, end, 1.0);
}
let mut bucket = self.times.partition_point(|boundary| *boundary <= t0);
let mut start = t0;
let mut variance = 0.0;
while start < end {
let stop = self.times.get(bucket).copied().unwrap_or(end).min(end);
variance += self.bucket_variance(start, stop, self.vols[bucket])?;
require!(variance.is_finite(), "transition variance overflow");
start = stop;
bucket += 1;
}
Ok(variance)
}
fn std_deviation(&self, t0: Time, x0: Real, dt: Time) -> QlResult<Real> {
Ok(self.variance(t0, x0, dt)?.sqrt())
}
fn evolve(&self, t0: Time, x0: Real, dt: Time, dw: Real) -> QlResult<Real> {
require!(dw.is_finite(), "Gaussian increment must be finite");
let value = self.expectation(t0, x0, dt)? + self.std_deviation(t0, x0, dt)? * dw;
require!(value.is_finite(), "evolved state overflow");
Ok(value)
}
}
fn validate_time(t: Time) -> QlResult<()> {
require!(
t.is_finite() && t >= 0.0,
"time must be finite and nonnegative"
);
Ok(())
}
fn validate_state(t: Time, x: Real) -> QlResult<()> {
validate_time(t)?;
require!(x.is_finite(), "state must be finite");
Ok(())
}
fn validate_interval(t0: Time, x0: Real, dt: Time) -> QlResult<Time> {
validate_state(t0, x0)?;
validate_time(dt)?;
let end = t0 + dt;
require!(end.is_finite(), "transition end time overflow");
Ok(end)
}