quantrs2_device/cloud/provider_optimizations/
ibmoptimizer_traits.rs1use 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 IBM_FREE_TIER_SECONDS_PER_MONTH: f64 = 600.0;
30
31const IBM_PREMIUM_PER_RUNTIME_SECOND_USD: f64 = 1.60;
33
34#[derive(Debug, Clone, Copy)]
39struct IbmBackendSpec {
40 name: &'static str,
41 qubit_count: usize,
42 relative_queue_depth: f64,
45 cx_fidelity: f64,
47 runtime_per_1k_shots_s: f64,
49}
50
51const IBM_BACKENDS: &[IbmBackendSpec] = &[
52 IbmBackendSpec {
53 name: "ibm_brisbane",
54 qubit_count: 127,
55 relative_queue_depth: 0.55,
56 cx_fidelity: 0.985,
57 runtime_per_1k_shots_s: 2.5,
58 },
59 IbmBackendSpec {
60 name: "ibm_kyoto",
61 qubit_count: 127,
62 relative_queue_depth: 0.40,
63 cx_fidelity: 0.982,
64 runtime_per_1k_shots_s: 2.6,
65 },
66 IbmBackendSpec {
67 name: "ibm_osaka",
68 qubit_count: 127,
69 relative_queue_depth: 0.70,
70 cx_fidelity: 0.987,
71 runtime_per_1k_shots_s: 2.4,
72 },
73 IbmBackendSpec {
74 name: "ibm_sherbrooke",
75 qubit_count: 127,
76 relative_queue_depth: 0.30,
77 cx_fidelity: 0.983,
78 runtime_per_1k_shots_s: 2.5,
79 },
80 IbmBackendSpec {
81 name: "ibm_torino",
82 qubit_count: 133,
83 relative_queue_depth: 0.25,
84 cx_fidelity: 0.991,
85 runtime_per_1k_shots_s: 2.2,
86 },
87];
88
89fn select_ibm_backend(qubit_count: usize, min_fidelity: f64) -> Option<&'static IbmBackendSpec> {
92 IBM_BACKENDS
93 .iter()
94 .filter(|b| b.qubit_count >= qubit_count && b.cx_fidelity >= min_fidelity)
95 .min_by(|a, b| {
96 a.relative_queue_depth
97 .partial_cmp(&b.relative_queue_depth)
98 .unwrap_or(std::cmp::Ordering::Equal)
99 })
100}
101
102impl ProviderOptimizer for IBMOptimizer {
107 fn optimize_workload(
117 &self,
118 workload: &WorkloadSpec,
119 ) -> DeviceResult<OptimizationRecommendation> {
120 let qubit_count = workload.circuit_characteristics.qubit_count;
121 let shots = workload.execution_requirements.shots;
122 let min_fidelity = workload
123 .circuit_characteristics
124 .coherence_requirements
125 .min_gate_fidelity;
126 let depth = workload.circuit_characteristics.circuit_depth;
127
128 let primary = select_ibm_backend(qubit_count, min_fidelity).ok_or_else(|| {
129 crate::DeviceError::InvalidInput(format!(
130 "No IBM Quantum backend can accommodate {qubit_count} qubits \
131 with CNOT fidelity ≥ {min_fidelity:.3}"
132 ))
133 })?;
134
135 let use_zne = depth > 50;
137
138 let recommended_config = ExecutionConfig {
139 provider: CloudProvider::IBM,
140 backend: primary.name.to_string(),
141 optimization_settings: OptimizationSettings {
142 circuit_optimization: CircuitOptimizationSettings {
143 gate_fusion: true,
144 gate_cancellation: true,
145 circuit_compression: true,
146 transpilation_level: TranspilationLevel::Advanced,
147 error_mitigation: ErrorMitigationSettings {
148 zero_noise_extrapolation: use_zne,
149 readout_error_mitigation: true,
150 gate_error_mitigation: false,
151 decoherence_mitigation: use_zne,
152 crosstalk_mitigation: false,
153 },
154 },
155 hardware_optimization: HardwareOptimizationSettings {
156 qubit_mapping: QubitMappingStrategy::NoiseAdaptive,
157 routing_optimization: RoutingOptimizationStrategy::MinimumSwaps,
158 calibration_optimization: CalibrationOptimizationStrategy::Dynamic,
159 noise_adaptation: NoiseAdaptationStrategy::ModelBased,
160 },
161 ..OptimizationSettings::default()
162 },
163 ..ExecutionConfig::default()
164 };
165
166 let cost_estimate = compute_ibm_cost(shots, primary);
167 let perf_prediction = compute_ibm_performance(workload, primary);
168
169 let alternatives: Vec<AlternativeRecommendation> = IBM_BACKENDS
170 .iter()
171 .filter(|b| {
172 b.name != primary.name
173 && b.qubit_count >= qubit_count
174 && b.cx_fidelity >= min_fidelity
175 })
176 .map(|b| {
177 let alt_cost = compute_ibm_cost(shots, b);
178 let alt_perf = compute_ibm_performance(workload, b);
179 AlternativeRecommendation {
180 alternative_id: Uuid::new_v4().to_string(),
181 config: ExecutionConfig {
182 provider: CloudProvider::IBM,
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 "{} — queue depth {:.0}%, Δcost ${:+.2}, Δfidelity {:+.3}",
195 b.name,
196 b.relative_queue_depth * 100.0,
197 alt_cost.total_cost - cost_estimate.total_cost,
198 alt_perf.expected_fidelity - perf_prediction.expected_fidelity
199 ),
200 },
201 use_case_suitability: alt_perf.success_probability,
202 }
203 })
204 .collect();
205
206 let rationale = format!(
207 "Selected {} as the least-busy IBM Quantum backend with ≥ {qubit_count} qubits \
208 and CNOT fidelity ≥ {min_fidelity:.3}. \
209 Current relative queue depth: {:.0}%. \
210 ZNE mitigation: {}. Estimated cost: ${:.2}.",
211 primary.name,
212 primary.relative_queue_depth * 100.0,
213 if use_zne { "enabled" } else { "disabled" },
214 cost_estimate.total_cost,
215 );
216
217 Ok(OptimizationRecommendation {
218 recommendation_id: Uuid::new_v4().to_string(),
219 workload_id: workload.workload_id.clone(),
220 provider: CloudProvider::IBM,
221 recommended_config,
222 optimization_strategies: self.get_optimization_strategies(),
223 expected_performance: perf_prediction,
224 cost_estimate,
225 confidence_score: 0.88,
226 rationale,
227 alternative_recommendations: alternatives,
228 })
229 }
230
231 fn get_provider(&self) -> CloudProvider {
232 CloudProvider::IBM
233 }
234
235 fn get_optimization_strategies(&self) -> Vec<OptimizationStrategy> {
236 vec![
237 OptimizationStrategy::CircuitOptimization,
238 OptimizationStrategy::HardwareSelection,
239 OptimizationStrategy::ErrorMitigation,
240 ]
241 }
242
243 fn predict_performance(
244 &self,
245 workload: &WorkloadSpec,
246 config: &ExecutionConfig,
247 ) -> DeviceResult<PerformancePrediction> {
248 let backend = IBM_BACKENDS
249 .iter()
250 .find(|b| b.name == config.backend)
251 .unwrap_or(&IBM_BACKENDS[0]);
252 Ok(compute_ibm_performance(workload, backend))
253 }
254
255 fn estimate_cost(
256 &self,
257 workload: &WorkloadSpec,
258 config: &ExecutionConfig,
259 ) -> DeviceResult<CostEstimate> {
260 let backend = IBM_BACKENDS
261 .iter()
262 .find(|b| b.name == config.backend)
263 .unwrap_or(&IBM_BACKENDS[0]);
264 Ok(compute_ibm_cost(
265 workload.execution_requirements.shots,
266 backend,
267 ))
268 }
269}
270
271fn compute_ibm_cost(shots: usize, backend: &IbmBackendSpec) -> CostEstimate {
276 let runtime_s = (shots as f64 / 1000.0) * backend.runtime_per_1k_shots_s;
278
279 let billable_s = (runtime_s - IBM_FREE_TIER_SECONDS_PER_MONTH).max(0.0);
281 let execution_cost = billable_s * IBM_PREMIUM_PER_RUNTIME_SECOND_USD;
282 let total = execution_cost;
283 let uncertainty = (total * 0.20).max(0.01);
284
285 CostEstimate {
286 total_cost: total,
287 cost_breakdown: CostBreakdown {
288 execution_cost,
289 queue_cost: 0.0,
290 storage_cost: 0.0,
291 network_cost: 0.0,
292 overhead_cost: 0.0,
293 discount_applied: (runtime_s.min(IBM_FREE_TIER_SECONDS_PER_MONTH))
294 * IBM_PREMIUM_PER_RUNTIME_SECOND_USD,
295 },
296 cost_model: CostModel::PayPerUse,
297 uncertainty_range: (total - uncertainty, total + uncertainty),
298 cost_optimization_opportunities: vec![CostOptimizationOpportunity {
299 opportunity_type: CostOptimizationType::SchedulingOptimization,
300 potential_savings: total * 0.15,
301 implementation_effort: 0.3,
302 description: "Schedule during off-peak hours to reduce queue wait time \
303 and stay within the monthly free-tier budget."
304 .to_string(),
305 }],
306 }
307}
308
309fn compute_ibm_performance(
310 workload: &WorkloadSpec,
311 backend: &IbmBackendSpec,
312) -> PerformancePrediction {
313 let cc = &workload.circuit_characteristics;
314 let shots = workload.execution_requirements.shots;
315
316 let cx_layers = (cc.circuit_depth / 3).max(1) as f64;
318 let expected_fidelity = backend.cx_fidelity.powf(cx_layers).clamp(0.01, 1.0);
319
320 let queue_s = backend.relative_queue_depth * 300.0;
322
323 let exec_s = (shots as f64 / 1000.0) * backend.runtime_per_1k_shots_s;
325
326 let success_probability = (expected_fidelity * 0.98).clamp(0.0, 1.0);
327
328 PerformancePrediction {
329 execution_time: Duration::from_secs_f64(exec_s),
330 queue_time: Duration::from_secs_f64(queue_s),
331 total_time: Duration::from_secs_f64(exec_s + queue_s),
332 success_probability,
333 expected_fidelity,
334 resource_utilization: ResourceUtilizationPrediction {
335 cpu_utilization: 0.03,
336 memory_utilization: 0.08,
337 quantum_resource_utilization: cc.qubit_count as f64 / backend.qubit_count as f64,
338 network_utilization: 0.01,
339 storage_utilization: 0.005,
340 },
341 bottlenecks: Vec::new(),
342 confidence_interval: (success_probability * 0.93, success_probability * 1.04),
343 }
344}