Files
symclaw/crates/symclaw-quantum/tests/integration.rs
T

145 lines
4.0 KiB
Rust

//! Integration tests for symclaw-quantum.
//!
//! Cross-module scenarios: Pauli algebra → circuit → codegen → ZX.
use symclaw_quantum::circuit::QuantumCircuit;
use symclaw_quantum::clifford_gates::CliffordGate1Q;
use symclaw_quantum::codegen;
use symclaw_quantum::pauli::{Pauli, PauliOp, Phase};
use symclaw_quantum::zx::ZXDiagram;
// ── Pauli algebra ─────────────────────────────────────────────────
#[test]
fn pauli_x_y_anticommute() {
let x = PauliOp::single(Pauli::X, 0, 1);
let y = PauliOp::single(Pauli::Y, 0, 1);
assert!(!x.commutes_with(&y), "X and Y should anticommute");
}
#[test]
fn pauli_x_z_anticommute() {
let x = PauliOp::single(Pauli::X, 0, 1);
let z = PauliOp::single(Pauli::Z, 0, 1);
assert!(!x.commutes_with(&z), "X and Z should anticommute");
}
#[test]
fn pauli_z_z_commute() {
let z1 = PauliOp::single(Pauli::Z, 0, 2);
let z2 = PauliOp::single(Pauli::Z, 1, 2);
assert!(z1.commutes_with(&z2), "Z₀ and Z₁ should commute");
}
#[test]
fn pauli_weight_single_qubit() {
let x = PauliOp::single(Pauli::X, 0, 3);
assert_eq!(x.weight(), 1, "single-qubit X has weight 1");
}
#[test]
fn pauli_identity_weight_zero() {
let id = PauliOp::identity(4);
assert_eq!(id.weight(), 0, "Identity has weight 0");
}
#[test]
fn phase_cycle() {
// i^0=1, i^1=i, i^2=-1, i^3=-i, i^4=1
for k in 0..4u8 {
let p = Phase(k);
let p_neg = p.neg();
let p_back = p_neg.neg();
// neg twice = same phase
assert_eq!(p_back.0, p.0, "neg(neg(Phase({k}))) = Phase({k})");
}
}
// ── Circuit construction ──────────────────────────────────────────
#[test]
fn circuit_gate_count() {
let mut c = QuantumCircuit::new(2);
c.h(0).cnot(0, 1);
assert_eq!(c.gate_count(), 2, "Bell state circuit has 2 gates");
}
#[test]
fn circuit_t_count() {
let mut c = QuantumCircuit::new(3);
for _ in 0..7 {
c.t(0);
}
assert_eq!(c.t_count(), 7, "T-count should be 7");
}
#[test]
fn circuit_qubit_count() {
let c = QuantumCircuit::new(5);
assert_eq!(c.n_qubits, 5);
}
// ── Clifford gates ────────────────────────────────────────────────
#[test]
fn clifford_h_dagger_is_h() {
// H† = H (Hermitian)
let h = CliffordGate1Q::H;
assert_eq!(h.dagger(), CliffordGate1Q::H, "H† = H");
}
#[test]
fn clifford_s_dagger_is_sdg() {
let s = CliffordGate1Q::S;
assert_eq!(s.dagger(), CliffordGate1Q::Sdg, "S† = S†");
}
#[test]
fn clifford_x_dagger_is_x() {
let x = CliffordGate1Q::X;
assert_eq!(x.dagger(), CliffordGate1Q::X, "X† = X");
}
// ── ZX-calculus ───────────────────────────────────────────────────
#[test]
fn zx_add_nodes_and_wires() {
let mut d = ZXDiagram::new();
let i = d.add_input();
let o = d.add_output();
d.add_wire(i, o);
assert_eq!(d.degree(i), 1);
assert_eq!(d.degree(o), 1);
}
#[test]
fn zx_node_count() {
let mut d = ZXDiagram::new();
d.add_input();
d.add_input();
d.add_output();
// 3 nodes total
assert_eq!(d.node_count(), 3);
}
// ── Codegen pipeline ─────────────────────────────────────────────
#[test]
fn codegen_qasm3_bell_state_nonempty() {
let mut c = QuantumCircuit::new(2);
c.h(0).cnot(0, 1);
let qasm = codegen::to_openqasm3(&c);
assert!(!qasm.is_empty(), "OpenQASM3 output should not be empty");
}
#[test]
fn codegen_qasm3_contains_h_gate() {
let mut c = QuantumCircuit::new(1);
c.h(0);
let qasm = codegen::to_openqasm3(&c);
assert!(
qasm.to_lowercase().contains('h') || qasm.contains("H"),
"OpenQASM output should contain H gate: {qasm}"
);
}