Repository: qBraid/pyqasm (language: python)

Issue:
[FEATURE] Migrate `qasm2_to_qasm3` function from qBraid SDK
Part of #12 

### Feature Description

Migrate [`qasm2_to_qasm3` function from qBraid-SDK](https://github.com/qBraid/qBraid/blob/7fe6e8360721936aa35e63141616453835bfa783/qbraid/transpiler/conversions/qasm2/qasm2_to_qasm3.py#L103) to the `pyqasm/modules.py::Qasm2Module`.  

### Implementation (Optional)

Develop a `to_qasm3()` method in the module with the following structure - 
```python
def to_qasm3(self, as_str : bool = True) -> Union[str, Qasm3Module]:
        """Convert the module to openqasm3 format
        
        Args:
            as_str (bool): Flag to indicate if the conversion should be to a string
                           or to a Qasm3Module object.
                           Default is True.

        Returns:
            Union[str, Qasm3Module]: The module in openqasm3 format.
        """
```



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5199742..16b2c31 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -105,6 +105,38 @@ Out[7]:
  'x q3[0];',
  'c[0] = measure q2[3];']
  ```
+ - Added the `to_qasm3()` method to the `Qasm2Module` class which can be used to convert a QASM2 program to QASM3 ([#62](https://github.com/qBraid/pyqasm/pull/62)). Usage is as follows - 
+ 
+ ```python
+ In [7]: import pyqasm
+
+In [8]: qasm2_str = """
+   ...:      OPENQASM 2.0;
+   ...:      include "qelib1.inc";
+   ...:      qreg q[2];
+   ...:      creg c[2];
+   ...:      h q[0];
+   ...:      cx q[0], q[1];
+   ...:      measure q -> c;
+   ...:      """
+
+In [9]: module = pyqasm.load(qasm2_str)
+
+In [10]: module.to_qasm3(as_str = True).splitlines()
+Out[10]:
+['OPENQASM 3.0;',
+ 'include "stdgates.inc";',
+ 'qubit[2] q;',
+ 'bit[2] c;',
+ 'h q[0];',
+ 'cx q[0], q[1];',
+ 'c = measure q;']
+
+In [11]: qasm3_mod = module.to_qasm3()
+
+In [12]: qasm3_mod
+Out[12]: <pyqasm.modules.qasm3.Qasm3Module at 0x107854ad0>
+ ```
 
 
 ### Improved / Modified
@@ -210,6 +242,7 @@ In [4]: module.validate()
 
 In [5]: module_copy = module.remove_measurements(in_place=False)
 ```
+- Restructured the `pyqasm` package to have a `modules` subpackage which contains the `QasmModule`, `Qasm2Module` and `Qasm3Module` classes ([#62](https://github.com/qBraid/pyqasm/pull/62))
 
 ### Deprecated
 
diff --git a/pyqasm/analyzer.py b/pyqasm/analyzer.py
index f6ce817..f28ecc5 100644
--- a/pyqasm/analyzer.py
+++ b/pyqasm/analyzer.py
@@ -30,11 +30,11 @@
 )
 from openqasm3.parser import QASM3ParsingError
 
-from .exceptions import QasmParsingError, ValidationError, raise_qasm3_error
+from pyqasm.exceptions import QasmParsingError, ValidationError, raise_qasm3_error
 
 if TYPE_CHECKING:
-    from .elements import Variable
-    from .expressions import Qasm3ExprEvaluator
+    from pyqasm.elements import Variable
+    from pyqasm.expressions import Qasm3ExprEvaluator
 
 
 class Qasm3Analyzer:
diff --git a/pyqasm/entrypoint.py b/pyqasm/entrypoint.py
index 37a04c4..2fae470 100644
--- a/pyqasm/entrypoint.py
+++ b/pyqasm/entrypoint.py
@@ -18,9 +18,9 @@
 
 import openqasm3
 
-from .exceptions import ValidationError
-from .maps import SUPPORTED_QASM_VERSIONS
-from .modules import Qasm2Module, Qasm3Module, QasmModule
+from pyqasm.exceptions import ValidationError
+from pyqasm.maps import SUPPORTED_QASM_VERSIONS
+from pyqasm.modules import Qasm2Module, Qasm3Module, QasmModule
 
 if TYPE_CHECKING:
     import openqasm3.ast
diff --git a/pyqasm/expressions.py b/pyqasm/expressions.py
index f39b283..dcfb5e9 100644
--- a/pyqasm/expressions.py
+++ b/pyqasm/expressions.py
@@ -32,10 +32,10 @@
 from openqasm3.ast import IntType as Qasm3IntType
 from openqasm3.ast import SizeOf, Statement, UnaryExpression
 
-from .analyzer import Qasm3Analyzer
-from .exceptions import ValidationError, raise_qasm3_error
-from .maps import CONSTANTS_MAP, qasm3_expression_op_map
-from .validator import Qasm3Validator
+from pyqasm.analyzer import Qasm3Analyzer
+from pyqasm.exceptions import ValidationError, raise_qasm3_error
+from pyqasm.maps import CONSTANTS_MAP, qasm3_expression_op_map
+from pyqasm.validator import Qasm3Validator
 
 
 class Qasm3ExprEvaluator:
diff --git a/pyqasm/maps.py b/pyqasm/maps.py
index e4a9742..fa26ac3 100644
--- a/pyqasm/maps.py
+++ b/pyqasm/maps.py
@@ -36,9 +36,9 @@
     UintType,
 )
 
-from .elements import InversionOp
-from .exceptions import ValidationError
-from .linalg import kak_decomposition_angles
+from pyqasm.elements import InversionOp
+from pyqasm.exceptions import ValidationError
+from pyqasm.linalg import kak_decomposition_angles
 
 # Define the type for the operator functions
 OperatorFunction = Union[
diff --git a/pyqasm/modules/__init__.py b/pyqasm/modules/__init__.py
new file mode 100644
index 0000000..e6cb095
--- /dev/null
+++ b/pyqasm/modules/__init__.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2024 qBraid
+#
+# This file is part of pyqasm
+#
+# Pyqasm is free software released under the GNU General Public License v3
+# or later. You can redistribute and/or modify it under the terms of the GPL v3.
+# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
+#
+# THERE IS NO WARRANTY for pyqasm, as per Section 15 of the GPL v3.
+
+"""
+Sub modules for handling different versions of OpenQASM.
+
+.. currentmodule:: pyqasm.modules
+
+Classes
+---------
+
+.. autosummary::
+   :toctree: ../stubs/
+
+   Qasm3Module
+   Qasm2Module
+   QasmModule
+
+"""
+
+from .base import QasmModule
+from .qasm2 import Qasm2Module
+from .qasm3 import Qasm3Module
+
+__all__ = [
+    "QasmModule",
+    "Qasm2Module",
+    "Qasm3Module",
+]
diff --git a/pyqasm/modules.py b/pyqasm/modules/base.py
similarity index 80%
rename from pyqasm/modules.py
rename to pyqasm/modules/base.py
index 3cb062c..e56228e 100644
--- a/pyqasm/modules.py
+++ b/pyqasm/modules/base.py
@@ -9,23 +9,21 @@
 # THERE IS NO WARRANTY for pyqasm, as per Section 15 of the GPL v3.
 
 """
-Module defining Qasm modules
+Definition of the base Qasm module
 """
 
-import re
 from abc import ABC, abstractmethod
 from copy import deepcopy
 from typing import Optional
 
 import openqasm3.ast as qasm3_ast
 from openqasm3.ast import Include, Program, Statement
-from openqasm3.printer import dumps
 
-from .analyzer import Qasm3Analyzer
-from .elements import ClbitDepthNode, QubitDepthNode
-from .exceptions import UnrollError, ValidationError
-from .maps import QUANTUM_STATEMENTS
-from .visitor import QasmVisitor
+from pyqasm.analyzer import Qasm3Analyzer
+from pyqasm.elements import ClbitDepthNode, QubitDepthNode
+from pyqasm.exceptions import UnrollError, ValidationError
+from pyqasm.maps import QUANTUM_STATEMENTS
+from pyqasm.visitor import QasmVisitor
 
 
 class QasmModule(ABC):  # pylint: disable=too-many-instance-attributes
@@ -461,107 +459,3 @@ def accept(self, visitor):
         Args:
             visitor (QasmVisitor): The visitor to accept
         """
-
-
-class Qasm2Module(QasmModule):
-    """
-    A module representing an unrolled openqasm2 quantum program.
-
-    Args:
-        name (str): Name of the module.
-        program (Program): The original openqasm2 program.
-        statements (list[Statement]): list of openqasm2 Statements.
-    """
-
-    def __init__(
-        self,
-        name: str,
-        program: Program,
-        statements: list,
-    ):
-        super().__init__(name, program, statements)
-        self._unrolled_ast = Program(statements=[Include("qelib1.inc")], version="2.0")
-        self._whitelist_statements = {
-            qasm3_ast.BranchingStatement,
-            qasm3_ast.QubitDeclaration,
-            qasm3_ast.ClassicalDeclaration,
-            qasm3_ast.Include,
-            qasm3_ast.QuantumGateDefinition,
-            qasm3_ast.QuantumGate,
-            qasm3_ast.QuantumMeasurement,
-            qasm3_ast.QuantumMeasurementStatement,
-            qasm3_ast.QuantumReset,
-            qasm3_ast.QuantumBarrier,
-        }
-
-    def _filter_statements(self):
-        """Filter statements according to the whitelist"""
-        for stmt in self._statements:
-            stmt_type = type(stmt)
-            if stmt_type not in self._whitelist_statements:
-                raise ValidationError(f"Statement of type {stmt_type} not supported in QASM 2.0")
-            # TODO: add more filtering here if needed
-
-    def _format_declarations(self, qasm_str):
-        """Format the unrolled qasm for declarations in openqasm 2.0 format"""
-        for declaration_type, replacement_type in [("qubit", "qreg"), ("bit", "creg")]:
-            pattern = rf"{declaration_type}\[(\d+)\]\s+(\w+);"
-            replacement = rf"{replacement_type} \2[\1];"
-            qasm_str = re.sub(pattern, replacement, qasm_str)
-        return qasm_str
-
-    def _qasm_ast_to_str(self, qasm_ast):
-        """Convert the qasm AST to a string"""
-        # set the version to 2.0
-        qasm_ast.version = "2.0"
-        raw_qasm = dumps(qasm_ast, old_measurement=True)
-        return self._format_declarations(raw_qasm)
-
-    def accept(self, visitor):
-        """Accept a visitor for the module
-
-        Args:
-            visitor (QasmVisitor): The visitor to accept
-        """
-        self._filter_statements()
-        unrolled_stmt_list = visitor.visit_basic_block(self._statements)
-        self.unrolled_ast.statements = [Include("qelib1.inc")]  # pylint: disable=W0201
-        self.unrolled_ast.statements.extend(unrolled_stmt_list)
-        # TODO: some finalizing method here probably
-
-
-class Qasm3Module(QasmModule):
-    """
-    A module representing an unrolled openqasm3 quantum program.
-
-    Args:
-        name (str): Name of the module.
-        program (Program): The original openqasm3 program.
-        statements (list[Statement]): list of openqasm3 Statements.
-    """
-
-    def __init__(
-        self,
-        name: str,
-        program: Program,
-        statements: list,
-    ):
-        super().__init__(name, program, statements)
-        self._unrolled_ast = Program(statements=[Include("stdgates.inc")], version="3.0")
-
-    def _qasm_ast_to_str(self, qasm_ast):
-        """Convert the qasm AST to a string"""
-        # set the version to 3.0
-        qasm_ast.version = "3.0"
-        return dumps(qasm_ast)
-
-    def accept(self, visitor):
-        """Accept a visitor for the module
-
-        Args:
-            visitor (QasmVisitor): The visitor to accept
-        """
-        unrolled_stmt_list = visitor.visit_basic_block(self._statements)
-        self.unrolled_ast.statements = [Include("stdgates.inc")]  # pylint: disable=W0201
-        self.unrolled_ast.statements.extend(unrolled_stmt_list)
-        # TODO: some finalizing method here probably
diff --git a/pyqasm/modules/qasm2.py b/pyqasm/modules/qasm2.py
new file mode 100644
index 0000000..ee66485
--- /dev/null
+++ b/pyqasm/modules/qasm2.py
@@ -0,0 +1,112 @@
+# Copyright (C) 2024 qBraid
+#
+# This file is part of pyqasm
+#
+# Pyqasm is free software released under the GNU General Public License v3
+# or later. You can redistribute and/or modify it under the terms of the GPL v3.
+# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
+#
+# THERE IS NO WARRANTY for pyqasm, as per Section 15 of the GPL v3.
+
+"""
+Defines a module for handling OpenQASM 2.0 programs.
+"""
+
+import re
+from copy import deepcopy
+from typing import Union
+
+import openqasm3.ast as qasm3_ast
+from openqasm3.ast import Include, Program
+from openqasm3.printer import dumps
+
+from pyqasm.exceptions import ValidationError
+from pyqasm.modules.base import QasmModule
+from pyqasm.modules.qasm3 import Qasm3Module
+
+
+class Qasm2Module(QasmModule):
+    """
+    A module representing an openqasm2 quantum program.
+
+    Args:
+        name (str): Name of the module.
+        program (Program): The original openqasm2 program.
+        statements (list[Statement]): list of openqasm2 Statements.
+    """
+
+    def __init__(
+        self,
+        name: str,
+        program: Program,
+        statements: list,
+    ):
+        super().__init__(name, program, statements)
+        self._unrolled_ast = Program(statements=[Include("qelib1.inc")], version="2.0")
+        self._whitelist_statements = {
+            qasm3_ast.BranchingStatement,
+            qasm3_ast.QubitDeclaration,
+            qasm3_ast.ClassicalDeclaration,
+            qasm3_ast.Include,
+            qasm3_ast.QuantumGateDefinition,
+            qasm3_ast.QuantumGate,
+            qasm3_ast.QuantumMeasurement,
+            qasm3_ast.QuantumMeasurementStatement,
+            qasm3_ast.QuantumReset,
+            qasm3_ast.QuantumBarrier,
+        }
+
+    def _filter_statements(self):
+        """Filter statements according to the whitelist"""
+        for stmt in self._statements:
+            stmt_type = type(stmt)
+            if stmt_type not in self._whitelist_statements:
+                raise ValidationError(f"Statement of type {stmt_type} not supported in QASM 2.0")
+            # TODO: add more filtering here if needed
+
+    def _format_declarations(self, qasm_str):
+        """Format the unrolled qasm for declarations in openqasm 2.0 format"""
+        for declaration_type, replacement_type in [("qubit", "qreg"), ("bit", "creg")]:
+            pattern = rf"{declaration_type}\[(\d+)\]\s+(\w+);"
+            replacement = rf"{replacement_type} \2[\1];"
+            qasm_str = re.sub(pattern, replacement, qasm_str)
+        return qasm_str
+
+    def _qasm_ast_to_str(self, qasm_ast):
+        """Convert the qasm AST to a string"""
+        # set the version to 2.0
+        qasm_ast.version = "2.0"
+        raw_qasm = dumps(qasm_ast, old_measurement=True)
+        return self._format_declarations(raw_qasm)
+
+    def to_qasm3(self, as_str: bool = False) -> Union[str, Qasm3Module]:
+        """Convert the module to openqasm3 format
+
+        Args:
+            as_str (bool): Flag to indicate if the conversion should be to a string
+                           or to a Qasm3Module object.
+                           Default is False.
+
+        Returns:
+            Union[str, Qasm3Module]: The module in openqasm3 format.
+        """
+        qasm_program = deepcopy(self._original_program)
+        # replace the include with stdgates.inc
+        for stmt in qasm_program.statements:
+            if isinstance(stmt, Include) and stmt.filename == "qelib1.inc":
+                stmt.filename = "stdgates.inc"
+                break
+        qasm_program.version = "3.0"
+        return dumps(qasm_program) if as_str else Qasm3Module.from_program(qasm_program)
+
+    def accept(self, visitor):
+        """Accept a visitor for the module
+
+        Args:
+            visitor (QasmVisitor): The visitor to accept
+        """
+        self._filter_statements()
+        unrolled_stmt_list = visitor.visit_basic_block(self._statements)
+        self.unrolled_ast.statements = [Include("qelib1.inc")]  # pylint: disable=W0201
+        self.unrolled_ast.statements.extend(unrolled_stmt_list)
+        # TODO: some finalizing method here probably
diff --git a/pyqasm/modules/qasm3.py b/pyqasm/modules/qasm3.py
new file mode 100644
index 0000000..6f0197a
--- /dev/null
+++ b/pyqasm/modules/qasm3.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2024 qBraid
+#
+# This file is part of pyqasm
+#
+# Pyqasm is free software released under the GNU General Public License v3
+# or later. You can redistribute and/or modify it under the terms of the GPL v3.
+# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
+#
+# THERE IS NO WARRANTY for pyqasm, as per Section 15 of the GPL v3.
+
+"""
+Defines a module for handling OpenQASM 3.0 programs.
+"""
+
+from openqasm3.ast import Include, Program
+from openqasm3.printer import dumps
+
+from pyqasm.modules.base import QasmModule
+
+
+class Qasm3Module(QasmModule):
+    """
+    A module representing an openqasm3 quantum program.
+
+    Args:
+        name (str): Name of the module.
+        program (Program): The original openqasm3 program.
+        statements (list[Statement]): list of openqasm3 Statements.
+    """
+
+    def __init__(
+        self,
+        name: str,
+        program: Program,
+        statements: list,
+    ):
+        super().__init__(name, program, statements)
+        self._unrolled_ast = Program(statements=[Include("stdgates.inc")], version="3.0")
+
+    def _qasm_ast_to_str(self, qasm_ast):
+        """Convert the qasm AST to a string"""
+        # set the version to 3.0
+        qasm_ast.version = "3.0"
+        return dumps(qasm_ast)
+
+    def accept(self, visitor):
+        """Accept a visitor for the module
+
+        Args:
+            visitor (QasmVisitor): The visitor to accept
+        """
+        unrolled_stmt_list = visitor.visit_basic_block(self._statements)
+        self.unrolled_ast.statements = [Include("stdgates.inc")]  # pylint: disable=W0201
+        self.unrolled_ast.statements.extend(unrolled_stmt_list)
+        # TODO: some finalizing method here probably
diff --git a/pyqasm/subroutines.py b/pyqasm/subroutines.py
index 3515746..105ff2b 100644
--- a/pyqasm/subroutines.py
+++ b/pyqasm/subroutines.py
@@ -23,12 +23,12 @@
     QubitDeclaration,
 )
 
-from .analyzer import Qasm3Analyzer
-from .elements import Variable
-from .exceptions import raise_qasm3_error
-from .expressions import Qasm3ExprEvaluator
-from .transformer import Qasm3Transformer
-from .validator import Qasm3Validator
+from pyqasm.analyzer import Qasm3Analyzer
+from pyqasm.elements import Variable
+from pyqasm.exceptions import raise_qasm3_error
+from pyqasm.expressions import Qasm3ExprEvaluator
+from pyqasm.transformer import Qasm3Transformer
+from pyqasm.validator import Qasm3Validator
 
 
 class Qasm3SubroutineProcessor:
diff --git a/pyqasm/transformer.py b/pyqasm/transformer.py
index 3a5233d..c3f3d70 100644
--- a/pyqasm/transformer.py
+++ b/pyqasm/transformer.py
@@ -37,11 +37,11 @@
     UnaryOperator,
 )
 
-from .elements import Variable
-from .exceptions import raise_qasm3_error
-from .expressions import Qasm3ExprEvaluator
-from .maps import VARIABLE_TYPE_MAP
-from .validator import Qasm3Validator
+from pyqasm.elements import Variable
+from pyqasm.exceptions import raise_qasm3_error
+from pyqasm.expressions import Qasm3ExprEvaluator
+from pyqasm.maps import VARIABLE_TYPE_MAP
+from pyqasm.validator import Qasm3Validator
 
 # mypy: disable-error-code="attr-defined, union-attr"
 
diff --git a/pyqasm/validator.py b/pyqasm/validator.py
index 0e63c26..3973e6c 100644
--- a/pyqasm/validator.py
+++ b/pyqasm/validator.py
@@ -19,9 +19,9 @@
 from openqasm3.ast import IntType as Qasm3IntType
 from openqasm3.ast import QuantumGate, QuantumGateDefinition, ReturnStatement, SubroutineDefinition
 
-from .elements import Variable
-from .exceptions import ValidationError, raise_qasm3_error
-from .maps import LIMITS_MAP, VARIABLE_TYPE_MAP, qasm_variable_type_cast
+from pyqasm.elements import Variable
+from pyqasm.exceptions import ValidationError, raise_qasm3_error
+from pyqasm.maps import LIMITS_MAP, VARIABLE_TYPE_MAP, qasm_variable_type_cast
 
 
 class Qasm3Validator:
diff --git a/pyqasm/visitor.py b/pyqasm/visitor.py
index e2c0c89..81458be 100644
--- a/pyqasm/visitor.py
+++ b/pyqasm/visitor.py
@@ -22,11 +22,11 @@
 import numpy as np
 import openqasm3.ast as qasm3_ast
 
-from .analyzer import Qasm3Analyzer
-from .elements import ClbitDepthNode, Context, InversionOp, QubitDepthNode, Variable
-from .exceptions import ValidationError, raise_qasm3_error
-from .expressions import Qasm3ExprEvaluator
-from .maps import (
+from pyqasm.analyzer import Qasm3Analyzer
+from pyqasm.elements import ClbitDepthNode, Context, InversionOp, QubitDepthNode, Variable
+from pyqasm.exceptions import ValidationError, raise_qasm3_error
+from pyqasm.expressions import Qasm3ExprEvaluator
+from pyqasm.maps import (
     ARRAY_TYPE_MAP,
     CONSTANTS_MAP,
     MAX_ARRAY_DIMENSIONS,
@@ -34,9 +34,9 @@
     map_qasm_inv_op_to_callable,
     map_qasm_op_to_callable,
 )
-from .subroutines import Qasm3SubroutineProcessor
-from .transformer import Qasm3Transformer
-from .validator import Qasm3Validator
+from pyqasm.subroutines import Qasm3SubroutineProcessor
+from pyqasm.transformer import Qasm3Transformer
+from pyqasm.validator import Qasm3Validator
 
 logger = logging.getLogger(__name__)
 


Test patch:
diff --git a/tests/qasm2/test_to_qasm3.py b/tests/qasm2/test_to_qasm3.py
new file mode 100644
index 0000000..1211fcf
--- /dev/null
+++ b/tests/qasm2/test_to_qasm3.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2024 qBraid
+#
+# This file is part of pyqasm
+#
+# Pyqasm is free software released under the GNU General Public License v3
+# or later. You can redistribute and/or modify it under the terms of the GPL v3.
+# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
+#
+# THERE IS NO WARRANTY for pyqasm, as per Section 15 of the GPL v3.
+
+"""
+Module containing unit tests for conversion to qasm3.
+
+"""
+
+from pyqasm.entrypoint import load
+from pyqasm.modules.qasm3 import Qasm3Module
+from tests.utils import check_unrolled_qasm
+
+
+def test_to_qasm3_str():
+    qasm2_string = """
+    OPENQASM 2.0;
+    include "qelib1.inc";
+    qreg q[1];
+    creg c[1];
+    h q;
+    measure q -> c;
+    """
+    result = load(qasm2_string)
+
+    expected_qasm3 = """
+    OPENQASM 3.0;
+    include "stdgates.inc";
+    qubit[1] q;
+    bit[1] c;
+    h q;
+    c = measure q;
+    """
+
+    returned_qasm3 = result.to_qasm3(as_str=True)
+    assert isinstance(returned_qasm3, str)
+    check_unrolled_qasm(returned_qasm3, expected_qasm3)
+
+
+def test_to_qasm3_module():
+    qasm2_string = """
+    OPENQASM 2.0;
+    include "stdgates.inc";
+    qreg q[1];
+    creg c[1];
+    h q;
+    measure q -> c;
+    """
+    result = load(qasm2_string)
+
+    qasm3_module = result.to_qasm3(as_str=False)
+    assert isinstance(qasm3_module, Qasm3Module)
+    qasm3_module.unroll()
+    assert qasm3_module.num_qubits == 1
+    assert qasm3_module.num_clbits == 1
+    assert qasm3_module.depth() == 2