1use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::GateOp,
10};
11use scirs2_core::ndarray::{Array1, Array2};
12use scirs2_core::Complex64;
13use std::{
14 collections::HashMap,
15 sync::{Arc, RwLock},
16 time::{Duration, Instant},
17};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum PrecisionMode {
22 Single,
24 Double,
26 Extended,
28 Arbitrary(u32), Adaptive,
32}
33
34impl Default for PrecisionMode {
35 fn default() -> Self {
36 Self::Double
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct AdaptivePrecisionConfig {
43 pub initial_precision: PrecisionMode,
45 pub target_accuracy: f64,
47 pub max_error_threshold: f64,
49 pub min_precision: PrecisionMode,
51 pub max_precision: PrecisionMode,
53 pub error_estimation_samples: usize,
55 pub adaptation_interval: usize,
57 pub enable_auto_adjustment: bool,
59 pub performance_weight: f64,
61}
62
63impl Default for AdaptivePrecisionConfig {
64 fn default() -> Self {
65 Self {
66 initial_precision: PrecisionMode::Double,
67 target_accuracy: 1e-12,
68 max_error_threshold: 1e-10,
69 min_precision: PrecisionMode::Single,
70 max_precision: PrecisionMode::Arbitrary(256),
71 error_estimation_samples: 100,
72 adaptation_interval: 1000,
73 enable_auto_adjustment: true,
74 performance_weight: 0.3,
75 }
76 }
77}
78
79#[derive(Debug)]
81pub struct AdaptivePrecisionSimulator {
82 config: AdaptivePrecisionConfig,
83 current_precision: PrecisionMode,
84 error_monitor: Arc<RwLock<PrecisionErrorMonitor>>,
85 performance_monitor: Arc<RwLock<PrecisionPerformanceMonitor>>,
86 operation_count: usize,
87 last_adaptation: Instant,
88}
89
90#[derive(Debug)]
92pub struct PrecisionErrorMonitor {
93 error_history: Vec<f64>,
95 error_estimators: Vec<Box<dyn ErrorEstimator>>,
97 current_error: f64,
99 error_trend: ErrorTrend,
101}
102
103#[derive(Debug)]
105pub struct PrecisionPerformanceMonitor {
106 timing_by_precision: HashMap<PrecisionMode, Vec<f64>>,
108 memory_by_precision: HashMap<PrecisionMode, Vec<usize>>,
110 current_performance: PerformanceMetrics,
112}
113
114#[derive(Debug, Clone)]
115pub struct PerformanceMetrics {
116 pub operations_per_second: f64,
117 pub memory_usage_bytes: usize,
118 pub error_rate: f64,
119 pub adaptation_overhead: f64,
120}
121
122#[derive(Debug, Clone, Copy)]
123pub enum ErrorTrend {
124 Decreasing,
125 Stable,
126 Increasing,
127}
128
129pub trait ErrorEstimator: Send + Sync + std::fmt::Debug {
131 fn estimate_error(&self, result: &AdaptiveResult, reference: Option<&AdaptiveResult>) -> f64;
133
134 fn name(&self) -> &str;
136
137 fn is_applicable(&self, computation_type: ComputationType) -> bool;
139}
140
141#[derive(Debug, Clone)]
143pub struct AdaptiveResult {
144 pub value: Complex64,
146 pub precision: PrecisionMode,
148 pub estimated_error: f64,
150 pub computation_time: Duration,
152 pub memory_used: usize,
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub enum ComputationType {
159 StateEvolution,
160 ExpectationValue,
161 Probability,
162 Measurement,
163 MatrixMultiplication,
164 EigenvalueDecomposition,
165 TensorContraction,
166}
167
168impl AdaptivePrecisionSimulator {
169 pub fn new(config: AdaptivePrecisionConfig) -> Self {
171 let error_monitor = Arc::new(RwLock::new(PrecisionErrorMonitor::new()));
172 let performance_monitor = Arc::new(RwLock::new(PrecisionPerformanceMonitor::new()));
173
174 Self {
175 current_precision: config.initial_precision,
176 config,
177 error_monitor,
178 performance_monitor,
179 operation_count: 0,
180 last_adaptation: Instant::now(),
181 }
182 }
183
184 pub fn execute_adaptive<F, R>(
186 &mut self,
187 computation: F,
188 comp_type: ComputationType,
189 ) -> QuantRS2Result<AdaptiveResult>
190 where
191 F: FnOnce(PrecisionMode) -> QuantRS2Result<R>,
192 R: Into<Complex64>,
193 {
194 let start_time = Instant::now();
195
196 let result = computation(self.current_precision)?;
198 let computation_time = start_time.elapsed();
199
200 let value = result.into();
202
203 let estimated_error = self.estimate_computation_error(&value, comp_type)?;
205
206 let adaptive_result = AdaptiveResult {
208 value,
209 precision: self.current_precision,
210 estimated_error,
211 computation_time,
212 memory_used: self.estimate_memory_usage(comp_type),
213 };
214
215 self.update_monitoring(&adaptive_result, comp_type)?;
217
218 if self.should_adapt()? {
220 self.adapt_precision(comp_type)?;
221 }
222
223 self.operation_count += 1;
224 Ok(adaptive_result)
225 }
226
227 pub fn apply_gate_adaptive(
229 &mut self,
230 gate: &dyn GateOp,
231 _state: &mut Array1<Complex64>,
232 ) -> QuantRS2Result<AdaptiveResult> {
233 let _matrix = gate.matrix()?;
234 self.execute_adaptive(
237 move |precision| {
238 let result = match precision {
240 PrecisionMode::Single => {
241 std::thread::sleep(Duration::from_micros(10));
243 Ok::<f64, QuantRS2Error>(1.0)
244 }
245 PrecisionMode::Double => {
246 std::thread::sleep(Duration::from_micros(20));
248 Ok::<f64, QuantRS2Error>(1.0)
249 }
250 PrecisionMode::Extended => {
251 std::thread::sleep(Duration::from_micros(40));
253 Ok::<f64, QuantRS2Error>(1.0)
254 }
255 PrecisionMode::Arbitrary(bits) => {
256 let delay = (bits as u64 / 32) * 50;
258 std::thread::sleep(Duration::from_micros(delay));
259 Ok::<f64, QuantRS2Error>(1.0)
260 }
261 PrecisionMode::Adaptive => {
262 std::thread::sleep(Duration::from_micros(20));
264 Ok::<f64, QuantRS2Error>(1.0)
265 }
266 };
267 let result = result?;
268
269 Ok(result)
270 },
271 ComputationType::StateEvolution,
272 )
273 }
274
275 pub fn expectation_value_adaptive(
277 &mut self,
278 _observable: &Array2<Complex64>,
279 _state: &Array1<Complex64>,
280 ) -> QuantRS2Result<AdaptiveResult> {
281 self.execute_adaptive(
282 |precision| {
283 let result = match precision {
284 PrecisionMode::Single => {
285 std::thread::sleep(Duration::from_micros(15));
286 Complex64::new(0.5, 0.0)
287 }
288 PrecisionMode::Double | PrecisionMode::Adaptive => {
289 std::thread::sleep(Duration::from_micros(30));
290 Complex64::new(0.5, 0.0)
291 }
292 PrecisionMode::Extended => {
293 std::thread::sleep(Duration::from_micros(60));
294 Complex64::new(0.5, 0.0)
295 }
296 PrecisionMode::Arbitrary(bits) => {
297 let delay = (bits as u64 / 32) * 75;
298 std::thread::sleep(Duration::from_micros(delay));
299 Complex64::new(0.5, 0.0)
300 }
301 };
302
303 Ok(result)
304 },
305 ComputationType::ExpectationValue,
306 )
307 }
308
309 pub const fn current_precision(&self) -> PrecisionMode {
311 self.current_precision
312 }
313
314 pub fn force_adaptation(&mut self, comp_type: ComputationType) -> QuantRS2Result<()> {
316 self.adapt_precision(comp_type)
317 }
318
319 pub fn get_precision_stats(&self) -> PrecisionStatistics {
321 let error_monitor = self
322 .error_monitor
323 .read()
324 .expect("Error monitor lock poisoned");
325 let perf_monitor = self
326 .performance_monitor
327 .read()
328 .expect("Performance monitor lock poisoned");
329
330 PrecisionStatistics {
331 current_precision: self.current_precision,
332 current_error: error_monitor.current_error,
333 error_trend: error_monitor.error_trend,
334 operations_count: self.operation_count,
335 adaptations_count: self.count_adaptations(),
336 performance_metrics: perf_monitor.current_performance.clone(),
337 precision_usage: self.get_precision_usage(),
338 }
339 }
340
341 fn estimate_computation_error(
344 &self,
345 _result: &Complex64,
346 _comp_type: ComputationType,
347 ) -> QuantRS2Result<f64> {
348 let error_monitor = self
349 .error_monitor
350 .read()
351 .map_err(|e| QuantRS2Error::RuntimeError(format!("Lock poisoned: {e}")))?;
352
353 Ok(error_monitor.error_history.last().copied().unwrap_or(1e-15))
355 }
356
357 fn estimate_memory_usage(&self, comp_type: ComputationType) -> usize {
358 let base_memory = match comp_type {
360 ComputationType::StateEvolution => 1024,
361 ComputationType::ExpectationValue => 512,
362 ComputationType::Probability => 256,
363 ComputationType::Measurement => 128,
364 ComputationType::MatrixMultiplication => 2048,
365 ComputationType::EigenvalueDecomposition => 4096,
366 ComputationType::TensorContraction => 8192,
367 };
368
369 let precision_multiplier = match self.current_precision {
370 PrecisionMode::Single => 1.0,
371 PrecisionMode::Double | PrecisionMode::Adaptive => 2.0,
372 PrecisionMode::Extended => 2.5,
373 PrecisionMode::Arbitrary(bits) => (bits as f64) / 32.0,
374 };
375
376 (base_memory as f64 * precision_multiplier) as usize
377 }
378
379 fn update_monitoring(
380 &self,
381 result: &AdaptiveResult,
382 _comp_type: ComputationType,
383 ) -> QuantRS2Result<()> {
384 {
386 let mut error_monitor = self
387 .error_monitor
388 .write()
389 .map_err(|e| QuantRS2Error::RuntimeError(format!("Lock poisoned: {e}")))?;
390 error_monitor.add_error_sample(result.estimated_error);
391 error_monitor.update_error_trend();
392 }
393
394 {
396 let mut perf_monitor = self
397 .performance_monitor
398 .write()
399 .map_err(|e| QuantRS2Error::RuntimeError(format!("Lock poisoned: {e}")))?;
400 perf_monitor.add_timing_sample(
401 result.precision,
402 result.computation_time.as_secs_f64() * 1000.0,
403 );
404 perf_monitor.add_memory_sample(result.precision, result.memory_used);
405 perf_monitor.update_current_performance(result);
406 }
407
408 Ok(())
409 }
410
411 fn should_adapt(&self) -> QuantRS2Result<bool> {
412 if !self.config.enable_auto_adjustment {
413 return Ok(false);
414 }
415
416 if self.operation_count % self.config.adaptation_interval != 0 {
418 return Ok(false);
419 }
420
421 if self.last_adaptation.elapsed() < Duration::from_secs(1) {
423 return Ok(false);
424 }
425
426 let error_monitor = self
428 .error_monitor
429 .read()
430 .map_err(|e| QuantRS2Error::RuntimeError(format!("Lock poisoned: {e}")))?;
431 if error_monitor.current_error > self.config.max_error_threshold {
432 return Ok(true);
433 }
434
435 if error_monitor.current_error < self.config.target_accuracy / 10.0 {
437 return Ok(true);
438 }
439
440 Ok(false)
441 }
442
443 fn adapt_precision(&mut self, comp_type: ComputationType) -> QuantRS2Result<()> {
444 let error_monitor = self
445 .error_monitor
446 .read()
447 .map_err(|e| QuantRS2Error::RuntimeError(format!("Lock poisoned: {e}")))?;
448 let perf_monitor = self
449 .performance_monitor
450 .read()
451 .map_err(|e| QuantRS2Error::RuntimeError(format!("Lock poisoned: {e}")))?;
452
453 let current_error = error_monitor.current_error;
454 let error_trend = error_monitor.error_trend;
455
456 let new_precision = if current_error > self.config.max_error_threshold {
458 Self::increase_precision(self.current_precision)
460 } else if current_error < self.config.target_accuracy / 10.0
461 && matches!(error_trend, ErrorTrend::Stable | ErrorTrend::Decreasing)
462 {
463 Self::decrease_precision(self.current_precision)
465 } else {
466 self.current_precision
468 };
469
470 let final_precision =
472 Self::consider_performance_factors(new_precision, &perf_monitor, comp_type);
473
474 if final_precision != self.current_precision {
475 println!(
476 "Adapting precision from {:?} to {:?} (error: {:.2e})",
477 self.current_precision, final_precision, current_error
478 );
479 self.current_precision = final_precision;
480 self.last_adaptation = Instant::now();
481 }
482
483 Ok(())
484 }
485
486 const fn increase_precision(current: PrecisionMode) -> PrecisionMode {
487 match current {
488 PrecisionMode::Single => PrecisionMode::Double,
489 PrecisionMode::Double => PrecisionMode::Extended,
490 PrecisionMode::Extended => PrecisionMode::Arbitrary(128),
491 PrecisionMode::Arbitrary(bits) if bits < 512 => PrecisionMode::Arbitrary(bits * 2),
492 _ => current, }
494 }
495
496 const fn decrease_precision(current: PrecisionMode) -> PrecisionMode {
497 match current {
498 PrecisionMode::Extended => PrecisionMode::Double,
499 PrecisionMode::Double => PrecisionMode::Single,
500 PrecisionMode::Arbitrary(bits) if bits > 64 => PrecisionMode::Arbitrary(bits / 2),
501 PrecisionMode::Arbitrary(_) => PrecisionMode::Extended,
502 _ => current, }
504 }
505
506 const fn consider_performance_factors(
507 suggested: PrecisionMode,
508 _perf_monitor: &PrecisionPerformanceMonitor,
509 _comp_type: ComputationType,
510 ) -> PrecisionMode {
511 suggested
514 }
515
516 const fn count_adaptations(&self) -> usize {
517 self.operation_count / self.config.adaptation_interval
519 }
520
521 fn get_precision_usage(&self) -> HashMap<PrecisionMode, f64> {
522 let mut usage = HashMap::new();
524 usage.insert(self.current_precision, 1.0);
525 usage
526 }
527
528 fn apply_gate_single_precision(
531 _matrix: &[Complex64],
532 _state: &mut Array1<Complex64>,
533 ) -> QuantRS2Result<f64> {
534 std::thread::sleep(Duration::from_micros(10)); Ok(1.0)
537 }
538
539 fn apply_gate_double_precision(
540 _matrix: &[Complex64],
541 _state: &mut Array1<Complex64>,
542 ) -> QuantRS2Result<f64> {
543 std::thread::sleep(Duration::from_micros(20)); Ok(1.0)
546 }
547
548 fn apply_gate_extended_precision(
549 _matrix: &[Complex64],
550 _state: &mut Array1<Complex64>,
551 ) -> QuantRS2Result<f64> {
552 std::thread::sleep(Duration::from_micros(40)); Ok(1.0)
555 }
556
557 fn apply_gate_arbitrary_precision(
558 _matrix: &[Complex64],
559 _state: &mut Array1<Complex64>,
560 bits: u32,
561 ) -> QuantRS2Result<f64> {
562 let delay = (bits as u64 / 32) * 50; std::thread::sleep(Duration::from_micros(delay));
565 Ok(1.0)
566 }
567
568 fn expectation_value_single_precision(
569 _observable: &Array2<Complex64>,
570 _state: &Array1<Complex64>,
571 ) -> QuantRS2Result<Complex64> {
572 std::thread::sleep(Duration::from_micros(15));
573 Ok(Complex64::new(0.5, 0.0))
574 }
575
576 fn expectation_value_double_precision(
577 _observable: &Array2<Complex64>,
578 _state: &Array1<Complex64>,
579 ) -> QuantRS2Result<Complex64> {
580 std::thread::sleep(Duration::from_micros(30));
581 Ok(Complex64::new(0.5, 0.0))
582 }
583
584 fn expectation_value_extended_precision(
585 _observable: &Array2<Complex64>,
586 _state: &Array1<Complex64>,
587 ) -> QuantRS2Result<Complex64> {
588 std::thread::sleep(Duration::from_micros(60));
589 Ok(Complex64::new(0.5, 0.0))
590 }
591
592 fn expectation_value_arbitrary_precision(
593 _observable: &Array2<Complex64>,
594 _state: &Array1<Complex64>,
595 bits: u32,
596 ) -> QuantRS2Result<Complex64> {
597 let delay = (bits as u64 / 32) * 75;
598 std::thread::sleep(Duration::from_micros(delay));
599 Ok(Complex64::new(0.5, 0.0))
600 }
601}
602
603#[derive(Debug, Clone)]
604pub struct PrecisionStatistics {
605 pub current_precision: PrecisionMode,
606 pub current_error: f64,
607 pub error_trend: ErrorTrend,
608 pub operations_count: usize,
609 pub adaptations_count: usize,
610 pub performance_metrics: PerformanceMetrics,
611 pub precision_usage: HashMap<PrecisionMode, f64>,
612}
613
614impl PrecisionErrorMonitor {
615 fn new() -> Self {
616 Self {
617 error_history: Vec::new(),
618 error_estimators: vec![
619 Box::new(RichardsonExtrapolationEstimator::new()),
620 Box::new(DoublePrecisionComparisonEstimator::new()),
621 Box::new(ResidualBasedEstimator::new()),
622 ],
623 current_error: 1e-15,
624 error_trend: ErrorTrend::Stable,
625 }
626 }
627
628 fn add_error_sample(&mut self, error: f64) {
629 self.error_history.push(error);
630 if self.error_history.len() > 1000 {
631 self.error_history.remove(0);
632 }
633 self.current_error = error;
634 }
635
636 fn update_error_trend(&mut self) {
637 if self.error_history.len() < 10 {
638 return;
639 }
640
641 let recent = &self.error_history[self.error_history.len().saturating_sub(10)..];
642 let first_half: f64 = recent[..5].iter().sum::<f64>() / 5.0;
643 let second_half: f64 = recent[5..].iter().sum::<f64>() / 5.0;
644
645 self.error_trend = if second_half > first_half * 1.1 {
646 ErrorTrend::Increasing
647 } else if second_half < first_half * 0.9 {
648 ErrorTrend::Decreasing
649 } else {
650 ErrorTrend::Stable
651 };
652 }
653}
654
655impl PrecisionPerformanceMonitor {
656 fn new() -> Self {
657 Self {
658 timing_by_precision: HashMap::new(),
659 memory_by_precision: HashMap::new(),
660 current_performance: PerformanceMetrics {
661 operations_per_second: 1000.0,
662 memory_usage_bytes: 1024,
663 error_rate: 1e-15,
664 adaptation_overhead: 0.01,
665 },
666 }
667 }
668
669 fn add_timing_sample(&mut self, precision: PrecisionMode, time_ms: f64) {
670 self.timing_by_precision
671 .entry(precision)
672 .or_insert_with(Vec::new)
673 .push(time_ms);
674 }
675
676 fn add_memory_sample(&mut self, precision: PrecisionMode, memory: usize) {
677 self.memory_by_precision
678 .entry(precision)
679 .or_insert_with(Vec::new)
680 .push(memory);
681 }
682
683 fn update_current_performance(&mut self, result: &AdaptiveResult) {
684 self.current_performance.operations_per_second =
685 1000.0 / result.computation_time.as_millis().max(1) as f64;
686 self.current_performance.memory_usage_bytes = result.memory_used;
687 self.current_performance.error_rate = result.estimated_error;
688 }
689}
690
691#[derive(Debug)]
694pub struct RichardsonExtrapolationEstimator {
695 name: String,
696}
697
698impl RichardsonExtrapolationEstimator {
699 pub fn new() -> Self {
700 Self {
701 name: "Richardson Extrapolation".to_string(),
702 }
703 }
704}
705
706impl ErrorEstimator for RichardsonExtrapolationEstimator {
707 fn estimate_error(&self, result: &AdaptiveResult, reference: Option<&AdaptiveResult>) -> f64 {
708 if let Some(ref_result) = reference {
710 (result.value - ref_result.value).norm() / 2.0
711 } else {
712 1e-15 }
714 }
715
716 fn name(&self) -> &str {
717 &self.name
718 }
719
720 fn is_applicable(&self, comp_type: ComputationType) -> bool {
721 matches!(
722 comp_type,
723 ComputationType::StateEvolution | ComputationType::ExpectationValue
724 )
725 }
726}
727
728#[derive(Debug)]
729pub struct DoublePrecisionComparisonEstimator {
730 name: String,
731}
732
733impl DoublePrecisionComparisonEstimator {
734 pub fn new() -> Self {
735 Self {
736 name: "Double Precision Comparison".to_string(),
737 }
738 }
739}
740
741impl ErrorEstimator for DoublePrecisionComparisonEstimator {
742 fn estimate_error(&self, result: &AdaptiveResult, _reference: Option<&AdaptiveResult>) -> f64 {
743 match result.precision {
745 PrecisionMode::Single => 1e-7,
746 PrecisionMode::Double | PrecisionMode::Adaptive => 1e-15,
747 PrecisionMode::Extended => 1e-19,
748 PrecisionMode::Arbitrary(bits) => 10.0_f64.powf(-(bits as f64) / 3.3),
749 }
750 }
751
752 fn name(&self) -> &str {
753 &self.name
754 }
755
756 fn is_applicable(&self, _comp_type: ComputationType) -> bool {
757 true
758 }
759}
760
761#[derive(Debug)]
762pub struct ResidualBasedEstimator {
763 name: String,
764}
765
766impl ResidualBasedEstimator {
767 pub fn new() -> Self {
768 Self {
769 name: "Residual Based".to_string(),
770 }
771 }
772}
773
774impl ErrorEstimator for ResidualBasedEstimator {
775 fn estimate_error(&self, result: &AdaptiveResult, _reference: Option<&AdaptiveResult>) -> f64 {
776 result.value.norm() * 1e-16 }
779
780 fn name(&self) -> &str {
781 &self.name
782 }
783
784 fn is_applicable(&self, comp_type: ComputationType) -> bool {
785 matches!(
786 comp_type,
787 ComputationType::MatrixMultiplication | ComputationType::EigenvalueDecomposition
788 )
789 }
790}
791
792pub struct AdaptivePrecisionFactory;
794
795impl AdaptivePrecisionFactory {
796 pub fn create_high_accuracy() -> AdaptivePrecisionSimulator {
798 let config = AdaptivePrecisionConfig {
799 initial_precision: PrecisionMode::Double,
800 target_accuracy: 1e-15,
801 max_error_threshold: 1e-12,
802 min_precision: PrecisionMode::Double,
803 max_precision: PrecisionMode::Arbitrary(512),
804 performance_weight: 0.1, ..Default::default()
806 };
807 AdaptivePrecisionSimulator::new(config)
808 }
809
810 pub fn create_performance_optimized() -> AdaptivePrecisionSimulator {
812 let config = AdaptivePrecisionConfig {
813 initial_precision: PrecisionMode::Single,
814 target_accuracy: 1e-6,
815 max_error_threshold: 1e-4,
816 min_precision: PrecisionMode::Single,
817 max_precision: PrecisionMode::Double,
818 performance_weight: 0.8, adaptation_interval: 100, ..Default::default()
821 };
822 AdaptivePrecisionSimulator::new(config)
823 }
824
825 pub fn create_balanced() -> AdaptivePrecisionSimulator {
827 AdaptivePrecisionSimulator::new(AdaptivePrecisionConfig::default())
828 }
829
830 pub fn create_for_computation_type(comp_type: ComputationType) -> AdaptivePrecisionSimulator {
832 let config = match comp_type {
833 ComputationType::StateEvolution => AdaptivePrecisionConfig {
834 target_accuracy: 1e-12,
835 max_error_threshold: 1e-10,
836 performance_weight: 0.3,
837 ..Default::default()
838 },
839 ComputationType::ExpectationValue => AdaptivePrecisionConfig {
840 target_accuracy: 1e-10,
841 max_error_threshold: 1e-8,
842 performance_weight: 0.4,
843 ..Default::default()
844 },
845 ComputationType::Probability => AdaptivePrecisionConfig {
846 target_accuracy: 1e-8,
847 max_error_threshold: 1e-6,
848 performance_weight: 0.6,
849 ..Default::default()
850 },
851 ComputationType::Measurement => AdaptivePrecisionConfig {
852 target_accuracy: 1e-6,
853 max_error_threshold: 1e-4,
854 performance_weight: 0.7,
855 initial_precision: PrecisionMode::Single,
856 ..Default::default()
857 },
858 ComputationType::MatrixMultiplication => AdaptivePrecisionConfig {
859 target_accuracy: 1e-14,
860 max_error_threshold: 1e-12,
861 performance_weight: 0.2,
862 ..Default::default()
863 },
864 ComputationType::EigenvalueDecomposition => AdaptivePrecisionConfig {
865 target_accuracy: 1e-13,
866 max_error_threshold: 1e-11,
867 performance_weight: 0.1,
868 max_precision: PrecisionMode::Arbitrary(256),
869 ..Default::default()
870 },
871 ComputationType::TensorContraction => AdaptivePrecisionConfig {
872 target_accuracy: 1e-11,
873 max_error_threshold: 1e-9,
874 performance_weight: 0.5,
875 ..Default::default()
876 },
877 };
878 AdaptivePrecisionSimulator::new(config)
879 }
880}
881
882#[cfg(test)]
883mod tests {
884 use super::*;
885 use crate::{gate::single::Hadamard, qubit::QubitId};
886
887 #[test]
888 fn test_adaptive_precision_simulator_creation() {
889 let config = AdaptivePrecisionConfig::default();
890 let simulator = AdaptivePrecisionSimulator::new(config);
891
892 assert_eq!(simulator.current_precision(), PrecisionMode::Double);
893 assert_eq!(simulator.operation_count, 0);
894 }
895
896 #[test]
897 fn test_precision_factory() {
898 let high_acc = AdaptivePrecisionFactory::create_high_accuracy();
899 let perf_opt = AdaptivePrecisionFactory::create_performance_optimized();
900 let balanced = AdaptivePrecisionFactory::create_balanced();
901
902 assert_eq!(high_acc.current_precision(), PrecisionMode::Double);
903 assert_eq!(perf_opt.current_precision(), PrecisionMode::Single);
904 assert_eq!(balanced.current_precision(), PrecisionMode::Double);
905 }
906
907 #[test]
908 fn test_computation_type_specific_creation() {
909 let state_sim =
910 AdaptivePrecisionFactory::create_for_computation_type(ComputationType::StateEvolution);
911 let measurement_sim =
912 AdaptivePrecisionFactory::create_for_computation_type(ComputationType::Measurement);
913
914 assert_eq!(state_sim.current_precision(), PrecisionMode::Double);
915 assert_eq!(measurement_sim.current_precision(), PrecisionMode::Single);
916 }
917
918 #[test]
919 fn test_gate_application_with_adaptive_precision() {
920 let mut simulator = AdaptivePrecisionSimulator::new(AdaptivePrecisionConfig::default());
921 let hadamard = Hadamard { target: QubitId(0) };
922 let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
923
924 let result = simulator.apply_gate_adaptive(&hadamard, &mut state);
925 assert!(result.is_ok());
926
927 let adaptive_result = result.expect("Gate application should succeed");
928 assert_eq!(adaptive_result.precision, PrecisionMode::Double);
929 assert!(adaptive_result.estimated_error > 0.0);
930 }
931
932 #[test]
933 fn test_expectation_value_adaptive() {
934 let mut simulator = AdaptivePrecisionSimulator::new(AdaptivePrecisionConfig::default());
935
936 let observable = Array2::from_shape_vec(
937 (2, 2),
938 vec![
939 Complex64::new(1.0, 0.0),
940 Complex64::new(0.0, 0.0),
941 Complex64::new(0.0, 0.0),
942 Complex64::new(-1.0, 0.0),
943 ],
944 )
945 .expect("Observable matrix construction should succeed");
946
947 let state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
948
949 let result = simulator.expectation_value_adaptive(&observable, &state);
950 assert!(result.is_ok());
951
952 let adaptive_result = result.expect("Expectation value computation should succeed");
953 assert_eq!(adaptive_result.value, Complex64::new(0.5, 0.0));
954 }
955
956 #[test]
957 fn test_precision_adaptation() {
958 let mut config = AdaptivePrecisionConfig::default();
959 config.adaptation_interval = 1; config.max_error_threshold = 1e-20; let mut simulator = AdaptivePrecisionSimulator::new(config);
963
964 let result = simulator.force_adaptation(ComputationType::StateEvolution);
966 assert!(result.is_ok());
967 }
968
969 #[test]
970 fn test_error_estimators() {
971 let richardson = RichardsonExtrapolationEstimator::new();
972 let comparison = DoublePrecisionComparisonEstimator::new();
973 let residual = ResidualBasedEstimator::new();
974
975 let result = AdaptiveResult {
976 value: Complex64::new(1.0, 0.0),
977 precision: PrecisionMode::Double,
978 estimated_error: 1e-15,
979 computation_time: Duration::from_millis(10),
980 memory_used: 1024,
981 };
982
983 assert!(richardson.is_applicable(ComputationType::StateEvolution));
984 assert!(comparison.is_applicable(ComputationType::ExpectationValue));
985 assert!(residual.is_applicable(ComputationType::MatrixMultiplication));
986
987 let error1 = richardson.estimate_error(&result, None);
988 let error2 = comparison.estimate_error(&result, None);
989 let error3 = residual.estimate_error(&result, None);
990
991 assert!(error1 > 0.0);
992 assert!(error2 > 0.0);
993 assert!(error3 > 0.0);
994 }
995
996 #[test]
997 fn test_precision_mode_transitions() {
998 let simulator = AdaptivePrecisionSimulator::new(AdaptivePrecisionConfig::default());
999
1000 assert_eq!(
1002 AdaptivePrecisionSimulator::increase_precision(PrecisionMode::Single),
1003 PrecisionMode::Double
1004 );
1005 assert_eq!(
1006 AdaptivePrecisionSimulator::increase_precision(PrecisionMode::Double),
1007 PrecisionMode::Extended
1008 );
1009 assert_eq!(
1010 AdaptivePrecisionSimulator::increase_precision(PrecisionMode::Extended),
1011 PrecisionMode::Arbitrary(128)
1012 );
1013
1014 assert_eq!(
1016 AdaptivePrecisionSimulator::decrease_precision(PrecisionMode::Extended),
1017 PrecisionMode::Double
1018 );
1019 assert_eq!(
1020 AdaptivePrecisionSimulator::decrease_precision(PrecisionMode::Double),
1021 PrecisionMode::Single
1022 );
1023 assert_eq!(
1024 AdaptivePrecisionSimulator::decrease_precision(PrecisionMode::Arbitrary(128)),
1025 PrecisionMode::Arbitrary(64)
1026 );
1027 }
1028
1029 #[test]
1030 fn test_precision_statistics() {
1031 let mut simulator = AdaptivePrecisionSimulator::new(AdaptivePrecisionConfig::default());
1032
1033 let hadamard = Hadamard { target: QubitId(0) };
1035 let mut state = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1036
1037 let _ = simulator.apply_gate_adaptive(&hadamard, &mut state);
1038 let _ = simulator.apply_gate_adaptive(&hadamard, &mut state);
1039
1040 let stats = simulator.get_precision_stats();
1041 assert_eq!(stats.current_precision, PrecisionMode::Double);
1042 assert_eq!(stats.operations_count, 2);
1043 assert!(stats.performance_metrics.operations_per_second > 0.0);
1044 }
1045
1046 #[test]
1047 fn test_memory_estimation() {
1048 let simulator = AdaptivePrecisionSimulator::new(AdaptivePrecisionConfig::default());
1049
1050 let mem_state = simulator.estimate_memory_usage(ComputationType::StateEvolution);
1051 let mem_tensor = simulator.estimate_memory_usage(ComputationType::TensorContraction);
1052 let mem_measurement = simulator.estimate_memory_usage(ComputationType::Measurement);
1053
1054 assert!(mem_tensor > mem_state);
1056 assert!(mem_state > mem_measurement);
1058 }
1059
1060 #[test]
1061 fn test_performance_vs_accuracy_tradeoff() {
1062 let high_acc_config = AdaptivePrecisionConfig {
1063 performance_weight: 0.1, target_accuracy: 1e-15,
1065 ..Default::default()
1066 };
1067
1068 let perf_config = AdaptivePrecisionConfig {
1069 performance_weight: 0.9, target_accuracy: 1e-6,
1071 ..Default::default()
1072 };
1073
1074 let acc_sim = AdaptivePrecisionSimulator::new(high_acc_config);
1075 let perf_sim = AdaptivePrecisionSimulator::new(perf_config);
1076
1077 assert!(acc_sim.config.target_accuracy < perf_sim.config.target_accuracy);
1078 assert!(acc_sim.config.performance_weight < perf_sim.config.performance_weight);
1079 }
1080}