dvcompute_utils 2.0.0

Discrete event simulation library (utilities)
Documentation
// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::f64;
use std::fmt;
use std::result;

use super::data::StatData;
use super::sampling::SamplingStats;

/// A time persistent variable statistics.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct TimingStats<T> {

    /// The total number of samples.
    pub count: usize,

    /// The minimum value among samples.
    pub min: T,

    /// The maximum value among samples.
    pub max: T,

    /// Return the last value.
    pub last: T,

    /// Return the time at which the minimum is attained.
    pub min_time: f64,

    /// Return the time as which the maximum is attained.
    pub max_time: f64,

    /// Return the start time of sampling.
    pub start_time: f64,

    /// Return the last time of sampling.
    pub last_time: f64,

    /// The sum of values.
    pub sum: f64,

    /// The sum of square values.
    pub sum2: f64
}

impl<T> TimingStats<T> where T: Copy + PartialOrd + StatData {

    /// Create from the sample.
    #[inline]
    pub fn from_sample(t: f64, sample: T) -> Self {
        TimingStats {
            count: 1,
            min: sample,
            max: sample,
            last: sample,
            min_time: t,
            max_time: t,
            start_time: t,
            last_time: t,
            sum: 0.0,
            sum2: 0.0
        }
    }

    /// An empty statistics that has no samples.
    #[inline]
    pub fn empty() -> Self {
        TimingStats {
            count: 0,
            min: <T as StatData>::max(),
            max: <T as StatData>::min(),
            last: <T as StatData>::any(),
            min_time: f64::INFINITY,
            max_time: f64::NEG_INFINITY,
            start_time: f64::INFINITY,
            last_time: f64::NEG_INFINITY,
            sum: f64::NAN,
            sum2: f64::NAN
        }
    }

    /// Add a new sample to the statistics.
    pub fn add(&self, t: f64, sample: T) -> Self {
        if t < self.last_time {
            panic!("The current time cannot be less than the previous one")
        } else {
            let x: f64 = <T as StatData>::into_f64(sample);
            if f64::is_nan(x) {
                *self
            } else if self.count == 0 {
                Self::from_sample(t, sample)
            } else {
                let count    = 1 + self.count;
                let x0       = <T as StatData>::into_f64(self.last);
                let min      = if sample < self.min { sample } else { self.min };
                let max      = if sample > self.max { sample } else { self.max };
                let min_time = if sample < self.min { t } else { self.min_time };
                let max_time = if sample > self.max { t } else { self.max_time };
                let sum      = self.sum + (t - self.last_time) * x0;
                let sum2     = self.sum2 + (t - self.last_time) * x0 * x0;

                TimingStats {
                    count: count,
                    min: min,
                    max: max,
                    last: sample,
                    min_time: min_time,
                    max_time: max_time,
                    start_time: self.start_time,
                    last_time: t,
                    sum: sum,
                    sum2: sum2
                }
            }
        }
    }

    /// Return the average value.
    #[inline]
    pub fn mean(&self) -> f64 {
        if self.count == 0 {
            f64::NAN
        } else if self.last_time <= self.start_time {
            <T as StatData>::into_f64(self.min)
        } else {
            self.sum / (self.last_time - self.start_time)
        }
    }

    /// Return the average square value.
    #[inline]
    pub fn mean2(&self) -> f64 {
        if self.count == 0 {
            f64::NAN
        } else if self.last_time <= self.start_time {
            let x = <T as StatData>::into_f64(self.min);
            x * x
        } else {
            self.sum2 / (self.last_time - self.start_time)
        }
    }

    /// Return the variance.
    #[inline]
    pub fn variance(&self) -> f64 {
        let e  = self.mean();
        let e2 = self.mean2();
        e2 - e * e
    }

    /// Return the deviation.
    #[inline]
    pub fn deviation(&self) -> f64 {
        f64::sqrt(self.variance())
    }

    /// Convert the statistics to its normalised observations-based representation,
    /// where the first argument specifies the number of pseudo-samples.
    #[inline]
    pub fn norm(&self, count: usize) -> SamplingStats<T> {
        SamplingStats {
            count: count,
            min: self.min,
            max: self.max,
            mean: self.mean(),
            mean2: self.mean2()
        }
    }
}

impl<T> fmt::Display for TimingStats<T>
    where T: fmt::Display + Copy + PartialOrd + StatData
{
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(f, "{{ count = {}, mean = {}, std = {}, min = {} (t = {}), max = {} (t = {}), t in [{}, {}] }}",
            self.count, self.mean(), self.deviation(), self.min, self.min_time, self.max, self.max_time, self.start_time, self.last_time)
    }
}

impl From<TimingStats<i32>> for TimingStats<f32> {
    fn from(stats: TimingStats<i32>) -> Self {
        TimingStats {
            count: stats.count,
            min: stats.min as f32,
            max: stats.max as f32,
            last: stats.last as f32,
            min_time: stats.min_time,
            max_time: stats.max_time,
            start_time: stats.start_time,
            last_time: stats.last_time,
            sum: stats.sum,
            sum2: stats.sum2
        }
    }
}

impl From<TimingStats<i32>> for TimingStats<f64> {
    fn from(stats: TimingStats<i32>) -> Self {
        TimingStats {
            count: stats.count,
            min: stats.min as f64,
            max: stats.max as f64,
            last: stats.last as f64,
            min_time: stats.min_time,
            max_time: stats.max_time,
            start_time: stats.start_time,
            last_time: stats.last_time,
            sum: stats.sum,
            sum2: stats.sum2
        }
    }
}

impl From<TimingStats<i64>> for TimingStats<f32> {
    fn from(stats: TimingStats<i64>) -> Self {
        TimingStats {
            count: stats.count,
            min: stats.min as f32,
            max: stats.max as f32,
            last: stats.last as f32,
            min_time: stats.min_time,
            max_time: stats.max_time,
            start_time: stats.start_time,
            last_time: stats.last_time,
            sum: stats.sum,
            sum2: stats.sum2
        }
    }
}

impl From<TimingStats<i64>> for TimingStats<f64> {
    fn from(stats: TimingStats<i64>) -> Self {
        TimingStats {
            count: stats.count,
            min: stats.min as f64,
            max: stats.max as f64,
            last: stats.last as f64,
            min_time: stats.min_time,
            max_time: stats.max_time,
            start_time: stats.start_time,
            last_time: stats.last_time,
            sum: stats.sum,
            sum2: stats.sum2
        }
    }
}

impl From<TimingStats<isize>> for TimingStats<f32> {
    fn from(stats: TimingStats<isize>) -> Self {
        TimingStats {
            count: stats.count,
            min: stats.min as f32,
            max: stats.max as f32,
            last: stats.last as f32,
            min_time: stats.min_time,
            max_time: stats.max_time,
            start_time: stats.start_time,
            last_time: stats.last_time,
            sum: stats.sum,
            sum2: stats.sum2
        }
    }
}

impl From<TimingStats<isize>> for TimingStats<f64> {
    fn from(stats: TimingStats<isize>) -> Self {
        TimingStats {
            count: stats.count,
            min: stats.min as f64,
            max: stats.max as f64,
            last: stats.last as f64,
            min_time: stats.min_time,
            max_time: stats.max_time,
            start_time: stats.start_time,
            last_time: stats.last_time,
            sum: stats.sum,
            sum2: stats.sum2
        }
    }
}