quantrs2_sim/mixed_precision_impl/
config.rs1use serde::{Deserialize, Serialize};
7
8use crate::error::{Result, SimulatorError};
9
10#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42pub enum QuantumPrecision {
43 Half,
45 Single,
47 Double,
49 Adaptive,
51}
52
53impl QuantumPrecision {
54 #[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 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, }
73 }
74
75 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, }
83 }
84
85 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, }
93 }
94
95 pub fn is_sufficient_for_tolerance(&self, tolerance: f64) -> bool {
97 self.typical_error() <= tolerance * 10.0 }
99
100 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct MixedPrecisionConfig {
124 pub state_vector_precision: QuantumPrecision,
126 pub gate_precision: QuantumPrecision,
128 pub measurement_precision: QuantumPrecision,
130 pub error_tolerance: f64,
132 pub adaptive_precision: bool,
134 pub min_precision: QuantumPrecision,
136 pub max_precision: QuantumPrecision,
138 pub large_system_threshold: usize,
140 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 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 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 pub fn balanced() -> Self {
193 Self::default()
194 }
195
196 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 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 pub fn adjust_for_qubits(&mut self, num_qubits: usize) {
222 if num_qubits >= self.large_system_threshold {
223 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 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; let factor = self.state_vector_precision.memory_factor();
244 (base_memory as f64 * factor) as usize
245 }
246
247 pub fn fits_in_memory(&self, num_qubits: usize, available_memory: usize) -> bool {
249 self.estimate_memory_usage(num_qubits) <= available_memory
250 }
251}