eryon-mem 0.0.4

this crate implements the memory-related aspects of the eryon framework
/*
    Appellation: statistics <module>
    Contrib: @FL03
*/

/// a type alias for a set of common patterns, where each pattern is represented as a tuple
/// containing a vector of indices and a frequency count.
pub(crate) type CommonPatterns<T = usize> = Vec<(Vec<T>, T)>;
/// a type alias for a [`HashMap`](std::collections::HashMap) that stores the average
/// importance of features by dimension, where the key is the dimension index and the value
/// is the average importance value.
pub(crate) type AverageImportance<T = f32> = std::collections::HashMap<usize, T>;
/// a type alias for a [`HashMap`](std::collections::HashMap) that maps counts of features
/// by dimension
pub(crate) type FeatureCountMap<T = usize> = std::collections::HashMap<T, T>;

/// Statistics about the memory system
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Deserialize, serde_derive::Serialize),
    serde(default, rename_all = "snake_case")
)]
#[repr(C)]
pub struct MemoryStatistics<T = f32> {
    /// Count of features by dimension
    pub(crate) feature_counts: FeatureCountMap<usize>,
    /// Average importance by dimension
    pub(crate) average_importance: AverageImportance<T>,
    /// Most common patterns (sequence, frequency)
    pub(crate) common_patterns: CommonPatterns<usize>,
    /// Number of active features
    pub(crate) active_features: usize,
    /// Current memory time
    pub(crate) memory_time: usize,
    /// Total number of patterns
    pub(crate) pattern_count: usize,
    /// Total number of relationships
    pub(crate) relationship_count: usize,
    /// Total number of features (including dead ones)
    pub(crate) total_features: usize,
}

impl<T> MemoryStatistics<T> {
    /// Creates a new instance of `MemoryStatistics`
    pub fn new() -> Self {
        Self {
            active_features: 0,
            memory_time: 0,
            pattern_count: 0,
            relationship_count: 0,
            total_features: 0,
            average_importance: AverageImportance::new(),
            common_patterns: CommonPatterns::new(),
            feature_counts: FeatureCountMap::new(),
        }
    }
    /// returns a copy of the number of active features
    pub const fn active_features(&self) -> usize {
        self.active_features
    }
    /// returns a mutable reference to the number of active features
    pub const fn active_features_mut(&mut self) -> &mut usize {
        &mut self.active_features
    }
    /// returns the average importance of a feature by dimension
    pub const fn avg_importance_by_dimension(&self) -> &AverageImportance<T> {
        &self.average_importance
    }
    /// returns a mutable reference to the average importance of a feature by dimension
    pub const fn avg_importance_by_dimension_mut(&mut self) -> &mut AverageImportance<T> {
        &mut self.average_importance
    }
    /// returns the number of features by dimension
    pub const fn feature_counts_by_dimension(&self) -> &FeatureCountMap<usize> {
        &self.feature_counts
    }
    /// returns a mutable reference to the number of features by dimension
    pub const fn feature_counts_by_dimension_mut(&mut self) -> &mut FeatureCountMap<usize> {
        &mut self.feature_counts
    }
    /// returns a copy of the current memory time
    pub const fn memory_time(&self) -> usize {
        self.memory_time
    }
    /// returns a mutable reference to the current memory time
    pub const fn memory_time_mut(&mut self) -> &mut usize {
        &mut self.memory_time
    }
    /// returns an immutable reference to the most common patterns
    pub const fn most_common_patterns(&self) -> &CommonPatterns<usize> {
        &self.common_patterns
    }
    /// returns a mutable reference to the most common patterns
    pub const fn most_common_patterns_mut(&mut self) -> &mut CommonPatterns<usize> {
        &mut self.common_patterns
    }
    /// returns a copy of the current pattern count
    pub const fn pattern_count(&self) -> usize {
        self.pattern_count
    }
    /// returns a mutable reference to the current pattern count
    pub const fn pattern_count_mut(&mut self) -> &mut usize {
        &mut self.pattern_count
    }
    /// returns a copy of the current relationship count
    pub const fn relationship_count(&self) -> usize {
        self.relationship_count
    }
    /// returns a mutable reference to the current relationship count
    pub const fn relationship_count_mut(&mut self) -> &mut usize {
        &mut self.relationship_count
    }
    /// returns a copy of the total number of features
    pub const fn total_features(&self) -> usize {
        self.total_features
    }
    /// returns a mutable reference to the total number of features
    pub const fn total_features_mut(&mut self) -> &mut usize {
        &mut self.total_features
    }
    /// update the totale number of active features and returns a mutable reference to the
    /// current statistics
    pub fn set_active_features(&mut self, count: usize) -> &mut Self {
        self.active_features = count;
        self
    }
    /// update the average importance by dimension and returns a mutable reference to the
    /// current statistics
    pub fn set_avg_importance_by_dimension(
        &mut self,
        avg_importance: AverageImportance<T>,
    ) -> &mut Self {
        self.average_importance = avg_importance;
        self
    }
    /// update the feature counts by dimension and returns a mutable reference to the current
    /// statistics
    pub fn set_feature_counts_by_dimension(
        &mut self,
        feature_counts: FeatureCountMap<usize>,
    ) -> &mut Self {
        self.feature_counts = feature_counts;
        self
    }
    /// update the current memory time and returns a mutable reference to the current
    /// statistics
    pub fn set_memory_time(&mut self, time: usize) -> &mut Self {
        self.memory_time = time;
        self
    }
    /// update the most common patterns and returns a mutable reference to the current
    /// statistics
    pub fn set_most_common_patterns(&mut self, patterns: CommonPatterns<usize>) -> &mut Self {
        self.common_patterns = patterns;
        self
    }
    /// update the current pattern count and returns a mutable reference to the current
    /// statistics
    pub fn set_pattern_count(&mut self, count: usize) -> &mut Self {
        self.pattern_count = count;
        self
    }
    /// update the current relationship count and returns a mutable reference to the current
    /// statistics
    pub fn set_relationship_count(&mut self, count: usize) -> &mut Self {
        self.relationship_count = count;
        self
    }
    /// update the total number of features and returns a mutable reference to the current
    /// statistics
    pub fn set_total_features(&mut self, count: usize) -> &mut Self {
        self.total_features = count;
        self
    }
    /// consumes the current instance to create another with the given number of active
    /// features
    pub fn with_active_features(self, active_features: usize) -> Self {
        Self {
            active_features,
            ..self
        }
    }
    /// consumes the current instance to create another with the given average importance by
    /// dimension
    pub fn with_avg_importance_by_dimension(self, avg_importance: AverageImportance<T>) -> Self {
        Self {
            average_importance: avg_importance,
            ..self
        }
    }
    /// consumes the current instance to create another with the given feature counts by
    /// dimension
    pub fn with_feature_counts_by_dimension(self, feature_counts: FeatureCountMap<usize>) -> Self {
        Self {
            feature_counts,
            ..self
        }
    }
    /// consumes the current instance to create another with the given memory time
    pub fn with_memory_time(self, time: usize) -> Self {
        Self {
            memory_time: time,
            ..self
        }
    }
    /// consumes the current instance to create another with the given most common patterns
    pub fn with_most_common_patterns(self, patterns: CommonPatterns<usize>) -> Self {
        Self {
            common_patterns: patterns,
            ..self
        }
    }
    /// consumes the current instance to create another with the given pattern count
    pub fn with_pattern_count(self, count: usize) -> Self {
        Self {
            pattern_count: count,
            ..self
        }
    }
    /// consumes the current instance to create another with the given relationship count
    pub fn with_relationship_count(self, count: usize) -> Self {
        Self {
            relationship_count: count,
            ..self
        }
    }
    /// consumes the current instance to create another with the given total number of
    /// features
    pub fn with_total_features(self, count: usize) -> Self {
        Self {
            total_features: count,
            ..self
        }
    }
    /// generates a report of the memory statistics, formatted as a string.
    pub fn generate_report(&self) -> String
    where
        T: core::fmt::Debug,
    {
        use rstmt::nrt::LPR;
        // initialize a buffer for the lines of the report
        let mut lines = Vec::new();
        // add the memory time to the report
        lines.push(format!("Memory time: {}", self.memory_time()));
        // add the features counts (active / total)
        lines.push(format!(
            "Features (active / total): {} / {}",
            self.active_features(),
            self.total_features()
        ));
        lines.push(format!("Patterns: {}", self.pattern_count()));
        lines.push(format!("Relationships: {}", self.relationship_count()));

        lines.push(format!(
            "Average importance by dimension: {:?}",
            self.avg_importance_by_dimension()
        ));
        // Check for patterns
        if self.pattern_count() > 0 {
            lines.push("Most common patterns:".to_string());
            for (i, (pattern, count)) in self.most_common_patterns().iter().enumerate() {
                let pattern_names = pattern.iter().copied().map(LPR::from).collect::<Vec<_>>();
                let tmp = format!(
                    "\tPattern {}: {} occurrences - {:?}",
                    i + 1,
                    count,
                    pattern_names
                );
                lines.push(tmp);
            }
        }
        lines.join("\n")
    }
}

impl<T> core::fmt::Display for MemoryStatistics<T>
where
    T: core::fmt::Debug,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{report}", report = self.generate_report())
    }
}