1#[cfg(feature = "ibm")]
2use async_trait::async_trait;
3#[cfg(feature = "ibm")]
4use chrono;
5use quantrs2_circuit::prelude::Circuit;
6use std::collections::HashMap;
7#[cfg(feature = "ibm")]
8use std::sync::Arc;
9#[cfg(feature = "ibm")]
10use std::time::Duration;
11
12#[cfg(feature = "ibm")]
13use crate::{
14 ibm::IBMQuantumClient, CircuitExecutor, CircuitResult, DeviceError, DeviceResult, QuantumDevice,
15};
16
17#[cfg(not(feature = "ibm"))]
18use crate::{
19 ibm::IBMQuantumClient, CircuitExecutor, CircuitResult, DeviceError, DeviceResult, QuantumDevice,
20};
21
22#[cfg(feature = "ibm")]
24pub struct IBMQuantumDevice {
25 client: Arc<IBMQuantumClient>,
27 backend: crate::ibm::IBMBackend,
29 config: IBMDeviceConfig,
31}
32
33#[cfg(not(feature = "ibm"))]
34pub struct IBMQuantumDevice;
35
36#[derive(Debug, Clone)]
38pub struct IBMDeviceConfig {
39 pub default_shots: usize,
41 pub optimization_level: usize,
43 pub timeout_seconds: u64,
45 pub optimize_routing: bool,
47 pub max_parallel_jobs: usize,
49}
50
51#[cfg(feature = "ibm")]
52impl Default for IBMDeviceConfig {
53 fn default() -> Self {
54 Self {
55 default_shots: 1024,
56 optimization_level: 1,
57 timeout_seconds: 300,
58 optimize_routing: true,
59 max_parallel_jobs: 5,
60 }
61 }
62}
63
64#[cfg(not(feature = "ibm"))]
65impl Default for IBMDeviceConfig {
66 fn default() -> Self {
67 Self {
68 default_shots: 1024,
69 optimization_level: 1,
70 timeout_seconds: 300,
71 optimize_routing: true,
72 max_parallel_jobs: 5,
73 }
74 }
75}
76
77#[cfg(feature = "ibm")]
78impl IBMQuantumDevice {
79 pub async fn new(
81 client: IBMQuantumClient,
82 backend_name: &str,
83 config: Option<IBMDeviceConfig>,
84 ) -> DeviceResult<Self> {
85 let backend = client.get_backend(backend_name).await?;
86 let client = Arc::new(client);
87
88 Ok(Self {
89 client,
90 backend,
91 config: config.unwrap_or_default(),
92 })
93 }
94
95 fn create_circuit_config<const N: usize>(
97 &self,
98 circuit: &Circuit<N>,
99 shots: Option<usize>,
100 ) -> DeviceResult<crate::ibm::IBMCircuitConfig> {
101 let qasm = self.circuit_to_qasm(circuit)?;
102 let shots = shots.unwrap_or(self.config.default_shots);
103
104 Ok(crate::ibm::IBMCircuitConfig {
105 name: format!("quantrs_circuit_{}", chrono::Utc::now().timestamp()),
106 qasm,
107 shots,
108 optimization_level: Some(self.config.optimization_level),
109 initial_layout: None, })
111 }
112
113 fn circuit_to_qasm<const N: usize>(&self, _circuit: &Circuit<N>) -> DeviceResult<String> {
115 if N > self.backend.n_qubits {
116 return Err(DeviceError::CircuitConversion(format!(
117 "Circuit has {} qubits but backend {} only supports {} qubits",
118 N, self.backend.name, self.backend.n_qubits
119 )));
120 }
121
122 let mut qasm = String::from("OPENQASM 2.0;\ninclude \"qelib1.inc\";\n\n");
124
125 use std::fmt::Write;
127 let _ = writeln!(qasm, "qreg q[{N}];");
128 let _ = writeln!(qasm, "creg c[{N}];");
129
130 Ok(qasm)
135 }
136}
137
138#[cfg(not(feature = "ibm"))]
139impl IBMQuantumDevice {
140 pub async fn new(
142 _client: IBMQuantumClient,
143 _backend_name: &str,
144 _config: Option<IBMDeviceConfig>,
145 ) -> DeviceResult<Self> {
146 Err(DeviceError::UnsupportedDevice(
147 "IBM Quantum support not enabled. Recompile with the 'ibm' feature.".to_string(),
148 ))
149 }
150}
151
152#[cfg(feature = "ibm")]
153#[async_trait]
154impl QuantumDevice for IBMQuantumDevice {
155 async fn is_available(&self) -> DeviceResult<bool> {
156 let backend = self.client.get_backend(&self.backend.name).await?;
158 Ok(backend.status == "active")
159 }
160
161 async fn qubit_count(&self) -> DeviceResult<usize> {
162 Ok(self.backend.n_qubits)
163 }
164
165 async fn properties(&self) -> DeviceResult<HashMap<String, String>> {
166 let mut props = HashMap::new();
169 props.insert("name".to_string(), self.backend.name.clone());
170 props.insert("description".to_string(), self.backend.description.clone());
171 props.insert("version".to_string(), self.backend.version.clone());
172 props.insert("n_qubits".to_string(), self.backend.n_qubits.to_string());
173 props.insert("simulator".to_string(), self.backend.simulator.to_string());
174
175 Ok(props)
176 }
177
178 async fn is_simulator(&self) -> DeviceResult<bool> {
179 Ok(self.backend.simulator)
180 }
181}
182
183#[cfg(not(feature = "ibm"))]
184impl QuantumDevice for IBMQuantumDevice {
185 fn is_available(&self) -> DeviceResult<bool> {
186 Err(DeviceError::UnsupportedDevice(
187 "IBM Quantum support not enabled".to_string(),
188 ))
189 }
190
191 fn qubit_count(&self) -> DeviceResult<usize> {
192 Err(DeviceError::UnsupportedDevice(
193 "IBM Quantum support not enabled".to_string(),
194 ))
195 }
196
197 fn properties(&self) -> DeviceResult<HashMap<String, String>> {
198 Err(DeviceError::UnsupportedDevice(
199 "IBM Quantum support not enabled".to_string(),
200 ))
201 }
202
203 fn is_simulator(&self) -> DeviceResult<bool> {
204 Err(DeviceError::UnsupportedDevice(
205 "IBM Quantum support not enabled".to_string(),
206 ))
207 }
208}
209
210#[cfg(feature = "ibm")]
211#[async_trait]
212impl CircuitExecutor for IBMQuantumDevice {
213 async fn execute_circuit<const N: usize>(
214 &self,
215 circuit: &Circuit<N>,
216 shots: usize,
217 ) -> DeviceResult<CircuitResult> {
218 let config = self.create_circuit_config(circuit, Some(shots))?;
220
221 let job_id = self
223 .client
224 .submit_circuit(&self.backend.name, config)
225 .await?;
226
227 let result = self
229 .client
230 .wait_for_job(&job_id, Some(self.config.timeout_seconds))
231 .await?;
232
233 let mut metadata = HashMap::new();
235 metadata.insert("job_id".to_string(), job_id);
236 metadata.insert("backend".to_string(), self.backend.name.clone());
237 metadata.insert("shots".to_string(), shots.to_string());
238
239 Ok(CircuitResult {
240 counts: result.counts,
241 shots: result.shots,
242 metadata,
243 })
244 }
245
246 async fn execute_circuits<const N: usize>(
247 &self,
248 circuits: Vec<&Circuit<N>>,
249 shots: usize,
250 ) -> DeviceResult<Vec<CircuitResult>> {
251 if circuits.is_empty() {
252 return Ok(Vec::new());
253 }
254
255 let chunk_size = self.config.max_parallel_jobs.max(1);
257 let mut results = Vec::new();
258
259 for chunk in circuits.chunks(chunk_size) {
261 let mut configs = Vec::new();
262
263 for circuit in chunk {
265 let config = self.create_circuit_config(circuit, Some(shots))?;
266 configs.push(config);
267 }
268
269 let job_ids = self
271 .client
272 .submit_circuits_parallel(&self.backend.name, configs)
273 .await?;
274
275 let mut chunk_results = Vec::new();
277 for job_id in job_ids {
278 let result = self
279 .client
280 .wait_for_job(&job_id, Some(self.config.timeout_seconds))
281 .await?;
282
283 let mut metadata = HashMap::new();
284 metadata.insert("job_id".to_string(), job_id);
285 metadata.insert("backend".to_string(), self.backend.name.clone());
286 metadata.insert("shots".to_string(), shots.to_string());
287
288 chunk_results.push(CircuitResult {
289 counts: result.counts,
290 shots: result.shots,
291 metadata,
292 });
293 }
294
295 results.extend(chunk_results);
296 }
297
298 Ok(results)
299 }
300
301 async fn can_execute_circuit<const N: usize>(
302 &self,
303 _circuit: &Circuit<N>,
304 ) -> DeviceResult<bool> {
305 if N > self.backend.n_qubits {
307 return Ok(false);
308 }
309
310 Ok(true)
317 }
318
319 async fn estimated_queue_time<const N: usize>(
320 &self,
321 _circuit: &Circuit<N>,
322 ) -> DeviceResult<Duration> {
323 if self.backend.simulator {
328 Ok(Duration::from_secs(10)) } else {
330 Ok(Duration::from_secs(3600)) }
332 }
333}
334
335#[cfg(not(feature = "ibm"))]
336impl CircuitExecutor for IBMQuantumDevice {
337 fn execute_circuit<const N: usize>(
338 &self,
339 _circuit: &Circuit<N>,
340 _shots: usize,
341 ) -> DeviceResult<CircuitResult> {
342 Err(DeviceError::UnsupportedDevice(
343 "IBM Quantum support not enabled".to_string(),
344 ))
345 }
346
347 fn execute_circuits<const N: usize>(
348 &self,
349 _circuits: Vec<&Circuit<N>>,
350 _shots: usize,
351 ) -> DeviceResult<Vec<CircuitResult>> {
352 Err(DeviceError::UnsupportedDevice(
353 "IBM Quantum support not enabled".to_string(),
354 ))
355 }
356
357 fn can_execute_circuit<const N: usize>(&self, _circuit: &Circuit<N>) -> DeviceResult<bool> {
358 Err(DeviceError::UnsupportedDevice(
359 "IBM Quantum support not enabled".to_string(),
360 ))
361 }
362
363 fn estimated_queue_time<const N: usize>(
364 &self,
365 _circuit: &Circuit<N>,
366 ) -> DeviceResult<std::time::Duration> {
367 Err(DeviceError::UnsupportedDevice(
368 "IBM Quantum support not enabled".to_string(),
369 ))
370 }
371}