Skip to main content

llm_optimizer_processor/aggregation/
mod.rs

1//! Aggregation module for computing statistics over streaming data
2//!
3//! This module provides a comprehensive set of aggregators for computing various
4//! statistics over streaming data. All aggregators support:
5//!
6//! - **Incremental updates**: Feed values one at a time
7//! - **Batch updates**: Feed multiple values at once
8//! - **Accumulator merging**: Combine results from distributed processing
9//! - **State serialization**: Persist and restore aggregator state
10//!
11//! # Available Aggregators
12//!
13//! - [`CountAggregator`]: Count the number of values
14//! - [`SumAggregator`]: Sum all values
15//! - [`AverageAggregator`]: Compute the mean
16//! - [`MinAggregator`]: Track the minimum value
17//! - [`MaxAggregator`]: Track the maximum value
18//! - [`PercentileAggregator`]: Compute percentiles (p50, p95, p99, etc.)
19//! - [`StandardDeviationAggregator`]: Compute standard deviation
20//! - [`CompositeAggregator`]: Run multiple aggregations in parallel
21//!
22//! # Examples
23//!
24//! ## Basic Usage
25//!
26//! ```rust
27//! use processor::aggregation::{Aggregator, AverageAggregator};
28//!
29//! let mut agg = AverageAggregator::new();
30//! agg.update(10.0).unwrap();
31//! agg.update(20.0).unwrap();
32//! agg.update(30.0).unwrap();
33//!
34//! assert_eq!(agg.finalize().unwrap(), 20.0);
35//! ```
36//!
37//! ## Distributed Processing
38//!
39//! ```rust
40//! use processor::aggregation::{Aggregator, SumAggregator};
41//!
42//! // Process data in parallel
43//! let mut agg1 = SumAggregator::new();
44//! agg1.update_batch(&[1.0, 2.0, 3.0]).unwrap();
45//!
46//! let mut agg2 = SumAggregator::new();
47//! agg2.update_batch(&[4.0, 5.0, 6.0]).unwrap();
48//!
49//! // Merge results
50//! let acc2 = agg2.accumulator();
51//! agg1.merge(acc2).unwrap();
52//!
53//! assert_eq!(agg1.finalize().unwrap(), 21.0);
54//! ```
55//!
56//! ## Composite Aggregation
57//!
58//! ```rust
59//! use processor::aggregation::CompositeAggregator;
60//!
61//! let mut composite = CompositeAggregator::new();
62//! composite
63//!     .add_count("request_count")
64//!     .add_avg("avg_latency")
65//!     .add_p95("p95_latency")
66//!     .add_stddev("stddev_latency");
67//!
68//! // Feed data
69//! for latency in vec![100.0, 150.0, 200.0, 250.0, 300.0] {
70//!     composite.update(latency).unwrap();
71//! }
72//!
73//! // Get all results at once
74//! let results = composite.finalize().unwrap();
75//! println!("Count: {}", results.get_count("request_count").unwrap());
76//! println!("Avg: {:.2}", results.get_value("avg_latency").unwrap());
77//! println!("P95: {:.2}", results.get_value("p95_latency").unwrap());
78//! ```
79
80// Re-name to avoid collision with trait
81mod trait_;
82
83mod count;
84mod sum;
85mod avg;
86mod minmax;
87mod percentile;
88mod stddev;
89mod composite;
90pub mod statistics;
91
92// Re-export the trait
93pub use trait_::{Aggregator, ToF64};
94
95// Re-export all aggregators
96pub use count::{CountAggregator, CountAccumulator};
97pub use sum::{SumAggregator, SumAccumulator};
98pub use avg::{AverageAggregator, AverageAccumulator};
99pub use minmax::{MinAggregator, MaxAggregator, MinAccumulator, MaxAccumulator};
100pub use percentile::{PercentileAggregator, PercentileAccumulator};
101pub use stddev::{StandardDeviationAggregator, StdDevAccumulator};
102pub use composite::{CompositeAggregator, AggregationResult};
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_multiple_aggregators_together() {
110        let data = vec![10.0, 20.0, 30.0, 40.0, 50.0];
111
112        let mut count = CountAggregator::new();
113        let mut sum = SumAggregator::new();
114        let mut avg = AverageAggregator::new();
115        let mut min = MinAggregator::new();
116        let mut max = MaxAggregator::new();
117
118        for value in &data {
119            count.update(*value).unwrap();
120            sum.update(*value).unwrap();
121            avg.update(*value).unwrap();
122            min.update(*value).unwrap();
123            max.update(*value).unwrap();
124        }
125
126        assert_eq!(count.finalize().unwrap(), 5);
127        assert_eq!(sum.finalize().unwrap(), 150.0);
128        assert_eq!(avg.finalize().unwrap(), 30.0);
129        assert_eq!(min.finalize().unwrap(), 10.0);
130        assert_eq!(max.finalize().unwrap(), 50.0);
131    }
132
133    #[test]
134    fn test_serialization_roundtrip() {
135        let mut avg = AverageAggregator::<f64>::new();
136        avg.update_batch(&[10.0, 20.0, 30.0]).unwrap();
137
138        // Serialize accumulator
139        let acc = avg.accumulator();
140        let serialized = serde_json::to_string(&acc).unwrap();
141
142        // Deserialize and merge into new aggregator
143        let deserialized: AverageAccumulator = serde_json::from_str(&serialized).unwrap();
144        let mut new_avg = AverageAggregator::<f64>::new();
145        new_avg.merge(deserialized).unwrap();
146
147        assert_eq!(new_avg.finalize().unwrap(), 20.0);
148    }
149
150    #[test]
151    fn test_distributed_processing_scenario() {
152        // Simulate processing data across 3 workers
153        let worker1_data = vec![1.0, 2.0, 3.0];
154        let worker2_data = vec![4.0, 5.0, 6.0];
155        let worker3_data = vec![7.0, 8.0, 9.0];
156
157        // Each worker computes its own aggregation
158        let mut agg1 = AverageAggregator::new();
159        agg1.update_batch(&worker1_data).unwrap();
160
161        let mut agg2 = AverageAggregator::new();
162        agg2.update_batch(&worker2_data).unwrap();
163
164        let mut agg3 = AverageAggregator::new();
165        agg3.update_batch(&worker3_data).unwrap();
166
167        // Merge all results
168        let mut combined = AverageAggregator::<f64>::new();
169        combined.merge(agg1.accumulator()).unwrap();
170        combined.merge(agg2.accumulator()).unwrap();
171        combined.merge(agg3.accumulator()).unwrap();
172
173        // Result should be average of 1-9
174        assert_eq!(combined.finalize().unwrap(), 5.0);
175        assert_eq!(combined.count(), 9);
176    }
177}