use crate::circuit::QuantumCircuit;
use crate::error::{QuantumError, Result};
use crate::gate::Gate;
use crate::stabilizer::StabilizerState;
use crate::types::{Complex, MeasurementOutcome};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
const DEFAULT_MAX_TERMS: usize = 65536;
#[derive(Debug, Clone)]
pub struct CliffordTResult {
pub measurements: Vec<MeasurementOutcome>,
pub t_count: usize,
pub num_terms: usize,
pub peak_terms: usize,
}
pub struct CliffordTState {
num_qubits: usize,
terms: Vec<(Complex, StabilizerState)>,
t_count: usize,
max_terms: usize,
seed: u64,
fork_counter: u64,
rng: StdRng,
}
impl CliffordTState {
pub fn new(num_qubits: usize, max_t_gates: usize, seed: u64) -> Result<Self> {
if num_qubits == 0 {
return Err(QuantumError::CircuitError(
"Clifford+T state requires at least 1 qubit".into(),
));
}
let max_terms = if max_t_gates >= 20 {
DEFAULT_MAX_TERMS
} else {
(1usize << max_t_gates).min(DEFAULT_MAX_TERMS)
};
let initial = StabilizerState::new_with_seed(num_qubits, seed)?;
Ok(Self {
num_qubits,
terms: vec![(Complex::ONE, initial)],
t_count: 0,
max_terms,
seed,
fork_counter: 1,
rng: StdRng::seed_from_u64(seed.wrapping_add(0xDEAD_BEEF)),
})
}
pub fn num_terms(&self) -> usize {
self.terms.len()
}
pub fn t_count(&self) -> usize {
self.t_count
}
pub fn num_qubits(&self) -> usize {
self.num_qubits
}
fn next_seed(&mut self) -> u64 {
let s = self
.seed
.wrapping_mul(6364136223846793005)
.wrapping_add(self.fork_counter);
self.fork_counter += 1;
s
}
fn check_qubit(&self, qubit: usize) -> Result<()> {
if qubit >= self.num_qubits {
Err(QuantumError::InvalidQubitIndex {
index: qubit as u32,
num_qubits: self.num_qubits as u32,
})
} else {
Ok(())
}
}
pub fn apply_clifford(&mut self, gate: &Gate) -> Result<()> {
if matches!(gate, Gate::Barrier) {
return Ok(());
}
if !StabilizerState::is_clifford_gate(gate) || matches!(gate, Gate::Measure(_)) {
return Err(QuantumError::CircuitError(format!(
"gate {:?} is not a (non-measurement) Clifford gate",
gate
)));
}
for &q in gate.qubits().iter() {
self.check_qubit(q as usize)?;
}
for (_coeff, state) in &mut self.terms {
state.apply_gate(gate)?;
}
Ok(())
}
fn apply_t_impl(&mut self, qubit: usize, c_plus: Complex, c_minus: Complex) -> Result<()> {
self.check_qubit(qubit)?;
let new_count = self.terms.len() * 2;
if new_count > self.max_terms {
return Err(QuantumError::CircuitError(format!(
"T/Tdg gate would create {} terms, exceeding max of {}",
new_count, self.max_terms
)));
}
let old_terms = std::mem::take(&mut self.terms);
let mut new_terms = Vec::with_capacity(new_count);
for (alpha, state) in old_terms {
let fork_seed = self.next_seed();
let mut forked = state.clone_with_seed(fork_seed)?;
forked.z_gate(qubit);
new_terms.push((alpha * c_plus, state));
new_terms.push((alpha * c_minus, forked));
}
self.terms = new_terms;
self.t_count += 1;
Ok(())
}
pub fn apply_t(&mut self, qubit: usize) -> Result<()> {
let omega = Complex::new(
std::f64::consts::FRAC_1_SQRT_2,
std::f64::consts::FRAC_1_SQRT_2,
);
let c_plus = (Complex::ONE + omega) * 0.5;
let c_minus = (Complex::ONE - omega) * 0.5;
self.apply_t_impl(qubit, c_plus, c_minus)
}
pub fn apply_tdg(&mut self, qubit: usize) -> Result<()> {
let omega_conj = Complex::new(
std::f64::consts::FRAC_1_SQRT_2,
-std::f64::consts::FRAC_1_SQRT_2,
);
let c_plus = (Complex::ONE + omega_conj) * 0.5;
let c_minus = (Complex::ONE - omega_conj) * 0.5;
self.apply_t_impl(qubit, c_plus, c_minus)
}
pub fn apply_gate(&mut self, gate: &Gate) -> Result<Vec<MeasurementOutcome>> {
match gate {
Gate::T(q) => {
self.apply_t(*q as usize)?;
Ok(vec![])
}
Gate::Tdg(q) => {
self.apply_tdg(*q as usize)?;
Ok(vec![])
}
Gate::Measure(q) => {
let outcome = self.measure(*q as usize)?;
Ok(vec![outcome])
}
Gate::Barrier => Ok(vec![]),
_ if StabilizerState::is_clifford_gate(gate) => {
self.apply_clifford(gate)?;
Ok(vec![])
}
_ => Err(QuantumError::CircuitError(format!(
"gate {:?} is not supported by the Clifford+T backend; \
only Clifford gates and T/Tdg are allowed",
gate
))),
}
}
pub fn measure(&mut self, qubit: usize) -> Result<MeasurementOutcome> {
self.check_qubit(qubit)?;
if self.terms.is_empty() {
return Err(QuantumError::CircuitError(
"no stabilizer terms remain".into(),
));
}
let n = self.terms.len();
let mut term_p0: Vec<f64> = Vec::with_capacity(n);
let mut total_weight = 0.0f64;
let mut p0_weighted = 0.0f64;
for i in 0..n {
let w = self.terms[i].0.norm_sq();
if w < 1e-30 {
term_p0.push(0.5);
continue;
}
total_weight += w;
let probe_seed = self.next_seed();
let mut probe = self.terms[i].1.clone_with_seed(probe_seed)?;
let probe_meas = probe.measure(qubit)?;
let p0_k = if (probe_meas.probability - 1.0).abs() < 1e-10 {
if !probe_meas.result { 1.0 } else { 0.0 }
} else {
0.5
};
term_p0.push(p0_k);
p0_weighted += w * p0_k;
}
let p0 = if total_weight > 1e-30 {
(p0_weighted / total_weight).clamp(0.0, 1.0)
} else {
0.5
};
let r: f64 = self.rng.gen();
let outcome = r >= p0; let prob = if outcome { 1.0 - p0 } else { p0 };
let old_terms = std::mem::take(&mut self.terms);
let mut new_terms: Vec<(Complex, StabilizerState)> = Vec::with_capacity(old_terms.len());
for (i, (alpha, state)) in old_terms.into_iter().enumerate() {
let w = alpha.norm_sq();
if w < 1e-30 {
continue;
}
let p0_k = term_p0[i];
let term_prob = if !outcome { p0_k } else { 1.0 - p0_k };
if term_prob < 1e-15 {
continue;
}
for _ in 0..50 {
let clone_seed = self.next_seed();
let mut cloned = state.clone_with_seed(clone_seed)?;
let meas = cloned.measure(qubit)?;
if meas.result == outcome {
let scale = term_prob.sqrt();
new_terms.push((alpha * scale, cloned));
break;
}
}
}
self.terms = new_terms;
self.renormalize();
Ok(MeasurementOutcome {
qubit: qubit as u32,
result: outcome,
probability: prob,
})
}
pub fn expectation_value(&self, qubit: usize) -> f64 {
if qubit >= self.num_qubits {
return 0.0;
}
let mut weighted_z = 0.0f64;
let mut total_weight = 0.0f64;
let mut probe_seed = self
.seed
.wrapping_add(self.fork_counter)
.wrapping_add(0xCAFE_BABE);
for (alpha, state) in &self.terms {
let w = alpha.norm_sq();
if w < 1e-30 {
continue;
}
total_weight += w;
probe_seed = probe_seed.wrapping_mul(6364136223846793005).wrapping_add(1);
if let Ok(mut probe) = state.clone_with_seed(probe_seed) {
if let Ok(meas) = probe.measure(qubit) {
let z_k = if (meas.probability - 1.0).abs() < 1e-10 {
if !meas.result { 1.0 } else { -1.0 }
} else {
0.0
};
weighted_z += w * z_k;
}
}
}
if total_weight > 1e-30 {
weighted_z / total_weight
} else {
0.0
}
}
pub fn prune_small_terms(&mut self, threshold: f64) {
let threshold_sq = threshold * threshold;
let old_terms = std::mem::take(&mut self.terms);
let mut new_terms = Vec::with_capacity(old_terms.len());
for (alpha, state) in old_terms {
if alpha.norm_sq() >= threshold_sq {
new_terms.push((alpha, state));
}
}
self.terms = new_terms;
self.renormalize();
}
fn renormalize(&mut self) {
let total: f64 = self.terms.iter().map(|(a, _)| a.norm_sq()).sum();
if total < 1e-30 || (total - 1.0).abs() < 1e-14 {
return;
}
let inv_sqrt = 1.0 / total.sqrt();
for (a, _) in &mut self.terms {
*a = *a * inv_sqrt;
}
}
pub fn run_circuit(
circuit: &QuantumCircuit,
max_t: usize,
seed: u64,
) -> Result<CliffordTResult> {
let mut state = CliffordTState::new(circuit.num_qubits() as usize, max_t, seed)?;
let mut measurements = Vec::new();
let mut peak_terms: usize = 1;
for gate in circuit.gates() {
let outcomes = state.apply_gate(gate)?;
measurements.extend(outcomes);
if state.num_terms() > peak_terms {
peak_terms = state.num_terms();
}
}
Ok(CliffordTResult {
measurements,
t_count: state.t_count(),
num_terms: state.num_terms(),
peak_terms,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::QuantumCircuit;
use crate::gate::Gate;
#[test]
fn test_pure_clifford_x_gate() {
let mut ct = CliffordTState::new(1, 0, 42).unwrap();
ct.apply_gate(&Gate::X(0)).unwrap();
let m = ct.measure(0).unwrap();
assert!(m.result, "X|0> should measure |1>");
assert_eq!(ct.num_terms(), 1, "pure Clifford keeps 1 term");
}
#[test]
fn test_pure_clifford_bell_state() {
for seed in 0..20u64 {
let mut ct = CliffordTState::new(2, 0, seed).unwrap();
ct.apply_gate(&Gate::H(0)).unwrap();
ct.apply_gate(&Gate::CNOT(0, 1)).unwrap();
let m0 = ct.measure(0).unwrap();
let m1 = ct.measure(1).unwrap();
assert_eq!(
m0.result, m1.result,
"Bell state qubits must agree (seed={})",
seed
);
}
}
#[test]
fn test_single_t_creates_two_terms() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
assert_eq!(st.num_terms(), 1);
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
assert_eq!(st.t_count(), 1);
}
#[test]
fn test_two_t_gates_create_four_terms() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 4);
assert_eq!(st.t_count(), 2);
}
#[test]
fn test_t_then_tdg_prunable() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
st.apply_gate(&Gate::Tdg(0)).unwrap();
assert_eq!(st.num_terms(), 4);
st.prune_small_terms(0.1);
let m = st.measure(0).unwrap();
assert!(!m.result, "T.Tdg|0> should measure |0>");
}
#[test]
fn test_bell_plus_t_correlation() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0);
circuit.cnot(0, 1);
circuit.t(0);
circuit.measure(0);
circuit.measure(1);
let shots = 100;
let mut correlated = 0;
for s in 0..shots {
let res = CliffordTState::run_circuit(&circuit, 4, s as u64 * 7919 + 13).unwrap();
assert_eq!(res.measurements.len(), 2);
assert_eq!(res.t_count, 1);
assert_eq!(res.peak_terms, 2);
if res.measurements[0].result == res.measurements[1].result {
correlated += 1;
}
}
assert!(
correlated > 90,
"Bell+T: qubits should be correlated ({}/{})",
correlated,
shots
);
}
#[test]
fn test_max_terms_exceeded() {
let mut st = CliffordTState::new(1, 2, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap(); st.apply_gate(&Gate::T(0)).unwrap(); let err = st.apply_gate(&Gate::T(0)); assert!(err.is_err());
}
#[test]
fn test_measure_collapses_terms() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
let _m = st.measure(0).unwrap();
assert!(st.num_terms() >= 1 && st.num_terms() <= 2);
}
#[test]
fn test_ghz_plus_t() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0);
circuit.cnot(0, 1);
circuit.cnot(1, 2);
circuit.t(0);
circuit.measure(0);
circuit.measure(1);
circuit.measure(2);
let shots = 100;
let mut all_same = 0;
for s in 0..shots {
let res = CliffordTState::run_circuit(&circuit, 4, s as u64 * 999983 + 7).unwrap();
assert_eq!(res.measurements.len(), 3);
assert_eq!(res.t_count, 1);
let (r0, r1, r2) = (
res.measurements[0].result,
res.measurements[1].result,
res.measurements[2].result,
);
if r0 == r1 && r1 == r2 {
all_same += 1;
}
}
assert!(
all_same > 90,
"GHZ+T: all qubits should agree ({}/{})",
all_same,
shots
);
}
#[test]
fn test_unsupported_gates_rejected() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
assert!(st.apply_gate(&Gate::Rx(0, 0.5)).is_err());
assert!(st.apply_gate(&Gate::Ry(0, 0.3)).is_err());
assert!(st.apply_gate(&Gate::Rz(0, 0.1)).is_err());
assert!(st.apply_gate(&Gate::Phase(0, 1.0)).is_err());
}
#[test]
fn test_zero_qubits() {
assert!(CliffordTState::new(0, 4, 42).is_err());
}
#[test]
fn test_expectation_z_ground() {
let st = CliffordTState::new(1, 4, 42).unwrap();
let z = st.expectation_value(0);
assert!(
(z - 1.0).abs() < 0.01,
"<Z> for |0> should be +1, got {}",
z
);
}
#[test]
fn test_expectation_z_excited() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::X(0)).unwrap();
let z = st.expectation_value(0);
assert!(
(z + 1.0).abs() < 0.01,
"<Z> for |1> should be -1, got {}",
z
);
}
#[test]
fn test_expectation_z_superposition() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
let z = st.expectation_value(0);
assert!(z.abs() < 0.01, "<Z> for |+> should be 0, got {}", z);
}
#[test]
fn test_tdg_creates_two_terms() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::Tdg(0)).unwrap();
assert_eq!(st.num_terms(), 2);
assert_eq!(st.t_count(), 1);
}
#[test]
fn test_run_circuit_statistics() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0);
circuit.t(0);
circuit.measure(0);
let res = CliffordTState::run_circuit(&circuit, 4, 42).unwrap();
assert_eq!(res.measurements.len(), 1);
assert_eq!(res.t_count, 1);
assert_eq!(res.peak_terms, 2);
}
#[test]
fn test_prune_low_threshold_keeps_all() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
st.prune_small_terms(1e-15);
assert_eq!(st.num_terms(), 2);
}
#[test]
fn test_prune_high_threshold_removes_all() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
st.prune_small_terms(100.0);
assert_eq!(st.num_terms(), 0);
}
#[test]
fn test_barrier() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::Barrier).unwrap();
assert_eq!(st.num_terms(), 1);
}
#[test]
fn test_invalid_qubit_t() {
let mut st = CliffordTState::new(2, 4, 42).unwrap();
assert!(st.apply_t(5).is_err());
}
#[test]
fn test_invalid_qubit_tdg() {
let mut st = CliffordTState::new(2, 4, 42).unwrap();
assert!(st.apply_tdg(5).is_err());
}
#[test]
fn test_invalid_qubit_measure() {
let mut st = CliffordTState::new(2, 4, 42).unwrap();
assert!(st.measure(5).is_err());
}
#[test]
fn test_t_on_different_qubits() {
let mut st = CliffordTState::new(2, 4, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
st.apply_gate(&Gate::T(1)).unwrap();
assert_eq!(st.num_terms(), 4);
assert_eq!(st.t_count(), 2);
}
#[test]
fn test_clifford_after_t() {
let mut st = CliffordTState::new(2, 4, 42).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
assert_eq!(st.num_terms(), 2);
st.apply_gate(&Gate::H(0)).unwrap();
assert_eq!(st.num_terms(), 2);
st.apply_gate(&Gate::CNOT(0, 1)).unwrap();
assert_eq!(st.num_terms(), 2);
}
#[test]
fn test_deterministic_measure_x() {
let mut st = CliffordTState::new(1, 4, 42).unwrap();
st.apply_gate(&Gate::X(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(m.result, "X|0> should measure |1>");
}
#[test]
fn test_multi_measure_circuit() {
let mut circuit = QuantumCircuit::new(3);
circuit.x(1);
circuit.measure(0);
circuit.measure(1);
circuit.measure(2);
let res = CliffordTState::run_circuit(&circuit, 0, 42).unwrap();
assert_eq!(res.measurements.len(), 3);
assert!(!res.measurements[0].result);
assert!(res.measurements[1].result);
assert!(!res.measurements[2].result);
}
#[test]
fn test_s_gate_clifford_t() {
let mut st = CliffordTState::new(1, 0, 42).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
st.apply_gate(&Gate::S(0)).unwrap();
st.apply_gate(&Gate::S(0)).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(m.result, "H.S.S.H|0> = X|0> = |1>");
}
#[test]
fn test_sdg_gate() {
let mut st = CliffordTState::new(1, 0, 42).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
st.apply_gate(&Gate::S(0)).unwrap();
st.apply_gate(&Gate::Sdg(0)).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(!m.result, "H.S.Sdg.H|0> = |0>");
}
#[test]
fn test_cz_gate_clifford_t() {
let mut st = CliffordTState::new(2, 0, 42).unwrap();
st.apply_gate(&Gate::H(0)).unwrap();
st.apply_gate(&Gate::CZ(0, 1)).unwrap();
let m0 = st.measure(0).unwrap();
assert_eq!(m0.probability, 0.5, "CZ on |+0> leaves q0 random");
}
#[test]
fn test_swap_gate_clifford_t() {
let mut st = CliffordTState::new(2, 0, 42).unwrap();
st.apply_gate(&Gate::X(0)).unwrap();
st.apply_gate(&Gate::SWAP(0, 1)).unwrap();
let m0 = st.measure(0).unwrap();
let m1 = st.measure(1).unwrap();
assert!(!m0.result, "after SWAP |10>, q0 = |0>");
assert!(m1.result, "after SWAP |10>, q1 = |1>");
}
#[test]
fn test_expectation_value_oob() {
let st = CliffordTState::new(1, 4, 42).unwrap();
assert_eq!(st.expectation_value(99), 0.0);
}
#[test]
fn test_t_on_zero_measure() {
for seed in 0..20u64 {
let mut st = CliffordTState::new(1, 4, seed).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(!m.result, "T|0> should measure |0> (seed={})", seed);
}
}
#[test]
fn test_t_on_one_measure() {
for seed in 0..20u64 {
let mut st = CliffordTState::new(1, 4, seed).unwrap();
st.apply_gate(&Gate::X(0)).unwrap();
st.apply_gate(&Gate::T(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(m.result, "T|1> should measure |1> (seed={})", seed);
}
}
#[test]
fn test_num_qubits_accessor() {
let st = CliffordTState::new(5, 4, 42).unwrap();
assert_eq!(st.num_qubits(), 5);
}
#[test]
fn test_y_gate() {
let mut st = CliffordTState::new(1, 0, 42).unwrap();
st.apply_gate(&Gate::Y(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(m.result, "Y|0> should measure |1>");
}
#[test]
fn test_z_gate_on_zero() {
let mut st = CliffordTState::new(1, 0, 42).unwrap();
st.apply_gate(&Gate::Z(0)).unwrap();
let m = st.measure(0).unwrap();
assert!(!m.result, "Z|0> = |0>");
}
}