1use crate::circuit::QuantumCircuit;
13use crate::gate::Gate;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum BackendType {
22 StateVector,
24 Stabilizer,
26 TensorNetwork,
28 CliffordT,
30 Auto,
32}
33
34#[derive(Debug, Clone)]
44pub struct CircuitAnalysis {
45 pub num_qubits: u32,
47 pub total_gates: usize,
49 pub clifford_gates: usize,
51 pub non_clifford_gates: usize,
53 pub clifford_fraction: f64,
55 pub measurement_gates: usize,
57 pub depth: u32,
59 pub max_connectivity: u32,
61 pub is_nearest_neighbor: bool,
63 pub recommended_backend: BackendType,
65 pub confidence: f64,
67 pub explanation: String,
69}
70
71pub fn analyze_circuit(circuit: &QuantumCircuit) -> CircuitAnalysis {
94 let num_qubits = circuit.num_qubits();
95 let gates = circuit.gates();
96 let total_gates = gates.len();
97
98 let mut clifford_gates = 0usize;
99 let mut non_clifford_gates = 0usize;
100 let mut measurement_gates = 0usize;
101 let mut max_connectivity: u32 = 0;
102 let mut is_nearest_neighbor = true;
103
104 for gate in gates {
105 match gate {
106 Gate::H(_)
108 | Gate::X(_)
109 | Gate::Y(_)
110 | Gate::Z(_)
111 | Gate::S(_)
112 | Gate::Sdg(_)
113 | Gate::CNOT(_, _)
114 | Gate::CZ(_, _)
115 | Gate::SWAP(_, _) => {
116 clifford_gates += 1;
117 }
118 Gate::T(_)
120 | Gate::Tdg(_)
121 | Gate::Rx(_, _)
122 | Gate::Ry(_, _)
123 | Gate::Rz(_, _)
124 | Gate::Phase(_, _)
125 | Gate::Rzz(_, _, _)
126 | Gate::Unitary1Q(_, _) => {
127 non_clifford_gates += 1;
128 }
129 Gate::Measure(_) => {
130 measurement_gates += 1;
131 }
132 Gate::Reset(_) | Gate::Barrier => {}
133 }
134
135 let qubits = gate.qubits();
137 if qubits.len() == 2 {
138 let dist = if qubits[0] > qubits[1] {
139 qubits[0] - qubits[1]
140 } else {
141 qubits[1] - qubits[0]
142 };
143 if dist > max_connectivity {
144 max_connectivity = dist;
145 }
146 if dist > 1 {
147 is_nearest_neighbor = false;
148 }
149 }
150 }
151
152 let unitary_gates = clifford_gates + non_clifford_gates;
153 let clifford_fraction = if unitary_gates > 0 {
154 clifford_gates as f64 / unitary_gates as f64
155 } else {
156 1.0
157 };
158
159 let depth = circuit.depth();
160
161 let (recommended_backend, confidence, explanation) = select_backend(
163 num_qubits,
164 clifford_fraction,
165 non_clifford_gates,
166 depth,
167 is_nearest_neighbor,
168 max_connectivity,
169 );
170
171 CircuitAnalysis {
172 num_qubits,
173 total_gates,
174 clifford_gates,
175 non_clifford_gates,
176 clifford_fraction,
177 measurement_gates,
178 depth,
179 max_connectivity,
180 is_nearest_neighbor,
181 recommended_backend,
182 confidence,
183 explanation,
184 }
185}
186
187fn select_backend(
196 num_qubits: u32,
197 clifford_fraction: f64,
198 non_clifford_gates: usize,
199 depth: u32,
200 is_nearest_neighbor: bool,
201 max_connectivity: u32,
202) -> (BackendType, f64, String) {
203 if clifford_fraction >= 1.0 {
205 return (
206 BackendType::Stabilizer,
207 0.99,
208 format!(
209 "Pure Clifford circuit: stabilizer backend handles {} qubits in O(n^2) per gate",
210 num_qubits
211 ),
212 );
213 }
214
215 if clifford_fraction >= 0.95 && num_qubits > 32 && non_clifford_gates <= 10 {
218 return (
219 BackendType::Stabilizer,
220 0.85,
221 format!(
222 "{}% Clifford with only {} non-Clifford gates: \
223 stabilizer backend recommended for {} qubits",
224 (clifford_fraction * 100.0) as u32,
225 non_clifford_gates,
226 num_qubits
227 ),
228 );
229 }
230
231 if num_qubits <= 25 {
233 return (
234 BackendType::StateVector,
235 0.95,
236 format!(
237 "{} qubits fits comfortably in state vector ({})",
238 num_qubits,
239 format_memory(num_qubits)
240 ),
241 );
242 }
243
244 if num_qubits <= 32 {
246 return (
247 BackendType::StateVector,
248 0.80,
249 format!(
250 "{} qubits requires {} for state vector - verify available memory",
251 num_qubits,
252 format_memory(num_qubits)
253 ),
254 );
255 }
256
257 if is_nearest_neighbor && depth < num_qubits * 2 {
259 return (
260 BackendType::TensorNetwork,
261 0.85,
262 format!(
263 "Nearest-neighbor connectivity with depth {} on {} qubits: \
264 tensor network efficient",
265 depth, num_qubits
266 ),
267 );
268 }
269
270 if num_qubits > 32 {
272 let conf = if is_nearest_neighbor { 0.75 } else { 0.55 };
273 return (
274 BackendType::TensorNetwork,
275 conf,
276 format!(
277 "{} qubits exceeds state vector capacity. \
278 Tensor network with connectivity {} - results are approximate",
279 num_qubits, max_connectivity
280 ),
281 );
282 }
283
284 (
286 BackendType::StateVector,
287 0.70,
288 "Default to exact state vector simulation".into(),
289 )
290}
291
292fn format_memory(num_qubits: u32) -> String {
300 let bytes = (1u128 << num_qubits) * 16;
302 if bytes >= 1 << 40 {
303 format!("{:.1} TiB", bytes as f64 / (1u128 << 40) as f64)
304 } else if bytes >= 1 << 30 {
305 format!("{:.1} GiB", bytes as f64 / (1u128 << 30) as f64)
306 } else if bytes >= 1 << 20 {
307 format!("{:.1} MiB", bytes as f64 / (1u128 << 20) as f64)
308 } else {
309 format!("{} bytes", bytes)
310 }
311}
312
313#[derive(Debug, Clone)]
319pub struct ScalingInfo {
320 pub backend: BackendType,
322 pub max_qubits_exact: u32,
324 pub max_qubits_approximate: u32,
326 pub time_complexity: String,
328 pub space_complexity: String,
330}
331
332pub fn scaling_report() -> Vec<ScalingInfo> {
337 vec![
338 ScalingInfo {
339 backend: BackendType::StateVector,
340 max_qubits_exact: 32,
341 max_qubits_approximate: 36,
342 time_complexity: "O(2^n * gates)".into(),
343 space_complexity: "O(2^n)".into(),
344 },
345 ScalingInfo {
346 backend: BackendType::Stabilizer,
347 max_qubits_exact: 10_000_000,
348 max_qubits_approximate: 10_000_000,
349 time_complexity: "O(n^2 * gates) for Clifford".into(),
350 space_complexity: "O(n^2)".into(),
351 },
352 ScalingInfo {
353 backend: BackendType::TensorNetwork,
354 max_qubits_exact: 100,
355 max_qubits_approximate: 10_000,
356 time_complexity: "O(n * chi^3 * gates)".into(),
357 space_complexity: "O(n * chi^2)".into(),
358 },
359 ScalingInfo {
360 backend: BackendType::CliffordT,
361 max_qubits_exact: 1000,
362 max_qubits_approximate: 10_000,
363 time_complexity: "O(2^t * n^2 * gates) for t T-gates".into(),
364 space_complexity: "O(2^t * n^2)".into(),
365 },
366 ]
367}
368
369#[cfg(test)]
374mod tests {
375 use super::*;
376 use crate::circuit::QuantumCircuit;
377
378 #[test]
379 fn pure_clifford_selects_stabilizer() {
380 let mut circ = QuantumCircuit::new(50);
381 for q in 0..50 {
382 circ.h(q);
383 }
384 for q in 0..49 {
385 circ.cnot(q, q + 1);
386 }
387 let analysis = analyze_circuit(&circ);
388 assert_eq!(analysis.recommended_backend, BackendType::Stabilizer);
389 assert!(analysis.clifford_fraction >= 1.0);
390 assert!(analysis.confidence > 0.9);
391 }
392
393 #[test]
394 fn small_circuit_selects_state_vector() {
395 let mut circ = QuantumCircuit::new(5);
396 circ.h(0).t(1).cnot(0, 1);
397 let analysis = analyze_circuit(&circ);
398 assert_eq!(analysis.recommended_backend, BackendType::StateVector);
399 assert!(analysis.confidence > 0.9);
400 }
401
402 #[test]
403 fn medium_circuit_selects_state_vector() {
404 let mut circ = QuantumCircuit::new(30);
405 circ.h(0).rx(1, 1.0).cnot(0, 1);
406 let analysis = analyze_circuit(&circ);
407 assert_eq!(analysis.recommended_backend, BackendType::StateVector);
408 assert!(analysis.confidence >= 0.80);
409 }
410
411 #[test]
412 fn large_nearest_neighbor_selects_tensor_network() {
413 let mut circ = QuantumCircuit::new(64);
414 for q in 0..63 {
416 circ.cnot(q, q + 1);
417 }
418 for q in 0..12 {
421 circ.t(q);
422 }
423 let analysis = analyze_circuit(&circ);
424 assert_eq!(analysis.recommended_backend, BackendType::TensorNetwork);
425 }
426
427 #[test]
428 fn empty_circuit_defaults() {
429 let circ = QuantumCircuit::new(10);
430 let analysis = analyze_circuit(&circ);
431 assert_eq!(analysis.total_gates, 0);
433 assert!(analysis.clifford_fraction >= 1.0);
434 }
435
436 #[test]
437 fn measurement_counted() {
438 let mut circ = QuantumCircuit::new(3);
439 circ.h(0).measure(0).measure(1).measure(2);
440 let analysis = analyze_circuit(&circ);
441 assert_eq!(analysis.measurement_gates, 3);
442 }
443
444 #[test]
445 fn connectivity_detected() {
446 let mut circ = QuantumCircuit::new(10);
447 circ.cnot(0, 5); let analysis = analyze_circuit(&circ);
449 assert_eq!(analysis.max_connectivity, 5);
450 assert!(!analysis.is_nearest_neighbor);
451 }
452
453 #[test]
454 fn scaling_report_has_four_entries() {
455 let report = scaling_report();
456 assert_eq!(report.len(), 4);
457 assert_eq!(report[0].backend, BackendType::StateVector);
458 assert_eq!(report[1].backend, BackendType::Stabilizer);
459 assert_eq!(report[2].backend, BackendType::TensorNetwork);
460 assert_eq!(report[3].backend, BackendType::CliffordT);
461 }
462
463 #[test]
464 fn format_memory_values() {
465 assert_eq!(format_memory(10), "16384 bytes");
467 assert_eq!(format_memory(20), "16.0 MiB");
469 assert_eq!(format_memory(30), "16.0 GiB");
471 }
472}