1use crate::circuit::gate::circuit_gate::{CircuitGate, FrozenCircuit};
14use crate::circuit::{
15 Circuit, ClassicalExpr, ClassicalType, ClassicalVar, Instruction, Parameter, ParameterValue,
16 Qubit, StandardGate, UnitaryGate,
17};
18use crate::ir::io_errors_equivalent;
19use oq3_semantics::asg::{
20 self, ArithOp, BinaryOp, CmpOp, Expr, ForIterable, GateModifier, GateOperand, IndexOperator,
21 LValue, Literal, Stmt, TExpr, UnaryOp,
22};
23use oq3_semantics::context::Context;
24use oq3_semantics::semantic_error::{SemanticErrorKind, SemanticErrorList};
25use oq3_semantics::symbols::{SymbolId, SymbolIdResult, SymbolTable, SymbolType};
26use oq3_semantics::syntax_to_semantics;
27use oq3_semantics::types::{ArrayDims, Type};
28use regex::Regex;
29use std::cell::RefCell;
30use std::collections::{HashMap, HashSet};
31use std::fs;
32use std::io;
33use std::panic::{AssertUnwindSafe, catch_unwind};
34use std::path::{Path, PathBuf};
35use std::sync::OnceLock;
36
37const DEFAULT_MAX_RECURSION_DEPTH: usize = 100;
38
39pub fn load<P: AsRef<Path>>(path: P) -> Result<Circuit, Qasm3ParseError> {
60 let path = path.as_ref();
61 let source = fs::read_to_string(path).map_err(Qasm3ParseError::IoError)?;
62 let source = prepare_source(&source)?;
63 let source = inject_extension_gate_definitions(&source);
64 let search_paths = path.parent().map(|parent| vec![parent.to_path_buf()]);
65 let result = catch_unwind(AssertUnwindSafe(|| {
66 syntax_to_semantics::parse_source_string(&source, path.to_str(), search_paths.as_deref())
67 }))
68 .map_err(frontend_panic_error)?;
69 let has_syntax_errors = result.any_syntax_errors();
70 convert_parse_result(result.take_context(), has_syntax_errors, &source)
71}
72
73pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Circuit, Qasm3ParseError> {
78 load(path)
79}
80
81pub fn loads(source: &str) -> Result<Circuit, Qasm3ParseError> {
102 let source = prepare_source(source)?;
103 let source = inject_extension_gate_definitions(&source);
104 let result = catch_unwind(AssertUnwindSafe(|| {
105 syntax_to_semantics::parse_source_string(&source, Some("qasm3_source"), None::<&[PathBuf]>)
106 }))
107 .map_err(frontend_panic_error)?;
108 let has_syntax_errors = result.any_syntax_errors();
109 convert_parse_result(result.take_context(), has_syntax_errors, &source)
110}
111
112pub fn from_str(source: &str) -> Result<Circuit, Qasm3ParseError> {
116 loads(source)
117}
118
119fn convert_parse_result(
120 context: Context,
121 has_syntax_errors: bool,
122 source: &str,
123) -> Result<Circuit, Qasm3ParseError> {
124 if has_syntax_errors {
125 return Err(Qasm3ParseError::ParseError(
126 "OpenQASM 3 parser reported syntax errors".to_string(),
127 ));
128 }
129 let (program, semantic_errors, symbols) = context.as_tuple();
130 if semantic_errors.any_semantic_errors()
131 && !only_scalar_measurement_frontend_errors(&semantic_errors, source)
132 {
133 return Err(Qasm3ParseError::SemanticError(
134 "OpenQASM 3 parser reported semantic errors".to_string(),
135 ));
136 }
137 let mut lowering = LoweringContext::new(&symbols);
138 lowering.lower_program(&program)
139}
140
141fn frontend_panic_error(payload: Box<dyn std::any::Any + Send>) -> Qasm3ParseError {
142 let message = payload
143 .downcast_ref::<&str>()
144 .copied()
145 .or_else(|| payload.downcast_ref::<String>().map(String::as_str))
146 .unwrap_or("unknown OpenQASM front-end failure");
147 Qasm3ParseError::ParseError(format!("OpenQASM 3 front-end failed: {message}"))
148}
149
150fn reject_end_statements(stmts: &[Stmt]) -> Result<(), Qasm3ParseError> {
151 let mut pending = vec![stmts];
154 while let Some(stmts) = pending.pop() {
155 for stmt in stmts {
156 match stmt {
157 Stmt::End => return Err(Qasm3ParseError::UnsupportedFeature("end".to_string())),
158 Stmt::Block(block) => pending.push(block.statements()),
159 Stmt::If(op) => {
160 pending.push(op.then_branch().statements());
161 if let Some(block) = op.else_branch() {
162 pending.push(block.statements());
163 }
164 }
165 Stmt::While(op) => pending.push(op.loop_body().statements()),
166 Stmt::ForStmt(op) => pending.push(op.loop_body().statements()),
167 Stmt::SwitchCaseStmt(op) => {
168 for case in op.cases() {
169 pending.push(case.statements());
170 }
171 if let Some(stmts) = op.default_block() {
172 pending.push(stmts);
173 }
174 }
175 Stmt::GateDefinition(def) => pending.push(def.block().statements()),
176 Stmt::DefStmt(def) => pending.push(def.block().statements()),
177 Stmt::AnnotatedStmt(op) => pending.push(std::slice::from_ref(op.statement())),
178 _ => {}
179 }
180 }
181 }
182 Ok(())
183}
184
185fn prepare_source(source: &str) -> Result<String, Qasm3ParseError> {
186 let layout = inspect_source(source)?;
187 reject_empty_pragmas(&layout.code_mask)?;
188
189 let source = normalize_single_quoted_includes(source, &layout)?;
190 let source = normalize_openqasm3_header(&source, &layout.code_mask);
191 let source = normalize_top_level_anonymous_scopes(&source)?;
192 normalize_bit_boolean_initializers(&source)
193}
194
195#[derive(Debug)]
196struct SourceLayout {
197 code_mask: String,
198 strings: Vec<StringSpan>,
199}
200
201#[derive(Clone, Copy, Debug)]
202struct StringSpan {
203 start: usize,
204 end: usize,
205 quote: u8,
206}
207
208#[derive(Clone, Copy)]
209enum ScanState {
210 Code,
211 LineComment,
212 BlockComment,
213 String { start: usize, quote: u8 },
214}
215
216fn inspect_source(source: &str) -> Result<SourceLayout, Qasm3ParseError> {
217 let bytes = source.as_bytes();
218 let mut mask = bytes.to_vec();
219 let mut strings = Vec::new();
220 let mut state = ScanState::Code;
221 let mut index = 0usize;
222
223 while index < bytes.len() {
224 match state {
225 ScanState::Code if bytes[index..].starts_with(b"//") => {
226 mask[index] = b' ';
227 mask[index + 1] = b' ';
228 state = ScanState::LineComment;
229 index += 2;
230 }
231 ScanState::Code if bytes[index..].starts_with(b"/*") => {
232 mask[index] = b' ';
233 mask[index + 1] = b' ';
234 state = ScanState::BlockComment;
235 index += 2;
236 }
237 ScanState::Code if matches!(bytes[index], b'\'' | b'"') => {
238 let quote = bytes[index];
239 mask[index] = b' ';
240 state = ScanState::String {
241 start: index,
242 quote,
243 };
244 index += 1;
245 }
246 ScanState::Code => index += 1,
247 ScanState::LineComment if matches!(bytes[index], b'\n' | b'\r') => {
248 state = ScanState::Code;
249 index += 1;
250 }
251 ScanState::LineComment => {
252 mask[index] = b' ';
253 index += 1;
254 }
255 ScanState::BlockComment if bytes[index..].starts_with(b"/*") => {
256 return Err(Qasm3ParseError::ParseError(
257 "nested block comments are not valid OpenQASM 3".to_string(),
258 ));
259 }
260 ScanState::BlockComment if bytes[index..].starts_with(b"*/") => {
261 mask[index] = b' ';
262 mask[index + 1] = b' ';
263 state = ScanState::Code;
264 index += 2;
265 }
266 ScanState::BlockComment => {
267 if !matches!(bytes[index], b'\n' | b'\r') {
268 mask[index] = b' ';
269 }
270 index += 1;
271 }
272 ScanState::String { start, quote } if bytes[index] == quote => {
273 mask[index] = b' ';
274 strings.push(StringSpan {
275 start,
276 end: index,
277 quote,
278 });
279 state = ScanState::Code;
280 index += 1;
281 }
282 ScanState::String { .. } if matches!(bytes[index], b'\n' | b'\r') => {
283 return Err(Qasm3ParseError::ParseError(
284 "unterminated string literal".to_string(),
285 ));
286 }
287 ScanState::String { .. } => {
288 mask[index] = b' ';
289 index += 1;
290 }
291 }
292 }
293
294 match state {
295 ScanState::BlockComment => {
296 return Err(Qasm3ParseError::ParseError(
297 "unterminated block comment".to_string(),
298 ));
299 }
300 ScanState::String { .. } => {
301 return Err(Qasm3ParseError::ParseError(
302 "unterminated string literal".to_string(),
303 ));
304 }
305 ScanState::Code | ScanState::LineComment => {}
306 }
307
308 let code_mask = String::from_utf8(mask).expect("source mask preserves UTF-8");
309 Ok(SourceLayout { code_mask, strings })
310}
311
312fn reject_empty_pragmas(code_mask: &str) -> Result<(), Qasm3ParseError> {
313 if code_mask
314 .lines()
315 .any(|line| matches!(line.trim(), "pragma" | "#pragma"))
316 {
317 return Err(Qasm3ParseError::ParseError(
318 "pragma requires content on the same line".to_string(),
319 ));
320 }
321 Ok(())
322}
323
324fn normalize_single_quoted_includes(
325 source: &str,
326 layout: &SourceLayout,
327) -> Result<String, Qasm3ParseError> {
328 let mut bytes = source.as_bytes().to_vec();
329 for span in layout.strings.iter().filter(|span| span.quote == b'\'') {
330 let line_start = layout.code_mask[..span.start]
331 .rfind('\n')
332 .map_or(0, |index| index + 1);
333 let prefix = layout.code_mask[line_start..span.start].trim_end();
334 if prefix.split_whitespace().last() != Some("include") {
335 continue;
336 }
337 let line_end = layout.code_mask[span.end + 1..]
338 .find('\n')
339 .map_or(layout.code_mask.len(), |offset| span.end + 1 + offset);
340 if !layout.code_mask[span.end + 1..line_end]
341 .trim_start()
342 .starts_with(';')
343 {
344 continue;
345 }
346 if source.as_bytes()[span.start + 1..span.end].contains(&b'"') {
347 return Err(Qasm3ParseError::UnsupportedFeature(
348 "single-quoted include path containing a double quote".to_string(),
349 ));
350 }
351 bytes[span.start] = b'"';
352 bytes[span.end] = b'"';
353 }
354 Ok(String::from_utf8(bytes).expect("quote normalization preserves UTF-8"))
355}
356
357fn normalize_openqasm3_header(source: &str, code_mask: &str) -> String {
358 let Some(captures) = short_header_regex().captures(code_mask) else {
359 return source.to_string();
360 };
361 let Some(major) = captures.name("major") else {
362 return source.to_string();
363 };
364 let mut normalized = source.to_string();
365 normalized.insert_str(major.end(), ".0");
366 normalized
367}
368
369fn normalize_top_level_anonymous_scopes(source: &str) -> Result<String, Qasm3ParseError> {
370 let layout = inspect_source(source)?;
371 let bytes = layout.code_mask.as_bytes();
372 let mut depth = 0usize;
373 let mut statement_start = 0usize;
374 let mut insertions = Vec::new();
375
376 for (index, byte) in bytes.iter().copied().enumerate() {
377 match byte {
378 b';' if depth == 0 => statement_start = index + 1,
379 b'{' if depth == 0 => {
380 if layout.code_mask[statement_start..index].trim().is_empty() {
381 insertions.push(index);
382 }
383 depth = 1;
384 }
385 b'{' => depth += 1,
386 b'}' if depth > 0 => {
387 depth -= 1;
388 if depth == 0 {
389 statement_start = index + 1;
390 }
391 }
392 _ => {}
393 }
394 }
395
396 let mut normalized = source.to_string();
397 for index in insertions.into_iter().rev() {
398 normalized.insert_str(index, "if (true) ");
401 }
402 Ok(normalized)
403}
404
405fn normalize_bit_boolean_initializers(source: &str) -> Result<String, Qasm3ParseError> {
406 let layout = inspect_source(source)?;
407 let mut insertions = bit_boolean_initializer_regex()
408 .captures_iter(&layout.code_mask)
409 .filter_map(|captures| captures.name("value"))
410 .flat_map(|value| [(value.start(), "bit("), (value.end(), ")")])
411 .collect::<Vec<_>>();
412 insertions.sort_unstable_by_key(|(index, _)| *index);
413
414 let mut normalized = source.to_string();
415 for (index, value) in insertions.into_iter().rev() {
416 normalized.insert_str(index, value);
417 }
418 Ok(normalized)
419}
420
421fn inject_extension_gate_definitions(source: &str) -> String {
422 let definitions = [
423 (
424 "rxx",
425 "gate rxx(theta) a,b { h a; h b; cx a,b; rz(theta) b; cx a,b; h a; h b; }",
426 ),
427 (
428 "ryy",
429 "gate ryy(theta) a,b { rx(pi/2) a; rx(pi/2) b; cx a,b; rz(theta) b; cx a,b; rx(-pi/2) a; rx(-pi/2) b; }",
430 ),
431 (
432 "rzz",
433 "gate rzz(theta) a,b { cx a,b; rz(theta) b; cx a,b; }",
434 ),
435 (
436 "rzx",
437 "gate rzx(theta) a,b { h b; cx a,b; rz(theta) b; cx a,b; h b; }",
438 ),
439 (
440 "fsim",
441 "gate fsim(theta,phi) a,b { rxx(theta) a,b; ryy(theta) a,b; gphase(-phi/4); rz(-phi/2) a; rz(-phi/2) b; rzz(phi/2) a,b; }",
442 ),
443 ];
444 let injected = definitions
445 .iter()
446 .filter_map(|(name, definition)| {
447 if has_gate_definition(source, name) || !has_gate_call(source, name) {
448 None
449 } else {
450 Some(*definition)
451 }
452 })
453 .collect::<Vec<_>>();
454 if injected.is_empty() {
455 return source.to_string();
456 }
457
458 let insertion = format!("\n{}\n", injected.join("\n\n"));
459 let mut lines = source
460 .lines()
461 .map(ToString::to_string)
462 .collect::<Vec<String>>();
463 let insert_after = lines
464 .iter()
465 .rposition(|line| line.trim() == "include \"stdgates.inc\";")
466 .or_else(|| {
467 lines
468 .iter()
469 .position(|line| line.trim_start().starts_with("OPENQASM "))
470 });
471 if let Some(index) = insert_after {
472 lines.insert(index + 1, insertion);
473 } else {
474 lines.insert(0, insertion);
475 }
476
477 let mut rewritten = lines.join("\n");
478 if source.ends_with('\n') {
479 rewritten.push('\n');
480 }
481 rewritten
482}
483
484fn has_gate_definition(source: &str, name: &str) -> bool {
485 gate_definition_regex(name).is_match(source)
486}
487
488fn has_gate_call(source: &str, name: &str) -> bool {
489 gate_call_regex(name).is_match(source)
490}
491
492fn only_scalar_measurement_frontend_errors(errors: &SemanticErrorList, source: &str) -> bool {
493 errors.include_errors().is_empty()
494 && !errors.is_empty()
495 && errors
496 .iter()
497 .all(|error| scalar_measurement_frontend_error(error, source))
498}
499
500fn scalar_measurement_frontend_error(
501 error: &oq3_semantics::semantic_error::SemanticError,
502 source: &str,
503) -> bool {
504 if !matches!(error.kind(), SemanticErrorKind::IncompatibleTypesError) {
505 return false;
506 }
507 let range = error.range();
508 let start = u32::from(range.start()) as usize;
509 let end = u32::from(range.end()) as usize;
510 let Some(snippet) = source.get(start..end) else {
511 return false;
512 };
513 let Some(captures) = scalar_measurement_error_regex().captures(snippet) else {
514 return false;
515 };
516 let Some(target) = captures.name("target") else {
517 return false;
518 };
519 scalar_bit_declaration_count(source, target.as_str()) == 1
520}
521
522fn scalar_bit_declaration_count(source: &str, identifier: &str) -> usize {
523 let Ok(layout) = inspect_source(source) else {
524 return 0;
525 };
526 let regex = Regex::new(&format!(
527 r"(?m)^[\t ]*bit[\t ]+{}[\t ]*;",
528 regex::escape(identifier)
529 ))
530 .expect("escaped identifier produces a valid regex");
531 regex.find_iter(&layout.code_mask).count()
532}
533
534fn short_header_regex() -> &'static Regex {
535 static REGEX: OnceLock<Regex> = OnceLock::new();
536 REGEX.get_or_init(|| {
537 Regex::new(r"\A\s*OPENQASM\s+(?P<major>3)[\t \r\n]*;").expect("valid short header regex")
538 })
539}
540
541fn bit_boolean_initializer_regex() -> &'static Regex {
542 static REGEX: OnceLock<Regex> = OnceLock::new();
543 REGEX.get_or_init(|| {
544 Regex::new(
545 r"(?m)^[\t ]*bit[\t ]+[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}_]*[\t ]*=[\t ]*(?P<value>true|false)[\t ]*;",
546 )
547 .expect("valid bit boolean initializer regex")
548 })
549}
550
551fn scalar_measurement_error_regex() -> &'static Regex {
552 static REGEX: OnceLock<Regex> = OnceLock::new();
553 REGEX.get_or_init(|| {
554 Regex::new(
555 r"(?s)^\s*(?P<target>[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}_]*)\s*=\s*measure\s+[\p{L}\p{Nl}_][\p{L}\p{Nl}\p{Nd}_]*\s*\[\s*\d+\s*\]\s*;?\s*$",
556 )
557 .expect("valid scalar measurement compatibility regex")
558 })
559}
560
561fn gate_definition_regex(name: &str) -> Regex {
562 Regex::new(&format!(r"(?m)^\s*gate\s+{}\b", regex::escape(name)))
563 .expect("valid gate definition regex")
564}
565
566fn gate_call_regex(name: &str) -> Regex {
567 Regex::new(&format!(
568 r"(?m)^\s*{}\s*(?:\(|[A-Za-z_])",
569 regex::escape(name)
570 ))
571 .expect("valid gate call regex")
572}
573
574#[derive(Debug)]
580pub enum Qasm3ParseError {
581 IoError(io::Error),
583 ParseError(String),
585 SemanticError(String),
588 ConversionError(String),
590 UnsupportedFeature(String),
592 UndefinedSymbol(String),
594 UndefinedGate(String),
596 TypeError(String),
598 InvalidArgument(String),
600 MismatchedQubitCount { expected: usize, actual: usize },
602 MismatchedParameterCount { expected: usize, actual: usize },
604 RecursionLimitExceeded(String),
606 CircularGateDependency { gate: String, dependency: String },
608}
609
610impl PartialEq for Qasm3ParseError {
611 fn eq(&self, other: &Self) -> bool {
612 match (self, other) {
613 (Self::IoError(lhs), Self::IoError(rhs)) => io_errors_equivalent(lhs, rhs),
614 (Self::ParseError(lhs), Self::ParseError(rhs)) => lhs == rhs,
615 (Self::SemanticError(lhs), Self::SemanticError(rhs)) => lhs == rhs,
616 (Self::ConversionError(lhs), Self::ConversionError(rhs)) => lhs == rhs,
617 (Self::UnsupportedFeature(lhs), Self::UnsupportedFeature(rhs)) => lhs == rhs,
618 (Self::UndefinedSymbol(lhs), Self::UndefinedSymbol(rhs)) => lhs == rhs,
619 (Self::UndefinedGate(lhs), Self::UndefinedGate(rhs)) => lhs == rhs,
620 (Self::TypeError(lhs), Self::TypeError(rhs)) => lhs == rhs,
621 (Self::InvalidArgument(lhs), Self::InvalidArgument(rhs)) => lhs == rhs,
622 (
623 Self::MismatchedQubitCount {
624 expected: lhs_expected,
625 actual: lhs_actual,
626 },
627 Self::MismatchedQubitCount {
628 expected: rhs_expected,
629 actual: rhs_actual,
630 },
631 ) => lhs_expected == rhs_expected && lhs_actual == rhs_actual,
632 (
633 Self::MismatchedParameterCount {
634 expected: lhs_expected,
635 actual: lhs_actual,
636 },
637 Self::MismatchedParameterCount {
638 expected: rhs_expected,
639 actual: rhs_actual,
640 },
641 ) => lhs_expected == rhs_expected && lhs_actual == rhs_actual,
642 (Self::RecursionLimitExceeded(lhs), Self::RecursionLimitExceeded(rhs)) => lhs == rhs,
643 (
644 Self::CircularGateDependency {
645 gate: lhs_gate,
646 dependency: lhs_dependency,
647 },
648 Self::CircularGateDependency {
649 gate: rhs_gate,
650 dependency: rhs_dependency,
651 },
652 ) => lhs_gate == rhs_gate && lhs_dependency == rhs_dependency,
653 _ => false,
654 }
655 }
656}
657
658impl std::fmt::Display for Qasm3ParseError {
659 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
660 match self {
661 Self::IoError(error) => write!(f, "IO error: {error}"),
662 Self::ParseError(s) => write!(f, "Parse error: {s}"),
663 Self::SemanticError(s) => write!(f, "Semantic error: {s}"),
664 Self::ConversionError(s) => write!(f, "Conversion error: {s}"),
665 Self::UnsupportedFeature(s) => write!(f, "Unsupported OpenQASM 3 feature: {s}"),
666 Self::UndefinedSymbol(s) => write!(f, "Undefined symbol: {s}"),
667 Self::UndefinedGate(s) => write!(f, "Undefined gate: {s}"),
668 Self::TypeError(s) => write!(f, "Type error: {s}"),
669 Self::InvalidArgument(s) => write!(f, "Invalid argument: {s}"),
670 Self::MismatchedQubitCount { expected, actual } => {
671 write!(
672 f,
673 "Mismatched qubit count: expected {expected}, got {actual}"
674 )
675 }
676 Self::MismatchedParameterCount { expected, actual } => {
677 write!(
678 f,
679 "Mismatched parameter count: expected {expected}, got {actual}"
680 )
681 }
682 Self::RecursionLimitExceeded(s) => write!(f, "Recursion limit exceeded: {s}"),
683 Self::CircularGateDependency { gate, dependency } => write!(
684 f,
685 "Circular gate dependency detected: '{gate}' depends on '{dependency}'"
686 ),
687 }
688 }
689}
690
691impl std::error::Error for Qasm3ParseError {
692 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
693 match self {
694 Self::IoError(error) => Some(error),
695 _ => None,
696 }
697 }
698}
699
700impl From<crate::circuit::CircuitError> for Qasm3ParseError {
701 fn from(value: crate::circuit::CircuitError) -> Self {
702 Self::ConversionError(value.to_string())
703 }
704}
705
706#[derive(Clone, Debug)]
707enum QuantumBinding {
708 Qubit(Qubit),
709 Register(Vec<Qubit>),
710}
711
712impl QuantumBinding {
713 fn qubits(&self) -> Vec<Qubit> {
714 match self {
715 Self::Qubit(q) => vec![*q],
716 Self::Register(qs) => qs.clone(),
717 }
718 }
719}
720
721#[derive(Clone, Debug)]
722enum GateDef {
723 Circuit(CircuitGate),
724 Opaque {
725 name: String,
726 qubits: usize,
727 params: usize,
728 },
729 Source(asg::GateDefinition),
730}
731
732struct LoweringContext<'a> {
733 symbols: &'a SymbolTable,
734 quantum: HashMap<SymbolId, QuantumBinding>,
735 classical: HashMap<SymbolId, ClassicalVar>,
736 gates: HashMap<SymbolId, GateDef>,
737 loop_constants: HashMap<SymbolId, u128>,
738 compiling_gates: HashSet<SymbolId>,
739 recursion_depth: usize,
740 max_recursion_depth: usize,
741}
742
743impl<'a> LoweringContext<'a> {
744 fn new(symbols: &'a SymbolTable) -> Self {
745 Self {
746 symbols,
747 quantum: HashMap::new(),
748 classical: HashMap::new(),
749 gates: HashMap::new(),
750 loop_constants: HashMap::new(),
751 compiling_gates: HashSet::new(),
752 recursion_depth: 0,
753 max_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
754 }
755 }
756
757 fn lower_program(&mut self, program: &asg::Program) -> Result<Circuit, Qasm3ParseError> {
758 if let Some(version) = program.version()
759 && version.major() != 3
760 {
761 return Err(Qasm3ParseError::UnsupportedFeature(format!(
762 "OPENQASM major version {}",
763 version.major()
764 )));
765 }
766
767 reject_end_statements(program.stmts())?;
768 let total_qubits = self.discover_quantum(program.stmts())?;
769 self.discover_gates(program.stmts())?;
770
771 let mut circuit = Circuit::new(total_qubits);
772
773 for stmt in program.stmts() {
774 self.lower_stmt(stmt, &mut circuit)?;
775 }
776
777 Ok(circuit)
778 }
779
780 fn discover_quantum(&mut self, stmts: &[Stmt]) -> Result<usize, Qasm3ParseError> {
781 let mut next = 0usize;
782 for stmt in stmts {
783 if let Stmt::DeclareQuantum(decl) = stmt {
784 let id = self.symbol_id(decl.name())?;
785 let ty = self.symbol_type(&id);
786 let width = match ty {
787 Type::Qubit => 1,
788 Type::QubitArray(ArrayDims::D1(width)) => *width,
789 _ => {
790 return Err(Qasm3ParseError::TypeError(format!(
791 "quantum declaration '{}' has type {:?}",
792 self.symbol_name(&id),
793 ty
794 )));
795 }
796 };
797 let qubits = (next..next + width)
798 .map(|index| Qubit::new(index as u32))
799 .collect::<Vec<_>>();
800 next += width;
801 let binding = if width == 1 {
802 QuantumBinding::Qubit(qubits[0])
803 } else {
804 QuantumBinding::Register(qubits)
805 };
806 self.quantum.insert(id, binding);
807 }
808 }
809 Ok(next)
810 }
811
812 fn discover_gates(&mut self, stmts: &[Stmt]) -> Result<(), Qasm3ParseError> {
813 for stmt in stmts {
814 match stmt {
815 Stmt::GateDefinition(def) => {
816 let id = self.symbol_id(def.name())?;
817 self.gates.insert(id, GateDef::Source(def.clone()));
818 }
819 Stmt::Include(_) => {}
820 _ => {}
821 }
822 }
823
824 for (name, id, params, qubits) in self.symbols.gates() {
825 self.gates.entry(id).or_insert_with(|| GateDef::Opaque {
826 name: name.to_string(),
827 qubits,
828 params,
829 });
830 }
831 Ok(())
832 }
833
834 fn lower_classical_declaration(
835 &mut self,
836 decl: &asg::DeclareClassical,
837 circuit: &mut Circuit,
838 ) -> Result<(), Qasm3ParseError> {
839 let id = self.symbol_id(decl.name())?;
840 let ty = self.classical_type(self.symbol_type(&id))?;
841 let var = circuit.var(ty);
842 self.classical.insert(id, var);
843 if let Some(initializer) = decl.initializer() {
844 let value = self.lower_classical_expr(initializer)?;
845 circuit.store(var, value)?;
846 }
847 Ok(())
848 }
849
850 fn lower_stmt(&mut self, stmt: &Stmt, circuit: &mut Circuit) -> Result<(), Qasm3ParseError> {
851 match stmt {
852 Stmt::DeclareQuantum(_) | Stmt::Include(_) => Ok(()),
853 Stmt::DeclareClassical(decl) => self.lower_classical_declaration(decl, circuit),
854 Stmt::GateDefinition(_) => Ok(()),
855 Stmt::GateCall(call) => self.lower_gate_call(call, circuit),
856 Stmt::GPhaseCall(call) => {
857 let phase = self.lower_angle(call.arg())?;
858 let new_phase = circuit.global_phase() + phase;
859 circuit.set_global_phase(new_phase);
860 Ok(())
861 }
862 Stmt::ModifiedGPhaseCall(_) => Err(Qasm3ParseError::UnsupportedFeature(
863 "modified gphase".to_string(),
864 )),
865 Stmt::Barrier(barrier) => {
866 let qubits = match barrier.qubits() {
867 Some(exprs) => self.expand_qubit_exprs(exprs)?,
868 None => circuit.qubits(),
869 };
870 circuit.barrier(qubits)?;
871 Ok(())
872 }
873 Stmt::Reset(reset) => {
874 for qubit in self.expand_qubit_expr(reset.gate_operand())? {
875 circuit.reset(qubit)?;
876 }
877 Ok(())
878 }
879 Stmt::Assignment(assignment) => self.lower_assignment(assignment, circuit),
880 Stmt::ExprStmt(expr) => {
881 if let Expr::MeasureExpression(measure) = expr.expression() {
882 for qubit in self.expand_qubit_expr(measure.operand())? {
883 circuit.measure(qubit)?;
884 }
885 Ok(())
886 } else {
887 Err(Qasm3ParseError::UnsupportedFeature(
888 "expression statement".to_string(),
889 ))
890 }
891 }
892 Stmt::If(op) => {
893 let condition = self.lower_condition(op.condition())?;
894 if condition.is_bool_true() {
895 return self.lower_stmt_slice(op.then_branch().statements(), circuit);
896 }
897 if condition.is_bool_false() {
898 return op.else_branch().map_or(Ok(()), |else_branch| {
899 self.lower_stmt_slice(else_branch.statements(), circuit)
900 });
901 }
902 if let Some(else_branch) = op.else_branch() {
903 let ctx = RefCell::new(self);
904 circuit.if_else(
905 condition,
906 |then_circuit| ctx.borrow_mut().lower_block(op.then_branch(), then_circuit),
907 |else_circuit| ctx.borrow_mut().lower_block(else_branch, else_circuit),
908 )?;
909 } else {
910 circuit.if_(condition, |then_circuit| {
911 self.lower_block(op.then_branch(), then_circuit)
912 })?;
913 }
914 Ok(())
915 }
916 Stmt::While(op) => {
917 let condition = self.lower_condition(op.condition())?;
918 circuit.while_(condition, |body| self.lower_block(op.loop_body(), body))?;
919 Ok(())
920 }
921 Stmt::ForStmt(op) => self.lower_for(op, circuit),
922 Stmt::SwitchCaseStmt(op) => self.lower_switch(op, circuit),
923 Stmt::Break => {
924 circuit.break_loop()?;
925 Ok(())
926 }
927 Stmt::Continue => {
928 circuit.continue_loop()?;
929 Ok(())
930 }
931 Stmt::Block(block) => {
932 self.lower_block(block, circuit)?;
933 Ok(())
934 }
935 Stmt::Pragma(_) | Stmt::AnnotatedStmt(_) => Err(Qasm3ParseError::UnsupportedFeature(
936 "pragma or annotation".to_string(),
937 )),
938 Stmt::Delay(_) => Err(Qasm3ParseError::UnsupportedFeature(
939 "delay/timing".to_string(),
940 )),
941 Stmt::DefStmt(_) => Err(Qasm3ParseError::UnsupportedFeature(
942 "subroutine def".to_string(),
943 )),
944 Stmt::Extern => Err(Qasm3ParseError::UnsupportedFeature("extern".to_string())),
945 Stmt::Cal | Stmt::DefCal => Err(Qasm3ParseError::UnsupportedFeature(
946 "calibration/defcal".to_string(),
947 )),
948 Stmt::InputDeclaration(input) => {
949 let id = self.symbol_id(input.name())?;
950 match self.symbol_type(&id) {
951 Type::Angle(_, _) | Type::Float(_, _) => Ok(()),
952 ty => Err(Qasm3ParseError::UnsupportedFeature(format!(
953 "input declaration of type {ty:?}"
954 ))),
955 }
956 }
957 Stmt::OutputDeclaration(_) => Err(Qasm3ParseError::UnsupportedFeature(
958 "output declaration".to_string(),
959 )),
960 Stmt::Alias(_) => Err(Qasm3ParseError::UnsupportedFeature("alias".to_string())),
961 Stmt::DeclareHardwareQubit(_) => Err(Qasm3ParseError::UnsupportedFeature(
962 "hardware qubit".to_string(),
963 )),
964 Stmt::Box => Err(Qasm3ParseError::UnsupportedFeature("box".to_string())),
965 Stmt::End => Err(Qasm3ParseError::UnsupportedFeature("end".to_string())),
966 Stmt::NullStmt => Ok(()),
967 Stmt::OldStyleDeclaration => Err(Qasm3ParseError::UnsupportedFeature(
968 "old-style declaration".to_string(),
969 )),
970 }
971 }
972
973 fn lower_block(
974 &mut self,
975 block: &asg::Block,
976 circuit: &mut Circuit,
977 ) -> Result<(), crate::circuit::CircuitError> {
978 self.lower_stmt_slice(block.statements(), circuit)
979 .map_err(|e| crate::circuit::CircuitError::InvalidOperation(e.to_string()))
980 }
981
982 fn lower_stmt_slice(
983 &mut self,
984 stmts: &[Stmt],
985 circuit: &mut Circuit,
986 ) -> Result<(), Qasm3ParseError> {
987 for stmt in stmts {
988 self.lower_stmt(stmt, circuit)?;
989 }
990 Ok(())
991 }
992
993 fn lower_assignment(
994 &mut self,
995 assignment: &asg::Assignment,
996 circuit: &mut Circuit,
997 ) -> Result<(), Qasm3ParseError> {
998 match assignment.lvalue() {
999 LValue::Identifier(target_id_result) => {
1000 let target_id = self.symbol_id(target_id_result)?;
1001 let Some(target) = self.classical.get(&target_id).copied() else {
1002 return Err(Qasm3ParseError::UndefinedSymbol(
1003 self.symbol_name(&target_id),
1004 ));
1005 };
1006
1007 if let Expr::MeasureExpression(measure) = assignment.rvalue().expression() {
1008 let qubits = self.expand_qubit_expr(measure.operand())?;
1009 if qubits.len() == 1 {
1010 circuit.measure_into(qubits[0], target)?;
1011 } else {
1012 circuit.measure_bits_into(qubits, target)?;
1013 }
1014 return Ok(());
1015 }
1016
1017 let value = self.lower_classical_expr(assignment.rvalue())?;
1018 circuit.store(target, value)?;
1019 Ok(())
1020 }
1021 LValue::IndexedIdentifier(indexed) => {
1022 self.lower_indexed_assignment(indexed, assignment.rvalue(), circuit)
1023 }
1024 }
1025 }
1026
1027 fn lower_indexed_assignment(
1028 &mut self,
1029 indexed: &asg::IndexedIdentifier,
1030 rvalue: &TExpr,
1031 circuit: &mut Circuit,
1032 ) -> Result<(), Qasm3ParseError> {
1033 let target = self.indexed_classical_target(indexed)?;
1034 let bit = match rvalue.expression() {
1035 Expr::MeasureExpression(measure) => {
1036 let qubits = self.expand_qubit_expr(measure.operand())?;
1037 if qubits.len() != 1 {
1038 return Err(Qasm3ParseError::MismatchedQubitCount {
1039 expected: 1,
1040 actual: qubits.len(),
1041 });
1042 }
1043 circuit.measure(qubits[0])?.expr()
1044 }
1045 _ => self.lower_classical_expr(rvalue)?,
1046 };
1047 self.store_indexed_classical_bit(target, bit, circuit)
1048 }
1049
1050 fn indexed_classical_target(
1051 &self,
1052 indexed: &asg::IndexedIdentifier,
1053 ) -> Result<(ClassicalVar, u32), Qasm3ParseError> {
1054 let id = self.symbol_id(indexed.identifier())?;
1055 let Some(var) = self.classical.get(&id).copied() else {
1056 return Err(Qasm3ParseError::UndefinedSymbol(self.symbol_name(&id)));
1057 };
1058 if indexed.indexes().len() != 1 {
1059 return Err(Qasm3ParseError::UnsupportedFeature(
1060 "multi-dimensional classical assignment index".to_string(),
1061 ));
1062 }
1063 let ClassicalType::BitVec(width) = var.ty() else {
1064 return Err(Qasm3ParseError::TypeError(format!(
1065 "indexed assignment target '{}' must be bit array, got {:?}",
1066 self.symbol_name(&id),
1067 var.ty()
1068 )));
1069 };
1070 let index = self.single_index(&indexed.indexes()[0])?;
1071 if index >= width.get() {
1072 return Err(Qasm3ParseError::InvalidArgument(format!(
1073 "classical index {index} out of bounds for '{}'",
1074 self.symbol_name(&id)
1075 )));
1076 }
1077 Ok((var, index))
1078 }
1079
1080 fn store_indexed_classical_bit(
1081 &self,
1082 (target, index): (ClassicalVar, u32),
1083 bit: ClassicalExpr,
1084 circuit: &mut Circuit,
1085 ) -> Result<(), Qasm3ParseError> {
1086 if bit.ty() != ClassicalType::Bit {
1087 return Err(Qasm3ParseError::TypeError(format!(
1088 "indexed bit assignment expects Bit expression, got {:?}",
1089 bit.ty()
1090 )));
1091 }
1092 let width = match target.ty() {
1093 ClassicalType::BitVec(width) => width.get(),
1094 ty => {
1095 return Err(Qasm3ParseError::TypeError(format!(
1096 "indexed assignment target must be BitVec, got {ty:?}"
1097 )));
1098 }
1099 };
1100 let current = target.expr();
1101 let bits = (0..width)
1102 .map(|bit_index| {
1103 if bit_index == index {
1104 Ok(bit.clone())
1105 } else {
1106 ClassicalExpr::extract_bit(current.clone(), bit_index)
1107 }
1108 })
1109 .collect::<Result<Vec<_>, _>>()?;
1110 let updated = ClassicalExpr::pack_bits(bits)?;
1111 circuit.store(target, updated)?;
1112 Ok(())
1113 }
1114
1115 fn lower_gate_call(
1116 &mut self,
1117 call: &asg::GateCall,
1118 circuit: &mut Circuit,
1119 ) -> Result<(), Qasm3ParseError> {
1120 let gate_id = self.symbol_id(call.name())?;
1121 let gate_name = self.symbol_name(&gate_id);
1122 let params = self.lower_params(call.params().unwrap_or(&[]))?;
1123 let mut qubits = self.expand_qubit_exprs(call.qubits())?;
1124
1125 if let Some((instruction, params)) =
1126 self.standard_gate(&gate_name, ¶ms, qubits.len())?
1127 {
1128 let instruction = self.apply_modifiers(instruction, call.modifiers())?;
1129 let (expected_qubits, expected_params) = instruction.gate_arity().ok_or_else(|| {
1130 Qasm3ParseError::UnsupportedFeature(format!(
1131 "gate '{gate_name}' has variable arity"
1132 ))
1133 })?;
1134 self.check_counts(expected_qubits, qubits.len(), expected_params, params.len())?;
1135 circuit.append(instruction, qubits, params, None)?;
1136 return Ok(());
1137 }
1138
1139 let gate = self.compile_gate_if_needed(&gate_id)?;
1140 match gate {
1141 GateDef::Circuit(circuit_gate) => {
1142 self.check_counts(
1143 circuit_gate.num_qubits(),
1144 qubits.len(),
1145 circuit_gate.num_params(),
1146 params.len(),
1147 )?;
1148 if !call.modifiers().is_empty() {
1149 return Err(Qasm3ParseError::UnsupportedFeature(
1150 "modifiers on circuit-defined gates".to_string(),
1151 ));
1152 }
1153 circuit.append(
1154 Instruction::CircuitGate(Box::new(circuit_gate)),
1155 qubits,
1156 params,
1157 None,
1158 )?;
1159 }
1160 GateDef::Opaque {
1161 name,
1162 qubits: expected_qubits,
1163 params: expected_params,
1164 } => {
1165 self.check_counts(expected_qubits, qubits.len(), expected_params, params.len())?;
1166 let mut instruction = Instruction::UnitaryGate(Box::new(UnitaryGate::new(
1167 &name,
1168 expected_qubits as u16,
1169 expected_params as u16,
1170 )));
1171 instruction = self.apply_modifiers(instruction, call.modifiers())?;
1172 let expected = instruction.gate_arity().ok_or_else(|| {
1173 Qasm3ParseError::UnsupportedFeature(format!("opaque gate '{name}' arity"))
1174 })?;
1175 if qubits.len() < expected.0 {
1176 return Err(Qasm3ParseError::MismatchedQubitCount {
1177 expected: expected.0,
1178 actual: qubits.len(),
1179 });
1180 }
1181 circuit.append(instruction, qubits.split_off(0), params, None)?;
1182 }
1183 GateDef::Source(_) => unreachable!("compile_gate_if_needed resolves source gates"),
1184 }
1185 Ok(())
1186 }
1187
1188 fn compile_gate_if_needed(&mut self, id: &SymbolId) -> Result<GateDef, Qasm3ParseError> {
1189 let Some(gate) = self.gates.get(id).cloned() else {
1190 return Err(Qasm3ParseError::UndefinedGate(self.symbol_name(id)));
1191 };
1192 if !matches!(gate, GateDef::Source(_)) {
1193 return Ok(gate);
1194 }
1195 if self.recursion_depth >= self.max_recursion_depth {
1196 return Err(Qasm3ParseError::RecursionLimitExceeded(
1197 self.symbol_name(id),
1198 ));
1199 }
1200 if !self.compiling_gates.insert(id.clone()) {
1201 return Err(Qasm3ParseError::CircularGateDependency {
1202 gate: self.symbol_name(id),
1203 dependency: self.symbol_name(id),
1204 });
1205 }
1206
1207 self.recursion_depth += 1;
1208 let result = self.build_circuit_gate(id);
1209 self.recursion_depth -= 1;
1210 self.compiling_gates.remove(id);
1211
1212 let compiled = result?;
1213 self.gates.insert(id.clone(), compiled.clone());
1214 Ok(compiled)
1215 }
1216
1217 fn build_circuit_gate(&mut self, id: &SymbolId) -> Result<GateDef, Qasm3ParseError> {
1218 let GateDef::Source(def) = self
1219 .gates
1220 .get(id)
1221 .cloned()
1222 .ok_or_else(|| Qasm3ParseError::UndefinedGate(format!("gate id {:?}", id)))?
1223 else {
1224 return self
1225 .gates
1226 .get(id)
1227 .cloned()
1228 .ok_or_else(|| Qasm3ParseError::UndefinedGate(format!("gate id {:?}", id)));
1229 };
1230
1231 let saved_quantum = self.quantum.clone();
1232 let mut gate_circuit = Circuit::new(def.qubits().len());
1233 for (index, qubit_id_result) in def.qubits().iter().enumerate() {
1234 let qubit_id = self.symbol_id(qubit_id_result)?;
1235 self.quantum
1236 .insert(qubit_id, QuantumBinding::Qubit(Qubit::new(index as u32)));
1237 }
1238
1239 for stmt in def.block().statements() {
1240 match stmt {
1241 Stmt::GateCall(_) | Stmt::GPhaseCall(_) | Stmt::Barrier(_) => {
1242 self.lower_stmt(stmt, &mut gate_circuit)?;
1243 }
1244 _ => {
1245 self.quantum = saved_quantum;
1246 return Err(Qasm3ParseError::UnsupportedFeature(format!(
1247 "statement in gate body: {stmt:?}"
1248 )));
1249 }
1250 }
1251 }
1252
1253 self.quantum = saved_quantum;
1254 let signature_params = def
1255 .params()
1256 .unwrap_or(&[])
1257 .iter()
1258 .map(|param| self.symbol_id(param).map(|id| self.symbol_name(&id)))
1259 .collect::<Result<Vec<_>, _>>()?;
1260 let gate = CircuitGate::with_signature(
1261 self.symbol_name(id),
1262 FrozenCircuit::new(gate_circuit),
1263 signature_params,
1264 )?;
1265 Ok(GateDef::Circuit(gate))
1266 }
1267
1268 fn lower_for(
1269 &mut self,
1270 op: &asg::ForStmt,
1271 circuit: &mut Circuit,
1272 ) -> Result<(), Qasm3ParseError> {
1273 let loop_id = self.symbol_id(op.loop_var())?;
1274 let ForIterable::RangeExpression(range) = op.iterable() else {
1275 return Err(Qasm3ParseError::UnsupportedFeature(
1276 "non-range for iterable".to_string(),
1277 ));
1278 };
1279 let start = self.const_u128(range.start())?;
1280 let stop = self.const_u128(range.stop())?;
1281 let step = match range.step() {
1282 Some(step) => self.const_u128(step)?,
1283 None => 1,
1284 };
1285 if step == 0 {
1286 return Err(Qasm3ParseError::InvalidArgument(
1287 "for loop step must be non-zero".to_string(),
1288 ));
1289 }
1290 let old = self.loop_constants.get(&loop_id).copied();
1291 let mut value = start;
1292 while value <= stop {
1293 self.loop_constants.insert(loop_id.clone(), value);
1294 self.lower_block(op.loop_body(), circuit)?;
1295 value = value.checked_add(step).ok_or_else(|| {
1296 Qasm3ParseError::InvalidArgument("for loop value overflow".to_string())
1297 })?;
1298 }
1299 match old {
1300 Some(value) => {
1301 self.loop_constants.insert(loop_id, value);
1302 }
1303 None => {
1304 self.loop_constants.remove(&loop_id);
1305 }
1306 }
1307 Ok(())
1308 }
1309
1310 fn lower_switch(
1311 &mut self,
1312 op: &asg::SwitchCaseStmt,
1313 circuit: &mut Circuit,
1314 ) -> Result<(), Qasm3ParseError> {
1315 let target = self.lower_classical_expr(op.control())?;
1316 let target = match target.ty() {
1317 ClassicalType::UInt(_) => target,
1318 ClassicalType::BitVec(_) => ClassicalExpr::bit_vec_to_uint(target)?,
1319 ty => {
1320 return Err(Qasm3ParseError::TypeError(format!(
1321 "switch target must be UInt or BitVec, got {ty:?}"
1322 )));
1323 }
1324 };
1325
1326 circuit.switch(target, |builder| {
1327 for case in op.cases() {
1328 for value_expr in case.control_values() {
1329 let value = self.const_u128(value_expr).map_err(|e| {
1330 crate::circuit::CircuitError::InvalidOperation(e.to_string())
1331 })?;
1332 builder.value(value, |case_circuit| {
1333 self.lower_stmt_slice(case.statements(), case_circuit)
1334 .map_err(|e| {
1335 crate::circuit::CircuitError::InvalidOperation(e.to_string())
1336 })
1337 })?;
1338 }
1339 }
1340 if let Some(default_block) = op.default_block() {
1341 builder.default(|default_circuit| {
1342 self.lower_stmt_slice(default_block, default_circuit)
1343 .map_err(|e| crate::circuit::CircuitError::InvalidOperation(e.to_string()))
1344 })?;
1345 }
1346 Ok(())
1347 })?;
1348 Ok(())
1349 }
1350
1351 fn standard_gate(
1352 &self,
1353 name: &str,
1354 params: &[ParameterValue],
1355 qubit_count: usize,
1356 ) -> Result<Option<(Instruction, Vec<ParameterValue>)>, Qasm3ParseError> {
1357 let instruction = match name {
1358 "id" | "i" => Some(Instruction::Standard(StandardGate::I)),
1359 "x" => Some(Instruction::Standard(StandardGate::X)),
1360 "y" => Some(Instruction::Standard(StandardGate::Y)),
1361 "z" => Some(Instruction::Standard(StandardGate::Z)),
1362 "h" => Some(Instruction::Standard(StandardGate::H)),
1363 "s" => Some(Instruction::Standard(StandardGate::S)),
1364 "sdg" => Some(Instruction::Standard(StandardGate::SDG)),
1365 "t" => Some(Instruction::Standard(StandardGate::T)),
1366 "tdg" => Some(Instruction::Standard(StandardGate::TDG)),
1367 "x2p" => Some(Instruction::Standard(StandardGate::X2P)),
1368 "x2m" => Some(Instruction::Standard(StandardGate::X2M)),
1369 "y2p" => Some(Instruction::Standard(StandardGate::Y2P)),
1370 "y2m" => Some(Instruction::Standard(StandardGate::Y2M)),
1371 "sx" => Some(Instruction::Standard(StandardGate::X2P)),
1372 "sxdg" => Some(Instruction::Standard(StandardGate::X2M)),
1373 "rx" => Some(Instruction::Standard(StandardGate::RX)),
1374 "ry" => Some(Instruction::Standard(StandardGate::RY)),
1375 "rz" => Some(Instruction::Standard(StandardGate::RZ)),
1376 "p" | "phase" | "u1" => Some(Instruction::Standard(StandardGate::Phase)),
1377 "u" | "U" | "u3" => Some(Instruction::Standard(StandardGate::U)),
1378 "u2" => {
1379 if params.len() != 2 {
1380 return Err(Qasm3ParseError::MismatchedParameterCount {
1381 expected: 2,
1382 actual: params.len(),
1383 });
1384 }
1385 let mut new_params = vec![ParameterValue::Param(Parameter::pi() / 2.0)];
1386 new_params.extend_from_slice(params);
1387 return Ok(Some((Instruction::Standard(StandardGate::U), new_params)));
1388 }
1389 "cx" | "CX" => Some(Instruction::Standard(StandardGate::CX)),
1390 "cy" => Some(Instruction::Standard(StandardGate::CY)),
1391 "cz" => Some(Instruction::Standard(StandardGate::CZ)),
1392 "swap" => Some(Instruction::Standard(StandardGate::SWAP)),
1393 "ccx" => Some(Instruction::Standard(StandardGate::CCX)),
1394 "rxx" => Some(Instruction::Standard(StandardGate::RXX)),
1395 "ryy" => Some(Instruction::Standard(StandardGate::RYY)),
1396 "rzz" => Some(Instruction::Standard(StandardGate::RZZ)),
1397 "rzx" => Some(Instruction::Standard(StandardGate::RZX)),
1398 "xy2p" => Some(Instruction::Standard(StandardGate::XY2P)),
1399 "xy2m" => Some(Instruction::Standard(StandardGate::XY2M)),
1400 "fsim" => Some(Instruction::Standard(StandardGate::FSIM)),
1401 "crx" => Some(Instruction::Standard(StandardGate::CRX)),
1402 "cry" => Some(Instruction::Standard(StandardGate::CRY)),
1403 "crz" => Some(Instruction::Standard(StandardGate::CRZ)),
1404 "gphase" => Some(Instruction::Standard(StandardGate::GPhase)),
1405 "ch" | "cp" | "cu" | "cswap" => {
1406 return Err(Qasm3ParseError::UnsupportedFeature(format!(
1407 "standard gate '{name}'"
1408 )));
1409 }
1410 _ => None,
1411 };
1412 if let Some(instruction) = instruction {
1413 let (expected_qubits, expected_params) = instruction.gate_arity().ok_or_else(|| {
1414 Qasm3ParseError::UnsupportedFeature(format!("gate '{name}' arity"))
1415 })?;
1416 self.check_counts(expected_qubits, qubit_count, expected_params, params.len())?;
1417 Ok(Some((instruction, params.to_vec())))
1418 } else {
1419 Ok(None)
1420 }
1421 }
1422
1423 fn apply_modifiers(
1424 &self,
1425 mut instruction: Instruction,
1426 modifiers: &[GateModifier],
1427 ) -> Result<Instruction, Qasm3ParseError> {
1428 for modifier in modifiers.iter().rev() {
1429 match modifier {
1430 GateModifier::Inv => {
1431 let params = Vec::<Parameter>::new();
1432 let Some((inverse, _)) = instruction.inverse(¶ms) else {
1433 return Err(Qasm3ParseError::UnsupportedFeature(
1434 "inverse modifier for this gate".to_string(),
1435 ));
1436 };
1437 instruction = inverse;
1438 }
1439 GateModifier::Ctrl(count) => {
1440 let count = match count {
1441 Some(expr) => self.const_u128(expr)? as usize,
1442 None => 1,
1443 };
1444 let Some(controlled) = instruction.control(count) else {
1445 return Err(Qasm3ParseError::UnsupportedFeature(
1446 "control modifier for this gate".to_string(),
1447 ));
1448 };
1449 instruction = controlled;
1450 }
1451 GateModifier::NegCtrl(_) | GateModifier::Pow(_) => {
1452 return Err(Qasm3ParseError::UnsupportedFeature(
1453 "negctrl or pow gate modifier".to_string(),
1454 ));
1455 }
1456 }
1457 }
1458 Ok(instruction)
1459 }
1460
1461 fn check_counts(
1462 &self,
1463 expected_qubits: usize,
1464 actual_qubits: usize,
1465 expected_params: usize,
1466 actual_params: usize,
1467 ) -> Result<(), Qasm3ParseError> {
1468 if expected_qubits != actual_qubits {
1469 return Err(Qasm3ParseError::MismatchedQubitCount {
1470 expected: expected_qubits,
1471 actual: actual_qubits,
1472 });
1473 }
1474 if expected_params != actual_params {
1475 return Err(Qasm3ParseError::MismatchedParameterCount {
1476 expected: expected_params,
1477 actual: actual_params,
1478 });
1479 }
1480 Ok(())
1481 }
1482
1483 fn lower_params(&self, params: &[TExpr]) -> Result<Vec<ParameterValue>, Qasm3ParseError> {
1484 params
1485 .iter()
1486 .map(|expr| {
1487 let param = self.lower_angle(expr)?;
1488 if let Ok(value) = param.evaluate(&None) {
1489 Ok(ParameterValue::Fixed(value))
1490 } else {
1491 Ok(ParameterValue::Param(param))
1492 }
1493 })
1494 .collect()
1495 }
1496
1497 fn lower_angle(&self, expr: &TExpr) -> Result<Parameter, Qasm3ParseError> {
1498 match expr.expression() {
1499 Expr::Literal(Literal::Int(value)) => {
1500 if !*value.sign() {
1501 return Err(Qasm3ParseError::InvalidArgument(
1502 "negative integer literal".to_string(),
1503 ));
1504 }
1505 Ok(Parameter::from(*value.value() as f64))
1506 }
1507 Expr::Literal(Literal::Float(value)) => value
1508 .value()
1509 .parse::<f64>()
1510 .map(Parameter::from)
1511 .map_err(|e| Qasm3ParseError::InvalidArgument(e.to_string())),
1512 Expr::Identifier(id_result) => {
1513 let id = self.symbol_id(id_result)?;
1514 let name = self.symbol_name(&id);
1515 match name.as_str() {
1516 "pi" | "π" => Ok(Parameter::pi()),
1517 "tau" | "τ" => Ok(Parameter::pi() * 2.0),
1518 "euler" | "ℇ" => Ok(Parameter::symbol("e")),
1519 _ => Ok(Parameter::symbol(&name)),
1520 }
1521 }
1522 Expr::Cast(cast) => self.lower_angle(cast.operand()),
1523 Expr::UnaryExpr(unary) => {
1524 let value = self.lower_angle(unary.operand())?;
1525 match unary.op() {
1526 UnaryOp::Minus => Ok(Parameter::from(0.0) - value),
1527 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1528 "angle unary operator {:?}",
1529 unary.op()
1530 ))),
1531 }
1532 }
1533 Expr::BinaryExpr(binary) => {
1534 let left = self.lower_angle(binary.left())?;
1535 let right = self.lower_angle(binary.right())?;
1536 match binary.op() {
1537 BinaryOp::ArithOp(ArithOp::Add) => Ok(left + right),
1538 BinaryOp::ArithOp(ArithOp::Sub) => Ok(left - right),
1539 BinaryOp::ArithOp(ArithOp::Mul) => Ok(left * right),
1540 BinaryOp::ArithOp(ArithOp::Div) => Ok(left / right),
1541 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1542 "angle binary operator {:?}",
1543 binary.op()
1544 ))),
1545 }
1546 }
1547 Expr::Call => Err(Qasm3ParseError::UnsupportedFeature(
1548 "function call in angle expression".to_string(),
1549 )),
1550 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1551 "angle expression {:?}",
1552 expr.expression()
1553 ))),
1554 }
1555 }
1556
1557 fn lower_condition(&self, expr: &TExpr) -> Result<ClassicalExpr, Qasm3ParseError> {
1558 let expr = self.lower_classical_expr(expr)?;
1559 match expr.ty() {
1560 ClassicalType::Bool => Ok(expr),
1561 ClassicalType::Bit => Ok(ClassicalExpr::bit_to_bool(expr)?),
1562 ty => Err(Qasm3ParseError::TypeError(format!(
1563 "condition must be Bool or Bit, got {ty:?}"
1564 ))),
1565 }
1566 }
1567
1568 fn lower_classical_expr(&self, expr: &TExpr) -> Result<ClassicalExpr, Qasm3ParseError> {
1569 match expr.expression() {
1570 Expr::Literal(Literal::Bool(value)) => Ok(ClassicalExpr::bool_literal(*value.value())),
1571 Expr::Literal(Literal::Int(value)) => {
1572 if !*value.sign() {
1573 return Err(Qasm3ParseError::UnsupportedFeature(
1574 "signed integer classical literal".to_string(),
1575 ));
1576 }
1577 let width = expr.get_type().width().unwrap_or(128);
1578 Ok(ClassicalExpr::uint_literal(width, *value.value())?)
1579 }
1580 Expr::Literal(Literal::BitString(bits)) => {
1581 let clean = bits
1582 .value()
1583 .chars()
1584 .filter(|ch| *ch == '0' || *ch == '1')
1585 .collect::<String>();
1586 let value = u128::from_str_radix(&clean, 2)
1587 .map_err(|e| Qasm3ParseError::InvalidArgument(e.to_string()))?;
1588 Ok(ClassicalExpr::bit_vec_literal(clean.len() as u32, value)?)
1589 }
1590 Expr::Identifier(id_result) => {
1591 let id = self.symbol_id(id_result)?;
1592 if let Some(value) = self.loop_constants.get(&id) {
1593 let width = self.symbol_type(&id).width().unwrap_or(128);
1594 return Ok(ClassicalExpr::uint_literal(width, *value)?);
1595 }
1596 let Some(var) = self.classical.get(&id).copied() else {
1597 return Err(Qasm3ParseError::UndefinedSymbol(self.symbol_name(&id)));
1598 };
1599 Ok(var.expr())
1600 }
1601 Expr::Cast(cast) => {
1602 if matches!(cast.get_type(), Type::Bit(_))
1603 && let Expr::Literal(Literal::Bool(value)) = cast.operand().expression()
1604 {
1605 return Ok(ClassicalExpr::bit_literal(*value.value()));
1606 }
1607 if let Type::UInt(Some(width), _) = cast.get_type()
1608 && let Ok(value) = self.const_u128(cast.operand())
1609 {
1610 return Ok(ClassicalExpr::uint_literal(*width, value)?);
1611 }
1612 let operand = self.lower_classical_expr(cast.operand())?;
1613 match (operand.ty(), cast.get_type()) {
1614 (ClassicalType::Bit, Type::Bool(_)) => Ok(ClassicalExpr::bit_to_bool(operand)?),
1615 (ClassicalType::BitVec(_), Type::UInt(_, _)) => {
1616 Ok(ClassicalExpr::bit_vec_to_uint(operand)?)
1617 }
1618 (_, _) if self.classical_type(cast.get_type()).ok() == Some(operand.ty()) => {
1619 Ok(operand)
1620 }
1621 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1622 "classical cast from {:?} to {:?}",
1623 operand.ty(),
1624 cast.get_type()
1625 ))),
1626 }
1627 }
1628 Expr::UnaryExpr(unary) => {
1629 let operand = self.lower_classical_expr(unary.operand())?;
1630 match unary.op() {
1631 UnaryOp::Not | UnaryOp::BitNot => Ok(ClassicalExpr::try_not(operand)?),
1632 UnaryOp::Minus => Err(Qasm3ParseError::UnsupportedFeature(
1633 "runtime unary minus".to_string(),
1634 )),
1635 }
1636 }
1637 Expr::BinaryExpr(binary) => {
1638 let left = self.lower_classical_expr(binary.left())?;
1639 let right = self.lower_classical_expr(binary.right())?;
1640 match binary.op() {
1641 BinaryOp::CmpOp(CmpOp::Eq) => Ok(ClassicalExpr::eq(left, right)?),
1642 BinaryOp::CmpOp(CmpOp::Neq) => Ok(ClassicalExpr::ne(left, right)?),
1643 BinaryOp::ArithOp(ArithOp::BitAnd) => Ok(ClassicalExpr::try_and(left, right)?),
1644 BinaryOp::ArithOp(ArithOp::BitXOr) => Ok(ClassicalExpr::try_xor(left, right)?),
1645 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1646 "runtime classical binary operator {:?}",
1647 binary.op()
1648 ))),
1649 }
1650 }
1651 Expr::IndexedIdentifier(indexed) => {
1652 let base = self.lower_classical_indexed_base(indexed)?;
1653 if indexed.indexes().len() != 1 {
1654 return Err(Qasm3ParseError::UnsupportedFeature(
1655 "multi-dimensional classical index".to_string(),
1656 ));
1657 }
1658 let index = self.single_index(&indexed.indexes()[0])?;
1659 Ok(ClassicalExpr::extract_bit(base, index)?)
1660 }
1661 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1662 "classical expression {:?}",
1663 expr.expression()
1664 ))),
1665 }
1666 }
1667
1668 fn lower_classical_indexed_base(
1669 &self,
1670 indexed: &asg::IndexedIdentifier,
1671 ) -> Result<ClassicalExpr, Qasm3ParseError> {
1672 let id = self.symbol_id(indexed.identifier())?;
1673 let Some(var) = self.classical.get(&id).copied() else {
1674 return Err(Qasm3ParseError::UndefinedSymbol(self.symbol_name(&id)));
1675 };
1676 Ok(var.expr())
1677 }
1678
1679 fn expand_qubit_exprs(&self, exprs: &[TExpr]) -> Result<Vec<Qubit>, Qasm3ParseError> {
1680 let mut out = Vec::new();
1681 for expr in exprs {
1682 out.extend(self.expand_qubit_expr(expr)?);
1683 }
1684 Ok(out)
1685 }
1686
1687 fn expand_qubit_expr(&self, expr: &TExpr) -> Result<Vec<Qubit>, Qasm3ParseError> {
1688 match expr.expression() {
1689 Expr::GateOperand(GateOperand::Identifier(id_result)) | Expr::Identifier(id_result) => {
1690 let id = self.symbol_id(id_result)?;
1691 self.quantum
1692 .get(&id)
1693 .map(QuantumBinding::qubits)
1694 .ok_or_else(|| Qasm3ParseError::UndefinedSymbol(self.symbol_name(&id)))
1695 }
1696 Expr::GateOperand(GateOperand::IndexedIdentifier(indexed))
1697 | Expr::IndexedIdentifier(indexed) => {
1698 self.resolve_indexed_qubit(indexed).map(|q| vec![q])
1699 }
1700 Expr::GateOperand(GateOperand::HardwareQubit(_)) | Expr::HardwareQubit(_) => Err(
1701 Qasm3ParseError::UnsupportedFeature("hardware qubit".to_string()),
1702 ),
1703 Expr::Cast(cast) => self.expand_qubit_expr(cast.operand()),
1704 _ => Err(Qasm3ParseError::TypeError(format!(
1705 "expected qubit operand, got {:?}",
1706 expr.expression()
1707 ))),
1708 }
1709 }
1710
1711 fn resolve_indexed_qubit(
1712 &self,
1713 indexed: &asg::IndexedIdentifier,
1714 ) -> Result<Qubit, Qasm3ParseError> {
1715 let id = self.symbol_id(indexed.identifier())?;
1716 if indexed.indexes().len() != 1 {
1717 return Err(Qasm3ParseError::UnsupportedFeature(
1718 "multi-dimensional qubit index".to_string(),
1719 ));
1720 }
1721 let index = self.single_index(&indexed.indexes()[0])? as usize;
1722 match self.quantum.get(&id) {
1723 Some(QuantumBinding::Qubit(q)) if index == 0 => Ok(*q),
1724 Some(QuantumBinding::Register(qs)) => qs.get(index).copied().ok_or_else(|| {
1725 Qasm3ParseError::InvalidArgument(format!(
1726 "qubit index {index} out of bounds for '{}'",
1727 self.symbol_name(&id)
1728 ))
1729 }),
1730 Some(QuantumBinding::Qubit(_)) => Err(Qasm3ParseError::InvalidArgument(format!(
1731 "qubit index {index} out of bounds for '{}'",
1732 self.symbol_name(&id)
1733 ))),
1734 None => Err(Qasm3ParseError::UndefinedSymbol(self.symbol_name(&id))),
1735 }
1736 }
1737
1738 fn single_index(&self, index: &IndexOperator) -> Result<u32, Qasm3ParseError> {
1739 match index {
1740 IndexOperator::ExpressionList(list) if list.expressions.len() == 1 => {
1741 let value = self.const_u128(&list.expressions[0])?;
1742 u32::try_from(value).map_err(|_| {
1743 Qasm3ParseError::InvalidArgument(format!("index {value} exceeds u32"))
1744 })
1745 }
1746 _ => Err(Qasm3ParseError::UnsupportedFeature(
1747 "non-scalar index".to_string(),
1748 )),
1749 }
1750 }
1751
1752 fn const_u128(&self, expr: &TExpr) -> Result<u128, Qasm3ParseError> {
1753 match expr.expression() {
1754 Expr::Literal(Literal::Int(value)) if *value.sign() => Ok(*value.value()),
1755 Expr::Cast(cast) => self.const_u128(cast.operand()),
1756 Expr::Identifier(id_result) => {
1757 let id = self.symbol_id(id_result)?;
1758 self.loop_constants
1759 .get(&id)
1760 .copied()
1761 .ok_or_else(|| Qasm3ParseError::InvalidArgument(self.symbol_name(&id)))
1762 }
1763 Expr::BinaryExpr(binary) => {
1764 let left = self.const_u128(binary.left())?;
1765 let right = self.const_u128(binary.right())?;
1766 match binary.op() {
1767 BinaryOp::ArithOp(ArithOp::Add) => left.checked_add(right).ok_or_else(|| {
1768 Qasm3ParseError::InvalidArgument("constant add overflow".to_string())
1769 }),
1770 BinaryOp::ArithOp(ArithOp::Sub) => left.checked_sub(right).ok_or_else(|| {
1771 Qasm3ParseError::InvalidArgument("constant sub underflow".to_string())
1772 }),
1773 BinaryOp::ArithOp(ArithOp::Mul) => left.checked_mul(right).ok_or_else(|| {
1774 Qasm3ParseError::InvalidArgument("constant mul overflow".to_string())
1775 }),
1776 BinaryOp::ArithOp(ArithOp::Div) => left.checked_div(right).ok_or_else(|| {
1777 Qasm3ParseError::InvalidArgument("division by zero".to_string())
1778 }),
1779 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1780 "constant operator {:?}",
1781 binary.op()
1782 ))),
1783 }
1784 }
1785 _ => Err(Qasm3ParseError::InvalidArgument(format!(
1786 "expected unsigned integer constant, got {:?}",
1787 expr.expression()
1788 ))),
1789 }
1790 }
1791
1792 fn classical_type(&self, ty: &Type) -> Result<ClassicalType, Qasm3ParseError> {
1793 match ty {
1794 Type::Bit(_) => Ok(ClassicalType::Bit),
1795 Type::Bool(_) => Ok(ClassicalType::Bool),
1796 Type::UInt(Some(width), _) => ClassicalType::uint(*width).ok_or_else(|| {
1797 Qasm3ParseError::InvalidArgument("uint width must be non-zero".to_string())
1798 }),
1799 Type::BitArray(ArrayDims::D1(width), _) => ClassicalType::bit_vec(*width as u32)
1800 .ok_or_else(|| {
1801 Qasm3ParseError::InvalidArgument("bit array width must be non-zero".to_string())
1802 }),
1803 _ => Err(Qasm3ParseError::UnsupportedFeature(format!(
1804 "classical type {ty:?}"
1805 ))),
1806 }
1807 }
1808
1809 fn symbol_id(&self, result: &SymbolIdResult) -> Result<SymbolId, Qasm3ParseError> {
1810 result
1811 .clone()
1812 .map_err(|err| Qasm3ParseError::UndefinedSymbol(format!("{err:?}")))
1813 }
1814
1815 fn symbol_name(&self, id: &SymbolId) -> String {
1816 self.symbols[id].name().to_string()
1817 }
1818
1819 fn symbol_type(&self, id: &SymbolId) -> &Type {
1820 self.symbols[id].symbol_type()
1821 }
1822}
1823
1824#[cfg(test)]
1825#[path = "./load_test.rs"]
1826mod load_test;