quantrs2_sim/mixed_precision_impl/
config.rs

1//! Configuration structures for mixed-precision quantum simulation.
2//!
3//! This module provides configuration types for precision levels,
4//! adaptive strategies, and performance optimization settings.
5
6use serde::{Deserialize, Serialize};
7
8use crate::error::{Result, SimulatorError};
9
10// Note: scirs2_linalg mixed_precision module temporarily unavailable
11// #[cfg(feature = "advanced_math")]
12// use scirs2_linalg::mixed_precision::{AdaptiveStrategy, MixedPrecisionContext, PrecisionLevel};
13
14// Placeholder types when the feature is not available
15#[derive(Debug)]
16pub struct MixedPrecisionContext;
17
18#[derive(Debug)]
19pub enum PrecisionLevel {
20    F16,
21    F32,
22    F64,
23    Adaptive,
24}
25
26#[derive(Debug)]
27pub enum AdaptiveStrategy {
28    ErrorBased(f64),
29    Fixed(PrecisionLevel),
30}
31
32impl MixedPrecisionContext {
33    pub fn new(_strategy: AdaptiveStrategy) -> Result<Self> {
34        Err(SimulatorError::UnsupportedOperation(
35            "Mixed precision context not available without advanced_math feature".to_string(),
36        ))
37    }
38}
39
40/// Precision levels for quantum computations
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42pub enum QuantumPrecision {
43    /// Half precision (16-bit floats)
44    Half,
45    /// Single precision (32-bit floats)
46    Single,
47    /// Double precision (64-bit floats)
48    Double,
49    /// Adaptive precision (automatically selected)
50    Adaptive,
51}
52
53impl QuantumPrecision {
54    /// Get the corresponding SciRS2 precision level
55    #[cfg(feature = "advanced_math")]
56    pub fn to_scirs2_precision(&self) -> PrecisionLevel {
57        match self {
58            QuantumPrecision::Half => PrecisionLevel::F16,
59            QuantumPrecision::Single => PrecisionLevel::F32,
60            QuantumPrecision::Double => PrecisionLevel::F64,
61            QuantumPrecision::Adaptive => PrecisionLevel::Adaptive,
62        }
63    }
64
65    /// Get memory usage factor relative to double precision
66    pub fn memory_factor(&self) -> f64 {
67        match self {
68            QuantumPrecision::Half => 0.25,
69            QuantumPrecision::Single => 0.5,
70            QuantumPrecision::Double => 1.0,
71            QuantumPrecision::Adaptive => 0.75, // Average case
72        }
73    }
74
75    /// Get computational cost factor relative to double precision
76    pub fn computation_factor(&self) -> f64 {
77        match self {
78            QuantumPrecision::Half => 0.5,
79            QuantumPrecision::Single => 0.7,
80            QuantumPrecision::Double => 1.0,
81            QuantumPrecision::Adaptive => 0.8, // Average case
82        }
83    }
84
85    /// Get typical numerical error for this precision
86    pub fn typical_error(&self) -> f64 {
87        match self {
88            QuantumPrecision::Half => 1e-3,
89            QuantumPrecision::Single => 1e-6,
90            QuantumPrecision::Double => 1e-15,
91            QuantumPrecision::Adaptive => 1e-6, // Conservative estimate
92        }
93    }
94
95    /// Check if this precision is sufficient for the given error tolerance
96    pub fn is_sufficient_for_tolerance(&self, tolerance: f64) -> bool {
97        self.typical_error() <= tolerance * 10.0 // Safety factor of 10
98    }
99
100    /// Get the next higher precision level
101    pub fn higher_precision(&self) -> Option<QuantumPrecision> {
102        match self {
103            QuantumPrecision::Half => Some(QuantumPrecision::Single),
104            QuantumPrecision::Single => Some(QuantumPrecision::Double),
105            QuantumPrecision::Double => None,
106            QuantumPrecision::Adaptive => Some(QuantumPrecision::Double),
107        }
108    }
109
110    /// Get the next lower precision level
111    pub fn lower_precision(&self) -> Option<QuantumPrecision> {
112        match self {
113            QuantumPrecision::Half => None,
114            QuantumPrecision::Single => Some(QuantumPrecision::Half),
115            QuantumPrecision::Double => Some(QuantumPrecision::Single),
116            QuantumPrecision::Adaptive => Some(QuantumPrecision::Single),
117        }
118    }
119}
120
121/// Mixed precision configuration
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct MixedPrecisionConfig {
124    /// Default precision for state vectors
125    pub state_vector_precision: QuantumPrecision,
126    /// Default precision for gate operations
127    pub gate_precision: QuantumPrecision,
128    /// Default precision for measurements
129    pub measurement_precision: QuantumPrecision,
130    /// Error tolerance for precision selection
131    pub error_tolerance: f64,
132    /// Enable automatic precision adaptation
133    pub adaptive_precision: bool,
134    /// Minimum precision level (never go below this)
135    pub min_precision: QuantumPrecision,
136    /// Maximum precision level (never go above this)
137    pub max_precision: QuantumPrecision,
138    /// Number of qubits threshold for precision reduction
139    pub large_system_threshold: usize,
140    /// Enable precision analysis and reporting
141    pub enable_analysis: bool,
142}
143
144impl Default for MixedPrecisionConfig {
145    fn default() -> Self {
146        Self {
147            state_vector_precision: QuantumPrecision::Single,
148            gate_precision: QuantumPrecision::Single,
149            measurement_precision: QuantumPrecision::Double,
150            error_tolerance: 1e-6,
151            adaptive_precision: true,
152            min_precision: QuantumPrecision::Half,
153            max_precision: QuantumPrecision::Double,
154            large_system_threshold: 20,
155            enable_analysis: true,
156        }
157    }
158}
159
160impl MixedPrecisionConfig {
161    /// Create configuration optimized for accuracy
162    pub fn for_accuracy() -> Self {
163        Self {
164            state_vector_precision: QuantumPrecision::Double,
165            gate_precision: QuantumPrecision::Double,
166            measurement_precision: QuantumPrecision::Double,
167            error_tolerance: 1e-12,
168            adaptive_precision: false,
169            min_precision: QuantumPrecision::Double,
170            max_precision: QuantumPrecision::Double,
171            large_system_threshold: 50,
172            enable_analysis: true,
173        }
174    }
175
176    /// Create configuration optimized for performance
177    pub fn for_performance() -> Self {
178        Self {
179            state_vector_precision: QuantumPrecision::Half,
180            gate_precision: QuantumPrecision::Single,
181            measurement_precision: QuantumPrecision::Single,
182            error_tolerance: 1e-3,
183            adaptive_precision: true,
184            min_precision: QuantumPrecision::Half,
185            max_precision: QuantumPrecision::Single,
186            large_system_threshold: 10,
187            enable_analysis: false,
188        }
189    }
190
191    /// Create configuration balanced between accuracy and performance
192    pub fn balanced() -> Self {
193        Self::default()
194    }
195
196    /// Validate the configuration
197    pub fn validate(&self) -> Result<()> {
198        if self.error_tolerance <= 0.0 {
199            return Err(SimulatorError::InvalidInput(
200                "Error tolerance must be positive".to_string(),
201            ));
202        }
203
204        if self.large_system_threshold == 0 {
205            return Err(SimulatorError::InvalidInput(
206                "Large system threshold must be positive".to_string(),
207            ));
208        }
209
210        // Check precision consistency
211        if self.min_precision as u8 > self.max_precision as u8 {
212            return Err(SimulatorError::InvalidInput(
213                "Minimum precision cannot be higher than maximum precision".to_string(),
214            ));
215        }
216
217        Ok(())
218    }
219
220    /// Adjust configuration for a specific number of qubits
221    pub fn adjust_for_qubits(&mut self, num_qubits: usize) {
222        if num_qubits >= self.large_system_threshold {
223            // For large systems, reduce precision to save memory
224            if self.adaptive_precision {
225                match self.state_vector_precision {
226                    QuantumPrecision::Double => {
227                        self.state_vector_precision = QuantumPrecision::Single
228                    }
229                    QuantumPrecision::Single => {
230                        self.state_vector_precision = QuantumPrecision::Half
231                    }
232                    _ => {}
233                }
234            }
235        }
236    }
237
238    /// Estimate memory usage for a given number of qubits
239    pub fn estimate_memory_usage(&self, num_qubits: usize) -> usize {
240        let state_vector_size = 1 << num_qubits;
241        let base_memory = state_vector_size * 16; // Complex64 size
242
243        let factor = self.state_vector_precision.memory_factor();
244        (base_memory as f64 * factor) as usize
245    }
246
247    /// Check if the configuration is suitable for the available memory
248    pub fn fits_in_memory(&self, num_qubits: usize, available_memory: usize) -> bool {
249        self.estimate_memory_usage(num_qubits) <= available_memory
250    }
251}