aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpsi-core/Cargo.toml1
-rw-r--r--libpsi-core/src/core/circuit.rs43
-rw-r--r--libpsi-core/src/core/mod.rs2
-rw-r--r--libpsi-core/src/core/runtime.rs175
-rw-r--r--libpsi-core/src/lib.rs1
-rw-r--r--tester/src/main.rs540
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
+ );
}