1use crate::circuit::bit::Qubit;
73use crate::circuit::circuit_param::ParameterValue;
74use crate::circuit::gate::circuit_gate::{CircuitGate, FrozenCircuit};
75use crate::circuit::gate::{Directive, Instruction, StandardGate};
76use crate::circuit::{Circuit, CircuitError, ClassicalExpr, ClassicalType, ClassicalVar};
77
78use crate::circuit::parameter::Parameter;
79use crate::ir::qasm2::ast::{
80 Argument, Expression, OpCode, OpenQASMProgram, Statement, UnaryOpCode,
81};
82use smallvec::{SmallVec, smallvec};
83use std::collections::{HashMap, HashSet};
84use std::io;
85use std::panic::{AssertUnwindSafe, catch_unwind};
86use std::path::{Component, Path, PathBuf};
87
88pub trait QasmSourceResolver {
95 fn resolve_source(&self, path: &Path) -> Result<String, io::Error>;
104}
105
106pub struct FileSystemResolver;
108
109impl QasmSourceResolver for FileSystemResolver {
110 fn resolve_source(&self, path: &Path) -> Result<String, io::Error> {
111 std::fs::read_to_string(path)
112 }
113}
114
115pub struct NullResolver;
117
118impl QasmSourceResolver for NullResolver {
119 fn resolve_source(&self, path: &Path) -> Result<String, io::Error> {
120 Err(io::Error::new(
121 io::ErrorKind::Unsupported,
122 format!("Cannot include files in raw string mode (path: {:?})", path),
123 ))
124 }
125}
126
127const QELIB1_DIRECT_GATES: &[&str] = &[
128 "u3", "u2", "u1", "cx", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "rx", "ry", "rz",
129 "cz", "cy", "ccx", "crz",
130];
131
132const QELIB1_CUSTOM_GATES: &[&str] = &["ch", "cu1", "cu3"];
133
134#[allow(dead_code)]
135const CQLIB_BUILTIN_EXTENSION_GATES: &[&str] = &[
136 "p", "sx", "sxdg", "rxx", "ryy", "rzz", "rzx", "fsim", "crx", "cry", "swap", "toffoli",
137];
138
139const QELIB1_RESERVED_GATE_NAMES: &[&str] = &[
146 "u3", "u2", "u1", "cx", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "rx", "ry", "rz",
147 "cz", "cy", "ch", "ccx", "crz", "cu1", "cu3",
148];
149
150#[rustfmt::skip]
151mod parser {
152 include!(concat!(env!("OUT_DIR"), "/ir/qasm2/parser.rs"));
153}
154
155pub fn load<P: AsRef<Path>>(path: P) -> Result<Circuit, QasmParseError> {
157 let path = path.as_ref();
158
159 let resolver = Box::new(FileSystemResolver);
161
162 let content = resolver
164 .resolve_source(path)
165 .map_err(QasmParseError::IoError)?;
166
167 let base_path = path.parent().map(|p| p.to_path_buf());
169
170 parse_qasm_file_with_context(&content, normalize_path(path), base_path, resolver)
171}
172
173pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Circuit, QasmParseError> {
178 load(path)
179}
180
181pub fn loads(source: &str) -> Result<Circuit, QasmParseError> {
183 parse_qasm_with_context(source, None, Box::new(NullResolver))
185}
186
187pub fn from_str(source: &str) -> Result<Circuit, QasmParseError> {
191 loads(source)
192}
193
194fn parse_qasm_with_context(
195 source: &str,
196 base_path: Option<PathBuf>,
197 resolver: Box<dyn QasmSourceResolver>,
198) -> Result<Circuit, QasmParseError> {
199 parse_qasm_source_with_context(source, None, base_path, resolver)
200}
201
202fn parse_qasm_file_with_context(
203 source: &str,
204 root_path: PathBuf,
205 base_path: Option<PathBuf>,
206 resolver: Box<dyn QasmSourceResolver>,
207) -> Result<Circuit, QasmParseError> {
208 parse_qasm_source_with_context(source, Some(root_path), base_path, resolver)
209}
210
211fn parse_qasm_source_with_context(
212 source: &str,
213 root_path: Option<PathBuf>,
214 base_path: Option<PathBuf>,
215 resolver: Box<dyn QasmSourceResolver>,
216) -> Result<Circuit, QasmParseError> {
217 if let Some(literal) = first_oversized_integer_literal(source) {
218 return Err(QasmParseError::ParseError(format!(
219 "Integer literal '{literal}' exceeds supported i64 range"
220 )));
221 }
222
223 let parser = parser::MainParser::new();
224 let parsed = catch_unwind(AssertUnwindSafe(|| parser.parse(source))).map_err(|payload| {
225 let message = if let Some(message) = payload.downcast_ref::<String>() {
226 message.clone()
227 } else if let Some(message) = payload.downcast_ref::<&'static str>() {
228 (*message).to_string()
229 } else {
230 "OpenQASM 2.0 parser panicked".to_string()
231 };
232 QasmParseError::ParseError(message)
233 })?;
234 let program = match parsed {
235 Ok(program) => program,
236 Err(e) => return Err(QasmParseError::ParseError(format!("{:?}", e))),
237 };
238 if (program.version - 2.0).abs() > f64::EPSILON {
239 return Err(QasmParseError::UnsupportedVersion(program.version));
240 }
241
242 let mut converter = AstToCircuit::new_with_root(base_path, root_path, resolver);
243 converter.convert(&program)
244}
245
246fn first_oversized_integer_literal(source: &str) -> Option<String> {
247 let bytes = source.as_bytes();
248 let mut index = 0;
249 while index < bytes.len() {
250 if bytes[index] == b'"' {
251 index += 1;
252 while index < bytes.len() && bytes[index] != b'"' {
253 index += 1;
254 }
255 index += usize::from(index < bytes.len());
256 continue;
257 }
258 if bytes[index] == b'/' && bytes.get(index + 1) == Some(&b'/') {
259 index += 2;
260 while index < bytes.len() && bytes[index] != b'\n' && bytes[index] != b'\r' {
261 index += 1;
262 }
263 continue;
264 }
265 if !bytes[index].is_ascii_digit() {
266 index += 1;
267 continue;
268 }
269
270 let start = index;
271 while index < bytes.len() && bytes[index].is_ascii_digit() {
272 index += 1;
273 }
274 let prev = start.checked_sub(1).and_then(|idx| bytes.get(idx)).copied();
275 let next = bytes.get(index).copied();
276 if matches!(prev, Some(b'.' | b'e' | b'E')) || matches!(next, Some(b'.' | b'e' | b'E')) {
277 continue;
278 }
279
280 let literal = &source[start..index];
281 if literal.parse::<i64>().is_err() {
282 return Some(literal.to_string());
283 }
284 }
285 None
286}
287
288#[derive(Debug)]
293pub enum QasmParseError {
294 IoError(io::Error),
296 ParseError(String),
298 ConversionError(String),
300 UndefinedQubit(String),
302 UndefinedRegister(String),
304 UndefinedGate(String),
306 ReservedGateName(String),
308 InvalidArgument(String),
310 MismatchedQubitCount { expected: usize, actual: usize },
312 MismatchedParameterCount { expected: usize, actual: usize },
314 RecursionLimitExceeded(String),
316 EvaluationError(String),
318 CircularGateDependency { gate: String, dependency: String },
320 UnsupportedVersion(f64),
322 DuplicateDeclaration(String),
324 IncludeCycle(Vec<PathBuf>),
326 UnsupportedOpaqueGate(String),
328}
329
330impl std::fmt::Display for QasmParseError {
331 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332 match self {
333 QasmParseError::IoError(s) => write!(f, "IO error: {}", s),
334 QasmParseError::ParseError(s) => write!(f, "Parse error: {}", s),
335 QasmParseError::ConversionError(s) => write!(f, "Conversion error: {}", s),
336 QasmParseError::UndefinedQubit(s) => write!(f, "Undefined qubit: {}", s),
337 QasmParseError::UndefinedRegister(s) => write!(f, "Undefined register: {}", s),
338 QasmParseError::UndefinedGate(s) => write!(f, "Undefined gate: {}", s),
339 QasmParseError::ReservedGateName(s) => write!(
340 f,
341 "Gate name '{}' is reserved by qelib1.inc and cannot be redefined",
342 s
343 ),
344 QasmParseError::InvalidArgument(s) => write!(f, "Invalid argument: {}", s),
345 QasmParseError::MismatchedQubitCount { expected, actual } => {
346 write!(
347 f,
348 "Mismatched qubit count: expected {}, got {}",
349 expected, actual
350 )
351 }
352 QasmParseError::MismatchedParameterCount { expected, actual } => {
353 write!(
354 f,
355 "Mismatched parameter count: expected {}, got {}",
356 expected, actual
357 )
358 }
359 QasmParseError::RecursionLimitExceeded(s) => {
360 write!(f, "Recursion limit exceeded: {}", s)
361 }
362 QasmParseError::EvaluationError(s) => write!(f, "Evaluation error: {}", s),
363 QasmParseError::CircularGateDependency { gate, dependency } => {
364 write!(
365 f,
366 "Circular gate dependency detected: '{}' depends on '{}'",
367 gate, dependency
368 )
369 }
370 QasmParseError::UnsupportedVersion(version) => {
371 write!(f, "Unsupported OpenQASM version {version}; expected 2.0")
372 }
373 QasmParseError::DuplicateDeclaration(s) => {
374 write!(f, "Duplicate declaration: {}", s)
375 }
376 QasmParseError::IncludeCycle(stack) => {
377 write!(f, "Include cycle detected: ")?;
378 for (index, path) in stack.iter().enumerate() {
379 if index > 0 {
380 write!(f, " -> ")?;
381 }
382 write!(f, "{}", path.display())?;
383 }
384 Ok(())
385 }
386 QasmParseError::UnsupportedOpaqueGate(s) => {
387 write!(f, "Opaque gate '{}' cannot be invoked", s)
388 }
389 }
390 }
391}
392
393impl std::error::Error for QasmParseError {
394 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
395 match self {
396 QasmParseError::IoError(error) => Some(error),
397 _ => None,
398 }
399 }
400}
401
402impl From<CircuitError> for QasmParseError {
403 fn from(value: CircuitError) -> Self {
404 Self::ConversionError(value.to_string())
405 }
406}
407
408const DEFAULT_MAX_RECURSION_DEPTH: usize = 100;
410
411struct AstToCircuit {
418 qregs: HashMap<String, i64>,
420 qreg_order: Vec<String>,
422 cregs: HashMap<String, i64>,
424 creg_order: Vec<String>,
426 creg_vars: HashMap<String, ClassicalVar>,
428 custom_gates: HashMap<String, CustomGateDef>,
430 declarations: HashMap<String, DeclarationKind>,
432 base_path: Option<PathBuf>,
434 root_path: Option<PathBuf>,
436 file_cache: HashMap<PathBuf, OpenQASMProgram>,
438 recursion_depth: usize,
440 max_recursion_depth: usize,
442 compiling_gates: HashSet<String>,
444 resolver: Box<dyn QasmSourceResolver>,
446 qelib1_included: bool,
447}
448
449#[derive(Clone, Copy, Debug, Eq, PartialEq)]
450enum DeclarationKind {
451 QReg,
452 CReg,
453 Gate,
454 Opaque,
455}
456
457#[derive(Clone, Debug)]
459struct CustomGateDef {
460 #[allow(dead_code)]
461 name: String,
462 params: Vec<String>,
463 qubits: Vec<String>,
464 body: Vec<Statement>,
466 circuit_gate: Option<CircuitGate>,
468 is_opaque: bool,
470}
471
472impl AstToCircuit {
473 #[cfg(test)]
474 fn new(base_path: Option<PathBuf>, resolver: Box<dyn QasmSourceResolver>) -> Self {
475 Self::new_with_root(base_path, None, resolver)
476 }
477
478 fn new_with_root(
479 base_path: Option<PathBuf>,
480 root_path: Option<PathBuf>,
481 resolver: Box<dyn QasmSourceResolver>,
482 ) -> Self {
483 let mut converter = Self {
484 qregs: HashMap::new(),
485 qreg_order: Vec::new(),
486 cregs: HashMap::new(),
487 creg_order: Vec::new(),
488 creg_vars: HashMap::new(),
489 custom_gates: HashMap::new(),
490 declarations: HashMap::new(),
491 base_path,
492 root_path,
493 file_cache: HashMap::new(),
494 recursion_depth: 0,
495 max_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
496 compiling_gates: HashSet::new(),
497 resolver,
498 qelib1_included: false,
499 };
500 converter
501 .register_qelib1_include()
502 .expect("built-in qelib1 gate construction should not fail");
503 converter
504 }
505
506 fn convert(&mut self, program: &OpenQASMProgram) -> Result<Circuit, QasmParseError> {
507 let current_dir = self.base_path.clone();
510 let mut include_stack = self.initial_include_stack();
511 self.discovery_pass_with_context(program, current_dir.as_deref(), &mut include_stack)?;
512
513 self.compile_all_gates()?;
516
517 let total_qubits = self.qregs.values().try_fold(0usize, |total, size| {
519 total.checked_add(*size as usize).ok_or_else(|| {
520 QasmParseError::ConversionError(
521 "Total quantum register size exceeds platform limits".to_string(),
522 )
523 })
524 })?;
525 let mut circuit = Circuit::new(total_qubits);
526
527 for name in &self.creg_order {
528 let width = self.cregs[name] as u32;
529 let ty = ClassicalType::bit_vec(width).ok_or_else(|| {
530 QasmParseError::ConversionError(format!(
531 "Classical register '{name}' must have non-zero width"
532 ))
533 })?;
534 let var = circuit.var(ty);
535 let zero =
536 ClassicalExpr::pack_bits((0..width).map(|_| ClassicalExpr::bit_literal(false)))
537 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
538 circuit
539 .store(var, zero)
540 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
541 self.creg_vars.insert(name.clone(), var);
542 }
543
544 let mut reg_start_map: HashMap<String, usize> = HashMap::new();
546 let mut global_idx = 0;
547
548 for name in &self.qreg_order {
550 reg_start_map.insert(name.clone(), global_idx);
551 let size = self.qregs[name];
552 global_idx += size as usize;
553 }
554
555 let mut include_stack = self.initial_include_stack();
558 self.generation_pass(
559 program,
560 &mut circuit,
561 ®_start_map,
562 current_dir.as_deref(),
563 &mut include_stack,
564 )?;
565
566 Ok(circuit)
567 }
568
569 fn initial_include_stack(&self) -> Vec<PathBuf> {
570 self.root_path.iter().cloned().collect()
571 }
572
573 fn compile_all_gates(&mut self) -> Result<(), QasmParseError> {
575 let gate_names: Vec<String> = self.custom_gates.keys().cloned().collect();
577
578 for name in gate_names {
579 self.compile_gate_if_needed(&name)?;
580 }
581
582 Ok(())
583 }
584
585 fn compile_gate_if_needed(
587 &mut self,
588 name: &str,
589 ) -> Result<Option<CircuitGate>, QasmParseError> {
590 if self.recursion_depth >= self.max_recursion_depth {
592 return Err(QasmParseError::RecursionLimitExceeded(format!(
593 "Gate expansion depth exceeded limit of {} (compiling {})",
594 self.max_recursion_depth, name
595 )));
596 }
597
598 if self.compiling_gates.contains(name) {
600 return Err(QasmParseError::CircularGateDependency {
601 gate: name.to_string(),
602 dependency: name.to_string(),
603 });
604 }
605
606 if let Some(def) = self.custom_gates.get(name) {
608 if def.is_opaque {
609 return Ok(None);
610 }
611 if def.circuit_gate.is_some() {
612 return Ok(def.circuit_gate.clone());
613 }
614 } else {
615 return Err(QasmParseError::UndefinedGate(name.to_string()));
616 }
617
618 let (params, qubits, body) = if let Some(def) = self.custom_gates.get(name) {
620 (def.params.clone(), def.qubits.clone(), def.body.clone())
621 } else {
622 return Err(QasmParseError::UndefinedGate(name.to_string()));
623 };
624
625 self.compiling_gates.insert(name.to_string());
627
628 self.recursion_depth += 1;
630
631 let result = (|| {
632 for stmt in &body {
634 if let Statement::CustomGate(dep_name, _, _) = stmt
635 && self.custom_gates.contains_key(dep_name)
636 {
637 if self.compiling_gates.contains(dep_name) {
639 return Err(QasmParseError::CircularGateDependency {
640 gate: name.to_string(),
641 dependency: dep_name.to_string(),
642 });
643 }
644 self.compile_gate_if_needed(dep_name)?;
645 }
646 }
647
648 self.build_circuit_gate(name, ¶ms, &qubits, &body)
649 })();
650
651 self.recursion_depth -= 1;
653 self.compiling_gates.remove(name);
654
655 let circuit_gate = result?;
656
657 if let Some(def) = self.custom_gates.get_mut(name) {
659 def.circuit_gate = Some(circuit_gate.clone());
660 }
661
662 Ok(Some(circuit_gate))
663 }
664
665 fn register_qelib1_include(&mut self) -> Result<(), QasmParseError> {
666 if self.qelib1_included {
667 return Ok(());
668 }
669
670 debug_assert!(
671 QELIB1_DIRECT_GATES
672 .iter()
673 .chain(QELIB1_CUSTOM_GATES.iter())
674 .all(|name| QELIB1_RESERVED_GATE_NAMES.contains(name))
675 );
676
677 for decl in [
678 Self::qelib1_ch_gate()?,
679 Self::qelib1_cu1_gate()?,
680 Self::qelib1_cu3_gate()?,
681 ] {
682 self.custom_gates.insert(decl.name.clone(), decl);
683 }
684 self.qelib1_included = true;
685 Ok(())
686 }
687
688 fn qelib1_ch_gate() -> Result<CustomGateDef, QasmParseError> {
689 let a = Qubit::new(0);
690 let b = Qubit::new(1);
691 let mut circuit = Circuit::new(2);
692
693 circuit.h(b)?;
694 circuit.sdg(b)?;
695 circuit.cx(a, b)?;
696 circuit.h(b)?;
697 circuit.t(b)?;
698 circuit.cx(a, b)?;
699 circuit.t(b)?;
700 circuit.h(b)?;
701 circuit.s(b)?;
702 circuit.x(b)?;
703 circuit.s(a)?;
704
705 Self::qelib1_gate_def("ch", &[], &["a", "b"], circuit)
706 }
707
708 fn qelib1_cu1_gate() -> Result<CustomGateDef, QasmParseError> {
709 let a = Qubit::new(0);
710 let b = Qubit::new(1);
711 let lambda = Parameter::symbol("lambda");
712 let mut circuit = Circuit::new(2);
713 circuit.add_parameter(lambda.clone());
714
715 circuit.phase(a, lambda.clone() / 2.0)?;
716 circuit.cx(a, b)?;
717 circuit.phase(b, (Parameter::from(0.0) - lambda.clone()) / 2.0)?;
718 circuit.cx(a, b)?;
719 circuit.phase(b, lambda / 2.0)?;
720
721 Self::qelib1_gate_def("cu1", &["lambda"], &["a", "b"], circuit)
722 }
723
724 fn qelib1_cu3_gate() -> Result<CustomGateDef, QasmParseError> {
725 let c = Qubit::new(0);
726 let t = Qubit::new(1);
727 let theta = Parameter::symbol("theta");
728 let phi = Parameter::symbol("phi");
729 let lambda = Parameter::symbol("lambda");
730 let mut circuit = Circuit::new(2);
731 circuit.add_parameter(theta.clone());
732 circuit.add_parameter(phi.clone());
733 circuit.add_parameter(lambda.clone());
734
735 circuit.phase(t, (lambda.clone() - phi.clone()) / 2.0)?;
736 circuit.cx(c, t)?;
737 circuit.u(
738 t,
739 (Parameter::from(0.0) - theta.clone()) / 2.0,
740 0.0,
741 (Parameter::from(0.0) - (phi.clone() + lambda.clone())) / 2.0,
742 )?;
743 circuit.cx(c, t)?;
744 circuit.u(t, theta / 2.0, phi, 0.0)?;
745
746 Self::qelib1_gate_def("cu3", &["theta", "phi", "lambda"], &["c", "t"], circuit)
747 }
748
749 fn qelib1_gate_def(
750 name: &str,
751 params: &[&str],
752 qubits: &[&str],
753 circuit: Circuit,
754 ) -> Result<CustomGateDef, QasmParseError> {
755 let circuit_gate = CircuitGate::with_signature(
756 name,
757 FrozenCircuit::new(circuit),
758 params.iter().map(|param| (*param).to_string()),
759 )
760 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
761 Ok(CustomGateDef {
762 name: name.to_string(),
763 params: params.iter().map(|param| (*param).to_string()).collect(),
764 qubits: qubits.iter().map(|qubit| (*qubit).to_string()).collect(),
765 body: Vec::new(),
766 circuit_gate: Some(circuit_gate),
767 is_opaque: false,
768 })
769 }
770
771 fn declare_name(&mut self, name: &str, kind: DeclarationKind) -> Result<(), QasmParseError> {
772 if let Some(existing) = self.declarations.get(name) {
773 return Err(QasmParseError::DuplicateDeclaration(format!(
774 "'{name}' declared as {existing:?} and {kind:?}"
775 )));
776 }
777 self.declarations.insert(name.to_string(), kind);
778 Ok(())
779 }
780
781 fn ensure_gate_name_allowed(&self, name: &str) -> Result<(), QasmParseError> {
782 if QELIB1_RESERVED_GATE_NAMES.contains(&name) {
783 return Err(QasmParseError::ReservedGateName(name.to_string()));
784 }
785 Ok(())
786 }
787
788 fn validate_gate_formals(params: &[String], qubits: &[String]) -> Result<(), QasmParseError> {
789 let mut seen_params = HashSet::new();
790 for param in params {
791 if !seen_params.insert(param) {
792 return Err(QasmParseError::InvalidArgument(format!(
793 "Duplicate gate parameter formal '{param}'"
794 )));
795 }
796 }
797
798 let mut seen_qubits = HashSet::new();
799 for qubit in qubits {
800 if !seen_qubits.insert(qubit) {
801 return Err(QasmParseError::InvalidArgument(format!(
802 "Duplicate gate qubit formal '{qubit}'"
803 )));
804 }
805 if seen_params.contains(qubit) {
806 return Err(QasmParseError::InvalidArgument(format!(
807 "Gate formal '{qubit}' is both a parameter and a qubit"
808 )));
809 }
810 }
811
812 Ok(())
813 }
814
815 fn resolve_include_path(&self, current_dir: Option<&Path>, filename: &str) -> PathBuf {
816 let path = if let Some(base) = current_dir.or(self.base_path.as_deref()) {
817 base.join(filename)
818 } else {
819 PathBuf::from(filename)
820 };
821 normalize_path(path)
822 }
823
824 fn include_cycle_error(stack: &[PathBuf], target: &Path) -> QasmParseError {
825 let mut cycle = stack.to_vec();
826 cycle.push(target.to_path_buf());
827 QasmParseError::IncludeCycle(cycle)
828 }
829
830 fn parse_include(
831 &mut self,
832 target_path: &Path,
833 filename: &str,
834 ) -> Result<OpenQASMProgram, QasmParseError> {
835 if let Some(program) = self.file_cache.get(target_path) {
836 return Ok(program.clone());
837 }
838
839 let content = self.resolver.resolve_source(target_path).map_err(|e| {
840 QasmParseError::IoError(io::Error::new(
841 e.kind(),
842 format!("Include {}: {}", filename, e),
843 ))
844 })?;
845 let parser = parser::ProgramBodyParser::new();
846 let included_program = parser
847 .parse(&content)
848 .map_err(|e| QasmParseError::ParseError(format!("In {}: {:?}", filename, e)))?;
849
850 self.file_cache
851 .insert(target_path.to_path_buf(), included_program.clone());
852 Ok(included_program)
853 }
854
855 #[cfg(test)]
856 fn discovery_pass(&mut self, program: &OpenQASMProgram) -> Result<(), QasmParseError> {
857 let current_dir = self.base_path.clone();
858 let mut include_stack = self.initial_include_stack();
859 self.discovery_pass_with_context(program, current_dir.as_deref(), &mut include_stack)
860 }
861
862 fn discovery_pass_with_context(
863 &mut self,
864 program: &OpenQASMProgram,
865 current_dir: Option<&Path>,
866 include_stack: &mut Vec<PathBuf>,
867 ) -> Result<(), QasmParseError> {
868 for stmt in &program.statements {
869 match stmt {
870 Statement::QReg(name, size) => {
871 if *size <= 0 {
872 return Err(QasmParseError::InvalidArgument(format!(
873 "Quantum register '{name}' must have positive width, got {size}"
874 )));
875 }
876 self.declare_name(name, DeclarationKind::QReg)?;
877 self.qregs.insert(name.clone(), *size);
878 self.qreg_order.push(name.clone());
879 }
880 Statement::CReg(name, size) => {
881 if *size <= 0 {
882 return Err(QasmParseError::InvalidArgument(format!(
883 "Classical register '{name}' must have positive width, got {size}"
884 )));
885 }
886 self.declare_name(name, DeclarationKind::CReg)?;
887 self.cregs.insert(name.clone(), *size);
888 self.creg_order.push(name.clone());
889 }
890 Statement::GateDecl(data) => {
891 self.ensure_gate_name_allowed(&data.name)?;
892 self.declare_name(&data.name, DeclarationKind::Gate)?;
893 Self::validate_gate_formals(&data.params, &data.qubits)?;
894
895 let decl = CustomGateDef {
897 name: data.name.clone(),
898 params: data.params.clone(),
899 qubits: data.qubits.clone(),
900 body: data.body.clone(),
901 circuit_gate: None,
902 is_opaque: false,
903 };
904 self.custom_gates.insert(data.name.clone(), decl);
905 }
906 Statement::Include(filename) => {
907 if filename == "qelib1.inc" {
908 self.register_qelib1_include()?;
909 continue;
910 }
911
912 let target_path = self.resolve_include_path(current_dir, filename);
913 if include_stack.contains(&target_path) {
914 return Err(Self::include_cycle_error(include_stack, &target_path));
915 }
916
917 let should_discover = !self.file_cache.contains_key(&target_path);
918 let included_program = self.parse_include(&target_path, filename)?;
919 if should_discover {
920 include_stack.push(target_path.clone());
921 let next_dir = target_path.parent().map(Path::to_path_buf);
922 self.discovery_pass_with_context(
923 &included_program,
924 next_dir.as_deref(),
925 include_stack,
926 )?;
927 include_stack.pop();
928 }
929 }
930 Statement::Opaque(name, params, qubits) => {
931 self.ensure_gate_name_allowed(name)?;
932 self.declare_name(name, DeclarationKind::Opaque)?;
933 Self::validate_gate_formals(params, qubits)?;
934
935 let decl = CustomGateDef {
937 name: name.clone(),
938 params: params.clone(),
939 qubits: qubits.clone(),
940 body: vec![],
941 circuit_gate: None,
942 is_opaque: true,
943 };
944 self.custom_gates.insert(name.clone(), decl);
945 }
946 _ => {}
947 }
948 }
949 Ok(())
950 }
951
952 fn build_circuit_gate(
954 &self,
955 name: &str,
956 params: &[String],
957 qubits: &[String],
958 body: &[Statement],
959 ) -> Result<CircuitGate, QasmParseError> {
960 let num_qubits = qubits.len();
962 let mut gate_circuit = Circuit::new(num_qubits);
963
964 let mut qubit_map: HashMap<String, Qubit> = HashMap::new();
966 for (i, qubit_name) in qubits.iter().enumerate() {
967 qubit_map.insert(qubit_name.clone(), Qubit::new(i as u32));
968 }
969
970 let mut param_map: HashMap<String, Parameter> = HashMap::new();
973 for param_name in params.iter() {
974 let param = Parameter::symbol(param_name);
976 param_map.insert(param_name.clone(), param);
977 }
978
979 for stmt in body {
981 self.build_gate_body_statement(stmt, &mut gate_circuit, &qubit_map, ¶m_map)?;
982 }
983
984 let frozen = FrozenCircuit::new(gate_circuit);
986 CircuitGate::with_signature(name, frozen, params.iter().cloned())
987 .map_err(|e| QasmParseError::ConversionError(e.to_string()))
988 }
989
990 fn build_gate_body_statement(
992 &self,
993 stmt: &Statement,
994 circuit: &mut Circuit,
995 qubit_map: &HashMap<String, Qubit>,
996 param_map: &HashMap<String, Parameter>,
997 ) -> Result<(), QasmParseError> {
998 match stmt {
999 Statement::CustomGate(name, args, qargs) => {
1000 let mut resolved_qubits = Vec::new();
1002 for arg in qargs {
1003 match arg {
1004 Argument::Id(qname) => {
1005 if let Some(&q) = qubit_map.get(qname) {
1006 resolved_qubits.push(q);
1007 } else {
1008 return Err(QasmParseError::UndefinedQubit(qname.clone()));
1009 }
1010 }
1011 Argument::IndexedId(_, _) => {
1012 return Err(QasmParseError::InvalidArgument(
1013 "Indexed arguments not supported in gate body".to_string(),
1014 ));
1015 }
1016 }
1017 }
1018
1019 let mut resolved_params: SmallVec<[ParameterValue; 3]> = smallvec![];
1021 for expr in args {
1022 let param = Self::expr_to_parameter(expr, param_map)?;
1023 resolved_params.push(ParameterValue::from(param));
1024 }
1025
1026 if let Some(gate_def) = self.custom_gates.get(name) {
1028 if let Some(ref cg) = gate_def.circuit_gate {
1029 circuit
1031 .append(
1032 Instruction::CircuitGate(Box::new(cg.clone())),
1033 resolved_qubits,
1034 resolved_params,
1035 None,
1036 )
1037 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1038 } else {
1039 return Err(QasmParseError::UndefinedGate(format!(
1041 "Opaque gate {} cannot be used in gate body",
1042 name
1043 )));
1044 }
1045 } else {
1046 self.append_standard_gate(circuit, name, &resolved_params, &resolved_qubits)?;
1048 }
1049 }
1050 Statement::Barrier(args) => {
1051 let mut resolved_qubits = Vec::new();
1052 for arg in args {
1053 match arg {
1054 Argument::Id(qname) => {
1055 if let Some(&q) = qubit_map.get(qname) {
1056 resolved_qubits.push(q);
1057 } else {
1058 return Err(QasmParseError::UndefinedQubit(qname.clone()));
1059 }
1060 }
1061 Argument::IndexedId(_, _) => {
1062 return Err(QasmParseError::InvalidArgument(
1063 "Indexed barrier arguments are not supported in gate body"
1064 .to_string(),
1065 ));
1066 }
1067 }
1068 }
1069 circuit
1070 .append(
1071 Instruction::Directive(Directive::Barrier),
1072 resolved_qubits,
1073 std::iter::empty::<ParameterValue>(),
1074 None,
1075 )
1076 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1077 }
1078 _ => {
1079 return Err(QasmParseError::ConversionError(format!(
1080 "Unsupported statement type in gate body: {:?}",
1081 stmt
1082 )));
1083 }
1084 }
1085 Ok(())
1086 }
1087
1088 fn expr_to_parameter(
1090 expr: &Expression,
1091 param_map: &HashMap<String, Parameter>,
1092 ) -> Result<Parameter, QasmParseError> {
1093 match expr {
1094 Expression::Real(v) => Ok(Parameter::from(*v)),
1095 Expression::Integer(v) => Ok(Parameter::from(*v as f64)),
1096 Expression::Pi => Ok(Parameter::pi()),
1097 Expression::Id(name) => {
1098 if let Some(param) = param_map.get(name) {
1099 Ok(param.clone())
1100 } else {
1101 Err(QasmParseError::EvaluationError(format!(
1103 "Unknown parameter: {}",
1104 name
1105 )))
1106 }
1107 }
1108 Expression::BinaryOp(left, op, right) => {
1109 let l = Self::expr_to_parameter(left, param_map)?;
1110 let r = Self::expr_to_parameter(right, param_map)?;
1111 Ok(match op {
1112 OpCode::Add => l + r,
1113 OpCode::Sub => l - r,
1114 OpCode::Mul => l * r,
1115 OpCode::Div => {
1116 if let Ok(val) = r.evaluate(&None)
1118 && val == 0.0
1119 {
1120 return Err(QasmParseError::EvaluationError(
1121 "Division by zero".to_string(),
1122 ));
1123 }
1124 l / r
1125 }
1126 OpCode::Pow => l.pow(r),
1127 })
1128 }
1129 Expression::UnaryOp(op, expr) => {
1130 let v = Self::expr_to_parameter(expr, param_map)?;
1131 Ok(match op {
1132 UnaryOpCode::Sin => v.sin(),
1133 UnaryOpCode::Cos => v.cos(),
1134 UnaryOpCode::Tan => v.tan(),
1135 UnaryOpCode::Exp => v.exp(),
1136 UnaryOpCode::Ln => v.ln(),
1137 UnaryOpCode::Sqrt => v.sqrt(),
1138 UnaryOpCode::Asin => v.asin(),
1139 UnaryOpCode::Acos => v.acos(),
1140 UnaryOpCode::Atan => v.atan(),
1141 UnaryOpCode::Neg => Parameter::from(0.0) - v,
1142 })
1143 }
1144 }
1145 }
1146
1147 fn build_standard_gate_instruction(
1154 name: &str,
1155 params: &[ParameterValue],
1156 qubits: &[Qubit],
1157 ) -> Result<(Instruction, Vec<ParameterValue>), QasmParseError> {
1158 let check_counts = |req_q: usize, req_p: usize| -> Result<(), QasmParseError> {
1160 if qubits.len() != req_q {
1161 return Err(QasmParseError::MismatchedQubitCount {
1162 expected: req_q,
1163 actual: qubits.len(),
1164 });
1165 }
1166 if params.len() != req_p {
1167 return Err(QasmParseError::MismatchedParameterCount {
1168 expected: req_p,
1169 actual: params.len(),
1170 });
1171 }
1172 Ok(())
1173 };
1174
1175 match name {
1176 "u2" | "U2" => {
1177 check_counts(1, 2)?;
1178 let pi_2 = ParameterValue::Param(Parameter::pi() / 2.0);
1180 let new_params = vec![pi_2, params[0].clone(), params[1].clone()];
1181 Ok((Instruction::Standard(StandardGate::U), new_params))
1182 }
1183
1184 "rx" | "RX" => {
1185 check_counts(1, 1)?;
1186 Ok((Instruction::Standard(StandardGate::RX), params.to_vec()))
1187 }
1188 "ry" | "RY" => {
1189 check_counts(1, 1)?;
1190 Ok((Instruction::Standard(StandardGate::RY), params.to_vec()))
1191 }
1192 "rz" | "RZ" => {
1193 check_counts(1, 1)?;
1194 Ok((Instruction::Standard(StandardGate::RZ), params.to_vec()))
1195 }
1196 "p" | "P" => {
1197 check_counts(1, 1)?;
1198 Ok((Instruction::Standard(StandardGate::Phase), params.to_vec()))
1199 }
1200 "u1" | "U1" => {
1201 check_counts(1, 1)?;
1202 Ok((Instruction::Standard(StandardGate::Phase), params.to_vec()))
1203 }
1204
1205 "u3" | "U3" | "u" | "U" => {
1207 check_counts(1, 3)?;
1208 Ok((Instruction::Standard(StandardGate::U), params.to_vec()))
1209 }
1210
1211 "rxx" | "RXX" => {
1212 check_counts(2, 1)?;
1213 Ok((Instruction::Standard(StandardGate::RXX), params.to_vec()))
1214 }
1215 "ryy" | "RYY" => {
1216 check_counts(2, 1)?;
1217 Ok((Instruction::Standard(StandardGate::RYY), params.to_vec()))
1218 }
1219 "rzz" | "RZZ" => {
1220 check_counts(2, 1)?;
1221 Ok((Instruction::Standard(StandardGate::RZZ), params.to_vec()))
1222 }
1223 "fsim" | "FSIM" => {
1224 check_counts(2, 2)?;
1225 Ok((Instruction::Standard(StandardGate::FSIM), params.to_vec()))
1226 }
1227
1228 "crx" | "CRX" => {
1230 check_counts(2, 1)?;
1231 Ok((Instruction::Standard(StandardGate::CRX), params.to_vec()))
1232 }
1233 "cry" | "CRY" => {
1234 check_counts(2, 1)?;
1235 Ok((Instruction::Standard(StandardGate::CRY), params.to_vec()))
1236 }
1237 "crz" | "CRZ" => {
1238 check_counts(2, 1)?;
1239 Ok((Instruction::Standard(StandardGate::CRZ), params.to_vec()))
1240 }
1241
1242 "h" | "H" => {
1243 check_counts(1, 0)?;
1244 Ok((Instruction::Standard(StandardGate::H), vec![]))
1245 }
1246 "x" | "X" => {
1247 check_counts(1, 0)?;
1248 Ok((Instruction::Standard(StandardGate::X), vec![]))
1249 }
1250 "y" | "Y" => {
1251 check_counts(1, 0)?;
1252 Ok((Instruction::Standard(StandardGate::Y), vec![]))
1253 }
1254 "z" | "Z" => {
1255 check_counts(1, 0)?;
1256 Ok((Instruction::Standard(StandardGate::Z), vec![]))
1257 }
1258 "s" | "S" => {
1259 check_counts(1, 0)?;
1260 Ok((Instruction::Standard(StandardGate::S), vec![]))
1261 }
1262 "sdg" | "SDG" => {
1263 check_counts(1, 0)?;
1264 Ok((Instruction::Standard(StandardGate::SDG), vec![]))
1265 }
1266 "t" | "T" => {
1267 check_counts(1, 0)?;
1268 Ok((Instruction::Standard(StandardGate::T), vec![]))
1269 }
1270 "tdg" | "TDG" => {
1271 check_counts(1, 0)?;
1272 Ok((Instruction::Standard(StandardGate::TDG), vec![]))
1273 }
1274 "sx" | "SX" => {
1275 check_counts(1, 0)?;
1276 Ok((Instruction::Standard(StandardGate::X2P), vec![]))
1277 }
1278 "sxdg" | "SXDG" => {
1279 check_counts(1, 0)?;
1280 Ok((Instruction::Standard(StandardGate::X2M), vec![]))
1281 }
1282 "id" | "ID" | "i" | "I" => {
1283 check_counts(1, 0)?;
1284 Ok((Instruction::Standard(StandardGate::I), vec![]))
1285 }
1286 "cx" | "CX" => {
1287 check_counts(2, 0)?;
1288 Ok((Instruction::Standard(StandardGate::CX), vec![]))
1289 }
1290 "cy" | "CY" => {
1291 check_counts(2, 0)?;
1292 Ok((Instruction::Standard(StandardGate::CY), vec![]))
1293 }
1294 "cz" | "CZ" => {
1295 check_counts(2, 0)?;
1296 Ok((Instruction::Standard(StandardGate::CZ), vec![]))
1297 }
1298 "swap" | "SWAP" => {
1299 check_counts(2, 0)?;
1300 Ok((Instruction::Standard(StandardGate::SWAP), vec![]))
1301 }
1302 "ccx" | "CCX" | "toffoli" | "TOFFOLI" => {
1303 check_counts(3, 0)?;
1304 Ok((Instruction::Standard(StandardGate::CCX), vec![]))
1305 }
1306
1307 _ => Err(QasmParseError::UndefinedGate(name.to_string())),
1308 }
1309 }
1310
1311 fn append_standard_gate(
1313 &self,
1314 circuit: &mut Circuit,
1315 name: &str,
1316 params: &[ParameterValue],
1317 qubits: &[Qubit],
1318 ) -> Result<(), QasmParseError> {
1319 let (instruction, valid_params) =
1320 Self::build_standard_gate_instruction(name, params, qubits)?;
1321 circuit
1322 .append(instruction, qubits.to_vec(), valid_params, None)
1323 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1324 Ok(())
1325 }
1326
1327 fn generation_pass(
1328 &mut self,
1329 program: &OpenQASMProgram,
1330 circuit: &mut Circuit,
1331 reg_start_map: &HashMap<String, usize>,
1332 current_dir: Option<&Path>,
1333 include_stack: &mut Vec<PathBuf>,
1334 ) -> Result<(), QasmParseError> {
1335 for stmt in &program.statements {
1336 match stmt {
1337 Statement::Include(filename) => {
1338 if filename == "qelib1.inc" {
1339 continue;
1340 }
1341
1342 let target_path = self.resolve_include_path(current_dir, filename);
1343 if include_stack.contains(&target_path) {
1344 return Err(Self::include_cycle_error(include_stack, &target_path));
1345 }
1346
1347 let included_program = self.file_cache.get(&target_path).cloned();
1350 if let Some(included) = included_program {
1351 include_stack.push(target_path.clone());
1352 let next_dir = target_path.parent().map(Path::to_path_buf);
1353 self.generation_pass(
1354 &included,
1355 circuit,
1356 reg_start_map,
1357 next_dir.as_deref(),
1358 include_stack,
1359 )?;
1360 include_stack.pop();
1361 } else {
1362 return Err(QasmParseError::ConversionError(format!(
1364 "Included file not found in cache: {:?}",
1365 target_path
1366 )));
1367 }
1368 }
1369 Statement::Barrier(args) => {
1370 let qubits = self.resolve_global_args(args, reg_start_map)?;
1371 circuit
1372 .barrier(qubits)
1373 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1374 }
1375 Statement::Reset(arg) => {
1376 let qubits = self.resolve_global_args_single_or_register(arg, reg_start_map)?;
1378 for qubit in qubits {
1379 circuit
1380 .reset(qubit)
1381 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1382 }
1383 }
1384 Statement::Measure(qarg, carg) => {
1385 self.append_measurement(circuit, qarg, carg, reg_start_map)?;
1386 }
1387 Statement::CustomGate(name, params, args) => {
1388 self.append_gate_invocation(circuit, name, params, args, reg_start_map)?;
1389 }
1390 Statement::If(creg, value, stmt) => {
1391 let condition = self.build_condition(creg, *value)?;
1392 circuit
1393 .if_(condition, |body| {
1394 self.append_conditional_statement(stmt, body, reg_start_map)
1395 .map_err(|e| CircuitError::InvalidOperation(e.to_string()))
1396 })
1397 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1398 }
1399 _ => {} }
1401 }
1402 Ok(())
1403 }
1404
1405 fn append_gate_invocation(
1406 &self,
1407 circuit: &mut Circuit,
1408 name: &str,
1409 params: &[Expression],
1410 args: &[Argument],
1411 reg_start_map: &HashMap<String, usize>,
1412 ) -> Result<(), QasmParseError> {
1413 let param_values = Self::resolve_invocation_params(params)?;
1414 let arg_groups = args
1415 .iter()
1416 .map(|arg| self.resolve_global_args_single_or_register(arg, reg_start_map))
1417 .collect::<Result<Vec<_>, _>>()?;
1418
1419 let width = arg_groups.iter().map(Vec::len).max().ok_or_else(|| {
1420 QasmParseError::InvalidArgument(format!("Gate '{name}' has no arguments"))
1421 })?;
1422
1423 for group in &arg_groups {
1424 if group.len() != 1 && group.len() != width {
1425 return Err(QasmParseError::InvalidArgument(format!(
1426 "Cannot broadcast gate '{name}': argument widths must be 1 or {width}, got {}",
1427 group.len()
1428 )));
1429 }
1430 }
1431
1432 for index in 0..width {
1433 let qubits = arg_groups
1434 .iter()
1435 .map(|group| group[if group.len() == 1 { 0 } else { index }])
1436 .collect::<Vec<_>>();
1437 self.append_single_gate_invocation(circuit, name, param_values.clone(), qubits)?;
1438 }
1439
1440 Ok(())
1441 }
1442
1443 fn resolve_invocation_params(
1444 params: &[Expression],
1445 ) -> Result<SmallVec<[ParameterValue; 3]>, QasmParseError> {
1446 let mut param_values: SmallVec<[ParameterValue; 3]> = smallvec![];
1447 let empty_param_map: HashMap<String, Parameter> = HashMap::new();
1448 for expr in params {
1449 let param = Self::expr_to_parameter(expr, &empty_param_map)?;
1450 param_values.push(ParameterValue::from(param));
1451 }
1452 Ok(param_values)
1453 }
1454
1455 fn append_single_gate_invocation(
1456 &self,
1457 circuit: &mut Circuit,
1458 name: &str,
1459 params: SmallVec<[ParameterValue; 3]>,
1460 qubits: Vec<Qubit>,
1461 ) -> Result<(), QasmParseError> {
1462 if let Some(gate_def) = self.custom_gates.get(name) {
1463 if gate_def.is_opaque {
1464 return Err(QasmParseError::UnsupportedOpaqueGate(name.to_string()));
1465 }
1466 if let Some(ref cg) = gate_def.circuit_gate {
1467 circuit
1468 .append(
1469 Instruction::CircuitGate(Box::new(cg.clone())),
1470 qubits,
1471 params,
1472 None,
1473 )
1474 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1475 return Ok(());
1476 }
1477 return Err(QasmParseError::UndefinedGate(name.to_string()));
1478 }
1479
1480 self.append_standard_gate_to_circuit(circuit, name, ¶ms, &qubits)
1481 }
1482
1483 fn append_measurement(
1484 &self,
1485 circuit: &mut Circuit,
1486 qarg: &Argument,
1487 carg: &Argument,
1488 reg_start_map: &HashMap<String, usize>,
1489 ) -> Result<(), QasmParseError> {
1490 let qubits = self.resolve_global_args_single_or_register(qarg, reg_start_map)?;
1491 let creg_indices = self.resolve_creg_indices(carg)?;
1492
1493 if qubits.len() != creg_indices.len() {
1494 return Err(QasmParseError::InvalidArgument(format!(
1495 "Measure qubit count ({}) does not match classical register count ({})",
1496 qubits.len(),
1497 creg_indices.len()
1498 )));
1499 }
1500
1501 let creg_name = match carg {
1502 Argument::Id(name) | Argument::IndexedId(name, _) => name,
1503 };
1504 let target = *self
1505 .creg_vars
1506 .get(creg_name)
1507 .ok_or_else(|| QasmParseError::UndefinedRegister(creg_name.clone()))?;
1508
1509 if matches!(carg, Argument::Id(_)) {
1510 let result = circuit.measure_bits(qubits)?;
1511 circuit.store(target, result.expr())?;
1512 } else {
1513 let result = circuit.measure(qubits[0])?;
1514 let width = target.ty().width();
1515 let measured_index = creg_indices[0] as u32;
1516 let mut bits = Vec::with_capacity(width as usize);
1517 for index in 0..width {
1518 let bit = if index == measured_index {
1519 result.expr()
1520 } else {
1521 ClassicalExpr::extract_bit(target.expr(), index)
1522 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?
1523 };
1524 bits.push(bit);
1525 }
1526 let packed = ClassicalExpr::pack_bits(bits)
1527 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1528 circuit.store(target, packed)?;
1529 }
1530 Ok(())
1531 }
1532
1533 fn append_conditional_statement(
1534 &mut self,
1535 stmt: &Statement,
1536 circuit: &mut Circuit,
1537 reg_start_map: &HashMap<String, usize>,
1538 ) -> Result<(), QasmParseError> {
1539 match stmt {
1540 Statement::CustomGate(name, params, args) => {
1541 self.append_gate_invocation(circuit, name, params, args, reg_start_map)?;
1542 }
1543 Statement::Reset(arg) => {
1544 let qubits = self.resolve_global_args_single_or_register(arg, reg_start_map)?;
1545 for qubit in qubits {
1546 circuit.reset(qubit)?;
1547 }
1548 }
1549 Statement::Measure(qarg, carg) => {
1550 self.append_measurement(circuit, qarg, carg, reg_start_map)?;
1551 }
1552 Statement::Barrier(_) => {
1553 return Err(QasmParseError::ConversionError(
1554 "Barrier in an OpenQASM 2.0 conditional body is not supported".to_string(),
1555 ));
1556 }
1557 _ => {
1558 return Err(QasmParseError::ConversionError(format!(
1559 "Unsupported statement type in if-body: {:?}",
1560 stmt
1561 )));
1562 }
1563 }
1564 Ok(())
1565 }
1566
1567 fn resolve_global_args(
1568 &self,
1569 args: &[Argument],
1570 reg_start_map: &HashMap<String, usize>,
1571 ) -> Result<Vec<Qubit>, QasmParseError> {
1572 let mut qubits = Vec::new();
1573 for arg in args {
1574 match arg {
1575 Argument::Id(name) => {
1576 if let Some(size) = self.qregs.get(name) {
1578 if let Some(&start_idx) = reg_start_map.get(name) {
1579 for i in 0..*size {
1580 qubits.push(Qubit::new((start_idx as i64 + i) as u32));
1581 }
1582 } else {
1583 return Err(QasmParseError::UndefinedRegister(name.clone()));
1584 }
1585 } else {
1586 return Err(QasmParseError::UndefinedRegister(name.clone()));
1587 }
1588 }
1589 Argument::IndexedId(name, idx) => {
1590 if let Some(&start_idx) = reg_start_map.get(name) {
1591 if let Some(size) = self.qregs.get(name) {
1593 if *idx < 0 || *idx >= *size {
1594 return Err(QasmParseError::UndefinedQubit(format!(
1595 "{}[{}] out of bounds",
1596 name, idx
1597 )));
1598 }
1599 qubits.push(Qubit::new((start_idx as i64 + *idx) as u32));
1600 } else {
1601 return Err(QasmParseError::UndefinedRegister(name.clone()));
1602 }
1603 } else {
1604 return Err(QasmParseError::UndefinedQubit(format!("{}[{}]", name, idx)));
1605 }
1606 }
1607 }
1608 }
1609 Ok(qubits)
1610 }
1611
1612 fn resolve_global_args_single_or_register(
1614 &self,
1615 arg: &Argument,
1616 reg_start_map: &HashMap<String, usize>,
1617 ) -> Result<Vec<Qubit>, QasmParseError> {
1618 let mut qubits = Vec::new();
1619 match arg {
1620 Argument::Id(name) => {
1621 if let Some(size) = self.qregs.get(name) {
1623 if let Some(&start_idx) = reg_start_map.get(name) {
1624 for i in 0..*size {
1625 qubits.push(Qubit::new((start_idx as i64 + i) as u32));
1626 }
1627 } else {
1628 return Err(QasmParseError::UndefinedRegister(name.clone()));
1629 }
1630 } else {
1631 return Err(QasmParseError::UndefinedRegister(name.clone()));
1632 }
1633 }
1634 Argument::IndexedId(name, idx) => {
1635 if let Some(&start_idx) = reg_start_map.get(name) {
1637 if let Some(size) = self.qregs.get(name) {
1638 if *idx < 0 || *idx >= *size {
1639 return Err(QasmParseError::UndefinedQubit(format!(
1640 "{}[{}]",
1641 name, idx
1642 )));
1643 }
1644 qubits.push(Qubit::new((start_idx as i64 + *idx) as u32));
1645 } else {
1646 return Err(QasmParseError::UndefinedRegister(name.clone()));
1647 }
1648 } else {
1649 return Err(QasmParseError::UndefinedQubit(format!("{}[{}]", name, idx)));
1650 }
1651 }
1652 }
1653 Ok(qubits)
1654 }
1655
1656 fn resolve_creg_indices(&self, arg: &Argument) -> Result<Vec<usize>, QasmParseError> {
1658 let mut indices = Vec::new();
1659 match arg {
1660 Argument::Id(name) => {
1661 if let Some(size) = self.cregs.get(name) {
1663 for i in 0..*size {
1664 indices.push(i as usize);
1665 }
1666 } else {
1667 return Err(QasmParseError::UndefinedRegister(format!(
1668 "Classical register '{}' not defined",
1669 name
1670 )));
1671 }
1672 }
1673 Argument::IndexedId(name, idx) => {
1674 let size = self.cregs.get(name).ok_or_else(|| {
1675 QasmParseError::UndefinedRegister(format!(
1676 "Classical register '{}' not defined",
1677 name
1678 ))
1679 })?;
1680 if *idx < 0 || *idx >= *size {
1681 return Err(QasmParseError::InvalidArgument(format!(
1682 "Classical register index {name}[{idx}] is out of bounds for width {size}"
1683 )));
1684 }
1685 indices.push(*idx as usize);
1686 }
1687 }
1688 Ok(indices)
1689 }
1690
1691 fn build_condition(&self, name: &str, value: i64) -> Result<ClassicalExpr, QasmParseError> {
1692 let var = *self
1693 .creg_vars
1694 .get(name)
1695 .ok_or_else(|| QasmParseError::UndefinedRegister(name.to_string()))?;
1696 let width = var.ty().width();
1697
1698 if value < 0 {
1699 return Err(QasmParseError::InvalidArgument(format!(
1700 "Classical condition value must be non-negative, got {value}"
1701 )));
1702 }
1703
1704 let value = value as u128;
1705 if width < 128 && value >= (1u128 << width) {
1706 return Err(QasmParseError::InvalidArgument(format!(
1707 "Condition value {value} does not fit classical register '{name}' width {width}"
1708 )));
1709 }
1710 let lhs = ClassicalExpr::bit_vec_to_uint(var.expr())
1711 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1712 let rhs = ClassicalExpr::uint_literal(width, value)
1713 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1714
1715 ClassicalExpr::eq(lhs, rhs).map_err(|e| QasmParseError::ConversionError(e.to_string()))
1716 }
1717
1718 fn append_standard_gate_to_circuit(
1720 &self,
1721 circuit: &mut Circuit,
1722 name: &str,
1723 params: &[ParameterValue],
1724 qubits: &[Qubit],
1725 ) -> Result<(), QasmParseError> {
1726 let (instruction, valid_params) =
1727 Self::build_standard_gate_instruction(name, params, qubits)?;
1728 circuit
1729 .append(instruction, qubits.to_vec(), valid_params, None)
1730 .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1731 Ok(())
1732 }
1733}
1734
1735fn normalize_path(path: impl AsRef<Path>) -> PathBuf {
1736 let mut normalized = PathBuf::new();
1737 for component in path.as_ref().components() {
1738 match component {
1739 Component::CurDir => {}
1740 Component::ParentDir => {
1741 normalized.pop();
1742 }
1743 Component::Normal(part) => normalized.push(part),
1744 Component::RootDir => normalized.push(component.as_os_str()),
1745 Component::Prefix(prefix) => normalized.push(prefix.as_os_str()),
1746 }
1747 }
1748 normalized
1749}
1750
1751#[cfg(test)]
1752#[path = "./load_test.rs"]
1753mod load_test;