diff options
| -rw-r--r-- | libpsi-core/Cargo.toml | 1 | ||||
| -rw-r--r-- | libpsi-core/src/core/circuit.rs | 43 | ||||
| -rw-r--r-- | libpsi-core/src/core/mod.rs | 2 | ||||
| -rw-r--r-- | libpsi-core/src/core/runtime.rs | 175 | ||||
| -rw-r--r-- | libpsi-core/src/lib.rs | 1 | ||||
| -rw-r--r-- | tester/src/main.rs | 540 |
6 files changed, 622 insertions, 140 deletions
diff --git a/libpsi-core/Cargo.toml b/libpsi-core/Cargo.toml index 6e73d65..5f17157 100644 --- a/libpsi-core/Cargo.toml +++ b/libpsi-core/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" lazy_static = "1.5.0" libm = "0.2.8" rand = "0.8.5" +rayon = "1.10" diff --git a/libpsi-core/src/core/circuit.rs b/libpsi-core/src/core/circuit.rs index e797511..5bb17ff 100644 --- a/libpsi-core/src/core/circuit.rs +++ b/libpsi-core/src/core/circuit.rs @@ -1,4 +1,4 @@ -use super::{CustomGate, QuantumRegister, QuantumState}; +use super::{CustomGate, QuantumState, Runtime}; use crate::{format_amplitude, format_probability, Vector}; use std::sync::Arc; use core::fmt; @@ -108,42 +108,15 @@ impl QuantumCircuit { } pub fn compute(&mut self) -> &QuantumState { + self.compute_with(Runtime::default()) + } + + pub fn compute_with(&mut self, runtime: Runtime) -> &QuantumState { if self.computed_state.is_some() { return self.computed_state.as_ref().unwrap(); } - let names: Vec<String> = (0..self.num_qubits).map(|i| format!("q{}", i)).collect(); - let leaked_names: &'static [String] = Box::leak(names.into_boxed_slice()); - let name_refs: Vec<&'static str> = leaked_names.iter().map(|s| s.as_str()).collect(); - - let mut register = QuantumRegister::new( - Box::leak(Box::new("circuit".to_string())).as_str(), - &name_refs, - ); - - use crate::gates::*; - for op in &self.operations { - match op { - GateOp::H(t) => register.apply_gate(&HADAMARD, &[*t]), - GateOp::X(t) => register.apply_gate(&PAULI_X, &[*t]), - GateOp::Y(t) => register.apply_gate(&PAULI_Y, &[*t]), - GateOp::Z(t) => register.apply_gate(&PAULI_Z, &[*t]), - GateOp::S(t) => register.apply_gate(&S_GATE, &[*t]), - GateOp::T(t) => register.apply_gate(&T_GATE, &[*t]), - GateOp::CNOT(c, t) => register.apply_gate(&CNOT, &[*c, *t]), - GateOp::CZ(c, t) => register.apply_gate(&CZ, &[*c, *t]), - GateOp::SWAP(a, b) => register.apply_gate(&SWAP, &[*a, *b]), - GateOp::CCNOT(c1, c2, t) => register.apply_gate(&TOFFOLI, &[*c1, *c2, *t]), - GateOp::CSWAP(c, t1, t2) => register.apply_gate(&FREDKIN, &[*c, *t1, *t2]), - GateOp::Measure(_, _) => {} - GateOp::Custom(gate, targets) => { - let quantum_gate = gate.to_quantum_gate(); - register.apply_gate(&quantum_gate, targets); - } - } - } - - self.computed_state = Some(register.get_state()); + self.computed_state = Some(runtime.compute(self.num_qubits, &self.operations)); self.computed_state.as_ref().unwrap() } @@ -151,6 +124,10 @@ impl QuantumCircuit { self.compute() } + pub fn state_with(&mut self, runtime: Runtime) -> &QuantumState { + self.compute_with(runtime) + } + pub fn h(&mut self, target: usize) -> &mut Self { self.operations.push(GateOp::H(target)); self.computed_state = None; diff --git a/libpsi-core/src/core/mod.rs b/libpsi-core/src/core/mod.rs index 1b950f3..697b0d7 100644 --- a/libpsi-core/src/core/mod.rs +++ b/libpsi-core/src/core/mod.rs @@ -3,9 +3,11 @@ pub mod classical_components; pub mod custom_gate; pub mod gates; pub mod quantum_components; +pub mod runtime; pub use circuit::*; pub use classical_components::*; pub use custom_gate::*; pub use gates::*; pub use quantum_components::*; +pub use runtime::*; diff --git a/libpsi-core/src/core/runtime.rs b/libpsi-core/src/core/runtime.rs new file mode 100644 index 0000000..7b73c47 --- /dev/null +++ b/libpsi-core/src/core/runtime.rs @@ -0,0 +1,175 @@ +use super::{GateOp, QuantumGate, QuantumRegister, QuantumState}; +use crate::gates::*; +use crate::maths::vector::Vector; +use crate::{complex, Complex, Matrix}; +use rayon::prelude::*; + +/// Minimum number of qubits to enable parallelism (2^8 = 256 state vector elements) +const PARALLEL_THRESHOLD: usize = 8; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum Runtime { + #[default] + BasicRT, + BasicRTMT, + WFEvolution, + WFEvolutionMT, + GPUAccelerated, +} + +impl Runtime { + pub fn compute(&self, num_qubits: usize, operations: &[GateOp]) -> QuantumState { + match self { + Runtime::BasicRT => Self::compute_basic(num_qubits, operations), + Runtime::BasicRTMT => Self::compute_basic_mt(num_qubits, operations), + Runtime::WFEvolution => { + unimplemented!("WFEvolution (Schrödinger equation) runtime not yet implemented") + } + Runtime::WFEvolutionMT => { + unimplemented!( + "WFEvolutionMT (multi-threaded Schrödinger) runtime not yet implemented" + ) + } + Runtime::GPUAccelerated => { + unimplemented!("GPUAccelerated runtime not yet implemented") + } + } + } + + fn compute_basic(num_qubits: usize, operations: &[GateOp]) -> QuantumState { + let names: Vec<String> = (0..num_qubits).map(|i| format!("q{}", i)).collect(); + let leaked_names: &'static [String] = Box::leak(names.into_boxed_slice()); + let name_refs: Vec<&'static str> = leaked_names.iter().map(|s| s.as_str()).collect(); + + let mut register = QuantumRegister::new( + Box::leak(Box::new("circuit".to_string())).as_str(), + &name_refs, + ); + + for op in operations { + match op { + GateOp::H(t) => register.apply_gate(&HADAMARD, &[*t]), + GateOp::X(t) => register.apply_gate(&PAULI_X, &[*t]), + GateOp::Y(t) => register.apply_gate(&PAULI_Y, &[*t]), + GateOp::Z(t) => register.apply_gate(&PAULI_Z, &[*t]), + GateOp::S(t) => register.apply_gate(&S_GATE, &[*t]), + GateOp::T(t) => register.apply_gate(&T_GATE, &[*t]), + GateOp::CNOT(c, t) => register.apply_gate(&CNOT, &[*c, *t]), + GateOp::CZ(c, t) => register.apply_gate(&CZ, &[*c, *t]), + GateOp::SWAP(a, b) => register.apply_gate(&SWAP, &[*a, *b]), + GateOp::CCNOT(c1, c2, t) => register.apply_gate(&TOFFOLI, &[*c1, *c2, *t]), + GateOp::CSWAP(c, t1, t2) => register.apply_gate(&FREDKIN, &[*c, *t1, *t2]), + GateOp::Measure(_, _) => {} + GateOp::Custom(gate, targets) => { + let quantum_gate = gate.to_quantum_gate(); + register.apply_gate(&quantum_gate, targets); + } + } + } + + register.get_state() + } + + fn compute_basic_mt(num_qubits: usize, operations: &[GateOp]) -> QuantumState { + // For small circuits, fall back to single-threaded (overhead not worth it) + if num_qubits < PARALLEL_THRESHOLD { + return Self::compute_basic(num_qubits, operations); + } + + let dim = 1 << num_qubits; + + // Initialize state to |0...0⟩ + let mut state: Vec<Complex<f64>> = vec![complex!(0.0, 0.0); dim]; + state[0] = complex!(1.0, 0.0); + + for op in operations { + let (gate, targets): (&QuantumGate, Vec<usize>) = match op { + GateOp::H(t) => (&HADAMARD, vec![*t]), + GateOp::X(t) => (&PAULI_X, vec![*t]), + GateOp::Y(t) => (&PAULI_Y, vec![*t]), + GateOp::Z(t) => (&PAULI_Z, vec![*t]), + GateOp::S(t) => (&S_GATE, vec![*t]), + GateOp::T(t) => (&T_GATE, vec![*t]), + GateOp::CNOT(c, t) => (&CNOT, vec![*c, *t]), + GateOp::CZ(c, t) => (&CZ, vec![*c, *t]), + GateOp::SWAP(a, b) => (&SWAP, vec![*a, *b]), + GateOp::CCNOT(c1, c2, t) => (&TOFFOLI, vec![*c1, *c2, *t]), + GateOp::CSWAP(c, t1, t2) => (&FREDKIN, vec![*c, *t1, *t2]), + GateOp::Measure(_, _) => continue, + GateOp::Custom(custom_gate, tgts) => { + let quantum_gate = custom_gate.to_quantum_gate(); + state = apply_gate_parallel(&state, &quantum_gate.matrix, tgts, num_qubits); + continue; + } + }; + + state = apply_gate_parallel(&state, &gate.matrix, &targets, num_qubits); + } + + QuantumState::new(state) + } +} + +/// Apply a gate to the state vector in parallel using sparse application +/// This is O(2^n * 2^g) instead of O(2^2n) for full matrix multiplication +fn apply_gate_parallel( + state: &[Complex<f64>], + gate_matrix: &Matrix<Complex<f64>>, + targets: &[usize], + num_qubits: usize, +) -> Vec<Complex<f64>> { + let dim = 1 << num_qubits; + let g = targets.len(); + let gate_dim = 1 << g; + + // Convert target qubit indices to bit positions (from MSB) + let target_bits: Vec<usize> = targets.iter().map(|&t| num_qubits - 1 - t).collect(); + + // Create a mask for non-target qubits + let mut non_target_mask: usize = (1 << num_qubits) - 1; + for &pos in &target_bits { + non_target_mask &= !(1 << pos); + } + + // Parallel computation of new state + let new_state: Vec<Complex<f64>> = (0..dim) + .into_par_iter() + .map(|i| { + // Extract the target qubit bits from index i + let mut target_idx = 0usize; + for (k, &pos) in target_bits.iter().enumerate() { + if (i >> pos) & 1 == 1 { + target_idx |= 1 << (g - 1 - k); + } + } + + // Compute the contribution to state[i] + let mut sum = complex!(0.0, 0.0); + + // For each possible input state that could contribute + for j in 0..gate_dim { + // Get the gate matrix element + let gate_elem = gate_matrix.data[target_idx * gate_dim + j]; + + // Skip if zero (sparse optimization) + if gate_elem.real.abs() < 1e-15 && gate_elem.imaginary.abs() < 1e-15 { + continue; + } + + // Compute the source index by replacing target bits in i with bits from j + let mut source_idx = i & non_target_mask; + for (k, &pos) in target_bits.iter().enumerate() { + if (j >> (g - 1 - k)) & 1 == 1 { + source_idx |= 1 << pos; + } + } + + sum = sum + gate_elem * state[source_idx]; + } + + sum + }) + .collect(); + + new_state +} diff --git a/libpsi-core/src/lib.rs b/libpsi-core/src/lib.rs index 68ced7b..7b99170 100644 --- a/libpsi-core/src/lib.rs +++ b/libpsi-core/src/lib.rs @@ -12,3 +12,4 @@ pub use core::classical_components::*; pub use core::custom_gate::*; pub use core::gates; pub use core::quantum_components::*; +pub use core::runtime::*; diff --git a/tester/src/main.rs b/tester/src/main.rs index 7981e8d..660dc2b 100644 --- a/tester/src/main.rs +++ b/tester/src/main.rs @@ -1,161 +1,487 @@ use libpsi_core::*; use libpsi_visualizer::*; +use std::time::{Duration, Instant}; + +struct BenchmarkResult { + name: String, + basic_time: Duration, + mt_time: Duration, + results_match: bool, +} + +fn benchmark_circuit<F>(name: &str, circuit_builder: F) -> BenchmarkResult +where + F: Fn() -> QuantumCircuit, +{ + let mut circuit_st = circuit_builder(); + let mut circuit_mt = circuit_builder(); + + let start_st = Instant::now(); + circuit_st.compute_with(Runtime::BasicRT); + let basic_time = start_st.elapsed(); + + let start_mt = Instant::now(); + circuit_mt.compute_with(Runtime::BasicRTMT); + let mt_time = start_mt.elapsed(); + + let state_st = circuit_st.state(); + let state_mt = circuit_mt.state(); + + let results_match = states_equal(state_st, state_mt); + + BenchmarkResult { + name: name.to_string(), + basic_time, + mt_time, + results_match, + } +} + +fn states_equal(a: &QuantumState, b: &QuantumState) -> bool { + use crate::maths::vector::Vector; + if a.size() != b.size() { + return false; + } + for i in 0..a.size() { + let amp_a = a.get(i); + let amp_b = b.get(i); + let diff_real = (amp_a.real - amp_b.real).abs(); + let diff_imag = (amp_a.imaginary - amp_b.imaginary).abs(); + if diff_real > 1e-10 || diff_imag > 1e-10 { + return false; + } + } + true +} + +fn format_duration(d: Duration) -> String { + if d.as_secs() > 0 { + format!("{:.3}s", d.as_secs_f64()) + } else if d.as_millis() > 0 { + format!("{:.3}ms", d.as_secs_f64() * 1000.0) + } else { + format!("{:.3}us", d.as_secs_f64() * 1_000_000.0) + } +} + +fn print_benchmark_table(results: &[BenchmarkResult]) { + // Column widths (content only, not including borders) + const C1: usize = 30; // Circuit name + const C2: usize = 12; // BasicRT + const C3: usize = 12; // BasicRTMT + const C4: usize = 10; // Speedup + const C5: usize = 5; // Match + + let top = format!( + "╔{}═{}═{}═{}═{}╗", + "═".repeat(C1 + 2), + "═".repeat(C2 + 2), + "═".repeat(C3 + 2), + "═".repeat(C4 + 2), + "═".repeat(C5 + 2) + ); + let title = format!( + "╠{}╤{}╤{}╤{}╤{}╣", + "═".repeat(C1 + 2), + "═".repeat(C2 + 2), + "═".repeat(C3 + 2), + "═".repeat(C4 + 2), + "═".repeat(C5 + 2) + ); + let header = format!( + "╠{}╪{}╪{}╪{}╪{}╣", + "═".repeat(C1 + 2), + "═".repeat(C2 + 2), + "═".repeat(C3 + 2), + "═".repeat(C4 + 2), + "═".repeat(C5 + 2) + ); + let bottom = format!( + "╚{}╧{}╧{}╧{}╧{}╝", + "═".repeat(C1 + 2), + "═".repeat(C2 + 2), + "═".repeat(C3 + 2), + "═".repeat(C4 + 2), + "═".repeat(C5 + 2) + ); + + let total_width = C1 + C2 + C3 + C4 + C5 + 14; + + println!("\n{}", top); + println!( + "║{:^width$}║", + "RUNTIME BENCHMARK RESULTS", + width = total_width + ); + println!("{}", title); + println!( + "║ {:<C1$} │ {:^C2$} │ {:^C3$} │ {:^C4$} │ {:^C5$} ║", + "Circuit", + "BasicRT", + "BasicRTMT", + "Speedup", + "Match", + C1 = C1, + C2 = C2, + C3 = C3, + C4 = C4, + C5 = C5 + ); + println!("{}", header); + + for r in results { + let speedup = r.basic_time.as_secs_f64() / r.mt_time.as_secs_f64(); + let speedup_str = format!("{:.2}x", speedup); + let match_str = if r.results_match { "✓" } else { "✗" }; + + println!( + "║ {:<C1$} │ {:>C2$} │ {:>C3$} │ {:>C4$} │ {:^C5$} ║", + r.name, + format_duration(r.basic_time), + format_duration(r.mt_time), + speedup_str, + match_str, + C1 = C1, + C2 = C2, + C3 = C3, + C4 = C4, + C5 = C5 + ); + } + + println!("{}", bottom); +} fn main() { - println!("Bell State with Measurement\n"); - let mut bell = QuantumCircuit::with_classical(2, 2); - bell.h(0).cnot(0, 1).measure(0, 0).measure(1, 1); + println!("═══════════════════════════════════════════════════════════════"); + println!(" PSI Quantum Simulator"); + println!("═══════════════════════════════════════════════════════════════\n"); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&bell)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&bell)); + let mut results: Vec<BenchmarkResult> = Vec::new(); - bell.compute(); - println!("{}", bell); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Bell State with Measurement │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - print!("------\n\n"); + let bell_builder = || { + let mut circuit = QuantumCircuit::with_classical(2, 2); + circuit.h(0).cnot(0, 1).measure(0, 0).measure(1, 1); + circuit + }; - println!("GHZ State\n"); - let mut ghz = QuantumCircuit::new(3); - ghz.h(0).cnot(0, 1).cnot(0, 2); + let bell = bell_builder(); + println!("Horizontal:\n{}", HorizontalRenderer::new(&bell)); + println!("Vertical:\n{}", VerticalRenderer::new(&bell)); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&ghz)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&ghz)); + results.push(benchmark_circuit("Bell State (2 qubits)", bell_builder)); + let mut bell_display = bell_builder(); + bell_display.compute(); + println!("{}\n", bell_display); - ghz.compute(); - println!("{}", ghz); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ GHZ State │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - print!("------\n\n"); + let ghz_builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit.h(0).cnot(0, 1).cnot(0, 2); + circuit + }; - println!("SWAP via 3 CNOTs\n"); - let mut swap_circuit = QuantumCircuit::new(2); - swap_circuit.x(0).cnot(0, 1).cnot(1, 0).cnot(0, 1); + let ghz = ghz_builder(); + println!("Horizontal:\n{}", HorizontalRenderer::new(&ghz)); + println!("Vertical:\n{}", VerticalRenderer::new(&ghz)); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&swap_circuit)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&swap_circuit)); + results.push(benchmark_circuit("GHZ State (3 qubits)", ghz_builder)); + let mut ghz_display = ghz_builder(); + ghz_display.compute(); + println!("{}\n", ghz_display); - swap_circuit.compute(); - println!("{}", swap_circuit); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ SWAP via 3 CNOTs │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - print!("------\n\n"); + let swap_builder = || { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).cnot(0, 1).cnot(1, 0).cnot(0, 1); + circuit + }; - println!("Toffoli Gate\n"); - let mut toffoli_circuit = QuantumCircuit::new(3); - toffoli_circuit.x(0).x(1).toffoli(0, 1, 2); + let swap_circuit = swap_builder(); + println!("Horizontal:\n{}", HorizontalRenderer::new(&swap_circuit)); + println!("Vertical:\n{}", VerticalRenderer::new(&swap_circuit)); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&toffoli_circuit)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&toffoli_circuit)); + results.push(benchmark_circuit("SWAP via CNOTs (2 qubits)", swap_builder)); + let mut swap_display = swap_builder(); + swap_display.compute(); + println!("{}\n", swap_display); - toffoli_circuit.compute(); - println!("{}", toffoli_circuit); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Toffoli Gate │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - print!("------\n\n"); + let toffoli_builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit.x(0).x(1).toffoli(0, 1, 2); + circuit + }; - println!("Full Circuit with Measurements\n"); - let mut full = QuantumCircuit::with_classical(3, 3); - full.h(0).h(1).h(2).measure_all(); + let toffoli = toffoli_builder(); + println!("Horizontal:\n{}", HorizontalRenderer::new(&toffoli)); + println!("Vertical:\n{}", VerticalRenderer::new(&toffoli)); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&full)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&full)); + results.push(benchmark_circuit("Toffoli (3 qubits)", toffoli_builder)); + let mut toffoli_display = toffoli_builder(); + toffoli_display.compute(); + println!("{}\n", toffoli_display); - full.compute(); - println!("{}", full); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Full Circuit with Measurements │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - print!("------\n\n"); + let full_builder = || { + let mut circuit = QuantumCircuit::with_classical(3, 3); + circuit.h(0).h(1).h(2).measure_all(); + circuit + }; - println!("Complex Circuit\n"); - let mut complex = QuantumCircuit::with_classical(4, 2); - complex - .h(0) - .h(1) - .cnot(0, 2) - .cnot(1, 3) - .cz(2, 3) - .swap(0, 1) - .measure(0, 0) - .measure(1, 1); + let full = full_builder(); + println!("Horizontal:\n{}", HorizontalRenderer::new(&full)); + println!("Vertical:\n{}", VerticalRenderer::new(&full)); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&complex)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&complex)); + results.push(benchmark_circuit( + "3-qubit Hadamard + Measure", + full_builder, + )); + let mut full_display = full_builder(); + full_display.compute(); + println!("{}\n", full_display); - complex.compute(); - println!("{}", complex); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Complex Circuit │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - print!("------\n\n"); + let complex_builder = || { + let mut circuit = QuantumCircuit::with_classical(4, 2); + circuit + .h(0) + .h(1) + .cnot(0, 2) + .cnot(1, 3) + .cz(2, 3) + .swap(0, 1) + .measure(0, 0) + .measure(1, 1); + circuit + }; - println!("Custom Gate: Bell Pair Creator\n"); + let complex = complex_builder(); + println!("Horizontal:\n{}", HorizontalRenderer::new(&complex)); + println!("Vertical:\n{}", VerticalRenderer::new(&complex)); - let bell_gate = CustomGateBuilder::new("BELL", 2).h(0).cnot(0, 1).build(); + results.push(benchmark_circuit("Complex (4 qubits)", complex_builder)); + let mut complex_display = complex_builder(); + complex_display.compute(); + println!("{}\n", complex_display); + + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Custom Gate: Bell Pair Creator │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - let mut custom_circuit = QuantumCircuit::new(4); - custom_circuit - .apply_custom(bell_gate.clone(), &[0, 1]) - .apply_custom(bell_gate.clone(), &[2, 3]); + let bell_gate = CustomGateBuilder::new("BELL", 2).h(0).cnot(0, 1).build(); + let bell_gate_clone = bell_gate.clone(); - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&custom_circuit)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&custom_circuit)); + let custom_bell_builder = move || { + let mut circuit = QuantumCircuit::new(4); + circuit + .apply_custom(bell_gate_clone.clone(), &[0, 1]) + .apply_custom(bell_gate_clone.clone(), &[2, 3]); + circuit + }; - custom_circuit.compute(); - println!("{}", custom_circuit); + let custom_bell = { + let mut circuit = QuantumCircuit::new(4); + circuit + .apply_custom(bell_gate.clone(), &[0, 1]) + .apply_custom(bell_gate.clone(), &[2, 3]); + circuit + }; + println!("Horizontal:\n{}", HorizontalRenderer::new(&custom_bell)); + println!("Vertical:\n{}", VerticalRenderer::new(&custom_bell)); - print!("------\n\n"); + results.push(benchmark_circuit( + "Custom BELL (4 qubits)", + custom_bell_builder, + )); + let mut custom_bell_display = { + let mut circuit = QuantumCircuit::new(4); + circuit + .apply_custom(bell_gate.clone(), &[0, 1]) + .apply_custom(bell_gate.clone(), &[2, 3]); + circuit + }; + custom_bell_display.compute(); + println!("{}\n", custom_bell_display); - println!("Custom Gate: Swap via CNOTs\n"); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Custom Gate: Swap via CNOTs │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); let swap_gate = CustomGateBuilder::new("MYSWAP", 2) .cnot(0, 1) .cnot(1, 0) .cnot(0, 1) .build(); + let swap_gate_clone = swap_gate.clone(); - let mut swap_test = QuantumCircuit::new(2); - swap_test.x(0).apply_custom(swap_gate, &[0, 1]); + let custom_swap_builder = move || { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).apply_custom(swap_gate_clone.clone(), &[0, 1]); + circuit + }; - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&swap_test)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&swap_test)); + let custom_swap = { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).apply_custom(swap_gate.clone(), &[0, 1]); + circuit + }; + println!("Horizontal:\n{}", HorizontalRenderer::new(&custom_swap)); + println!("Vertical:\n{}", VerticalRenderer::new(&custom_swap)); - swap_test.compute(); - println!("{}", swap_test); + results.push(benchmark_circuit( + "Custom SWAP (2 qubits)", + custom_swap_builder, + )); + let mut custom_swap_display = { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).apply_custom(swap_gate.clone(), &[0, 1]); + circuit + }; + custom_swap_display.compute(); + println!("{}\n", custom_swap_display); - print!("------\n\n"); + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Custom Gate: Matrix-defined √X gate │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); - println!("Custom Gate: Matrix-defined √X gate\n"); - - // √X gate (square root of NOT) - // When applied twice, it equals X gate let sqrt_x_matrix = matrix!( [complex!(0.5, 0.5), complex!(0.5, -0.5)]; [complex!(0.5, -0.5), complex!(0.5, 0.5)] ); - let sqrt_x = CustomGate::from_matrix("√X", sqrt_x_matrix); + let sqrt_x_clone = sqrt_x.clone(); - let mut matrix_test = QuantumCircuit::new(1); - matrix_test - .apply_custom(sqrt_x.clone(), &[0]) - .apply_custom(sqrt_x, &[0]); // Two √X = X + let sqrt_x_builder = move || { + let mut circuit = QuantumCircuit::new(1); + circuit + .apply_custom(sqrt_x_clone.clone(), &[0]) + .apply_custom(sqrt_x_clone.clone(), &[0]); + circuit + }; - println!("Horizontal:"); - println!("{}", HorizontalRenderer::new(&matrix_test)); - println!("Vertical:"); - println!("{}", VerticalRenderer::new(&matrix_test)); + let sqrt_x_circuit = { + let mut circuit = QuantumCircuit::new(1); + circuit + .apply_custom(sqrt_x.clone(), &[0]) + .apply_custom(sqrt_x.clone(), &[0]); + circuit + }; + println!("Horizontal:\n{}", HorizontalRenderer::new(&sqrt_x_circuit)); + println!("Vertical:\n{}", VerticalRenderer::new(&sqrt_x_circuit)); - matrix_test.compute(); - println!("{}", matrix_test); - println!("(Two √X gates should equal X, so |0⟩ becomes |1⟩)"); + results.push(benchmark_circuit("√X gate (1 qubit)", sqrt_x_builder)); + let mut sqrt_x_display = { + let mut circuit = QuantumCircuit::new(1); + circuit + .apply_custom(sqrt_x.clone(), &[0]) + .apply_custom(sqrt_x.clone(), &[0]); + circuit + }; + sqrt_x_display.compute(); + println!("{}", sqrt_x_display); + println!("(Two √X gates should equal X, so |0⟩ becomes |1⟩)\n"); + + println!("┌─────────────────────────────────────────────────────────────┐"); + println!("│ Larger Circuits (for benchmark comparison) │"); + println!("└─────────────────────────────────────────────────────────────┘\n"); + + let large_8_builder = || { + let mut circuit = QuantumCircuit::new(8); + for i in 0..8 { + circuit.h(i); + } + for i in 0..7 { + circuit.cnot(i, i + 1); + } + circuit + }; + println!("8-qubit entangled:"); + println!("{}", HorizontalRenderer::new(&large_8_builder())); + results.push(benchmark_circuit("8-qubit entangled", large_8_builder)); + + let large_10_builder = || { + let mut circuit = QuantumCircuit::new(10); + for i in 0..10 { + circuit.h(i); + } + for i in 0..9 { + circuit.cnot(i, i + 1); + } + circuit.cz(0, 9); + circuit + }; + println!("10-qubit entangled:"); + println!("{}", HorizontalRenderer::new(&large_10_builder())); + results.push(benchmark_circuit("10-qubit entangled", large_10_builder)); + + let large_12_builder = || { + let mut circuit = QuantumCircuit::new(12); + for i in 0..12 { + circuit.h(i); + } + for i in 0..11 { + circuit.cnot(i, i + 1); + } + circuit.cz(0, 11); + circuit.swap(5, 6); + circuit + }; + println!("12-qubit entangled:"); + println!("{}", HorizontalRenderer::new(&large_12_builder())); + results.push(benchmark_circuit("12-qubit entangled", large_12_builder)); + + let large_14_builder = || { + let mut circuit = QuantumCircuit::new(14); + for i in 0..14 { + circuit.h(i); + } + for i in 0..13 { + circuit.cnot(i, i + 1); + } + circuit + }; + println!("14-qubit entangled:"); + println!("{}", HorizontalRenderer::new(&large_14_builder())); + results.push(benchmark_circuit("14-qubit entangled", large_14_builder)); + + print_benchmark_table(&results); + + let all_match = results.iter().all(|r| r.results_match); + println!("\n"); + if all_match { + println!("✓ All circuits produced identical results with both runtimes!"); + } else { + println!("✗ WARNING: Some circuits produced different results!"); + } + + let total_basic: Duration = results.iter().map(|r| r.basic_time).sum(); + let total_mt: Duration = results.iter().map(|r| r.mt_time).sum(); + let overall_speedup = total_basic.as_secs_f64() / total_mt.as_secs_f64(); + + println!( + "\nTotal time - BasicRT: {} | BasicRTMT: {} | Overall speedup: {:.2}x", + format_duration(total_basic), + format_duration(total_mt), + overall_speedup + ); } |
