1use crate::{
23 error::{QuantRS2Error, QuantRS2Result},
24 gate::GateOp,
25 qubit::QubitId,
26};
27use scirs2_core::ndarray::{Array1, Array2};
28use scirs2_core::Complex64 as Complex;
29use std::collections::HashMap;
30use std::time::{Duration, SystemTime};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum CloudPlatform {
39 IBM,
41 AWS,
43 Google,
45 Azure,
47 Rigetti,
49 IonQ,
51}
52
53impl CloudPlatform {
54 pub fn name(&self) -> &'static str {
56 match self {
57 CloudPlatform::IBM => "IBM Quantum",
58 CloudPlatform::AWS => "AWS Braket",
59 CloudPlatform::Google => "Google Quantum AI",
60 CloudPlatform::Azure => "Azure Quantum",
61 CloudPlatform::Rigetti => "Rigetti QCS",
62 CloudPlatform::IonQ => "IonQ Cloud",
63 }
64 }
65
66 pub fn endpoint(&self) -> &'static str {
68 match self {
69 CloudPlatform::IBM => "https://auth.quantum-computing.ibm.com/api",
70 CloudPlatform::AWS => "https://braket.us-east-1.amazonaws.com",
71 CloudPlatform::Google => "https://quantumengine.googleapis.com",
72 CloudPlatform::Azure => "https://quantum.azure.com",
73 CloudPlatform::Rigetti => "https://api.rigetti.com",
74 CloudPlatform::IonQ => "https://api.ionq.com",
75 }
76 }
77
78 pub fn supports_qubits(&self, num_qubits: usize) -> bool {
80 match self {
81 CloudPlatform::IBM => num_qubits <= 127, CloudPlatform::AWS => num_qubits <= 34, CloudPlatform::Google => num_qubits <= 72, CloudPlatform::Azure => num_qubits <= 40, CloudPlatform::Rigetti => num_qubits <= 80, CloudPlatform::IonQ => num_qubits <= 32, }
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum DeviceType {
94 QPU,
96 Simulator,
98 TensorNetworkSimulator,
100 NoisySimulator,
102}
103
104#[derive(Debug, Clone)]
106pub struct DeviceInfo {
107 pub platform: CloudPlatform,
109 pub name: String,
111 pub device_type: DeviceType,
113 pub num_qubits: usize,
115 pub connectivity: Vec<(usize, usize)>,
117 pub gate_set: Vec<String>,
119 pub gate_fidelities: HashMap<String, f64>,
121 pub t1_times: Vec<f64>,
123 pub t2_times: Vec<f64>,
125 pub readout_fidelity: Vec<f64>,
127 pub is_available: bool,
129 pub queue_depth: usize,
131 pub cost_per_shot: f64,
133}
134
135impl DeviceInfo {
136 pub fn avg_single_qubit_fidelity(&self) -> f64 {
138 let single_qubit_gates = vec!["X", "Y", "Z", "H", "RX", "RY", "RZ"];
139 let mut sum = 0.0;
140 let mut count = 0;
141
142 for gate in single_qubit_gates {
143 if let Some(&fidelity) = self.gate_fidelities.get(gate) {
144 sum += fidelity;
145 count += 1;
146 }
147 }
148
149 if count > 0 {
150 sum / count as f64
151 } else {
152 0.99 }
154 }
155
156 pub fn avg_two_qubit_fidelity(&self) -> f64 {
158 let two_qubit_gates = vec!["CNOT", "CZ", "SWAP", "iSWAP"];
159 let mut sum = 0.0;
160 let mut count = 0;
161
162 for gate in two_qubit_gates {
163 if let Some(&fidelity) = self.gate_fidelities.get(gate) {
164 sum += fidelity;
165 count += 1;
166 }
167 }
168
169 if count > 0 {
170 sum / count as f64
171 } else {
172 0.95 }
174 }
175
176 pub fn quality_score(&self) -> f64 {
178 let gate_score = (self.avg_single_qubit_fidelity() + self.avg_two_qubit_fidelity()) / 2.0;
179 let readout_score =
180 self.readout_fidelity.iter().sum::<f64>() / self.readout_fidelity.len() as f64;
181 let availability_score = if self.is_available { 1.0 } else { 0.5 };
182 let queue_score = 1.0 / (1.0 + self.queue_depth as f64 / 10.0);
183
184 gate_score * 0.4 + readout_score * 0.3 + availability_score * 0.2 + queue_score * 0.1
185 }
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194pub enum JobStatus {
195 Queued,
197 Running,
199 Completed,
201 Failed,
203 Cancelled,
205}
206
207#[derive(Debug, Clone)]
209pub struct QuantumJob {
210 pub job_id: String,
212 pub platform: CloudPlatform,
214 pub device_name: String,
216 pub status: JobStatus,
218 pub shots: usize,
220 pub submitted_at: SystemTime,
222 pub completed_at: Option<SystemTime>,
224 pub result: Option<JobResult>,
226 pub error_message: Option<String>,
228 pub estimated_cost: f64,
230}
231
232impl QuantumJob {
233 pub fn execution_time(&self) -> Option<Duration> {
235 self.completed_at
236 .and_then(|completed| completed.duration_since(self.submitted_at).ok())
237 }
238
239 pub fn is_finished(&self) -> bool {
241 matches!(
242 self.status,
243 JobStatus::Completed | JobStatus::Failed | JobStatus::Cancelled
244 )
245 }
246}
247
248#[derive(Debug, Clone)]
250pub struct JobResult {
251 pub counts: HashMap<String, usize>,
253 pub expectation_values: Option<Vec<f64>>,
255 pub state_vector: Option<Array1<Complex>>,
257 pub density_matrix: Option<Array2<Complex>>,
259 pub raw_data: Vec<Vec<usize>>,
261 pub metadata: HashMap<String, String>,
263}
264
265impl JobResult {
266 pub fn probabilities(&self) -> HashMap<String, f64> {
268 let total: usize = self.counts.values().sum();
269 self.counts
270 .iter()
271 .map(|(k, v)| (k.clone(), *v as f64 / total as f64))
272 .collect()
273 }
274
275 pub fn most_probable_outcome(&self) -> Option<String> {
277 self.counts
278 .iter()
279 .max_by_key(|(_, count)| *count)
280 .map(|(outcome, _)| outcome.clone())
281 }
282
283 pub fn entropy(&self) -> f64 {
285 let probs = self.probabilities();
286 -probs
287 .values()
288 .filter(|&&p| p > 0.0)
289 .map(|&p| p * p.log2())
290 .sum::<f64>()
291 }
292}
293
294#[derive(Debug, Clone)]
300pub struct CloudConfig {
301 pub platform: CloudPlatform,
303 pub api_token: String,
305 pub endpoint: Option<String>,
307 pub default_shots: usize,
309 pub timeout: u64,
311 pub auto_optimize: bool,
313 pub max_qubits: Option<usize>,
315}
316
317impl Default for CloudConfig {
318 fn default() -> Self {
319 Self {
320 platform: CloudPlatform::IBM,
321 api_token: String::new(),
322 endpoint: None,
323 default_shots: 1000,
324 timeout: 300,
325 auto_optimize: true,
326 max_qubits: None,
327 }
328 }
329}
330
331pub struct CloudClient {
333 config: CloudConfig,
334 devices: Vec<DeviceInfo>,
335}
336
337impl CloudClient {
338 pub fn new(config: CloudConfig) -> Self {
340 Self {
341 config,
342 devices: Vec::new(),
343 }
344 }
345
346 pub fn connect(&mut self) -> QuantRS2Result<()> {
348 if self.config.api_token.is_empty() {
350 return Err(QuantRS2Error::InvalidInput(
351 "API token is required".to_string(),
352 ));
353 }
354
355 self.devices = self.load_devices()?;
357
358 Ok(())
359 }
360
361 fn load_devices(&self) -> QuantRS2Result<Vec<DeviceInfo>> {
363 match self.config.platform {
365 CloudPlatform::IBM => Ok(vec![
366 DeviceInfo {
367 platform: CloudPlatform::IBM,
368 name: "ibmq_jakarta".to_string(),
369 device_type: DeviceType::QPU,
370 num_qubits: 7,
371 connectivity: vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)],
372 gate_set: vec!["X", "Y", "Z", "H", "CNOT", "RZ"]
373 .iter()
374 .map(|s| s.to_string())
375 .collect(),
376 gate_fidelities: HashMap::from([
377 ("X".to_string(), 0.9993),
378 ("CNOT".to_string(), 0.987),
379 ]),
380 t1_times: vec![100.0, 95.0, 110.0, 98.0, 105.0, 92.0, 88.0],
381 t2_times: vec![120.0, 110.0, 115.0, 108.0, 125.0, 105.0, 98.0],
382 readout_fidelity: vec![0.98, 0.97, 0.98, 0.96, 0.97, 0.98, 0.97],
383 is_available: true,
384 queue_depth: 5,
385 cost_per_shot: 0.001,
386 },
387 DeviceInfo {
388 platform: CloudPlatform::IBM,
389 name: "ibmq_qasm_simulator".to_string(),
390 device_type: DeviceType::Simulator,
391 num_qubits: 32,
392 connectivity: vec![], gate_set: vec!["X", "Y", "Z", "H", "CNOT", "RX", "RY", "RZ"]
394 .iter()
395 .map(|s| s.to_string())
396 .collect(),
397 gate_fidelities: HashMap::from([
398 ("X".to_string(), 1.0),
399 ("CNOT".to_string(), 1.0),
400 ]),
401 t1_times: vec![],
402 t2_times: vec![],
403 readout_fidelity: vec![],
404 is_available: true,
405 queue_depth: 0,
406 cost_per_shot: 0.0,
407 },
408 ]),
409 CloudPlatform::AWS => Ok(vec![DeviceInfo {
410 platform: CloudPlatform::AWS,
411 name: "SV1".to_string(),
412 device_type: DeviceType::Simulator,
413 num_qubits: 34,
414 connectivity: vec![],
415 gate_set: vec!["X", "Y", "Z", "H", "CNOT", "RX", "RY", "RZ", "CZ"]
416 .iter()
417 .map(|s| s.to_string())
418 .collect(),
419 gate_fidelities: HashMap::from([("X".to_string(), 1.0), ("CNOT".to_string(), 1.0)]),
420 t1_times: vec![],
421 t2_times: vec![],
422 readout_fidelity: vec![],
423 is_available: true,
424 queue_depth: 0,
425 cost_per_shot: 0.00075,
426 }]),
427 CloudPlatform::Google => Ok(vec![DeviceInfo {
428 platform: CloudPlatform::Google,
429 name: "rainbow".to_string(),
430 device_type: DeviceType::QPU,
431 num_qubits: 23,
432 connectivity: vec![(0, 1), (1, 2), (2, 3)], gate_set: vec!["X", "Y", "Z", "PhasedXZ", "CZ", "SQRT_ISWAP"]
434 .iter()
435 .map(|s| s.to_string())
436 .collect(),
437 gate_fidelities: HashMap::from([
438 ("X".to_string(), 0.9995),
439 ("CZ".to_string(), 0.993),
440 ]),
441 t1_times: vec![15.0; 23],
442 t2_times: vec![20.0; 23],
443 readout_fidelity: vec![0.96; 23],
444 is_available: true,
445 queue_depth: 3,
446 cost_per_shot: 0.002,
447 }]),
448 CloudPlatform::Azure => Ok(vec![DeviceInfo {
449 platform: CloudPlatform::Azure,
450 name: "azure-simulator".to_string(),
451 device_type: DeviceType::Simulator,
452 num_qubits: 40,
453 connectivity: vec![],
454 gate_set: vec!["X", "Y", "Z", "H", "CNOT", "T"]
455 .iter()
456 .map(|s| s.to_string())
457 .collect(),
458 gate_fidelities: HashMap::from([("X".to_string(), 1.0), ("CNOT".to_string(), 1.0)]),
459 t1_times: vec![],
460 t2_times: vec![],
461 readout_fidelity: vec![],
462 is_available: true,
463 queue_depth: 0,
464 cost_per_shot: 0.0005,
465 }]),
466 CloudPlatform::Rigetti => Ok(vec![DeviceInfo {
467 platform: CloudPlatform::Rigetti,
468 name: "Aspen-M-3".to_string(),
469 device_type: DeviceType::QPU,
470 num_qubits: 80,
471 connectivity: vec![(0, 1), (1, 2)], gate_set: vec!["RX", "RZ", "CZ", "XY"]
473 .iter()
474 .map(|s| s.to_string())
475 .collect(),
476 gate_fidelities: HashMap::from([
477 ("RX".to_string(), 0.998),
478 ("CZ".to_string(), 0.95),
479 ]),
480 t1_times: vec![20.0; 80],
481 t2_times: vec![15.0; 80],
482 readout_fidelity: vec![0.95; 80],
483 is_available: true,
484 queue_depth: 8,
485 cost_per_shot: 0.0015,
486 }]),
487 CloudPlatform::IonQ => Ok(vec![DeviceInfo {
488 platform: CloudPlatform::IonQ,
489 name: "ionq.qpu.aria-1".to_string(),
490 device_type: DeviceType::QPU,
491 num_qubits: 25,
492 connectivity: vec![], gate_set: vec!["X", "Y", "Z", "RX", "RY", "RZ", "MS"]
494 .iter()
495 .map(|s| s.to_string())
496 .collect(),
497 gate_fidelities: HashMap::from([
498 ("X".to_string(), 0.9999),
499 ("MS".to_string(), 0.995),
500 ]),
501 t1_times: vec![10000.0; 25], t2_times: vec![1000.0; 25],
503 readout_fidelity: vec![0.995; 25],
504 is_available: true,
505 queue_depth: 12,
506 cost_per_shot: 0.003,
507 }]),
508 }
509 }
510
511 pub fn list_devices(&self) -> &[DeviceInfo] {
513 &self.devices
514 }
515
516 pub fn get_device(&self, name: &str) -> Option<&DeviceInfo> {
518 self.devices.iter().find(|d| d.name == name)
519 }
520
521 pub fn select_best_device(&self, min_qubits: usize, prefer_qpu: bool) -> Option<&DeviceInfo> {
523 self.devices
524 .iter()
525 .filter(|d| {
526 d.num_qubits >= min_qubits
527 && (!prefer_qpu || matches!(d.device_type, DeviceType::QPU))
528 })
529 .max_by(|a, b| a.quality_score().partial_cmp(&b.quality_score()).unwrap())
530 }
531
532 pub fn submit_job(
534 &self,
535 device_name: &str,
536 circuit: &QuantumCircuit,
537 shots: Option<usize>,
538 ) -> QuantRS2Result<QuantumJob> {
539 let device = self.get_device(device_name).ok_or_else(|| {
540 QuantRS2Error::InvalidInput(format!("Device {} not found", device_name))
541 })?;
542
543 let shots = shots.unwrap_or(self.config.default_shots);
544
545 if circuit.num_qubits > device.num_qubits {
547 return Err(QuantRS2Error::InvalidInput(format!(
548 "Circuit requires {} qubits, device only has {}",
549 circuit.num_qubits, device.num_qubits
550 )));
551 }
552
553 let estimated_cost = shots as f64 * device.cost_per_shot;
555
556 Ok(QuantumJob {
558 job_id: format!(
559 "job_{}",
560 SystemTime::now()
561 .duration_since(SystemTime::UNIX_EPOCH)
562 .unwrap()
563 .as_millis()
564 ),
565 platform: self.config.platform,
566 device_name: device_name.to_string(),
567 status: JobStatus::Queued,
568 shots,
569 submitted_at: SystemTime::now(),
570 completed_at: None,
571 result: None,
572 error_message: None,
573 estimated_cost,
574 })
575 }
576
577 pub fn check_job_status(&self, job_id: &str) -> QuantRS2Result<JobStatus> {
579 Ok(JobStatus::Queued)
581 }
582
583 pub fn wait_for_job(
585 &self,
586 job_id: &str,
587 timeout: Option<Duration>,
588 ) -> QuantRS2Result<QuantumJob> {
589 Err(QuantRS2Error::UnsupportedOperation(
591 "Job waiting not implemented in this simplified version".to_string(),
592 ))
593 }
594
595 pub fn get_job_result(&self, job_id: &str) -> QuantRS2Result<JobResult> {
597 Err(QuantRS2Error::UnsupportedOperation(
599 "Job result retrieval not implemented in this simplified version".to_string(),
600 ))
601 }
602
603 pub fn cancel_job(&self, job_id: &str) -> QuantRS2Result<()> {
605 Ok(())
607 }
608
609 pub fn list_jobs(&self, limit: Option<usize>) -> QuantRS2Result<Vec<QuantumJob>> {
611 Ok(Vec::new())
613 }
614}
615
616#[derive(Debug, Clone)]
618pub struct QuantumCircuit {
619 pub num_qubits: usize,
621 pub gates: Vec<Box<dyn GateOp>>,
623 pub measurements: Vec<usize>,
625}
626
627impl QuantumCircuit {
628 pub fn new(num_qubits: usize) -> Self {
630 Self {
631 num_qubits,
632 gates: Vec::new(),
633 measurements: Vec::new(),
634 }
635 }
636
637 pub fn add_gate(&mut self, gate: Box<dyn GateOp>) {
639 self.gates.push(gate);
640 }
641
642 pub fn measure(&mut self, qubit: usize) {
644 if qubit < self.num_qubits {
645 self.measurements.push(qubit);
646 }
647 }
648
649 pub fn measure_all(&mut self) {
651 self.measurements = (0..self.num_qubits).collect();
652 }
653
654 pub fn depth(&self) -> usize {
656 self.gates.len()
658 }
659
660 pub fn gate_counts(&self) -> HashMap<String, usize> {
662 let mut counts = HashMap::new();
663 for gate in &self.gates {
664 *counts.entry(gate.name().to_string()).or_insert(0) += 1;
665 }
666 counts
667 }
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673
674 #[test]
675 fn test_cloud_platform_names() {
676 assert_eq!(CloudPlatform::IBM.name(), "IBM Quantum");
677 assert_eq!(CloudPlatform::AWS.name(), "AWS Braket");
678 assert_eq!(CloudPlatform::Google.name(), "Google Quantum AI");
679 }
680
681 #[test]
682 fn test_device_quality_score() {
683 let device = DeviceInfo {
684 platform: CloudPlatform::IBM,
685 name: "test_device".to_string(),
686 device_type: DeviceType::QPU,
687 num_qubits: 5,
688 connectivity: vec![],
689 gate_set: vec![],
690 gate_fidelities: HashMap::from([("X".to_string(), 0.999), ("CNOT".to_string(), 0.99)]),
691 t1_times: vec![],
692 t2_times: vec![],
693 readout_fidelity: vec![0.95, 0.96, 0.97, 0.98, 0.99],
694 is_available: true,
695 queue_depth: 5,
696 cost_per_shot: 0.001,
697 };
698
699 let score = device.quality_score();
700 assert!(score > 0.8 && score < 1.0);
701 }
702
703 #[test]
704 fn test_job_result_probabilities() {
705 let result = JobResult {
706 counts: HashMap::from([
707 ("00".to_string(), 500),
708 ("01".to_string(), 250),
709 ("10".to_string(), 150),
710 ("11".to_string(), 100),
711 ]),
712 expectation_values: None,
713 state_vector: None,
714 density_matrix: None,
715 raw_data: vec![],
716 metadata: HashMap::new(),
717 };
718
719 let probs = result.probabilities();
720 assert_eq!(probs.get("00"), Some(&0.5));
721 assert_eq!(probs.get("01"), Some(&0.25));
722
723 let most_probable = result.most_probable_outcome().unwrap();
724 assert_eq!(most_probable, "00");
725 }
726
727 #[test]
728 fn test_quantum_circuit() {
729 let mut circuit = QuantumCircuit::new(2);
730 assert_eq!(circuit.num_qubits, 2);
731 assert_eq!(circuit.gates.len(), 0);
732
733 circuit.measure_all();
734 assert_eq!(circuit.measurements.len(), 2);
735 }
736}