quantrs2_sim/stabilizer/
functions.rs1use quantrs2_circuit::prelude::*;
6use quantrs2_core::gate::GateOp;
7use quantrs2_core::prelude::*;
8use scirs2_core::random::prelude::*;
9use std::sync::Arc;
10
11use super::types::{
12 CliffordCircuitBuilder, StabilizerGate, StabilizerSimulator, StabilizerTableau,
13};
14
15pub type StabilizerPhase = u8;
18pub mod phase {
20 pub const PLUS_ONE: u8 = 0;
22 pub const PLUS_I: u8 = 1;
24 pub const MINUS_ONE: u8 = 2;
26 pub const MINUS_I: u8 = 3;
28}
29#[must_use]
31pub fn is_clifford_circuit<const N: usize>(circuit: &Circuit<N>) -> bool {
32 circuit.gates().iter().all(|gate| {
33 matches!(
34 gate.name(),
35 "H" | "S" | "S†" | "CNOT" | "X" | "Y" | "Z" | "CZ" | "Phase" | "PhaseDagger"
36 )
37 })
38}
39pub(super) fn gate_to_stabilizer(gate: &Arc<dyn GateOp + Send + Sync>) -> Option<StabilizerGate> {
41 let gate_name = gate.name();
42 let qubits = gate.qubits();
43 match gate_name {
44 "H" => {
45 if qubits.len() == 1 {
46 Some(StabilizerGate::H(qubits[0].0 as usize))
47 } else {
48 None
49 }
50 }
51 "S" | "Phase" => {
52 if qubits.len() == 1 {
53 Some(StabilizerGate::S(qubits[0].0 as usize))
54 } else {
55 None
56 }
57 }
58 "X" => {
59 if qubits.len() == 1 {
60 Some(StabilizerGate::X(qubits[0].0 as usize))
61 } else {
62 None
63 }
64 }
65 "Y" => {
66 if qubits.len() == 1 {
67 Some(StabilizerGate::Y(qubits[0].0 as usize))
68 } else {
69 None
70 }
71 }
72 "Z" => {
73 if qubits.len() == 1 {
74 Some(StabilizerGate::Z(qubits[0].0 as usize))
75 } else {
76 None
77 }
78 }
79 "CNOT" => {
80 if qubits.len() == 2 {
81 Some(StabilizerGate::CNOT(
82 qubits[0].0 as usize,
83 qubits[1].0 as usize,
84 ))
85 } else {
86 None
87 }
88 }
89 "CZ" => {
90 if qubits.len() == 2 {
91 Some(StabilizerGate::CZ(
92 qubits[0].0 as usize,
93 qubits[1].0 as usize,
94 ))
95 } else {
96 None
97 }
98 }
99 _ => None,
100 }
101}
102#[cfg(test)]
103mod tests {
104 use super::*;
105 #[test]
106 fn test_stabilizer_init() {
107 let sim = StabilizerSimulator::new(3);
108 let stabs = sim.get_stabilizers();
109 assert_eq!(stabs.len(), 3);
110 assert_eq!(stabs[0], "+ZII");
111 assert_eq!(stabs[1], "+IZI");
112 assert_eq!(stabs[2], "+IIZ");
113 }
114 #[test]
115 fn test_hadamard_gate() {
116 let mut sim = StabilizerSimulator::new(1);
117 sim.apply_gate(StabilizerGate::H(0))
118 .expect("Hadamard gate application should succeed");
119 let stabs = sim.get_stabilizers();
120 assert_eq!(stabs[0], "+X");
121 }
122 #[test]
123 fn test_bell_state() {
124 let mut sim = StabilizerSimulator::new(2);
125 sim.apply_gate(StabilizerGate::H(0))
126 .expect("Hadamard gate application should succeed");
127 sim.apply_gate(StabilizerGate::CNOT(0, 1))
128 .expect("CNOT gate application should succeed");
129 let stabs = sim.get_stabilizers();
130 assert!(stabs.contains(&"+XX".to_string()));
131 assert!(stabs.contains(&"+ZZ".to_string()));
132 }
133 #[test]
134 fn test_ghz_state() {
135 let mut sim = StabilizerSimulator::new(3);
136 sim.apply_gate(StabilizerGate::H(0))
137 .expect("Hadamard gate application should succeed");
138 sim.apply_gate(StabilizerGate::CNOT(0, 1))
139 .expect("CNOT gate application should succeed");
140 sim.apply_gate(StabilizerGate::CNOT(1, 2))
141 .expect("CNOT gate application should succeed");
142 let stabs = sim.get_stabilizers();
143 assert!(stabs.contains(&"+XXX".to_string()));
144 assert!(stabs.contains(&"+ZZI".to_string()));
145 assert!(stabs.contains(&"+IZZ".to_string()));
146 }
147 #[test]
148 fn test_s_dag_gate() {
149 let mut sim = StabilizerSimulator::new(1);
150 sim.apply_gate(StabilizerGate::S(0))
151 .expect("S gate application should succeed");
152 sim.apply_gate(StabilizerGate::SDag(0))
153 .expect("S†gate application should succeed");
154 let stabs = sim.get_stabilizers();
155 assert_eq!(stabs[0], "+Z");
156 }
157 #[test]
158 fn test_sqrt_x_gate() {
159 let mut sim1 = StabilizerSimulator::new(1);
160 sim1.apply_gate(StabilizerGate::SqrtX(0))
161 .expect("√X gate application should succeed");
162 sim1.apply_gate(StabilizerGate::SqrtX(0))
163 .expect("√X gate application should succeed");
164 let stabs1 = sim1.get_stabilizers();
165 let mut sim2 = StabilizerSimulator::new(1);
166 sim2.apply_gate(StabilizerGate::X(0))
167 .expect("X gate application should succeed");
168 let stabs2 = sim2.get_stabilizers();
169 assert!(stabs1[0] == "+Z" || stabs1[0] == "-Z");
170 assert!(stabs2[0] == "+Z" || stabs2[0] == "-Z");
171 }
172 #[test]
173 fn test_sqrt_y_gate() {
174 let mut sim1 = StabilizerSimulator::new(1);
175 sim1.apply_gate(StabilizerGate::SqrtY(0))
176 .expect("√Y gate application should succeed");
177 sim1.apply_gate(StabilizerGate::SqrtY(0))
178 .expect("√Y gate application should succeed");
179 let stabs1 = sim1.get_stabilizers();
180 let mut sim2 = StabilizerSimulator::new(1);
181 sim2.apply_gate(StabilizerGate::Y(0))
182 .expect("Y gate application should succeed");
183 let stabs2 = sim2.get_stabilizers();
184 assert_eq!(stabs1[0], stabs2[0]);
185 }
186 #[test]
187 fn test_cz_gate() {
188 let mut sim = StabilizerSimulator::new(2);
189 sim.apply_gate(StabilizerGate::H(0))
190 .expect("Hadamard gate application should succeed");
191 sim.apply_gate(StabilizerGate::H(1))
192 .expect("Hadamard gate application should succeed");
193 sim.apply_gate(StabilizerGate::CZ(0, 1))
194 .expect("CZ gate application should succeed");
195 let stabs = sim.get_stabilizers();
196 assert!(stabs.len() == 2);
197 }
198 #[test]
199 fn test_cy_gate() {
200 let mut sim = StabilizerSimulator::new(2);
201 sim.apply_gate(StabilizerGate::H(0))
202 .expect("Hadamard gate application should succeed");
203 sim.apply_gate(StabilizerGate::CY(0, 1))
204 .expect("CY gate application should succeed");
205 let stabs = sim.get_stabilizers();
206 assert!(stabs.len() == 2);
207 }
208 #[test]
209 fn test_swap_gate() {
210 let mut sim = StabilizerSimulator::new(2);
211 sim.apply_gate(StabilizerGate::X(1))
212 .expect("X gate application should succeed");
213 sim.apply_gate(StabilizerGate::SWAP(0, 1))
214 .expect("SWAP gate application should succeed");
215 let stabs = sim.get_stabilizers();
216 assert!(stabs.len() == 2);
217 }
218 #[test]
219 fn test_builder_pattern_new_gates() {
220 let sim = CliffordCircuitBuilder::new(2)
221 .h(0)
222 .s_dag(0)
223 .sqrt_x(1)
224 .cz(0, 1)
225 .run()
226 .expect("Circuit execution should succeed");
227 let stabs = sim.get_stabilizers();
228 assert!(stabs.len() == 2);
229 }
230 #[test]
231 fn test_large_clifford_circuit() {
232 let mut sim = StabilizerSimulator::new(100);
233 for i in 0..100 {
234 sim.apply_gate(StabilizerGate::H(i))
235 .expect("Hadamard gate application should succeed");
236 }
237 for i in 0..99 {
238 sim.apply_gate(StabilizerGate::CNOT(i, i + 1))
239 .expect("CNOT gate application should succeed");
240 }
241 let stabs = sim.get_stabilizers();
242 assert_eq!(stabs.len(), 100);
243 }
244 #[test]
245 fn test_measurement_randomness() {
246 let mut sim = StabilizerSimulator::new(1);
247 sim.apply_gate(StabilizerGate::H(0))
248 .expect("Hadamard gate application should succeed");
249 let mut outcomes = Vec::new();
250 for _ in 0..10 {
251 let mut test_sim = StabilizerSimulator::new(1);
252 test_sim
253 .apply_gate(StabilizerGate::H(0))
254 .expect("Hadamard gate application should succeed");
255 let outcome = test_sim.measure(0).expect("Measurement should succeed");
256 outcomes.push(outcome);
257 }
258 let first = outcomes[0];
259 let all_same = outcomes.iter().all(|&x| x == first);
260 assert!(
261 !all_same || outcomes.len() < 5,
262 "Measurements should show randomness"
263 );
264 }
265 #[test]
266 fn test_measure_x_basis() {
267 let mut sim = StabilizerSimulator::new(1);
268 sim.apply_gate(StabilizerGate::H(0))
269 .expect("Hadamard gate application should succeed");
270 let outcome = sim
271 .tableau
272 .measure_x(0)
273 .expect("X-basis measurement should succeed");
274 assert!(!outcome);
275 }
276 #[test]
277 fn test_measure_y_basis() {
278 let mut sim = StabilizerSimulator::new(1);
279 sim.apply_gate(StabilizerGate::H(0)).unwrap();
280 sim.apply_gate(StabilizerGate::S(0)).unwrap();
281 let stabs = sim.get_stabilizers();
282 assert_eq!(stabs[0], "+Y");
283 let outcome = sim
284 .tableau
285 .measure_y(0)
286 .expect("Y-basis measurement should succeed");
287 assert!(!outcome);
288 }
289 #[test]
290 fn test_reset_operation() {
291 let mut sim = StabilizerSimulator::new(1);
292 sim.apply_gate(StabilizerGate::X(0))
293 .expect("X gate application should succeed");
294 sim.tableau.reset(0).expect("Reset should succeed");
295 let outcome = sim.measure(0).expect("Measurement should succeed");
296 assert!(!outcome);
297 let stabs = sim.get_stabilizers();
298 assert_eq!(stabs[0], "+Z");
299 }
300 #[test]
301 fn test_reset_from_superposition() {
302 let mut sim = StabilizerSimulator::new(1);
303 sim.apply_gate(StabilizerGate::H(0))
304 .expect("Hadamard gate application should succeed");
305 sim.tableau.reset(0).expect("Reset should succeed");
306 let outcome = sim.measure(0).expect("Measurement should succeed");
307 assert!(!outcome);
308 }
309 #[test]
310 fn test_x_y_measurements_commute() {
311 let mut sim = StabilizerSimulator::new(2);
312 sim.apply_gate(StabilizerGate::H(0)).unwrap();
313 sim.apply_gate(StabilizerGate::H(1)).unwrap();
314 sim.apply_gate(StabilizerGate::S(1)).unwrap();
315 let _outcome_x = sim.tableau.measure_x(0).unwrap();
316 let _outcome_y = sim.tableau.measure_y(1).unwrap();
317 }
318 #[test]
319 fn test_imaginary_phase_tracking() {
320 let mut tableau = StabilizerTableau::new(1);
321 tableau.apply_h(0).unwrap();
322 tableau.apply_s(0).unwrap();
323 let stabs = tableau.get_stabilizers();
324 assert_eq!(stabs[0], "+Y");
325 }
326 #[test]
327 fn test_imaginary_phase_with_s_dag() {
328 let mut tableau = StabilizerTableau::new(1);
329 tableau.apply_h(0).unwrap();
330 tableau.apply_s_dag(0).unwrap();
331 let stabs = tableau.get_stabilizers();
332 assert_eq!(stabs[0], "-Y");
333 }
334 #[test]
335 fn test_stim_format_identity() {
336 let mut tableau = StabilizerTableau::with_format(2, true);
337 let stabs = tableau.get_stabilizers();
338 assert_eq!(stabs[0], "+Z_");
339 assert_eq!(stabs[1], "+_Z");
340 tableau.apply_h(0).unwrap();
341 let stabs = tableau.get_stabilizers();
342 assert_eq!(stabs[0], "+X_");
343 assert_eq!(stabs[1], "+_Z");
344 }
345 #[test]
346 fn test_standard_format_identity() {
347 let tableau = StabilizerTableau::with_format(2, false);
348 let stabs = tableau.get_stabilizers();
349 assert_eq!(stabs[0], "+ZI");
350 assert_eq!(stabs[1], "+IZ");
351 }
352 #[test]
353 fn test_destabilizers_output() {
354 let mut tableau = StabilizerTableau::new(2);
355 let destabs = tableau.get_destabilizers();
356 assert_eq!(destabs[0], "+XI");
357 assert_eq!(destabs[1], "+IX");
358 tableau.apply_h(0).unwrap();
359 let destabs = tableau.get_destabilizers();
360 assert_eq!(destabs[0], "+ZI");
361 assert_eq!(destabs[1], "+IX");
362 }
363 #[test]
364 fn test_phase_constants() {
365 assert_eq!(phase::PLUS_ONE, 0);
366 assert_eq!(phase::PLUS_I, 1);
367 assert_eq!(phase::MINUS_ONE, 2);
368 assert_eq!(phase::MINUS_I, 3);
369 }
370 #[test]
371 fn test_phase_arithmetic() {
372 assert_eq!(
373 StabilizerTableau::negate_phase(phase::PLUS_ONE),
374 phase::MINUS_ONE
375 );
376 assert_eq!(
377 StabilizerTableau::negate_phase(phase::PLUS_I),
378 phase::MINUS_I
379 );
380 assert_eq!(
381 StabilizerTableau::negate_phase(phase::MINUS_ONE),
382 phase::PLUS_ONE
383 );
384 assert_eq!(
385 StabilizerTableau::negate_phase(phase::MINUS_I),
386 phase::PLUS_I
387 );
388 assert_eq!(
389 StabilizerTableau::multiply_by_i(phase::PLUS_ONE),
390 phase::PLUS_I
391 );
392 assert_eq!(
393 StabilizerTableau::multiply_by_i(phase::PLUS_I),
394 phase::MINUS_ONE
395 );
396 assert_eq!(
397 StabilizerTableau::multiply_by_i(phase::MINUS_ONE),
398 phase::MINUS_I
399 );
400 assert_eq!(
401 StabilizerTableau::multiply_by_i(phase::MINUS_I),
402 phase::PLUS_ONE
403 );
404 assert_eq!(
405 StabilizerTableau::multiply_by_minus_i(phase::PLUS_ONE),
406 phase::MINUS_I
407 );
408 assert_eq!(
409 StabilizerTableau::multiply_by_minus_i(phase::PLUS_I),
410 phase::PLUS_ONE
411 );
412 assert_eq!(
413 StabilizerTableau::multiply_by_minus_i(phase::MINUS_ONE),
414 phase::PLUS_I
415 );
416 assert_eq!(
417 StabilizerTableau::multiply_by_minus_i(phase::MINUS_I),
418 phase::MINUS_ONE
419 );
420 }
421 #[test]
422 fn test_y_gate_phase_tracking() {
423 let mut tableau = StabilizerTableau::new(1);
424 tableau.apply_y(0).unwrap();
425 let stabs = tableau.get_stabilizers();
426 assert_eq!(stabs[0], "-Z");
427 }
428 #[test]
429 fn test_sqrt_gates_produce_imaginary_phases() {
430 let mut tableau = StabilizerTableau::new(1);
431 tableau.apply_sqrt_y(0).unwrap();
432 let stabs = tableau.get_stabilizers();
433 assert_eq!(stabs[0], "-X");
434 }
435 #[test]
436 fn test_cz_gate_to_stabilizer_conversion() {
437 let mut circuit = Circuit::<2>::new();
440 circuit.cz(0, 1).expect("cz gate should be added");
441 let gates = circuit.gates();
442 assert_eq!(gates.len(), 1);
443 let converted = gate_to_stabilizer(&gates[0]);
444 assert!(
445 matches!(converted, Some(StabilizerGate::CZ(0, 1))),
446 "CZ must convert to StabilizerGate::CZ, got {converted:?}"
447 );
448 }
449 #[test]
450 fn test_stabilizer_run_applies_cz() {
451 use crate::simulator::Simulator;
457 let mut circuit = Circuit::<2>::new();
458 circuit
459 .h(0)
460 .expect("h(0)")
461 .h(1)
462 .expect("h(1)")
463 .cz(0, 1)
464 .expect("cz(0,1)");
465 let mut sim = StabilizerSimulator::new(2);
466 assert!(
467 sim.run(&circuit).is_ok(),
468 "Clifford circuit containing a CZ must simulate successfully"
469 );
470
471 let mut tableau_sim = StabilizerSimulator::new(2);
474 tableau_sim
475 .apply_gate(StabilizerGate::H(0))
476 .expect("h(0) on tableau");
477 tableau_sim
478 .apply_gate(StabilizerGate::H(1))
479 .expect("h(1) on tableau");
480 tableau_sim
481 .apply_gate(StabilizerGate::CZ(0, 1))
482 .expect("cz(0,1) on tableau");
483 let stabs = tableau_sim.get_stabilizers();
484 assert!(
485 stabs.contains(&"+XZ".to_string()),
486 "cluster-state stabilizer +XZ missing, got {stabs:?}"
487 );
488 assert!(
489 stabs.contains(&"+ZX".to_string()),
490 "cluster-state stabilizer +ZX missing, got {stabs:?}"
491 );
492 }
493 #[test]
494 fn test_stabilizer_run_rejects_non_clifford() {
495 use crate::simulator::Simulator;
498 let mut circuit = Circuit::<1>::new();
499 circuit.t(0).expect("t gate should be added");
500 let mut sim = StabilizerSimulator::new(1);
501 let result = sim.run(&circuit);
502 assert!(
503 result.is_err(),
504 "running a non-Clifford T gate through the stabilizer simulator must error"
505 );
506 }
507}