1use crate::error::{Result, SimulatorError};
6use crate::stabilizer::StabilizerGate;
7
8use std::collections::HashMap;
9
10use super::functions::parse_instruction;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum PauliType {
15 I,
16 X,
17 Y,
18 Z,
19}
20#[derive(Debug, Clone)]
22pub struct StimCircuit {
23 pub instructions: Vec<StimInstruction>,
25 pub num_qubits: usize,
27 pub metadata: Vec<String>,
29}
30impl StimCircuit {
31 pub fn new() -> Self {
33 Self {
34 instructions: Vec::new(),
35 num_qubits: 0,
36 metadata: Vec::new(),
37 }
38 }
39 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 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 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 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 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 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 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 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
455pub struct PauliTarget {
456 pub pauli: PauliType,
457 pub qubit: usize,
458}
459#[derive(Debug, Clone, PartialEq)]
461pub enum StimInstruction {
462 SingleQubitGate {
464 gate_type: SingleQubitGateType,
465 qubit: usize,
466 },
467 TwoQubitGate {
469 gate_type: TwoQubitGateType,
470 control: usize,
471 target: usize,
472 },
473 Measure {
475 basis: MeasurementBasis,
476 qubits: Vec<usize>,
477 },
478 Reset { qubits: Vec<usize> },
480 Comment(String),
482 Tick,
484 Detector {
487 coordinates: Vec<f64>,
488 record_targets: Vec<i32>,
489 },
490 ObservableInclude {
493 observable_index: usize,
494 record_targets: Vec<i32>,
495 },
496 MeasureReset {
499 basis: MeasurementBasis,
500 qubits: Vec<usize>,
501 },
502 Depolarize1 {
505 probability: f64,
506 qubits: Vec<usize>,
507 },
508 Depolarize2 {
511 probability: f64,
512 qubit_pairs: Vec<(usize, usize)>,
513 },
514 XError {
517 probability: f64,
518 qubits: Vec<usize>,
519 },
520 YError {
523 probability: f64,
524 qubits: Vec<usize>,
525 },
526 ZError {
529 probability: f64,
530 qubits: Vec<usize>,
531 },
532 PauliChannel1 {
535 px: f64,
536 py: f64,
537 pz: f64,
538 qubits: Vec<usize>,
539 },
540 PauliChannel2 {
543 probabilities: Vec<f64>,
544 qubit_pairs: Vec<(usize, usize)>,
545 },
546 CorrelatedError {
549 probability: f64,
550 targets: Vec<PauliTarget>,
551 },
552 ElseCorrelatedError {
555 probability: f64,
556 targets: Vec<PauliTarget>,
557 },
558 ShiftCoords { shifts: Vec<f64> },
561 QubitCoords { qubit: usize, coordinates: Vec<f64> },
564 Repeat {
567 count: usize,
568 instructions: Vec<StimInstruction>,
569 },
570}
571#[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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
604pub enum TwoQubitGateType {
605 CNOT,
606 CZ,
607 CY,
608 SWAP,
609}
610impl TwoQubitGateType {
611 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
623pub enum MeasurementBasis {
624 Z,
625 X,
626 Y,
627}
628#[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#[derive(Debug, Clone, Copy)]
639pub(super) enum ErrorType {
640 X,
641 Y,
642 Z,
643}