diff options
| -rw-r--r-- | libpsi-core/src/core/circuit.rs | 191 | ||||
| -rw-r--r-- | libpsi-core/src/core/gates.rs | 126 | ||||
| -rw-r--r-- | libpsi-core/src/core/runtime.rs | 160 | ||||
| -rw-r--r-- | libpsi-visualizer/src/cli/horizontal_cli.rs | 174 | ||||
| -rw-r--r-- | libpsi-visualizer/src/cli/vertical_cli.rs | 47 | ||||
| -rw-r--r-- | tester/src/benchmarks.rs | 90 | ||||
| -rw-r--r-- | tester/src/clifford.rs | 126 | ||||
| -rw-r--r-- | tester/src/common.rs | 171 | ||||
| -rw-r--r-- | tester/src/custom_gates.rs | 121 | ||||
| -rw-r--r-- | tester/src/main.rs | 524 | ||||
| -rw-r--r-- | tester/src/non_clifford.rs | 137 |
11 files changed, 1370 insertions, 497 deletions
diff --git a/libpsi-core/src/core/circuit.rs b/libpsi-core/src/core/circuit.rs index 5bb17ff..e5d7847 100644 --- a/libpsi-core/src/core/circuit.rs +++ b/libpsi-core/src/core/circuit.rs @@ -1,7 +1,7 @@ use super::{CustomGate, QuantumState, Runtime}; use crate::{format_amplitude, format_probability, Vector}; -use std::sync::Arc; use core::fmt; +use std::sync::Arc; #[derive(Clone)] pub enum GateOp { @@ -11,9 +11,24 @@ pub enum GateOp { Z(usize), S(usize), T(usize), + Sdg(usize), + Tdg(usize), + Sx(usize), + Sxdg(usize), + Rx(usize, f64), + Ry(usize, f64), + Rz(usize, f64), + P(usize, f64), + U1(usize, f64), + U2(usize, f64, f64), + U3(usize, f64, f64, f64), CNOT(usize, usize), CZ(usize, usize), SWAP(usize, usize), + CRx(usize, usize, f64), + CRy(usize, usize, f64), + CRz(usize, usize, f64), + CP(usize, usize, f64), CCNOT(usize, usize, usize), CSWAP(usize, usize, usize), Measure(usize, usize), @@ -29,6 +44,21 @@ impl GateOp { GateOp::Z(_) => "Z", GateOp::S(_) => "S", GateOp::T(_) => "T", + GateOp::Sdg(_) => "S†", + GateOp::Tdg(_) => "T†", + GateOp::Sx(_) => "√X", + GateOp::Sxdg(_) => "√X†", + GateOp::Rx(_, _) => "Rx", + GateOp::Ry(_, _) => "Ry", + GateOp::Rz(_, _) => "Rz", + GateOp::P(_, _) => "P", + GateOp::U1(_, _) => "U1", + GateOp::U2(_, _, _) => "U2", + GateOp::U3(_, _, _, _) => "U3", + GateOp::CRx(_, _, _) => "CRx", + GateOp::CRy(_, _, _) => "CRy", + GateOp::CRz(_, _, _) => "CRz", + GateOp::CP(_, _, _) => "CP", GateOp::CNOT(_, _) => "CNOT", GateOp::CZ(_, _) => "CZ", GateOp::SWAP(_, _) => "SWAP", @@ -41,8 +71,30 @@ impl GateOp { pub fn quantum_targets(&self) -> Vec<usize> { match self { - GateOp::H(t) | GateOp::X(t) | GateOp::Y(t) | GateOp::Z(t) | GateOp::S(t) | GateOp::T(t) => vec![*t], - GateOp::CNOT(c, t) | GateOp::CZ(c, t) | GateOp::SWAP(c, t) => vec![*c, *t], + GateOp::H(t) + | GateOp::X(t) + | GateOp::Y(t) + | GateOp::Z(t) + | GateOp::S(t) + | GateOp::T(t) + | GateOp::Sdg(t) + | GateOp::Tdg(t) + | GateOp::Sx(t) + | GateOp::Sxdg(t) + | GateOp::Rx(t, _) + | GateOp::Ry(t, _) + | GateOp::Rz(t, _) + | GateOp::P(t, _) + | GateOp::U1(t, _) + | GateOp::U2(t, _, _) + | GateOp::U3(t, _, _, _) => vec![*t], + GateOp::CNOT(c, t) + | GateOp::CZ(c, t) + | GateOp::SWAP(c, t) + | GateOp::CRx(c, t, _) + | GateOp::CRy(c, t, _) + | GateOp::CRz(c, t, _) + | GateOp::CP(c, t, _) => vec![*c, *t], GateOp::CCNOT(c1, c2, t) | GateOp::CSWAP(c1, c2, t) => vec![*c1, *c2, *t], GateOp::Measure(q, _) => vec![*q], GateOp::Custom(_, targets) => targets.clone(), @@ -63,6 +115,27 @@ impl GateOp { pub fn is_custom(&self) -> bool { matches!(self, GateOp::Custom(_, _)) } + + pub fn is_non_clifford(&self) -> bool { + matches!( + self, + GateOp::T(_) + | GateOp::Tdg(_) + | GateOp::Sx(_) + | GateOp::Sxdg(_) + | GateOp::Rx(_, _) + | GateOp::Ry(_, _) + | GateOp::Rz(_, _) + | GateOp::P(_, _) + | GateOp::U1(_, _) + | GateOp::U2(_, _, _) + | GateOp::U3(_, _, _, _) + | GateOp::CRx(_, _, _) + | GateOp::CRy(_, _, _) + | GateOp::CRz(_, _, _) + | GateOp::CP(_, _, _) + ) + } } pub struct QuantumCircuit { @@ -164,6 +237,96 @@ impl QuantumCircuit { self } + pub fn sdg(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Sdg(target)); + self.computed_state = None; + self + } + + pub fn tdg(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Tdg(target)); + self.computed_state = None; + self + } + + pub fn sx(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Sx(target)); + self.computed_state = None; + self + } + + pub fn sxdg(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Sxdg(target)); + self.computed_state = None; + self + } + + pub fn rx(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::Rx(target, theta)); + self.computed_state = None; + self + } + + pub fn ry(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::Ry(target, theta)); + self.computed_state = None; + self + } + + pub fn rz(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::Rz(target, theta)); + self.computed_state = None; + self + } + + pub fn p(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::P(target, theta)); + self.computed_state = None; + self + } + + pub fn u1(&mut self, target: usize, lambda: f64) -> &mut Self { + self.operations.push(GateOp::U1(target, lambda)); + self.computed_state = None; + self + } + + pub fn u2(&mut self, target: usize, phi: f64, lambda: f64) -> &mut Self { + self.operations.push(GateOp::U2(target, phi, lambda)); + self.computed_state = None; + self + } + + pub fn u3(&mut self, target: usize, theta: f64, phi: f64, lambda: f64) -> &mut Self { + self.operations.push(GateOp::U3(target, theta, phi, lambda)); + self.computed_state = None; + self + } + + pub fn crx(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CRx(control, target, theta)); + self.computed_state = None; + self + } + + pub fn cry(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CRy(control, target, theta)); + self.computed_state = None; + self + } + + pub fn crz(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CRz(control, target, theta)); + self.computed_state = None; + self + } + + pub fn cp(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CP(control, target, theta)); + self.computed_state = None; + self + } + pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self { self.operations.push(GateOp::CNOT(control, target)); self.computed_state = None; @@ -187,7 +350,8 @@ impl QuantumCircuit { } pub fn ccnot(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self { - self.operations.push(GateOp::CCNOT(control1, control2, target)); + self.operations + .push(GateOp::CCNOT(control1, control2, target)); self.computed_state = None; self } @@ -197,7 +361,8 @@ impl QuantumCircuit { } pub fn cswap(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self { - self.operations.push(GateOp::CSWAP(control, target1, target2)); + self.operations + .push(GateOp::CSWAP(control, target1, target2)); self.computed_state = None; self } @@ -222,13 +387,15 @@ impl QuantumCircuit { } pub fn custom(&mut self, gate: &Arc<CustomGate>, targets: &[usize]) -> &mut Self { - self.operations.push(GateOp::Custom(Arc::clone(gate), targets.to_vec())); + self.operations + .push(GateOp::Custom(Arc::clone(gate), targets.to_vec())); self.computed_state = None; self } pub fn apply_custom(&mut self, gate: CustomGate, targets: &[usize]) -> &mut Self { - self.operations.push(GateOp::Custom(Arc::new(gate), targets.to_vec())); + self.operations + .push(GateOp::Custom(Arc::new(gate), targets.to_vec())); self.computed_state = None; self } @@ -268,12 +435,18 @@ impl QuantumCircuit { impl fmt::Display for QuantumCircuit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "QuantumCircuit ({} qubits, {} classical)", self.num_qubits, self.num_classical)?; + writeln!( + f, + "QuantumCircuit ({} qubits, {} classical)", + self.num_qubits, self.num_classical + )?; writeln!(f, "Operations:")?; for (i, op) in self.operations.iter().enumerate() { match op { GateOp::Measure(q, c) => writeln!(f, " {}: {} q{} → c{}", i, op.name(), q, c)?, - GateOp::Custom(gate, targets) => writeln!(f, " {}: [{}] on {:?}", i, gate.name, targets)?, + GateOp::Custom(gate, targets) => { + writeln!(f, " {}: [{}] on {:?}", i, gate.name, targets)? + } _ => writeln!(f, " {}: {} on {:?}", i, op.name(), op.quantum_targets())?, } } diff --git a/libpsi-core/src/core/gates.rs b/libpsi-core/src/core/gates.rs index 4f922a8..cee4ebc 100644 --- a/libpsi-core/src/core/gates.rs +++ b/libpsi-core/src/core/gates.rs @@ -1,4 +1,100 @@ -use crate::{complex, matrix, QuantumGate}; +use crate::{complex, matrix, Complex, Matrix, QuantumGate}; +use std::f64::consts::FRAC_1_SQRT_2; + +pub fn rx_matrix(theta: f64) -> Matrix<Complex<f64>> { + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + matrix!( + [complex!(cos, 0.0), complex!(0.0, -sin)]; + [complex!(0.0, -sin), complex!(cos, 0.0)] + ) +} + +pub fn ry_matrix(theta: f64) -> Matrix<Complex<f64>> { + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + matrix!( + [complex!(cos, 0.0), complex!(-sin, 0.0)]; + [complex!(sin, 0.0), complex!(cos, 0.0)] + ) +} + +pub fn rz_matrix(theta: f64) -> Matrix<Complex<f64>> { + let half = theta / 2.0; + matrix!( + [complex!(half.cos(), -half.sin()), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(half.cos(), half.sin())] + ) +} + +pub fn p_matrix(theta: f64) -> Matrix<Complex<f64>> { + matrix!( + [complex!(1.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(theta.cos(), theta.sin())] + ) +} + +pub fn u1_matrix(lambda: f64) -> Matrix<Complex<f64>> { + p_matrix(lambda) +} + +pub fn u2_matrix(phi: f64, lambda: f64) -> Matrix<Complex<f64>> { + let inv_sqrt2 = FRAC_1_SQRT_2; + matrix!( + [complex!(inv_sqrt2, 0.0), complex!(-inv_sqrt2 * lambda.cos(), -inv_sqrt2 * lambda.sin())]; + [complex!(inv_sqrt2 * phi.cos(), inv_sqrt2 * phi.sin()), complex!((phi + lambda).cos() * inv_sqrt2, (phi + lambda).sin() * inv_sqrt2)] + ) +} + +pub fn u3_matrix(theta: f64, phi: f64, lambda: f64) -> Matrix<Complex<f64>> { + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + matrix!( + [complex!(cos, 0.0), complex!(-sin * lambda.cos(), -sin * lambda.sin())]; + [complex!(sin * phi.cos(), sin * phi.sin()), complex!(cos * (phi + lambda).cos(), cos * (phi + lambda).sin())] + ) +} + +pub fn crx_matrix(theta: f64) -> Matrix<Complex<f64>> { + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + matrix!( + [complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(cos, 0.0), complex!(0.0, -sin)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, -sin), complex!(cos, 0.0)] + ) +} + +pub fn cry_matrix(theta: f64) -> Matrix<Complex<f64>> { + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + matrix!( + [complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(cos, 0.0), complex!(-sin, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(sin, 0.0), complex!(cos, 0.0)] + ) +} + +pub fn crz_matrix(theta: f64) -> Matrix<Complex<f64>> { + let half = theta / 2.0; + matrix!( + [complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(half.cos(), -half.sin()), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(half.cos(), half.sin())] + ) +} + +pub fn cp_matrix(theta: f64) -> Matrix<Complex<f64>> { + matrix!( + [complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(1.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(1.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(0.0, 0.0), complex!(theta.cos(), theta.sin())] + ) +} #[rustfmt::skip] lazy_static::lazy_static! { @@ -45,6 +141,34 @@ lazy_static::lazy_static! { num_qubits: 1, }; + pub static ref SDG_GATE: QuantumGate<'static> = QuantumGate { + name: "S†", + matrix: matrix!([complex!(1.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(0.0, -1.0)]), + num_qubits: 1, + }; + + pub static ref TDG_GATE: QuantumGate<'static> = QuantumGate { + name: "T†", + matrix: matrix!([complex!(1.0, 0.0), complex!(0.0, 0.0)]; + [complex!(0.0, 0.0), complex!(core::f64::consts::FRAC_1_SQRT_2, -core::f64::consts::FRAC_1_SQRT_2)]), + num_qubits: 1, + }; + + pub static ref SX_GATE: QuantumGate<'static> = QuantumGate { + name: "√X", + matrix: matrix!([complex!(0.5, 0.5), complex!(0.5, -0.5)]; + [complex!(0.5, -0.5), complex!(0.5, 0.5)]), + num_qubits: 1, + }; + + pub static ref SXDG_GATE: QuantumGate<'static> = QuantumGate { + name: "√X†", + matrix: matrix!([complex!(0.5, -0.5), complex!(0.5, 0.5)]; + [complex!(0.5, 0.5), complex!(0.5, -0.5)]), + num_qubits: 1, + }; + pub static ref IDENTITY: QuantumGate<'static> = QuantumGate { name: "I", matrix: matrix!([complex!(1.0, 0.0), complex!(0.0, 0.0)]; diff --git a/libpsi-core/src/core/runtime.rs b/libpsi-core/src/core/runtime.rs index 7b73c47..0a78f0f 100644 --- a/libpsi-core/src/core/runtime.rs +++ b/libpsi-core/src/core/runtime.rs @@ -1,5 +1,10 @@ use super::{GateOp, QuantumGate, QuantumRegister, QuantumState}; -use crate::gates::*; +use crate::gates::{ + cp_matrix, crx_matrix, cry_matrix, crz_matrix, p_matrix, rx_matrix, ry_matrix, + rz_matrix, u1_matrix, u2_matrix, u3_matrix, CNOT, CZ, FREDKIN, HADAMARD, + PAULI_X, PAULI_Y, PAULI_Z, SDG_GATE, SWAP, SXDG_GATE, SX_GATE, S_GATE, TDG_GATE, + TOFFOLI, T_GATE, +}; use crate::maths::vector::Vector; use crate::{complex, Complex, Matrix}; use rayon::prelude::*; @@ -48,17 +53,118 @@ impl Runtime { for op in operations { match op { + // Clifford gates 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]), + + // Non-Clifford fixed gates + GateOp::T(t) => register.apply_gate(&T_GATE, &[*t]), + GateOp::Sdg(t) => register.apply_gate(&SDG_GATE, &[*t]), + GateOp::Tdg(t) => register.apply_gate(&TDG_GATE, &[*t]), + GateOp::Sx(t) => register.apply_gate(&SX_GATE, &[*t]), + GateOp::Sxdg(t) => register.apply_gate(&SXDG_GATE, &[*t]), + + // Parametric single-qubit gates (non-Clifford for most angles) + GateOp::Rx(t, theta) => { + let gate = QuantumGate { + name: "Rx", + matrix: rx_matrix(*theta), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + GateOp::Ry(t, theta) => { + let gate = QuantumGate { + name: "Ry", + matrix: ry_matrix(*theta), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + GateOp::Rz(t, theta) => { + let gate = QuantumGate { + name: "Rz", + matrix: rz_matrix(*theta), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + GateOp::P(t, theta) => { + let gate = QuantumGate { + name: "P", + matrix: p_matrix(*theta), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + GateOp::U1(t, lambda) => { + let gate = QuantumGate { + name: "U1", + matrix: u1_matrix(*lambda), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + GateOp::U2(t, phi, lambda) => { + let gate = QuantumGate { + name: "U2", + matrix: u2_matrix(*phi, *lambda), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + GateOp::U3(t, theta, phi, lambda) => { + let gate = QuantumGate { + name: "U3", + matrix: u3_matrix(*theta, *phi, *lambda), + num_qubits: 1, + }; + register.apply_gate(&gate, &[*t]); + } + + // Controlled parametric gates + GateOp::CRx(c, t, theta) => { + let gate = QuantumGate { + name: "CRx", + matrix: crx_matrix(*theta), + num_qubits: 2, + }; + register.apply_gate(&gate, &[*c, *t]); + } + GateOp::CRy(c, t, theta) => { + let gate = QuantumGate { + name: "CRy", + matrix: cry_matrix(*theta), + num_qubits: 2, + }; + register.apply_gate(&gate, &[*c, *t]); + } + GateOp::CRz(c, t, theta) => { + let gate = QuantumGate { + name: "CRz", + matrix: crz_matrix(*theta), + num_qubits: 2, + }; + register.apply_gate(&gate, &[*c, *t]); + } + GateOp::CP(c, t, theta) => { + let gate = QuantumGate { + name: "CP", + matrix: cp_matrix(*theta), + num_qubits: 2, + }; + register.apply_gate(&gate, &[*c, *t]); + } + + // Measurement and custom gates GateOp::Measure(_, _) => {} GateOp::Custom(gate, targets) => { let quantum_gate = gate.to_quantum_gate(); @@ -83,18 +189,42 @@ impl Runtime { 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]), + let (gate_matrix, targets): (Matrix<Complex<f64>>, Vec<usize>) = match op { + // Clifford gates + GateOp::H(t) => (HADAMARD.matrix.clone(), vec![*t]), + GateOp::X(t) => (PAULI_X.matrix.clone(), vec![*t]), + GateOp::Y(t) => (PAULI_Y.matrix.clone(), vec![*t]), + GateOp::Z(t) => (PAULI_Z.matrix.clone(), vec![*t]), + GateOp::S(t) => (S_GATE.matrix.clone(), vec![*t]), + GateOp::CNOT(c, t) => (CNOT.matrix.clone(), vec![*c, *t]), + GateOp::CZ(c, t) => (CZ.matrix.clone(), vec![*c, *t]), + GateOp::SWAP(a, b) => (SWAP.matrix.clone(), vec![*a, *b]), + GateOp::CCNOT(c1, c2, t) => (TOFFOLI.matrix.clone(), vec![*c1, *c2, *t]), + GateOp::CSWAP(c, t1, t2) => (FREDKIN.matrix.clone(), vec![*c, *t1, *t2]), + + // Non-Clifford fixed gates + GateOp::T(t) => (T_GATE.matrix.clone(), vec![*t]), + GateOp::Sdg(t) => (SDG_GATE.matrix.clone(), vec![*t]), + GateOp::Tdg(t) => (TDG_GATE.matrix.clone(), vec![*t]), + GateOp::Sx(t) => (SX_GATE.matrix.clone(), vec![*t]), + GateOp::Sxdg(t) => (SXDG_GATE.matrix.clone(), vec![*t]), + + // Parametric single-qubit gates + GateOp::Rx(t, theta) => (rx_matrix(*theta), vec![*t]), + GateOp::Ry(t, theta) => (ry_matrix(*theta), vec![*t]), + GateOp::Rz(t, theta) => (rz_matrix(*theta), vec![*t]), + GateOp::P(t, theta) => (p_matrix(*theta), vec![*t]), + GateOp::U1(t, lambda) => (u1_matrix(*lambda), vec![*t]), + GateOp::U2(t, phi, lambda) => (u2_matrix(*phi, *lambda), vec![*t]), + GateOp::U3(t, theta, phi, lambda) => (u3_matrix(*theta, *phi, *lambda), vec![*t]), + + // Controlled parametric gates + GateOp::CRx(c, t, theta) => (crx_matrix(*theta), vec![*c, *t]), + GateOp::CRy(c, t, theta) => (cry_matrix(*theta), vec![*c, *t]), + GateOp::CRz(c, t, theta) => (crz_matrix(*theta), vec![*c, *t]), + GateOp::CP(c, t, theta) => (cp_matrix(*theta), vec![*c, *t]), + + // Measurement (skip) and custom gates GateOp::Measure(_, _) => continue, GateOp::Custom(custom_gate, tgts) => { let quantum_gate = custom_gate.to_quantum_gate(); @@ -103,7 +233,7 @@ impl Runtime { } }; - state = apply_gate_parallel(&state, &gate.matrix, &targets, num_qubits); + state = apply_gate_parallel(&state, &gate_matrix, &targets, num_qubits); } QuantumState::new(state) diff --git a/libpsi-visualizer/src/cli/horizontal_cli.rs b/libpsi-visualizer/src/cli/horizontal_cli.rs index bac0c5e..f1a6ba4 100644 --- a/libpsi-visualizer/src/cli/horizontal_cli.rs +++ b/libpsi-visualizer/src/cli/horizontal_cli.rs @@ -144,6 +144,180 @@ impl<'a> fmt::Display for HorizontalRenderer<'a> { } gap_line.push_str(" "); } + GateOp::Sdg(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[S†]─"); + } else { + line.push_str("──────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("══════"); + } + gap_line.push_str(" "); + } + GateOp::Tdg(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[T†]─"); + } else { + line.push_str("──────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("══════"); + } + gap_line.push_str(" "); + } + GateOp::Sx(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[√X]─"); + } else { + line.push_str("──────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("══════"); + } + gap_line.push_str(" "); + } + GateOp::Sxdg(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[√X†]─"); + } else { + line.push_str("───────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═══════"); + } + gap_line.push_str(" "); + } + GateOp::Rx(t, theta) => { + let label = format!("[Rx({:.2})]", theta); + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::Ry(t, theta) => { + let label = format!("[Ry({:.2})]", theta); + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::Rz(t, theta) => { + let label = format!("[Rz({:.2})]", theta); + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::P(t, theta) => { + let label = format!("[P({:.2})]", theta); + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::U1(t, lambda) => { + let label = format!("[U1({:.2})]", lambda); + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::U2(t, _, _) => { + let label = "[U2]"; + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::U3(t, _, _, _) => { + let label = "[U3]"; + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str(&format!("─{}─", label)); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } + GateOp::CRx(c, t, theta) | GateOp::CRy(c, t, theta) | GateOp::CRz(c, t, theta) | GateOp::CP(c, t, theta) => { + let label = match op { + GateOp::CRx(_, _, _) => format!("[CRx({:.2})]", theta), + GateOp::CRy(_, _, _) => format!("[CRy({:.2})]", theta), + GateOp::CRz(_, _, _) => format!("[CRz({:.2})]", theta), + GateOp::CP(_, _, _) => format!("[CP({:.2})]", theta), + _ => unreachable!(), + }; + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *c { + line.push_str(&format!("─{}─", "●".to_string() + &"─".repeat(label.len() - 1))); + } else if i == *t { + line.push_str(&format!("─{}─", label)); + } else if i > min_q && i < max_q { + line.push_str(&format!("─{}─", "│".to_string() + &"─".repeat(label.len() - 1))); + } else { + line.push_str(&format!("─{}─", "─".repeat(label.len()))); + } + } + for line in c_lines.iter_mut() { + line.push_str(&format!("═{}═", "═".repeat(label.len()))); + } + gap_line.push_str(&format!(" {} ", " ".repeat(label.len()))); + } GateOp::CNOT(c, t) => { for (i, line) in q_lines.iter_mut().enumerate() { if i == *c { diff --git a/libpsi-visualizer/src/cli/vertical_cli.rs b/libpsi-visualizer/src/cli/vertical_cli.rs index 317833c..6698961 100644 --- a/libpsi-visualizer/src/cli/vertical_cli.rs +++ b/libpsi-visualizer/src/cli/vertical_cli.rs @@ -19,6 +19,21 @@ impl<'a> VerticalRenderer<'a> { GateOp::Z(_) => "[Z]".to_string(), GateOp::S(_) => "[S]".to_string(), GateOp::T(_) => "[T]".to_string(), + GateOp::Sdg(_) => "[S†]".to_string(), + GateOp::Tdg(_) => "[T†]".to_string(), + GateOp::Sx(_) => "[√X]".to_string(), + GateOp::Sxdg(_) => "[√X†]".to_string(), + GateOp::Rx(_, theta) => format!("[Rx({:.2})]", theta), + GateOp::Ry(_, theta) => format!("[Ry({:.2})]", theta), + GateOp::Rz(_, theta) => format!("[Rz({:.2})]", theta), + GateOp::P(_, theta) => format!("[P({:.2})]", theta), + GateOp::U1(_, lambda) => format!("[U1({:.2})]", lambda), + GateOp::U2(_, _, _) => "[U2]".to_string(), + GateOp::U3(_, _, _, _) => "[U3]".to_string(), + GateOp::CRx(_, _, _) => "[CRx]".to_string(), + GateOp::CRy(_, _, _) => "[CRy]".to_string(), + GateOp::CRz(_, _, _) => "[CRz]".to_string(), + GateOp::CP(_, _, _) => "[CP]".to_string(), GateOp::CNOT(_, _) => "●".to_string(), GateOp::CZ(_, _) => "●".to_string(), GateOp::SWAP(_, _) => "╳".to_string(), @@ -121,7 +136,18 @@ impl<'a> fmt::Display for VerticalRenderer<'a> { | GateOp::Y(t) | GateOp::Z(t) | GateOp::S(t) - | GateOp::T(t) => { + | GateOp::T(t) + | GateOp::Sdg(t) + | GateOp::Tdg(t) + | GateOp::Sx(t) + | GateOp::Sxdg(t) + | GateOp::Rx(t, _) + | GateOp::Ry(t, _) + | GateOp::Rz(t, _) + | GateOp::P(t, _) + | GateOp::U1(t, _) + | GateOp::U2(t, _, _) + | GateOp::U3(t, _, _, _) => { let mut line: Vec<char> = vec![' '; total_width]; for i in 0..nq { @@ -145,24 +171,37 @@ impl<'a> fmt::Display for VerticalRenderer<'a> { let gate_line: String = line.into_iter().collect(); writeln!(f, "{}", gate_line)?; } - GateOp::CNOT(c, t) | GateOp::CZ(c, t) | GateOp::SWAP(c, t) => { + GateOp::CNOT(c, t) | GateOp::CZ(c, t) | GateOp::SWAP(c, t) + | GateOp::CRx(c, t, _) | GateOp::CRy(c, t, _) | GateOp::CRz(c, t, _) | GateOp::CP(c, t, _) => { let (sym1, sym2) = match op { GateOp::CNOT(_, _) => ('●', '⊕'), GateOp::CZ(_, _) => ('●', '●'), GateOp::SWAP(_, _) => ('╳', '╳'), + GateOp::CRx(_, _, _) | GateOp::CRy(_, _, _) | GateOp::CRz(_, _, _) | GateOp::CP(_, _, _) => ('●', '□'), _ => unreachable!(), }; let mut line: Vec<char> = vec![' '; total_width]; for i in 0..nq { - let center = i * (col_width + 1) + col_width / 2; + let col_start = i * (col_width + 1); + let center = col_start + col_width / 2; if i < min_q || i > max_q { line[center] = '│'; } else if i == *c { line[center] = sym1; } else if i == *t { - line[center] = sym2; + // For controlled parametric gates, show the gate label on target + if matches!(op, GateOp::CRx(_, _, _) | GateOp::CRy(_, _, _) | GateOp::CRz(_, _, _) | GateOp::CP(_, _, _)) { + let label_start = col_start + (col_width - label.chars().count()) / 2; + for (j, ch) in label.chars().enumerate() { + if label_start + j < line.len() { + line[label_start + j] = ch; + } + } + } else { + line[center] = sym2; + } } } diff --git a/tester/src/benchmarks.rs b/tester/src/benchmarks.rs new file mode 100644 index 0000000..0a88502 --- /dev/null +++ b/tester/src/benchmarks.rs @@ -0,0 +1,90 @@ +use crate::common::{benchmark_circuit, print_section, BenchmarkResult}; +use libpsi_core::QuantumCircuit; +use libpsi_visualizer::HorizontalRenderer; + +pub fn run_all(results: &mut Vec<BenchmarkResult>) { + println!("═══════════════════════════════════════════════════════════════"); + println!(" BENCHMARK CIRCUITS"); + println!("═══════════════════════════════════════════════════════════════\n"); + + test_8_qubit(results); + test_10_qubit(results); + test_12_qubit(results); + test_14_qubit(results); +} + +pub fn test_8_qubit(results: &mut Vec<BenchmarkResult>) { + print_section("8-qubit Entangled Circuit"); + + let 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!("{}", HorizontalRenderer::new(&builder())); + results.push(benchmark_circuit("8-qubit entangled", builder)); +} + +pub fn test_10_qubit(results: &mut Vec<BenchmarkResult>) { + print_section("10-qubit Entangled Circuit"); + + let 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!("{}", HorizontalRenderer::new(&builder())); + results.push(benchmark_circuit("10-qubit entangled", builder)); +} + +pub fn test_12_qubit(results: &mut Vec<BenchmarkResult>) { + print_section("12-qubit Entangled Circuit"); + + let 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!("{}", HorizontalRenderer::new(&builder())); + results.push(benchmark_circuit("12-qubit entangled", builder)); +} + +pub fn test_14_qubit(results: &mut Vec<BenchmarkResult>) { + print_section("14-qubit Entangled Circuit"); + + let 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!("{}", HorizontalRenderer::new(&builder())); + results.push(benchmark_circuit("14-qubit entangled", builder)); +} + diff --git a/tester/src/clifford.rs b/tester/src/clifford.rs new file mode 100644 index 0000000..5c0b453 --- /dev/null +++ b/tester/src/clifford.rs @@ -0,0 +1,126 @@ +use crate::common::{benchmark_circuit, print_circuit, print_section, BenchmarkResult}; +use libpsi_core::QuantumCircuit; + +pub fn run_all(results: &mut Vec<BenchmarkResult>) { + println!("═══════════════════════════════════════════════════════════════"); + println!(" CLIFFORD GATES TESTS"); + println!("═══════════════════════════════════════════════════════════════\n"); + + test_bell_state(results); + test_ghz_state(results); + test_swap_via_cnots(results); + test_toffoli(results); + test_hadamard_measure(results); + test_complex_circuit(results); +} + +pub fn test_bell_state(results: &mut Vec<BenchmarkResult>) { + print_section("Bell State with Measurement"); + + let builder = || { + let mut circuit = QuantumCircuit::with_classical(2, 2); + circuit.h(0).cnot(0, 1).measure(0, 0).measure(1, 1); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Bell State (2 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_ghz_state(results: &mut Vec<BenchmarkResult>) { + print_section("GHZ State"); + + let builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit.h(0).cnot(0, 1).cnot(0, 2); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("GHZ State (3 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_swap_via_cnots(results: &mut Vec<BenchmarkResult>) { + print_section("SWAP via 3 CNOTs"); + + let builder = || { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).cnot(0, 1).cnot(1, 0).cnot(0, 1); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("SWAP via CNOTs (2 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_toffoli(results: &mut Vec<BenchmarkResult>) { + print_section("Toffoli Gate"); + + let builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit.x(0).x(1).toffoli(0, 1, 2); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Toffoli (3 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_hadamard_measure(results: &mut Vec<BenchmarkResult>) { + print_section("Full Circuit with Measurements"); + + let builder = || { + let mut circuit = QuantumCircuit::with_classical(3, 3); + circuit.h(0).h(1).h(2).measure_all(); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("3-qubit Hadamard + Measure", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_complex_circuit(results: &mut Vec<BenchmarkResult>) { + print_section("Complex Circuit"); + + let 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 + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Complex (4 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + diff --git a/tester/src/common.rs b/tester/src/common.rs new file mode 100644 index 0000000..90eabd8 --- /dev/null +++ b/tester/src/common.rs @@ -0,0 +1,171 @@ +use libpsi_core::{QuantumCircuit, QuantumState, Runtime, Vector}; +use libpsi_visualizer::{HorizontalRenderer, VerticalRenderer}; +use std::time::{Duration, Instant}; + +pub struct BenchmarkResult { + pub name: String, + pub basic_time: Duration, + pub mt_time: Duration, + pub results_match: bool, +} + +pub 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, + } +} + +pub fn states_equal(a: &QuantumState, b: &QuantumState) -> bool { + 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 +} + +pub 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) + } +} + +pub fn print_section(title: &str) { + let width = 61; + let padding = width - title.len() - 2; + println!("┌{}┐", "─".repeat(width)); + println!("│ {}{} │", title, " ".repeat(padding)); + println!("└{}┘\n", "─".repeat(width)); +} + +pub fn print_circuit(circuit: &QuantumCircuit) { + println!("Horizontal:\n{}", HorizontalRenderer::new(circuit)); + println!("Vertical:\n{}", VerticalRenderer::new(circuit)); +} + +pub fn print_benchmark_table(results: &[BenchmarkResult]) { + const C1: usize = 30; + const C2: usize = 12; + const C3: usize = 12; + const C4: usize = 10; + const C5: usize = 5; + + 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", + ); + 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, + ); + } + + println!("{}", bottom); +} + +pub fn print_summary(results: &[BenchmarkResult]) { + 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 + ); +} diff --git a/tester/src/custom_gates.rs b/tester/src/custom_gates.rs new file mode 100644 index 0000000..0e21614 --- /dev/null +++ b/tester/src/custom_gates.rs @@ -0,0 +1,121 @@ +use crate::common::{benchmark_circuit, print_circuit, print_section, BenchmarkResult}; +use libpsi_core::{complex, matrix, CustomGate, CustomGateBuilder, QuantumCircuit}; + +pub fn run_all(results: &mut Vec<BenchmarkResult>) { + println!("═══════════════════════════════════════════════════════════════"); + println!(" CUSTOM GATES TESTS"); + println!("═══════════════════════════════════════════════════════════════\n"); + + test_bell_gate(results); + test_swap_gate(results); + test_sqrt_x_gate(results); +} + +pub fn test_bell_gate(results: &mut Vec<BenchmarkResult>) { + print_section("Custom Gate: Bell Pair Creator"); + + let bell_gate = CustomGateBuilder::new("BELL", 2).h(0).cnot(0, 1).build(); + let gate_clone = bell_gate.clone(); + + let builder = move || { + let mut circuit = QuantumCircuit::new(4); + circuit + .apply_custom(gate_clone.clone(), &[0, 1]) + .apply_custom(gate_clone.clone(), &[2, 3]); + circuit + }; + + let display_circuit = { + let mut circuit = QuantumCircuit::new(4); + circuit + .apply_custom(bell_gate.clone(), &[0, 1]) + .apply_custom(bell_gate.clone(), &[2, 3]); + circuit + }; + print_circuit(&display_circuit); + results.push(benchmark_circuit("Custom BELL (4 qubits)", builder)); + + let mut display = { + let mut circuit = QuantumCircuit::new(4); + circuit + .apply_custom(bell_gate.clone(), &[0, 1]) + .apply_custom(bell_gate.clone(), &[2, 3]); + circuit + }; + display.compute(); + println!("{}\n", display); +} + +pub fn test_swap_gate(results: &mut Vec<BenchmarkResult>) { + print_section("Custom Gate: Swap via CNOTs"); + + let swap_gate = CustomGateBuilder::new("MYSWAP", 2) + .cnot(0, 1) + .cnot(1, 0) + .cnot(0, 1) + .build(); + let gate_clone = swap_gate.clone(); + + let builder = move || { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).apply_custom(gate_clone.clone(), &[0, 1]); + circuit + }; + + let display_circuit = { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).apply_custom(swap_gate.clone(), &[0, 1]); + circuit + }; + print_circuit(&display_circuit); + results.push(benchmark_circuit("Custom SWAP (2 qubits)", builder)); + + let mut display = { + let mut circuit = QuantumCircuit::new(2); + circuit.x(0).apply_custom(swap_gate.clone(), &[0, 1]); + circuit + }; + display.compute(); + println!("{}\n", display); +} + +pub fn test_sqrt_x_gate(results: &mut Vec<BenchmarkResult>) { + print_section("Custom Gate: Matrix-defined √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 gate_clone = sqrt_x.clone(); + + let builder = move || { + let mut circuit = QuantumCircuit::new(1); + circuit + .apply_custom(gate_clone.clone(), &[0]) + .apply_custom(gate_clone.clone(), &[0]); + circuit + }; + + let display_circuit = { + let mut circuit = QuantumCircuit::new(1); + circuit + .apply_custom(sqrt_x.clone(), &[0]) + .apply_custom(sqrt_x.clone(), &[0]); + circuit + }; + print_circuit(&display_circuit); + results.push(benchmark_circuit("√X gate (1 qubit)", builder)); + + let mut display = { + let mut circuit = QuantumCircuit::new(1); + circuit + .apply_custom(sqrt_x.clone(), &[0]) + .apply_custom(sqrt_x.clone(), &[0]); + circuit + }; + display.compute(); + println!("{}", display); + println!("(Two √X gates should equal X, so |0⟩ becomes |1⟩)\n"); +} + diff --git a/tester/src/main.rs b/tester/src/main.rs index 660dc2b..ac0a61f 100644 --- a/tester/src/main.rs +++ b/tester/src/main.rs @@ -1,487 +1,75 @@ -use libpsi_core::*; -use libpsi_visualizer::*; -use std::time::{Duration, Instant}; +mod benchmarks; +mod clifford; +mod common; +mod custom_gates; +mod non_clifford; -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, - } -} +use common::{print_benchmark_table, print_summary, BenchmarkResult}; +use std::env; -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 print_header() { + println!("═══════════════════════════════════════════════════════════════"); + println!(" PSI Quantum Simulator"); + println!("═══════════════════════════════════════════════════════════════\n"); } -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_usage() { + println!("Usage: tester [OPTIONS]"); + println!(); + println!("Options:"); + println!(" all Run all tests (default)"); + println!(" clifford Run Clifford gate tests only"); + println!(" non-clifford Run non-Clifford gate tests only"); + println!(" custom Run custom gate tests only"); + println!(" bench Run benchmark tests only"); + println!(" help Show this help message"); + println!(); + println!("Examples:"); + println!(" tester # Run all tests"); + println!(" tester clifford # Run only Clifford gate tests"); + println!(" tester non-clifford # Run only rotation/parametric gate tests"); + println!(" tester custom bench # Run custom gates and benchmarks"); } -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 { "✗" }; +fn main() { + let args: Vec<String> = env::args().skip(1).collect(); - 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 - ); + if args + .iter() + .any(|a| a == "help" || a == "--help" || a == "-h") + { + print_usage(); + return; } - println!("{}", bottom); -} - -fn main() { - println!("═══════════════════════════════════════════════════════════════"); - println!(" PSI Quantum Simulator"); - println!("═══════════════════════════════════════════════════════════════\n"); + print_header(); let mut results: Vec<BenchmarkResult> = Vec::new(); - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ Bell State with Measurement │"); - println!("└─────────────────────────────────────────────────────────────┘\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 - }; - - let bell = bell_builder(); - println!("Horizontal:\n{}", HorizontalRenderer::new(&bell)); - println!("Vertical:\n{}", VerticalRenderer::new(&bell)); - - results.push(benchmark_circuit("Bell State (2 qubits)", bell_builder)); - let mut bell_display = bell_builder(); - bell_display.compute(); - println!("{}\n", bell_display); - - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ GHZ State │"); - println!("└─────────────────────────────────────────────────────────────┘\n"); - - let ghz_builder = || { - let mut circuit = QuantumCircuit::new(3); - circuit.h(0).cnot(0, 1).cnot(0, 2); - circuit - }; - - let ghz = ghz_builder(); - println!("Horizontal:\n{}", HorizontalRenderer::new(&ghz)); - println!("Vertical:\n{}", VerticalRenderer::new(&ghz)); - - results.push(benchmark_circuit("GHZ State (3 qubits)", ghz_builder)); - let mut ghz_display = ghz_builder(); - ghz_display.compute(); - println!("{}\n", ghz_display); - - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ SWAP via 3 CNOTs │"); - println!("└─────────────────────────────────────────────────────────────┘\n"); - - let swap_builder = || { - let mut circuit = QuantumCircuit::new(2); - circuit.x(0).cnot(0, 1).cnot(1, 0).cnot(0, 1); - circuit - }; - - let swap_circuit = swap_builder(); - println!("Horizontal:\n{}", HorizontalRenderer::new(&swap_circuit)); - println!("Vertical:\n{}", VerticalRenderer::new(&swap_circuit)); + let run_all = args.is_empty() || args.iter().any(|a| a == "all"); + let run_clifford = run_all || args.iter().any(|a| a == "clifford"); + let run_non_clifford = run_all || args.iter().any(|a| a == "non-clifford"); + let run_custom = run_all || args.iter().any(|a| a == "custom"); + let run_bench = run_all || args.iter().any(|a| a == "bench"); - results.push(benchmark_circuit("SWAP via CNOTs (2 qubits)", swap_builder)); - let mut swap_display = swap_builder(); - swap_display.compute(); - println!("{}\n", swap_display); - - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ Toffoli Gate │"); - println!("└─────────────────────────────────────────────────────────────┘\n"); - - let toffoli_builder = || { - let mut circuit = QuantumCircuit::new(3); - circuit.x(0).x(1).toffoli(0, 1, 2); - circuit - }; - - let toffoli = toffoli_builder(); - println!("Horizontal:\n{}", HorizontalRenderer::new(&toffoli)); - println!("Vertical:\n{}", VerticalRenderer::new(&toffoli)); - - results.push(benchmark_circuit("Toffoli (3 qubits)", toffoli_builder)); - let mut toffoli_display = toffoli_builder(); - toffoli_display.compute(); - println!("{}\n", toffoli_display); - - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ Full Circuit with Measurements │"); - println!("└─────────────────────────────────────────────────────────────┘\n"); - - let full_builder = || { - let mut circuit = QuantumCircuit::with_classical(3, 3); - circuit.h(0).h(1).h(2).measure_all(); - circuit - }; - - let full = full_builder(); - println!("Horizontal:\n{}", HorizontalRenderer::new(&full)); - println!("Vertical:\n{}", VerticalRenderer::new(&full)); - - results.push(benchmark_circuit( - "3-qubit Hadamard + Measure", - full_builder, - )); - let mut full_display = full_builder(); - full_display.compute(); - println!("{}\n", full_display); - - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ Complex Circuit │"); - println!("└─────────────────────────────────────────────────────────────┘\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 - }; - - let complex = complex_builder(); - println!("Horizontal:\n{}", HorizontalRenderer::new(&complex)); - println!("Vertical:\n{}", VerticalRenderer::new(&complex)); - - 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 bell_gate = CustomGateBuilder::new("BELL", 2).h(0).cnot(0, 1).build(); - let bell_gate_clone = bell_gate.clone(); - - 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 - }; - - 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)); - - 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!("┌─────────────────────────────────────────────────────────────┐"); - 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 custom_swap_builder = move || { - let mut circuit = QuantumCircuit::new(2); - circuit.x(0).apply_custom(swap_gate_clone.clone(), &[0, 1]); - circuit - }; - - 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)); - - 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); - - println!("┌─────────────────────────────────────────────────────────────┐"); - println!("│ Custom Gate: Matrix-defined √X gate │"); - println!("└─────────────────────────────────────────────────────────────┘\n"); - - 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 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 - }; - - 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)); - - 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)); + if run_clifford { + clifford::run_all(&mut results); + } - print_benchmark_table(&results); + if run_non_clifford { + non_clifford::run_all(&mut 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!"); + if run_custom { + custom_gates::run_all(&mut 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(); + if run_bench { + benchmarks::run_all(&mut results); + } - println!( - "\nTotal time - BasicRT: {} | BasicRTMT: {} | Overall speedup: {:.2}x", - format_duration(total_basic), - format_duration(total_mt), - overall_speedup - ); + if !results.is_empty() { + print_benchmark_table(&results); + print_summary(&results); + } } diff --git a/tester/src/non_clifford.rs b/tester/src/non_clifford.rs new file mode 100644 index 0000000..8545d01 --- /dev/null +++ b/tester/src/non_clifford.rs @@ -0,0 +1,137 @@ +use crate::common::{benchmark_circuit, print_circuit, print_section, BenchmarkResult}; +use libpsi_core::QuantumCircuit; +use std::f64::consts::PI; + +pub fn run_all(results: &mut Vec<BenchmarkResult>) { + println!("═══════════════════════════════════════════════════════════════"); + println!(" NON-CLIFFORD GATES TESTS"); + println!("═══════════════════════════════════════════════════════════════\n"); + + test_fixed_gates(results); + test_rotation_gates(results); + test_phase_gates(results); + test_general_unitaries(results); + test_controlled_rotations(results); + test_variational_circuit(results); +} + +pub fn test_fixed_gates(results: &mut Vec<BenchmarkResult>) { + print_section("Non-Clifford Gates: T, T†, √X, S†"); + + let builder = || { + let mut circuit = QuantumCircuit::new(2); + circuit.h(0).t(0).tdg(0).sx(1).sxdg(1).h(0).s(0).sdg(0); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Non-Clifford fixed gates", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_rotation_gates(results: &mut Vec<BenchmarkResult>) { + print_section("Rotation Gates: Rx, Ry, Rz"); + + let builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit + .rx(0, PI / 4.0) + .ry(1, PI / 2.0) + .rz(2, PI) + .rx(0, -PI / 4.0); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Rotation gates (3 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_phase_gates(results: &mut Vec<BenchmarkResult>) { + print_section("Phase Gate: P(θ)"); + + let builder = || { + let mut circuit = QuantumCircuit::new(2); + circuit.h(0).p(0, PI / 4.0).h(1).p(1, PI / 2.0); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Phase gates (2 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_general_unitaries(results: &mut Vec<BenchmarkResult>) { + print_section("General Unitaries: U1, U2, U3"); + + let builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit + .u1(0, PI / 4.0) + .u2(1, 0.0, PI) + .u3(2, PI / 2.0, 0.0, PI); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("General unitaries (3 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_controlled_rotations(results: &mut Vec<BenchmarkResult>) { + print_section("Controlled Rotation Gates: CRx, CRy, CRz, CP"); + + let builder = || { + let mut circuit = QuantumCircuit::new(4); + circuit + .x(0) + .crx(0, 1, PI / 2.0) + .x(2) + .cry(2, 3, PI / 4.0) + .crz(0, 2, PI) + .cp(1, 3, PI / 2.0); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit( + "Controlled rotations (4 qubits)", + builder, + )); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} + +pub fn test_variational_circuit(results: &mut Vec<BenchmarkResult>) { + print_section("Variational Circuit (VQE-like)"); + + let builder = || { + let mut circuit = QuantumCircuit::new(3); + circuit.ry(0, 0.5).ry(1, 0.3).ry(2, 0.7); + circuit.cnot(0, 1).cnot(1, 2); + circuit.rx(0, 0.2).rx(1, 0.4).rx(2, 0.6); + circuit.cz(0, 2); + circuit + }; + + print_circuit(&builder()); + results.push(benchmark_circuit("Variational circuit (3 qubits)", builder)); + + let mut display = builder(); + display.compute(); + println!("{}\n", display); +} |
