use super::traits::*;
use rand::prelude::*;
use ndarray::prelude::*;
use crate::poisson::{PoissonProcess, VariablePoissonProcess};
pub trait Kernel {
fn eval(&self, t: f64) -> f64;
}
#[derive(Debug)]
pub struct Hawkes<T, K: Kernel> {
background: T,
kernel: K
}
impl<T, K: Kernel> Hawkes<T, K> {
pub fn get_kernel(&self) -> &K {
&self.kernel
}
pub fn get_background(&self) -> &T {
&self.background
}
}
pub type ConstBackground = PoissonProcess;
pub type DeterministicBackground<F> = VariablePoissonProcess<F>;
#[derive(Debug)]
pub struct ExpKernel {
pub alpha: f64,
pub beta: f64
}
impl Kernel for ExpKernel {
fn eval(&self, t: f64) -> f64 {
self.alpha * (-self.beta * t).exp()
}
}
#[derive(Debug)]
pub struct SumExpKernel {
num_exp: usize,
alphas: Vec<f64>,
betas: Vec<f64>
}
impl SumExpKernel {
pub fn new(alphas: Vec<f64>, betas: Vec<f64>) -> Self {
assert_eq!(alphas.len(), betas.len());
SumExpKernel {
num_exp: alphas.len(),
alphas,
betas
}
}
}
impl Kernel for SumExpKernel {
fn eval(&self, t: f64) -> f64 {
let alphabetazip = self.alphas.iter().zip(self.betas.iter());
let mut res = 0.;
for (alpha,beta) in alphabetazip {
res += alpha * (-beta*t).exp();
}
res
}
}
#[derive(Debug)]
pub struct PowerLawKernel {
alpha: f64,
beta: f64,
delta: f64
}
impl Kernel for PowerLawKernel {
fn eval(&self, t: f64) -> f64 {
self.alpha / (self.delta + t).powf(self.beta)
}
}
pub type PowerLawHawkes = Hawkes<ConstBackground, PowerLawKernel>;
impl PowerLawHawkes {
pub fn new(alpha: f64, beta: f64, delta: f64, lambda0: f64) -> Self {
let kernel = PowerLawKernel {
alpha, beta, delta
};
Self {
background: ConstBackground::new(lambda0), kernel
}
}
}
pub type ExpHawkes = Hawkes<ConstBackground, ExpKernel>;
impl ExpHawkes {
pub fn new(alpha: f64, beta: f64, lambda0: f64) -> Self {
let kernel = ExpKernel {
alpha, beta
};
let background = ConstBackground::new(lambda0);
Self {
background, kernel
}
}
}
impl TemporalProcess for ExpHawkes {
fn sample(&self, tmax: f64) -> TimeProcessResult {
simulate_hawkes_exp_const_bk(self, tmax)
}
}
impl<F> Hawkes<DeterministicBackground<F>, ExpKernel>
where F: Fn(f64) -> f64 + Send + Sync {
pub fn new(alpha: f64, beta: f64, func: F, max_lbda0: f64) -> Self {
let kernel = ExpKernel { alpha, beta };
let background = DeterministicBackground::new(func, max_lbda0);
Self {
background, kernel
}
}
}
impl<F> TemporalProcess for Hawkes<DeterministicBackground<F>, ExpKernel>
where F: Fn(f64) -> f64 + Send + Sync {
fn sample(&self, tmax: f64) -> TimeProcessResult {
simulate_hawkes_exp_var_bk(self, tmax)
}
}
fn simulate_hawkes_exp_const_bk(model: &ExpHawkes, tmax: f64) -> TimeProcessResult {
let kernel = &model.kernel;
let alpha = kernel.alpha;
let decay = kernel.beta;
let lambda0 = &model.background.intensity(0.);
let mut rng = thread_rng(); let mut timestamps = Vec::new();
let mut intensities = Vec::new();
let mut s = -1.0/lambda0*rng.gen::<f64>().ln();
let mut cur_lambda = lambda0 + alpha;
let mut lbda_max = cur_lambda;
while s < tmax {
let u: f64 = rng.gen();
let ds = -1.0/lbda_max*u.ln();
cur_lambda = lambda0 + (cur_lambda-lambda0)*(-decay*ds).exp();
s += ds; if s > tmax {
break;
}
let d: f64 = rng.gen();
if d < cur_lambda/lbda_max {
cur_lambda = cur_lambda + alpha; timestamps.push(s);
intensities.push(cur_lambda);
}
lbda_max = cur_lambda;
}
let timestamps = Array1::from_vec(timestamps);
let intensities = Array1::from_vec(intensities);
TimeProcessResult {
timestamps, intensities
}
}
fn simulate_hawkes_exp_var_bk<F>(
model: &Hawkes<DeterministicBackground<F>, ExpKernel>,
tmax: f64
) -> TimeProcessResult where F: Fn(f64) -> f64 + Send + Sync
{
let kernel = &model.kernel;
let alpha = kernel.alpha;
let beta = kernel.beta;
let lambda0 = &model.background; let max_lbda0 = model.background.get_max_lambda();
let mut rng = thread_rng(); let mut timestamps = Vec::new();
let mut intensities = Vec::new();
let mut max_lbda = max_lbda0;
let mut s = 0.;
let mut cur_slbda = 0.;
while s < tmax {
let u: f64 = rng.gen();
let ds = -u.ln()/max_lbda;
s += ds;
let cur_blbda = lambda0.intensity(s);
cur_slbda = cur_slbda * (-beta * ds).exp();
let cur_lbda = cur_blbda + cur_slbda;
if s > tmax {
break;
}
let acceptance = cur_lbda / max_lbda;
let d: f64 = rng.gen();
if d < acceptance {
cur_slbda += alpha; let cur_lbda = cur_blbda + cur_slbda;
timestamps.push(s);
intensities.push(cur_lbda);
}
max_lbda = max_lbda0 + cur_slbda; }
let timestamps = Array1::from_vec(timestamps);
let intensities = Array1::from_vec(intensities);
TimeProcessResult {
timestamps, intensities
}
}