#![cfg(feature = "simd-burstiness")]
use crate::interbar_types::TradeSnapshot;
use smallvec::SmallVec;
pub(crate) fn compute_burstiness_simd(lookback: &[&TradeSnapshot]) -> f64 {
if lookback.len() < 2 {
return 0.0;
}
let inter_arrivals = compute_inter_arrivals_simd(lookback);
let n = inter_arrivals.len() as f64;
let mu = sum_f64_simd(&inter_arrivals) / n;
let variance = variance_f64_simd(&inter_arrivals, mu);
let sigma = variance.sqrt();
let denominator = sigma + mu;
if denominator > f64::EPSILON {
(sigma - mu) / denominator
} else {
0.0
}
}
#[inline]
fn compute_inter_arrivals_simd(lookback: &[&TradeSnapshot]) -> SmallVec<[f64; 64]> {
let n = lookback.len();
if n < 2 {
return SmallVec::new();
}
let mut inter_arrivals = SmallVec::<[f64; 64]>::with_capacity(n - 1);
unsafe { inter_arrivals.set_len(n - 1); }
let simd_chunks = (n - 1) / 2;
for i in 0..simd_chunks {
let idx = i * 2;
inter_arrivals[idx] = (lookback[idx + 1].timestamp - lookback[idx].timestamp) as f64;
inter_arrivals[idx + 1] =
(lookback[idx + 2].timestamp - lookback[idx + 1].timestamp) as f64;
}
if (n - 1) % 2 == 1 {
let idx = simd_chunks * 2;
inter_arrivals[idx] = (lookback[idx + 2].timestamp - lookback[idx + 1].timestamp) as f64;
}
inter_arrivals
}
#[inline]
fn sum_f64_simd(values: &[f64]) -> f64 {
if values.is_empty() {
return 0.0;
}
let mut sum = 0.0;
let chunks = values.len() / 2;
for i in 0..chunks {
sum += values[i * 2] + values[i * 2 + 1];
}
if values.len() % 2 == 1 {
sum += values[values.len() - 1];
}
sum
}
#[inline]
fn variance_f64_simd(values: &[f64], mu: f64) -> f64 {
if values.is_empty() {
return 0.0;
}
let mut sum_sq = 0.0;
let chunks = values.len() / 2;
for i in 0..chunks {
let v0 = values[i * 2] - mu;
let v1 = values[i * 2 + 1] - mu;
sum_sq += v0 * v0 + v1 * v1;
}
if values.len() % 2 == 1 {
let v = values[values.len() - 1] - mu;
sum_sq += v * v;
}
sum_sq / (values.len() as f64)
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_snapshot(ts: i64, price: f64, volume: f64) -> TradeSnapshot {
TradeSnapshot {
timestamp: ts,
price: crate::types::FixedPoint((price * 1e8) as i64),
volume: crate::types::FixedPoint((volume * 1e8) as i64),
is_buyer_maker: false,
turnover: (price * volume * 1e8) as i128,
}
}
#[test]
fn test_burstiness_simd_edge_case_empty() {
let lookback: Vec<&TradeSnapshot> = vec![];
assert_eq!(compute_burstiness_simd(&lookback), 0.0);
}
#[test]
fn test_burstiness_simd_edge_case_single() {
let t0 = create_test_snapshot(0, 100.0, 1.0);
let lookback = vec![&t0];
assert_eq!(compute_burstiness_simd(&lookback), 0.0);
}
#[test]
fn test_burstiness_simd_regular_intervals() {
let t0 = create_test_snapshot(0, 100.0, 1.0);
let t1 = create_test_snapshot(1000, 100.0, 1.0);
let t2 = create_test_snapshot(2000, 100.0, 1.0);
let t3 = create_test_snapshot(3000, 100.0, 1.0);
let t4 = create_test_snapshot(4000, 100.0, 1.0);
let lookback = vec![&t0, &t1, &t2, &t3, &t4];
let b = compute_burstiness_simd(&lookback);
assert!((b - (-1.0)).abs() < 0.01);
}
#[test]
fn test_burstiness_simd_clustered_arrivals() {
let t0 = create_test_snapshot(0, 100.0, 1.0);
let t1 = create_test_snapshot(10, 100.0, 1.0);
let t2 = create_test_snapshot(20, 100.0, 1.0);
let t3 = create_test_snapshot(5000, 100.0, 1.0); let t4 = create_test_snapshot(5010, 100.0, 1.0);
let t5 = create_test_snapshot(5020, 100.0, 1.0);
let lookback = vec![&t0, &t1, &t2, &t3, &t4, &t5];
let b = compute_burstiness_simd(&lookback);
assert!(b > 0.0);
assert!(b <= 1.0);
}
#[test]
fn test_burstiness_simd_bounds() {
let t0 = create_test_snapshot(0, 100.0, 1.0);
let t1 = create_test_snapshot(100, 100.0, 1.0);
let t2 = create_test_snapshot(200, 100.0, 1.0);
let t3 = create_test_snapshot(300, 100.0, 1.0);
let lookback = vec![&t0, &t1, &t2, &t3];
let b = compute_burstiness_simd(&lookback);
assert!(b >= -1.0 && b <= 1.0);
}
#[test]
fn test_simd_remainder_handling() {
let trades: Vec<_> = (0..7)
.map(|i| create_test_snapshot((i * 100) as i64, 100.0, 1.0))
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b = compute_burstiness_simd(&trade_refs);
assert!(b >= -1.0 && b <= 1.0);
}
#[test]
fn test_simd_large_dataset() {
let trades: Vec<_> = (0..150)
.map(|i| create_test_snapshot((i * 50) as i64, 100.0 + (i as f64 * 0.01), 1.0))
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b = compute_burstiness_simd(&trade_refs);
assert!(b >= -1.0 && b <= 1.0, "Large dataset SIMD result out of bounds");
}
#[test]
fn test_simd_poisson_process() {
let mut ts = 0i64;
let trades: Vec<_> = (0..20)
.map(|i| {
let delta = (100.0 * (1.0 + (i as f64 / 10.0).sin())) as i64;
ts += delta;
create_test_snapshot(ts, 100.0, 1.0)
})
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b = compute_burstiness_simd(&trade_refs);
assert!(b.abs() < 0.5, "Poisson process B-statistic too far from 0");
}
#[test]
fn test_simd_identical_intervals() {
let trades: Vec<_> = (0..10)
.map(|i| create_test_snapshot((i * 1000) as i64, 100.0, 1.0))
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b = compute_burstiness_simd(&trade_refs);
assert!((b - (-1.0)).abs() < 0.01, "Identical intervals should give B ≈ -1");
}
#[test]
fn test_simd_highly_bursty() {
let timestamps = vec![0, 1, 2, 3, 4000, 4001, 4002, 4003, 8000, 8001, 8002, 8003];
let trades: Vec<_> = timestamps
.iter()
.map(|&ts| create_test_snapshot(ts, 100.0, 1.0))
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b = compute_burstiness_simd(&trade_refs);
assert!(b > 0.3, "Bursty pattern B-statistic too low: {}", b);
assert!(b <= 1.0, "Burstiness should not exceed 1.0");
}
#[test]
fn test_simd_two_trades() {
let t0 = create_test_snapshot(0, 100.0, 1.0);
let t1 = create_test_snapshot(1000, 100.0, 1.0);
let lookback = vec![&t0, &t1];
let b = compute_burstiness_simd(&lookback);
assert_eq!(b, -1.0, "Two-trade case should give B = -1");
}
#[test]
fn test_simd_statistical_equivalence() {
let trades: Vec<_> = (0..50)
.map(|i| create_test_snapshot((i * 100) as i64, 100.0, 1.0))
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b_simd = compute_burstiness_simd(&trade_refs);
let n = trade_refs.len();
let mut mean = 0.0;
let mut m2 = 0.0;
let mut count = 0.0;
for i in 1..n {
let delta_t = (trade_refs[i].timestamp - trade_refs[i - 1].timestamp) as f64;
count += 1.0;
let delta = delta_t - mean;
mean += delta / count;
let delta2 = delta_t - mean;
m2 += delta * delta2;
}
let variance = m2 / count;
let sigma = variance.sqrt();
let denominator = sigma + mean;
let b_scalar = if denominator > f64::EPSILON {
(sigma - mean) / denominator
} else {
0.0
};
assert!(
(b_simd - b_scalar).abs() < 1e-8,
"SIMD result diverged from scalar: {} vs {}",
b_simd,
b_scalar
);
}
#[test]
fn test_simd_very_large_timestamps() {
let base_ts = 1_700_000_000_000_000i64; let trades: Vec<_> = (0..10)
.map(|i| create_test_snapshot(base_ts + (i * 1000) as i64, 100.0, 1.0))
.collect();
let trade_refs: Vec<_> = trades.iter().collect();
let b = compute_burstiness_simd(&trade_refs);
assert!(b >= -1.0 && b <= 1.0, "Large timestamp SIMD failed");
}
}