Skip to main content

quantrs2_sim/stabilizer/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use quantrs2_circuit::prelude::*;
6use quantrs2_core::prelude::*;
7use scirs2_core::ndarray::{Array2, ArrayView2};
8use scirs2_core::random::prelude::*;
9use scirs2_core::Complex64;
10
11use super::functions::{phase, StabilizerPhase};
12
13/// Builder for creating circuits that can be simulated with the stabilizer formalism
14pub struct CliffordCircuitBuilder {
15    gates: Vec<StabilizerGate>,
16    num_qubits: usize,
17}
18impl CliffordCircuitBuilder {
19    /// Create a new Clifford circuit builder
20    #[must_use]
21    pub const fn new(num_qubits: usize) -> Self {
22        Self {
23            gates: Vec::new(),
24            num_qubits,
25        }
26    }
27    /// Add a Hadamard gate
28    #[must_use]
29    pub fn h(mut self, qubit: usize) -> Self {
30        self.gates.push(StabilizerGate::H(qubit));
31        self
32    }
33    /// Add an S gate
34    #[must_use]
35    pub fn s(mut self, qubit: usize) -> Self {
36        self.gates.push(StabilizerGate::S(qubit));
37        self
38    }
39    /// Add an S† (S-dagger) gate
40    #[must_use]
41    pub fn s_dag(mut self, qubit: usize) -> Self {
42        self.gates.push(StabilizerGate::SDag(qubit));
43        self
44    }
45    /// Add a √X gate
46    #[must_use]
47    pub fn sqrt_x(mut self, qubit: usize) -> Self {
48        self.gates.push(StabilizerGate::SqrtX(qubit));
49        self
50    }
51    /// Add a √X† gate
52    #[must_use]
53    pub fn sqrt_x_dag(mut self, qubit: usize) -> Self {
54        self.gates.push(StabilizerGate::SqrtXDag(qubit));
55        self
56    }
57    /// Add a √Y gate
58    #[must_use]
59    pub fn sqrt_y(mut self, qubit: usize) -> Self {
60        self.gates.push(StabilizerGate::SqrtY(qubit));
61        self
62    }
63    /// Add a √Y† gate
64    #[must_use]
65    pub fn sqrt_y_dag(mut self, qubit: usize) -> Self {
66        self.gates.push(StabilizerGate::SqrtYDag(qubit));
67        self
68    }
69    /// Add a Pauli-X gate
70    #[must_use]
71    pub fn x(mut self, qubit: usize) -> Self {
72        self.gates.push(StabilizerGate::X(qubit));
73        self
74    }
75    /// Add a Pauli-Y gate
76    #[must_use]
77    pub fn y(mut self, qubit: usize) -> Self {
78        self.gates.push(StabilizerGate::Y(qubit));
79        self
80    }
81    /// Add a Pauli-Z gate
82    #[must_use]
83    pub fn z(mut self, qubit: usize) -> Self {
84        self.gates.push(StabilizerGate::Z(qubit));
85        self
86    }
87    /// Add a CNOT gate
88    #[must_use]
89    pub fn cnot(mut self, control: usize, target: usize) -> Self {
90        self.gates.push(StabilizerGate::CNOT(control, target));
91        self
92    }
93    /// Add a CZ (Controlled-Z) gate
94    #[must_use]
95    pub fn cz(mut self, control: usize, target: usize) -> Self {
96        self.gates.push(StabilizerGate::CZ(control, target));
97        self
98    }
99    /// Add a CY (Controlled-Y) gate
100    #[must_use]
101    pub fn cy(mut self, control: usize, target: usize) -> Self {
102        self.gates.push(StabilizerGate::CY(control, target));
103        self
104    }
105    /// Add a SWAP gate
106    #[must_use]
107    pub fn swap(mut self, qubit1: usize, qubit2: usize) -> Self {
108        self.gates.push(StabilizerGate::SWAP(qubit1, qubit2));
109        self
110    }
111    /// Build and run the circuit
112    pub fn run(self) -> Result<StabilizerSimulator, QuantRS2Error> {
113        let mut sim = StabilizerSimulator::new(self.num_qubits);
114        for gate in self.gates {
115            sim.apply_gate(gate)?;
116        }
117        Ok(sim)
118    }
119}
120/// Stabilizer tableau representation
121///
122/// The tableau stores generators of the stabilizer group as rows.
123/// Each row represents a Pauli string with phase.
124///
125/// Phase encoding (Stim-compatible):
126/// - 0 = +1
127/// - 1 = +i
128/// - 2 = -1
129/// - 3 = -i
130#[derive(Debug, Clone)]
131pub struct StabilizerTableau {
132    /// Number of qubits
133    num_qubits: usize,
134    /// X part of stabilizers (n x n matrix)
135    x_matrix: Array2<bool>,
136    /// Z part of stabilizers (n x n matrix)
137    z_matrix: Array2<bool>,
138    /// Phase vector (n elements, encoded as 0=+1, 1=+i, 2=-1, 3=-i)
139    phase: Vec<StabilizerPhase>,
140    /// Destabilizers X part (n x n matrix)
141    destab_x: Array2<bool>,
142    /// Destabilizers Z part (n x n matrix)
143    destab_z: Array2<bool>,
144    /// Destabilizer phases (same encoding as phase)
145    destab_phase: Vec<StabilizerPhase>,
146    /// Pauli string format: true for Stim-style (`_` for identity), false for standard (`I`)
147    stim_format: bool,
148}
149impl StabilizerTableau {
150    /// Create a new tableau in the |0...0⟩ state
151    #[must_use]
152    pub fn new(num_qubits: usize) -> Self {
153        Self::with_format(num_qubits, false)
154    }
155    /// Create a new tableau with specified Pauli string format
156    ///
157    /// # Arguments
158    /// * `num_qubits` - Number of qubits
159    /// * `stim_format` - Use Stim format (`_` for identity) if true, standard format (`I`) if false
160    #[must_use]
161    pub fn with_format(num_qubits: usize, stim_format: bool) -> Self {
162        let mut x_matrix = Array2::from_elem((num_qubits, num_qubits), false);
163        let mut z_matrix = Array2::from_elem((num_qubits, num_qubits), false);
164        let mut destab_x = Array2::from_elem((num_qubits, num_qubits), false);
165        let mut destab_z = Array2::from_elem((num_qubits, num_qubits), false);
166        for i in 0..num_qubits {
167            z_matrix[[i, i]] = true;
168            destab_x[[i, i]] = true;
169        }
170        Self {
171            num_qubits,
172            x_matrix,
173            z_matrix,
174            phase: vec![phase::PLUS_ONE; num_qubits],
175            destab_x,
176            destab_z,
177            destab_phase: vec![phase::PLUS_ONE; num_qubits],
178            stim_format,
179        }
180    }
181    /// Set the Pauli string format
182    pub fn set_stim_format(&mut self, stim_format: bool) {
183        self.stim_format = stim_format;
184    }
185    /// Get the Pauli string format
186    #[must_use]
187    pub const fn is_stim_format(&self) -> bool {
188        self.stim_format
189    }
190    /// Multiply phase by -1 (add 2 mod 4)
191    #[inline]
192    pub(crate) fn negate_phase(p: StabilizerPhase) -> StabilizerPhase {
193        (p + 2) & 3
194    }
195    /// Multiply phase by i (add 1 mod 4)
196    #[inline]
197    pub(crate) fn multiply_by_i(p: StabilizerPhase) -> StabilizerPhase {
198        (p + 1) & 3
199    }
200    /// Multiply phase by -i (add 3 mod 4)
201    #[inline]
202    pub(crate) fn multiply_by_minus_i(p: StabilizerPhase) -> StabilizerPhase {
203        (p + 3) & 3
204    }
205    /// Add two phases (mod 4)
206    #[inline]
207    fn add_phases(p1: StabilizerPhase, p2: StabilizerPhase) -> StabilizerPhase {
208        (p1 + p2) & 3
209    }
210    /// Compute the phase contribution from row multiplication
211    /// When multiplying Pauli strings P1 and P2, the phase depends on the anticommutation
212    /// XZ = iY, ZX = -iY, etc.
213    #[inline]
214    fn rowsum_phase(x1: bool, z1: bool, x2: bool, z2: bool) -> StabilizerPhase {
215        match (x1, z1, x2, z2) {
216            (false, false, _, _) | (_, _, false, false) => 0,
217            (true, false, false, true) => 1,
218            (false, true, true, false) => 3,
219            (true, false, true, true) => 1,
220            (true, true, true, false) => 3,
221            (true, true, false, true) => 1,
222            (false, true, true, true) => 3,
223            (true, false, true, false) => 0,
224            (false, true, false, true) => 0,
225            (true, true, true, true) => 0,
226        }
227    }
228    /// Compute the phase contribution when multiplying a Pauli string (given as vectors)
229    /// with row `row_idx` from the tableau
230    fn compute_multiplication_phase(
231        &self,
232        result_x: &[bool],
233        result_z: &[bool],
234        row_idx: usize,
235    ) -> StabilizerPhase {
236        let mut total_phase: StabilizerPhase = 0;
237        for j in 0..self.num_qubits {
238            let phase_contrib = Self::rowsum_phase(
239                result_x[j],
240                result_z[j],
241                self.x_matrix[[row_idx, j]],
242                self.z_matrix[[row_idx, j]],
243            );
244            total_phase = Self::add_phases(total_phase, phase_contrib);
245        }
246        total_phase
247    }
248    /// Apply a Hadamard gate
249    ///
250    /// H: X → Z, Z → X, Y → -Y
251    /// Phase tracking: HYH = -Y, so Y component contributes i^2 = -1
252    pub fn apply_h(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
253        if qubit >= self.num_qubits {
254            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
255        }
256        for i in 0..self.num_qubits {
257            let x_val = self.x_matrix[[i, qubit]];
258            let z_val = self.z_matrix[[i, qubit]];
259            if x_val && z_val {
260                self.phase[i] = Self::negate_phase(self.phase[i]);
261            }
262            self.x_matrix[[i, qubit]] = z_val;
263            self.z_matrix[[i, qubit]] = x_val;
264            let dx_val = self.destab_x[[i, qubit]];
265            let dz_val = self.destab_z[[i, qubit]];
266            if dx_val && dz_val {
267                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
268            }
269            self.destab_x[[i, qubit]] = dz_val;
270            self.destab_z[[i, qubit]] = dx_val;
271        }
272        Ok(())
273    }
274    /// Apply an S gate (phase gate)
275    ///
276    /// S conjugation rules (SPS†):
277    /// - S: X → Y (no phase change, Pauli relabeling)
278    /// - S: Y → -X (phase negation due to SYS† = -X)
279    /// - S: Z → Z (no change)
280    ///
281    /// Note: The `i` in Y = iXZ is a matrix identity, not relevant to stabilizer
282    /// conjugation. In stabilizer formalism, X, Y, Z are atomic Pauli labels.
283    pub fn apply_s(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
284        if qubit >= self.num_qubits {
285            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
286        }
287        for i in 0..self.num_qubits {
288            let x_val = self.x_matrix[[i, qubit]];
289            let z_val = self.z_matrix[[i, qubit]];
290            if x_val {
291                if !z_val {
292                    self.z_matrix[[i, qubit]] = true;
293                } else {
294                    self.z_matrix[[i, qubit]] = false;
295                    self.phase[i] = Self::negate_phase(self.phase[i]);
296                }
297            }
298            let dx_val = self.destab_x[[i, qubit]];
299            let dz_val = self.destab_z[[i, qubit]];
300            if dx_val {
301                if !dz_val {
302                    self.destab_z[[i, qubit]] = true;
303                } else {
304                    self.destab_z[[i, qubit]] = false;
305                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
306                }
307            }
308        }
309        Ok(())
310    }
311    /// Apply a CNOT gate
312    pub fn apply_cnot(&mut self, control: usize, target: usize) -> Result<(), QuantRS2Error> {
313        if control >= self.num_qubits || target >= self.num_qubits {
314            return Err(QuantRS2Error::InvalidQubitId(control.max(target) as u32));
315        }
316        if control == target {
317            return Err(QuantRS2Error::InvalidInput(
318                "CNOT control and target must be different".to_string(),
319            ));
320        }
321        for i in 0..self.num_qubits {
322            if self.x_matrix[[i, control]] {
323                self.x_matrix[[i, target]] ^= true;
324            }
325            if self.z_matrix[[i, target]] {
326                self.z_matrix[[i, control]] ^= true;
327            }
328            if self.destab_x[[i, control]] {
329                self.destab_x[[i, target]] ^= true;
330            }
331            if self.destab_z[[i, target]] {
332                self.destab_z[[i, control]] ^= true;
333            }
334        }
335        Ok(())
336    }
337    /// Apply a Pauli X gate
338    ///
339    /// X anticommutes with Z and Y, commutes with X
340    /// Phase: adds -1 when Z or Y is present on the qubit
341    pub fn apply_x(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
342        if qubit >= self.num_qubits {
343            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
344        }
345        for i in 0..self.num_qubits {
346            if self.z_matrix[[i, qubit]] {
347                self.phase[i] = Self::negate_phase(self.phase[i]);
348            }
349            if self.destab_z[[i, qubit]] {
350                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
351            }
352        }
353        Ok(())
354    }
355    /// Apply a Pauli Y gate
356    ///
357    /// Y = iXZ, anticommutes with X and Z (separately), commutes with Y
358    /// Phase: adds -1 when X XOR Z is present (pure X or pure Z, not Y)
359    pub fn apply_y(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
360        if qubit >= self.num_qubits {
361            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
362        }
363        for i in 0..self.num_qubits {
364            let has_x = self.x_matrix[[i, qubit]];
365            let has_z = self.z_matrix[[i, qubit]];
366            if has_x != has_z {
367                self.phase[i] = Self::negate_phase(self.phase[i]);
368            }
369            let has_dx = self.destab_x[[i, qubit]];
370            let has_dz = self.destab_z[[i, qubit]];
371            if has_dx != has_dz {
372                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
373            }
374        }
375        Ok(())
376    }
377    /// Apply a Pauli Z gate
378    ///
379    /// Z anticommutes with X and Y, commutes with Z
380    /// Phase: adds -1 when X or Y is present on the qubit
381    pub fn apply_z(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
382        if qubit >= self.num_qubits {
383            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
384        }
385        for i in 0..self.num_qubits {
386            if self.x_matrix[[i, qubit]] {
387                self.phase[i] = Self::negate_phase(self.phase[i]);
388            }
389            if self.destab_x[[i, qubit]] {
390                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
391            }
392        }
393        Ok(())
394    }
395    /// Apply S† (S-dagger) gate
396    ///
397    /// S† conjugation rules (S†PS):
398    /// - S†: X → -Y (phase becomes -1)
399    /// - S†: Y → X (no phase change)
400    /// - S†: Z → Z (no change)
401    pub fn apply_s_dag(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
402        if qubit >= self.num_qubits {
403            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
404        }
405        for i in 0..self.num_qubits {
406            let x_val = self.x_matrix[[i, qubit]];
407            let z_val = self.z_matrix[[i, qubit]];
408            if x_val {
409                if !z_val {
410                    self.z_matrix[[i, qubit]] = true;
411                    self.phase[i] = Self::negate_phase(self.phase[i]);
412                } else {
413                    self.z_matrix[[i, qubit]] = false;
414                }
415            }
416            let dx_val = self.destab_x[[i, qubit]];
417            let dz_val = self.destab_z[[i, qubit]];
418            if dx_val {
419                if !dz_val {
420                    self.destab_z[[i, qubit]] = true;
421                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
422                } else {
423                    self.destab_z[[i, qubit]] = false;
424                }
425            }
426        }
427        Ok(())
428    }
429    /// Apply √X gate (SQRT_X, also called SX or V gate)
430    ///
431    /// Conjugation rules:
432    /// - √X: X → X (no change)
433    /// - √X: Y → -Z (phase becomes -1)
434    /// - √X: Z → Y (no phase change)
435    pub fn apply_sqrt_x(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
436        if qubit >= self.num_qubits {
437            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
438        }
439        for i in 0..self.num_qubits {
440            let x_val = self.x_matrix[[i, qubit]];
441            let z_val = self.z_matrix[[i, qubit]];
442            match (x_val, z_val) {
443                (false, false) => {}
444                (true, false) => {}
445                (false, true) => {
446                    self.x_matrix[[i, qubit]] = true;
447                }
448                (true, true) => {
449                    self.x_matrix[[i, qubit]] = false;
450                    self.phase[i] = Self::negate_phase(self.phase[i]);
451                }
452            }
453            let dx_val = self.destab_x[[i, qubit]];
454            let dz_val = self.destab_z[[i, qubit]];
455            match (dx_val, dz_val) {
456                (false, false) => {}
457                (true, false) => {}
458                (false, true) => {
459                    self.destab_x[[i, qubit]] = true;
460                }
461                (true, true) => {
462                    self.destab_x[[i, qubit]] = false;
463                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
464                }
465            }
466        }
467        Ok(())
468    }
469    /// Apply √X† gate (SQRT_X_DAG)
470    ///
471    /// Conjugation rules:
472    /// - √X†: X → X (no change)
473    /// - √X†: Y → Z (no phase change)
474    /// - √X†: Z → -Y (phase becomes -1)
475    pub fn apply_sqrt_x_dag(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
476        if qubit >= self.num_qubits {
477            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
478        }
479        for i in 0..self.num_qubits {
480            let x_val = self.x_matrix[[i, qubit]];
481            let z_val = self.z_matrix[[i, qubit]];
482            match (x_val, z_val) {
483                (false, false) => {}
484                (true, false) => {}
485                (false, true) => {
486                    self.x_matrix[[i, qubit]] = true;
487                    self.phase[i] = Self::negate_phase(self.phase[i]);
488                }
489                (true, true) => {
490                    self.x_matrix[[i, qubit]] = false;
491                }
492            }
493            let dx_val = self.destab_x[[i, qubit]];
494            let dz_val = self.destab_z[[i, qubit]];
495            match (dx_val, dz_val) {
496                (false, false) => {}
497                (true, false) => {}
498                (false, true) => {
499                    self.destab_x[[i, qubit]] = true;
500                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
501                }
502                (true, true) => {
503                    self.destab_x[[i, qubit]] = false;
504                }
505            }
506        }
507        Ok(())
508    }
509    /// Apply √Y gate (SQRT_Y)
510    ///
511    /// Conjugation rules:
512    /// - √Y: X → Z (no phase change)
513    /// - √Y: Y → Y (no change)
514    /// - √Y: Z → -X (phase becomes -1)
515    pub fn apply_sqrt_y(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
516        if qubit >= self.num_qubits {
517            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
518        }
519        for i in 0..self.num_qubits {
520            let x_val = self.x_matrix[[i, qubit]];
521            let z_val = self.z_matrix[[i, qubit]];
522            match (x_val, z_val) {
523                (false, false) => {}
524                (true, false) => {
525                    self.x_matrix[[i, qubit]] = false;
526                    self.z_matrix[[i, qubit]] = true;
527                }
528                (false, true) => {
529                    self.x_matrix[[i, qubit]] = true;
530                    self.z_matrix[[i, qubit]] = false;
531                    self.phase[i] = Self::negate_phase(self.phase[i]);
532                }
533                (true, true) => {}
534            }
535            let dx_val = self.destab_x[[i, qubit]];
536            let dz_val = self.destab_z[[i, qubit]];
537            match (dx_val, dz_val) {
538                (false, false) => {}
539                (true, false) => {
540                    self.destab_x[[i, qubit]] = false;
541                    self.destab_z[[i, qubit]] = true;
542                }
543                (false, true) => {
544                    self.destab_x[[i, qubit]] = true;
545                    self.destab_z[[i, qubit]] = false;
546                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
547                }
548                (true, true) => {}
549            }
550        }
551        Ok(())
552    }
553    /// Apply √Y† gate (SQRT_Y_DAG)
554    ///
555    /// Conjugation rules:
556    /// - √Y†: X → -Z (phase becomes -1)
557    /// - √Y†: Y → Y (no change)
558    /// - √Y†: Z → X (no phase change)
559    pub fn apply_sqrt_y_dag(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
560        if qubit >= self.num_qubits {
561            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
562        }
563        for i in 0..self.num_qubits {
564            let x_val = self.x_matrix[[i, qubit]];
565            let z_val = self.z_matrix[[i, qubit]];
566            match (x_val, z_val) {
567                (false, false) => {}
568                (true, false) => {
569                    self.x_matrix[[i, qubit]] = false;
570                    self.z_matrix[[i, qubit]] = true;
571                    self.phase[i] = Self::negate_phase(self.phase[i]);
572                }
573                (false, true) => {
574                    self.x_matrix[[i, qubit]] = true;
575                    self.z_matrix[[i, qubit]] = false;
576                }
577                (true, true) => {}
578            }
579            let dx_val = self.destab_x[[i, qubit]];
580            let dz_val = self.destab_z[[i, qubit]];
581            match (dx_val, dz_val) {
582                (false, false) => {}
583                (true, false) => {
584                    self.destab_x[[i, qubit]] = false;
585                    self.destab_z[[i, qubit]] = true;
586                    self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
587                }
588                (false, true) => {
589                    self.destab_x[[i, qubit]] = true;
590                    self.destab_z[[i, qubit]] = false;
591                }
592                (true, true) => {}
593            }
594        }
595        Ok(())
596    }
597    /// Apply CZ (Controlled-Z) gate
598    ///
599    /// CZ: X_c → X_c Z_t, X_t → Z_c X_t, Z_c → Z_c, Z_t → Z_t
600    /// When both qubits have X component (product of X or Y), phase picks up -1
601    pub fn apply_cz(&mut self, control: usize, target: usize) -> Result<(), QuantRS2Error> {
602        if control >= self.num_qubits {
603            return Err(QuantRS2Error::InvalidQubitId(control as u32));
604        }
605        if target >= self.num_qubits {
606            return Err(QuantRS2Error::InvalidQubitId(target as u32));
607        }
608        for i in 0..self.num_qubits {
609            if self.x_matrix[[i, control]] && self.x_matrix[[i, target]] {
610                self.phase[i] = Self::negate_phase(self.phase[i]);
611            }
612            if self.x_matrix[[i, control]] {
613                self.z_matrix[[i, target]] = !self.z_matrix[[i, target]];
614            }
615            if self.x_matrix[[i, target]] {
616                self.z_matrix[[i, control]] = !self.z_matrix[[i, control]];
617            }
618            if self.destab_x[[i, control]] && self.destab_x[[i, target]] {
619                self.destab_phase[i] = Self::negate_phase(self.destab_phase[i]);
620            }
621            if self.destab_x[[i, control]] {
622                self.destab_z[[i, target]] = !self.destab_z[[i, target]];
623            }
624            if self.destab_x[[i, target]] {
625                self.destab_z[[i, control]] = !self.destab_z[[i, control]];
626            }
627        }
628        Ok(())
629    }
630    /// Apply CY (Controlled-Y) gate
631    pub fn apply_cy(&mut self, control: usize, target: usize) -> Result<(), QuantRS2Error> {
632        if control >= self.num_qubits {
633            return Err(QuantRS2Error::InvalidQubitId(control as u32));
634        }
635        if target >= self.num_qubits {
636            return Err(QuantRS2Error::InvalidQubitId(target as u32));
637        }
638        self.apply_s_dag(target)?;
639        self.apply_cnot(control, target)?;
640        self.apply_s(target)?;
641        Ok(())
642    }
643    /// Apply SWAP gate
644    pub fn apply_swap(&mut self, qubit1: usize, qubit2: usize) -> Result<(), QuantRS2Error> {
645        if qubit1 >= self.num_qubits {
646            return Err(QuantRS2Error::InvalidQubitId(qubit1 as u32));
647        }
648        if qubit2 >= self.num_qubits {
649            return Err(QuantRS2Error::InvalidQubitId(qubit2 as u32));
650        }
651        self.apply_cnot(qubit1, qubit2)?;
652        self.apply_cnot(qubit2, qubit1)?;
653        self.apply_cnot(qubit1, qubit2)?;
654        Ok(())
655    }
656    /// Measure a qubit in the computational (Z) basis
657    /// Returns the measurement outcome (0 or 1)
658    ///
659    /// For phases with imaginary components, we project onto real eigenvalues:
660    /// - Phase 0 (+1) or 1 (+i) → eigenvalue +1 → outcome 0
661    /// - Phase 2 (-1) or 3 (-i) → eigenvalue -1 → outcome 1
662    pub fn measure(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
663        if qubit >= self.num_qubits {
664            return Err(QuantRS2Error::InvalidQubitId(qubit as u32));
665        }
666        let mut anticommuting_row = None;
667        for i in 0..self.num_qubits {
668            if self.x_matrix[[i, qubit]] {
669                anticommuting_row = Some(i);
670                break;
671            }
672        }
673        if let Some(p) = anticommuting_row {
674            for j in 0..self.num_qubits {
675                self.x_matrix[[p, j]] = false;
676                self.z_matrix[[p, j]] = j == qubit;
677            }
678            let mut rng = thread_rng();
679            let outcome = rng.random_bool(0.5);
680            self.phase[p] = if outcome {
681                phase::MINUS_ONE
682            } else {
683                phase::PLUS_ONE
684            };
685            for i in 0..self.num_qubits {
686                if i != p && self.x_matrix[[i, qubit]] {
687                    let mut total_phase_contrib: StabilizerPhase = 0;
688                    for j in 0..self.num_qubits {
689                        let phase_contrib = Self::rowsum_phase(
690                            self.x_matrix[[i, j]],
691                            self.z_matrix[[i, j]],
692                            self.x_matrix[[p, j]],
693                            self.z_matrix[[p, j]],
694                        );
695                        total_phase_contrib = Self::add_phases(total_phase_contrib, phase_contrib);
696                        self.x_matrix[[i, j]] ^= self.x_matrix[[p, j]];
697                        self.z_matrix[[i, j]] ^= self.z_matrix[[p, j]];
698                    }
699                    self.phase[i] = Self::add_phases(
700                        Self::add_phases(self.phase[i], self.phase[p]),
701                        total_phase_contrib,
702                    );
703                }
704            }
705            Ok(outcome)
706        } else {
707            let mut pivot_row = None;
708            for i in 0..self.num_qubits {
709                if self.z_matrix[[i, qubit]] && !self.x_matrix[[i, qubit]] {
710                    pivot_row = Some(i);
711                    break;
712                }
713            }
714            let Some(pivot) = pivot_row else {
715                return Ok(false);
716            };
717            let mut result_x = vec![false; self.num_qubits];
718            let mut result_z = vec![false; self.num_qubits];
719            let mut result_phase = self.phase[pivot];
720            for j in 0..self.num_qubits {
721                result_x[j] = self.x_matrix[[pivot, j]];
722                result_z[j] = self.z_matrix[[pivot, j]];
723            }
724            for other_qubit in 0..self.num_qubits {
725                if other_qubit == qubit {
726                    continue;
727                }
728                if result_z[other_qubit] && !result_x[other_qubit] {
729                    for i in 0..self.num_qubits {
730                        if i == pivot {
731                            continue;
732                        }
733                        if self.z_matrix[[i, other_qubit]] && !self.x_matrix[[i, other_qubit]] {
734                            let phase_contrib =
735                                self.compute_multiplication_phase(&result_x, &result_z, i);
736                            result_phase = Self::add_phases(result_phase, self.phase[i]);
737                            result_phase = Self::add_phases(result_phase, phase_contrib);
738                            for j in 0..self.num_qubits {
739                                result_x[j] ^= self.x_matrix[[i, j]];
740                                result_z[j] ^= self.z_matrix[[i, j]];
741                            }
742                            break;
743                        }
744                    }
745                }
746            }
747            let outcome = result_phase >= phase::MINUS_ONE;
748            Ok(outcome)
749        }
750    }
751    /// Measure a qubit in the X basis (Stim MX instruction)
752    ///
753    /// Equivalent to: H · measure_z · H
754    pub fn measure_x(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
755        self.apply_h(qubit)?;
756        let outcome = self.measure(qubit)?;
757        self.apply_h(qubit)?;
758        Ok(outcome)
759    }
760    /// Measure a qubit in the Y basis (Stim MY instruction)
761    ///
762    /// Equivalent to: S† · H · measure_z · H · S
763    pub fn measure_y(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
764        self.apply_s_dag(qubit)?;
765        self.apply_h(qubit)?;
766        let outcome = self.measure(qubit)?;
767        self.apply_h(qubit)?;
768        self.apply_s(qubit)?;
769        Ok(outcome)
770    }
771    /// Reset a qubit to |0⟩ state (Stim R instruction)
772    ///
773    /// Performs measurement and applies X if outcome is |1⟩
774    pub fn reset(&mut self, qubit: usize) -> Result<(), QuantRS2Error> {
775        let outcome = self.measure(qubit)?;
776        if outcome {
777            self.apply_x(qubit)?;
778        }
779        Ok(())
780    }
781    /// Get the current stabilizer generators as strings
782    ///
783    /// Phase encoding in output:
784    /// - `+` for phase 0 (+1)
785    /// - `+i` for phase 1 (+i)
786    /// - `-` for phase 2 (-1)
787    /// - `-i` for phase 3 (-i)
788    ///
789    /// Identity representation depends on `stim_format`:
790    /// - Standard format: `I` for identity
791    /// - Stim format: `_` for identity
792    #[must_use]
793    pub fn get_stabilizers(&self) -> Vec<String> {
794        let mut stabilizers = Vec::new();
795        let identity_char = if self.stim_format { '_' } else { 'I' };
796        for i in 0..self.num_qubits {
797            let mut stab = String::new();
798            match self.phase[i] & 3 {
799                phase::PLUS_ONE => stab.push('+'),
800                phase::PLUS_I => stab.push_str("+i"),
801                phase::MINUS_ONE => stab.push('-'),
802                phase::MINUS_I => stab.push_str("-i"),
803                _ => unreachable!(),
804            }
805            for j in 0..self.num_qubits {
806                let has_x = self.x_matrix[[i, j]];
807                let has_z = self.z_matrix[[i, j]];
808                match (has_x, has_z) {
809                    (false, false) => stab.push(identity_char),
810                    (true, false) => stab.push('X'),
811                    (false, true) => stab.push('Z'),
812                    (true, true) => stab.push('Y'),
813                }
814            }
815            stabilizers.push(stab);
816        }
817        stabilizers
818    }
819    /// Get the current destabilizer generators as strings
820    ///
821    /// Same format as `get_stabilizers()` but for destabilizers
822    #[must_use]
823    pub fn get_destabilizers(&self) -> Vec<String> {
824        let mut destabilizers = Vec::new();
825        let identity_char = if self.stim_format { '_' } else { 'I' };
826        for i in 0..self.num_qubits {
827            let mut destab = String::new();
828            match self.destab_phase[i] & 3 {
829                phase::PLUS_ONE => destab.push('+'),
830                phase::PLUS_I => destab.push_str("+i"),
831                phase::MINUS_ONE => destab.push('-'),
832                phase::MINUS_I => destab.push_str("-i"),
833                _ => unreachable!(),
834            }
835            for j in 0..self.num_qubits {
836                let has_x = self.destab_x[[i, j]];
837                let has_z = self.destab_z[[i, j]];
838                match (has_x, has_z) {
839                    (false, false) => destab.push(identity_char),
840                    (true, false) => destab.push('X'),
841                    (false, true) => destab.push('Z'),
842                    (true, true) => destab.push('Y'),
843                }
844            }
845            destabilizers.push(destab);
846        }
847        destabilizers
848    }
849}
850/// Gates supported by the stabilizer simulator
851#[derive(Debug, Clone, Copy)]
852pub enum StabilizerGate {
853    H(usize),
854    S(usize),
855    SDag(usize),
856    SqrtX(usize),
857    SqrtXDag(usize),
858    SqrtY(usize),
859    SqrtYDag(usize),
860    X(usize),
861    Y(usize),
862    Z(usize),
863    CNOT(usize, usize),
864    CZ(usize, usize),
865    CY(usize, usize),
866    SWAP(usize, usize),
867}
868/// Stabilizer simulator that efficiently simulates Clifford circuits
869#[derive(Debug, Clone)]
870pub struct StabilizerSimulator {
871    /// The stabilizer tableau
872    pub tableau: StabilizerTableau,
873    measurement_record: Vec<(usize, bool)>,
874}
875impl StabilizerSimulator {
876    /// Create a new stabilizer simulator
877    #[must_use]
878    pub fn new(num_qubits: usize) -> Self {
879        Self {
880            tableau: StabilizerTableau::new(num_qubits),
881            measurement_record: Vec::new(),
882        }
883    }
884    /// Apply a gate to the simulator
885    pub fn apply_gate(&mut self, gate: StabilizerGate) -> Result<(), QuantRS2Error> {
886        match gate {
887            StabilizerGate::H(q) => self.tableau.apply_h(q),
888            StabilizerGate::S(q) => self.tableau.apply_s(q),
889            StabilizerGate::SDag(q) => self.tableau.apply_s_dag(q),
890            StabilizerGate::SqrtX(q) => self.tableau.apply_sqrt_x(q),
891            StabilizerGate::SqrtXDag(q) => self.tableau.apply_sqrt_x_dag(q),
892            StabilizerGate::SqrtY(q) => self.tableau.apply_sqrt_y(q),
893            StabilizerGate::SqrtYDag(q) => self.tableau.apply_sqrt_y_dag(q),
894            StabilizerGate::X(q) => self.tableau.apply_x(q),
895            StabilizerGate::Y(q) => self.tableau.apply_y(q),
896            StabilizerGate::Z(q) => self.tableau.apply_z(q),
897            StabilizerGate::CNOT(c, t) => self.tableau.apply_cnot(c, t),
898            StabilizerGate::CZ(c, t) => self.tableau.apply_cz(c, t),
899            StabilizerGate::CY(c, t) => self.tableau.apply_cy(c, t),
900            StabilizerGate::SWAP(q1, q2) => self.tableau.apply_swap(q1, q2),
901        }
902    }
903    /// Measure a qubit
904    pub fn measure(&mut self, qubit: usize) -> Result<bool, QuantRS2Error> {
905        let outcome = self.tableau.measure(qubit)?;
906        self.measurement_record.push((qubit, outcome));
907        Ok(outcome)
908    }
909    /// Get the current stabilizers
910    #[must_use]
911    pub fn get_stabilizers(&self) -> Vec<String> {
912        self.tableau.get_stabilizers()
913    }
914    /// Get measurement record
915    #[must_use]
916    pub fn get_measurements(&self) -> &[(usize, bool)] {
917        &self.measurement_record
918    }
919    /// Reset the simulator
920    pub fn reset(&mut self) {
921        let num_qubits = self.tableau.num_qubits;
922        self.tableau = StabilizerTableau::new(num_qubits);
923        self.measurement_record.clear();
924    }
925    /// Get the number of qubits
926    #[must_use]
927    pub const fn num_qubits(&self) -> usize {
928        self.tableau.num_qubits
929    }
930    /// Get the state vector (for compatibility with other simulators)
931    /// Note: This is expensive for stabilizer states and returns a sparse representation
932    #[must_use]
933    pub fn get_statevector(&self) -> Vec<Complex64> {
934        let n = self.tableau.num_qubits;
935        let dim = 1 << n;
936        let mut state = vec![Complex64::new(0.0, 0.0); dim];
937        state[0] = Complex64::new(1.0, 0.0);
938        state
939    }
940}