quantrs2_device/cloud/provider_optimizations/
awsoptimizer_traits.rs1use super::traits::ProviderOptimizer;
13use super::types::*;
14use crate::prelude::CloudProvider;
15use crate::DeviceResult;
16use std::collections::HashMap;
17use std::time::Duration;
18use uuid::Uuid;
19
20const AWS_PER_TASK_FEE_USD: f64 = 0.30;
26
27const AWS_IONQ_PER_SHOT_USD: f64 = 0.00035;
29
30const AWS_RIGETTI_PER_SHOT_USD: f64 = 0.00075;
32
33const AWS_OQC_PER_SHOT_USD: f64 = 0.00035;
35
36const IONQ_TWO_QUBIT_FIDELITY: f64 = 0.965;
42
43const RIGETTI_TWO_QUBIT_FIDELITY: f64 = 0.935;
45
46const OQC_TWO_QUBIT_FIDELITY: f64 = 0.958;
48
49#[derive(Debug, Clone, Copy)]
54struct BackendSpec {
55 name: &'static str,
56 per_shot_usd: f64,
57 two_qubit_fidelity: f64,
58 max_qubits: usize,
59 queue_latency_s: f64,
61}
62
63const AWS_BACKENDS: &[BackendSpec] = &[
64 BackendSpec {
65 name: "ionq.harmony",
66 per_shot_usd: AWS_IONQ_PER_SHOT_USD,
67 two_qubit_fidelity: IONQ_TWO_QUBIT_FIDELITY,
68 max_qubits: 11,
69 queue_latency_s: 30.0,
70 },
71 BackendSpec {
72 name: "ionq.aria-1",
73 per_shot_usd: AWS_IONQ_PER_SHOT_USD,
74 two_qubit_fidelity: 0.975,
75 max_qubits: 25,
76 queue_latency_s: 45.0,
77 },
78 BackendSpec {
79 name: "rigetti.aspen-m-3",
80 per_shot_usd: AWS_RIGETTI_PER_SHOT_USD,
81 two_qubit_fidelity: RIGETTI_TWO_QUBIT_FIDELITY,
82 max_qubits: 79,
83 queue_latency_s: 20.0,
84 },
85 BackendSpec {
86 name: "oqc.lucy",
87 per_shot_usd: AWS_OQC_PER_SHOT_USD,
88 two_qubit_fidelity: OQC_TWO_QUBIT_FIDELITY,
89 max_qubits: 8,
90 queue_latency_s: 25.0,
91 },
92];
93
94fn select_aws_backend(qubit_count: usize, min_fidelity: f64) -> Option<&'static BackendSpec> {
97 AWS_BACKENDS
98 .iter()
99 .filter(|b| b.max_qubits >= qubit_count && b.two_qubit_fidelity >= min_fidelity)
100 .min_by(|a, b| {
101 a.per_shot_usd
102 .partial_cmp(&b.per_shot_usd)
103 .unwrap_or(std::cmp::Ordering::Equal)
104 })
105}
106
107impl ProviderOptimizer for AWSOptimizer {
112 fn optimize_workload(
123 &self,
124 workload: &WorkloadSpec,
125 ) -> DeviceResult<OptimizationRecommendation> {
126 let qubit_count = workload.circuit_characteristics.qubit_count;
127 let shots = workload.execution_requirements.shots;
128 let min_fidelity = workload
129 .circuit_characteristics
130 .coherence_requirements
131 .min_gate_fidelity;
132
133 let primary = select_aws_backend(qubit_count, min_fidelity).ok_or_else(|| {
135 crate::DeviceError::InvalidInput(format!(
136 "No AWS Braket backend can accommodate {qubit_count} qubits \
137 with fidelity ≥ {min_fidelity:.3}"
138 ))
139 })?;
140
141 let recommended_config = ExecutionConfig {
143 provider: CloudProvider::AWS,
144 backend: primary.name.to_string(),
145 optimization_settings: OptimizationSettings {
146 circuit_optimization: CircuitOptimizationSettings {
147 gate_fusion: true,
148 gate_cancellation: true,
149 circuit_compression: true,
150 transpilation_level: TranspilationLevel::Advanced,
151 error_mitigation: ErrorMitigationSettings {
152 zero_noise_extrapolation: false,
153 readout_error_mitigation: true,
154 gate_error_mitigation: false,
155 decoherence_mitigation: false,
156 crosstalk_mitigation: false,
157 },
158 },
159 ..OptimizationSettings::default()
160 },
161 ..ExecutionConfig::default()
162 };
163
164 let cost_estimate = compute_aws_cost(shots, primary);
166 let perf_prediction = compute_aws_performance(workload, primary);
167
168 let alternatives: Vec<AlternativeRecommendation> = AWS_BACKENDS
170 .iter()
171 .filter(|b| {
172 b.name != primary.name
173 && b.max_qubits >= qubit_count
174 && b.two_qubit_fidelity >= min_fidelity
175 })
176 .map(|b| {
177 let alt_cost = compute_aws_cost(shots, b);
178 let alt_perf = compute_aws_performance(workload, b);
179 AlternativeRecommendation {
180 alternative_id: Uuid::new_v4().to_string(),
181 config: ExecutionConfig {
182 provider: CloudProvider::AWS,
183 backend: b.name.to_string(),
184 optimization_settings: OptimizationSettings::default(),
185 ..ExecutionConfig::default()
186 },
187 trade_offs: TradeOffAnalysis {
188 performance_impact: alt_perf.expected_fidelity
189 - perf_prediction.expected_fidelity,
190 cost_impact: alt_cost.total_cost - cost_estimate.total_cost,
191 reliability_impact: 0.0,
192 complexity_impact: 0.0,
193 trade_off_summary: format!(
194 "{} — Δcost ${:+.4}, Δfidelity {:+.3}",
195 b.name,
196 alt_cost.total_cost - cost_estimate.total_cost,
197 alt_perf.expected_fidelity - perf_prediction.expected_fidelity
198 ),
199 },
200 use_case_suitability: alt_perf.success_probability,
201 }
202 })
203 .collect();
204
205 let rationale = format!(
206 "Selected {} as the cheapest AWS Braket backend that supports {qubit_count} qubits \
207 with two-qubit gate fidelity ≥ {min_fidelity:.3}. \
208 Estimated cost: ${:.4} for {shots} shots.",
209 primary.name, cost_estimate.total_cost,
210 );
211
212 Ok(OptimizationRecommendation {
213 recommendation_id: Uuid::new_v4().to_string(),
214 workload_id: workload.workload_id.clone(),
215 provider: CloudProvider::AWS,
216 recommended_config,
217 optimization_strategies: self.get_optimization_strategies(),
218 expected_performance: perf_prediction,
219 cost_estimate,
220 confidence_score: 0.82,
221 rationale,
222 alternative_recommendations: alternatives,
223 })
224 }
225
226 fn get_provider(&self) -> CloudProvider {
227 CloudProvider::AWS
228 }
229
230 fn get_optimization_strategies(&self) -> Vec<OptimizationStrategy> {
231 vec![
232 OptimizationStrategy::CostOptimization,
233 OptimizationStrategy::LoadBalancing,
234 OptimizationStrategy::ResourceProvisioning,
235 ]
236 }
237
238 fn predict_performance(
246 &self,
247 workload: &WorkloadSpec,
248 config: &ExecutionConfig,
249 ) -> DeviceResult<PerformancePrediction> {
250 let backend = AWS_BACKENDS
252 .iter()
253 .find(|b| b.name == config.backend)
254 .unwrap_or(&AWS_BACKENDS[2]);
255
256 Ok(compute_aws_performance(workload, backend))
257 }
258
259 fn estimate_cost(
264 &self,
265 workload: &WorkloadSpec,
266 config: &ExecutionConfig,
267 ) -> DeviceResult<CostEstimate> {
268 let backend = AWS_BACKENDS
269 .iter()
270 .find(|b| b.name == config.backend)
271 .unwrap_or(&AWS_BACKENDS[2]);
272 Ok(compute_aws_cost(
273 workload.execution_requirements.shots,
274 backend,
275 ))
276 }
277}
278
279fn compute_aws_cost(shots: usize, backend: &BackendSpec) -> CostEstimate {
284 let execution_cost = shots as f64 * backend.per_shot_usd;
285 let total = execution_cost + AWS_PER_TASK_FEE_USD;
286 let uncertainty = total * 0.15;
288
289 CostEstimate {
290 total_cost: total,
291 cost_breakdown: CostBreakdown {
292 execution_cost,
293 queue_cost: 0.0,
294 storage_cost: 0.0,
295 network_cost: 0.0,
296 overhead_cost: AWS_PER_TASK_FEE_USD,
297 discount_applied: 0.0,
298 },
299 cost_model: CostModel::PayPerUse,
300 uncertainty_range: (total - uncertainty, total + uncertainty),
301 cost_optimization_opportunities: vec![CostOptimizationOpportunity {
302 opportunity_type: CostOptimizationType::VolumeDiscount,
303 potential_savings: total * 0.10,
304 implementation_effort: 0.2,
305 description: "Consolidate multiple small jobs into a single batch task to \
306 amortise the per-task fee."
307 .to_string(),
308 }],
309 }
310}
311
312fn compute_aws_performance(
313 workload: &WorkloadSpec,
314 backend: &BackendSpec,
315) -> PerformancePrediction {
316 let cc = &workload.circuit_characteristics;
317 let shots = workload.execution_requirements.shots;
318
319 let two_qubit_gates = (cc.circuit_depth / 2).max(1) as f64;
322 let expected_fidelity = (backend.two_qubit_fidelity.powf(two_qubit_gates)).clamp(0.01, 1.0);
323
324 let routing_penalty = if cc.qubit_count as f64 > backend.max_qubits as f64 * 0.6 {
326 0.90
327 } else {
328 1.0
329 };
330 let adjusted_fidelity = (expected_fidelity * routing_penalty).clamp(0.0, 1.0);
331
332 let success_probability = (adjusted_fidelity * 0.97).clamp(0.0, 1.0);
334
335 let exec_time_ms = shots as f64 * 1.5;
337 let queue_time_s = backend.queue_latency_s;
338
339 PerformancePrediction {
340 execution_time: Duration::from_millis(exec_time_ms as u64),
341 queue_time: Duration::from_secs_f64(queue_time_s),
342 total_time: Duration::from_millis(exec_time_ms as u64)
343 + Duration::from_secs_f64(queue_time_s),
344 success_probability,
345 expected_fidelity: adjusted_fidelity,
346 resource_utilization: ResourceUtilizationPrediction {
347 cpu_utilization: 0.05,
348 memory_utilization: 0.10,
349 quantum_resource_utilization: cc.qubit_count as f64 / backend.max_qubits as f64,
350 network_utilization: 0.02,
351 storage_utilization: 0.01,
352 },
353 bottlenecks: Vec::new(),
354 confidence_interval: (success_probability * 0.92, success_probability * 1.05),
355 }
356}