1use crate::error::{QuantRS2Error, QuantRS2Result};
8use scirs2_core::Complex64;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum PrecisionLevel {
14 Single,
16 Double,
18 Extended,
20 Mixed,
22}
23
24impl PrecisionLevel {
25 pub fn decimal_digits(self) -> usize {
27 match self {
28 PrecisionLevel::Single => 7,
29 PrecisionLevel::Double => 15,
30 PrecisionLevel::Extended => 34,
31 PrecisionLevel::Mixed => 15, }
33 }
34
35 pub fn epsilon(self) -> f64 {
37 match self {
38 PrecisionLevel::Single => f32::EPSILON as f64,
39 PrecisionLevel::Double => f64::EPSILON,
40 PrecisionLevel::Extended => 1e-34,
41 PrecisionLevel::Mixed => f64::EPSILON,
42 }
43 }
44
45 pub fn performance_factor(self) -> f64 {
47 match self {
48 PrecisionLevel::Single => 1.8, PrecisionLevel::Double => 1.0, PrecisionLevel::Extended => 0.3, PrecisionLevel::Mixed => 1.2, }
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct PrecisionRequirements {
59 pub min_precision: PrecisionLevel,
60 pub error_tolerance: f64,
61 pub relative_importance: f64, }
63
64impl Default for PrecisionRequirements {
65 fn default() -> Self {
66 Self {
67 min_precision: PrecisionLevel::Double,
68 error_tolerance: 1e-12,
69 relative_importance: 0.5,
70 }
71 }
72}
73
74pub struct AdaptivePrecisionManager {
76 current_precision: PrecisionLevel,
78 operation_requirements: HashMap<String, PrecisionRequirements>,
80 error_estimates: HashMap<String, f64>,
82 statistics: PrecisionStatistics,
84 global_error_budget: f64,
86}
87
88#[derive(Debug, Clone, Default)]
90pub struct PrecisionStatistics {
91 pub total_operations: u64,
92 pub precision_downgrades: u64,
93 pub precision_upgrades: u64,
94 pub mixed_precision_ops: u64,
95 pub error_budget_used: f64,
96 pub average_speedup: f64,
97 pub memory_savings: f64,
98}
99
100impl AdaptivePrecisionManager {
101 pub fn new(global_error_budget: f64) -> Self {
103 let mut manager = Self {
104 current_precision: PrecisionLevel::Double,
105 operation_requirements: HashMap::new(),
106 error_estimates: HashMap::new(),
107 statistics: PrecisionStatistics::default(),
108 global_error_budget,
109 };
110
111 manager.initialize_default_requirements();
113 manager
114 }
115
116 fn initialize_default_requirements(&mut self) {
118 let operations = vec![
119 (
121 "eigenvalue_decomposition",
122 PrecisionRequirements {
123 min_precision: PrecisionLevel::Double,
124 error_tolerance: 1e-14,
125 relative_importance: 0.9,
126 },
127 ),
128 (
129 "quantum_fourier_transform",
130 PrecisionRequirements {
131 min_precision: PrecisionLevel::Double,
132 error_tolerance: 1e-13,
133 relative_importance: 0.8,
134 },
135 ),
136 (
137 "phase_estimation",
138 PrecisionRequirements {
139 min_precision: PrecisionLevel::Double,
140 error_tolerance: 1e-12,
141 relative_importance: 0.9,
142 },
143 ),
144 (
146 "gate_application",
147 PrecisionRequirements {
148 min_precision: PrecisionLevel::Single,
149 error_tolerance: 1e-10,
150 relative_importance: 0.6,
151 },
152 ),
153 (
154 "state_vector_norm",
155 PrecisionRequirements {
156 min_precision: PrecisionLevel::Single,
157 error_tolerance: 1e-8,
158 relative_importance: 0.4,
159 },
160 ),
161 (
162 "expectation_value",
163 PrecisionRequirements {
164 min_precision: PrecisionLevel::Double,
165 error_tolerance: 1e-11,
166 relative_importance: 0.7,
167 },
168 ),
169 (
171 "probability_calculation",
172 PrecisionRequirements {
173 min_precision: PrecisionLevel::Single,
174 error_tolerance: 1e-6,
175 relative_importance: 0.3,
176 },
177 ),
178 (
179 "measurement_sampling",
180 PrecisionRequirements {
181 min_precision: PrecisionLevel::Single,
182 error_tolerance: 1e-5,
183 relative_importance: 0.2,
184 },
185 ),
186 ];
187
188 for (op_name, requirements) in operations {
189 self.operation_requirements
190 .insert(op_name.to_string(), requirements);
191 }
192 }
193
194 pub fn determine_optimal_precision(
196 &mut self,
197 operation_name: &str,
198 input_size: usize,
199 target_accuracy: Option<f64>,
200 ) -> PrecisionLevel {
201 self.statistics.total_operations += 1;
202
203 let requirements = self
205 .operation_requirements
206 .get(operation_name)
207 .cloned()
208 .unwrap_or_default();
209
210 let effective_tolerance = if let Some(target) = target_accuracy {
212 target.min(requirements.error_tolerance)
213 } else {
214 requirements.error_tolerance
215 };
216
217 let remaining_budget = self.global_error_budget - self.statistics.error_budget_used;
219
220 let size_factor = self.calculate_size_factor(input_size);
222
223 let optimal_precision = self.select_precision_level(
225 &requirements,
226 effective_tolerance,
227 remaining_budget,
228 size_factor,
229 );
230
231 self.update_precision_statistics(optimal_precision, &requirements);
233
234 let estimated_error =
236 self.estimate_operation_error(operation_name, optimal_precision, input_size);
237 self.error_estimates
238 .insert(operation_name.to_string(), estimated_error);
239 self.statistics.error_budget_used += estimated_error;
240
241 optimal_precision
242 }
243
244 fn calculate_size_factor(&self, input_size: usize) -> f64 {
246 match input_size {
248 0..=100 => 1.0,
249 101..=1000 => 1.2,
250 1001..=10000 => 1.5,
251 _ => 2.0,
252 }
253 }
254
255 fn select_precision_level(
257 &self,
258 requirements: &PrecisionRequirements,
259 effective_tolerance: f64,
260 remaining_budget: f64,
261 size_factor: f64,
262 ) -> PrecisionLevel {
263 let adjusted_tolerance = effective_tolerance / size_factor;
264
265 if remaining_budget < self.global_error_budget * 0.1 {
267 return match requirements.min_precision {
268 PrecisionLevel::Single => PrecisionLevel::Double,
269 PrecisionLevel::Double => PrecisionLevel::Extended,
270 other => other,
271 };
272 }
273
274 if adjusted_tolerance < 1e-14 {
276 PrecisionLevel::Extended
277 } else if adjusted_tolerance < 1e-10 {
278 PrecisionLevel::Double
279 } else if adjusted_tolerance < 1e-6 {
280 PrecisionLevel::Single
281 } else {
282 PrecisionLevel::Single
283 }
284 }
285
286 fn update_precision_statistics(
288 &mut self,
289 chosen_precision: PrecisionLevel,
290 requirements: &PrecisionRequirements,
291 ) {
292 match chosen_precision.cmp(&requirements.min_precision) {
293 std::cmp::Ordering::Less => {
294 }
296 std::cmp::Ordering::Greater => {
297 self.statistics.precision_upgrades += 1;
298 }
299 std::cmp::Ordering::Equal => {
300 }
302 }
303
304 if chosen_precision == PrecisionLevel::Mixed {
305 self.statistics.mixed_precision_ops += 1;
306 }
307
308 let speedup = chosen_precision.performance_factor();
310 self.statistics.average_speedup = (self.statistics.average_speedup
311 * (self.statistics.total_operations - 1) as f64
312 + speedup)
313 / self.statistics.total_operations as f64;
314
315 let memory_factor = match chosen_precision {
317 PrecisionLevel::Single => 0.5, PrecisionLevel::Double => 1.0, PrecisionLevel::Extended => 2.0, PrecisionLevel::Mixed => 0.75, };
322
323 self.statistics.memory_savings = (self.statistics.memory_savings
324 * (self.statistics.total_operations - 1) as f64
325 + memory_factor)
326 / self.statistics.total_operations as f64;
327 }
328
329 fn estimate_operation_error(
331 &self,
332 operation_name: &str,
333 precision: PrecisionLevel,
334 input_size: usize,
335 ) -> f64 {
336 let base_error = precision.epsilon();
337 let size_scaling = (input_size as f64).log2() * 0.1; let operation_factor = match operation_name {
341 "eigenvalue_decomposition" => 10.0, "quantum_fourier_transform" => 5.0, "matrix_multiplication" => 3.0, "gate_application" => 1.0, "probability_calculation" => 0.5, _ => 2.0, };
348
349 base_error * operation_factor * (1.0 + size_scaling)
350 }
351
352 pub fn get_statistics(&self) -> &PrecisionStatistics {
354 &self.statistics
355 }
356
357 pub fn reset_error_budget(&mut self) {
359 self.statistics.error_budget_used = 0.0;
360 self.error_estimates.clear();
361 }
362
363 pub fn within_error_budget(&self) -> bool {
365 self.statistics.error_budget_used < self.global_error_budget
366 }
367
368 pub fn error_budget_utilization(&self) -> f64 {
370 self.statistics.error_budget_used / self.global_error_budget
371 }
372
373 pub fn set_operation_requirements(
375 &mut self,
376 operation_name: String,
377 requirements: PrecisionRequirements,
378 ) {
379 self.operation_requirements
380 .insert(operation_name, requirements);
381 }
382
383 pub fn suggest_circuit_precision(
385 &self,
386 circuit_operations: &[(String, usize)],
387 target_fidelity: f64,
388 ) -> PrecisionLevel {
389 let error_tolerance = 1.0 - target_fidelity;
390 let total_operations = circuit_operations.len();
391
392 let per_operation_budget = error_tolerance / total_operations as f64;
394
395 let mut max_precision = PrecisionLevel::Single;
397
398 for (op_name, size) in circuit_operations {
399 let requirements = self
400 .operation_requirements
401 .get(op_name)
402 .cloned()
403 .unwrap_or_default();
404
405 if per_operation_budget < requirements.error_tolerance {
406 max_precision = match max_precision {
407 PrecisionLevel::Single => {
408 if requirements.min_precision != PrecisionLevel::Single {
409 requirements.min_precision
410 } else {
411 PrecisionLevel::Double
412 }
413 }
414 PrecisionLevel::Double => {
415 if per_operation_budget < 1e-14 {
416 PrecisionLevel::Extended
417 } else {
418 PrecisionLevel::Double
419 }
420 }
421 PrecisionLevel::Extended => PrecisionLevel::Extended,
422 PrecisionLevel::Mixed => PrecisionLevel::Mixed,
423 };
424 }
425 }
426
427 max_precision
428 }
429}
430
431pub fn adapt_precision_for_circuit(
433 circuit_operations: &[(String, usize)],
434 target_fidelity: f64,
435 error_budget: f64,
436) -> QuantRS2Result<PrecisionLevel> {
437 let manager = AdaptivePrecisionManager::new(error_budget);
438 Ok(manager.suggest_circuit_precision(circuit_operations, target_fidelity))
439}
440
441pub struct PrecisionAwareOps;
443
444impl PrecisionAwareOps {
445 pub fn multiply_complex(a: Complex64, b: Complex64, precision: PrecisionLevel) -> Complex64 {
447 match precision {
448 PrecisionLevel::Single => {
449 let a32 = Complex::<f32>::new(a.re as f32, a.im as f32);
450 let b32 = Complex::<f32>::new(b.re as f32, b.im as f32);
451 let result32 = a32 * b32;
452 Complex64::new(result32.re as f64, result32.im as f64)
453 }
454 PrecisionLevel::Double | PrecisionLevel::Mixed => a * b,
455 PrecisionLevel::Extended => {
456 a * b
459 }
460 }
461 }
462
463 pub fn add_complex(a: Complex64, b: Complex64, precision: PrecisionLevel) -> Complex64 {
465 match precision {
466 PrecisionLevel::Single => {
467 let a32 = Complex::<f32>::new(a.re as f32, a.im as f32);
468 let b32 = Complex::<f32>::new(b.re as f32, b.im as f32);
469 let result32 = a32 + b32;
470 Complex64::new(result32.re as f64, result32.im as f64)
471 }
472 PrecisionLevel::Double | PrecisionLevel::Mixed => a + b,
473 PrecisionLevel::Extended => a + b,
474 }
475 }
476
477 pub fn norm_complex(a: Complex64, precision: PrecisionLevel) -> f64 {
479 match precision {
480 PrecisionLevel::Single => {
481 let a32 = Complex::<f32>::new(a.re as f32, a.im as f32);
482 a32.norm() as f64
483 }
484 PrecisionLevel::Double | PrecisionLevel::Mixed | PrecisionLevel::Extended => a.norm(),
485 }
486 }
487}
488
489use scirs2_core::Complex;
491
492#[cfg(test)]
493mod tests {
494 use super::*;
495
496 #[test]
497 fn test_precision_level_properties() {
498 assert_eq!(PrecisionLevel::Single.decimal_digits(), 7);
499 assert_eq!(PrecisionLevel::Double.decimal_digits(), 15);
500 assert!(
501 PrecisionLevel::Single.performance_factor()
502 > PrecisionLevel::Double.performance_factor()
503 );
504 }
505
506 #[test]
507 fn test_adaptive_precision_manager() {
508 let mut manager = AdaptivePrecisionManager::new(1e-10);
509
510 let precision = manager.determine_optimal_precision("gate_application", 100, Some(1e-8));
512 assert!(precision == PrecisionLevel::Single || precision == PrecisionLevel::Double);
513
514 let high_precision =
516 manager.determine_optimal_precision("eigenvalue_decomposition", 1000, Some(1e-14));
517 assert!(
518 high_precision == PrecisionLevel::Double || high_precision == PrecisionLevel::Extended
519 );
520 }
521
522 #[test]
523 fn test_error_budget_management() {
524 let mut manager = AdaptivePrecisionManager::new(1e-6);
525
526 manager.determine_optimal_precision("gate_application", 100, None);
528 manager.determine_optimal_precision("measurement_sampling", 50, None);
529
530 assert!(manager.error_budget_utilization() > 0.0);
531 assert!(manager.error_budget_utilization() < 1.0);
532 }
533
534 #[test]
535 fn test_circuit_precision_suggestion() {
536 let manager = AdaptivePrecisionManager::new(1e-10);
537
538 let operations = vec![
539 ("gate_application".to_string(), 100),
540 ("measurement_sampling".to_string(), 50),
541 ("expectation_value".to_string(), 200),
542 ];
543
544 let suggested = manager.suggest_circuit_precision(&operations, 0.999);
545 assert!(matches!(
546 suggested,
547 PrecisionLevel::Single | PrecisionLevel::Double
548 ));
549
550 let high_fidelity = manager.suggest_circuit_precision(&operations, 0.9999999);
552 assert!(matches!(
553 high_fidelity,
554 PrecisionLevel::Double | PrecisionLevel::Extended
555 ));
556 }
557
558 #[test]
559 fn test_precision_aware_operations() {
560 let a = Complex64::new(1.1234567890123456, 2.9876543210987654);
561 let b = Complex64::new(std::f64::consts::PI, std::f64::consts::SQRT_2);
562
563 let single_result = PrecisionAwareOps::multiply_complex(a, b, PrecisionLevel::Single);
564 let double_result = PrecisionAwareOps::multiply_complex(a, b, PrecisionLevel::Double);
565
566 assert!((single_result - double_result).norm() > 0.0);
568
569 assert!((single_result - double_result).norm() < 1e-6);
571 }
572
573 #[test]
574 fn test_operation_requirements() {
575 let mut manager = AdaptivePrecisionManager::new(1e-10);
576
577 let custom_req = PrecisionRequirements {
578 min_precision: PrecisionLevel::Extended,
579 error_tolerance: 1e-16,
580 relative_importance: 1.0,
581 };
582
583 manager.set_operation_requirements("custom_operation".to_string(), custom_req);
584
585 let precision = manager.determine_optimal_precision("custom_operation", 1000, None);
586 assert_eq!(precision, PrecisionLevel::Extended);
587 }
588
589 #[test]
590 fn test_size_factor_scaling() {
591 let manager = AdaptivePrecisionManager::new(1e-10);
592
593 let small_factor = manager.calculate_size_factor(50);
594 let large_factor = manager.calculate_size_factor(50000);
595
596 assert!(large_factor > small_factor);
597 assert_eq!(small_factor, 1.0); }
599}