use crate::errors::QlResult;
use crate::require;
use super::sequence_shape::*;
use crate::types::Real;
#[path = "sequence_moments.rs"]
mod moments;
pub fn evaluate_sequence_batch(
values: &[Real],
rows: usize,
dimension: usize,
weights: Option<&[Real]>,
measure: SequenceStatistic,
) -> QlResult<Vec<Real>> {
validate_sequence_shape(rows, dimension, measure)?;
require!(
values.len() == rows * dimension,
"sequence sample shape mismatch"
);
if let Some(weights) = weights {
require!(weights.len() == rows, "sequence weight length mismatch");
}
let mut statistics = SequenceStatistics::new(dimension)?;
for (index, sample) in values.chunks_exact(dimension).enumerate() {
statistics.add_weighted(sample, weights.map_or(1.0, |weights| weights[index]))?;
}
match measure {
SequenceStatistic::Mean => statistics.mean(),
SequenceStatistic::Variance => statistics.variance(),
SequenceStatistic::StandardDeviation => statistics.standard_deviation(),
SequenceStatistic::ErrorEstimate => statistics.error_estimate(),
SequenceStatistic::Minimum => statistics.min(),
SequenceStatistic::Maximum => statistics.max(),
SequenceStatistic::Covariance => statistics.covariance(),
SequenceStatistic::Correlation => statistics.correlation(),
}
}
#[derive(Clone, Debug)]
pub struct SequenceStatistics {
dimension: usize,
values: Vec<Real>,
weights: Vec<Real>,
weight_sum: Real,
}
impl SequenceStatistics {
pub fn new(dimension: usize) -> QlResult<Self> {
validate_sequence_shape(1, dimension, SequenceStatistic::Mean)?;
Ok(Self {
dimension,
values: Vec::new(),
weights: Vec::new(),
weight_sum: 0.0,
})
}
pub fn dimension(&self) -> usize {
self.dimension
}
pub fn samples(&self) -> usize {
self.weights.len()
}
pub fn weight_sum(&self) -> Real {
self.weight_sum
}
pub fn add(&mut self, sample: &[Real]) -> QlResult<()> {
self.add_weighted(sample, 1.0)
}
pub fn add_weighted(&mut self, sample: &[Real], weight: Real) -> QlResult<()> {
require!(
sample.len() == self.dimension,
"sequence sample dimension mismatch"
);
require!(
sample.iter().all(|value| value.is_finite()),
"nonfinite sequence sample"
);
require!(
weight.is_finite() && weight >= 0.0,
"invalid sequence weight"
);
let total = self.weight_sum + weight;
require!(total.is_finite(), "sequence weight sum overflow");
validate_sequence_shape(self.samples() + 1, self.dimension, SequenceStatistic::Mean)?;
self.values.extend_from_slice(sample);
self.weights.push(weight);
self.weight_sum = total;
Ok(())
}
pub fn reset(&mut self, dimension: usize) -> QlResult<()> {
validate_sequence_shape(1, dimension, SequenceStatistic::Mean)?;
self.values.clear();
self.weights.clear();
self.weight_sum = 0.0;
self.dimension = dimension;
Ok(())
}
pub fn mean(&self) -> QlResult<Vec<Real>> {
self.require_weight()?;
(0..self.dimension)
.map(|column| self.center(column).map(|center| center.mean))
.collect()
}
pub fn min(&self) -> QlResult<Vec<Real>> {
self.extrema(Real::min)
}
pub fn max(&self) -> QlResult<Vec<Real>> {
self.extrema(Real::max)
}
fn require_weight(&self) -> QlResult<()> {
require!(
self.weight_sum.is_finite() && self.weight_sum > 0.0,
"sequence total weight must be positive"
);
Ok(())
}
fn extrema(&self, operation: fn(Real, Real) -> Real) -> QlResult<Vec<Real>> {
self.require_weight()?;
let mut result = self.values[..self.dimension].to_vec();
for row in self.values.chunks_exact(self.dimension).skip(1) {
for (value, &sample) in result.iter_mut().zip(row) {
*value = operation(*value, sample);
}
}
Ok(result)
}
}
#[cfg(test)]
#[path = "sequence_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "sequence_state_tests.rs"]
mod state_tests;