ruvector-memopt 0.5.0

Intelligent cross-platform memory optimizer with neural learning capabilities for smart optimization decisions
Documentation
//! Spectral analysis for memory pattern classification
//!
//! Uses eigenvalue-based methods to classify memory usage patterns
//! and identify anomalies.

use std::collections::VecDeque;

/// Spectral analyzer for memory pattern classification
pub struct SpectralAnalyzer {
    /// Rolling window of memory samples
    samples: VecDeque<f64>,
    /// Window size
    window_size: usize,
    /// FFT-like frequency bins (simplified)
    frequency_bins: Vec<f64>,
    /// Number of frequency bins
    num_bins: usize,
}

impl SpectralAnalyzer {
    pub fn new(window_size: usize) -> Self {
        let num_bins = 8; // Simplified frequency analysis
        Self {
            samples: VecDeque::with_capacity(window_size),
            window_size,
            frequency_bins: vec![0.0; num_bins],
            num_bins,
        }
    }

    /// Add a memory usage sample (0.0 to 1.0)
    pub fn add_sample(&mut self, usage: f64) {
        if self.samples.len() >= self.window_size {
            self.samples.pop_front();
        }
        self.samples.push_back(usage.clamp(0.0, 1.0));

        if self.samples.len() >= self.window_size / 2 {
            self.update_spectrum();
        }
    }

    /// Update frequency spectrum using simplified DFT
    fn update_spectrum(&mut self) {
        let n = self.samples.len();
        if n < 2 {
            return;
        }

        let samples: Vec<f64> = self.samples.iter().copied().collect();

        // Simplified frequency analysis (not full FFT, but captures patterns)
        for k in 0..self.num_bins {
            let mut real = 0.0;
            let mut imag = 0.0;

            for (i, &sample) in samples.iter().enumerate() {
                let angle = 2.0 * std::f64::consts::PI * (k as f64) * (i as f64) / (n as f64);
                real += sample * angle.cos();
                imag += sample * angle.sin();
            }

            self.frequency_bins[k] = (real * real + imag * imag).sqrt() / (n as f64);
        }
    }

    /// Classify the current memory pattern
    pub fn classify(&self) -> MemoryPatternClass {
        if self.samples.len() < self.window_size / 4 {
            return MemoryPatternClass::Unknown;
        }

        let mean = self.mean();
        let variance = self.variance();
        let trend = self.trend();
        let dominant_freq = self.dominant_frequency();

        // Classification rules
        if variance < 0.01 {
            if mean > 0.8 {
                MemoryPatternClass::ConstantHigh
            } else if mean < 0.4 {
                MemoryPatternClass::ConstantLow
            } else {
                MemoryPatternClass::Stable
            }
        } else if trend > 0.1 {
            MemoryPatternClass::Increasing
        } else if trend < -0.1 {
            MemoryPatternClass::Decreasing
        } else if dominant_freq > 0 && self.frequency_bins[dominant_freq] > 0.1 {
            MemoryPatternClass::Oscillating
        } else if variance > 0.1 {
            MemoryPatternClass::Volatile
        } else {
            MemoryPatternClass::Normal
        }
    }

    /// Calculate mean of samples
    fn mean(&self) -> f64 {
        if self.samples.is_empty() {
            return 0.0;
        }
        self.samples.iter().sum::<f64>() / self.samples.len() as f64
    }

    /// Calculate variance of samples
    fn variance(&self) -> f64 {
        if self.samples.len() < 2 {
            return 0.0;
        }
        let mean = self.mean();
        self.samples.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / self.samples.len() as f64
    }

    /// Calculate linear trend (slope)
    fn trend(&self) -> f64 {
        let n = self.samples.len();
        if n < 2 {
            return 0.0;
        }

        let mut sum_x = 0.0;
        let mut sum_y = 0.0;
        let mut sum_xy = 0.0;
        let mut sum_xx = 0.0;

        for (i, &y) in self.samples.iter().enumerate() {
            let x = i as f64;
            sum_x += x;
            sum_y += y;
            sum_xy += x * y;
            sum_xx += x * x;
        }

        let n = n as f64;
        let denom = n * sum_xx - sum_x * sum_x;
        if denom.abs() < 1e-10 {
            return 0.0;
        }

        (n * sum_xy - sum_x * sum_y) / denom
    }

    /// Find dominant frequency bin
    fn dominant_frequency(&self) -> usize {
        self.frequency_bins
            .iter()
            .enumerate()
            .skip(1) // Skip DC component
            .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
            .map(|(i, _)| i)
            .unwrap_or(0)
    }

    /// Get recommendation based on pattern
    pub fn get_recommendation(&self) -> SpectralRecommendation {
        let class = self.classify();
        let mean = self.mean();
        let trend = self.trend();

        match class {
            MemoryPatternClass::ConstantHigh => SpectralRecommendation {
                action: RecommendedAction::OptimizeNow,
                confidence: 0.95,
                reason: "Consistently high memory usage".into(),
                predicted_relief_mb: (mean * 1000.0) as u64,
            },
            MemoryPatternClass::Increasing => SpectralRecommendation {
                action: RecommendedAction::OptimizeSoon,
                confidence: 0.85,
                reason: format!("Memory usage trending up (slope: {:.3})", trend),
                predicted_relief_mb: ((mean + trend * 10.0) * 500.0) as u64,
            },
            MemoryPatternClass::Volatile => SpectralRecommendation {
                action: RecommendedAction::Monitor,
                confidence: 0.7,
                reason: "Volatile memory usage pattern".into(),
                predicted_relief_mb: (mean * 300.0) as u64,
            },
            MemoryPatternClass::Oscillating => SpectralRecommendation {
                action: RecommendedAction::ScheduleOptimization,
                confidence: 0.75,
                reason: "Cyclic memory pattern detected".into(),
                predicted_relief_mb: (mean * 400.0) as u64,
            },
            MemoryPatternClass::Decreasing => SpectralRecommendation {
                action: RecommendedAction::Wait,
                confidence: 0.8,
                reason: "Memory usage decreasing naturally".into(),
                predicted_relief_mb: 0,
            },
            MemoryPatternClass::ConstantLow | MemoryPatternClass::Stable => SpectralRecommendation {
                action: RecommendedAction::NoAction,
                confidence: 0.9,
                reason: "Memory usage is healthy".into(),
                predicted_relief_mb: 0,
            },
            _ => SpectralRecommendation {
                action: RecommendedAction::Monitor,
                confidence: 0.5,
                reason: "Insufficient data".into(),
                predicted_relief_mb: 0,
            },
        }
    }

    /// Statistics for benchmarking
    pub fn stats(&self) -> SpectralStats {
        SpectralStats {
            sample_count: self.samples.len(),
            mean: self.mean(),
            variance: self.variance(),
            trend: self.trend(),
            dominant_frequency: self.dominant_frequency(),
            classification: self.classify(),
        }
    }
}

impl Default for SpectralAnalyzer {
    fn default() -> Self {
        Self::new(60) // 1 minute window at 1 sample/sec
    }
}

/// Memory usage pattern classification
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryPatternClass {
    Unknown,
    Stable,
    ConstantLow,
    ConstantHigh,
    Increasing,
    Decreasing,
    Oscillating,
    Volatile,
    Normal,
}

/// Recommended action based on spectral analysis
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecommendedAction {
    NoAction,
    Monitor,
    Wait,
    ScheduleOptimization,
    OptimizeSoon,
    OptimizeNow,
}

/// Spectral analysis recommendation
#[derive(Debug, Clone)]
pub struct SpectralRecommendation {
    pub action: RecommendedAction,
    pub confidence: f64,
    pub reason: String,
    pub predicted_relief_mb: u64,
}

#[derive(Debug, Clone)]
pub struct SpectralStats {
    pub sample_count: usize,
    pub mean: f64,
    pub variance: f64,
    pub trend: f64,
    pub dominant_frequency: usize,
    pub classification: MemoryPatternClass,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_spectral_stable() {
        let mut analyzer = SpectralAnalyzer::new(20);

        // Add stable samples around 0.5
        for _ in 0..20 {
            analyzer.add_sample(0.5);
        }

        let class = analyzer.classify();
        assert!(matches!(class, MemoryPatternClass::Stable | MemoryPatternClass::Normal));
    }

    #[test]
    fn test_spectral_increasing() {
        let mut analyzer = SpectralAnalyzer::new(20);

        // Add strongly increasing samples
        for i in 0..20 {
            analyzer.add_sample(0.2 + (i as f64) * 0.04);
        }

        let class = analyzer.classify();
        // Should be increasing or normal with positive trend
        let stats = analyzer.stats();
        assert!(stats.trend > 0.0, "Trend should be positive: {}", stats.trend);
    }

    #[test]
    fn test_recommendation() {
        let mut analyzer = SpectralAnalyzer::new(20);

        // High constant usage
        for _ in 0..20 {
            analyzer.add_sample(0.9);
        }

        let rec = analyzer.get_recommendation();
        assert_eq!(rec.action, RecommendedAction::OptimizeNow);
        assert!(rec.confidence > 0.9);
    }
}