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 const fn to_scirs2_precision(&self) -> PrecisionLevel {
57 match self {
58 Self::Half => PrecisionLevel::F16,
59 Self::Single => PrecisionLevel::F32,
60 Self::Double => PrecisionLevel::F64,
61 Self::Adaptive => PrecisionLevel::Adaptive,
62 }
63 }
64
65 #[must_use]
67 pub const fn memory_factor(&self) -> f64 {
68 match self {
69 Self::Half => 0.25,
70 Self::Single => 0.5,
71 Self::Double => 1.0,
72 Self::Adaptive => 0.75, }
74 }
75
76 #[must_use]
78 pub const fn computation_factor(&self) -> f64 {
79 match self {
80 Self::Half => 0.5,
81 Self::Single => 0.7,
82 Self::Double => 1.0,
83 Self::Adaptive => 0.8, }
85 }
86
87 #[must_use]
89 pub const fn typical_error(&self) -> f64 {
90 match self {
91 Self::Half => 1e-3,
92 Self::Single => 1e-6,
93 Self::Double => 1e-15,
94 Self::Adaptive => 1e-6, }
96 }
97
98 #[must_use]
100 pub fn is_sufficient_for_tolerance(&self, tolerance: f64) -> bool {
101 self.typical_error() <= tolerance * 10.0 }
103
104 #[must_use]
106 pub const fn higher_precision(&self) -> Option<Self> {
107 match self {
108 Self::Half => Some(Self::Single),
109 Self::Single => Some(Self::Double),
110 Self::Double => None,
111 Self::Adaptive => Some(Self::Double),
112 }
113 }
114
115 #[must_use]
117 pub const fn lower_precision(&self) -> Option<Self> {
118 match self {
119 Self::Half => None,
120 Self::Single => Some(Self::Half),
121 Self::Double => Some(Self::Single),
122 Self::Adaptive => Some(Self::Single),
123 }
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct MixedPrecisionConfig {
130 pub state_vector_precision: QuantumPrecision,
132 pub gate_precision: QuantumPrecision,
134 pub measurement_precision: QuantumPrecision,
136 pub error_tolerance: f64,
138 pub adaptive_precision: bool,
140 pub min_precision: QuantumPrecision,
142 pub max_precision: QuantumPrecision,
144 pub large_system_threshold: usize,
146 pub enable_analysis: bool,
148}
149
150impl Default for MixedPrecisionConfig {
151 fn default() -> Self {
152 Self {
153 state_vector_precision: QuantumPrecision::Single,
154 gate_precision: QuantumPrecision::Single,
155 measurement_precision: QuantumPrecision::Double,
156 error_tolerance: 1e-6,
157 adaptive_precision: true,
158 min_precision: QuantumPrecision::Half,
159 max_precision: QuantumPrecision::Double,
160 large_system_threshold: 20,
161 enable_analysis: true,
162 }
163 }
164}
165
166impl MixedPrecisionConfig {
167 #[must_use]
169 pub const fn for_accuracy() -> Self {
170 Self {
171 state_vector_precision: QuantumPrecision::Double,
172 gate_precision: QuantumPrecision::Double,
173 measurement_precision: QuantumPrecision::Double,
174 error_tolerance: 1e-12,
175 adaptive_precision: false,
176 min_precision: QuantumPrecision::Double,
177 max_precision: QuantumPrecision::Double,
178 large_system_threshold: 50,
179 enable_analysis: true,
180 }
181 }
182
183 #[must_use]
185 pub const fn for_performance() -> Self {
186 Self {
187 state_vector_precision: QuantumPrecision::Half,
188 gate_precision: QuantumPrecision::Single,
189 measurement_precision: QuantumPrecision::Single,
190 error_tolerance: 1e-3,
191 adaptive_precision: true,
192 min_precision: QuantumPrecision::Half,
193 max_precision: QuantumPrecision::Single,
194 large_system_threshold: 10,
195 enable_analysis: false,
196 }
197 }
198
199 #[must_use]
201 pub fn balanced() -> Self {
202 Self::default()
203 }
204
205 pub fn validate(&self) -> Result<()> {
207 if self.error_tolerance <= 0.0 {
208 return Err(SimulatorError::InvalidInput(
209 "Error tolerance must be positive".to_string(),
210 ));
211 }
212
213 if self.large_system_threshold == 0 {
214 return Err(SimulatorError::InvalidInput(
215 "Large system threshold must be positive".to_string(),
216 ));
217 }
218
219 if self.min_precision as u8 > self.max_precision as u8 {
221 return Err(SimulatorError::InvalidInput(
222 "Minimum precision cannot be higher than maximum precision".to_string(),
223 ));
224 }
225
226 Ok(())
227 }
228
229 pub const fn adjust_for_qubits(&mut self, num_qubits: usize) {
231 if num_qubits >= self.large_system_threshold {
232 if self.adaptive_precision {
234 match self.state_vector_precision {
235 QuantumPrecision::Double => {
236 self.state_vector_precision = QuantumPrecision::Single;
237 }
238 QuantumPrecision::Single => {
239 self.state_vector_precision = QuantumPrecision::Half;
240 }
241 _ => {}
242 }
243 }
244 }
245 }
246
247 #[must_use]
249 pub fn estimate_memory_usage(&self, num_qubits: usize) -> usize {
250 let state_vector_size = 1 << num_qubits;
251 let base_memory = state_vector_size * 16; let factor = self.state_vector_precision.memory_factor();
254 (f64::from(base_memory) * factor) as usize
255 }
256
257 #[must_use]
259 pub fn fits_in_memory(&self, num_qubits: usize, available_memory: usize) -> bool {
260 self.estimate_memory_usage(num_qubits) <= available_memory
261 }
262}