use std::f64::INFINITY;
use num_traits::{Float, NumAssign};
use serde::{Deserialize, Serialize};
pub mod t_scores;
use crate::utils::error::SimulationError;
use crate::utils::usize_sqrt;
fn sum<T: Float>(points: &[T]) -> T
where
f64: Into<T>,
{
points.iter().fold(0.0.into(), |sum, point| sum + *point)
}
fn sample_mean<T: Float>(points: &[T]) -> Result<T, SimulationError>
where
f64: Into<T>,
{
Ok(sum(points) / usize_to_float(points.len())?)
}
fn sample_variance<T: Float>(points: &[T], mean: &T) -> Result<T, SimulationError>
where
f64: Into<T>,
{
Ok(points
.iter()
.fold(0.0.into(), |acc, point| acc + (*point - *mean).powi(2))
/ usize_to_float(points.len())?)
}
fn usize_to_float<T: Float>(unconv: usize) -> Result<T, SimulationError> {
T::from(unconv).ok_or_else(|| SimulationError::FloatConvError)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfidenceInterval<T: Float> {
lower: T,
upper: T,
}
impl<T: Float> ConfidenceInterval<T>
where
f64: Into<T>,
{
pub fn lower(&self) -> T {
self.lower
}
pub fn upper(&self) -> T {
self.upper
}
pub fn half_width(&self) -> T {
(self.upper - self.lower) / 2.0.into()
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct IndependentSample<T> {
points: Vec<T>,
mean: T,
variance: T,
}
impl<T: Float> IndependentSample<T>
where
f64: Into<T>,
{
pub fn post(points: Vec<T>) -> Result<IndependentSample<T>, SimulationError> {
let mean = sample_mean(&points)?;
let variance = sample_variance(&points, &mean)?;
Ok(IndependentSample {
points,
mean,
variance,
})
}
pub fn confidence_interval_mean(
&self,
alpha: T,
) -> Result<ConfidenceInterval<T>, SimulationError> {
if self.points.len() == 1 {
return Ok(ConfidenceInterval {
lower: self.mean,
upper: self.mean,
});
}
let points_len: T = usize_to_float(self.points.len())?;
Ok(ConfidenceInterval {
lower: self.mean
- t_scores::t_score(alpha, self.points.len() - 1) * self.variance.sqrt()
/ points_len.sqrt(),
upper: self.mean
+ t_scores::t_score(alpha, self.points.len() - 1) * self.variance.sqrt()
/ points_len.sqrt(),
})
}
pub fn point_estimate_mean(&self) -> T {
self.mean
}
pub fn variance(&self) -> T {
self.variance
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TerminatingSimulationOutput<T> {
time_series_replications: Vec<Vec<T>>,
replication_means: Vec<T>,
replications_mean: Option<T>,
replications_variance: Option<T>,
}
impl<T: Float> TerminatingSimulationOutput<T> {
pub fn post(time_series: Vec<T>) -> TerminatingSimulationOutput<T> {
TerminatingSimulationOutput {
time_series_replications: vec![time_series],
replication_means: Vec::new(),
replications_mean: None,
replications_variance: None,
}
}
pub fn put_time_series(&mut self, time_series: Vec<T>) {
self.time_series_replications.push(time_series);
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SteadyStateOutput<T> {
time_series: Vec<T>,
deletion_point: Option<usize>,
batch_size: Option<usize>,
batch_count: Option<usize>,
batch_means: Vec<T>,
batches_mean: Option<T>,
batches_variance: Option<T>,
}
impl<T: Float + NumAssign> SteadyStateOutput<T>
where
f64: Into<T>,
{
pub fn post(time_series: Vec<T>) -> SteadyStateOutput<T> {
SteadyStateOutput {
time_series,
deletion_point: None,
batch_size: None,
batch_count: None,
batch_means: Vec::new(),
batches_mean: None,
batches_variance: None,
}
}
fn set_to_fixed_budget(&mut self) -> Result<(), SimulationError> {
let mut s = 0.0.into();
let mut q = 0.0.into();
let mut d = self.time_series.len() - 2;
let mut mser = vec![0.0.into(); self.time_series.len() - 1];
let time_series_len: T = usize_to_float(self.time_series.len())?;
loop {
s += self.time_series[d + 1];
q += self.time_series[d + 1].powi(2);
mser[d] = q - s.powi(2) / (time_series_len - usize_to_float(d)?).powi(3);
if d == 0 {
let min_mser = (0..(self.time_series.len() - 1) / 2)
.fold(INFINITY.into(), |min_mser, mser_index| {
min_mser.min(mser[mser_index])
});
self.deletion_point = mser.iter().position(|mser_value| *mser_value == min_mser);
break;
} else {
d -= 1;
}
}
let deletion_point = self
.deletion_point
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
let batch_count = usize::min(usize_sqrt(self.time_series.len() - deletion_point), 30);
self.batch_count = Some(batch_count);
let batch_size = (self.time_series.len() - deletion_point) / batch_count;
self.deletion_point = Some(self.time_series.len() - batch_count * batch_size);
self.batch_size = Some(batch_size);
Ok(())
}
fn calculate_batch_statistics(&mut self) -> Result<(), SimulationError> {
if self.batch_count.is_none() {
self.set_to_fixed_budget()?;
}
let deletion_point = self
.deletion_point
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
let batch_size = self
.batch_size
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
let batch_count = self
.batch_count
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
let batch_means: Result<Vec<T>, SimulationError> = (0..batch_count)
.map(|batch_index| {
let batch_start_index = deletion_point + batch_size * batch_index;
let batch_end_index = deletion_point + batch_size * (batch_index + 1);
let points: Vec<T> = (batch_start_index..batch_end_index)
.map(|index| self.time_series[index])
.collect();
sample_mean(&points)
})
.collect();
self.batch_means = batch_means?;
let batches_mean = sample_mean(&self.batch_means)?;
self.batches_variance = Some(sample_variance(&self.batch_means, &batches_mean)?);
self.batches_mean = Some(batches_mean);
Ok(())
}
pub fn confidence_interval_mean(
&mut self,
alpha: T,
) -> Result<ConfidenceInterval<T>, SimulationError> {
if self.batches_mean.is_none() {
self.calculate_batch_statistics()?;
}
let batches_mean = self
.batches_mean
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
let batch_count = self
.batch_count
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
let f_batch_count: T = usize_to_float(batch_count)?;
let batches_variance = self
.batches_variance
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?;
if batch_count == 1 {
return Ok(ConfidenceInterval {
lower: batches_mean,
upper: batches_mean,
});
}
Ok(ConfidenceInterval {
lower: batches_mean
- t_scores::t_score(alpha, batch_count) * batches_variance.sqrt()
/ f_batch_count.sqrt(),
upper: batches_mean
+ t_scores::t_score(alpha, batch_count - 1) * batches_variance.sqrt()
/ f_batch_count.sqrt(),
})
}
pub fn point_estimate_mean(&mut self) -> Result<T, SimulationError> {
if self.batches_mean.is_none() {
self.calculate_batch_statistics()?;
}
Ok(self
.batches_mean
.ok_or_else(|| SimulationError::PrerequisiteCalcError)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn epsilon() -> f64 {
1.0e-12
}
#[test]
fn confidence_interval_mean() {
let sample = IndependentSample::post(vec![
1.02, 0.73, 3.20, 0.23, 1.76, 0.47, 1.89, 1.45, 0.44, 0.23,
]);
let confidence_interval = sample.unwrap().confidence_interval_mean(0.1).unwrap();
assert!((confidence_interval.lower - 0.7492630635369267).abs() < epsilon());
assert!((confidence_interval.upper - 1.534736936463073).abs() < epsilon());
}
}