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