use std::f64::consts::PI;
use rand::Rng;
use rand_distr::{Exp1, Distribution};
use spfunc::gamma::gamma;
use crate::integrator::Integrator;
use crate::error::Error;
#[derive(Debug)]
pub struct AlphaStable {
alpha: f64,
beta: f64,
sigma: f64,
mu: f64,
mu_0: f64,
tol: Tol,
integrator: Integrator,
}
impl Default for AlphaStable {
fn default() -> Self {
AlphaStable { alpha: 2.0, beta: 0.0, sigma: 0.0, mu: 0.0, mu_0: 0.0, tol: Tol::default(), integrator: Integrator::default() }
}
}
impl AlphaStable {
pub fn new(alpha: f64, beta: f64, sigma: f64, mu: f64) -> Result<AlphaStable, Error> {
if alpha <= 0.0 || alpha > 2.0 {
return Err(Error::AlphaError {alpha});
}
if beta < -1.0 || beta > 1.0 {
return Err(Error::BetaError {beta});
}
let tol = Tol::default();
let mu_0 = if close( alpha, 1.0, tol.alpha ) {
mu + beta * sigma * 2.0 * sigma.ln() / PI
} else {
mu + beta * sigma * (0.5 * PI * alpha).tan()
};
Ok(AlphaStable { alpha, beta, sigma, mu, mu_0, tol, integrator: Integrator::default() })
}
#[allow(non_snake_case)]
pub fn new_S0(alpha: f64, beta: f64, sigma: f64, mu_0: f64) -> Result<AlphaStable, Error> {
if alpha <= 0.0 || alpha > 2.0 {
return Err(Error::AlphaError {alpha});
}
if beta < -1.0 || beta > 1.0 {
return Err(Error::BetaError {beta});
}
let tol = Tol::default();
let mu = if close( alpha, 1.0, tol.alpha ) {
mu_0 - beta * sigma * 2.0 * sigma.ln() / PI
} else {
mu_0 - beta * sigma * (0.5 * PI * alpha).tan()
};
Ok(AlphaStable { alpha, beta, sigma, mu, mu_0, tol, integrator: Integrator::default() })
}
pub fn with_tol(&mut self, tol: Tol) -> &mut Self {
self.tol = tol;
self
}
pub fn with_integrator(&mut self, integrator: Integrator) -> &mut Self {
self.integrator = integrator;
self
}
pub fn get_params(&self) -> (f64, f64, f64, f64, f64) {
(self.alpha, self.beta, self.sigma, self.mu, self.mu_0)
}
pub fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
if close( self.beta, 0.0, self.tol.beta ) {
return self.sample_symmetric(rng);
}
let v = PI * (rng.gen::<f64>() - 0.5);
let mut w = 0.0;
while w == 0.0 {
w = Exp1.sample(rng);
}
if close( self.alpha, 1.0, self.tol.alpha ) {
let x = ( (0.5 * PI + self.beta * v) * v.tan() -
self.beta * ( (0.5 * PI * w * v.cos()) / (0.5 * PI + self.beta * v) ).ln()) / (0.5 * PI);
return self.sigma * x + self.beta * self.sigma * self.sigma.ln() / (0.5 * PI) + self.mu;
}
let t = self.beta * (0.5 * PI * self.alpha).tan();
let s = (1.0 + t * t).powf( 0.5 / self.alpha );
let b = t.atan() / self.alpha;
let x = s * ( self.alpha * (v + b) ).sin() *
( ( (v - self.alpha*(v + b)).cos() / w ).powf((1.0 - self.alpha) / self.alpha) ) /
( v.cos().powf(1.0 / self.alpha) );
return self.sigma * x + self.mu;
}
fn sample_symmetric<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
let v = PI * (rng.gen::<f64>() - 0.5);
if close( self.alpha, 1.0, self.tol.alpha ) {
return self.sigma * v.tan() + self.mu;
}
let mut w = 0.0_f64;
while w == 0.0 {
w = Exp1.sample(rng);
}
if close( self.alpha, 2.0, self.tol.alpha ) {
return 2.0 * v.sin() * w.sqrt() * self.sigma + self.mu;
}
let t = (self.alpha * v).sin() / v.cos().powf( 1.0 / self.alpha );
let s = ( ((1.0 - self.alpha) * v).cos() / w ).powf( (1.0-self.alpha)/self.alpha );
self.sigma * t * s + self.mu
}
pub fn pdf(&self, x: f64) -> Result<f64, Error> {
let x = (x - self.mu_0) / self.sigma;
let val = pdf_scaled(x, self.alpha, self.beta, &self.tol, &self.integrator)?;
Ok(val/self.sigma)
}
}
fn pdf_scaled(x: f64, alpha: f64, beta: f64, tol: &Tol, integrator: &Integrator) -> Result<f64, Error> {
if close( alpha, 2.0, tol.alpha) {
return Ok((-0.25 * x * x).exp() / (4.0 * PI).sqrt());
} else if close( alpha, 1.0, tol.alpha) && !close(beta, 0.0, tol.beta) {
let gamma = (-0.5 * PI * x / beta).exp();
let a = -0.5 * PI;
let b = 0.5 * PI;
let val = integrator.integrate(
&|theta| {
derivative_alpha_eq_1(theta, beta) * gamma - 1.0
},
&|theta| {
integrand_alpha_eq_1(theta, beta, gamma)
},
a, b,
)?;
return Ok(val * 0.5 * gamma / beta.abs());
} else if close(alpha, 1.0, tol.alpha) && close(beta, 0.0, tol.beta) {
return Ok(1.0 / ((1.0 + x * x) * PI));
} else if !close(alpha, 1.0, tol.alpha) {
let zeta = -beta * (0.5 * PI * alpha).tan();
let eps = (-zeta).atan() / alpha;
if close(x, zeta, tol.zeta) {
return Ok(gamma(1.0 + 1.0 / alpha) * eps.cos() / (PI * (1.0 + zeta * zeta).powf(0.5 / alpha)));
} else if x > zeta {
let gamma = (x - zeta).powf(alpha / (alpha - 1.0));
let a = -eps;
let b = 0.5 * PI;
let val = integrator.integrate(
&|theta| {
derivative_alpha_neq_1(theta, alpha, eps) * gamma - 1.0
},
&|theta| {
integrand_alpha_neq_1(theta, alpha, eps, gamma)
},
a, b,
)?;
return Ok(val * alpha * (x - zeta).powf(1.0/(alpha - 1.0)) / (PI * (alpha - 1.0).abs()));
} else if x < zeta {
return pdf_scaled(-x, alpha, -beta, tol, integrator);
}
}
return Ok(0.0);
}
pub(crate) fn close( arg: f64, close_to: f64, with_tol: f64 ) -> bool {
(arg-close_to).abs() <= with_tol.abs()
}
fn derivative_alpha_neq_1(theta: f64, alpha: f64, eps: f64) -> f64 {
(alpha * eps).cos().powf(1.0/(alpha - 1.0)) *
(theta.cos() / (alpha * (theta + eps)).sin()).powf(alpha / (alpha - 1.0)) *
(alpha * eps + (alpha - 1.0) * theta).cos() / theta.cos()
}
fn integrand_alpha_neq_1(theta: f64, alpha: f64, eps: f64, gamma: f64) -> f64 {
let val = derivative_alpha_neq_1(theta, alpha, eps);
if val.is_nan() || val.is_infinite() {
return 0.0;
}
(val.ln() - val * gamma).exp()
}
fn derivative_alpha_eq_1(theta: f64, beta: f64) -> f64 {
(1.0 + 2.0 * beta * theta / PI) * ( (0.5 * PI / beta + theta) * theta.tan() ).exp() / theta.cos()
}
fn integrand_alpha_eq_1(theta: f64, beta: f64, gamma: f64) -> f64 {
let val = derivative_alpha_eq_1(theta, beta);
if val.is_nan() || val.is_infinite() {
return 0.0;
}
(val.ln() - val * gamma).exp()
}
#[derive(Debug)]
pub struct Tol {
alpha: f64,
beta: f64,
zeta: f64,
}
impl Tol {
pub fn new( alpha: f64, beta: f64, zeta: f64) -> Self {
Tol { alpha, beta, zeta }
}
}
impl Default for Tol {
fn default() -> Self {
Tol { alpha: 1e-6, beta: 1e-6, zeta: 1e-6 }
}
}