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