quantrs2_sim/
stabilizer.rs

1//! Stabilizer simulator for efficient simulation of Clifford circuits
2//!
3//! The stabilizer formalism provides an efficient way to simulate quantum circuits
4//! that consist only of Clifford gates (H, S, CNOT) and Pauli measurements.
5//! This implementation uses the tableau representation and leverages `SciRS2`
6//! for efficient data structures and operations.
7
8use crate::simulator::{Simulator, SimulatorResult};
9use quantrs2_circuit::prelude::*;
10use quantrs2_core::gate::GateOp;
11use quantrs2_core::prelude::*;
12use scirs2_core::ndarray::{Array2, ArrayView2};
13use scirs2_core::random::prelude::*;
14use scirs2_core::Complex64;
15use std::collections::HashMap;
16use std::sync::Arc;
17
18/// Phase encoding for Stim compatibility
19/// 0 = +1, 1 = +i, 2 = -1, 3 = -i
20pub type StabilizerPhase = u8;
21
22/// Phase constants for clarity
23pub mod phase {
24    /// Phase +1
25    pub const PLUS_ONE: u8 = 0;
26    /// Phase +i
27    pub const PLUS_I: u8 = 1;
28    /// Phase -1
29    pub const MINUS_ONE: u8 = 2;
30    /// Phase -i
31    pub const MINUS_I: u8 = 3;
32}
33
34/// Stabilizer tableau representation
35///
36/// The tableau stores generators of the stabilizer group as rows.
37/// Each row represents a Pauli string with phase.
38///
39/// Phase encoding (Stim-compatible):
40/// - 0 = +1
41/// - 1 = +i
42/// - 2 = -1
43/// - 3 = -i
44#[derive(Debug, Clone)]
45pub struct StabilizerTableau {
46    /// Number of qubits
47    num_qubits: usize,
48    /// X part of stabilizers (n x n matrix)
49    x_matrix: Array2<bool>,
50    /// Z part of stabilizers (n x n matrix)
51    z_matrix: Array2<bool>,
52    /// Phase vector (n elements, encoded as 0=+1, 1=+i, 2=-1, 3=-i)
53    phase: Vec<StabilizerPhase>,
54    /// Destabilizers X part (n x n matrix)
55    destab_x: Array2<bool>,
56    /// Destabilizers Z part (n x n matrix)
57    destab_z: Array2<bool>,
58    /// Destabilizer phases (same encoding as phase)
59    destab_phase: Vec<StabilizerPhase>,
60    /// Pauli string format: true for Stim-style (`_` for identity), false for standard (`I`)
61    stim_format: bool,
62}
63
64impl StabilizerTableau {
65    /// Create a new tableau in the |0...0⟩ state
66    #[must_use]
67    pub fn new(num_qubits: usize) -> Self {
68        Self::with_format(num_qubits, false)
69    }
70
71    /// Create a new tableau with specified Pauli string format
72    ///
73    /// # Arguments
74    /// * `num_qubits` - Number of qubits
75    /// * `stim_format` - Use Stim format (`_` for identity) if true, standard format (`I`) if false
76    #[must_use]
77    pub fn with_format(num_qubits: usize, stim_format: bool) -> Self {
78        let mut x_matrix = Array2::from_elem((num_qubits, num_qubits), false);
79        let mut z_matrix = Array2::from_elem((num_qubits, num_qubits), false);
80        let mut destab_x = Array2::from_elem((num_qubits, num_qubits), false);
81        let mut destab_z = Array2::from_elem((num_qubits, num_qubits), false);
82
83        // Initialize stabilizers as Z_i and destabilizers as X_i
84        for i in 0..num_qubits {
85            z_matrix[[i, i]] = true; // Stabilizer i is Z_i
86            destab_x[[i, i]] = true; // Destabilizer i is X_i
87        }
88
89        Self {
90            num_qubits,
91            x_matrix,
92            z_matrix,
93            phase: vec![phase::PLUS_ONE; num_qubits],
94            destab_x,
95            destab_z,
96            destab_phase: vec![phase::PLUS_ONE; num_qubits],
97            stim_format,
98        }
99    }
100
101    /// Set the Pauli string format
102    pub fn set_stim_format(&mut self, stim_format: bool) {
103        self.stim_format = stim_format;
104    }
105
106    /// Get the Pauli string format
107    #[must_use]
108    pub const fn is_stim_format(&self) -> bool {
109        self.stim_format
110    }
111
112    /// Multiply phase by -1 (add 2 mod 4)
113    #[inline]
114    fn negate_phase(p: StabilizerPhase) -> StabilizerPhase {
115        (p + 2) & 3
116    }
117
118    /// Multiply phase by i (add 1 mod 4)
119    #[inline]
120    fn multiply_by_i(p: StabilizerPhase) -> StabilizerPhase {
121        (p + 1) & 3
122    }
123
124    /// Multiply phase by -i (add 3 mod 4)
125    #[inline]
126    fn multiply_by_minus_i(p: StabilizerPhase) -> StabilizerPhase {
127        (p + 3) & 3
128    }
129
130    /// Add two phases (mod 4)
131    #[inline]
132    fn add_phases(p1: StabilizerPhase, p2: StabilizerPhase) -> StabilizerPhase {
133        (p1 + p2) & 3
134    }
135
136    /// Compute the phase contribution from row multiplication
137    /// When multiplying Pauli strings P1 and P2, the phase depends on the anticommutation
138    /// XZ = iY, ZX = -iY, etc.
139    #[inline]
140    fn rowsum_phase(x1: bool, z1: bool, x2: bool, z2: bool) -> StabilizerPhase {
141        // This computes the phase exponent from multiplying two Pauli operators
142        // Using the rule: XZ = iY, ZX = -iY, etc.
143        // The formula is: i^(x1*z2*(1 - 2*x2) + x2*z1*(2*z2 - 1)) but simplified
144        // For efficiency, we use a lookup table approach
145        match (x1, z1, x2, z2) {
146            // Identity cases (no contribution)
147            (false, false, _, _) | (_, _, false, false) => 0,
148            // X * Z = iY
149            (true, false, false, true) => 1,
150            // Z * X = -iY
151            (false, true, true, false) => 3,
152            // X * Y = iZ, Y * X = -iZ, etc.
153            (true, false, true, true) => 1, // X * Y = iZ
154            (true, true, true, false) => 3, // Y * X = -iZ
155            // Y * Z = iX, Z * Y = -iX
156            (true, true, false, true) => 1, // Y * Z = iX
157            (false, true, true, true) => 3, // Z * Y = -iX
158            // Same Pauli (no phase change): X*X=I, Y*Y=I, Z*Z=I
159            (true, false, true, false) => 0, // X * X
160            (false, true, false, true) => 0, // Z * Z
161            (true, true, true, true) => 0,   // Y * Y
162        }
163    }
164
165    /// Compute the phase contribution when multiplying a Pauli string (given as vectors)
166    /// with row `row_idx` from the tableau
167    fn compute_multiplication_phase(
168        &self,
169        result_x: &[bool],
170        result_z: &[bool],
171        row_idx: usize,
172    ) -> StabilizerPhase {
173        let mut total_phase: StabilizerPhase = 0;
174        for j in 0..self.num_qubits {
175            let phase_contrib = Self::rowsum_phase(
176                result_x[j],
177                result_z[j],
178                self.x_matrix[[row_idx, j]],
179                self.z_matrix[[row_idx, j]],
180            );
181            total_phase = Self::add_phases(total_phase, phase_contrib);
182        }
183        total_phase
184    }
185
186    /// Apply a Hadamard gate
187    ///
188    /// H: X → Z, Z → X, Y → -Y
189    /// Phase tracking: HYH = -Y, so Y component contributes i^2 = -1
190    pub fn apply_h(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
191        if qubit >= self.num_qubits {
192            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
193        }
194
195        // H: X ↔ Z, phase changes according to Y → -Y
196        for i in 0..self.num_qubits {
197            // For stabilizers
198            let x_val = self.x_matrix[[i, qubit]];
199            let z_val = self.z_matrix[[i, qubit]];
200
201            // Update phase: if both X and Z are present (Y), add phase of -1
202            // HYH = H(iXZ)H = iZX = i(-iY) = Y... wait, let's recalculate
203            // H transforms: X → Z, Y → -Y, Z → X
204            // Y = iXZ, so HYH = i(HXH)(HZH) = iZX = i(-iY) = Y? No...
205            // Actually: HYH = -Y, so Y component gets a -1 = i^2 phase
206            if x_val && z_val {
207                self.phase[i] = Self::negate_phase(self.phase[i]);
208            }
209
210            // Swap X and Z
211            self.x_matrix[[i, qubit]] = z_val;
212            self.z_matrix[[i, qubit]] = x_val;
213
214            // For destabilizers
215            let dx_val = self.destab_x[[i, qubit]];
216            let dz_val = self.destab_z[[i, qubit]];
217
218            if dx_val && dz_val {
219                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
220            }
221
222            self.destab_x[[i, qubit]] = dz_val;
223            self.destab_z[[i, qubit]] = dx_val;
224        }
225
226        Ok(())
227    }
228
229    /// Apply an S gate (phase gate)
230    ///
231    /// S conjugation rules (SPS†):
232    /// - S: X → Y (no phase change, Pauli relabeling)
233    /// - S: Y → -X (phase negation due to SYS† = -X)
234    /// - S: Z → Z (no change)
235    ///
236    /// Note: The `i` in Y = iXZ is a matrix identity, not relevant to stabilizer
237    /// conjugation. In stabilizer formalism, X, Y, Z are atomic Pauli labels.
238    pub fn apply_s(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
239        if qubit >= self.num_qubits {
240            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
241        }
242
243        // S conjugation: X → Y, Y → -X, Z → Z
244        for i in 0..self.num_qubits {
245            let x_val = self.x_matrix[[i, qubit]];
246            let z_val = self.z_matrix[[i, qubit]];
247
248            // For stabilizers
249            if x_val {
250                // X is present
251                if !z_val {
252                    // Pure X → Y: just add Z component (no phase change)
253                    self.z_matrix[[i, qubit]] = true;
254                } else {
255                    // Y → -X: remove Z, add phase -1
256                    self.z_matrix[[i, qubit]] = false;
257                    self.phase[i] = Self::negate_phase(self.phase[i]);
258                }
259            }
260            // Z → Z: no change needed
261
262            // For destabilizers
263            let dx_val = self.destab_x[[i, qubit]];
264            let dz_val = self.destab_z[[i, qubit]];
265
266            if dx_val {
267                if !dz_val {
268                    // X → Y
269                    self.destab_z[[i, qubit]] = true;
270                } else {
271                    // Y → -X
272                    self.destab_z[[i, qubit]] = false;
273                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
274                }
275            }
276        }
277
278        Ok(())
279    }
280
281    /// Apply a CNOT gate
282    pub fn apply_cnot(&mut self, control: usize, target: usize) -> Result<(), QuantRS2Error> {
283        if control >= self.num_qubits || target >= self.num_qubits {
284            return Err(QuantRS2Error::InvalidQubitId(control.max(target) as u32));
285        }
286
287        if control == target {
288            return Err(QuantRS2Error::InvalidInput(
289                "CNOT control and target must be different".to_string(),
290            ));
291        }
292
293        // CNOT: X_c → X_c X_t, Z_t → Z_c Z_t
294        for i in 0..self.num_qubits {
295            // For stabilizers
296            if self.x_matrix[[i, control]] {
297                self.x_matrix[[i, target]] ^= true;
298            }
299            if self.z_matrix[[i, target]] {
300                self.z_matrix[[i, control]] ^= true;
301            }
302
303            // For destabilizers
304            if self.destab_x[[i, control]] {
305                self.destab_x[[i, target]] ^= true;
306            }
307            if self.destab_z[[i, target]] {
308                self.destab_z[[i, control]] ^= true;
309            }
310        }
311
312        Ok(())
313    }
314
315    /// Apply a Pauli X gate
316    ///
317    /// X anticommutes with Z and Y, commutes with X
318    /// Phase: adds -1 when Z or Y is present on the qubit
319    pub fn apply_x(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
320        if qubit >= self.num_qubits {
321            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
322        }
323
324        // X anticommutes with Z (and Y since Y = iXZ)
325        for i in 0..self.num_qubits {
326            if self.z_matrix[[i, qubit]] {
327                self.phase[i] = Self::negate_phase(self.phase[i]);
328            }
329            if self.destab_z[[i, qubit]] {
330                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
331            }
332        }
333
334        Ok(())
335    }
336
337    /// Apply a Pauli Y gate
338    ///
339    /// Y = iXZ, anticommutes with X and Z (separately), commutes with Y
340    /// Phase: adds -1 when X XOR Z is present (pure X or pure Z, not Y)
341    pub fn apply_y(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
342        if qubit >= self.num_qubits {
343            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
344        }
345
346        // Y anticommutes with pure X and pure Z, commutes with Y and I
347        for i in 0..self.num_qubits {
348            let has_x = self.x_matrix[[i, qubit]];
349            let has_z = self.z_matrix[[i, qubit]];
350
351            // Anticommutes when exactly one of X or Z is present (XOR)
352            if has_x != has_z {
353                self.phase[i] = Self::negate_phase(self.phase[i]);
354            }
355
356            let has_dx = self.destab_x[[i, qubit]];
357            let has_dz = self.destab_z[[i, qubit]];
358
359            if has_dx != has_dz {
360                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
361            }
362        }
363
364        Ok(())
365    }
366
367    /// Apply a Pauli Z gate
368    ///
369    /// Z anticommutes with X and Y, commutes with Z
370    /// Phase: adds -1 when X or Y is present on the qubit
371    pub fn apply_z(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
372        if qubit >= self.num_qubits {
373            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
374        }
375
376        // Z anticommutes with X (and Y since Y = iXZ)
377        for i in 0..self.num_qubits {
378            if self.x_matrix[[i, qubit]] {
379                self.phase[i] = Self::negate_phase(self.phase[i]);
380            }
381            if self.destab_x[[i, qubit]] {
382                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
383            }
384        }
385
386        Ok(())
387    }
388
389    /// Apply S† (S-dagger) gate
390    ///
391    /// S† conjugation rules (S†PS):
392    /// - S†: X → -Y (phase becomes -1)
393    /// - S†: Y → X (no phase change)
394    /// - S†: Z → Z (no change)
395    pub fn apply_s_dag(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
396        if qubit >= self.num_qubits {
397            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
398        }
399
400        // S† conjugation: X → -Y, Y → X, Z → Z
401        for i in 0..self.num_qubits {
402            let x_val = self.x_matrix[[i, qubit]];
403            let z_val = self.z_matrix[[i, qubit]];
404
405            if x_val {
406                if !z_val {
407                    // Pure X → -Y: add Z component, negate phase
408                    self.z_matrix[[i, qubit]] = true;
409                    self.phase[i] = Self::negate_phase(self.phase[i]);
410                } else {
411                    // Y → X: remove Z component, no phase change
412                    self.z_matrix[[i, qubit]] = false;
413                }
414            }
415            // Z → Z: no change needed
416
417            // Destabilizers
418            let dx_val = self.destab_x[[i, qubit]];
419            let dz_val = self.destab_z[[i, qubit]];
420
421            if dx_val {
422                if !dz_val {
423                    // X → -Y
424                    self.destab_z[[i, qubit]] = true;
425                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
426                } else {
427                    // Y → X
428                    self.destab_z[[i, qubit]] = false;
429                }
430            }
431        }
432
433        Ok(())
434    }
435
436    /// Apply √X gate (SQRT_X, also called SX or V gate)
437    ///
438    /// Conjugation rules:
439    /// - √X: X → X (no change)
440    /// - √X: Y → -Z (phase becomes -1)
441    /// - √X: Z → Y (no phase change)
442    pub fn apply_sqrt_x(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
443        if qubit >= self.num_qubits {
444            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
445        }
446
447        // √X conjugation: X → X, Y → -Z, Z → Y
448        for i in 0..self.num_qubits {
449            let x_val = self.x_matrix[[i, qubit]];
450            let z_val = self.z_matrix[[i, qubit]];
451
452            match (x_val, z_val) {
453                (false, false) => {} // I → I
454                (true, false) => {}  // X → X
455                (false, true) => {
456                    // Z → Y: add X component, no phase change
457                    self.x_matrix[[i, qubit]] = true;
458                }
459                (true, true) => {
460                    // Y → -Z: remove X component, negate phase
461                    self.x_matrix[[i, qubit]] = false;
462                    self.phase[i] = Self::negate_phase(self.phase[i]);
463                }
464            }
465
466            // Destabilizers
467            let dx_val = self.destab_x[[i, qubit]];
468            let dz_val = self.destab_z[[i, qubit]];
469
470            match (dx_val, dz_val) {
471                (false, false) => {}
472                (true, false) => {}
473                (false, true) => {
474                    self.destab_x[[i, qubit]] = true;
475                }
476                (true, true) => {
477                    self.destab_x[[i, qubit]] = false;
478                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
479                }
480            }
481        }
482
483        Ok(())
484    }
485
486    /// Apply √X† gate (SQRT_X_DAG)
487    ///
488    /// Conjugation rules:
489    /// - √X†: X → X (no change)
490    /// - √X†: Y → Z (no phase change)
491    /// - √X†: Z → -Y (phase becomes -1)
492    pub fn apply_sqrt_x_dag(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
493        if qubit >= self.num_qubits {
494            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
495        }
496
497        // √X† conjugation: X → X, Y → Z, Z → -Y
498        for i in 0..self.num_qubits {
499            let x_val = self.x_matrix[[i, qubit]];
500            let z_val = self.z_matrix[[i, qubit]];
501
502            match (x_val, z_val) {
503                (false, false) => {}
504                (true, false) => {}
505                (false, true) => {
506                    // Z → -Y: add X component, negate phase
507                    self.x_matrix[[i, qubit]] = true;
508                    self.phase[i] = Self::negate_phase(self.phase[i]);
509                }
510                (true, true) => {
511                    // Y → Z: remove X component, no phase change
512                    self.x_matrix[[i, qubit]] = false;
513                }
514            }
515
516            // Destabilizers
517            let dx_val = self.destab_x[[i, qubit]];
518            let dz_val = self.destab_z[[i, qubit]];
519
520            match (dx_val, dz_val) {
521                (false, false) => {}
522                (true, false) => {}
523                (false, true) => {
524                    self.destab_x[[i, qubit]] = true;
525                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
526                }
527                (true, true) => {
528                    self.destab_x[[i, qubit]] = false;
529                }
530            }
531        }
532
533        Ok(())
534    }
535
536    /// Apply √Y gate (SQRT_Y)
537    ///
538    /// Conjugation rules:
539    /// - √Y: X → Z (no phase change)
540    /// - √Y: Y → Y (no change)
541    /// - √Y: Z → -X (phase becomes -1)
542    pub fn apply_sqrt_y(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
543        if qubit >= self.num_qubits {
544            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
545        }
546
547        // √Y: X → Z, Y → Y, Z → -X
548        for i in 0..self.num_qubits {
549            let x_val = self.x_matrix[[i, qubit]];
550            let z_val = self.z_matrix[[i, qubit]];
551
552            match (x_val, z_val) {
553                (false, false) => {}
554                (true, false) => {
555                    // X → Z: swap X to Z
556                    self.x_matrix[[i, qubit]] = false;
557                    self.z_matrix[[i, qubit]] = true;
558                }
559                (false, true) => {
560                    // Z → -X: swap Z to X, add -1 phase
561                    self.x_matrix[[i, qubit]] = true;
562                    self.z_matrix[[i, qubit]] = false;
563                    self.phase[i] = Self::negate_phase(self.phase[i]);
564                }
565                (true, true) => {} // Y → Y: no change
566            }
567
568            // Destabilizers
569            let dx_val = self.destab_x[[i, qubit]];
570            let dz_val = self.destab_z[[i, qubit]];
571
572            match (dx_val, dz_val) {
573                (false, false) => {}
574                (true, false) => {
575                    self.destab_x[[i, qubit]] = false;
576                    self.destab_z[[i, qubit]] = true;
577                }
578                (false, true) => {
579                    self.destab_x[[i, qubit]] = true;
580                    self.destab_z[[i, qubit]] = false;
581                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
582                }
583                (true, true) => {}
584            }
585        }
586
587        Ok(())
588    }
589
590    /// Apply √Y† gate (SQRT_Y_DAG)
591    ///
592    /// Conjugation rules:
593    /// - √Y†: X → -Z (phase becomes -1)
594    /// - √Y†: Y → Y (no change)
595    /// - √Y†: Z → X (no phase change)
596    pub fn apply_sqrt_y_dag(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
597        if qubit >= self.num_qubits {
598            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
599        }
600
601        // √Y†: X → -Z, Y → Y, Z → X
602        for i in 0..self.num_qubits {
603            let x_val = self.x_matrix[[i, qubit]];
604            let z_val = self.z_matrix[[i, qubit]];
605
606            match (x_val, z_val) {
607                (false, false) => {}
608                (true, false) => {
609                    // X → -Z: swap X to Z, add -1 phase
610                    self.x_matrix[[i, qubit]] = false;
611                    self.z_matrix[[i, qubit]] = true;
612                    self.phase[i] = Self::negate_phase(self.phase[i]);
613                }
614                (false, true) => {
615                    // Z → X: swap Z to X
616                    self.x_matrix[[i, qubit]] = true;
617                    self.z_matrix[[i, qubit]] = false;
618                }
619                (true, true) => {} // Y → Y: no change
620            }
621
622            // Destabilizers
623            let dx_val = self.destab_x[[i, qubit]];
624            let dz_val = self.destab_z[[i, qubit]];
625
626            match (dx_val, dz_val) {
627                (false, false) => {}
628                (true, false) => {
629                    self.destab_x[[i, qubit]] = false;
630                    self.destab_z[[i, qubit]] = true;
631                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
632                }
633                (false, true) => {
634                    self.destab_x[[i, qubit]] = true;
635                    self.destab_z[[i, qubit]] = false;
636                }
637                (true, true) => {}
638            }
639        }
640
641        Ok(())
642    }
643
644    /// Apply CZ (Controlled-Z) gate
645    ///
646    /// CZ: X_c → X_c Z_t, X_t → Z_c X_t, Z_c → Z_c, Z_t → Z_t
647    /// When both qubits have X component (product of X or Y), phase picks up -1
648    pub fn apply_cz(&mut self, control: usize, target: usize) -> Result<(), QuantRS2Error> {
649        if control >= self.num_qubits {
650            return Err(QuantRS2Error::InvalidQubitId(control as u32));
651        }
652        if target >= self.num_qubits {
653            return Err(QuantRS2Error::InvalidQubitId(target as u32));
654        }
655
656        // CZ: Phase flips when both qubits have X component
657        for i in 0..self.num_qubits {
658            if self.x_matrix[[i, control]] && self.x_matrix[[i, target]] {
659                self.phase[i] = Self::negate_phase(self.phase[i]);
660            }
661            if self.x_matrix[[i, control]] {
662                self.z_matrix[[i, target]] = !self.z_matrix[[i, target]];
663            }
664            if self.x_matrix[[i, target]] {
665                self.z_matrix[[i, control]] = !self.z_matrix[[i, control]];
666            }
667
668            // Destabilizers
669            if self.destab_x[[i, control]] && self.destab_x[[i, target]] {
670                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
671            }
672            if self.destab_x[[i, control]] {
673                self.destab_z[[i, target]] = !self.destab_z[[i, target]];
674            }
675            if self.destab_x[[i, target]] {
676                self.destab_z[[i, control]] = !self.destab_z[[i, control]];
677            }
678        }
679
680        Ok(())
681    }
682
683    /// Apply CY (Controlled-Y) gate
684    pub fn apply_cy(&mut self, control: usize, target: usize) -> Result<(), QuantRS2Error> {
685        if control >= self.num_qubits {
686            return Err(QuantRS2Error::InvalidQubitId(control as u32));
687        }
688        if target >= self.num_qubits {
689            return Err(QuantRS2Error::InvalidQubitId(target as u32));
690        }
691
692        // CY = S_dag(target) · CNOT(control, target) · S(target)
693        self.apply_s_dag(target)?;
694        self.apply_cnot(control, target)?;
695        self.apply_s(target)?;
696
697        Ok(())
698    }
699
700    /// Apply SWAP gate
701    pub fn apply_swap(&mut self, qubit1: usize, qubit2: usize) -> Result<(), QuantRS2Error> {
702        if qubit1 >= self.num_qubits {
703            return Err(QuantRS2Error::InvalidQubitId(qubit1 as u32));
704        }
705        if qubit2 >= self.num_qubits {
706            return Err(QuantRS2Error::InvalidQubitId(qubit2 as u32));
707        }
708
709        // SWAP = CNOT(a,b) · CNOT(b,a) · CNOT(a,b)
710        self.apply_cnot(qubit1, qubit2)?;
711        self.apply_cnot(qubit2, qubit1)?;
712        self.apply_cnot(qubit1, qubit2)?;
713
714        Ok(())
715    }
716
717    /// Measure a qubit in the computational (Z) basis
718    /// Returns the measurement outcome (0 or 1)
719    ///
720    /// For phases with imaginary components, we project onto real eigenvalues:
721    /// - Phase 0 (+1) or 1 (+i) → eigenvalue +1 → outcome 0
722    /// - Phase 2 (-1) or 3 (-i) → eigenvalue -1 → outcome 1
723    pub fn measure(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
724        if qubit >= self.num_qubits {
725            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
726        }
727
728        // Find a stabilizer that anticommutes with Z_qubit
729        let mut anticommuting_row = None;
730
731        for i in 0..self.num_qubits {
732            if self.x_matrix[[i, qubit]] {
733                anticommuting_row = Some(i);
734                break;
735            }
736        }
737
738        if let Some(p) = anticommuting_row {
739            // Random outcome case
740            // Set the p-th stabilizer to Z_qubit (or -Z_qubit based on random outcome)
741            for j in 0..self.num_qubits {
742                self.x_matrix[[p, j]] = false;
743                self.z_matrix[[p, j]] = j == qubit;
744            }
745
746            // Random measurement outcome
747            let mut rng = thread_rng();
748            let outcome = rng.gen_bool(0.5);
749            // Set phase: 0 (+1) for outcome 0, 2 (-1) for outcome 1
750            self.phase[p] = if outcome {
751                phase::MINUS_ONE
752            } else {
753                phase::PLUS_ONE
754            };
755
756            // Update other stabilizers that anticommute
757            for i in 0..self.num_qubits {
758                if i != p && self.x_matrix[[i, qubit]] {
759                    // Multiply by stabilizer p
760                    // Need to properly compute phase when multiplying Pauli strings
761                    let mut total_phase_contrib: StabilizerPhase = 0;
762                    for j in 0..self.num_qubits {
763                        let phase_contrib = Self::rowsum_phase(
764                            self.x_matrix[[i, j]],
765                            self.z_matrix[[i, j]],
766                            self.x_matrix[[p, j]],
767                            self.z_matrix[[p, j]],
768                        );
769                        total_phase_contrib = Self::add_phases(total_phase_contrib, phase_contrib);
770                        self.x_matrix[[i, j]] ^= self.x_matrix[[p, j]];
771                        self.z_matrix[[i, j]] ^= self.z_matrix[[p, j]];
772                    }
773                    // Update phase: add both phases and the phase contribution from multiplication
774                    self.phase[i] = Self::add_phases(
775                        Self::add_phases(self.phase[i], self.phase[p]),
776                        total_phase_contrib,
777                    );
778                }
779            }
780
781            Ok(outcome)
782        } else {
783            // Deterministic outcome
784            // We need to express Z_qubit as a product of stabilizers and compute
785            // the accumulated phase. This handles correlated states (like Bell states).
786            //
787            // Algorithm:
788            // 1. Find a stabilizer that has Z on target qubit (and no X)
789            // 2. Eliminate Z contributions from other qubits by multiplying
790            //    with other stabilizers
791            // 3. The accumulated phase determines the outcome
792
793            // First, find a stabilizer with Z on target (no X)
794            let mut pivot_row = None;
795            for i in 0..self.num_qubits {
796                if self.z_matrix[[i, qubit]] && !self.x_matrix[[i, qubit]] {
797                    pivot_row = Some(i);
798                    break;
799                }
800            }
801
802            let Some(pivot) = pivot_row else {
803                // No stabilizer has Z on this qubit - outcome is 0
804                return Ok(false);
805            };
806
807            // Make a copy of the pivot row to work with
808            let mut result_x = vec![false; self.num_qubits];
809            let mut result_z = vec![false; self.num_qubits];
810            let mut result_phase = self.phase[pivot];
811
812            for j in 0..self.num_qubits {
813                result_x[j] = self.x_matrix[[pivot, j]];
814                result_z[j] = self.z_matrix[[pivot, j]];
815            }
816
817            // Eliminate Z on other qubits by multiplying with appropriate stabilizers
818            for other_qubit in 0..self.num_qubits {
819                if other_qubit == qubit {
820                    continue;
821                }
822
823                // If result has Z on other_qubit, find a stabilizer to cancel it
824                if result_z[other_qubit] && !result_x[other_qubit] {
825                    // Find another stabilizer with Z on other_qubit (and no X)
826                    for i in 0..self.num_qubits {
827                        if i == pivot {
828                            continue;
829                        }
830                        if self.z_matrix[[i, other_qubit]] && !self.x_matrix[[i, other_qubit]] {
831                            // Multiply result by this stabilizer
832                            let phase_contrib =
833                                self.compute_multiplication_phase(&result_x, &result_z, i);
834                            result_phase = Self::add_phases(result_phase, self.phase[i]);
835                            result_phase = Self::add_phases(result_phase, phase_contrib);
836
837                            for j in 0..self.num_qubits {
838                                result_x[j] ^= self.x_matrix[[i, j]];
839                                result_z[j] ^= self.z_matrix[[i, j]];
840                            }
841                            break;
842                        }
843                    }
844                }
845            }
846
847            // The accumulated phase determines the outcome
848            // Phase 0 (+1) or 1 (+i) → eigenvalue +1 → outcome 0 (false)
849            // Phase 2 (-1) or 3 (-i) → eigenvalue -1 → outcome 1 (true)
850            let outcome = result_phase >= phase::MINUS_ONE;
851
852            Ok(outcome)
853        }
854    }
855
856    /// Measure a qubit in the X basis (Stim MX instruction)
857    ///
858    /// Equivalent to: H · measure_z · H
859    pub fn measure_x(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
860        // Transform to Z basis
861        self.apply_h(qubit)?;
862
863        // Measure in Z basis
864        let outcome = self.measure(qubit)?;
865
866        // Transform back to X basis
867        self.apply_h(qubit)?;
868
869        Ok(outcome)
870    }
871
872    /// Measure a qubit in the Y basis (Stim MY instruction)
873    ///
874    /// Equivalent to: S† · H · measure_z · H · S
875    pub fn measure_y(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
876        // Transform to Z basis: Y → Z requires S† · H
877        self.apply_s_dag(qubit)?;
878        self.apply_h(qubit)?;
879
880        // Measure in Z basis
881        let outcome = self.measure(qubit)?;
882
883        // Transform back to Y basis: H · S
884        self.apply_h(qubit)?;
885        self.apply_s(qubit)?;
886
887        Ok(outcome)
888    }
889
890    /// Reset a qubit to |0⟩ state (Stim R instruction)
891    ///
892    /// Performs measurement and applies X if outcome is |1⟩
893    pub fn reset(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
894        // Measure in Z basis
895        let outcome = self.measure(qubit)?;
896
897        // If measured |1⟩, flip to |0⟩
898        if outcome {
899            self.apply_x(qubit)?;
900        }
901
902        Ok(())
903    }
904
905    /// Get the current stabilizer generators as strings
906    ///
907    /// Phase encoding in output:
908    /// - `+` for phase 0 (+1)
909    /// - `+i` for phase 1 (+i)
910    /// - `-` for phase 2 (-1)
911    /// - `-i` for phase 3 (-i)
912    ///
913    /// Identity representation depends on `stim_format`:
914    /// - Standard format: `I` for identity
915    /// - Stim format: `_` for identity
916    #[must_use]
917    pub fn get_stabilizers(&self) -> Vec<String> {
918        let mut stabilizers = Vec::new();
919        let identity_char = if self.stim_format { '_' } else { 'I' };
920
921        for i in 0..self.num_qubits {
922            let mut stab = String::new();
923
924            // Phase prefix (Stim-compatible encoding)
925            match self.phase[i] & 3 {
926                phase::PLUS_ONE => stab.push('+'),
927                phase::PLUS_I => stab.push_str("+i"),
928                phase::MINUS_ONE => stab.push('-'),
929                phase::MINUS_I => stab.push_str("-i"),
930                _ => unreachable!(), // phase is always 0-3 due to & 3
931            }
932
933            // Pauli string
934            for j in 0..self.num_qubits {
935                let has_x = self.x_matrix[[i, j]];
936                let has_z = self.z_matrix[[i, j]];
937
938                match (has_x, has_z) {
939                    (false, false) => stab.push(identity_char),
940                    (true, false) => stab.push('X'),
941                    (false, true) => stab.push('Z'),
942                    (true, true) => stab.push('Y'),
943                }
944            }
945
946            stabilizers.push(stab);
947        }
948
949        stabilizers
950    }
951
952    /// Get the current destabilizer generators as strings
953    ///
954    /// Same format as `get_stabilizers()` but for destabilizers
955    #[must_use]
956    pub fn get_destabilizers(&self) -> Vec<String> {
957        let mut destabilizers = Vec::new();
958        let identity_char = if self.stim_format { '_' } else { 'I' };
959
960        for i in 0..self.num_qubits {
961            let mut destab = String::new();
962
963            // Phase prefix (Stim-compatible encoding)
964            match self.destab_phase[i] & 3 {
965                phase::PLUS_ONE => destab.push('+'),
966                phase::PLUS_I => destab.push_str("+i"),
967                phase::MINUS_ONE => destab.push('-'),
968                phase::MINUS_I => destab.push_str("-i"),
969                _ => unreachable!(), // phase is always 0-3 due to & 3
970            }
971
972            // Pauli string
973            for j in 0..self.num_qubits {
974                let has_x = self.destab_x[[i, j]];
975                let has_z = self.destab_z[[i, j]];
976
977                match (has_x, has_z) {
978                    (false, false) => destab.push(identity_char),
979                    (true, false) => destab.push('X'),
980                    (false, true) => destab.push('Z'),
981                    (true, true) => destab.push('Y'),
982                }
983            }
984
985            destabilizers.push(destab);
986        }
987
988        destabilizers
989    }
990}
991
992/// Stabilizer simulator that efficiently simulates Clifford circuits
993#[derive(Debug, Clone)]
994pub struct StabilizerSimulator {
995    /// The stabilizer tableau
996    pub tableau: StabilizerTableau,
997    measurement_record: Vec<(usize, bool)>,
998}
999
1000impl StabilizerSimulator {
1001    /// Create a new stabilizer simulator
1002    #[must_use]
1003    pub fn new(num_qubits: usize) -> Self {
1004        Self {
1005            tableau: StabilizerTableau::new(num_qubits),
1006            measurement_record: Vec::new(),
1007        }
1008    }
1009
1010    /// Apply a gate to the simulator
1011    pub fn apply_gate(&mut self, gate: StabilizerGate) -> Result<(), QuantRS2Error> {
1012        match gate {
1013            StabilizerGate::H(q) => self.tableau.apply_h(q),
1014            StabilizerGate::S(q) => self.tableau.apply_s(q),
1015            StabilizerGate::SDag(q) => self.tableau.apply_s_dag(q),
1016            StabilizerGate::SqrtX(q) => self.tableau.apply_sqrt_x(q),
1017            StabilizerGate::SqrtXDag(q) => self.tableau.apply_sqrt_x_dag(q),
1018            StabilizerGate::SqrtY(q) => self.tableau.apply_sqrt_y(q),
1019            StabilizerGate::SqrtYDag(q) => self.tableau.apply_sqrt_y_dag(q),
1020            StabilizerGate::X(q) => self.tableau.apply_x(q),
1021            StabilizerGate::Y(q) => self.tableau.apply_y(q),
1022            StabilizerGate::Z(q) => self.tableau.apply_z(q),
1023            StabilizerGate::CNOT(c, t) => self.tableau.apply_cnot(c, t),
1024            StabilizerGate::CZ(c, t) => self.tableau.apply_cz(c, t),
1025            StabilizerGate::CY(c, t) => self.tableau.apply_cy(c, t),
1026            StabilizerGate::SWAP(q1, q2) => self.tableau.apply_swap(q1, q2),
1027        }
1028    }
1029
1030    /// Measure a qubit
1031    pub fn measure(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
1032        let outcome = self.tableau.measure(qubit)?;
1033        self.measurement_record.push((qubit, outcome));
1034        Ok(outcome)
1035    }
1036
1037    /// Get the current stabilizers
1038    #[must_use]
1039    pub fn get_stabilizers(&self) -> Vec<String> {
1040        self.tableau.get_stabilizers()
1041    }
1042
1043    /// Get measurement record
1044    #[must_use]
1045    pub fn get_measurements(&self) -> &[(usize, bool)] {
1046        &self.measurement_record
1047    }
1048
1049    /// Reset the simulator
1050    pub fn reset(&mut self) {
1051        let num_qubits = self.tableau.num_qubits;
1052        self.tableau = StabilizerTableau::new(num_qubits);
1053        self.measurement_record.clear();
1054    }
1055
1056    /// Get the number of qubits
1057    #[must_use]
1058    pub const fn num_qubits(&self) -> usize {
1059        self.tableau.num_qubits
1060    }
1061
1062    /// Get the state vector (for compatibility with other simulators)
1063    /// Note: This is expensive for stabilizer states and returns a sparse representation
1064    #[must_use]
1065    pub fn get_statevector(&self) -> Vec<Complex64> {
1066        let n = self.tableau.num_qubits;
1067        let dim = 1 << n;
1068        let mut state = vec![Complex64::new(0.0, 0.0); dim];
1069
1070        // For a stabilizer state, we can determine which computational basis states
1071        // have non-zero amplitude by finding the simultaneous +1 eigenstates of all stabilizers
1072        // This is a simplified implementation that assumes the state is in |0...0>
1073        // A full implementation would need to solve the stabilizer equations
1074
1075        // For now, return a simple state
1076        state[0] = Complex64::new(1.0, 0.0);
1077        state
1078    }
1079}
1080
1081/// Gates supported by the stabilizer simulator
1082#[derive(Debug, Clone, Copy)]
1083pub enum StabilizerGate {
1084    H(usize),
1085    S(usize),
1086    SDag(usize),
1087    SqrtX(usize),
1088    SqrtXDag(usize),
1089    SqrtY(usize),
1090    SqrtYDag(usize),
1091    X(usize),
1092    Y(usize),
1093    Z(usize),
1094    CNOT(usize, usize),
1095    CZ(usize, usize),
1096    CY(usize, usize),
1097    SWAP(usize, usize),
1098}
1099
1100/// Check if a circuit can be simulated by the stabilizer simulator
1101#[must_use]
1102pub fn is_clifford_circuit<const N: usize>(circuit: &Circuit<N>) -> bool {
1103    // Check if all gates in the circuit are Clifford gates
1104    // Clifford gates: H, S, S†, CNOT, X, Y, Z, CZ
1105    circuit.gates().iter().all(|gate| {
1106        matches!(
1107            gate.name(),
1108            "H" | "S" | "S†" | "CNOT" | "X" | "Y" | "Z" | "CZ" | "Phase" | "PhaseDagger"
1109        )
1110    })
1111}
1112
1113/// Convert a gate operation to a stabilizer gate
1114fn gate_to_stabilizer(gate: &Arc<dyn GateOp + Send + Sync>) -> Option<StabilizerGate> {
1115    let gate_name = gate.name();
1116    let qubits = gate.qubits();
1117
1118    match gate_name {
1119        "H" => {
1120            if qubits.len() == 1 {
1121                Some(StabilizerGate::H(qubits[0].0 as usize))
1122            } else {
1123                None
1124            }
1125        }
1126        "S" | "Phase" => {
1127            if qubits.len() == 1 {
1128                Some(StabilizerGate::S(qubits[0].0 as usize))
1129            } else {
1130                None
1131            }
1132        }
1133        "X" => {
1134            if qubits.len() == 1 {
1135                Some(StabilizerGate::X(qubits[0].0 as usize))
1136            } else {
1137                None
1138            }
1139        }
1140        "Y" => {
1141            if qubits.len() == 1 {
1142                Some(StabilizerGate::Y(qubits[0].0 as usize))
1143            } else {
1144                None
1145            }
1146        }
1147        "Z" => {
1148            if qubits.len() == 1 {
1149                Some(StabilizerGate::Z(qubits[0].0 as usize))
1150            } else {
1151                None
1152            }
1153        }
1154        "CNOT" => {
1155            if qubits.len() == 2 {
1156                Some(StabilizerGate::CNOT(
1157                    qubits[0].0 as usize,
1158                    qubits[1].0 as usize,
1159                ))
1160            } else {
1161                None
1162            }
1163        }
1164        "CZ" => {
1165            if qubits.len() == 2 {
1166                // CZ = H(target) CNOT H(target)
1167                // For now, we can decompose this or add native support
1168                // Let's return None for unsupported gates
1169                None
1170            } else {
1171                None
1172            }
1173        }
1174        _ => None,
1175    }
1176}
1177
1178/// Implement the Simulator trait for `StabilizerSimulator`
1179impl Simulator for StabilizerSimulator {
1180    fn run<const N: usize>(
1181        &mut self,
1182        circuit: &Circuit<N>,
1183    ) -> crate::error::Result<SimulatorResult<N>> {
1184        // Create a new simulator instance
1185        let mut sim = Self::new(N);
1186
1187        // Apply all gates in the circuit
1188        for gate in circuit.gates() {
1189            if let Some(stab_gate) = gate_to_stabilizer(gate) {
1190                // Ignore errors for now - in production we'd handle them properly
1191                let _ = sim.apply_gate(stab_gate);
1192            }
1193        }
1194
1195        // Get the state vector (expensive operation)
1196        let amplitudes = sim.get_statevector();
1197
1198        Ok(SimulatorResult::new(amplitudes))
1199    }
1200}
1201
1202/// Builder for creating circuits that can be simulated with the stabilizer formalism
1203pub struct CliffordCircuitBuilder {
1204    gates: Vec<StabilizerGate>,
1205    num_qubits: usize,
1206}
1207
1208impl CliffordCircuitBuilder {
1209    /// Create a new Clifford circuit builder
1210    #[must_use]
1211    pub const fn new(num_qubits: usize) -> Self {
1212        Self {
1213            gates: Vec::new(),
1214            num_qubits,
1215        }
1216    }
1217
1218    /// Add a Hadamard gate
1219    #[must_use]
1220    pub fn h(mut self, qubit: usize) -> Self {
1221        self.gates.push(StabilizerGate::H(qubit));
1222        self
1223    }
1224
1225    /// Add an S gate
1226    #[must_use]
1227    pub fn s(mut self, qubit: usize) -> Self {
1228        self.gates.push(StabilizerGate::S(qubit));
1229        self
1230    }
1231
1232    /// Add an S† (S-dagger) gate
1233    #[must_use]
1234    pub fn s_dag(mut self, qubit: usize) -> Self {
1235        self.gates.push(StabilizerGate::SDag(qubit));
1236        self
1237    }
1238
1239    /// Add a √X gate
1240    #[must_use]
1241    pub fn sqrt_x(mut self, qubit: usize) -> Self {
1242        self.gates.push(StabilizerGate::SqrtX(qubit));
1243        self
1244    }
1245
1246    /// Add a √X† gate
1247    #[must_use]
1248    pub fn sqrt_x_dag(mut self, qubit: usize) -> Self {
1249        self.gates.push(StabilizerGate::SqrtXDag(qubit));
1250        self
1251    }
1252
1253    /// Add a √Y gate
1254    #[must_use]
1255    pub fn sqrt_y(mut self, qubit: usize) -> Self {
1256        self.gates.push(StabilizerGate::SqrtY(qubit));
1257        self
1258    }
1259
1260    /// Add a √Y† gate
1261    #[must_use]
1262    pub fn sqrt_y_dag(mut self, qubit: usize) -> Self {
1263        self.gates.push(StabilizerGate::SqrtYDag(qubit));
1264        self
1265    }
1266
1267    /// Add a Pauli-X gate
1268    #[must_use]
1269    pub fn x(mut self, qubit: usize) -> Self {
1270        self.gates.push(StabilizerGate::X(qubit));
1271        self
1272    }
1273
1274    /// Add a Pauli-Y gate
1275    #[must_use]
1276    pub fn y(mut self, qubit: usize) -> Self {
1277        self.gates.push(StabilizerGate::Y(qubit));
1278        self
1279    }
1280
1281    /// Add a Pauli-Z gate
1282    #[must_use]
1283    pub fn z(mut self, qubit: usize) -> Self {
1284        self.gates.push(StabilizerGate::Z(qubit));
1285        self
1286    }
1287
1288    /// Add a CNOT gate
1289    #[must_use]
1290    pub fn cnot(mut self, control: usize, target: usize) -> Self {
1291        self.gates.push(StabilizerGate::CNOT(control, target));
1292        self
1293    }
1294
1295    /// Add a CZ (Controlled-Z) gate
1296    #[must_use]
1297    pub fn cz(mut self, control: usize, target: usize) -> Self {
1298        self.gates.push(StabilizerGate::CZ(control, target));
1299        self
1300    }
1301
1302    /// Add a CY (Controlled-Y) gate
1303    #[must_use]
1304    pub fn cy(mut self, control: usize, target: usize) -> Self {
1305        self.gates.push(StabilizerGate::CY(control, target));
1306        self
1307    }
1308
1309    /// Add a SWAP gate
1310    #[must_use]
1311    pub fn swap(mut self, qubit1: usize, qubit2: usize) -> Self {
1312        self.gates.push(StabilizerGate::SWAP(qubit1, qubit2));
1313        self
1314    }
1315
1316    /// Build and run the circuit
1317    pub fn run(self) -> Result<StabilizerSimulator, QuantRS2Error> {
1318        let mut sim = StabilizerSimulator::new(self.num_qubits);
1319
1320        for gate in self.gates {
1321            sim.apply_gate(gate)?;
1322        }
1323
1324        Ok(sim)
1325    }
1326}
1327
1328#[cfg(test)]
1329mod tests {
1330    use super::*;
1331
1332    #[test]
1333    fn test_stabilizer_init() {
1334        let sim = StabilizerSimulator::new(3);
1335        let stabs = sim.get_stabilizers();
1336
1337        assert_eq!(stabs.len(), 3);
1338        assert_eq!(stabs[0], "+ZII");
1339        assert_eq!(stabs[1], "+IZI");
1340        assert_eq!(stabs[2], "+IIZ");
1341    }
1342
1343    #[test]
1344    fn test_hadamard_gate() {
1345        let mut sim = StabilizerSimulator::new(1);
1346        sim.apply_gate(StabilizerGate::H(0))
1347            .expect("Hadamard gate application should succeed");
1348
1349        let stabs = sim.get_stabilizers();
1350        assert_eq!(stabs[0], "+X");
1351    }
1352
1353    #[test]
1354    fn test_bell_state() {
1355        let mut sim = StabilizerSimulator::new(2);
1356        sim.apply_gate(StabilizerGate::H(0))
1357            .expect("Hadamard gate application should succeed");
1358        sim.apply_gate(StabilizerGate::CNOT(0, 1))
1359            .expect("CNOT gate application should succeed");
1360
1361        let stabs = sim.get_stabilizers();
1362        assert!(stabs.contains(&"+XX".to_string()));
1363        assert!(stabs.contains(&"+ZZ".to_string()));
1364    }
1365
1366    #[test]
1367    fn test_ghz_state() {
1368        let mut sim = StabilizerSimulator::new(3);
1369        sim.apply_gate(StabilizerGate::H(0))
1370            .expect("Hadamard gate application should succeed");
1371        sim.apply_gate(StabilizerGate::CNOT(0, 1))
1372            .expect("CNOT gate application should succeed");
1373        sim.apply_gate(StabilizerGate::CNOT(1, 2))
1374            .expect("CNOT gate application should succeed");
1375
1376        let stabs = sim.get_stabilizers();
1377        assert!(stabs.contains(&"+XXX".to_string()));
1378        assert!(stabs.contains(&"+ZZI".to_string()));
1379        assert!(stabs.contains(&"+IZZ".to_string()));
1380    }
1381
1382    #[test]
1383    fn test_s_dag_gate() {
1384        // S† · S = I (identity)
1385        let mut sim = StabilizerSimulator::new(1);
1386        sim.apply_gate(StabilizerGate::S(0))
1387            .expect("S gate application should succeed");
1388        sim.apply_gate(StabilizerGate::SDag(0))
1389            .expect("S† gate application should succeed");
1390
1391        let stabs = sim.get_stabilizers();
1392        assert_eq!(stabs[0], "+Z"); // Should return to original Z state
1393    }
1394
1395    #[test]
1396    fn test_sqrt_x_gate() {
1397        // √X · √X = X (up to global phase)
1398        let mut sim1 = StabilizerSimulator::new(1);
1399        sim1.apply_gate(StabilizerGate::SqrtX(0))
1400            .expect("√X gate application should succeed");
1401        sim1.apply_gate(StabilizerGate::SqrtX(0))
1402            .expect("√X gate application should succeed");
1403
1404        let stabs1 = sim1.get_stabilizers();
1405
1406        // Compare with direct X gate (may have phase difference)
1407        let mut sim2 = StabilizerSimulator::new(1);
1408        sim2.apply_gate(StabilizerGate::X(0))
1409            .expect("X gate application should succeed");
1410
1411        let stabs2 = sim2.get_stabilizers();
1412
1413        // Check that both have Z stabilizer (possibly with different signs)
1414        assert!(stabs1[0] == "+Z" || stabs1[0] == "-Z");
1415        assert!(stabs2[0] == "+Z" || stabs2[0] == "-Z");
1416    }
1417
1418    #[test]
1419    fn test_sqrt_y_gate() {
1420        // √Y · √Y = Y
1421        let mut sim1 = StabilizerSimulator::new(1);
1422        sim1.apply_gate(StabilizerGate::SqrtY(0))
1423            .expect("√Y gate application should succeed");
1424        sim1.apply_gate(StabilizerGate::SqrtY(0))
1425            .expect("√Y gate application should succeed");
1426
1427        let stabs1 = sim1.get_stabilizers();
1428
1429        // Compare with direct Y gate
1430        let mut sim2 = StabilizerSimulator::new(1);
1431        sim2.apply_gate(StabilizerGate::Y(0))
1432            .expect("Y gate application should succeed");
1433
1434        let stabs2 = sim2.get_stabilizers();
1435        assert_eq!(stabs1[0], stabs2[0]);
1436    }
1437
1438    #[test]
1439    fn test_cz_gate() {
1440        // Test CZ gate creates entanglement
1441        let mut sim = StabilizerSimulator::new(2);
1442        sim.apply_gate(StabilizerGate::H(0))
1443            .expect("Hadamard gate application should succeed");
1444        sim.apply_gate(StabilizerGate::H(1))
1445            .expect("Hadamard gate application should succeed");
1446        sim.apply_gate(StabilizerGate::CZ(0, 1))
1447            .expect("CZ gate application should succeed");
1448
1449        let stabs = sim.get_stabilizers();
1450        // CZ on |++⟩ creates entangled state
1451        // Should have correlation in Z basis
1452        assert!(stabs.len() == 2);
1453    }
1454
1455    #[test]
1456    fn test_cy_gate() {
1457        // Test CY gate
1458        let mut sim = StabilizerSimulator::new(2);
1459        sim.apply_gate(StabilizerGate::H(0))
1460            .expect("Hadamard gate application should succeed");
1461        sim.apply_gate(StabilizerGate::CY(0, 1))
1462            .expect("CY gate application should succeed");
1463
1464        let stabs = sim.get_stabilizers();
1465        assert!(stabs.len() == 2);
1466    }
1467
1468    #[test]
1469    fn test_swap_gate() {
1470        // Test SWAP gate
1471        let mut sim = StabilizerSimulator::new(2);
1472        // Prepare |01⟩
1473        sim.apply_gate(StabilizerGate::X(1))
1474            .expect("X gate application should succeed");
1475
1476        // Apply SWAP
1477        sim.apply_gate(StabilizerGate::SWAP(0, 1))
1478            .expect("SWAP gate application should succeed");
1479
1480        let stabs = sim.get_stabilizers();
1481        // After SWAP, should be equivalent to |10⟩
1482        assert!(stabs.len() == 2);
1483    }
1484
1485    #[test]
1486    fn test_builder_pattern_new_gates() {
1487        // Test using builder pattern with new gates
1488        let sim = CliffordCircuitBuilder::new(2)
1489            .h(0)
1490            .s_dag(0)
1491            .sqrt_x(1)
1492            .cz(0, 1)
1493            .run()
1494            .expect("Circuit execution should succeed");
1495
1496        let stabs = sim.get_stabilizers();
1497        assert!(stabs.len() == 2);
1498    }
1499
1500    #[test]
1501    fn test_large_clifford_circuit() {
1502        // Test a larger Clifford circuit (100 qubits)
1503        let mut sim = StabilizerSimulator::new(100);
1504
1505        // Apply Hadamard to all qubits
1506        for i in 0..100 {
1507            sim.apply_gate(StabilizerGate::H(i))
1508                .expect("Hadamard gate application should succeed");
1509        }
1510
1511        // Apply CNOT chain
1512        for i in 0..99 {
1513            sim.apply_gate(StabilizerGate::CNOT(i, i + 1))
1514                .expect("CNOT gate application should succeed");
1515        }
1516
1517        let stabs = sim.get_stabilizers();
1518        assert_eq!(stabs.len(), 100);
1519    }
1520
1521    #[test]
1522    fn test_measurement_randomness() {
1523        // Test that measurement produces random outcomes for superposition
1524        let mut sim = StabilizerSimulator::new(1);
1525        sim.apply_gate(StabilizerGate::H(0))
1526            .expect("Hadamard gate application should succeed");
1527
1528        // Measure multiple times (need new states each time)
1529        let mut outcomes = Vec::new();
1530        for _ in 0..10 {
1531            let mut test_sim = StabilizerSimulator::new(1);
1532            test_sim
1533                .apply_gate(StabilizerGate::H(0))
1534                .expect("Hadamard gate application should succeed");
1535            let outcome = test_sim.measure(0).expect("Measurement should succeed");
1536            outcomes.push(outcome);
1537        }
1538
1539        // Should have at least some variety (not all same)
1540        let first = outcomes[0];
1541        let all_same = outcomes.iter().all(|&x| x == first);
1542        assert!(
1543            !all_same || outcomes.len() < 5,
1544            "Measurements should show randomness"
1545        );
1546    }
1547
1548    #[test]
1549    fn test_measure_x_basis() {
1550        // Prepare |+⟩ state (eigenstate of X with eigenvalue +1)
1551        let mut sim = StabilizerSimulator::new(1);
1552        sim.apply_gate(StabilizerGate::H(0))
1553            .expect("Hadamard gate application should succeed");
1554
1555        // Measure in X basis should always give 0 (deterministic)
1556        let outcome = sim
1557            .tableau
1558            .measure_x(0)
1559            .expect("X-basis measurement should succeed");
1560
1561        // For |+⟩ state, X measurement should be deterministic (eigenvalue +1 → outcome 0)
1562        assert!(!outcome);
1563    }
1564
1565    #[test]
1566    fn test_measure_y_basis() {
1567        // Prepare |+Y⟩ = S·H|0⟩ = (|0⟩ + i|1⟩)/√2, eigenstate of Y with eigenvalue +1
1568        // Note: Apply H first, then S (right-to-left gate application)
1569        let mut sim = StabilizerSimulator::new(1);
1570        sim.apply_gate(StabilizerGate::H(0)).unwrap();
1571        sim.apply_gate(StabilizerGate::S(0)).unwrap();
1572
1573        // State now has stabilizer +Y
1574        let stabs = sim.get_stabilizers();
1575        assert_eq!(stabs[0], "+Y");
1576
1577        // Measure in Y basis should give outcome 0 (eigenvalue +1)
1578        let outcome = sim
1579            .tableau
1580            .measure_y(0)
1581            .expect("Y-basis measurement should succeed");
1582
1583        // Should be deterministic for Y eigenstate
1584        assert!(!outcome);
1585    }
1586
1587    #[test]
1588    fn test_reset_operation() {
1589        // Prepare |1⟩ state
1590        let mut sim = StabilizerSimulator::new(1);
1591        sim.apply_gate(StabilizerGate::X(0))
1592            .expect("X gate application should succeed");
1593
1594        // Reset to |0⟩
1595        sim.tableau.reset(0).expect("Reset should succeed");
1596
1597        // Measure should give 0
1598        let outcome = sim.measure(0).expect("Measurement should succeed");
1599        assert!(!outcome);
1600
1601        // Stabilizer should be +Z
1602        let stabs = sim.get_stabilizers();
1603        assert_eq!(stabs[0], "+Z");
1604    }
1605
1606    #[test]
1607    fn test_reset_from_superposition() {
1608        // Prepare |+⟩ state
1609        let mut sim = StabilizerSimulator::new(1);
1610        sim.apply_gate(StabilizerGate::H(0))
1611            .expect("Hadamard gate application should succeed");
1612
1613        // Reset to |0⟩
1614        sim.tableau.reset(0).expect("Reset should succeed");
1615
1616        // Measure should give 0
1617        let outcome = sim.measure(0).expect("Measurement should succeed");
1618        assert!(!outcome);
1619    }
1620
1621    #[test]
1622    fn test_x_y_measurements_commute() {
1623        // X and Y measurements on different qubits should not interfere
1624        let mut sim = StabilizerSimulator::new(2);
1625        sim.apply_gate(StabilizerGate::H(0)).unwrap();
1626        sim.apply_gate(StabilizerGate::H(1)).unwrap();
1627        sim.apply_gate(StabilizerGate::S(1)).unwrap();
1628
1629        // Measure qubit 0 in X basis and qubit 1 in Y basis
1630        let _outcome_x = sim.tableau.measure_x(0).unwrap();
1631        let _outcome_y = sim.tableau.measure_y(1).unwrap();
1632
1633        // Both measurements completed successfully (would have panicked otherwise)
1634    }
1635
1636    #[test]
1637    fn test_imaginary_phase_tracking() {
1638        // Test that S gate properly tracks imaginary phases
1639        // |0⟩ has stabilizer +Z
1640        // S|0⟩ = |0⟩ still has stabilizer +Z (global phase change only)
1641        // But H|0⟩ = |+⟩ has stabilizer +X
1642        // S·H|0⟩ = |i⟩ = (|0⟩ + i|1⟩)/√2 has stabilizer +Y
1643
1644        let mut tableau = StabilizerTableau::new(1);
1645        tableau.apply_h(0).unwrap();
1646        tableau.apply_s(0).unwrap();
1647
1648        let stabs = tableau.get_stabilizers();
1649        // After H·S, the state is |i⟩ = S|+⟩
1650        // S transforms X → Y, Z → Z
1651        // Starting with +X (from H|0⟩), S maps to +Y
1652        assert_eq!(stabs[0], "+Y");
1653    }
1654
1655    #[test]
1656    fn test_imaginary_phase_with_s_dag() {
1657        // S† should give conjugate phases
1658        // H·S†|0⟩ has stabilizer -Y (since S† maps X → -Y)
1659        let mut tableau = StabilizerTableau::new(1);
1660        tableau.apply_h(0).unwrap();
1661        tableau.apply_s_dag(0).unwrap();
1662
1663        let stabs = tableau.get_stabilizers();
1664        // After H·S†, the state has stabilizer -Y
1665        assert_eq!(stabs[0], "-Y");
1666    }
1667
1668    #[test]
1669    fn test_stim_format_identity() {
1670        // Test Stim format uses `_` for identity
1671        let mut tableau = StabilizerTableau::with_format(2, true);
1672        let stabs = tableau.get_stabilizers();
1673
1674        // In Stim format, identity should be `_`
1675        assert_eq!(stabs[0], "+Z_");
1676        assert_eq!(stabs[1], "+_Z");
1677
1678        // Apply Hadamard to first qubit
1679        tableau.apply_h(0).unwrap();
1680        let stabs = tableau.get_stabilizers();
1681        assert_eq!(stabs[0], "+X_");
1682        assert_eq!(stabs[1], "+_Z");
1683    }
1684
1685    #[test]
1686    fn test_standard_format_identity() {
1687        // Test standard format uses `I` for identity
1688        let tableau = StabilizerTableau::with_format(2, false);
1689        let stabs = tableau.get_stabilizers();
1690
1691        assert_eq!(stabs[0], "+ZI");
1692        assert_eq!(stabs[1], "+IZ");
1693    }
1694
1695    #[test]
1696    fn test_destabilizers_output() {
1697        // Test destabilizers are properly tracked
1698        let mut tableau = StabilizerTableau::new(2);
1699
1700        // Initial state: stabilizers are +Z_i, destabilizers are +X_i
1701        let destabs = tableau.get_destabilizers();
1702        assert_eq!(destabs[0], "+XI");
1703        assert_eq!(destabs[1], "+IX");
1704
1705        // Apply Hadamard - swaps X and Z
1706        tableau.apply_h(0).unwrap();
1707        let destabs = tableau.get_destabilizers();
1708        assert_eq!(destabs[0], "+ZI");
1709        assert_eq!(destabs[1], "+IX");
1710    }
1711
1712    #[test]
1713    fn test_phase_constants() {
1714        // Verify phase constant values
1715        assert_eq!(phase::PLUS_ONE, 0);
1716        assert_eq!(phase::PLUS_I, 1);
1717        assert_eq!(phase::MINUS_ONE, 2);
1718        assert_eq!(phase::MINUS_I, 3);
1719    }
1720
1721    #[test]
1722    fn test_phase_arithmetic() {
1723        // Test phase helper functions
1724        // negate_phase: +1 → -1, +i → -i, -1 → +1, -i → +i
1725        assert_eq!(
1726            StabilizerTableau::negate_phase(phase::PLUS_ONE),
1727            phase::MINUS_ONE
1728        );
1729        assert_eq!(
1730            StabilizerTableau::negate_phase(phase::PLUS_I),
1731            phase::MINUS_I
1732        );
1733        assert_eq!(
1734            StabilizerTableau::negate_phase(phase::MINUS_ONE),
1735            phase::PLUS_ONE
1736        );
1737        assert_eq!(
1738            StabilizerTableau::negate_phase(phase::MINUS_I),
1739            phase::PLUS_I
1740        );
1741
1742        // multiply_by_i: +1 → +i, +i → -1, -1 → -i, -i → +1
1743        assert_eq!(
1744            StabilizerTableau::multiply_by_i(phase::PLUS_ONE),
1745            phase::PLUS_I
1746        );
1747        assert_eq!(
1748            StabilizerTableau::multiply_by_i(phase::PLUS_I),
1749            phase::MINUS_ONE
1750        );
1751        assert_eq!(
1752            StabilizerTableau::multiply_by_i(phase::MINUS_ONE),
1753            phase::MINUS_I
1754        );
1755        assert_eq!(
1756            StabilizerTableau::multiply_by_i(phase::MINUS_I),
1757            phase::PLUS_ONE
1758        );
1759
1760        // multiply_by_minus_i: +1 → -i, +i → +1, -1 → +i, -i → -1
1761        assert_eq!(
1762            StabilizerTableau::multiply_by_minus_i(phase::PLUS_ONE),
1763            phase::MINUS_I
1764        );
1765        assert_eq!(
1766            StabilizerTableau::multiply_by_minus_i(phase::PLUS_I),
1767            phase::PLUS_ONE
1768        );
1769        assert_eq!(
1770            StabilizerTableau::multiply_by_minus_i(phase::MINUS_ONE),
1771            phase::PLUS_I
1772        );
1773        assert_eq!(
1774            StabilizerTableau::multiply_by_minus_i(phase::MINUS_I),
1775            phase::MINUS_ONE
1776        );
1777    }
1778
1779    #[test]
1780    fn test_y_gate_phase_tracking() {
1781        // Y gate should properly track phases
1782        // Y|0⟩ = i|1⟩, Y|1⟩ = -i|0⟩
1783        let mut tableau = StabilizerTableau::new(1);
1784        tableau.apply_y(0).unwrap();
1785
1786        let stabs = tableau.get_stabilizers();
1787        // Y flips Z stabilizer with phase -1: +Z → -Z
1788        assert_eq!(stabs[0], "-Z");
1789    }
1790
1791    #[test]
1792    fn test_sqrt_gates_produce_imaginary_phases() {
1793        // √Y gate should produce states with imaginary stabilizer phases
1794        // √Y transforms: X → Z, Y → Y, Z → -X
1795        let mut tableau = StabilizerTableau::new(1);
1796        tableau.apply_sqrt_y(0).unwrap();
1797
1798        let stabs = tableau.get_stabilizers();
1799        // Initial +Z gets mapped to -X by √Y
1800        assert_eq!(stabs[0], "-X");
1801    }
1802}