Skip to main content

quantrs2_device/
qasm3.rs

1//! OpenQASM 3.0 support for QuantRS2.
2//!
3//! This module provides QASM 3.0 circuit representation and conversion,
4//! compatible with IBM Quantum and other modern quantum systems.
5//!
6//! ## Features
7//!
8//! - Full OpenQASM 3.0 syntax support
9//! - Classical control flow (if/else, while, for)
10//! - Dynamic circuits with mid-circuit measurements
11//! - Gate modifiers (ctrl, inv, pow)
12//! - Type system (qubit, bit, int, float, angle)
13//! - Subroutine definitions
14//!
15//! ## Example
16//!
17//! ```rust,ignore
18//! use quantrs2_device::qasm3::{Qasm3Builder, Qasm3Circuit};
19//!
20//! let mut builder = Qasm3Builder::new(5);
21//! builder.gate("h", &[0])?;
22//! builder.gate("cx", &[0, 1])?;
23//! builder.measure(0, "c", 0)?;
24//! builder.if_statement("c[0] == 1", |b| {
25//!     b.gate("x", &[1])
26//! })?;
27//!
28//! let qasm3 = builder.build()?;
29//! println!("{}", qasm3.to_string());
30//! ```
31
32use std::collections::HashMap;
33use std::fmt;
34
35use crate::{DeviceError, DeviceResult};
36
37/// OpenQASM 3.0 version
38pub const QASM3_VERSION: &str = "3.0";
39
40/// QASM 3.0 data types
41#[derive(Debug, Clone, PartialEq)]
42pub enum Qasm3Type {
43    /// Quantum bit
44    Qubit,
45    /// Array of quantum bits
46    QubitArray(usize),
47    /// Classical bit
48    Bit,
49    /// Array of classical bits
50    BitArray(usize),
51    /// Integer type
52    Int(Option<usize>),
53    /// Unsigned integer
54    Uint(Option<usize>),
55    /// Floating point
56    Float(Option<usize>),
57    /// Angle type
58    Angle(Option<usize>),
59    /// Boolean
60    Bool,
61    /// Duration type
62    Duration,
63    /// Stretch (timing constraint)
64    Stretch,
65    /// Complex number
66    Complex(Option<usize>),
67}
68
69impl fmt::Display for Qasm3Type {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match self {
72            Self::Qubit => write!(f, "qubit"),
73            Self::QubitArray(n) => write!(f, "qubit[{}]", n),
74            Self::Bit => write!(f, "bit"),
75            Self::BitArray(n) => write!(f, "bit[{}]", n),
76            Self::Int(Some(n)) => write!(f, "int[{}]", n),
77            Self::Int(None) => write!(f, "int"),
78            Self::Uint(Some(n)) => write!(f, "uint[{}]", n),
79            Self::Uint(None) => write!(f, "uint"),
80            Self::Float(Some(n)) => write!(f, "float[{}]", n),
81            Self::Float(None) => write!(f, "float"),
82            Self::Angle(Some(n)) => write!(f, "angle[{}]", n),
83            Self::Angle(None) => write!(f, "angle"),
84            Self::Bool => write!(f, "bool"),
85            Self::Duration => write!(f, "duration"),
86            Self::Stretch => write!(f, "stretch"),
87            Self::Complex(Some(n)) => write!(f, "complex[float[{}]]", n),
88            Self::Complex(None) => write!(f, "complex[float]"),
89        }
90    }
91}
92
93/// QASM 3.0 gate modifier
94#[derive(Debug, Clone)]
95pub enum GateModifier {
96    /// Control modifier: ctrl @ gate
97    Ctrl(usize),
98    /// Negated control: negctrl @ gate
99    NegCtrl(usize),
100    /// Inverse modifier: inv @ gate
101    Inv,
102    /// Power modifier: pow(n) @ gate
103    Pow(f64),
104}
105
106impl fmt::Display for GateModifier {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            Self::Ctrl(n) if *n == 1 => write!(f, "ctrl @"),
110            Self::Ctrl(n) => write!(f, "ctrl({}) @", n),
111            Self::NegCtrl(n) if *n == 1 => write!(f, "negctrl @"),
112            Self::NegCtrl(n) => write!(f, "negctrl({}) @", n),
113            Self::Inv => write!(f, "inv @"),
114            Self::Pow(n) => write!(f, "pow({}) @", n),
115        }
116    }
117}
118
119/// QASM 3.0 statement
120#[derive(Debug, Clone)]
121pub enum Qasm3Statement {
122    /// Include directive
123    Include(String),
124    /// Variable declaration
125    Declaration {
126        var_type: Qasm3Type,
127        name: String,
128        init_value: Option<String>,
129    },
130    /// Gate definition
131    GateDef {
132        name: String,
133        params: Vec<String>,
134        qubits: Vec<String>,
135        body: Vec<Qasm3Statement>,
136    },
137    /// Gate application
138    Gate {
139        name: String,
140        modifiers: Vec<GateModifier>,
141        params: Vec<String>,
142        qubits: Vec<String>,
143    },
144    /// Measurement
145    Measure { qubit: String, classical: String },
146    /// Reset
147    Reset(String),
148    /// Barrier
149    Barrier(Vec<String>),
150    /// If statement
151    If {
152        condition: String,
153        then_body: Vec<Qasm3Statement>,
154        else_body: Option<Vec<Qasm3Statement>>,
155    },
156    /// Switch statement (QASM 3.0 dynamic circuits)
157    Switch {
158        expression: String,
159        cases: Vec<(Vec<i64>, Vec<Qasm3Statement>)>,
160        default_case: Option<Vec<Qasm3Statement>>,
161    },
162    /// While loop
163    While {
164        condition: String,
165        body: Vec<Qasm3Statement>,
166    },
167    /// For loop
168    For {
169        var_name: String,
170        range: String,
171        body: Vec<Qasm3Statement>,
172    },
173    /// Classical assignment
174    Assignment { target: String, value: String },
175    /// Delay
176    Delay {
177        duration: String,
178        qubits: Vec<String>,
179    },
180    /// Box (timing block)
181    Box { body: Vec<Qasm3Statement> },
182    /// Subroutine definition
183    DefCal {
184        name: String,
185        params: Vec<String>,
186        qubits: Vec<String>,
187        body: String,
188    },
189    /// Comment
190    Comment(String),
191    /// Pragma
192    Pragma(String),
193}
194
195impl fmt::Display for Qasm3Statement {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        match self {
198            Self::Include(file) => writeln!(f, "include \"{}\";", file),
199            Self::Declaration {
200                var_type,
201                name,
202                init_value,
203            } => {
204                if let Some(val) = init_value {
205                    writeln!(f, "{} {} = {};", var_type, name, val)
206                } else {
207                    writeln!(f, "{} {};", var_type, name)
208                }
209            }
210            Self::GateDef {
211                name,
212                params,
213                qubits,
214                body,
215            } => {
216                let params_str = if params.is_empty() {
217                    String::new()
218                } else {
219                    format!("({})", params.join(", "))
220                };
221                writeln!(f, "gate {}{} {} {{", name, params_str, qubits.join(", "))?;
222                for stmt in body {
223                    write!(f, "  {}", stmt)?;
224                }
225                writeln!(f, "}}")
226            }
227            Self::Gate {
228                name,
229                modifiers,
230                params,
231                qubits,
232            } => {
233                let mod_str = if modifiers.is_empty() {
234                    String::new()
235                } else {
236                    modifiers
237                        .iter()
238                        .map(|m| m.to_string())
239                        .collect::<Vec<_>>()
240                        .join(" ")
241                        + " "
242                };
243                let params_str = if params.is_empty() {
244                    String::new()
245                } else {
246                    format!("({})", params.join(", "))
247                };
248                writeln!(
249                    f,
250                    "{}{}{} {};",
251                    mod_str,
252                    name,
253                    params_str,
254                    qubits.join(", ")
255                )
256            }
257            Self::Measure { qubit, classical } => {
258                writeln!(f, "{} = measure {};", classical, qubit)
259            }
260            Self::Reset(qubit) => writeln!(f, "reset {};", qubit),
261            Self::Barrier(qubits) => writeln!(f, "barrier {};", qubits.join(", ")),
262            Self::If {
263                condition,
264                then_body,
265                else_body,
266            } => {
267                writeln!(f, "if ({}) {{", condition)?;
268                for stmt in then_body {
269                    write!(f, "  {}", stmt)?;
270                }
271                if let Some(else_stmts) = else_body {
272                    writeln!(f, "}} else {{")?;
273                    for stmt in else_stmts {
274                        write!(f, "  {}", stmt)?;
275                    }
276                }
277                writeln!(f, "}}")
278            }
279            Self::Switch {
280                expression,
281                cases,
282                default_case,
283            } => {
284                writeln!(f, "switch ({}) {{", expression)?;
285                for (values, body) in cases {
286                    let values_str = values
287                        .iter()
288                        .map(|v| v.to_string())
289                        .collect::<Vec<_>>()
290                        .join(", ");
291                    writeln!(f, "  case {}: {{", values_str)?;
292                    for stmt in body {
293                        write!(f, "    {}", stmt)?;
294                    }
295                    writeln!(f, "  }}")?;
296                }
297                if let Some(default_body) = default_case {
298                    writeln!(f, "  default: {{")?;
299                    for stmt in default_body {
300                        write!(f, "    {}", stmt)?;
301                    }
302                    writeln!(f, "  }}")?;
303                }
304                writeln!(f, "}}")
305            }
306            Self::While { condition, body } => {
307                writeln!(f, "while ({}) {{", condition)?;
308                for stmt in body {
309                    write!(f, "  {}", stmt)?;
310                }
311                writeln!(f, "}}")
312            }
313            Self::For {
314                var_name,
315                range,
316                body,
317            } => {
318                writeln!(f, "for {} in {} {{", var_name, range)?;
319                for stmt in body {
320                    write!(f, "  {}", stmt)?;
321                }
322                writeln!(f, "}}")
323            }
324            Self::Assignment { target, value } => {
325                writeln!(f, "{} = {};", target, value)
326            }
327            Self::Delay { duration, qubits } => {
328                writeln!(f, "delay[{}] {};", duration, qubits.join(", "))
329            }
330            Self::Box { body } => {
331                writeln!(f, "box {{")?;
332                for stmt in body {
333                    write!(f, "  {}", stmt)?;
334                }
335                writeln!(f, "}}")
336            }
337            Self::DefCal {
338                name,
339                params,
340                qubits,
341                body,
342            } => {
343                let params_str = if params.is_empty() {
344                    String::new()
345                } else {
346                    format!("({})", params.join(", "))
347                };
348                writeln!(f, "defcal {}{} {} {{", name, params_str, qubits.join(", "))?;
349                writeln!(f, "  {}", body)?;
350                writeln!(f, "}}")
351            }
352            Self::Comment(text) => writeln!(f, "// {}", text),
353            Self::Pragma(text) => writeln!(f, "#pragma {}", text),
354        }
355    }
356}
357
358/// QASM 3.0 circuit representation
359#[derive(Debug, Clone)]
360pub struct Qasm3Circuit {
361    /// Version (always "3.0")
362    pub version: String,
363    /// Statements in the circuit
364    pub statements: Vec<Qasm3Statement>,
365    /// Input parameters
366    pub inputs: HashMap<String, Qasm3Type>,
367    /// Output parameters
368    pub outputs: HashMap<String, Qasm3Type>,
369}
370
371impl Qasm3Circuit {
372    /// Create a new empty QASM 3.0 circuit
373    pub fn new() -> Self {
374        Self {
375            version: QASM3_VERSION.to_string(),
376            statements: Vec::new(),
377            inputs: HashMap::new(),
378            outputs: HashMap::new(),
379        }
380    }
381
382    /// Add an input parameter
383    pub fn add_input(&mut self, name: &str, var_type: Qasm3Type) {
384        self.inputs.insert(name.to_string(), var_type);
385    }
386
387    /// Add an output parameter
388    pub fn add_output(&mut self, name: &str, var_type: Qasm3Type) {
389        self.outputs.insert(name.to_string(), var_type);
390    }
391
392    /// Add a statement
393    pub fn add_statement(&mut self, stmt: Qasm3Statement) {
394        self.statements.push(stmt);
395    }
396
397    /// Get the number of qubits used
398    pub fn num_qubits(&self) -> usize {
399        let mut max_qubit = 0;
400        for stmt in &self.statements {
401            if let Qasm3Statement::Declaration {
402                var_type: Qasm3Type::QubitArray(n),
403                ..
404            } = stmt
405            {
406                max_qubit = max_qubit.max(*n);
407            }
408        }
409        max_qubit
410    }
411
412    /// Convert to QASM 3.0 string
413    pub fn to_qasm3(&self) -> String {
414        self.to_string()
415    }
416
417    /// Convert to QASM 2.0 string (for backward compatibility)
418    pub fn to_qasm2(&self) -> DeviceResult<String> {
419        let mut qasm2 = String::from("OPENQASM 2.0;\ninclude \"qelib1.inc\";\n\n");
420
421        // Find qubit and bit declarations
422        let mut num_qubits = 0;
423        let mut num_bits = 0;
424
425        for stmt in &self.statements {
426            match stmt {
427                Qasm3Statement::Declaration {
428                    var_type: Qasm3Type::QubitArray(n),
429                    ..
430                } => {
431                    num_qubits = num_qubits.max(*n);
432                }
433                Qasm3Statement::Declaration {
434                    var_type: Qasm3Type::BitArray(n),
435                    ..
436                } => {
437                    num_bits = num_bits.max(*n);
438                }
439                _ => {}
440            }
441        }
442
443        if num_qubits > 0 {
444            qasm2.push_str(&format!("qreg q[{}];\n", num_qubits));
445        }
446        if num_bits > 0 {
447            qasm2.push_str(&format!("creg c[{}];\n", num_bits));
448        }
449        qasm2.push('\n');
450
451        // Convert statements
452        for stmt in &self.statements {
453            match stmt {
454                Qasm3Statement::Gate {
455                    name,
456                    modifiers,
457                    params,
458                    qubits,
459                } => {
460                    if !modifiers.is_empty() {
461                        return Err(DeviceError::QasmError(
462                            "Gate modifiers not supported in QASM 2.0".to_string(),
463                        ));
464                    }
465                    let params_str = if params.is_empty() {
466                        String::new()
467                    } else {
468                        format!("({})", params.join(", "))
469                    };
470                    qasm2.push_str(&format!("{}{} {};\n", name, params_str, qubits.join(", ")));
471                }
472                Qasm3Statement::Measure { qubit, classical } => {
473                    qasm2.push_str(&format!("measure {} -> {};\n", qubit, classical));
474                }
475                Qasm3Statement::Reset(qubit) => {
476                    qasm2.push_str(&format!("reset {};\n", qubit));
477                }
478                Qasm3Statement::Barrier(qubits) => {
479                    qasm2.push_str(&format!("barrier {};\n", qubits.join(", ")));
480                }
481                Qasm3Statement::If {
482                    condition,
483                    then_body,
484                    else_body,
485                } => {
486                    if else_body.is_some() {
487                        return Err(DeviceError::QasmError(
488                            "If-else not supported in QASM 2.0".to_string(),
489                        ));
490                    }
491                    // QASM 2.0 only supports simple if statements
492                    qasm2.push_str(&format!("if ({}) ", condition));
493                    if let Some(stmt) = then_body.first() {
494                        if let Qasm3Statement::Gate {
495                            name,
496                            params,
497                            qubits,
498                            ..
499                        } = stmt
500                        {
501                            let params_str = if params.is_empty() {
502                                String::new()
503                            } else {
504                                format!("({})", params.join(", "))
505                            };
506                            qasm2.push_str(&format!(
507                                "{}{} {};\n",
508                                name,
509                                params_str,
510                                qubits.join(", ")
511                            ));
512                        }
513                    }
514                }
515                Qasm3Statement::While { .. } | Qasm3Statement::For { .. } => {
516                    return Err(DeviceError::QasmError(
517                        "Loops not supported in QASM 2.0".to_string(),
518                    ));
519                }
520                Qasm3Statement::Declaration { .. }
521                | Qasm3Statement::Include(_)
522                | Qasm3Statement::Comment(_) => {
523                    // Skip declarations (handled above) and comments
524                }
525                _ => {}
526            }
527        }
528
529        Ok(qasm2)
530    }
531}
532
533impl Default for Qasm3Circuit {
534    fn default() -> Self {
535        Self::new()
536    }
537}
538
539impl fmt::Display for Qasm3Circuit {
540    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
541        writeln!(f, "OPENQASM {};", self.version)?;
542        writeln!(f)?;
543
544        // Include standard library
545        writeln!(f, "include \"stdgates.inc\";")?;
546        writeln!(f)?;
547
548        // Input declarations
549        for (name, var_type) in &self.inputs {
550            writeln!(f, "input {} {};", var_type, name)?;
551        }
552        if !self.inputs.is_empty() {
553            writeln!(f)?;
554        }
555
556        // Output declarations
557        for (name, var_type) in &self.outputs {
558            writeln!(f, "output {} {};", var_type, name)?;
559        }
560        if !self.outputs.is_empty() {
561            writeln!(f)?;
562        }
563
564        // Statements
565        for stmt in &self.statements {
566            write!(f, "{}", stmt)?;
567        }
568
569        Ok(())
570    }
571}
572
573/// Builder for QASM 3.0 circuits
574pub struct Qasm3Builder {
575    /// The circuit being built
576    pub circuit: Qasm3Circuit,
577    /// Number of qubits in the circuit
578    pub num_qubits: usize,
579    /// Number of classical bits in the circuit
580    pub num_bits: usize,
581}
582
583impl Qasm3Builder {
584    /// Create a new builder with specified number of qubits
585    pub fn new(num_qubits: usize) -> Self {
586        let mut builder = Self {
587            circuit: Qasm3Circuit::new(),
588            num_qubits,
589            num_bits: num_qubits, // Default same number of classical bits
590        };
591
592        // Add qubit and bit declarations
593        builder.circuit.add_statement(Qasm3Statement::Declaration {
594            var_type: Qasm3Type::QubitArray(num_qubits),
595            name: "q".to_string(),
596            init_value: None,
597        });
598        builder.circuit.add_statement(Qasm3Statement::Declaration {
599            var_type: Qasm3Type::BitArray(num_qubits),
600            name: "c".to_string(),
601            init_value: None,
602        });
603
604        builder
605    }
606
607    /// Set number of classical bits
608    pub fn with_bits(mut self, num_bits: usize) -> Self {
609        self.num_bits = num_bits;
610        // Update the bit declaration
611        for stmt in &mut self.circuit.statements {
612            if let Qasm3Statement::Declaration {
613                var_type: Qasm3Type::BitArray(n),
614                name,
615                ..
616            } = stmt
617            {
618                if name == "c" {
619                    *n = num_bits;
620                }
621            }
622        }
623        self
624    }
625
626    /// Add a gate operation
627    pub fn gate(&mut self, name: &str, qubits: &[usize]) -> DeviceResult<&mut Self> {
628        self.gate_with_params(name, &[], qubits)
629    }
630
631    /// Add a gate with parameters
632    pub fn gate_with_params(
633        &mut self,
634        name: &str,
635        params: &[f64],
636        qubits: &[usize],
637    ) -> DeviceResult<&mut Self> {
638        for &q in qubits {
639            if q >= self.num_qubits {
640                return Err(DeviceError::InvalidInput(format!(
641                    "Qubit {} out of range (max {})",
642                    q,
643                    self.num_qubits - 1
644                )));
645            }
646        }
647
648        let qubit_strs: Vec<String> = qubits.iter().map(|q| format!("q[{}]", q)).collect();
649        let param_strs: Vec<String> = params.iter().map(|p| format!("{}", p)).collect();
650
651        self.circuit.add_statement(Qasm3Statement::Gate {
652            name: name.to_string(),
653            modifiers: Vec::new(),
654            params: param_strs,
655            qubits: qubit_strs,
656        });
657
658        Ok(self)
659    }
660
661    /// Add a controlled gate
662    pub fn ctrl_gate(
663        &mut self,
664        name: &str,
665        control: usize,
666        target: usize,
667        params: &[f64],
668    ) -> DeviceResult<&mut Self> {
669        if control >= self.num_qubits || target >= self.num_qubits {
670            return Err(DeviceError::InvalidInput("Qubit out of range".to_string()));
671        }
672
673        let param_strs: Vec<String> = params.iter().map(|p| format!("{}", p)).collect();
674
675        self.circuit.add_statement(Qasm3Statement::Gate {
676            name: name.to_string(),
677            modifiers: vec![GateModifier::Ctrl(1)],
678            params: param_strs,
679            qubits: vec![format!("q[{}]", control), format!("q[{}]", target)],
680        });
681
682        Ok(self)
683    }
684
685    /// Add an inverse gate
686    pub fn inv_gate(&mut self, name: &str, qubits: &[usize]) -> DeviceResult<&mut Self> {
687        for &q in qubits {
688            if q >= self.num_qubits {
689                return Err(DeviceError::InvalidInput("Qubit out of range".to_string()));
690            }
691        }
692
693        let qubit_strs: Vec<String> = qubits.iter().map(|q| format!("q[{}]", q)).collect();
694
695        self.circuit.add_statement(Qasm3Statement::Gate {
696            name: name.to_string(),
697            modifiers: vec![GateModifier::Inv],
698            params: Vec::new(),
699            qubits: qubit_strs,
700        });
701
702        Ok(self)
703    }
704
705    /// Add measurement
706    pub fn measure(&mut self, qubit: usize, bit: usize) -> DeviceResult<&mut Self> {
707        if qubit >= self.num_qubits {
708            return Err(DeviceError::InvalidInput("Qubit out of range".to_string()));
709        }
710        if bit >= self.num_bits {
711            return Err(DeviceError::InvalidInput("Bit out of range".to_string()));
712        }
713
714        self.circuit.add_statement(Qasm3Statement::Measure {
715            qubit: format!("q[{}]", qubit),
716            classical: format!("c[{}]", bit),
717        });
718
719        Ok(self)
720    }
721
722    /// Measure all qubits
723    pub fn measure_all(&mut self) -> DeviceResult<&mut Self> {
724        for i in 0..self.num_qubits.min(self.num_bits) {
725            self.measure(i, i)?;
726        }
727        Ok(self)
728    }
729
730    /// Add reset operation
731    pub fn reset(&mut self, qubit: usize) -> DeviceResult<&mut Self> {
732        if qubit >= self.num_qubits {
733            return Err(DeviceError::InvalidInput("Qubit out of range".to_string()));
734        }
735
736        self.circuit
737            .add_statement(Qasm3Statement::Reset(format!("q[{}]", qubit)));
738
739        Ok(self)
740    }
741
742    /// Add barrier
743    pub fn barrier(&mut self, qubits: &[usize]) -> DeviceResult<&mut Self> {
744        for &q in qubits {
745            if q >= self.num_qubits {
746                return Err(DeviceError::InvalidInput("Qubit out of range".to_string()));
747            }
748        }
749
750        let qubit_strs: Vec<String> = qubits.iter().map(|q| format!("q[{}]", q)).collect();
751        self.circuit
752            .add_statement(Qasm3Statement::Barrier(qubit_strs));
753
754        Ok(self)
755    }
756
757    /// Add if statement (dynamic circuit)
758    pub fn if_statement<F>(&mut self, condition: &str, then_block: F) -> DeviceResult<&mut Self>
759    where
760        F: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
761    {
762        let mut inner_builder = Qasm3Builder {
763            circuit: Qasm3Circuit::new(),
764            num_qubits: self.num_qubits,
765            num_bits: self.num_bits,
766        };
767        inner_builder.circuit.statements.clear(); // Clear default declarations
768
769        then_block(&mut inner_builder)?;
770
771        self.circuit.add_statement(Qasm3Statement::If {
772            condition: condition.to_string(),
773            then_body: inner_builder.circuit.statements,
774            else_body: None,
775        });
776
777        Ok(self)
778    }
779
780    /// Add if-else statement (dynamic circuit)
781    pub fn if_else_statement<F, G>(
782        &mut self,
783        condition: &str,
784        then_block: F,
785        else_block: G,
786    ) -> DeviceResult<&mut Self>
787    where
788        F: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
789        G: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
790    {
791        let mut then_builder = Qasm3Builder {
792            circuit: Qasm3Circuit::new(),
793            num_qubits: self.num_qubits,
794            num_bits: self.num_bits,
795        };
796        then_builder.circuit.statements.clear();
797        then_block(&mut then_builder)?;
798
799        let mut else_builder = Qasm3Builder {
800            circuit: Qasm3Circuit::new(),
801            num_qubits: self.num_qubits,
802            num_bits: self.num_bits,
803        };
804        else_builder.circuit.statements.clear();
805        else_block(&mut else_builder)?;
806
807        self.circuit.add_statement(Qasm3Statement::If {
808            condition: condition.to_string(),
809            then_body: then_builder.circuit.statements,
810            else_body: Some(else_builder.circuit.statements),
811        });
812
813        Ok(self)
814    }
815
816    /// Add while loop
817    pub fn while_loop<F>(&mut self, condition: &str, body: F) -> DeviceResult<&mut Self>
818    where
819        F: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
820    {
821        let mut inner_builder = Qasm3Builder {
822            circuit: Qasm3Circuit::new(),
823            num_qubits: self.num_qubits,
824            num_bits: self.num_bits,
825        };
826        inner_builder.circuit.statements.clear();
827
828        body(&mut inner_builder)?;
829
830        self.circuit.add_statement(Qasm3Statement::While {
831            condition: condition.to_string(),
832            body: inner_builder.circuit.statements,
833        });
834
835        Ok(self)
836    }
837
838    /// Add for loop
839    pub fn for_loop<F>(&mut self, var_name: &str, range: &str, body: F) -> DeviceResult<&mut Self>
840    where
841        F: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
842    {
843        let mut inner_builder = Qasm3Builder {
844            circuit: Qasm3Circuit::new(),
845            num_qubits: self.num_qubits,
846            num_bits: self.num_bits,
847        };
848        inner_builder.circuit.statements.clear();
849
850        body(&mut inner_builder)?;
851
852        self.circuit.add_statement(Qasm3Statement::For {
853            var_name: var_name.to_string(),
854            range: range.to_string(),
855            body: inner_builder.circuit.statements,
856        });
857
858        Ok(self)
859    }
860
861    /// Add switch statement (QASM 3.0 dynamic circuit feature)
862    ///
863    /// # Arguments
864    /// * `expression` - The expression to switch on (e.g., "c")
865    /// * `case_builder` - A closure that builds the switch cases
866    ///
867    /// # Example
868    /// ```ignore
869    /// builder.switch_statement("c", |sw| {
870    ///     sw.case(&[0], |b| b.gate("x", &[0]))?;
871    ///     sw.case(&[1], |b| b.gate("y", &[0]))?;
872    ///     sw.default(|b| b.gate("z", &[0]))?;
873    ///     Ok(())
874    /// })?;
875    /// ```
876    pub fn switch_statement<F>(
877        &mut self,
878        expression: &str,
879        case_builder: F,
880    ) -> DeviceResult<&mut Self>
881    where
882        F: FnOnce(&mut SwitchBuilder) -> DeviceResult<()>,
883    {
884        let mut switch_builder = SwitchBuilder::new(self.num_qubits, self.num_bits);
885        case_builder(&mut switch_builder)?;
886
887        self.circuit.add_statement(Qasm3Statement::Switch {
888            expression: expression.to_string(),
889            cases: switch_builder.cases,
890            default_case: switch_builder.default_case,
891        });
892
893        Ok(self)
894    }
895
896    /// Add classical assignment
897    ///
898    /// # Arguments
899    /// * `target` - Target variable (e.g., `"c[0]"`)
900    /// * `value` - Value expression (e.g., `"c[0] + 1"`, `"c[0] & c[1]"`)
901    pub fn assign(&mut self, target: &str, value: &str) -> &mut Self {
902        self.circuit.add_statement(Qasm3Statement::Assignment {
903            target: target.to_string(),
904            value: value.to_string(),
905        });
906        self
907    }
908
909    /// Add comment
910    pub fn comment(&mut self, text: &str) -> &mut Self {
911        self.circuit
912            .add_statement(Qasm3Statement::Comment(text.to_string()));
913        self
914    }
915
916    /// Build the QASM 3.0 circuit
917    pub fn build(self) -> DeviceResult<Qasm3Circuit> {
918        Ok(self.circuit)
919    }
920}
921
922/// Builder for switch statement cases
923pub struct SwitchBuilder {
924    num_qubits: usize,
925    num_bits: usize,
926    cases: Vec<(Vec<i64>, Vec<Qasm3Statement>)>,
927    default_case: Option<Vec<Qasm3Statement>>,
928}
929
930impl SwitchBuilder {
931    fn new(num_qubits: usize, num_bits: usize) -> Self {
932        Self {
933            num_qubits,
934            num_bits,
935            cases: Vec::new(),
936            default_case: None,
937        }
938    }
939
940    /// Add a case to the switch statement
941    ///
942    /// # Arguments
943    /// * `values` - The case values (can be multiple for fallthrough)
944    /// * `body` - A closure that builds the case body
945    pub fn case<F>(&mut self, values: &[i64], body: F) -> DeviceResult<&mut Self>
946    where
947        F: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
948    {
949        let mut inner_builder = Qasm3Builder {
950            circuit: Qasm3Circuit::new(),
951            num_qubits: self.num_qubits,
952            num_bits: self.num_bits,
953        };
954        inner_builder.circuit.statements.clear();
955
956        body(&mut inner_builder)?;
957
958        self.cases
959            .push((values.to_vec(), inner_builder.circuit.statements));
960        Ok(self)
961    }
962
963    /// Add a default case to the switch statement
964    pub fn default<F>(&mut self, body: F) -> DeviceResult<&mut Self>
965    where
966        F: FnOnce(&mut Qasm3Builder) -> DeviceResult<()>,
967    {
968        let mut inner_builder = Qasm3Builder {
969            circuit: Qasm3Circuit::new(),
970            num_qubits: self.num_qubits,
971            num_bits: self.num_bits,
972        };
973        inner_builder.circuit.statements.clear();
974
975        body(&mut inner_builder)?;
976
977        self.default_case = Some(inner_builder.circuit.statements);
978        Ok(self)
979    }
980}
981
982/// Convert a `QuantRS2` circuit to QASM 3.0.
983///
984/// Iterates the circuit gates and emits one QASM 3.0 statement per gate using
985/// the standard OpenQASM 3.0 gate names. Parameterized rotations are emitted
986/// with their angle; gates with no QASM 3.0 standard-library equivalent produce
987/// an honest [`DeviceError::CircuitConversion`] naming the offending gate rather
988/// than being silently dropped.
989pub fn circuit_to_qasm3<const N: usize>(
990    circuit: &quantrs2_circuit::prelude::Circuit<N>,
991) -> DeviceResult<Qasm3Circuit> {
992    use quantrs2_core::gate::{multi::*, single::*, GateOp};
993
994    let mut builder = Qasm3Builder::new(N);
995
996    for gate in circuit.gates() {
997        let any = gate.as_any();
998        let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
999
1000        // Parameterized single-qubit rotations.
1001        if let Some(g) = any.downcast_ref::<RotationX>() {
1002            builder.gate_with_params("rx", &[g.theta], &qubits)?;
1003            continue;
1004        }
1005        if let Some(g) = any.downcast_ref::<RotationY>() {
1006            builder.gate_with_params("ry", &[g.theta], &qubits)?;
1007            continue;
1008        }
1009        if let Some(g) = any.downcast_ref::<RotationZ>() {
1010            builder.gate_with_params("rz", &[g.theta], &qubits)?;
1011            continue;
1012        }
1013        if let Some(g) = any.downcast_ref::<PGate>() {
1014            builder.gate_with_params("p", &[g.lambda], &qubits)?;
1015            continue;
1016        }
1017        if let Some(g) = any.downcast_ref::<UGate>() {
1018            builder.gate_with_params("u", &[g.theta, g.phi, g.lambda], &qubits)?;
1019            continue;
1020        }
1021        // Parameterized two-qubit rotations.
1022        if let Some(g) = any.downcast_ref::<CRX>() {
1023            builder.gate_with_params("crx", &[g.theta], &qubits)?;
1024            continue;
1025        }
1026        if let Some(g) = any.downcast_ref::<CRY>() {
1027            builder.gate_with_params("cry", &[g.theta], &qubits)?;
1028            continue;
1029        }
1030        if let Some(g) = any.downcast_ref::<CRZ>() {
1031            builder.gate_with_params("crz", &[g.theta], &qubits)?;
1032            continue;
1033        }
1034        if let Some(g) = any.downcast_ref::<RXX>() {
1035            builder.gate_with_params("rxx", &[g.theta], &qubits)?;
1036            continue;
1037        }
1038        if let Some(g) = any.downcast_ref::<RYY>() {
1039            builder.gate_with_params("ryy", &[g.theta], &qubits)?;
1040            continue;
1041        }
1042        if let Some(g) = any.downcast_ref::<RZZ>() {
1043            builder.gate_with_params("rzz", &[g.theta], &qubits)?;
1044            continue;
1045        }
1046
1047        // Non-parameterized gates: map QuantRS2 names to QASM 3.0 standard names.
1048        let qasm_name = match gate.name() {
1049            "H" => "h",
1050            "X" => "x",
1051            "Y" => "y",
1052            "Z" => "z",
1053            "S" => "s",
1054            "S†" => "sdg",
1055            "T" => "t",
1056            "T†" => "tdg",
1057            "√X" => "sx",
1058            "√X†" => "sxdg",
1059            "I" => "id",
1060            "CNOT" => "cx",
1061            "CZ" => "cz",
1062            "CY" => "cy",
1063            "CH" => "ch",
1064            "SWAP" => "swap",
1065            "Toffoli" => "ccx",
1066            "Fredkin" => "cswap",
1067            other => {
1068                return Err(DeviceError::CircuitConversion(format!(
1069                    "Gate '{other}' has no OpenQASM 3.0 standard-library equivalent"
1070                )));
1071            }
1072        };
1073        builder.gate(qasm_name, &qubits)?;
1074    }
1075
1076    builder.build()
1077}
1078
1079#[cfg(test)]
1080mod tests {
1081    use super::*;
1082
1083    #[test]
1084    fn test_qasm3_type_display() {
1085        assert_eq!(format!("{}", Qasm3Type::Qubit), "qubit");
1086        assert_eq!(format!("{}", Qasm3Type::QubitArray(5)), "qubit[5]");
1087        assert_eq!(format!("{}", Qasm3Type::BitArray(3)), "bit[3]");
1088        assert_eq!(format!("{}", Qasm3Type::Int(Some(32))), "int[32]");
1089        assert_eq!(format!("{}", Qasm3Type::Float(None)), "float");
1090    }
1091
1092    #[test]
1093    fn test_gate_modifier_display() {
1094        assert_eq!(format!("{}", GateModifier::Ctrl(1)), "ctrl @");
1095        assert_eq!(format!("{}", GateModifier::Ctrl(2)), "ctrl(2) @");
1096        assert_eq!(format!("{}", GateModifier::Inv), "inv @");
1097        assert_eq!(format!("{}", GateModifier::Pow(2.0)), "pow(2) @");
1098    }
1099
1100    #[test]
1101    fn test_qasm3_builder_basic() {
1102        let mut builder = Qasm3Builder::new(3);
1103        builder.gate("h", &[0]).unwrap();
1104        builder.gate("cx", &[0, 1]).unwrap();
1105        builder.measure(0, 0).unwrap();
1106
1107        let circuit = builder.build().unwrap();
1108        let qasm = circuit.to_string();
1109
1110        assert!(qasm.contains("OPENQASM 3.0"));
1111        assert!(qasm.contains("qubit[3]"));
1112        assert!(qasm.contains("h q[0]"));
1113        assert!(qasm.contains("cx q[0], q[1]"));
1114    }
1115
1116    #[test]
1117    fn test_qasm3_builder_params() {
1118        let mut builder = Qasm3Builder::new(2);
1119        builder
1120            .gate_with_params("rx", &[std::f64::consts::PI / 2.0], &[0])
1121            .unwrap();
1122
1123        let circuit = builder.build().unwrap();
1124        let qasm = circuit.to_string();
1125
1126        assert!(qasm.contains("rx"));
1127    }
1128
1129    #[test]
1130    fn test_qasm3_if_statement() {
1131        let mut builder = Qasm3Builder::new(2);
1132        builder.gate("h", &[0]).unwrap();
1133        builder.measure(0, 0).unwrap();
1134        builder
1135            .if_statement("c[0] == 1", |b| {
1136                b.gate("x", &[1])?;
1137                Ok(())
1138            })
1139            .unwrap();
1140
1141        let circuit = builder.build().unwrap();
1142        let qasm = circuit.to_string();
1143
1144        assert!(qasm.contains("if (c[0] == 1)"));
1145        assert!(qasm.contains("x q[1]"));
1146    }
1147
1148    #[test]
1149    fn test_qasm3_to_qasm2() {
1150        let mut builder = Qasm3Builder::new(2);
1151        builder.gate("h", &[0]).unwrap();
1152        builder.gate("cx", &[0, 1]).unwrap();
1153        builder.measure_all().unwrap();
1154
1155        let circuit = builder.build().unwrap();
1156        let qasm2 = circuit.to_qasm2().unwrap();
1157
1158        assert!(qasm2.contains("OPENQASM 2.0"));
1159        assert!(qasm2.contains("qreg q[2]"));
1160        assert!(qasm2.contains("creg c[2]"));
1161    }
1162
1163    #[test]
1164    fn test_qasm3_ctrl_gate() {
1165        let mut builder = Qasm3Builder::new(3);
1166        builder.ctrl_gate("x", 0, 1, &[]).unwrap();
1167
1168        let circuit = builder.build().unwrap();
1169        let qasm = circuit.to_string();
1170
1171        assert!(qasm.contains("ctrl @"));
1172    }
1173
1174    #[test]
1175    fn test_qasm3_inv_gate() {
1176        let mut builder = Qasm3Builder::new(2);
1177        builder.inv_gate("s", &[0]).unwrap();
1178
1179        let circuit = builder.build().unwrap();
1180        let qasm = circuit.to_string();
1181
1182        assert!(qasm.contains("inv @"));
1183    }
1184
1185    #[test]
1186    fn test_qasm3_for_loop() {
1187        let mut builder = Qasm3Builder::new(4);
1188        builder
1189            .for_loop("i", "[0:3]", |b| {
1190                b.gate("h", &[0])?;
1191                Ok(())
1192            })
1193            .unwrap();
1194
1195        let circuit = builder.build().unwrap();
1196        let qasm = circuit.to_string();
1197
1198        assert!(qasm.contains("for i in [0:3]"));
1199    }
1200
1201    #[test]
1202    fn test_circuit_to_qasm3_emits_real_statements() {
1203        use quantrs2_circuit::prelude::Circuit;
1204
1205        // H, CNOT, RZ(pi/2): 3 gates → expect exactly 3 gate statements.
1206        let mut circuit = Circuit::<2>::new();
1207        circuit.h(0).expect("add H");
1208        circuit.cnot(0, 1).expect("add CNOT");
1209        circuit.rz(1, std::f64::consts::PI / 2.0).expect("add RZ");
1210
1211        let qasm3 = circuit_to_qasm3(&circuit).expect("conversion should succeed");
1212        let text = qasm3.to_string();
1213
1214        // The real gate statements must be present, not silently dropped.
1215        assert!(text.contains("h q[0]"), "missing H: {text}");
1216        assert!(text.contains("cx q[0], q[1]"), "missing CNOT: {text}");
1217        assert!(text.contains("rz"), "missing RZ: {text}");
1218
1219        // Statement count must match the gate count (3 gates → 3 statements).
1220        let gate_statements = qasm3
1221            .statements
1222            .iter()
1223            .filter(|s| matches!(s, Qasm3Statement::Gate { .. }))
1224            .count();
1225        assert_eq!(
1226            gate_statements,
1227            circuit.gates().len(),
1228            "QASM3 statement count must equal circuit gate count"
1229        );
1230    }
1231
1232    #[test]
1233    fn test_circuit_to_qasm3_unsupported_gate_is_honest_error() {
1234        use quantrs2_circuit::prelude::Circuit;
1235        use quantrs2_core::gate::multi::XXPlusYY;
1236
1237        // XXPlusYY has no QASM 3.0 standard-library mapping here.
1238        let mut circuit = Circuit::<2>::new();
1239        circuit
1240            .add_gate(XXPlusYY {
1241                qubit1: quantrs2_core::qubit::QubitId(0),
1242                qubit2: quantrs2_core::qubit::QubitId(1),
1243                theta: 0.5,
1244                beta: 0.25,
1245            })
1246            .expect("add XXPlusYY");
1247
1248        let result = circuit_to_qasm3(&circuit);
1249        assert!(
1250            matches!(result, Err(DeviceError::CircuitConversion(_))),
1251            "expected honest CircuitConversion error, got {result:?}"
1252        );
1253    }
1254}