1use super::traits::ProviderOptimizer;
16use super::types::*;
17use crate::prelude::CloudProvider;
18use crate::DeviceResult;
19use std::collections::HashMap;
20use std::time::Duration;
21use uuid::Uuid;
22
23const AZURE_IONQ_PER_CRU_USD: f64 = 0.00097;
29
30const AZURE_QUANTINUUM_USD_PER_HQC: f64 = 2.00;
32
33const AZURE_QUANTINUUM_HQC_DEPTH_CONSTANT: f64 = 5000.0;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41enum AzureTarget {
42 IonQAria,
43 IonQHarmony,
44 QuantinuumH1,
45 QuantinuumH2,
46}
47
48#[derive(Debug, Clone, Copy)]
49struct AzureBackendSpec {
50 target: AzureTarget,
51 name: &'static str,
52 max_qubits: usize,
53 two_qubit_fidelity: f64,
54 queue_latency_s: f64,
56}
57
58const AZURE_BACKENDS: &[AzureBackendSpec] = &[
59 AzureBackendSpec {
60 target: AzureTarget::IonQAria,
61 name: "ionq.simulator",
62 max_qubits: 29,
63 two_qubit_fidelity: 0.972,
64 queue_latency_s: 20.0,
65 },
66 AzureBackendSpec {
67 target: AzureTarget::IonQHarmony,
68 name: "ionq.qpu",
69 max_qubits: 11,
70 two_qubit_fidelity: 0.965,
71 queue_latency_s: 40.0,
72 },
73 AzureBackendSpec {
74 target: AzureTarget::QuantinuumH1,
75 name: "quantinuum.hqs-lt-s1",
76 max_qubits: 20,
77 two_qubit_fidelity: 0.997,
78 queue_latency_s: 60.0,
79 },
80 AzureBackendSpec {
81 target: AzureTarget::QuantinuumH2,
82 name: "quantinuum.hqs-lt-s2",
83 max_qubits: 32,
84 two_qubit_fidelity: 0.998,
85 queue_latency_s: 90.0,
86 },
87];
88
89fn estimate_azure_cost_for_backend(
91 shots: usize,
92 circuit_depth: usize,
93 qubit_count: usize,
94 backend: &AzureBackendSpec,
95) -> f64 {
96 match backend.target {
97 AzureTarget::IonQAria | AzureTarget::IonQHarmony => {
98 let approx_gates = (circuit_depth * qubit_count) as f64 / 2.0;
101 let cru = approx_gates * qubit_count as f64;
102 cru * AZURE_IONQ_PER_CRU_USD * shots as f64
103 }
104 AzureTarget::QuantinuumH1 | AzureTarget::QuantinuumH2 => {
105 let hqc = (circuit_depth as f64 * qubit_count as f64)
107 / AZURE_QUANTINUUM_HQC_DEPTH_CONSTANT
108 * shots as f64;
109 hqc * AZURE_QUANTINUUM_USD_PER_HQC
110 }
111 }
112}
113
114fn select_azure_backend(
116 qubit_count: usize,
117 min_fidelity: f64,
118 circuit_depth: usize,
119 shots: usize,
120) -> Option<&'static AzureBackendSpec> {
121 let candidates: Vec<&AzureBackendSpec> = AZURE_BACKENDS
123 .iter()
124 .filter(|b| b.max_qubits >= qubit_count && b.two_qubit_fidelity >= min_fidelity)
125 .collect();
126
127 candidates.into_iter().max_by(|a, b| {
129 let cost_a =
130 estimate_azure_cost_for_backend(shots, circuit_depth, qubit_count, a).max(1e-9);
131 let cost_b =
132 estimate_azure_cost_for_backend(shots, circuit_depth, qubit_count, b).max(1e-9);
133 let score_a = a.two_qubit_fidelity / cost_a;
134 let score_b = b.two_qubit_fidelity / cost_b;
135 score_a
136 .partial_cmp(&score_b)
137 .unwrap_or(std::cmp::Ordering::Equal)
138 })
139}
140
141impl ProviderOptimizer for AzureOptimizer {
146 fn optimize_workload(
153 &self,
154 workload: &WorkloadSpec,
155 ) -> DeviceResult<OptimizationRecommendation> {
156 let qubit_count = workload.circuit_characteristics.qubit_count;
157 let shots = workload.execution_requirements.shots;
158 let min_fidelity = workload
159 .circuit_characteristics
160 .coherence_requirements
161 .min_gate_fidelity;
162 let circuit_depth = workload.circuit_characteristics.circuit_depth;
163
164 let primary = select_azure_backend(qubit_count, min_fidelity, circuit_depth, shots)
165 .ok_or_else(|| {
166 crate::DeviceError::InvalidInput(format!(
167 "No Azure Quantum backend can accommodate {qubit_count} qubits \
168 with two-qubit fidelity ≥ {min_fidelity:.3}"
169 ))
170 })?;
171
172 let (gate_fusion, zne) = match primary.target {
174 AzureTarget::QuantinuumH1 | AzureTarget::QuantinuumH2 => (true, circuit_depth > 40),
175 _ => (true, false),
176 };
177
178 let recommended_config = ExecutionConfig {
179 provider: CloudProvider::Azure,
180 backend: primary.name.to_string(),
181 optimization_settings: OptimizationSettings {
182 circuit_optimization: CircuitOptimizationSettings {
183 gate_fusion,
184 gate_cancellation: true,
185 circuit_compression: true,
186 transpilation_level: TranspilationLevel::Aggressive,
187 error_mitigation: ErrorMitigationSettings {
188 zero_noise_extrapolation: zne,
189 readout_error_mitigation: true,
190 gate_error_mitigation: false,
191 decoherence_mitigation: false,
192 crosstalk_mitigation: false,
193 },
194 },
195 hardware_optimization: HardwareOptimizationSettings {
196 qubit_mapping: QubitMappingStrategy::FidelityOptimized,
197 routing_optimization: RoutingOptimizationStrategy::FidelityAware,
198 calibration_optimization: CalibrationOptimizationStrategy::Dynamic,
199 noise_adaptation: NoiseAdaptationStrategy::Statistical,
200 },
201 ..OptimizationSettings::default()
202 },
203 ..ExecutionConfig::default()
204 };
205
206 let cost_estimate = compute_azure_cost(shots, circuit_depth, qubit_count, primary);
207 let perf_prediction = compute_azure_performance(workload, primary);
208
209 let alternatives: Vec<AlternativeRecommendation> = AZURE_BACKENDS
210 .iter()
211 .filter(|b| {
212 b.name != primary.name
213 && b.max_qubits >= qubit_count
214 && b.two_qubit_fidelity >= min_fidelity
215 })
216 .map(|b| {
217 let alt_cost = compute_azure_cost(shots, circuit_depth, qubit_count, b);
218 let alt_perf = compute_azure_performance(workload, b);
219 AlternativeRecommendation {
220 alternative_id: Uuid::new_v4().to_string(),
221 config: ExecutionConfig {
222 provider: CloudProvider::Azure,
223 backend: b.name.to_string(),
224 optimization_settings: OptimizationSettings::default(),
225 ..ExecutionConfig::default()
226 },
227 trade_offs: TradeOffAnalysis {
228 performance_impact: alt_perf.expected_fidelity
229 - perf_prediction.expected_fidelity,
230 cost_impact: alt_cost.total_cost - cost_estimate.total_cost,
231 reliability_impact: 0.0,
232 complexity_impact: 0.0,
233 trade_off_summary: format!(
234 "{} — Δcost ${:+.4}, Δfidelity {:+.4}",
235 b.name,
236 alt_cost.total_cost - cost_estimate.total_cost,
237 alt_perf.expected_fidelity - perf_prediction.expected_fidelity
238 ),
239 },
240 use_case_suitability: alt_perf.success_probability,
241 }
242 })
243 .collect();
244
245 let rationale = format!(
246 "Selected {} on Azure Quantum as the best fidelity/cost tradeoff \
247 for {qubit_count} qubits at ≥{min_fidelity:.3} two-qubit fidelity. \
248 Estimated cost: ${:.4}. ZNE: {}.",
249 primary.name,
250 cost_estimate.total_cost,
251 if zne { "enabled" } else { "disabled" }
252 );
253
254 Ok(OptimizationRecommendation {
255 recommendation_id: Uuid::new_v4().to_string(),
256 workload_id: workload.workload_id.clone(),
257 provider: CloudProvider::Azure,
258 recommended_config,
259 optimization_strategies: self.get_optimization_strategies(),
260 expected_performance: perf_prediction,
261 cost_estimate,
262 confidence_score: 0.85,
263 rationale,
264 alternative_recommendations: alternatives,
265 })
266 }
267
268 fn get_provider(&self) -> CloudProvider {
269 CloudProvider::Azure
270 }
271
272 fn get_optimization_strategies(&self) -> Vec<OptimizationStrategy> {
273 vec![
274 OptimizationStrategy::SchedulingOptimization,
275 OptimizationStrategy::HardwareSelection,
276 OptimizationStrategy::CacheOptimization,
277 ]
278 }
279
280 fn predict_performance(
281 &self,
282 workload: &WorkloadSpec,
283 config: &ExecutionConfig,
284 ) -> DeviceResult<PerformancePrediction> {
285 let backend = AZURE_BACKENDS
286 .iter()
287 .find(|b| b.name == config.backend)
288 .unwrap_or(&AZURE_BACKENDS[0]);
289 Ok(compute_azure_performance(workload, backend))
290 }
291
292 fn estimate_cost(
293 &self,
294 workload: &WorkloadSpec,
295 config: &ExecutionConfig,
296 ) -> DeviceResult<CostEstimate> {
297 let backend = AZURE_BACKENDS
298 .iter()
299 .find(|b| b.name == config.backend)
300 .unwrap_or(&AZURE_BACKENDS[0]);
301 Ok(compute_azure_cost(
302 workload.execution_requirements.shots,
303 workload.circuit_characteristics.circuit_depth,
304 workload.circuit_characteristics.qubit_count,
305 backend,
306 ))
307 }
308}
309
310fn compute_azure_cost(
315 shots: usize,
316 circuit_depth: usize,
317 qubit_count: usize,
318 backend: &AzureBackendSpec,
319) -> CostEstimate {
320 let total = estimate_azure_cost_for_backend(shots, circuit_depth, qubit_count, backend);
321 let uncertainty = total * 0.18;
322
323 CostEstimate {
324 total_cost: total,
325 cost_breakdown: CostBreakdown {
326 execution_cost: total,
327 queue_cost: 0.0,
328 storage_cost: 0.0,
329 network_cost: 0.0,
330 overhead_cost: 0.0,
331 discount_applied: 0.0,
332 },
333 cost_model: CostModel::PayPerUse,
334 uncertainty_range: (total - uncertainty, total + uncertainty),
335 cost_optimization_opportunities: vec![CostOptimizationOpportunity {
336 opportunity_type: CostOptimizationType::ResourceRightSizing,
337 potential_savings: total * 0.12,
338 implementation_effort: 0.25,
339 description: "Reduce circuit depth via aggressive TKET optimisation to lower \
340 HQC credit consumption on Quantinuum targets."
341 .to_string(),
342 }],
343 }
344}
345
346fn compute_azure_performance(
347 workload: &WorkloadSpec,
348 backend: &AzureBackendSpec,
349) -> PerformancePrediction {
350 let cc = &workload.circuit_characteristics;
351 let shots = workload.execution_requirements.shots;
352
353 let two_qubit_layers = (cc.circuit_depth / 4).max(1) as f64;
355 let expected_fidelity = backend
356 .two_qubit_fidelity
357 .powf(two_qubit_layers)
358 .clamp(0.01, 1.0);
359 let success_probability = (expected_fidelity * 0.99).clamp(0.0, 1.0);
360
361 let exec_s = shots as f64 * 0.002;
363
364 PerformancePrediction {
365 execution_time: Duration::from_secs_f64(exec_s),
366 queue_time: Duration::from_secs_f64(backend.queue_latency_s),
367 total_time: Duration::from_secs_f64(exec_s + backend.queue_latency_s),
368 success_probability,
369 expected_fidelity,
370 resource_utilization: ResourceUtilizationPrediction {
371 cpu_utilization: 0.03,
372 memory_utilization: 0.06,
373 quantum_resource_utilization: cc.qubit_count as f64 / backend.max_qubits as f64,
374 network_utilization: 0.01,
375 storage_utilization: 0.005,
376 },
377 bottlenecks: Vec::new(),
378 confidence_interval: (success_probability * 0.94, success_probability * 1.03),
379 }
380}