Skip to main content

quantrs2_sim/stim_parser/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::error::{Result, SimulatorError};
6use crate::stabilizer::StabilizerGate;
7
8use std::collections::HashMap;
9
10use super::functions::parse_instruction;
11
12/// Pauli operator type
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum PauliType {
15    I,
16    X,
17    Y,
18    Z,
19}
20/// Stim circuit representation
21#[derive(Debug, Clone)]
22pub struct StimCircuit {
23    /// Instructions in the circuit
24    pub instructions: Vec<StimInstruction>,
25    /// Number of qubits (inferred from max qubit index)
26    pub num_qubits: usize,
27    /// Metadata/comments
28    pub metadata: Vec<String>,
29}
30impl StimCircuit {
31    /// Create new empty Stim circuit
32    pub fn new() -> Self {
33        Self {
34            instructions: Vec::new(),
35            num_qubits: 0,
36            metadata: Vec::new(),
37        }
38    }
39    /// Add an instruction
40    pub fn add_instruction(&mut self, instruction: StimInstruction) {
41        match &instruction {
42            StimInstruction::SingleQubitGate { qubit, .. } => {
43                self.num_qubits = self.num_qubits.max(*qubit + 1);
44            }
45            StimInstruction::TwoQubitGate {
46                control, target, ..
47            } => {
48                self.num_qubits = self.num_qubits.max(*control + 1).max(*target + 1);
49            }
50            StimInstruction::Measure { qubits, .. } | StimInstruction::Reset { qubits } => {
51                if let Some(&max_qubit) = qubits.iter().max() {
52                    self.num_qubits = self.num_qubits.max(max_qubit + 1);
53                }
54            }
55            _ => {}
56        }
57        self.instructions.push(instruction);
58    }
59    /// Parse from Stim format string
60    pub fn from_str(s: &str) -> Result<Self> {
61        let mut circuit = Self::new();
62        for (line_num, line) in s.lines().enumerate() {
63            let line = line.trim();
64            if line.is_empty() {
65                continue;
66            }
67            if let Some(stripped) = line.strip_prefix('#') {
68                circuit.metadata.push(stripped.trim().to_string());
69                circuit.add_instruction(StimInstruction::Comment(stripped.trim().to_string()));
70                continue;
71            }
72            match parse_instruction(line) {
73                Ok(instruction) => circuit.add_instruction(instruction),
74                Err(e) => {
75                    return Err(SimulatorError::InvalidOperation(format!(
76                        "Line {}: {}",
77                        line_num + 1,
78                        e
79                    )));
80                }
81            }
82        }
83        Ok(circuit)
84    }
85    /// Get all gates (excluding measurements, resets, comments)
86    pub fn gates(&self) -> Vec<StabilizerGate> {
87        self.instructions
88            .iter()
89            .filter_map(|inst| match inst {
90                StimInstruction::SingleQubitGate { gate_type, qubit } => {
91                    Some(gate_type.to_stabilizer_gate(*qubit))
92                }
93                StimInstruction::TwoQubitGate {
94                    gate_type,
95                    control,
96                    target,
97                } => Some(gate_type.to_stabilizer_gate(*control, *target)),
98                _ => None,
99            })
100            .collect()
101    }
102    /// Get measurement instructions
103    pub fn measurements(&self) -> Vec<(MeasurementBasis, Vec<usize>)> {
104        self.instructions
105            .iter()
106            .filter_map(|inst| match inst {
107                StimInstruction::Measure { basis, qubits } => Some((*basis, qubits.clone())),
108                _ => None,
109            })
110            .collect()
111    }
112    /// Get reset instructions
113    pub fn resets(&self) -> Vec<Vec<usize>> {
114        self.instructions
115            .iter()
116            .filter_map(|inst| match inst {
117                StimInstruction::Reset { qubits } => Some(qubits.clone()),
118                _ => None,
119            })
120            .collect()
121    }
122    /// Convert to Stim format string
123    pub fn to_stim_string(&self) -> String {
124        let mut output = String::new();
125        for instruction in &self.instructions {
126            match instruction {
127                StimInstruction::SingleQubitGate { gate_type, qubit } => {
128                    let gate_name = match gate_type {
129                        SingleQubitGateType::H => "H",
130                        SingleQubitGateType::S => "S",
131                        SingleQubitGateType::SDag => "S_DAG",
132                        SingleQubitGateType::SqrtX => "SQRT_X",
133                        SingleQubitGateType::SqrtXDag => "SQRT_X_DAG",
134                        SingleQubitGateType::SqrtY => "SQRT_Y",
135                        SingleQubitGateType::SqrtYDag => "SQRT_Y_DAG",
136                        SingleQubitGateType::X => "X",
137                        SingleQubitGateType::Y => "Y",
138                        SingleQubitGateType::Z => "Z",
139                    };
140                    output.push_str(&format!("{} {}\n", gate_name, qubit));
141                }
142                StimInstruction::TwoQubitGate {
143                    gate_type,
144                    control,
145                    target,
146                } => {
147                    let gate_name = match gate_type {
148                        TwoQubitGateType::CNOT => "CNOT",
149                        TwoQubitGateType::CZ => "CZ",
150                        TwoQubitGateType::CY => "CY",
151                        TwoQubitGateType::SWAP => "SWAP",
152                    };
153                    output.push_str(&format!("{} {} {}\n", gate_name, control, target));
154                }
155                StimInstruction::Measure { basis, qubits } => {
156                    let measure_name = match basis {
157                        MeasurementBasis::Z => "M",
158                        MeasurementBasis::X => "MX",
159                        MeasurementBasis::Y => "MY",
160                    };
161                    output.push_str(&format!(
162                        "{} {}\n",
163                        measure_name,
164                        qubits
165                            .iter()
166                            .map(|q| q.to_string())
167                            .collect::<Vec<_>>()
168                            .join(" ")
169                    ));
170                }
171                StimInstruction::Reset { qubits } => {
172                    output.push_str(&format!(
173                        "R {}\n",
174                        qubits
175                            .iter()
176                            .map(|q| q.to_string())
177                            .collect::<Vec<_>>()
178                            .join(" ")
179                    ));
180                }
181                StimInstruction::Comment(comment) => {
182                    output.push_str(&format!("# {}\n", comment));
183                }
184                StimInstruction::Tick => {
185                    output.push_str("TICK\n");
186                }
187                StimInstruction::Detector {
188                    coordinates,
189                    record_targets,
190                } => {
191                    output.push_str("DETECTOR");
192                    for coord in coordinates {
193                        output.push_str(&format!(" {}", coord));
194                    }
195                    for target in record_targets {
196                        output.push_str(&format!(" rec[{}]", target));
197                    }
198                    output.push('\n');
199                }
200                StimInstruction::ObservableInclude {
201                    observable_index,
202                    record_targets,
203                } => {
204                    output.push_str(&format!("OBSERVABLE_INCLUDE({})", observable_index));
205                    for target in record_targets {
206                        output.push_str(&format!(" rec[{}]", target));
207                    }
208                    output.push('\n');
209                }
210                StimInstruction::MeasureReset { basis, qubits } => {
211                    let measure_name = match basis {
212                        MeasurementBasis::Z => "MR",
213                        MeasurementBasis::X => "MRX",
214                        MeasurementBasis::Y => "MRY",
215                    };
216                    output.push_str(&format!(
217                        "{} {}\n",
218                        measure_name,
219                        qubits
220                            .iter()
221                            .map(|q| q.to_string())
222                            .collect::<Vec<_>>()
223                            .join(" ")
224                    ));
225                }
226                StimInstruction::Depolarize1 {
227                    probability,
228                    qubits,
229                } => {
230                    output.push_str(&format!(
231                        "DEPOLARIZE1({}) {}\n",
232                        probability,
233                        qubits
234                            .iter()
235                            .map(|q| q.to_string())
236                            .collect::<Vec<_>>()
237                            .join(" ")
238                    ));
239                }
240                StimInstruction::Depolarize2 {
241                    probability,
242                    qubit_pairs,
243                } => {
244                    output.push_str(&format!("DEPOLARIZE2({})", probability));
245                    for (q1, q2) in qubit_pairs {
246                        output.push_str(&format!(" {} {}", q1, q2));
247                    }
248                    output.push('\n');
249                }
250                StimInstruction::XError {
251                    probability,
252                    qubits,
253                } => {
254                    output.push_str(&format!(
255                        "X_ERROR({}) {}\n",
256                        probability,
257                        qubits
258                            .iter()
259                            .map(|q| q.to_string())
260                            .collect::<Vec<_>>()
261                            .join(" ")
262                    ));
263                }
264                StimInstruction::YError {
265                    probability,
266                    qubits,
267                } => {
268                    output.push_str(&format!(
269                        "Y_ERROR({}) {}\n",
270                        probability,
271                        qubits
272                            .iter()
273                            .map(|q| q.to_string())
274                            .collect::<Vec<_>>()
275                            .join(" ")
276                    ));
277                }
278                StimInstruction::ZError {
279                    probability,
280                    qubits,
281                } => {
282                    output.push_str(&format!(
283                        "Z_ERROR({}) {}\n",
284                        probability,
285                        qubits
286                            .iter()
287                            .map(|q| q.to_string())
288                            .collect::<Vec<_>>()
289                            .join(" ")
290                    ));
291                }
292                StimInstruction::PauliChannel1 { px, py, pz, qubits } => {
293                    output.push_str(&format!(
294                        "PAULI_CHANNEL_1({},{},{}) {}\n",
295                        px,
296                        py,
297                        pz,
298                        qubits
299                            .iter()
300                            .map(|q| q.to_string())
301                            .collect::<Vec<_>>()
302                            .join(" ")
303                    ));
304                }
305                StimInstruction::PauliChannel2 {
306                    probabilities,
307                    qubit_pairs,
308                } => {
309                    output.push_str(&format!(
310                        "PAULI_CHANNEL_2({})",
311                        probabilities
312                            .iter()
313                            .map(|p| p.to_string())
314                            .collect::<Vec<_>>()
315                            .join(",")
316                    ));
317                    for (q1, q2) in qubit_pairs {
318                        output.push_str(&format!(" {} {}", q1, q2));
319                    }
320                    output.push('\n');
321                }
322                StimInstruction::CorrelatedError {
323                    probability,
324                    targets,
325                } => {
326                    output.push_str(&format!("E({})", probability));
327                    for target in targets {
328                        let pauli_char = match target.pauli {
329                            PauliType::I => 'I',
330                            PauliType::X => 'X',
331                            PauliType::Y => 'Y',
332                            PauliType::Z => 'Z',
333                        };
334                        output.push_str(&format!(" {}{}", pauli_char, target.qubit));
335                    }
336                    output.push('\n');
337                }
338                StimInstruction::ElseCorrelatedError {
339                    probability,
340                    targets,
341                } => {
342                    output.push_str(&format!("ELSE_CORRELATED_ERROR({})", probability));
343                    for target in targets {
344                        let pauli_char = match target.pauli {
345                            PauliType::I => 'I',
346                            PauliType::X => 'X',
347                            PauliType::Y => 'Y',
348                            PauliType::Z => 'Z',
349                        };
350                        output.push_str(&format!(" {}{}", pauli_char, target.qubit));
351                    }
352                    output.push('\n');
353                }
354                StimInstruction::ShiftCoords { shifts } => {
355                    output.push_str(&format!(
356                        "SHIFT_COORDS {}\n",
357                        shifts
358                            .iter()
359                            .map(|s| s.to_string())
360                            .collect::<Vec<_>>()
361                            .join(" ")
362                    ));
363                }
364                StimInstruction::QubitCoords { qubit, coordinates } => {
365                    output.push_str(&format!(
366                        "QUBIT_COORDS({}) {}\n",
367                        qubit,
368                        coordinates
369                            .iter()
370                            .map(|c| c.to_string())
371                            .collect::<Vec<_>>()
372                            .join(" ")
373                    ));
374                }
375                StimInstruction::Repeat {
376                    count,
377                    instructions,
378                } => {
379                    output.push_str(&format!("REPEAT {} {{\n", count));
380                    for inst in instructions {
381                        output.push_str("  # nested instruction\n");
382                    }
383                    output.push_str("}\n");
384                }
385            }
386        }
387        output
388    }
389    /// Get circuit statistics
390    pub fn statistics(&self) -> CircuitStatistics {
391        let mut num_gates = 0;
392        let mut num_measurements = 0;
393        let mut num_resets = 0;
394        let mut gate_counts = std::collections::HashMap::new();
395        for instruction in &self.instructions {
396            match instruction {
397                StimInstruction::SingleQubitGate { gate_type, .. } => {
398                    num_gates += 1;
399                    *gate_counts.entry(format!("{:?}", gate_type)).or_insert(0) += 1;
400                }
401                StimInstruction::TwoQubitGate { gate_type, .. } => {
402                    num_gates += 1;
403                    *gate_counts.entry(format!("{:?}", gate_type)).or_insert(0) += 1;
404                }
405                StimInstruction::Measure { qubits, .. } => {
406                    num_measurements += qubits.len();
407                }
408                StimInstruction::Reset { qubits } => {
409                    num_resets += qubits.len();
410                }
411                _ => {}
412            }
413        }
414        CircuitStatistics {
415            num_qubits: self.num_qubits,
416            num_gates,
417            num_measurements,
418            num_resets,
419            gate_counts,
420        }
421    }
422}
423/// Pauli target for correlated errors
424#[derive(Debug, Clone, Copy, PartialEq, Eq)]
425pub struct PauliTarget {
426    pub pauli: PauliType,
427    pub qubit: usize,
428}
429/// Stim instruction type
430#[derive(Debug, Clone, PartialEq)]
431pub enum StimInstruction {
432    /// Single-qubit gate
433    SingleQubitGate {
434        gate_type: SingleQubitGateType,
435        qubit: usize,
436    },
437    /// Two-qubit gate
438    TwoQubitGate {
439        gate_type: TwoQubitGateType,
440        control: usize,
441        target: usize,
442    },
443    /// Measurement
444    Measure {
445        basis: MeasurementBasis,
446        qubits: Vec<usize>,
447    },
448    /// Reset operation
449    Reset { qubits: Vec<usize> },
450    /// Comment (ignored during execution)
451    Comment(String),
452    /// Barrier/tick (for timing information)
453    Tick,
454    /// Detector annotation for error correction
455    /// DETECTOR [coords...] rec[-1] rec[-2] ...
456    Detector {
457        coordinates: Vec<f64>,
458        record_targets: Vec<i32>,
459    },
460    /// Observable annotation for logical observables
461    /// OBSERVABLE_INCLUDE(k) rec[-1] rec[-2] ...
462    ObservableInclude {
463        observable_index: usize,
464        record_targets: Vec<i32>,
465    },
466    /// Measure and reset to |0⟩
467    /// MR q1 q2 ...
468    MeasureReset {
469        basis: MeasurementBasis,
470        qubits: Vec<usize>,
471    },
472    /// Single-qubit depolarizing noise
473    /// DEPOLARIZE1(p) q1 q2 ...
474    Depolarize1 {
475        probability: f64,
476        qubits: Vec<usize>,
477    },
478    /// Two-qubit depolarizing noise
479    /// DEPOLARIZE2(p) q1 q2 q3 q4 ...
480    Depolarize2 {
481        probability: f64,
482        qubit_pairs: Vec<(usize, usize)>,
483    },
484    /// X error (bit flip)
485    /// X_ERROR(p) q1 q2 ...
486    XError {
487        probability: f64,
488        qubits: Vec<usize>,
489    },
490    /// Y error (bit-phase flip)
491    /// Y_ERROR(p) q1 q2 ...
492    YError {
493        probability: f64,
494        qubits: Vec<usize>,
495    },
496    /// Z error (phase flip)
497    /// Z_ERROR(p) q1 q2 ...
498    ZError {
499        probability: f64,
500        qubits: Vec<usize>,
501    },
502    /// Pauli channel (single qubit)
503    /// PAULI_CHANNEL_1(px, py, pz) q1 q2 ...
504    PauliChannel1 {
505        px: f64,
506        py: f64,
507        pz: f64,
508        qubits: Vec<usize>,
509    },
510    /// Pauli channel (two qubits)
511    /// PAULI_CHANNEL_2(p_IX, p_IY, ..., p_ZZ) q1 q2
512    PauliChannel2 {
513        probabilities: Vec<f64>,
514        qubit_pairs: Vec<(usize, usize)>,
515    },
516    /// Correlated error
517    /// CORRELATED_ERROR(p) X1 Y2 Z3 ... or shorthand: E(p) X1 Y2 Z3 ...
518    CorrelatedError {
519        probability: f64,
520        targets: Vec<PauliTarget>,
521    },
522    /// Else correlated error (conditional on previous E not triggering)
523    /// ELSE_CORRELATED_ERROR(p) X1 Y2 Z3 ...
524    ElseCorrelatedError {
525        probability: f64,
526        targets: Vec<PauliTarget>,
527    },
528    /// Shift coordinate system
529    /// SHIFT_COORDS [dx, dy, dz, ...]
530    ShiftCoords { shifts: Vec<f64> },
531    /// Qubit coordinates
532    /// QUBIT_COORDS(q) x y z ...
533    QubitCoords { qubit: usize, coordinates: Vec<f64> },
534    /// Repeat block
535    /// REPEAT N { ... }
536    Repeat {
537        count: usize,
538        instructions: Vec<StimInstruction>,
539    },
540}
541/// Single-qubit Clifford gate types
542#[derive(Debug, Clone, Copy, PartialEq, Eq)]
543pub enum SingleQubitGateType {
544    H,
545    S,
546    SDag,
547    SqrtX,
548    SqrtXDag,
549    SqrtY,
550    SqrtYDag,
551    X,
552    Y,
553    Z,
554}
555impl SingleQubitGateType {
556    /// Convert to stabilizer gate
557    pub fn to_stabilizer_gate(self, qubit: usize) -> StabilizerGate {
558        match self {
559            Self::H => StabilizerGate::H(qubit),
560            Self::S => StabilizerGate::S(qubit),
561            Self::SDag => StabilizerGate::SDag(qubit),
562            Self::SqrtX => StabilizerGate::SqrtX(qubit),
563            Self::SqrtXDag => StabilizerGate::SqrtXDag(qubit),
564            Self::SqrtY => StabilizerGate::SqrtY(qubit),
565            Self::SqrtYDag => StabilizerGate::SqrtYDag(qubit),
566            Self::X => StabilizerGate::X(qubit),
567            Self::Y => StabilizerGate::Y(qubit),
568            Self::Z => StabilizerGate::Z(qubit),
569        }
570    }
571}
572/// Two-qubit Clifford gate types
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub enum TwoQubitGateType {
575    CNOT,
576    CZ,
577    CY,
578    SWAP,
579}
580impl TwoQubitGateType {
581    /// Convert to stabilizer gate
582    pub fn to_stabilizer_gate(self, control: usize, target: usize) -> StabilizerGate {
583        match self {
584            Self::CNOT => StabilizerGate::CNOT(control, target),
585            Self::CZ => StabilizerGate::CZ(control, target),
586            Self::CY => StabilizerGate::CY(control, target),
587            Self::SWAP => StabilizerGate::SWAP(control, target),
588        }
589    }
590}
591/// Measurement basis
592#[derive(Debug, Clone, Copy, PartialEq, Eq)]
593pub enum MeasurementBasis {
594    Z,
595    X,
596    Y,
597}
598/// Circuit statistics
599#[derive(Debug, Clone)]
600pub struct CircuitStatistics {
601    pub num_qubits: usize,
602    pub num_gates: usize,
603    pub num_measurements: usize,
604    pub num_resets: usize,
605    pub gate_counts: std::collections::HashMap<String, usize>,
606}
607/// Error type helper for parsing
608#[derive(Debug, Clone, Copy)]
609pub(super) enum ErrorType {
610    X,
611    Y,
612    Z,
613}