use crate::backend::factored::FactoredBackend;
use crate::backend::statevector::StatevectorBackend;
use crate::backend::Backend;
use crate::circuit::{smallvec, Circuit, ClassicalCondition, Instruction};
use crate::gates::Gate;
use crate::sim;
fn assert_probs_close(actual: &[f64], expected: &[f64], eps: f64) {
assert_eq!(
actual.len(),
expected.len(),
"probability vector length mismatch: got {}, expected {}",
actual.len(),
expected.len()
);
for (i, (a, e)) in actual.iter().zip(expected).enumerate() {
assert!(
(a - e).abs() < eps,
"prob[{i}]: expected {e}, got {a} (diff {})",
(a - e).abs()
);
}
}
fn compare_with_statevector(circuit: &Circuit, eps: f64) {
let mut sv = StatevectorBackend::new(42);
let sv_result = sim::run_on(&mut sv, circuit).unwrap();
let sv_probs = sv_result.probabilities.unwrap().to_vec();
let mut fac = FactoredBackend::new(42);
let fac_result = sim::run_on(&mut fac, circuit).unwrap();
let fac_probs = fac_result.probabilities.unwrap().to_vec();
assert_probs_close(&fac_probs, &sv_probs, eps);
}
#[test]
fn test_x_gate() {
let mut c = Circuit::new(1, 0);
c.add_gate(Gate::X, &[0]);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
assert_probs_close(&b.probabilities().unwrap(), &[0.0, 1.0], 1e-12);
}
#[test]
fn test_h_gate() {
let mut c = Circuit::new(1, 0);
c.add_gate(Gate::H, &[0]);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
assert_probs_close(&b.probabilities().unwrap(), &[0.5, 0.5], 1e-12);
}
#[test]
fn test_h_on_second_qubit() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[2]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_diagonal_gates() {
let mut c = Circuit::new(2, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::T, &[0]);
c.add_gate(Gate::S, &[0]);
c.add_gate(Gate::Z, &[0]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_bell_state() {
let mut c = Circuit::new(2, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 1]);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
assert_probs_close(&b.probabilities().unwrap(), &[0.5, 0.0, 0.0, 0.5], 1e-12);
}
#[test]
fn test_swap() {
let mut c = Circuit::new(2, 0);
c.add_gate(Gate::X, &[1]);
c.add_gate(Gate::Swap, &[0, 1]);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
assert_probs_close(&b.probabilities().unwrap(), &[0.0, 1.0, 0.0, 0.0], 1e-12);
}
#[test]
fn test_cz() {
let mut c = Circuit::new(2, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::Cz, &[0, 1]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_independent_bell_pairs() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 1]);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::Cx, &[2, 3]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_independent_groups_stay_separate() {
let mut c = Circuit::new(6, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 1]);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::Cx, &[2, 3]);
c.add_gate(Gate::X, &[4]);
c.add_gate(Gate::Cx, &[4, 5]);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
let active_count = b.substates.iter().filter(|s| s.is_some()).count();
assert_eq!(active_count, 3);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_cx_non_adjacent() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 3]); compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_progressive_merge() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::H, &[3]);
c.add_gate(Gate::Cx, &[0, 1]); c.add_gate(Gate::Cx, &[2, 3]); c.add_gate(Gate::Cx, &[1, 2]); compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_cu_gate() {
let mut c = Circuit::new(3, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::H, &[1]);
let mat = Gate::H.matrix_2x2();
c.add_gate(Gate::Cu(Box::new(mat)), &[0, 2]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_cphase() {
let mut c = Circuit::new(3, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::cphase(std::f64::consts::FRAC_PI_4), &[0, 1]);
c.add_gate(Gate::cphase(std::f64::consts::FRAC_PI_2), &[1, 2]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_mcu_toffoli() {
use crate::gates::McuData;
let mut c = Circuit::new(3, 0);
c.add_gate(Gate::X, &[0]);
c.add_gate(Gate::X, &[1]);
c.add_gate(
Gate::Mcu(Box::new(McuData {
mat: Gate::X.matrix_2x2(),
num_controls: 2,
})),
&[0, 1, 2],
);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_measurement() {
let mut c = Circuit::new(2, 2);
c.add_gate(Gate::X, &[0]);
c.add_measure(0, 0);
c.add_measure(1, 1);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
assert!(b.classical_results()[0]); assert!(!b.classical_results()[1]); }
#[test]
fn test_measurement_in_substate() {
let mut c = Circuit::new(4, 1);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 1]);
c.add_gate(Gate::X, &[2]); c.add_measure(0, 0);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
}
#[test]
fn test_golden_qft_8() {
let circuit = crate::circuits::qft_circuit(8);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_golden_qft_12() {
let circuit = crate::circuits::qft_circuit(12);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_golden_random_16() {
let circuit = crate::circuits::random_circuit(16, 10, 42);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_golden_hea_8() {
let circuit = crate::circuits::hardware_efficient_ansatz(8, 3, 42);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_golden_bell_pairs() {
let circuit = crate::circuits::independent_bell_pairs(6);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_golden_independent_blocks() {
let circuit = crate::circuits::independent_random_blocks(4, 3, 5, 42);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_golden_qpe() {
let circuit = crate::circuits::phase_estimation_circuit(6);
compare_with_statevector(&circuit, 1e-10);
}
#[test]
fn test_single_qubit_circuit() {
let mut c = Circuit::new(1, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::T, &[0]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_no_entanglement() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::X, &[1]);
c.add_gate(Gate::S, &[2]);
c.add_gate(Gate::T, &[3]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_immediate_full_merge() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 3]);
c.add_gate(Gate::Cx, &[1, 2]);
c.add_gate(Gate::Cx, &[0, 2]); compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_backend_kind_factored() {
use crate::sim::BackendKind;
let circuit = crate::circuits::independent_bell_pairs(4);
let result = sim::run_with(BackendKind::Factored, &circuit, 42).unwrap();
let probs = result.probabilities.unwrap().to_vec();
assert!((probs.iter().sum::<f64>() - 1.0).abs() < 1e-10);
}
#[test]
fn test_conditional_gate() {
let mut c = Circuit::new(2, 1);
c.add_gate(Gate::X, &[0]);
c.add_measure(0, 0);
c.instructions.push(Instruction::Conditional {
condition: ClassicalCondition::BitIsOne(0),
gate: Gate::X,
targets: smallvec![1],
});
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
let probs = b.probabilities().unwrap();
assert!((probs[3] - 1.0).abs() < 1e-10); }
#[test]
fn test_factored_parallel_multifused_16q() {
let mut c = Circuit::new(16, 0);
for q in 0..16 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..15 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_parallel_cx_large_substate_16q() {
let mut c = Circuit::new(16, 0);
for q in 0..15 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
for q in (0..15).step_by(2) {
c.add_gate(Gate::H, &[q]);
c.add_gate(Gate::Cx, &[q, q + 1]);
}
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_parallel_measure_16q() {
let mut c = Circuit::new(16, 1);
for q in 0..15 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
c.add_gate(Gate::X, &[0]);
c.add_measure(0, 0);
let mut fac = FactoredBackend::new(42);
sim::run_on(&mut fac, &c).unwrap();
let bits = fac.classical_results();
assert!(bits[0]);
}
#[test]
fn test_merge_src_low_path() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::Cx, &[2, 3]);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 1]);
c.add_gate(Gate::T, &[2]);
c.add_gate(Gate::T, &[3]);
c.add_gate(Gate::T, &[0]);
c.add_gate(Gate::T, &[1]);
c.add_gate(Gate::Cx, &[2, 0]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_merge_interleaved_qubits_path() {
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 2]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::Cx, &[1, 3]);
c.add_gate(Gate::T, &[0]);
c.add_gate(Gate::S, &[2]);
c.add_gate(Gate::T, &[1]);
c.add_gate(Gate::S, &[3]);
c.add_gate(Gate::Cx, &[0, 1]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_merge_interleaved_three_way() {
let mut c = Circuit::new(5, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 4]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::Cx, &[1, 2]);
c.add_gate(Gate::Cx, &[2, 3]);
c.add_gate(Gate::T, &[0]);
c.add_gate(Gate::T, &[4]);
c.add_gate(Gate::Cx, &[0, 1]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_merge_parallel_balanced_14q() {
let mut c = Circuit::new(14, 0);
for q in 0..7 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..6 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
for q in 7..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 7..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
c.add_gate(Gate::Cx, &[0, 7]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_merge_parallel_imbalanced_15q() {
let mut c = Circuit::new(15, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
c.add_gate(Gate::X, &[14]);
c.add_gate(Gate::Cx, &[0, 14]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_merge_parallel_interleaved_14q() {
let mut c = Circuit::new(14, 0);
for q in (0..14).step_by(2) {
c.add_gate(Gate::H, &[q]);
}
for q in (0..12).step_by(2) {
c.add_gate(Gate::Cx, &[q, q + 2]);
}
for q in (1..14).step_by(2) {
c.add_gate(Gate::H, &[q]);
}
for q in (1..12).step_by(2) {
c.add_gate(Gate::Cx, &[q, q + 2]);
}
c.add_gate(Gate::Cx, &[0, 1]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_par_diagonal_z_gate_14q() {
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
for q in 0..14 {
c.add_gate(Gate::Z, &[q]);
}
for q in 0..14 {
c.add_gate(Gate::X, &[q]);
}
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_par_swap_14q() {
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
c.add_gate(Gate::Swap, &[0, 13]);
c.add_gate(Gate::Cz, &[1, 12]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_substate_larger_than_smallvec_inline() {
let mut c = Circuit::new(10, 0);
for q in 0..10 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..9 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_reduced_density_matrix_returns_ok() {
let mut c = Circuit::new(3, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::Cx, &[0, 1]);
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
let rho = b.reduced_density_matrix_1q(0).unwrap();
let diag = rho[0][0].re + rho[1][1].re;
assert!((diag - 1.0).abs() < 1e-10);
}
#[test]
fn test_factored_reset_after_measure() {
let mut c = Circuit::new(2, 1);
c.add_gate(Gate::X, &[0]);
c.instructions.push(Instruction::Measure {
qubit: 0,
classical_bit: 0,
});
c.instructions.push(Instruction::Reset { qubit: 0 });
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
let probs = b.probabilities().unwrap();
assert!(probs[0] > 0.99);
}
#[test]
fn test_factored_conditional_branches() {
let mut c = Circuit::new(2, 1);
c.add_gate(Gate::X, &[0]);
c.instructions.push(Instruction::Measure {
qubit: 0,
classical_bit: 0,
});
c.instructions.push(Instruction::Conditional {
condition: ClassicalCondition::BitIsOne(0),
gate: Gate::X,
targets: smallvec![1],
});
let mut b = FactoredBackend::new(42);
sim::run_on(&mut b, &c).unwrap();
let probs = b.probabilities().unwrap();
assert!(probs[3] > 0.99);
}
#[test]
fn test_factored_seq_batch_rzz_direct() {
use crate::gates::BatchRzzData;
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::H, &[3]);
let data = Box::new(BatchRzzData {
edges: vec![(0, 1, 0.3), (1, 2, 0.5), (2, 3, 0.7)],
});
c.add_gate(Gate::BatchRzz(data), &[0, 1, 2, 3]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_seq_diagonal_batch_direct() {
use crate::gates::{DiagEntry, DiagonalBatchData};
use num_complex::Complex64;
let mut c = Circuit::new(4, 0);
c.add_gate(Gate::H, &[0]);
c.add_gate(Gate::H, &[1]);
c.add_gate(Gate::H, &[2]);
c.add_gate(Gate::H, &[3]);
let phase = Complex64::from_polar(1.0, 0.4);
let entries = vec![
DiagEntry::Phase1q {
qubit: 0,
d0: Complex64::new(1.0, 0.0),
d1: phase,
},
DiagEntry::Phase2q {
q0: 1,
q1: 2,
phase,
},
DiagEntry::Parity2q {
q0: 2,
q1: 3,
same: Complex64::from_polar(1.0, 0.2),
diff: Complex64::from_polar(1.0, -0.2),
},
];
let data = Box::new(DiagonalBatchData { entries });
c.add_gate(Gate::DiagonalBatch(data), &[0, 1, 2, 3]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_seq_mcu_phase_3controls() {
use crate::gates::McuData;
use num_complex::Complex64;
let mut c = Circuit::new(4, 0);
for q in 0..4 {
c.add_gate(Gate::X, &[q]);
}
let phase = Complex64::from_polar(1.0, 0.3);
let mat = [
[Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
[Complex64::new(0.0, 0.0), phase],
];
c.add_gate(
Gate::Mcu(Box::new(McuData {
mat,
num_controls: 3,
})),
&[0, 1, 2, 3],
);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_seq_cu_general_matrix() {
use num_complex::Complex64;
let mut c = Circuit::new(2, 0);
c.add_gate(Gate::H, &[0]);
let inv_sqrt2 = std::f64::consts::FRAC_1_SQRT_2;
let mat = [
[
Complex64::new(inv_sqrt2, 0.0),
Complex64::new(inv_sqrt2, 0.0),
],
[
Complex64::new(inv_sqrt2, 0.0),
Complex64::new(-inv_sqrt2, 0.0),
],
];
c.add_gate(Gate::Cu(Box::new(mat)), &[0, 1]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_par_rzz_14q() {
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
for q in 0..13 {
c.add_gate(Gate::Rzz(0.1 * q as f64), &[q, q + 1]);
}
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_par_cu_general_14q() {
use num_complex::Complex64;
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
let mat = [
[Complex64::new(0.6, 0.0), Complex64::new(0.8, 0.0)],
[Complex64::new(0.8, 0.0), Complex64::new(-0.6, 0.0)],
];
c.add_gate(Gate::Cu(Box::new(mat)), &[0, 13]);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_par_mcu_3control_14q() {
use crate::gates::McuData;
use num_complex::Complex64;
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
let phase = Complex64::from_polar(1.0, 0.5);
let mat = [
[Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)],
[Complex64::new(0.0, 0.0), phase],
];
c.add_gate(
Gate::Mcu(Box::new(McuData {
mat,
num_controls: 3,
})),
&[0, 1, 2, 13],
);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_par_diagonal_batch_14q() {
use crate::gates::{DiagEntry, DiagonalBatchData};
use num_complex::Complex64;
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
let entries = vec![
DiagEntry::Phase1q {
qubit: 0,
d0: Complex64::new(1.0, 0.0),
d1: Complex64::from_polar(1.0, 0.2),
},
DiagEntry::Phase2q {
q0: 1,
q1: 2,
phase: Complex64::from_polar(1.0, 0.3),
},
DiagEntry::Parity2q {
q0: 3,
q1: 5,
same: Complex64::from_polar(1.0, 0.1),
diff: Complex64::from_polar(1.0, -0.1),
},
];
c.add_gate(
Gate::DiagonalBatch(Box::new(DiagonalBatchData { entries })),
&[0, 1, 2, 3, 5],
);
compare_with_statevector(&c, 1e-10);
}
#[test]
fn test_factored_par_batch_rzz_14q() {
use crate::gates::BatchRzzData;
let mut c = Circuit::new(14, 0);
for q in 0..14 {
c.add_gate(Gate::H, &[q]);
}
for q in 0..13 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
let edges: Vec<(usize, usize, f64)> =
(0..13).map(|q| (q, q + 1, 0.05 * (q + 1) as f64)).collect();
c.add_gate(
Gate::BatchRzz(Box::new(BatchRzzData { edges })),
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
);
compare_with_statevector(&c, 1e-10);
}