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