use ndarray::Array1;
use stochastic_rs_core::simd_rng::Deterministic;
use stochastic_rs_core::simd_rng::SeedExt;
use stochastic_rs_core::simd_rng::Unseeded;
use stochastic_rs_distributions::normal::SimdNormal;
use crate::traits::FloatExt;
use crate::traits::ProcessExt;
#[derive(Debug, Clone)]
pub struct Arima<T: FloatExt, S: SeedExt = Unseeded> {
pub ar_coefs: Array1<T>,
pub ma_coefs: Array1<T>,
pub d: usize,
pub sigma: T,
pub n: usize,
pub seed: S,
}
impl<T: FloatExt> Arima<T> {
pub fn new(ar_coefs: Array1<T>, ma_coefs: Array1<T>, d: usize, sigma: T, n: usize) -> Self {
assert!(sigma > T::zero(), "Arima requires sigma > 0");
Self {
ar_coefs,
ma_coefs,
d,
sigma,
n,
seed: Unseeded,
}
}
}
impl<T: FloatExt> Arima<T, Deterministic> {
pub fn seeded(
ar_coefs: Array1<T>,
ma_coefs: Array1<T>,
d: usize,
sigma: T,
n: usize,
seed: u64,
) -> Self {
assert!(sigma > T::zero(), "Arima requires sigma > 0");
Self {
ar_coefs,
ma_coefs,
d,
sigma,
n,
seed: Deterministic::new(seed),
}
}
}
impl<T: FloatExt, S: SeedExt> ProcessExt<T> for Arima<T, S> {
type Output = Array1<T>;
fn sample(&self) -> Self::Output {
let p = self.ar_coefs.len();
let q = self.ma_coefs.len();
let mut noise = Array1::<T>::zeros(self.n);
if self.n > 0 {
let slice = noise.as_slice_mut().expect("contiguous");
let normal = SimdNormal::<T>::from_seed_source(T::zero(), self.sigma, &self.seed);
normal.fill_slice_fast(slice);
}
let mut arma_series = Array1::<T>::zeros(self.n);
for t in 0..self.n {
let mut val = noise[t];
for k in 1..=p {
if t >= k {
val += self.ar_coefs[k - 1] * arma_series[t - k];
}
}
for k in 1..=q {
if t >= k {
val += self.ma_coefs[k - 1] * noise[t - k];
}
}
arma_series[t] = val;
}
let mut result = arma_series;
for _ in 0..self.d {
result = Self::inverse_difference(&result);
}
result
}
}
impl<T: FloatExt, S: SeedExt> Arima<T, S> {
fn inverse_difference(y: &Array1<T>) -> Array1<T> {
let n = y.len();
if n == 0 {
return y.clone();
}
let mut x = Array1::<T>::zeros(n);
x[0] = y[0];
for t in 1..n {
x[t] = x[t - 1] + y[t];
}
x
}
}
py_process_1d!(PyArima, Arima,
sig: (ar_coefs, ma_coefs, d, sigma, n, seed=None, dtype=None),
params: (ar_coefs: Vec<f64>, ma_coefs: Vec<f64>, d: usize, sigma: f64, n: usize)
);