Skip to main content

ringkernel_procint/analytics/
pattern_analysis.rs

1//! Pattern analysis and aggregation.
2//!
3//! Aggregates detected patterns and provides trend analysis.
4
5use crate::models::{GpuPatternMatch, PatternSeverity, PatternType};
6use std::collections::HashMap;
7
8/// Pattern aggregator for tracking pattern statistics.
9#[derive(Debug, Default)]
10pub struct PatternAggregator {
11    /// Pattern counts by type.
12    pub type_counts: HashMap<PatternType, u64>,
13    /// Pattern counts by severity.
14    pub severity_counts: HashMap<PatternSeverity, u64>,
15    /// Recent patterns (for display).
16    pub recent_patterns: Vec<PatternSummary>,
17    /// Maximum recent patterns to keep.
18    max_recent: usize,
19    /// Total patterns detected.
20    pub total_detected: u64,
21}
22
23impl PatternAggregator {
24    /// Create a new pattern aggregator.
25    pub fn new() -> Self {
26        Self {
27            max_recent: 100,
28            ..Default::default()
29        }
30    }
31
32    /// Set maximum recent patterns.
33    pub fn with_max_recent(mut self, max: usize) -> Self {
34        self.max_recent = max;
35        self
36    }
37
38    /// Add patterns from detection result.
39    pub fn add_patterns(&mut self, patterns: &[GpuPatternMatch]) {
40        for pattern in patterns {
41            let pattern_type = pattern.get_pattern_type();
42            let severity = pattern.get_severity();
43
44            *self.type_counts.entry(pattern_type).or_insert(0) += 1;
45            *self.severity_counts.entry(severity).or_insert(0) += 1;
46            self.total_detected += 1;
47
48            // Add to recent
49            let summary = PatternSummary {
50                pattern_type,
51                severity,
52                confidence: pattern.confidence,
53                frequency: pattern.frequency,
54                activities: pattern.activities().to_vec(),
55                impact: pattern.impact,
56            };
57            self.recent_patterns.push(summary);
58
59            // Trim if needed
60            if self.recent_patterns.len() > self.max_recent {
61                self.recent_patterns.remove(0);
62            }
63        }
64    }
65
66    /// Get count by pattern type.
67    pub fn count_by_type(&self, pattern_type: PatternType) -> u64 {
68        *self.type_counts.get(&pattern_type).unwrap_or(&0)
69    }
70
71    /// Get count by severity.
72    pub fn count_by_severity(&self, severity: PatternSeverity) -> u64 {
73        *self.severity_counts.get(&severity).unwrap_or(&0)
74    }
75
76    /// Get top patterns by frequency.
77    pub fn top_patterns(&self, n: usize) -> Vec<&PatternSummary> {
78        let mut sorted: Vec<_> = self.recent_patterns.iter().collect();
79        sorted.sort_by_key(|a| std::cmp::Reverse(a.frequency));
80        sorted.truncate(n);
81        sorted
82    }
83
84    /// Get critical patterns.
85    pub fn critical_patterns(&self) -> Vec<&PatternSummary> {
86        self.recent_patterns
87            .iter()
88            .filter(|p| p.severity == PatternSeverity::Critical)
89            .collect()
90    }
91
92    /// Get pattern distribution.
93    pub fn distribution(&self) -> PatternDistribution {
94        PatternDistribution {
95            bottleneck: self.count_by_type(PatternType::Bottleneck),
96            loop_count: self.count_by_type(PatternType::Loop),
97            rework: self.count_by_type(PatternType::Rework),
98            long_running: self.count_by_type(PatternType::LongRunning),
99            circular: self.count_by_type(PatternType::Circular),
100            other: self.total_detected
101                - self.count_by_type(PatternType::Bottleneck)
102                - self.count_by_type(PatternType::Loop)
103                - self.count_by_type(PatternType::Rework)
104                - self.count_by_type(PatternType::LongRunning)
105                - self.count_by_type(PatternType::Circular),
106        }
107    }
108
109    /// Reset aggregator.
110    pub fn reset(&mut self) {
111        self.type_counts.clear();
112        self.severity_counts.clear();
113        self.recent_patterns.clear();
114        self.total_detected = 0;
115    }
116}
117
118/// Summary of a detected pattern.
119#[derive(Debug, Clone)]
120pub struct PatternSummary {
121    /// Pattern type.
122    pub pattern_type: PatternType,
123    /// Severity level.
124    pub severity: PatternSeverity,
125    /// Detection confidence.
126    pub confidence: f32,
127    /// Pattern frequency.
128    pub frequency: u32,
129    /// Involved activities.
130    pub activities: Vec<u32>,
131    /// Impact score.
132    pub impact: f32,
133}
134
135/// Distribution of pattern types.
136#[derive(Debug, Clone, Default)]
137pub struct PatternDistribution {
138    /// Bottleneck pattern count.
139    pub bottleneck: u64,
140    /// Loop pattern count.
141    pub loop_count: u64,
142    /// Rework pattern count.
143    pub rework: u64,
144    /// Long-running pattern count.
145    pub long_running: u64,
146    /// Circular dependency count.
147    pub circular: u64,
148    /// Other patterns count.
149    pub other: u64,
150}
151
152impl PatternDistribution {
153    /// Get total patterns.
154    pub fn total(&self) -> u64 {
155        self.bottleneck
156            + self.loop_count
157            + self.rework
158            + self.long_running
159            + self.circular
160            + self.other
161    }
162
163    /// Get percentages.
164    pub fn percentages(&self) -> [f32; 6] {
165        let total = self.total() as f32;
166        if total == 0.0 {
167            return [0.0; 6];
168        }
169        [
170            self.bottleneck as f32 / total * 100.0,
171            self.loop_count as f32 / total * 100.0,
172            self.rework as f32 / total * 100.0,
173            self.long_running as f32 / total * 100.0,
174            self.circular as f32 / total * 100.0,
175            self.other as f32 / total * 100.0,
176        ]
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    fn create_test_patterns() -> Vec<GpuPatternMatch> {
185        vec![
186            GpuPatternMatch::new(PatternType::Bottleneck, PatternSeverity::Critical)
187                .with_metrics(0.9, 50, 10000.0),
188            GpuPatternMatch::new(PatternType::Loop, PatternSeverity::Warning)
189                .with_metrics(0.7, 30, 5000.0),
190        ]
191    }
192
193    #[test]
194    fn test_pattern_aggregation() {
195        let mut agg = PatternAggregator::new();
196        let patterns = create_test_patterns();
197        agg.add_patterns(&patterns);
198
199        assert_eq!(agg.total_detected, 2);
200        assert_eq!(agg.count_by_type(PatternType::Bottleneck), 1);
201        assert_eq!(agg.count_by_severity(PatternSeverity::Critical), 1);
202    }
203
204    #[test]
205    fn test_distribution() {
206        let mut agg = PatternAggregator::new();
207        let patterns = create_test_patterns();
208        agg.add_patterns(&patterns);
209
210        let dist = agg.distribution();
211        assert_eq!(dist.bottleneck, 1);
212        assert_eq!(dist.loop_count, 1);
213    }
214}