use crate::circuit::QuantumCircuit;
use crate::gate::Gate;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendType {
StateVector,
Stabilizer,
TensorNetwork,
CliffordT,
Auto,
}
#[derive(Debug, Clone)]
pub struct CircuitAnalysis {
pub num_qubits: u32,
pub total_gates: usize,
pub clifford_gates: usize,
pub non_clifford_gates: usize,
pub clifford_fraction: f64,
pub measurement_gates: usize,
pub depth: u32,
pub max_connectivity: u32,
pub is_nearest_neighbor: bool,
pub recommended_backend: BackendType,
pub confidence: f64,
pub explanation: String,
}
pub fn analyze_circuit(circuit: &QuantumCircuit) -> CircuitAnalysis {
let num_qubits = circuit.num_qubits();
let gates = circuit.gates();
let total_gates = gates.len();
let mut clifford_gates = 0usize;
let mut non_clifford_gates = 0usize;
let mut measurement_gates = 0usize;
let mut max_connectivity: u32 = 0;
let mut is_nearest_neighbor = true;
for gate in gates {
match gate {
Gate::H(_)
| Gate::X(_)
| Gate::Y(_)
| Gate::Z(_)
| Gate::S(_)
| Gate::Sdg(_)
| Gate::CNOT(_, _)
| Gate::CZ(_, _)
| Gate::SWAP(_, _) => {
clifford_gates += 1;
}
Gate::T(_)
| Gate::Tdg(_)
| Gate::Rx(_, _)
| Gate::Ry(_, _)
| Gate::Rz(_, _)
| Gate::Phase(_, _)
| Gate::Rzz(_, _, _)
| Gate::Unitary1Q(_, _) => {
non_clifford_gates += 1;
}
Gate::Measure(_) => {
measurement_gates += 1;
}
Gate::Reset(_) | Gate::Barrier => {}
}
let qubits = gate.qubits();
if qubits.len() == 2 {
let dist = if qubits[0] > qubits[1] {
qubits[0] - qubits[1]
} else {
qubits[1] - qubits[0]
};
if dist > max_connectivity {
max_connectivity = dist;
}
if dist > 1 {
is_nearest_neighbor = false;
}
}
}
let unitary_gates = clifford_gates + non_clifford_gates;
let clifford_fraction = if unitary_gates > 0 {
clifford_gates as f64 / unitary_gates as f64
} else {
1.0
};
let depth = circuit.depth();
let (recommended_backend, confidence, explanation) = select_backend(
num_qubits,
clifford_fraction,
non_clifford_gates,
depth,
is_nearest_neighbor,
max_connectivity,
);
CircuitAnalysis {
num_qubits,
total_gates,
clifford_gates,
non_clifford_gates,
clifford_fraction,
measurement_gates,
depth,
max_connectivity,
is_nearest_neighbor,
recommended_backend,
confidence,
explanation,
}
}
fn select_backend(
num_qubits: u32,
clifford_fraction: f64,
non_clifford_gates: usize,
depth: u32,
is_nearest_neighbor: bool,
max_connectivity: u32,
) -> (BackendType, f64, String) {
if clifford_fraction >= 1.0 {
return (
BackendType::Stabilizer,
0.99,
format!(
"Pure Clifford circuit: stabilizer backend handles {} qubits in O(n^2) per gate",
num_qubits
),
);
}
if clifford_fraction >= 0.95 && num_qubits > 32 && non_clifford_gates <= 10 {
return (
BackendType::Stabilizer,
0.85,
format!(
"{}% Clifford with only {} non-Clifford gates: \
stabilizer backend recommended for {} qubits",
(clifford_fraction * 100.0) as u32,
non_clifford_gates,
num_qubits
),
);
}
if num_qubits <= 25 {
return (
BackendType::StateVector,
0.95,
format!(
"{} qubits fits comfortably in state vector ({})",
num_qubits,
format_memory(num_qubits)
),
);
}
if num_qubits <= 32 {
return (
BackendType::StateVector,
0.80,
format!(
"{} qubits requires {} for state vector - verify available memory",
num_qubits,
format_memory(num_qubits)
),
);
}
if is_nearest_neighbor && depth < num_qubits * 2 {
return (
BackendType::TensorNetwork,
0.85,
format!(
"Nearest-neighbor connectivity with depth {} on {} qubits: \
tensor network efficient",
depth, num_qubits
),
);
}
if num_qubits > 32 {
let conf = if is_nearest_neighbor { 0.75 } else { 0.55 };
return (
BackendType::TensorNetwork,
conf,
format!(
"{} qubits exceeds state vector capacity. \
Tensor network with connectivity {} - results are approximate",
num_qubits, max_connectivity
),
);
}
(
BackendType::StateVector,
0.70,
"Default to exact state vector simulation".into(),
)
}
fn format_memory(num_qubits: u32) -> String {
let bytes = (1u128 << num_qubits) * 16;
if bytes >= 1 << 40 {
format!("{:.1} TiB", bytes as f64 / (1u128 << 40) as f64)
} else if bytes >= 1 << 30 {
format!("{:.1} GiB", bytes as f64 / (1u128 << 30) as f64)
} else if bytes >= 1 << 20 {
format!("{:.1} MiB", bytes as f64 / (1u128 << 20) as f64)
} else {
format!("{} bytes", bytes)
}
}
#[derive(Debug, Clone)]
pub struct ScalingInfo {
pub backend: BackendType,
pub max_qubits_exact: u32,
pub max_qubits_approximate: u32,
pub time_complexity: String,
pub space_complexity: String,
}
pub fn scaling_report() -> Vec<ScalingInfo> {
vec![
ScalingInfo {
backend: BackendType::StateVector,
max_qubits_exact: 32,
max_qubits_approximate: 36,
time_complexity: "O(2^n * gates)".into(),
space_complexity: "O(2^n)".into(),
},
ScalingInfo {
backend: BackendType::Stabilizer,
max_qubits_exact: 10_000_000,
max_qubits_approximate: 10_000_000,
time_complexity: "O(n^2 * gates) for Clifford".into(),
space_complexity: "O(n^2)".into(),
},
ScalingInfo {
backend: BackendType::TensorNetwork,
max_qubits_exact: 100,
max_qubits_approximate: 10_000,
time_complexity: "O(n * chi^3 * gates)".into(),
space_complexity: "O(n * chi^2)".into(),
},
ScalingInfo {
backend: BackendType::CliffordT,
max_qubits_exact: 1000,
max_qubits_approximate: 10_000,
time_complexity: "O(2^t * n^2 * gates) for t T-gates".into(),
space_complexity: "O(2^t * n^2)".into(),
},
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::QuantumCircuit;
#[test]
fn pure_clifford_selects_stabilizer() {
let mut circ = QuantumCircuit::new(50);
for q in 0..50 {
circ.h(q);
}
for q in 0..49 {
circ.cnot(q, q + 1);
}
let analysis = analyze_circuit(&circ);
assert_eq!(analysis.recommended_backend, BackendType::Stabilizer);
assert!(analysis.clifford_fraction >= 1.0);
assert!(analysis.confidence > 0.9);
}
#[test]
fn small_circuit_selects_state_vector() {
let mut circ = QuantumCircuit::new(5);
circ.h(0).t(1).cnot(0, 1);
let analysis = analyze_circuit(&circ);
assert_eq!(analysis.recommended_backend, BackendType::StateVector);
assert!(analysis.confidence > 0.9);
}
#[test]
fn medium_circuit_selects_state_vector() {
let mut circ = QuantumCircuit::new(30);
circ.h(0).rx(1, 1.0).cnot(0, 1);
let analysis = analyze_circuit(&circ);
assert_eq!(analysis.recommended_backend, BackendType::StateVector);
assert!(analysis.confidence >= 0.80);
}
#[test]
fn large_nearest_neighbor_selects_tensor_network() {
let mut circ = QuantumCircuit::new(64);
for q in 0..63 {
circ.cnot(q, q + 1);
}
for q in 0..12 {
circ.t(q);
}
let analysis = analyze_circuit(&circ);
assert_eq!(analysis.recommended_backend, BackendType::TensorNetwork);
}
#[test]
fn empty_circuit_defaults() {
let circ = QuantumCircuit::new(10);
let analysis = analyze_circuit(&circ);
assert_eq!(analysis.total_gates, 0);
assert!(analysis.clifford_fraction >= 1.0);
}
#[test]
fn measurement_counted() {
let mut circ = QuantumCircuit::new(3);
circ.h(0).measure(0).measure(1).measure(2);
let analysis = analyze_circuit(&circ);
assert_eq!(analysis.measurement_gates, 3);
}
#[test]
fn connectivity_detected() {
let mut circ = QuantumCircuit::new(10);
circ.cnot(0, 5); let analysis = analyze_circuit(&circ);
assert_eq!(analysis.max_connectivity, 5);
assert!(!analysis.is_nearest_neighbor);
}
#[test]
fn scaling_report_has_four_entries() {
let report = scaling_report();
assert_eq!(report.len(), 4);
assert_eq!(report[0].backend, BackendType::StateVector);
assert_eq!(report[1].backend, BackendType::Stabilizer);
assert_eq!(report[2].backend, BackendType::TensorNetwork);
assert_eq!(report[3].backend, BackendType::CliffordT);
}
#[test]
fn format_memory_values() {
assert_eq!(format_memory(10), "16384 bytes");
assert_eq!(format_memory(20), "16.0 MiB");
assert_eq!(format_memory(30), "16.0 GiB");
}
}