ruvector_sparse_inference/pi/
drift.rs1use crate::precision::PrecisionLane;
21use std::f32::consts::PI;
22
23const DRIFT_RATE_3BIT: f32 = 0.15; const DRIFT_RATE_5BIT: f32 = 0.05; const DRIFT_RATE_7BIT: f32 = 0.01; const DRIFT_RATE_FLOAT: f32 = 0.0001; #[derive(Debug, Clone)]
31pub struct DriftDetector {
32 lane: PrecisionLane,
34 accumulated_error: f32,
36 sample_count: usize,
38 error_history: Vec<f32>,
40 history_idx: usize,
42 expected_drift_rate: f32,
44 pi_reference: f32,
46 escalation_threshold: f32,
48}
49
50impl DriftDetector {
51 pub fn new(lane: PrecisionLane) -> Self {
53 let expected_drift_rate = match lane {
54 PrecisionLane::Bit3 => DRIFT_RATE_3BIT,
55 PrecisionLane::Bit5 => DRIFT_RATE_5BIT,
56 PrecisionLane::Bit7 => DRIFT_RATE_7BIT,
57 PrecisionLane::Float32 => DRIFT_RATE_FLOAT,
58 };
59
60 Self {
61 lane,
62 accumulated_error: 0.0,
63 sample_count: 0,
64 error_history: vec![0.0; 64], history_idx: 0,
66 expected_drift_rate,
67 pi_reference: PI,
68 escalation_threshold: expected_drift_rate * 3.0, }
70 }
71
72 pub fn check(&mut self, original: &[f32], quantized: &[f32]) -> QuantizationHonesty {
74 assert_eq!(original.len(), quantized.len());
75
76 let pi_original: Vec<f32> = original.iter()
78 .map(|&x| self.pi_transform(x))
79 .collect();
80 let pi_quantized: Vec<f32> = quantized.iter()
81 .map(|&x| self.pi_transform(x))
82 .collect();
83
84 let error = self.compute_error(&pi_original, &pi_quantized);
86 self.update(error);
87
88 let ratio = error / self.expected_drift_rate.max(0.0001);
90 let is_honest = ratio < 2.0;
91 let should_escalate = ratio > 3.0;
92
93 QuantizationHonesty {
94 error,
95 expected_error: self.expected_drift_rate,
96 ratio,
97 is_honest,
98 should_escalate,
99 sample_count: self.sample_count,
100 }
101 }
102
103 fn pi_transform(&self, value: f32) -> f32 {
105 let angle = value * self.pi_reference;
107 angle.sin() + angle.cos() * 0.5
108 }
109
110 fn inverse_pi_transform(&self, transformed: f32) -> f32 {
112 let angle = transformed.atan2(1.0);
114 angle / self.pi_reference
115 }
116
117 fn compute_error(&self, a: &[f32], b: &[f32]) -> f32 {
119 if a.is_empty() {
120 return 0.0;
121 }
122
123 let mse: f32 = a.iter()
124 .zip(b.iter())
125 .map(|(&x, &y)| (x - y).powi(2))
126 .sum::<f32>() / a.len() as f32;
127
128 mse.sqrt()
129 }
130
131 pub fn update(&mut self, error: f32) {
133 self.accumulated_error += error;
134 self.sample_count += 1;
135
136 self.error_history[self.history_idx] = error;
138 self.history_idx = (self.history_idx + 1) % self.error_history.len();
139 }
140
141 pub fn report(&self) -> DriftReport {
143 let mean_error = if self.sample_count > 0 {
144 self.accumulated_error / self.sample_count as f32
145 } else {
146 0.0
147 };
148
149 let trend = self.compute_trend();
151
152 let is_accelerating = trend > self.expected_drift_rate * 0.1;
154
155 DriftReport {
156 mean_error,
157 accumulated_error: self.accumulated_error,
158 sample_count: self.sample_count,
159 trend,
160 is_accelerating,
161 should_escalate: mean_error > self.escalation_threshold,
162 lane: self.lane,
163 }
164 }
165
166 fn compute_trend(&self) -> f32 {
168 if self.sample_count < 2 {
169 return 0.0;
170 }
171
172 let n = self.error_history.len().min(self.sample_count);
173 if n < 2 {
174 return 0.0;
175 }
176
177 let mut sum_x = 0.0f32;
179 let mut sum_y = 0.0f32;
180 let mut sum_xy = 0.0f32;
181 let mut sum_xx = 0.0f32;
182
183 for i in 0..n {
184 let x = i as f32;
185 let y = self.error_history[i];
186 sum_x += x;
187 sum_y += y;
188 sum_xy += x * y;
189 sum_xx += x * x;
190 }
191
192 let n_f = n as f32;
193 let denominator = n_f * sum_xx - sum_x * sum_x;
194 if denominator.abs() < 1e-10 {
195 return 0.0;
196 }
197
198 (n_f * sum_xy - sum_x * sum_y) / denominator
199 }
200
201 pub fn reset(&mut self) {
203 self.accumulated_error = 0.0;
204 self.sample_count = 0;
205 self.error_history.fill(0.0);
206 self.history_idx = 0;
207 }
208
209 pub fn pi_checksum(&self, signal: &[f32]) -> f32 {
211 if signal.is_empty() {
212 return 0.0;
213 }
214
215 let mut checksum = 0.0f32;
217 for (i, &val) in signal.iter().enumerate() {
218 let pi_phase = (i as f32 + 1.0) * PI / signal.len() as f32;
219 checksum += val * pi_phase.sin();
220 }
221
222 checksum / signal.len() as f32
223 }
224
225 pub fn verify_checksum(&self, original: &[f32], quantized: &[f32]) -> bool {
227 let orig_checksum = self.pi_checksum(original);
228 let quant_checksum = self.pi_checksum(quantized);
229
230 let error = (orig_checksum - quant_checksum).abs();
231 error < self.expected_drift_rate
232 }
233}
234
235#[derive(Debug, Clone, Copy)]
237pub struct QuantizationHonesty {
238 pub error: f32,
240 pub expected_error: f32,
242 pub ratio: f32,
244 pub is_honest: bool,
246 pub should_escalate: bool,
248 pub sample_count: usize,
250}
251
252#[derive(Debug, Clone)]
254pub struct DriftReport {
255 pub mean_error: f32,
257 pub accumulated_error: f32,
259 pub sample_count: usize,
261 pub trend: f32,
263 pub is_accelerating: bool,
265 pub should_escalate: bool,
267 pub lane: PrecisionLane,
269}
270
271impl DriftReport {
272 pub fn severity(&self) -> u8 {
274 if self.should_escalate {
275 3
276 } else if self.is_accelerating {
277 2
278 } else if self.mean_error > 0.05 {
279 1
280 } else {
281 0
282 }
283 }
284
285 pub fn suggested_lane(&self) -> Option<PrecisionLane> {
287 if self.should_escalate {
288 match self.lane {
289 PrecisionLane::Bit3 => Some(PrecisionLane::Bit5),
290 PrecisionLane::Bit5 => Some(PrecisionLane::Bit7),
291 PrecisionLane::Bit7 => Some(PrecisionLane::Float32),
292 PrecisionLane::Float32 => None,
293 }
294 } else {
295 None
296 }
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_drift_detector_creation() {
306 let detector = DriftDetector::new(PrecisionLane::Bit5);
307 assert_eq!(detector.sample_count, 0);
308 }
309
310 #[test]
311 fn test_pi_transform_deterministic() {
312 let detector = DriftDetector::new(PrecisionLane::Bit5);
313 let v1 = detector.pi_transform(0.5);
314 let v2 = detector.pi_transform(0.5);
315 assert_eq!(v1, v2);
316 }
317
318 #[test]
319 fn test_honesty_check_identical() {
320 let mut detector = DriftDetector::new(PrecisionLane::Bit7);
321 let values = vec![0.1, 0.2, 0.3, 0.4, 0.5];
322 let honesty = detector.check(&values, &values);
323 assert!(honesty.error < 0.001);
324 assert!(honesty.is_honest);
325 }
326
327 #[test]
328 fn test_honesty_check_with_error() {
329 let mut detector = DriftDetector::new(PrecisionLane::Bit3);
330 let original = vec![0.1, 0.2, 0.3, 0.4, 0.5];
331 let quantized = vec![0.15, 0.25, 0.35, 0.45, 0.55]; let honesty = detector.check(&original, &quantized);
333 assert!(honesty.error > 0.0);
334 }
335
336 #[test]
337 fn test_drift_report() {
338 let mut detector = DriftDetector::new(PrecisionLane::Bit5);
339 detector.update(0.01);
340 detector.update(0.02);
341 detector.update(0.03);
342
343 let report = detector.report();
344 assert_eq!(report.sample_count, 3);
345 assert!(report.mean_error > 0.0);
346 }
347
348 #[test]
349 fn test_pi_checksum() {
350 let detector = DriftDetector::new(PrecisionLane::Bit5);
351 let signal = vec![1.0, 2.0, 3.0, 4.0, 5.0];
352 let checksum = detector.pi_checksum(&signal);
353 assert!(checksum.is_finite());
354
355 assert_eq!(detector.pi_checksum(&signal), checksum);
357 }
358
359 #[test]
360 fn test_verify_checksum() {
361 let detector = DriftDetector::new(PrecisionLane::Bit7);
362 let original = vec![1.0, 2.0, 3.0, 4.0, 5.0];
363 let nearly_same = vec![1.001, 2.001, 3.001, 4.001, 5.001];
364 assert!(detector.verify_checksum(&original, &nearly_same));
365 }
366
367 #[test]
368 fn test_severity_levels() {
369 let report = DriftReport {
370 mean_error: 0.5,
371 accumulated_error: 1.0,
372 sample_count: 2,
373 trend: 0.1,
374 is_accelerating: true,
375 should_escalate: true,
376 lane: PrecisionLane::Bit3,
377 };
378 assert_eq!(report.severity(), 3);
379 assert_eq!(report.suggested_lane(), Some(PrecisionLane::Bit5));
380 }
381}