Skip to main content

quantrs2_anneal/advanced_quantum_algorithms/
utils.rs

1//! Utility functions and helper types for advanced quantum algorithms
2
3use scirs2_core::Complex64;
4use std::f64::consts::PI;
5
6/// Helper function for complex phase calculation
7#[must_use]
8pub fn complex_phase(phase: f64) -> Complex64 {
9    Complex64::new(phase.cos(), phase.sin())
10}
11
12/// Type alias kept for any code that imports `super::utils::Complex`
13pub type Complex = Complex64;
14
15/// Validate parameter ranges for quantum algorithms
16#[must_use]
17pub fn validate_parameters(params: &[f64]) -> bool {
18    params.iter().all(|&p| p >= 0.0 && p <= 2.0 * PI)
19}
20
21/// Normalize parameter values to valid range
22pub fn normalize_parameters(params: &mut [f64]) {
23    for param in params.iter_mut() {
24        *param = param.clamp(0.0, 2.0 * PI);
25    }
26}
27
28/// Estimate problem complexity based on size and structure
29#[must_use]
30pub fn estimate_problem_complexity(num_variables: usize, density: f64) -> f64 {
31    let size_factor = (num_variables as f64).log2();
32    let density_factor = density.clamp(0.1, 1.0);
33    size_factor * density_factor
34}
35
36/// Calculate relative improvement between values
37#[must_use]
38pub fn calculate_relative_improvement(old_value: f64, new_value: f64) -> f64 {
39    if old_value.abs() < 1e-8 {
40        if new_value.abs() < 1e-8 {
41            0.0
42        } else {
43            f64::INFINITY
44        }
45    } else {
46        (old_value - new_value) / old_value.abs()
47    }
48}
49
50/// Linear interpolation between two values
51#[must_use]
52pub fn linear_interpolate(start: f64, end: f64, fraction: f64) -> f64 {
53    fraction.mul_add(end - start, start)
54}
55
56/// Compute running average with decay factor
57#[must_use]
58pub fn running_average(current_avg: f64, new_value: f64, decay_factor: f64) -> f64 {
59    decay_factor.mul_add(current_avg, (1.0 - decay_factor) * new_value)
60}
61
62/// Check if a value has converged within tolerance
63#[must_use]
64pub fn has_converged(current: f64, previous: f64, tolerance: f64) -> bool {
65    (current - previous).abs() < tolerance
66}
67
68/// Compute exponential moving average
69#[must_use]
70pub fn exponential_moving_average(values: &[f64], alpha: f64) -> Vec<f64> {
71    if values.is_empty() {
72        return Vec::new();
73    }
74
75    let mut ema = Vec::with_capacity(values.len());
76    ema.push(values[0]);
77
78    for i in 1..values.len() {
79        let new_ema = alpha.mul_add(values[i], (1.0 - alpha) * ema[i - 1]);
80        ema.push(new_ema);
81    }
82
83    ema
84}
85
86/// Generate Fibonacci sequence for adaptive depth selection
87#[must_use]
88pub fn fibonacci_sequence(n: usize) -> Vec<usize> {
89    if n == 0 {
90        return Vec::new();
91    }
92    if n == 1 {
93        return vec![1];
94    }
95
96    let mut fib = vec![1, 1];
97    for i in 2..n {
98        let next = fib[i - 1] + fib[i - 2];
99        fib.push(next);
100    }
101    fib
102}
103
104/// Calculate golden ratio increment
105#[must_use]
106pub fn golden_ratio_increment(current: usize) -> usize {
107    ((current as f64) * 1.618) as usize
108}
109
110/// Compute autocorrelation at lag
111#[must_use]
112pub fn autocorrelation(data: &[f64], lag: usize) -> f64 {
113    if data.len() <= lag {
114        return 0.0;
115    }
116
117    let n = data.len() - lag;
118    let mean = data.iter().sum::<f64>() / data.len() as f64;
119
120    let mut numerator = 0.0;
121    let mut denominator = 0.0;
122
123    for i in 0..n {
124        let x_i = data[i] - mean;
125        let x_lag = data[i + lag] - mean;
126        numerator += x_i * x_lag;
127        denominator += x_i * x_i;
128    }
129
130    if denominator > 1e-8 {
131        numerator / denominator
132    } else {
133        0.0
134    }
135}
136
137/// Compute moving window statistics
138#[derive(Debug, Clone)]
139pub struct WindowStats {
140    pub mean: f64,
141    pub std: f64,
142    pub min: f64,
143    pub max: f64,
144}
145
146impl WindowStats {
147    #[must_use]
148    pub fn new(data: &[f64]) -> Self {
149        if data.is_empty() {
150            return Self {
151                mean: 0.0,
152                std: 0.0,
153                min: 0.0,
154                max: 0.0,
155            };
156        }
157
158        let mean = data.iter().sum::<f64>() / data.len() as f64;
159        let variance = data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / data.len() as f64;
160        let std = variance.sqrt();
161        let min = data.iter().fold(f64::INFINITY, |a, &b| a.min(b));
162        let max = data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
163
164        Self {
165            mean,
166            std,
167            min,
168            max,
169        }
170    }
171}
172
173/// Performance tracking utilities
174#[derive(Debug, Clone)]
175pub struct PerformanceTracker {
176    values: Vec<f64>,
177    max_size: usize,
178}
179
180impl PerformanceTracker {
181    #[must_use]
182    pub const fn new(max_size: usize) -> Self {
183        Self {
184            values: Vec::new(),
185            max_size,
186        }
187    }
188
189    pub fn add_value(&mut self, value: f64) {
190        self.values.push(value);
191        if self.values.len() > self.max_size {
192            self.values.remove(0);
193        }
194    }
195
196    #[must_use]
197    pub fn recent_improvement(&self, window_size: usize) -> f64 {
198        if self.values.len() < window_size {
199            return 0.0;
200        }
201
202        let recent_start = self.values.len() - window_size;
203        let recent_values = &self.values[recent_start..];
204
205        if recent_values.len() < 2 {
206            return 0.0;
207        }
208
209        let initial = recent_values[0];
210        let final_val = recent_values[recent_values.len() - 1];
211
212        calculate_relative_improvement(initial, final_val)
213    }
214
215    #[must_use]
216    pub fn is_stagnating(&self, threshold: f64, window_size: usize) -> bool {
217        let improvement = self.recent_improvement(window_size);
218        improvement.abs() < threshold
219    }
220
221    #[must_use]
222    pub fn get_trend(&self, window_size: usize) -> f64 {
223        if self.values.len() < window_size {
224            return 0.0;
225        }
226
227        let recent_start = self.values.len() - window_size;
228        let recent_values = &self.values[recent_start..];
229
230        // Simple linear trend calculation
231        let n = recent_values.len() as f64;
232        let sum_x = (0..recent_values.len()).sum::<usize>() as f64;
233        let sum_y = recent_values.iter().sum::<f64>();
234        let sum_xy = recent_values
235            .iter()
236            .enumerate()
237            .map(|(i, &y)| i as f64 * y)
238            .sum::<f64>();
239        let sum_x2 = (0..recent_values.len())
240            .map(|i| (i as f64).powi(2))
241            .sum::<f64>();
242
243        let denominator = sum_x.mul_add(-sum_x, n * sum_x2);
244        if denominator.abs() < 1e-8 {
245            0.0
246        } else {
247            n.mul_add(sum_xy, -(sum_x * sum_y)) / denominator
248        }
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn test_complex_phase() {
258        let phase = PI / 4.0;
259        let complex_val = complex_phase(phase);
260
261        assert!((complex_val.re - (PI / 4.0).cos()).abs() < 1e-10);
262        assert!((complex_val.im - (PI / 4.0).sin()).abs() < 1e-10);
263    }
264
265    #[test]
266    fn test_parameter_validation() {
267        let valid_params = vec![0.0, PI, 2.0 * PI];
268        let invalid_params = vec![-1.0, 3.0 * PI];
269
270        assert!(validate_parameters(&valid_params));
271        assert!(!validate_parameters(&invalid_params));
272    }
273
274    #[test]
275    fn test_parameter_normalization() {
276        let mut params = vec![-1.0, PI, 3.0 * PI];
277        normalize_parameters(&mut params);
278
279        assert!(validate_parameters(&params));
280        assert_eq!(params[0], 0.0);
281        assert_eq!(params[1], PI);
282        assert_eq!(params[2], 2.0 * PI);
283    }
284
285    #[test]
286    fn test_relative_improvement() {
287        assert_eq!(calculate_relative_improvement(10.0, 8.0), 0.2);
288        assert_eq!(calculate_relative_improvement(8.0, 10.0), -0.25);
289        assert_eq!(calculate_relative_improvement(0.0, 0.0), 0.0);
290    }
291
292    #[test]
293    fn test_fibonacci_sequence() {
294        let fib = fibonacci_sequence(5);
295        assert_eq!(fib, vec![1, 1, 2, 3, 5]);
296    }
297
298    #[test]
299    fn test_exponential_moving_average() {
300        let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
301        let ema = exponential_moving_average(&values, 0.5);
302
303        assert_eq!(ema.len(), values.len());
304        assert_eq!(ema[0], 1.0);
305        // Each subsequent value should be a weighted average
306        assert!(ema[1] > 1.0 && ema[1] < 2.0);
307    }
308
309    #[test]
310    fn test_window_stats() {
311        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
312        let stats = WindowStats::new(&data);
313
314        assert_eq!(stats.mean, 3.0);
315        assert_eq!(stats.min, 1.0);
316        assert_eq!(stats.max, 5.0);
317        assert!(stats.std > 0.0);
318    }
319
320    #[test]
321    fn test_performance_tracker() {
322        let mut tracker = PerformanceTracker::new(5);
323
324        tracker.add_value(10.0);
325        tracker.add_value(8.0);
326        tracker.add_value(6.0);
327
328        let improvement = tracker.recent_improvement(3);
329        assert!(improvement > 0.0); // Decreasing values = improvement
330
331        let trend = tracker.get_trend(3);
332        assert!(trend < 0.0); // Negative trend = decreasing values
333    }
334}