1use crate::builder::Circuit;
6use crate::noise_models::NoiseModel;
7use crate::routing::CouplingMap;
8use crate::transpiler::{HardwareSpec, NativeGateSet};
9use quantrs2_core::{
10 error::{QuantRS2Error, QuantRS2Result},
11 gate::GateOp,
12 qubit::QubitId,
13};
14use serde::{Deserialize, Serialize};
15use std::collections::{HashMap, HashSet};
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ValidationRules {
19 pub backend_name: String,
21 pub max_qubits: usize,
23 pub connectivity: ConnectivityConstraints,
25 pub gate_restrictions: GateRestrictions,
27 pub depth_limits: DepthLimits,
29 pub measurement_constraints: MeasurementConstraints,
31 pub classical_constraints: ClassicalConstraints,
33 pub resource_limits: ResourceLimits,
35}
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ConnectivityConstraints {
39 pub coupling_map: Option<CouplingMap>,
41 pub all_to_all: bool,
43 pub max_distance: Option<usize>,
45 pub forbidden_pairs: HashSet<(usize, usize)>,
47}
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct GateRestrictions {
51 pub native_gates: NativeGateSet,
53 pub require_native: bool,
55 pub max_parameters: usize,
57 pub forbidden_sequences: Vec<Vec<String>>,
59 pub gate_qubit_restrictions: HashMap<String, HashSet<usize>>,
61}
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct DepthLimits {
65 pub max_depth: Option<usize>,
67 pub max_execution_time: Option<f64>,
69 pub max_gates: Option<usize>,
71 pub gate_type_limits: HashMap<String, usize>,
73}
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct MeasurementConstraints {
77 pub allow_mid_circuit: bool,
79 pub max_measurements: Option<usize>,
81 pub allow_conditional: bool,
83 pub required_basis: Option<String>,
85 pub non_measurable_qubits: HashSet<usize>,
87}
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct ClassicalConstraints {
91 pub allow_classical_control: bool,
93 pub max_classical_registers: Option<usize>,
95 pub allow_feedback: bool,
97 pub max_conditional_depth: Option<usize>,
99}
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ResourceLimits {
103 pub max_memory_mb: Option<usize>,
105 pub max_shots: Option<usize>,
107 pub max_runtime_seconds: Option<usize>,
109 pub priority_constraints: Option<PriorityConstraints>,
111}
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct PriorityConstraints {
115 pub min_priority: u32,
117 pub max_queue_position: Option<usize>,
119 pub time_restrictions: Option<TimeRestrictions>,
121}
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct TimeRestrictions {
125 pub allowed_hours: HashSet<u8>,
127 pub maintenance_windows: Vec<(String, String)>,
129 pub duration_limits: HashMap<u8, usize>,
131}
132#[derive(Debug, Clone)]
134pub struct ValidationResult {
135 pub is_valid: bool,
137 pub errors: Vec<ValidationError>,
139 pub warnings: Vec<ValidationWarning>,
141 pub stats: ValidationStats,
143 pub suggestions: Vec<ValidationSuggestion>,
145}
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub enum ValidationError {
149 ExceedsQubitLimit { required: usize, available: usize },
151 UnsupportedGate { gate_name: String, position: usize },
153 ConnectivityViolation {
155 gate_name: String,
156 qubits: Vec<usize>,
157 position: usize,
158 },
159 DepthLimitExceeded {
161 actual_depth: usize,
162 max_depth: usize,
163 },
164 GateCountExceeded {
166 actual_count: usize,
167 max_count: usize,
168 },
169 MeasurementViolation {
171 violation_type: String,
172 details: String,
173 },
174 ClassicalControlViolation {
176 violation_type: String,
177 details: String,
178 },
179 ResourceLimitExceeded {
181 resource_type: String,
182 required: usize,
183 available: usize,
184 },
185 InvalidGateSequence {
187 sequence: Vec<String>,
188 position: usize,
189 },
190}
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub enum ValidationWarning {
194 SuboptimalGateUsage {
196 gate_name: String,
197 suggested_alternative: String,
198 positions: Vec<usize>,
199 },
200 HighErrorRate {
202 estimated_error: f64,
203 threshold: f64,
204 },
205 LongExecutionTime {
207 estimated_time: f64,
208 recommended_max: f64,
209 },
210 ResourceUsageWarning {
212 resource_type: String,
213 usage_percentage: f64,
214 },
215}
216#[derive(Debug, Clone)]
218pub struct ValidationStats {
219 pub validation_time: std::time::Duration,
221 pub gates_checked: usize,
223 pub constraints_evaluated: usize,
225 pub estimated_fidelity: Option<f64>,
227 pub estimated_execution_time: Option<f64>,
229}
230#[derive(Debug, Clone)]
232pub enum ValidationSuggestion {
233 UseTranspilation { suggested_router: String },
235 DecomposeGates { gates_to_decompose: Vec<String> },
237 ReduceDepth { suggested_passes: Vec<String> },
239 SplitCircuit { suggested_split_points: Vec<usize> },
241 SwitchBackend { recommended_backends: Vec<String> },
243}
244pub struct CircuitValidator {
246 backend_rules: HashMap<String, ValidationRules>,
248 validation_cache: HashMap<String, ValidationResult>,
250}
251impl CircuitValidator {
252 #[must_use]
254 pub fn new() -> Self {
255 let mut validator = Self {
256 backend_rules: HashMap::new(),
257 validation_cache: HashMap::new(),
258 };
259 validator.load_standard_backends();
260 validator
261 }
262 pub fn add_backend_rules(&mut self, rules: ValidationRules) {
264 self.backend_rules.insert(rules.backend_name.clone(), rules);
265 }
266 #[must_use]
268 pub fn available_backends(&self) -> Vec<String> {
269 self.backend_rules.keys().cloned().collect()
270 }
271 pub fn validate<const N: usize>(
273 &mut self,
274 circuit: &Circuit<N>,
275 backend: &str,
276 noise_model: Option<&NoiseModel>,
277 ) -> QuantRS2Result<ValidationResult> {
278 let start_time = std::time::Instant::now();
279 let rules = self
280 .backend_rules
281 .get(backend)
282 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown backend: {backend}")))?;
283 let mut errors = Vec::new();
284 let mut warnings = Vec::new();
285 let mut suggestions = Vec::new();
286 if N > rules.max_qubits {
287 errors.push(ValidationError::ExceedsQubitLimit {
288 required: N,
289 available: rules.max_qubits,
290 });
291 suggestions.push(ValidationSuggestion::SwitchBackend {
292 recommended_backends: self.find_backends_with_qubits(N),
293 });
294 }
295 Self::validate_gate_set(circuit, rules, &mut errors, &warnings, &mut suggestions)?;
296 Self::validate_connectivity(circuit, rules, &mut errors, &mut suggestions)?;
297 self.validate_depth_limits(circuit, rules, &mut errors, &mut warnings, &mut suggestions)?;
298 Self::validate_measurements(circuit, rules, &errors, &warnings)?;
299 self.validate_resources(circuit, rules, &mut errors, &mut warnings)?;
300 let estimated_fidelity = if let Some(noise) = noise_model {
301 Some(Self::estimate_fidelity(circuit, noise)?)
302 } else {
303 None
304 };
305 let validation_time = start_time.elapsed();
306 let is_valid = errors.is_empty();
307 let result = ValidationResult {
308 is_valid,
309 errors,
310 warnings,
311 stats: ValidationStats {
312 validation_time,
313 gates_checked: circuit.gates().len(),
314 constraints_evaluated: Self::count_constraints(rules),
315 estimated_fidelity,
316 estimated_execution_time: Some(Self::estimate_execution_time(circuit, rules)),
317 },
318 suggestions,
319 };
320 Ok(result)
321 }
322 fn validate_gate_set<const N: usize>(
324 circuit: &Circuit<N>,
325 rules: &ValidationRules,
326 errors: &mut Vec<ValidationError>,
327 warnings: &[ValidationWarning],
328 suggestions: &mut Vec<ValidationSuggestion>,
329 ) -> QuantRS2Result<()> {
330 let mut non_native_gates = Vec::new();
331 let mut invalid_sequences: Vec<String> = Vec::new();
332 for (i, gate) in circuit.gates().iter().enumerate() {
333 let gate_name = gate.name();
334 let qubit_count = gate.qubits().len();
335 let is_native = match qubit_count {
336 1 => rules
337 .gate_restrictions
338 .native_gates
339 .single_qubit
340 .contains(gate_name),
341 2 => rules
342 .gate_restrictions
343 .native_gates
344 .two_qubit
345 .contains(gate_name),
346 _ => rules
347 .gate_restrictions
348 .native_gates
349 .multi_qubit
350 .contains(gate_name),
351 };
352 if !is_native {
353 if rules.gate_restrictions.require_native {
354 errors.push(ValidationError::UnsupportedGate {
355 gate_name: gate_name.to_string(),
356 position: i,
357 });
358 } else {
359 non_native_gates.push(gate_name.to_string());
360 }
361 }
362 if let Some(allowed_qubits) = rules
363 .gate_restrictions
364 .gate_qubit_restrictions
365 .get(gate_name)
366 {
367 for qubit in gate.qubits() {
368 let qubit_id = qubit.id() as usize;
369 if !allowed_qubits.contains(&qubit_id) {
370 errors.push(ValidationError::ConnectivityViolation {
371 gate_name: gate_name.to_string(),
372 qubits: vec![qubit_id],
373 position: i,
374 });
375 }
376 }
377 }
378 }
379 if !non_native_gates.is_empty() {
380 suggestions.push(ValidationSuggestion::DecomposeGates {
381 gates_to_decompose: non_native_gates,
382 });
383 }
384 Ok(())
385 }
386 fn validate_connectivity<const N: usize>(
388 circuit: &Circuit<N>,
389 rules: &ValidationRules,
390 errors: &mut Vec<ValidationError>,
391 suggestions: &mut Vec<ValidationSuggestion>,
392 ) -> QuantRS2Result<()> {
393 if rules.connectivity.all_to_all {
394 return Ok(());
395 }
396 let coupling_map = rules.connectivity.coupling_map.as_ref();
397 let mut connectivity_violations = false;
398 for (i, gate) in circuit.gates().iter().enumerate() {
399 if gate.qubits().len() >= 2 {
400 let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
401 if gate.qubits().len() == 2 {
402 let q1 = qubits[0];
403 let q2 = qubits[1];
404 if rules.connectivity.forbidden_pairs.contains(&(q1, q2))
405 || rules.connectivity.forbidden_pairs.contains(&(q2, q1))
406 {
407 errors.push(ValidationError::ConnectivityViolation {
408 gate_name: gate.name().to_string(),
409 qubits: vec![q1, q2],
410 position: i,
411 });
412 connectivity_violations = true;
413 }
414 if let Some(coupling) = coupling_map {
415 if !coupling.are_connected(q1, q2) {
416 errors.push(ValidationError::ConnectivityViolation {
417 gate_name: gate.name().to_string(),
418 qubits: vec![q1, q2],
419 position: i,
420 });
421 connectivity_violations = true;
422 }
423 }
424 }
425 if let Some(max_dist) = rules.connectivity.max_distance {
426 if let Some(coupling) = coupling_map {
427 for i in 0..qubits.len() {
428 for j in i + 1..qubits.len() {
429 let distance = coupling.distance(qubits[i], qubits[j]);
430 if distance > max_dist {
431 errors.push(ValidationError::ConnectivityViolation {
432 gate_name: gate.name().to_string(),
433 qubits: vec![qubits[i], qubits[j]],
434 position: i,
435 });
436 connectivity_violations = true;
437 }
438 }
439 }
440 }
441 }
442 }
443 }
444 if connectivity_violations {
445 suggestions.push(ValidationSuggestion::UseTranspilation {
446 suggested_router: "SABRE".to_string(),
447 });
448 }
449 Ok(())
450 }
451 fn validate_depth_limits<const N: usize>(
453 &self,
454 circuit: &Circuit<N>,
455 rules: &ValidationRules,
456 errors: &mut Vec<ValidationError>,
457 warnings: &mut Vec<ValidationWarning>,
458 suggestions: &mut Vec<ValidationSuggestion>,
459 ) -> QuantRS2Result<()> {
460 let circuit_depth = Self::calculate_circuit_depth(circuit);
461 let gate_count = circuit.gates().len();
462 if let Some(max_depth) = rules.depth_limits.max_depth {
463 if circuit_depth > max_depth {
464 errors.push(ValidationError::DepthLimitExceeded {
465 actual_depth: circuit_depth,
466 max_depth,
467 });
468 suggestions.push(ValidationSuggestion::ReduceDepth {
469 suggested_passes: vec![
470 "GateCommutation".to_string(),
471 "GateCancellation".to_string(),
472 ],
473 });
474 }
475 }
476 if let Some(max_gates) = rules.depth_limits.max_gates {
477 if gate_count > max_gates {
478 errors.push(ValidationError::GateCountExceeded {
479 actual_count: gate_count,
480 max_count: max_gates,
481 });
482 suggestions.push(ValidationSuggestion::SplitCircuit {
483 suggested_split_points: vec![max_gates / 2],
484 });
485 }
486 }
487 if let Some(max_time) = rules.depth_limits.max_execution_time {
488 let estimated_time = Self::estimate_execution_time(circuit, rules);
489 if estimated_time > max_time {
490 warnings.push(ValidationWarning::LongExecutionTime {
491 estimated_time,
492 recommended_max: max_time,
493 });
494 }
495 }
496 Ok(())
497 }
498 const fn validate_measurements<const N: usize>(
500 circuit: &Circuit<N>,
501 rules: &ValidationRules,
502 errors: &[ValidationError],
503 warnings: &[ValidationWarning],
504 ) -> QuantRS2Result<()> {
505 Ok(())
506 }
507 fn validate_resources<const N: usize>(
509 &self,
510 circuit: &Circuit<N>,
511 rules: &ValidationRules,
512 errors: &mut Vec<ValidationError>,
513 warnings: &mut Vec<ValidationWarning>,
514 ) -> QuantRS2Result<()> {
515 let estimated_memory = Self::estimate_memory_usage(circuit);
516 if let Some(max_memory) = rules.resource_limits.max_memory_mb {
517 let estimated_memory_mb = estimated_memory / (1024 * 1024);
518 if estimated_memory_mb > max_memory {
519 errors.push(ValidationError::ResourceLimitExceeded {
520 resource_type: "memory".to_string(),
521 required: estimated_memory_mb,
522 available: max_memory,
523 });
524 } else if estimated_memory_mb as f64 > max_memory as f64 * 0.8 {
525 warnings.push(ValidationWarning::ResourceUsageWarning {
526 resource_type: "memory".to_string(),
527 usage_percentage: (estimated_memory_mb as f64 / max_memory as f64) * 100.0,
528 });
529 }
530 }
531 Ok(())
532 }
533 fn calculate_circuit_depth<const N: usize>(circuit: &Circuit<N>) -> usize {
535 circuit.gates().len()
536 }
537 fn estimate_execution_time<const N: usize>(
539 circuit: &Circuit<N>,
540 rules: &ValidationRules,
541 ) -> f64 {
542 circuit.gates().len() as f64 * 0.1
543 }
544 const fn estimate_memory_usage<const N: usize>(circuit: &Circuit<N>) -> usize {
546 if N <= 30 {
547 (1usize << N) * 16
548 } else {
549 usize::MAX
550 }
551 }
552 fn estimate_fidelity<const N: usize>(
554 circuit: &Circuit<N>,
555 noise_model: &NoiseModel,
556 ) -> QuantRS2Result<f64> {
557 let mut total_error = 0.0;
558 for gate in circuit.gates() {
559 let gate_name = gate.name();
560 let error = match gate.qubits().len() {
561 1 => noise_model
562 .single_qubit_errors
563 .get(gate_name)
564 .map_or(0.001, |e| {
565 e.depolarizing + e.amplitude_damping + e.phase_damping
566 }),
567 2 => noise_model
568 .two_qubit_errors
569 .get(gate_name)
570 .map_or(0.01, |e| e.depolarizing),
571 _ => 0.05,
572 };
573 total_error += error;
574 }
575 Ok((1.0 - total_error).max(0.0))
576 }
577 fn count_constraints(rules: &ValidationRules) -> usize {
579 let mut count = 0;
580 count += 1;
581 count += rules.gate_restrictions.native_gates.single_qubit.len();
582 count += rules.gate_restrictions.native_gates.two_qubit.len();
583 count += rules.gate_restrictions.native_gates.multi_qubit.len();
584 if rules.depth_limits.max_depth.is_some() {
585 count += 1;
586 }
587 if rules.depth_limits.max_gates.is_some() {
588 count += 1;
589 }
590 if rules.depth_limits.max_execution_time.is_some() {
591 count += 1;
592 }
593 count
594 }
595 fn find_backends_with_qubits(&self, required_qubits: usize) -> Vec<String> {
597 self.backend_rules
598 .iter()
599 .filter(|(_, rules)| rules.max_qubits >= required_qubits)
600 .map(|(name, _)| name.clone())
601 .collect()
602 }
603 fn load_standard_backends(&mut self) {
605 self.add_backend_rules(ValidationRules::ibm_quantum());
606 self.add_backend_rules(ValidationRules::google_quantum());
607 self.add_backend_rules(ValidationRules::aws_braket());
608 self.add_backend_rules(ValidationRules::simulator());
609 }
610}
611impl ValidationRules {
612 #[must_use]
614 pub fn ibm_quantum() -> Self {
615 let native_gates = NativeGateSet {
616 single_qubit: ["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY"]
617 .iter()
618 .map(|s| (*s).to_string())
619 .collect(),
620 two_qubit: ["CNOT", "CZ"].iter().map(|s| (*s).to_string()).collect(),
621 multi_qubit: HashSet::new(),
622 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
623 .iter()
624 .map(|(k, v)| ((*k).to_string(), *v))
625 .collect(),
626 };
627 Self {
628 backend_name: "ibm_quantum".to_string(),
629 max_qubits: 127,
630 connectivity: ConnectivityConstraints {
631 coupling_map: Some(CouplingMap::grid(11, 12)),
632 all_to_all: false,
633 max_distance: Some(10),
634 forbidden_pairs: HashSet::new(),
635 },
636 gate_restrictions: GateRestrictions {
637 native_gates,
638 require_native: true,
639 max_parameters: 3,
640 forbidden_sequences: Vec::new(),
641 gate_qubit_restrictions: HashMap::new(),
642 },
643 depth_limits: DepthLimits {
644 max_depth: Some(10_000),
645 max_execution_time: Some(100_000.0),
646 max_gates: Some(50_000),
647 gate_type_limits: HashMap::new(),
648 },
649 measurement_constraints: MeasurementConstraints {
650 allow_mid_circuit: true,
651 max_measurements: Some(1_000),
652 allow_conditional: true,
653 required_basis: None,
654 non_measurable_qubits: HashSet::new(),
655 },
656 classical_constraints: ClassicalConstraints {
657 allow_classical_control: true,
658 max_classical_registers: Some(100),
659 allow_feedback: true,
660 max_conditional_depth: Some(100),
661 },
662 resource_limits: ResourceLimits {
663 max_memory_mb: Some(8_192),
664 max_shots: Some(100_000),
665 max_runtime_seconds: Some(3_600),
666 priority_constraints: None,
667 },
668 }
669 }
670 #[must_use]
672 pub fn google_quantum() -> Self {
673 let native_gates = NativeGateSet {
674 single_qubit: ["X", "Y", "Z", "H", "RZ", "SQRT_X"]
675 .iter()
676 .map(|s| (*s).to_string())
677 .collect(),
678 two_qubit: ["CZ", "ISWAP"].iter().map(|s| (*s).to_string()).collect(),
679 multi_qubit: HashSet::new(),
680 parameterized: [("RZ", 1)]
681 .iter()
682 .map(|(k, v)| ((*k).to_string(), *v))
683 .collect(),
684 };
685 Self {
686 backend_name: "google_quantum".to_string(),
687 max_qubits: 70,
688 connectivity: ConnectivityConstraints {
689 coupling_map: Some(CouplingMap::grid(8, 9)),
690 all_to_all: false,
691 max_distance: Some(5),
692 forbidden_pairs: HashSet::new(),
693 },
694 gate_restrictions: GateRestrictions {
695 native_gates,
696 require_native: true,
697 max_parameters: 1,
698 forbidden_sequences: Vec::new(),
699 gate_qubit_restrictions: HashMap::new(),
700 },
701 depth_limits: DepthLimits {
702 max_depth: Some(5_000),
703 max_execution_time: Some(50_000.0),
704 max_gates: Some(20_000),
705 gate_type_limits: HashMap::new(),
706 },
707 measurement_constraints: MeasurementConstraints {
708 allow_mid_circuit: false,
709 max_measurements: Some(70),
710 allow_conditional: false,
711 required_basis: Some("Z".to_string()),
712 non_measurable_qubits: HashSet::new(),
713 },
714 classical_constraints: ClassicalConstraints {
715 allow_classical_control: false,
716 max_classical_registers: Some(10),
717 allow_feedback: false,
718 max_conditional_depth: None,
719 },
720 resource_limits: ResourceLimits {
721 max_memory_mb: Some(4_096),
722 max_shots: Some(50_000),
723 max_runtime_seconds: Some(1_800),
724 priority_constraints: None,
725 },
726 }
727 }
728 #[must_use]
730 pub fn aws_braket() -> Self {
731 let native_gates = NativeGateSet {
732 single_qubit: ["X", "Y", "Z", "H", "RZ", "RX", "RY"]
733 .iter()
734 .map(|s| (*s).to_string())
735 .collect(),
736 two_qubit: ["CNOT", "CZ", "ISWAP"]
737 .iter()
738 .map(|s| (*s).to_string())
739 .collect(),
740 multi_qubit: HashSet::new(),
741 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1)]
742 .iter()
743 .map(|(k, v)| ((*k).to_string(), *v))
744 .collect(),
745 };
746 Self {
747 backend_name: "aws_braket".to_string(),
748 max_qubits: 100,
749 connectivity: ConnectivityConstraints {
750 coupling_map: None,
751 all_to_all: true,
752 max_distance: None,
753 forbidden_pairs: HashSet::new(),
754 },
755 gate_restrictions: GateRestrictions {
756 native_gates,
757 require_native: false,
758 max_parameters: 5,
759 forbidden_sequences: Vec::new(),
760 gate_qubit_restrictions: HashMap::new(),
761 },
762 depth_limits: DepthLimits {
763 max_depth: None,
764 max_execution_time: Some(200_000.0),
765 max_gates: None,
766 gate_type_limits: HashMap::new(),
767 },
768 measurement_constraints: MeasurementConstraints {
769 allow_mid_circuit: true,
770 max_measurements: None,
771 allow_conditional: true,
772 required_basis: None,
773 non_measurable_qubits: HashSet::new(),
774 },
775 classical_constraints: ClassicalConstraints {
776 allow_classical_control: true,
777 max_classical_registers: None,
778 allow_feedback: true,
779 max_conditional_depth: None,
780 },
781 resource_limits: ResourceLimits {
782 max_memory_mb: Some(16_384),
783 max_shots: Some(1_000_000),
784 max_runtime_seconds: Some(7_200),
785 priority_constraints: None,
786 },
787 }
788 }
789 #[must_use]
791 pub fn simulator() -> Self {
792 let native_gates = NativeGateSet {
793 single_qubit: ["X", "Y", "Z", "H", "S", "T", "RZ", "RX", "RY", "U"]
794 .iter()
795 .map(|s| (*s).to_string())
796 .collect(),
797 two_qubit: ["CNOT", "CZ", "ISWAP", "SWAP", "CX"]
798 .iter()
799 .map(|s| (*s).to_string())
800 .collect(),
801 multi_qubit: ["Toffoli", "Fredkin"]
802 .iter()
803 .map(|s| (*s).to_string())
804 .collect(),
805 parameterized: [("RZ", 1), ("RX", 1), ("RY", 1), ("U", 3)]
806 .iter()
807 .map(|(k, v)| ((*k).to_string(), *v))
808 .collect(),
809 };
810 Self {
811 backend_name: "simulator".to_string(),
812 max_qubits: 30,
813 connectivity: ConnectivityConstraints {
814 coupling_map: None,
815 all_to_all: true,
816 max_distance: None,
817 forbidden_pairs: HashSet::new(),
818 },
819 gate_restrictions: GateRestrictions {
820 native_gates,
821 require_native: false,
822 max_parameters: 10,
823 forbidden_sequences: Vec::new(),
824 gate_qubit_restrictions: HashMap::new(),
825 },
826 depth_limits: DepthLimits {
827 max_depth: None,
828 max_execution_time: None,
829 max_gates: None,
830 gate_type_limits: HashMap::new(),
831 },
832 measurement_constraints: MeasurementConstraints {
833 allow_mid_circuit: true,
834 max_measurements: None,
835 allow_conditional: true,
836 required_basis: None,
837 non_measurable_qubits: HashSet::new(),
838 },
839 classical_constraints: ClassicalConstraints {
840 allow_classical_control: true,
841 max_classical_registers: None,
842 allow_feedback: true,
843 max_conditional_depth: None,
844 },
845 resource_limits: ResourceLimits {
846 max_memory_mb: None,
847 max_shots: None,
848 max_runtime_seconds: None,
849 priority_constraints: None,
850 },
851 }
852 }
853}
854impl Default for CircuitValidator {
855 fn default() -> Self {
856 Self::new()
857 }
858}
859#[cfg(test)]
860mod tests {
861 use super::*;
862 use quantrs2_core::gate::multi::CNOT;
863 use quantrs2_core::gate::single::Hadamard;
864 #[test]
865 fn test_validator_creation() {
866 let validator = CircuitValidator::new();
867 assert!(!validator.available_backends().is_empty());
868 }
869 #[test]
870 fn test_validation_rules_creation() {
871 let rules = ValidationRules::ibm_quantum();
872 assert_eq!(rules.backend_name, "ibm_quantum");
873 assert_eq!(rules.max_qubits, 127);
874 }
875 #[test]
876 fn test_simple_circuit_validation() {
877 let mut validator = CircuitValidator::new();
878 let mut circuit = Circuit::<2>::new();
879 circuit
880 .add_gate(Hadamard { target: QubitId(0) })
881 .expect("Failed to add Hadamard gate");
882 let result = validator
883 .validate(&circuit, "simulator", None)
884 .expect("Validation should succeed for simple circuit");
885 assert!(result.is_valid);
886 }
887 #[test]
888 fn test_qubit_limit_validation() {
889 let mut validator = CircuitValidator::new();
890 let circuit = Circuit::<200>::new();
891 let result = validator
892 .validate(&circuit, "ibm_quantum", None)
893 .expect("Validation should return result even for exceeding qubit limit");
894 assert!(!result.is_valid);
895 assert!(!result.errors.is_empty());
896 }
897 #[test]
898 fn test_connectivity_validation() {
899 let mut validator = CircuitValidator::new();
900 let mut circuit = Circuit::<3>::new();
901 circuit
902 .add_gate(CNOT {
903 control: QubitId(0),
904 target: QubitId(1),
905 })
906 .expect("Failed to add CNOT gate");
907 let result = validator
908 .validate(&circuit, "ibm_quantum", None)
909 .expect("Validation should return result for connectivity test");
910 }
911}