quantrs2_circuit/
validation.rs

1//! Circuit validation for different quantum backends
2//!
3//! This module provides comprehensive validation capabilities to ensure quantum circuits
4//! are compatible with specific backend requirements, constraints, and capabilities.
5
6use 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/// Validation rules for a quantum backend
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ValidationRules {
21    /// Backend identifier
22    pub backend_name: String,
23    /// Maximum number of qubits
24    pub max_qubits: usize,
25    /// Qubit connectivity constraints
26    pub connectivity: ConnectivityConstraints,
27    /// Gate set restrictions
28    pub gate_restrictions: GateRestrictions,
29    /// Circuit depth limits
30    pub depth_limits: DepthLimits,
31    /// Measurement constraints
32    pub measurement_constraints: MeasurementConstraints,
33    /// Classical control flow constraints
34    pub classical_constraints: ClassicalConstraints,
35    /// Resource limits
36    pub resource_limits: ResourceLimits,
37}
38
39/// Connectivity constraints for qubits
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ConnectivityConstraints {
42    /// Coupling map defining allowed connections
43    pub coupling_map: Option<CouplingMap>,
44    /// Whether all-to-all connectivity is allowed
45    pub all_to_all: bool,
46    /// Maximum distance for multi-qubit operations
47    pub max_distance: Option<usize>,
48    /// Restricted qubit pairs (forbidden connections)
49    pub forbidden_pairs: HashSet<(usize, usize)>,
50}
51
52/// Gate set restrictions
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct GateRestrictions {
55    /// Allowed native gates
56    pub native_gates: NativeGateSet,
57    /// Whether decomposition to native gates is required
58    pub require_native: bool,
59    /// Maximum gate parameters per gate
60    pub max_parameters: usize,
61    /// Forbidden gate combinations
62    pub forbidden_sequences: Vec<Vec<String>>,
63    /// Gate-specific qubit restrictions
64    pub gate_qubit_restrictions: HashMap<String, HashSet<usize>>,
65}
66
67/// Circuit depth and timing limits
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DepthLimits {
70    /// Maximum circuit depth
71    pub max_depth: Option<usize>,
72    /// Maximum execution time in microseconds
73    pub max_execution_time: Option<f64>,
74    /// Maximum number of gates
75    pub max_gates: Option<usize>,
76    /// Depth limits by gate type
77    pub gate_type_limits: HashMap<String, usize>,
78}
79
80/// Measurement operation constraints
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct MeasurementConstraints {
83    /// Whether mid-circuit measurements are allowed
84    pub allow_mid_circuit: bool,
85    /// Maximum number of measurements
86    pub max_measurements: Option<usize>,
87    /// Whether measurements can be conditional
88    pub allow_conditional: bool,
89    /// Required measurement basis
90    pub required_basis: Option<String>,
91    /// Qubits that cannot be measured
92    pub non_measurable_qubits: HashSet<usize>,
93}
94
95/// Classical control flow constraints
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct ClassicalConstraints {
98    /// Whether classical control is supported
99    pub allow_classical_control: bool,
100    /// Maximum classical registers
101    pub max_classical_registers: Option<usize>,
102    /// Whether feedback is allowed
103    pub allow_feedback: bool,
104    /// Maximum conditional depth
105    pub max_conditional_depth: Option<usize>,
106}
107
108/// Resource usage limits
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ResourceLimits {
111    /// Maximum memory usage in MB
112    pub max_memory_mb: Option<usize>,
113    /// Maximum execution shots
114    pub max_shots: Option<usize>,
115    /// Maximum job runtime in seconds
116    pub max_runtime_seconds: Option<usize>,
117    /// Priority constraints
118    pub priority_constraints: Option<PriorityConstraints>,
119}
120
121/// Job priority constraints
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct PriorityConstraints {
124    /// Minimum user priority level
125    pub min_priority: u32,
126    /// Queue position limits
127    pub max_queue_position: Option<usize>,
128    /// Time-based restrictions
129    pub time_restrictions: Option<TimeRestrictions>,
130}
131
132/// Time-based execution restrictions
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct TimeRestrictions {
135    /// Allowed execution hours (0-23)
136    pub allowed_hours: HashSet<u8>,
137    /// Maintenance windows (UTC)
138    pub maintenance_windows: Vec<(String, String)>,
139    /// Maximum job duration by time of day
140    pub duration_limits: HashMap<u8, usize>,
141}
142
143/// Circuit validation result
144#[derive(Debug, Clone)]
145pub struct ValidationResult {
146    /// Whether the circuit is valid
147    pub is_valid: bool,
148    /// Validation errors
149    pub errors: Vec<ValidationError>,
150    /// Validation warnings
151    pub warnings: Vec<ValidationWarning>,
152    /// Validation statistics
153    pub stats: ValidationStats,
154    /// Suggested fixes
155    pub suggestions: Vec<ValidationSuggestion>,
156}
157
158/// Validation error types
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub enum ValidationError {
161    /// Too many qubits for backend
162    ExceedsQubitLimit { required: usize, available: usize },
163    /// Gate not supported by backend
164    UnsupportedGate { gate_name: String, position: usize },
165    /// Qubit connectivity violation
166    ConnectivityViolation {
167        gate_name: String,
168        qubits: Vec<usize>,
169        position: usize,
170    },
171    /// Circuit depth exceeds limit
172    DepthLimitExceeded {
173        actual_depth: usize,
174        max_depth: usize,
175    },
176    /// Too many gates
177    GateCountExceeded {
178        actual_count: usize,
179        max_count: usize,
180    },
181    /// Measurement constraint violation
182    MeasurementViolation {
183        violation_type: String,
184        details: String,
185    },
186    /// Classical control violation
187    ClassicalControlViolation {
188        violation_type: String,
189        details: String,
190    },
191    /// Resource limit exceeded
192    ResourceLimitExceeded {
193        resource_type: String,
194        required: usize,
195        available: usize,
196    },
197    /// Invalid gate sequence
198    InvalidGateSequence {
199        sequence: Vec<String>,
200        position: usize,
201    },
202}
203
204/// Validation warning types
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub enum ValidationWarning {
207    /// Suboptimal gate usage
208    SuboptimalGateUsage {
209        gate_name: String,
210        suggested_alternative: String,
211        positions: Vec<usize>,
212    },
213    /// High error rate expected
214    HighErrorRate {
215        estimated_error: f64,
216        threshold: f64,
217    },
218    /// Long execution time
219    LongExecutionTime {
220        estimated_time: f64,
221        recommended_max: f64,
222    },
223    /// Resource usage warning
224    ResourceUsageWarning {
225        resource_type: String,
226        usage_percentage: f64,
227    },
228}
229
230/// Validation statistics
231#[derive(Debug, Clone)]
232pub struct ValidationStats {
233    /// Total validation time
234    pub validation_time: std::time::Duration,
235    /// Number of gates checked
236    pub gates_checked: usize,
237    /// Number of constraints evaluated
238    pub constraints_evaluated: usize,
239    /// Estimated circuit fidelity
240    pub estimated_fidelity: Option<f64>,
241    /// Estimated execution time
242    pub estimated_execution_time: Option<f64>,
243}
244
245/// Validation suggestion for fixing errors
246#[derive(Debug, Clone)]
247pub enum ValidationSuggestion {
248    /// Use transpilation to fix connectivity
249    UseTranspilation { suggested_router: String },
250    /// Decompose gates to native set
251    DecomposeGates { gates_to_decompose: Vec<String> },
252    /// Reduce circuit depth
253    ReduceDepth { suggested_passes: Vec<String> },
254    /// Split circuit into subcircuits
255    SplitCircuit { suggested_split_points: Vec<usize> },
256    /// Use different backend
257    SwitchBackend { recommended_backends: Vec<String> },
258}
259
260/// Circuit validator for different backends
261pub struct CircuitValidator {
262    /// Validation rules by backend
263    backend_rules: HashMap<String, ValidationRules>,
264    /// Cached validation results
265    validation_cache: HashMap<String, ValidationResult>,
266}
267
268impl CircuitValidator {
269    /// Create a new circuit validator
270    pub fn new() -> Self {
271        let mut validator = Self {
272            backend_rules: HashMap::new(),
273            validation_cache: HashMap::new(),
274        };
275
276        // Load standard backend validation rules
277        validator.load_standard_backends();
278        validator
279    }
280
281    /// Add validation rules for a backend
282    pub fn add_backend_rules(&mut self, rules: ValidationRules) {
283        self.backend_rules.insert(rules.backend_name.clone(), rules);
284    }
285
286    /// Get available backends for validation
287    pub fn available_backends(&self) -> Vec<String> {
288        self.backend_rules.keys().cloned().collect()
289    }
290
291    /// Validate a circuit against backend requirements
292    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        // Check if backend is supported
301        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        // Validate qubit count
311        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        // Validate gate set
322        self.validate_gate_set(circuit, rules, &mut errors, &mut warnings, &mut suggestions)?;
323
324        // Validate connectivity
325        self.validate_connectivity(circuit, rules, &mut errors, &mut suggestions)?;
326
327        // Validate circuit depth and limits
328        self.validate_depth_limits(circuit, rules, &mut errors, &mut warnings, &mut suggestions)?;
329
330        // Validate measurements
331        self.validate_measurements(circuit, rules, &mut errors, &mut warnings)?;
332
333        // Validate resource requirements
334        self.validate_resources(circuit, rules, &mut errors, &mut warnings)?;
335
336        // Estimate fidelity if noise model provided
337        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    /// Validate gate set compliance
364    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            // Check if gate is native
380            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            // Check gate-specific qubit restrictions
410            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        // Add suggestions for non-native gates
429        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    /// Validate qubit connectivity constraints
439    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(()); // No connectivity constraints
448        }
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                // Check two-qubit connectivity
458                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                // Check multi-qubit distance constraints
486                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    /// Validate circuit depth and timing limits
516    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        // Check maximum depth
528        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        // Check maximum gate count
544        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        // Check execution time limits
557        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    /// Validate measurement constraints
571    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        // For now, basic measurement validation
579        // In a full implementation, this would analyze measurement operations in the circuit
580        Ok(())
581    }
582
583    /// Validate resource requirements
584    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        // Estimate memory usage
592        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    /// Calculate circuit depth
614    fn calculate_circuit_depth<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
615        // Simplified depth calculation - in practice, this would consider gate parallelization
616        circuit.gates().len()
617    }
618
619    /// Estimate execution time
620    fn estimate_execution_time<const N: usize>(
621        &self,
622        circuit: &Circuit<N>,
623        rules: &ValidationRules,
624    ) -> f64 {
625        // Simplified estimation based on gate count
626        circuit.gates().len() as f64 * 0.1 // 0.1 microseconds per gate average
627    }
628
629    /// Estimate memory usage
630    fn estimate_memory_usage<const N: usize>(&self, circuit: &Circuit<N>) -> usize {
631        // Estimate based on state vector simulation
632        if N <= 30 {
633            (1usize << N) * 16 // 16 bytes per complex amplitude
634        } else {
635            usize::MAX // Too large for classical simulation
636        }
637    }
638
639    /// Estimate circuit fidelity
640    fn estimate_fidelity<const N: usize>(
641        &self,
642        circuit: &Circuit<N>,
643        noise_model: &NoiseModel,
644    ) -> QuantRS2Result<f64> {
645        // Simplified fidelity estimation
646        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, // Multi-qubit gates
662            };
663            total_error += error;
664        }
665
666        Ok((1.0 - total_error).max(0.0))
667    }
668
669    /// Count validation constraints
670    fn count_constraints(&self, rules: &ValidationRules) -> usize {
671        let mut count = 0;
672
673        count += 1; // Qubit count
674        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    /// Find backends that support the required number of qubits
692    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    /// Load standard backend validation rules
701    fn load_standard_backends(&mut self) {
702        // IBM Quantum validation rules
703        self.add_backend_rules(ValidationRules::ibm_quantum());
704
705        // Google Quantum AI validation rules
706        self.add_backend_rules(ValidationRules::google_quantum());
707
708        // AWS Braket validation rules
709        self.add_backend_rules(ValidationRules::aws_braket());
710
711        // Simulator validation rules
712        self.add_backend_rules(ValidationRules::simulator());
713    }
714}
715
716impl ValidationRules {
717    /// IBM Quantum validation rules
718    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)), // Roughly sqrt(127) grid
737                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), // 100ms
751                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    /// Google Quantum AI validation rules
777    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    /// AWS Braket validation rules
836    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, // Varies by device
858                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    /// Simulator validation rules
898    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, // Practical limit for state vector simulation
921            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(); // Too many qubits
1002
1003        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        // This would require checking actual connectivity constraints
1013        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        // Result depends on the specific connectivity of the coupling map
1022    }
1023}