quantrs2_sim/mixed_precision_impl/
mod.rs

1//! Mixed-precision quantum simulation module.
2//!
3//! This module provides adaptive precision algorithms that automatically
4//! select optimal numerical precision (f16, f32, f64) for different parts
5//! of quantum computations, leveraging performance optimization while
6//! maintaining required accuracy.
7
8pub mod analysis;
9pub mod config;
10pub mod simulator;
11pub mod state_vector;
12
13// Re-export commonly used types and structs
14pub use analysis::{AnalysisSummary, PerformanceMetrics, PrecisionAnalysis, PrecisionAnalyzer};
15pub use config::{
16    AdaptiveStrategy, MixedPrecisionConfig, MixedPrecisionContext, PrecisionLevel, QuantumPrecision,
17};
18pub use simulator::{MixedPrecisionSimulator, MixedPrecisionStats};
19pub use state_vector::MixedPrecisionStateVector;
20
21use crate::error::Result;
22
23/// Initialize the mixed-precision subsystem
24pub fn initialize() -> Result<()> {
25    // Perform any necessary initialization
26    #[cfg(feature = "advanced_math")]
27    {
28        // Initialize SciRS2 mixed precision context if available
29        let _context = MixedPrecisionContext::new(AdaptiveStrategy::ErrorBased(1e-6));
30    }
31
32    Ok(())
33}
34
35/// Check if mixed-precision features are available
36pub fn is_available() -> bool {
37    cfg!(feature = "advanced_math")
38}
39
40/// Get supported precision levels
41pub fn get_supported_precisions() -> Vec<QuantumPrecision> {
42    vec![
43        QuantumPrecision::Half,
44        QuantumPrecision::Single,
45        QuantumPrecision::Double,
46        QuantumPrecision::Adaptive,
47    ]
48}
49
50/// Create a default configuration for accuracy
51pub fn default_accuracy_config() -> MixedPrecisionConfig {
52    MixedPrecisionConfig::for_accuracy()
53}
54
55/// Create a default configuration for performance
56pub fn default_performance_config() -> MixedPrecisionConfig {
57    MixedPrecisionConfig::for_performance()
58}
59
60/// Create a balanced configuration
61pub fn default_balanced_config() -> MixedPrecisionConfig {
62    MixedPrecisionConfig::balanced()
63}
64
65/// Validate a mixed-precision configuration
66pub fn validate_config(config: &MixedPrecisionConfig) -> Result<()> {
67    config.validate()
68}
69
70/// Estimate memory usage for a given configuration and number of qubits
71pub fn estimate_memory_usage(config: &MixedPrecisionConfig, num_qubits: usize) -> usize {
72    config.estimate_memory_usage(num_qubits)
73}
74
75/// Calculate memory savings compared to double precision
76pub fn calculate_memory_savings(config: &MixedPrecisionConfig, num_qubits: usize) -> f64 {
77    simulator::utils::memory_savings(config, num_qubits)
78}
79
80/// Get performance improvement factor for a precision level
81pub fn get_performance_factor(precision: QuantumPrecision) -> f64 {
82    simulator::utils::performance_improvement_factor(precision)
83}
84
85/// Benchmark different precision levels
86pub fn benchmark_precisions() -> Result<analysis::PrecisionAnalysis> {
87    let mut analyzer = PrecisionAnalyzer::new();
88    Ok(analyzer.analyze_for_tolerance(1e-6))
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use scirs2_core::ndarray::Array1;
95    use scirs2_core::Complex64;
96
97    #[test]
98    fn test_precision_initialization() {
99        let result = initialize();
100        assert!(result.is_ok());
101    }
102
103    #[test]
104    fn test_supported_precisions() {
105        let precisions = get_supported_precisions();
106        assert_eq!(precisions.len(), 4);
107        assert!(precisions.contains(&QuantumPrecision::Half));
108        assert!(precisions.contains(&QuantumPrecision::Single));
109        assert!(precisions.contains(&QuantumPrecision::Double));
110        assert!(precisions.contains(&QuantumPrecision::Adaptive));
111    }
112
113    #[test]
114    fn test_config_creation() {
115        let accuracy_config = default_accuracy_config();
116        assert_eq!(
117            accuracy_config.state_vector_precision,
118            QuantumPrecision::Double
119        );
120
121        let performance_config = default_performance_config();
122        assert_eq!(
123            performance_config.state_vector_precision,
124            QuantumPrecision::Half
125        );
126
127        let balanced_config = default_balanced_config();
128        assert_eq!(
129            balanced_config.state_vector_precision,
130            QuantumPrecision::Single
131        );
132    }
133
134    #[test]
135    fn test_config_validation() {
136        let config = MixedPrecisionConfig::default();
137        assert!(validate_config(&config).is_ok());
138
139        let mut invalid_config = config.clone();
140        invalid_config.error_tolerance = -1.0;
141        assert!(validate_config(&invalid_config).is_err());
142    }
143
144    #[test]
145    fn test_memory_estimation() {
146        let config = MixedPrecisionConfig::default();
147        let memory_4q = estimate_memory_usage(&config, 4);
148        let memory_8q = estimate_memory_usage(&config, 8);
149
150        // Memory should scale exponentially with qubits
151        assert!(memory_8q > memory_4q * 10);
152    }
153
154    #[test]
155    fn test_precision_properties() {
156        assert_eq!(QuantumPrecision::Half.memory_factor(), 0.25);
157        assert_eq!(QuantumPrecision::Single.memory_factor(), 0.5);
158        assert_eq!(QuantumPrecision::Double.memory_factor(), 1.0);
159
160        assert!(QuantumPrecision::Half.typical_error() > QuantumPrecision::Single.typical_error());
161        assert!(
162            QuantumPrecision::Single.typical_error() > QuantumPrecision::Double.typical_error()
163        );
164    }
165
166    #[test]
167    fn test_precision_transitions() {
168        assert_eq!(
169            QuantumPrecision::Half.higher_precision(),
170            Some(QuantumPrecision::Single)
171        );
172        assert_eq!(
173            QuantumPrecision::Single.higher_precision(),
174            Some(QuantumPrecision::Double)
175        );
176        assert_eq!(QuantumPrecision::Double.higher_precision(), None);
177
178        assert_eq!(
179            QuantumPrecision::Double.lower_precision(),
180            Some(QuantumPrecision::Single)
181        );
182        assert_eq!(
183            QuantumPrecision::Single.lower_precision(),
184            Some(QuantumPrecision::Half)
185        );
186        assert_eq!(QuantumPrecision::Half.lower_precision(), None);
187    }
188
189    #[test]
190    fn test_state_vector_creation() {
191        let state = MixedPrecisionStateVector::new(4, QuantumPrecision::Single);
192        assert_eq!(state.len(), 4);
193        assert_eq!(state.precision(), QuantumPrecision::Single);
194
195        let basis_state =
196            MixedPrecisionStateVector::computational_basis(2, QuantumPrecision::Double);
197        assert_eq!(basis_state.len(), 4);
198        assert_eq!(basis_state.precision(), QuantumPrecision::Double);
199    }
200
201    #[test]
202    fn test_state_vector_operations() {
203        let mut state = MixedPrecisionStateVector::new(4, QuantumPrecision::Single);
204
205        // Test setting and getting amplitudes
206        let amplitude = Complex64::new(0.5, 0.3);
207        assert!(state.set_amplitude(0, amplitude).is_ok());
208
209        // For single precision, we need to account for precision loss
210        let retrieved_amplitude = state.amplitude(0).unwrap();
211        assert!((retrieved_amplitude.re - amplitude.re).abs() < 1e-6);
212        assert!((retrieved_amplitude.im - amplitude.im).abs() < 1e-6);
213
214        // Test probability calculation
215        let prob = state.probability(0).unwrap();
216        assert!((prob - amplitude.norm_sqr()).abs() < 1e-6);
217    }
218
219    #[test]
220    fn test_precision_conversion() {
221        let state_single = MixedPrecisionStateVector::new(4, QuantumPrecision::Single);
222        let state_double = state_single.to_precision(QuantumPrecision::Double);
223
224        assert!(state_double.is_ok());
225        let converted = state_double.unwrap();
226        assert_eq!(converted.precision(), QuantumPrecision::Double);
227        assert_eq!(converted.len(), 4);
228    }
229
230    #[test]
231    fn test_simulator_creation() {
232        let config = MixedPrecisionConfig::default();
233        let simulator = MixedPrecisionSimulator::new(2, config);
234
235        assert!(simulator.is_ok());
236        let sim = simulator.unwrap();
237        assert!(sim.get_state().is_some());
238    }
239
240    #[test]
241    fn test_performance_metrics() {
242        let metrics = PerformanceMetrics::new(100.0, 1024, 10.0, 5.0);
243        assert_eq!(metrics.execution_time_ms, 100.0);
244        assert_eq!(metrics.memory_usage_bytes, 1024);
245        assert_eq!(metrics.throughput_ops_per_sec, 10.0);
246        assert_eq!(metrics.energy_efficiency, 5.0);
247
248        let score = metrics.performance_score();
249        assert!(score >= 0.0 && score <= 1.0);
250    }
251
252    #[test]
253    fn test_precision_analysis() {
254        let mut analysis = PrecisionAnalysis::new();
255        analysis.add_recommendation("test_op".to_string(), QuantumPrecision::Single);
256        analysis.add_error_estimate(QuantumPrecision::Single, 1e-6);
257
258        let metrics = PerformanceMetrics::new(50.0, 512, 20.0, 10.0);
259        analysis.add_performance_metrics(QuantumPrecision::Single, metrics);
260
261        analysis.calculate_quality_score();
262
263        assert_eq!(
264            analysis.get_best_precision("test_op"),
265            Some(QuantumPrecision::Single)
266        );
267        assert!(analysis.quality_score > 0.0);
268    }
269
270    #[test]
271    fn test_analyzer() {
272        let mut analyzer = PrecisionAnalyzer::new();
273        let result = analyzer.analyze_for_tolerance(1e-6);
274        assert!(!result.error_estimates.is_empty());
275        assert!(!result.performance_metrics.is_empty());
276    }
277}