1use crate::builder::Circuit;
7use crate::noise_models::NoiseModel;
8use crate::routing::CouplingMap;
9use crate::transpiler::{HardwareSpec, NativeGateSet};
10use quantrs2_core::{
11 error::{QuantRS2Error, QuantRS2Result},
12 gate::GateOp,
13 qubit::QubitId,
14};
15use serde::{Deserialize, Serialize};
16use std::collections::{HashMap, HashSet};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ValidationRules {
21 pub backend_name: String,
23 pub max_qubits: usize,
25 pub connectivity: ConnectivityConstraints,
27 pub gate_restrictions: GateRestrictions,
29 pub depth_limits: DepthLimits,
31 pub measurement_constraints: MeasurementConstraints,
33 pub classical_constraints: ClassicalConstraints,
35 pub resource_limits: ResourceLimits,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ConnectivityConstraints {
42 pub coupling_map: Option<CouplingMap>,
44 pub all_to_all: bool,
46 pub max_distance: Option<usize>,
48 pub forbidden_pairs: HashSet<(usize, usize)>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct GateRestrictions {
55 pub native_gates: NativeGateSet,
57 pub require_native: bool,
59 pub max_parameters: usize,
61 pub forbidden_sequences: Vec<Vec<String>>,
63 pub gate_qubit_restrictions: HashMap<String, HashSet<usize>>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DepthLimits {
70 pub max_depth: Option<usize>,
72 pub max_execution_time: Option<f64>,
74 pub max_gates: Option<usize>,
76 pub gate_type_limits: HashMap<String, usize>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct MeasurementConstraints {
83 pub allow_mid_circuit: bool,
85 pub max_measurements: Option<usize>,
87 pub allow_conditional: bool,
89 pub required_basis: Option<String>,
91 pub non_measurable_qubits: HashSet<usize>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct ClassicalConstraints {
98 pub allow_classical_control: bool,
100 pub max_classical_registers: Option<usize>,
102 pub allow_feedback: bool,
104 pub max_conditional_depth: Option<usize>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ResourceLimits {
111 pub max_memory_mb: Option<usize>,
113 pub max_shots: Option<usize>,
115 pub max_runtime_seconds: Option<usize>,
117 pub priority_constraints: Option<PriorityConstraints>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct PriorityConstraints {
124 pub min_priority: u32,
126 pub max_queue_position: Option<usize>,
128 pub time_restrictions: Option<TimeRestrictions>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct TimeRestrictions {
135 pub allowed_hours: HashSet<u8>,
137 pub maintenance_windows: Vec<(String, String)>,
139 pub duration_limits: HashMap<u8, usize>,
141}
142
143#[derive(Debug, Clone)]
145pub struct ValidationResult {
146 pub is_valid: bool,
148 pub errors: Vec<ValidationError>,
150 pub warnings: Vec<ValidationWarning>,
152 pub stats: ValidationStats,
154 pub suggestions: Vec<ValidationSuggestion>,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub enum ValidationError {
161 ExceedsQubitLimit { required: usize, available: usize },
163 UnsupportedGate { gate_name: String, position: usize },
165 ConnectivityViolation {
167 gate_name: String,
168 qubits: Vec<usize>,
169 position: usize,
170 },
171 DepthLimitExceeded {
173 actual_depth: usize,
174 max_depth: usize,
175 },
176 GateCountExceeded {
178 actual_count: usize,
179 max_count: usize,
180 },
181 MeasurementViolation {
183 violation_type: String,
184 details: String,
185 },
186 ClassicalControlViolation {
188 violation_type: String,
189 details: String,
190 },
191 ResourceLimitExceeded {
193 resource_type: String,
194 required: usize,
195 available: usize,
196 },
197 InvalidGateSequence {
199 sequence: Vec<String>,
200 position: usize,
201 },
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206pub enum ValidationWarning {
207 SuboptimalGateUsage {
209 gate_name: String,
210 suggested_alternative: String,
211 positions: Vec<usize>,
212 },
213 HighErrorRate {
215 estimated_error: f64,
216 threshold: f64,
217 },
218 LongExecutionTime {
220 estimated_time: f64,
221 recommended_max: f64,
222 },
223 ResourceUsageWarning {
225 resource_type: String,
226 usage_percentage: f64,
227 },
228}
229
230#[derive(Debug, Clone)]
232pub struct ValidationStats {
233 pub validation_time: std::time::Duration,
235 pub gates_checked: usize,
237 pub constraints_evaluated: usize,
239 pub estimated_fidelity: Option<f64>,
241 pub estimated_execution_time: Option<f64>,
243}
244
245#[derive(Debug, Clone)]
247pub enum ValidationSuggestion {
248 UseTranspilation { suggested_router: String },
250 DecomposeGates { gates_to_decompose: Vec<String> },
252 ReduceDepth { suggested_passes: Vec<String> },
254 SplitCircuit { suggested_split_points: Vec<usize> },
256 SwitchBackend { recommended_backends: Vec<String> },
258}
259
260pub struct CircuitValidator {
262 backend_rules: HashMap<String, ValidationRules>,
264 validation_cache: HashMap<String, ValidationResult>,
266}
267
268impl CircuitValidator {
269 pub fn new() -> Self {
271 let mut validator = Self {
272 backend_rules: HashMap::new(),
273 validation_cache: HashMap::new(),
274 };
275
276 validator.load_standard_backends();
278 validator
279 }
280
281 pub fn add_backend_rules(&mut self, rules: ValidationRules) {
283 self.backend_rules.insert(rules.backend_name.clone(), rules);
284 }
285
286 pub fn available_backends(&self) -> Vec<String> {
288 self.backend_rules.keys().cloned().collect()
289 }
290
291 pub fn validate<const N: usize>(
293 &mut self,
294 circuit: &Circuit<N>,
295 backend: &str,
296 noise_model: Option<&NoiseModel>,
297 ) -> QuantRS2Result<ValidationResult> {
298 let start_time = std::time::Instant::now();
299
300 let rules = self
302 .backend_rules
303 .get(backend)
304 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown backend: {}", backend)))?;
305
306 let mut errors = Vec::new();
307 let mut warnings = Vec::new();
308 let mut suggestions = Vec::new();
309
310 if N > rules.max_qubits {
312 errors.push(ValidationError::ExceedsQubitLimit {
313 required: N,
314 available: rules.max_qubits,
315 });
316 suggestions.push(ValidationSuggestion::SwitchBackend {
317 recommended_backends: self.find_backends_with_qubits(N),
318 });
319 }
320
321 self.validate_gate_set(circuit, rules, &mut errors, &mut warnings, &mut suggestions)?;
323
324 self.validate_connectivity(circuit, rules, &mut errors, &mut suggestions)?;
326
327 self.validate_depth_limits(circuit, rules, &mut errors, &mut warnings, &mut suggestions)?;
329
330 self.validate_measurements(circuit, rules, &mut errors, &mut warnings)?;
332
333 self.validate_resources(circuit, rules, &mut errors, &mut warnings)?;
335
336 let estimated_fidelity = if let Some(noise) = noise_model {
338 Some(self.estimate_fidelity(circuit, noise)?)
339 } else {
340 None
341 };
342
343 let validation_time = start_time.elapsed();
344 let is_valid = errors.is_empty();
345
346 let result = ValidationResult {
347 is_valid,
348 errors,
349 warnings,
350 stats: ValidationStats {
351 validation_time,
352 gates_checked: circuit.gates().len(),
353 constraints_evaluated: self.count_constraints(rules),
354 estimated_fidelity,
355 estimated_execution_time: Some(self.estimate_execution_time(circuit, rules)),
356 },
357 suggestions,
358 };
359
360 Ok(result)
361 }
362
363 fn validate_gate_set<const N: usize>(
365 &self,
366 circuit: &Circuit<N>,
367 rules: &ValidationRules,
368 errors: &mut Vec<ValidationError>,
369 warnings: &mut Vec<ValidationWarning>,
370 suggestions: &mut Vec<ValidationSuggestion>,
371 ) -> QuantRS2Result<()> {
372 let mut non_native_gates = Vec::new();
373 let mut invalid_sequences: Vec<String> = Vec::new();
374
375 for (i, gate) in circuit.gates().iter().enumerate() {
376 let gate_name = gate.name();
377 let qubit_count = gate.qubits().len();
378
379 let is_native = match qubit_count {
381 1 => rules
382 .gate_restrictions
383 .native_gates
384 .single_qubit
385 .contains(gate_name),
386 2 => rules
387 .gate_restrictions
388 .native_gates
389 .two_qubit
390 .contains(gate_name),
391 _ => rules
392 .gate_restrictions
393 .native_gates
394 .multi_qubit
395 .contains(gate_name),
396 };
397
398 if !is_native {
399 if rules.gate_restrictions.require_native {
400 errors.push(ValidationError::UnsupportedGate {
401 gate_name: gate_name.to_string(),
402 position: i,
403 });
404 } else {
405 non_native_gates.push(gate_name.to_string());
406 }
407 }
408
409 if let Some(allowed_qubits) = rules
411 .gate_restrictions
412 .gate_qubit_restrictions
413 .get(gate_name)
414 {
415 for qubit in gate.qubits() {
416 let qubit_id = qubit.id() as usize;
417 if !allowed_qubits.contains(&qubit_id) {
418 errors.push(ValidationError::ConnectivityViolation {
419 gate_name: gate_name.to_string(),
420 qubits: vec![qubit_id],
421 position: i,
422 });
423 }
424 }
425 }
426 }
427
428 if !non_native_gates.is_empty() {
430 suggestions.push(ValidationSuggestion::DecomposeGates {
431 gates_to_decompose: non_native_gates,
432 });
433 }
434
435 Ok(())
436 }
437
438 fn validate_connectivity<const N: usize>(
440 &self,
441 circuit: &Circuit<N>,
442 rules: &ValidationRules,
443 errors: &mut Vec<ValidationError>,
444 suggestions: &mut Vec<ValidationSuggestion>,
445 ) -> QuantRS2Result<()> {
446 if rules.connectivity.all_to_all {
447 return Ok(()); }
449
450 let coupling_map = rules.connectivity.coupling_map.as_ref();
451 let mut connectivity_violations = false;
452
453 for (i, gate) in circuit.gates().iter().enumerate() {
454 if gate.qubits().len() >= 2 {
455 let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
456
457 if gate.qubits().len() == 2 {
459 let q1 = qubits[0];
460 let q2 = qubits[1];
461
462 if rules.connectivity.forbidden_pairs.contains(&(q1, q2))
463 || rules.connectivity.forbidden_pairs.contains(&(q2, q1))
464 {
465 errors.push(ValidationError::ConnectivityViolation {
466 gate_name: gate.name().to_string(),
467 qubits: vec![q1, q2],
468 position: i,
469 });
470 connectivity_violations = true;
471 }
472
473 if let Some(coupling) = coupling_map {
474 if !coupling.are_connected(q1, q2) {
475 errors.push(ValidationError::ConnectivityViolation {
476 gate_name: gate.name().to_string(),
477 qubits: vec![q1, q2],
478 position: i,
479 });
480 connectivity_violations = true;
481 }
482 }
483 }
484
485 if let Some(max_dist) = rules.connectivity.max_distance {
487 if let Some(coupling) = coupling_map {
488 for i in 0..qubits.len() {
489 for j in i + 1..qubits.len() {
490 let distance = coupling.distance(qubits[i], qubits[j]);
491 if distance > max_dist {
492 errors.push(ValidationError::ConnectivityViolation {
493 gate_name: gate.name().to_string(),
494 qubits: vec![qubits[i], qubits[j]],
495 position: i,
496 });
497 connectivity_violations = true;
498 }
499 }
500 }
501 }
502 }
503 }
504 }
505
506 if connectivity_violations {
507 suggestions.push(ValidationSuggestion::UseTranspilation {
508 suggested_router: "SABRE".to_string(),
509 });
510 }
511
512 Ok(())
513 }
514
515 fn validate_depth_limits<const N: usize>(
517 &self,
518 circuit: &Circuit<N>,
519 rules: &ValidationRules,
520 errors: &mut Vec<ValidationError>,
521 warnings: &mut Vec<ValidationWarning>,
522 suggestions: &mut Vec<ValidationSuggestion>,
523 ) -> QuantRS2Result<()> {
524 let circuit_depth = self.calculate_circuit_depth(circuit);
525 let gate_count = circuit.gates().len();
526
527 if let Some(max_depth) = rules.depth_limits.max_depth {
529 if circuit_depth > max_depth {
530 errors.push(ValidationError::DepthLimitExceeded {
531 actual_depth: circuit_depth,
532 max_depth,
533 });
534 suggestions.push(ValidationSuggestion::ReduceDepth {
535 suggested_passes: vec![
536 "GateCommutation".to_string(),
537 "GateCancellation".to_string(),
538 ],
539 });
540 }
541 }
542
543 if let Some(max_gates) = rules.depth_limits.max_gates {
545 if gate_count > max_gates {
546 errors.push(ValidationError::GateCountExceeded {
547 actual_count: gate_count,
548 max_count: max_gates,
549 });
550 suggestions.push(ValidationSuggestion::SplitCircuit {
551 suggested_split_points: vec![max_gates / 2],
552 });
553 }
554 }
555
556 if let Some(max_time) = rules.depth_limits.max_execution_time {
558 let estimated_time = self.estimate_execution_time(circuit, rules);
559 if estimated_time > max_time {
560 warnings.push(ValidationWarning::LongExecutionTime {
561 estimated_time,
562 recommended_max: max_time,
563 });
564 }
565 }
566
567 Ok(())
568 }
569
570 fn validate_measurements<const N: usize>(
572 &self,
573 circuit: &Circuit<N>,
574 rules: &ValidationRules,
575 errors: &mut Vec<ValidationError>,
576 warnings: &mut Vec<ValidationWarning>,
577 ) -> QuantRS2Result<()> {
578 Ok(())
581 }
582
583 fn validate_resources<const N: usize>(
585 &self,
586 circuit: &Circuit<N>,
587 rules: &ValidationRules,
588 errors: &mut Vec<ValidationError>,
589 warnings: &mut Vec<ValidationWarning>,
590 ) -> QuantRS2Result<()> {
591 let estimated_memory = self.estimate_memory_usage(circuit);
593
594 if let Some(max_memory) = rules.resource_limits.max_memory_mb {
595 let estimated_memory_mb = estimated_memory / (1024 * 1024);
596 if estimated_memory_mb > max_memory {
597 errors.push(ValidationError::ResourceLimitExceeded {
598 resource_type: "memory".to_string(),
599 required: estimated_memory_mb,
600 available: max_memory,
601 });
602 } else if estimated_memory_mb as f64 > max_memory as f64 * 0.8 {
603 warnings.push(ValidationWarning::ResourceUsageWarning {
604 resource_type: "memory".to_string(),
605 usage_percentage: (estimated_memory_mb as f64 / max_memory as f64) * 100.0,
606 });
607 }
608 }
609
610 Ok(())
611 }
612
613 fn calculate_circuit_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
615 circuit.gates().len()
617 }
618
619 fn estimate_execution_time<const N: usize>(
621 &self,
622 circuit: &Circuit<N>,
623 rules: &ValidationRules,
624 ) -> f64 {
625 circuit.gates().len() as f64 * 0.1 }
628
629 fn estimate_memory_usage<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
631 if N <= 30 {
633 (1usize << N) * 16 } else {
635 usize::MAX }
637 }
638
639 fn estimate_fidelity<const N: usize>(
641 &self,
642 circuit: &Circuit<N>,
643 noise_model: &NoiseModel,
644 ) -> QuantRS2Result<f64> {
645 let mut total_error = 0.0;
647
648 for gate in circuit.gates() {
649 let gate_name = gate.name();
650 let error = match gate.qubits().len() {
651 1 => noise_model
652 .single_qubit_errors
653 .get(gate_name)
654 .map(|e| e.depolarizing + e.amplitude_damping + e.phase_damping)
655 .unwrap_or(0.001),
656 2 => noise_model
657 .two_qubit_errors
658 .get(gate_name)
659 .map(|e| e.depolarizing)
660 .unwrap_or(0.01),
661 _ => 0.05, };
663 total_error += error;
664 }
665
666 Ok((1.0 - total_error).max(0.0))
667 }
668
669 fn count_constraints(&self, rules: &ValidationRules) -> usize {
671 let mut count = 0;
672
673 count += 1; count += rules.gate_restrictions.native_gates.single_qubit.len();
675 count += rules.gate_restrictions.native_gates.two_qubit.len();
676 count += rules.gate_restrictions.native_gates.multi_qubit.len();
677
678 if rules.depth_limits.max_depth.is_some() {
679 count += 1;
680 }
681 if rules.depth_limits.max_gates.is_some() {
682 count += 1;
683 }
684 if rules.depth_limits.max_execution_time.is_some() {
685 count += 1;
686 }
687
688 count
689 }
690
691 fn find_backends_with_qubits(&self, required_qubits: usize) -> Vec<String> {
693 self.backend_rules
694 .iter()
695 .filter(|(_, rules)| rules.max_qubits >= required_qubits)
696 .map(|(name, _)| name.clone())
697 .collect()
698 }
699
700 fn load_standard_backends(&mut self) {
702 self.add_backend_rules(ValidationRules::ibm_quantum());
704
705 self.add_backend_rules(ValidationRules::google_quantum());
707
708 self.add_backend_rules(ValidationRules::aws_braket());
710
711 self.add_backend_rules(ValidationRules::simulator());
713 }
714}
715
716impl ValidationRules {
717 pub fn ibm_quantum() -> Self {
719 let native_gates = NativeGateSet {
720 single_qubit: ["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY"]
721 .iter()
722 .map(|s| s.to_string())
723 .collect(),
724 two_qubit: ["CNOT", "CZ"].iter().map(|s| s.to_string()).collect(),
725 multi_qubit: HashSet::new(),
726 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
727 .iter()
728 .map(|(k, v)| (k.to_string(), *v))
729 .collect(),
730 };
731
732 Self {
733 backend_name: "ibm_quantum".to_string(),
734 max_qubits: 127,
735 connectivity: ConnectivityConstraints {
736 coupling_map: Some(CouplingMap::grid(11, 12)), all_to_all: false,
738 max_distance: Some(10),
739 forbidden_pairs: HashSet::new(),
740 },
741 gate_restrictions: GateRestrictions {
742 native_gates,
743 require_native: true,
744 max_parameters: 3,
745 forbidden_sequences: Vec::new(),
746 gate_qubit_restrictions: HashMap::new(),
747 },
748 depth_limits: DepthLimits {
749 max_depth: Some(10000),
750 max_execution_time: Some(100000.0), max_gates: Some(50000),
752 gate_type_limits: HashMap::new(),
753 },
754 measurement_constraints: MeasurementConstraints {
755 allow_mid_circuit: true,
756 max_measurements: Some(1000),
757 allow_conditional: true,
758 required_basis: None,
759 non_measurable_qubits: HashSet::new(),
760 },
761 classical_constraints: ClassicalConstraints {
762 allow_classical_control: true,
763 max_classical_registers: Some(100),
764 allow_feedback: true,
765 max_conditional_depth: Some(100),
766 },
767 resource_limits: ResourceLimits {
768 max_memory_mb: Some(8192),
769 max_shots: Some(100000),
770 max_runtime_seconds: Some(3600),
771 priority_constraints: None,
772 },
773 }
774 }
775
776 pub fn google_quantum() -> Self {
778 let native_gates = NativeGateSet {
779 single_qubit: ["X", "Y", "Z", "H", "RZ", "SQRT_X"]
780 .iter()
781 .map(|s| s.to_string())
782 .collect(),
783 two_qubit: ["CZ", "ISWAP"].iter().map(|s| s.to_string()).collect(),
784 multi_qubit: HashSet::new(),
785 parameterized: [("RZ", 1)]
786 .iter()
787 .map(|(k, v)| (k.to_string(), *v))
788 .collect(),
789 };
790
791 Self {
792 backend_name: "google_quantum".to_string(),
793 max_qubits: 70,
794 connectivity: ConnectivityConstraints {
795 coupling_map: Some(CouplingMap::grid(8, 9)),
796 all_to_all: false,
797 max_distance: Some(5),
798 forbidden_pairs: HashSet::new(),
799 },
800 gate_restrictions: GateRestrictions {
801 native_gates,
802 require_native: true,
803 max_parameters: 1,
804 forbidden_sequences: Vec::new(),
805 gate_qubit_restrictions: HashMap::new(),
806 },
807 depth_limits: DepthLimits {
808 max_depth: Some(5000),
809 max_execution_time: Some(50000.0),
810 max_gates: Some(20000),
811 gate_type_limits: HashMap::new(),
812 },
813 measurement_constraints: MeasurementConstraints {
814 allow_mid_circuit: false,
815 max_measurements: Some(70),
816 allow_conditional: false,
817 required_basis: Some("Z".to_string()),
818 non_measurable_qubits: HashSet::new(),
819 },
820 classical_constraints: ClassicalConstraints {
821 allow_classical_control: false,
822 max_classical_registers: Some(10),
823 allow_feedback: false,
824 max_conditional_depth: None,
825 },
826 resource_limits: ResourceLimits {
827 max_memory_mb: Some(4096),
828 max_shots: Some(50000),
829 max_runtime_seconds: Some(1800),
830 priority_constraints: None,
831 },
832 }
833 }
834
835 pub fn aws_braket() -> Self {
837 let native_gates = NativeGateSet {
838 single_qubit: ["X", "Y", "Z", "H", "RZ", "RX", "RY"]
839 .iter()
840 .map(|s| s.to_string())
841 .collect(),
842 two_qubit: ["CNOT", "CZ", "ISWAP"]
843 .iter()
844 .map(|s| s.to_string())
845 .collect(),
846 multi_qubit: HashSet::new(),
847 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
848 .iter()
849 .map(|(k, v)| (k.to_string(), *v))
850 .collect(),
851 };
852
853 Self {
854 backend_name: "aws_braket".to_string(),
855 max_qubits: 100,
856 connectivity: ConnectivityConstraints {
857 coupling_map: None, all_to_all: true,
859 max_distance: None,
860 forbidden_pairs: HashSet::new(),
861 },
862 gate_restrictions: GateRestrictions {
863 native_gates,
864 require_native: false,
865 max_parameters: 5,
866 forbidden_sequences: Vec::new(),
867 gate_qubit_restrictions: HashMap::new(),
868 },
869 depth_limits: DepthLimits {
870 max_depth: None,
871 max_execution_time: Some(200000.0),
872 max_gates: None,
873 gate_type_limits: HashMap::new(),
874 },
875 measurement_constraints: MeasurementConstraints {
876 allow_mid_circuit: true,
877 max_measurements: None,
878 allow_conditional: true,
879 required_basis: None,
880 non_measurable_qubits: HashSet::new(),
881 },
882 classical_constraints: ClassicalConstraints {
883 allow_classical_control: true,
884 max_classical_registers: None,
885 allow_feedback: true,
886 max_conditional_depth: None,
887 },
888 resource_limits: ResourceLimits {
889 max_memory_mb: Some(16384),
890 max_shots: Some(1000000),
891 max_runtime_seconds: Some(7200),
892 priority_constraints: None,
893 },
894 }
895 }
896
897 pub fn simulator() -> Self {
899 let native_gates = NativeGateSet {
900 single_qubit: ["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY", "U"]
901 .iter()
902 .map(|s| s.to_string())
903 .collect(),
904 two_qubit: ["CNOT", "CZ", "ISWAP", "SWAP", "CX"]
905 .iter()
906 .map(|s| s.to_string())
907 .collect(),
908 multi_qubit: ["Toffoli", "Fredkin"]
909 .iter()
910 .map(|s| s.to_string())
911 .collect(),
912 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1), ("U", 3)]
913 .iter()
914 .map(|(k, v)| (k.to_string(), *v))
915 .collect(),
916 };
917
918 Self {
919 backend_name: "simulator".to_string(),
920 max_qubits: 30, connectivity: ConnectivityConstraints {
922 coupling_map: None,
923 all_to_all: true,
924 max_distance: None,
925 forbidden_pairs: HashSet::new(),
926 },
927 gate_restrictions: GateRestrictions {
928 native_gates,
929 require_native: false,
930 max_parameters: 10,
931 forbidden_sequences: Vec::new(),
932 gate_qubit_restrictions: HashMap::new(),
933 },
934 depth_limits: DepthLimits {
935 max_depth: None,
936 max_execution_time: None,
937 max_gates: None,
938 gate_type_limits: HashMap::new(),
939 },
940 measurement_constraints: MeasurementConstraints {
941 allow_mid_circuit: true,
942 max_measurements: None,
943 allow_conditional: true,
944 required_basis: None,
945 non_measurable_qubits: HashSet::new(),
946 },
947 classical_constraints: ClassicalConstraints {
948 allow_classical_control: true,
949 max_classical_registers: None,
950 allow_feedback: true,
951 max_conditional_depth: None,
952 },
953 resource_limits: ResourceLimits {
954 max_memory_mb: None,
955 max_shots: None,
956 max_runtime_seconds: None,
957 priority_constraints: None,
958 },
959 }
960 }
961}
962
963impl Default for CircuitValidator {
964 fn default() -> Self {
965 Self::new()
966 }
967}
968
969#[cfg(test)]
970mod tests {
971 use super::*;
972 use quantrs2_core::gate::multi::CNOT;
973 use quantrs2_core::gate::single::Hadamard;
974
975 #[test]
976 fn test_validator_creation() {
977 let validator = CircuitValidator::new();
978 assert!(!validator.available_backends().is_empty());
979 }
980
981 #[test]
982 fn test_validation_rules_creation() {
983 let rules = ValidationRules::ibm_quantum();
984 assert_eq!(rules.backend_name, "ibm_quantum");
985 assert_eq!(rules.max_qubits, 127);
986 }
987
988 #[test]
989 fn test_simple_circuit_validation() {
990 let mut validator = CircuitValidator::new();
991 let mut circuit = Circuit::<2>::new();
992 circuit.add_gate(Hadamard { target: QubitId(0) }).unwrap();
993
994 let result = validator.validate(&circuit, "simulator", None).unwrap();
995 assert!(result.is_valid);
996 }
997
998 #[test]
999 fn test_qubit_limit_validation() {
1000 let mut validator = CircuitValidator::new();
1001 let circuit = Circuit::<200>::new(); let result = validator.validate(&circuit, "ibm_quantum", None).unwrap();
1004 assert!(!result.is_valid);
1005 assert!(!result.errors.is_empty());
1006 }
1007
1008 #[test]
1009 fn test_connectivity_validation() {
1010 let mut validator = CircuitValidator::new();
1011 let mut circuit = Circuit::<3>::new();
1012 circuit
1014 .add_gate(CNOT {
1015 control: QubitId(0),
1016 target: QubitId(1),
1017 })
1018 .unwrap();
1019
1020 let result = validator.validate(&circuit, "ibm_quantum", None).unwrap();
1021 }
1023}