From 8d285d46e11de682b33ce4e482e70f7eea0e9e41 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Tue, 4 Aug 2026 15:40:00 -0400 Subject: [PATCH 01/21] Standardize the use of periods and fix a typo --- src/pyqasm/modules/base.py | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index ea2ac84..6305cbf 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -63,7 +63,7 @@ def wrapper(self, *args, **kwargs): class QasmModule(ABC): # pylint: disable=too-many-instance-attributes, too-many-public-methods - """Abstract class for a Qasm module + """Abstract class for a Qasm module. Args: name (str): Name of the module. @@ -117,14 +117,14 @@ def num_qubits(self) -> int: @num_qubits.setter def num_qubits(self, value: int): - """Setter for the number of qubits""" + """Setter for the number of qubits.""" self._num_qubits = value def _add_qubit_register(self, reg_name: str, num_qubits: int): - """Add qubits to the module + """Add qubits to the module. Args: - num_qubits (int): The number of qubits to add to the module + num_qubits (int): The number of qubits to add to the module. Returns: None @@ -142,14 +142,14 @@ def num_clbits(self) -> int: @num_clbits.setter def num_clbits(self, value: int): - """Setter for the number of classical bits""" + """Setter for the number of classical bits.""" self._num_clbits = value def _add_classical_register(self, reg_name: str, num_clbits: int): - """Add classical bits to the module + """Add classical bits to the module. Args: - num_clbits (int): The number of classical bits to add to the module + num_clbits (int): The number of classical bits to add to the module. Returns: None @@ -159,17 +159,17 @@ def _add_classical_register(self, reg_name: str, num_clbits: int): @property def original_program(self) -> Program: - """Returns the program AST for the original qasm supplied by the user""" + """Returns the program AST for the original qasm supplied by the user.""" return self._original_program @property def unrolled_ast(self) -> Program: - """Returns the unrolled AST for the module""" + """Returns the unrolled AST for the module.""" return self._unrolled_ast @unrolled_ast.setter def unrolled_ast(self, value: Program): - """Setter for the unrolled AST""" + """Setter for the unrolled AST.""" self._unrolled_ast = value def has_measurements(self) -> bool: @@ -191,13 +191,13 @@ def has_measurements(self) -> bool: @track_user_operation def remove_measurements(self, in_place: bool = True) -> Optional["QasmModule"]: - """Remove the measurement operations + """Remove the measurement operations. Args: in_place (bool): Flag to indicate if the removal should be done in place. Returns: - QasmModule: The module with the measurements removed if in_place is False + QasmModule: The module with the measurements removed if in_place is False. """ stmt_list = ( self._statements @@ -232,7 +232,7 @@ def has_barriers(self) -> bool: None Returns: - bool: True if the module has barrier operations, False otherwise + bool: True if the module has barrier operations, False otherwise. """ if self._has_barriers is None: self._has_barriers = False @@ -251,7 +251,7 @@ def has_barriers(self) -> bool: @track_user_operation def remove_barriers(self, in_place: bool = True) -> Optional["QasmModule"]: - """Remove the barrier operations + """Remove the barrier operations. Args: in_place (bool): Flag to indicate if the removal should be done in place. @@ -282,13 +282,13 @@ def remove_barriers(self, in_place: bool = True) -> Optional["QasmModule"]: @track_user_operation def remove_includes(self, in_place=True) -> Optional["QasmModule"]: - """Remove the include statements from the module + """Remove the include statements from the module. Args: in_place (bool): Flag to indicate if the removal should be done in place. Returns: - QasmModule: The module with the includes removed if in_place is False, None otherwise + QasmModule: The module with the includes removed if in_place is False, None otherwise. """ stmt_list = ( self._statements @@ -317,7 +317,7 @@ def depth(self, decompose_native_gates=True): Defaults to True. Returns: - int: The depth of the current "unrolled" openqasm program + int: The depth of the current "unrolled" openqasm program. """ # 1. Since the program will be unrolled before its execution on a QC, it makes sense to # calculate the depth of the unrolled program. @@ -348,7 +348,7 @@ def depth(self, decompose_native_gates=True): def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]): """Remap the qubits in a register after removing idle qubits and update the operations - using this register accordingly""" + using this register accordingly.""" used_indices = [idx for idx in range(size) if idx not in idle_indices] new_size = size - len(idle_indices) @@ -393,11 +393,11 @@ def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]): index_node.value = idx_map[index_node.value] # type: ignore[union-attr] def _get_idle_qubit_indices(self) -> dict[str, list[int]]: - """Get the indices of the idle qubits in the module + """Get the indices of the idle qubits in the module. Returns: dict[str, list[int]]: A dictionary mapping the register name to the list of idle qubit - indices in that register + indices in that register. """ idle_qubits = [qubit for qubit in self._qubit_depths.values() if qubit.is_idle()] @@ -411,9 +411,9 @@ def _get_idle_qubit_indices(self) -> dict[str, list[int]]: return qubit_indices def populate_idle_qubits(self, in_place: bool = True): - """Populate the idle qubits in the module with identity gates + """Populate the idle qubits in the module with identity gates. - Note: unrolling is not performed while calling this function + Note: unrolling is not performed while calling this function. Args: in_place (bool): Flag to indicate if the population should be done in place. @@ -742,10 +742,10 @@ def compare(self, other_module: QasmModule): ) def __str__(self) -> str: - """Return the string representation of the QASM program + """Return the string representation of the QASM program. Returns: - str: The string representation of the module + str: The string representation of the module. """ if len(self._unrolled_ast.statements) > 1: @@ -753,17 +753,17 @@ def __str__(self) -> str: return self._qasm_ast_to_str(self.original_program) def copy(self): - """Return a deep copy of the module""" + """Return a deep copy of the module.""" return deepcopy(self) @abstractmethod def _qasm_ast_to_str(self, qasm_ast): - """Convert the qasm AST to a string""" + """Convert the qasm AST to a string.""" @abstractmethod def accept(self, visitor): - """Accept a visitor for the mßodule + """Accept a visitor for the module. Args: - visitor (QasmVisitor): The visitor to accept + visitor (QasmVisitor): The visitor to accept. """ From d547fe19f772294efd22d12225e5b01ed1bc75ed Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Tue, 4 Aug 2026 22:27:19 -0400 Subject: [PATCH 02/21] modules typing --- src/pyqasm/modules/base.py | 38 ++++++++++++++++++------------------- src/pyqasm/modules/qasm2.py | 23 +++++++++++----------- src/pyqasm/modules/qasm3.py | 13 +++++++------ src/pyqasm/visitor.py | 2 +- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 6305cbf..0691029 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -22,21 +22,21 @@ from abc import ABC, abstractmethod from collections import Counter from copy import deepcopy -from typing import Optional +from typing import Any, Callable, Optional, Self import openqasm3.ast as qasm3_ast from openqasm3.ast import BranchingStatement, Program, QuantumGate from pyqasm.analyzer import Qasm3Analyzer from pyqasm.decomposer import Decomposer -from pyqasm.elements import ClbitDepthNode, QubitDepthNode +from pyqasm.elements import BasisSet, ClbitDepthNode, QubitDepthNode from pyqasm.exceptions import UnrollError, ValidationError from pyqasm.maps import QUANTUM_STATEMENTS from pyqasm.maps.decomposition_rules import DECOMPOSITION_RULES from pyqasm.visitor import QasmVisitor, ScopeManager -def track_user_operation(func): +def track_user_operation(func: Callable[..., Any]) -> Callable[..., Any]: """Decorator to track user operations on a QasmModule.""" @functools.wraps(func) @@ -116,7 +116,7 @@ def num_qubits(self) -> int: return self._num_qubits @num_qubits.setter - def num_qubits(self, value: int): + def num_qubits(self, value: int) -> None: """Setter for the number of qubits.""" self._num_qubits = value @@ -141,7 +141,7 @@ def num_clbits(self) -> int: return self._num_clbits @num_clbits.setter - def num_clbits(self, value: int): + def num_clbits(self, value: int) -> None: """Setter for the number of classical bits.""" self._num_clbits = value @@ -168,7 +168,7 @@ def unrolled_ast(self) -> Program: return self._unrolled_ast @unrolled_ast.setter - def unrolled_ast(self, value: Program): + def unrolled_ast(self, value: Program) -> None: """Setter for the unrolled AST.""" self._unrolled_ast = value @@ -281,7 +281,7 @@ def remove_barriers(self, in_place: bool = True) -> Optional["QasmModule"]: return curr_module @track_user_operation - def remove_includes(self, in_place=True) -> Optional["QasmModule"]: + def remove_includes(self, in_place: bool = True) -> Optional["QasmModule"]: """Remove the include statements from the module. Args: @@ -308,7 +308,7 @@ def remove_includes(self, in_place=True) -> Optional["QasmModule"]: return curr_module @track_user_operation - def depth(self, decompose_native_gates=True): + def depth(self, decompose_native_gates: bool = True) -> int: """Calculate the depth of the unrolled openqasm program. Args: @@ -346,7 +346,7 @@ def depth(self, decompose_native_gates=True): max_depth = max(max_qubit_depth, max_clbit_depth) return max_depth - def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]): + def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]) -> None: """Remap the qubits in a register after removing idle qubits and update the operations using this register accordingly.""" @@ -410,7 +410,7 @@ def _get_idle_qubit_indices(self) -> dict[str, list[int]]: return qubit_indices - def populate_idle_qubits(self, in_place: bool = True): + def populate_idle_qubits(self, in_place: bool = True) -> QasmModule: """Populate the idle qubits in the module with identity gates. Note: unrolling is not performed while calling this function. @@ -453,7 +453,7 @@ def populate_idle_qubits(self, in_place: bool = True): return qasm_module - def remove_idle_qubits(self, in_place: bool = True): + def remove_idle_qubits(self, in_place: bool = True) -> QasmModule: """Remove idle qubits from the module. Either collapse the size of a partially used quantum register OR remove the unused quantum register entirely. @@ -506,7 +506,7 @@ def remove_idle_qubits(self, in_place: bool = True): return qasm_module @track_user_operation - def reverse_qubit_order(self, in_place=True): + def reverse_qubit_order(self, in_place: bool = True) -> QasmModule: """Reverse the order of qubits in the module. Will unroll the module if not already done. @@ -567,7 +567,7 @@ def reverse_qubit_order(self, in_place=True): return qasm_module @track_user_operation - def validate(self): + def validate(self) -> None: """Validate the module""" if self._validated_program is True: return @@ -588,7 +588,7 @@ def validate(self): self._validated_program = True @track_user_operation - def unroll(self, **kwargs): + def unroll(self, **kwargs: Any) -> None: """Unroll the module into basic qasm operations. Args: @@ -630,7 +630,7 @@ def unroll(self, **kwargs): raise err @track_user_operation - def rebase(self, target_basis_set, in_place=True): + def rebase(self, target_basis_set: BasisSet, in_place: bool = True) -> QasmModule: """Rebase the AST to use a specified target basis set. Will unroll the module if not already done. @@ -693,7 +693,7 @@ def _get_gate_counts(self) -> dict[str, int]: ] return dict(Counter(gate.name.name for gate in gate_nodes)) - def compare(self, other_module: QasmModule): + def compare(self, other_module: QasmModule) -> None: """Compare two QasmModule objects across multiple attributes. Args: @@ -752,16 +752,16 @@ def __str__(self) -> str: return self._qasm_ast_to_str(self.unrolled_ast) return self._qasm_ast_to_str(self.original_program) - def copy(self): + def copy(self) -> Self: """Return a deep copy of the module.""" return deepcopy(self) @abstractmethod - def _qasm_ast_to_str(self, qasm_ast): + def _qasm_ast_to_str(self, qasm_ast: Program) -> str: """Convert the qasm AST to a string.""" @abstractmethod - def accept(self, visitor): + def accept(self, visitor: QasmVisitor) -> None: """Accept a visitor for the module. Args: diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index f4b0de9..024a83b 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -26,6 +26,7 @@ from pyqasm.exceptions import ValidationError from pyqasm.modules.base import QasmModule from pyqasm.modules.qasm3 import Qasm3Module +from pyqasm.visitor import QasmVisitor class Qasm2Module(QasmModule): @@ -38,7 +39,7 @@ class Qasm2Module(QasmModule): statements (list[Statement]): list of openqasm2 Statements. """ - def __init__(self, name: str, program: Program): + def __init__(self, name: str, program: Program) -> None: super().__init__(name, program) self._unrolled_ast = Program(statements=[], version="2.0") self._whitelist_statements = { @@ -54,31 +55,31 @@ def __init__(self, name: str, program: Program): qasm3_ast.QuantumBarrier, } - def _filter_statements(self): - """Filter statements according to the whitelist""" + def _filter_statements(self) -> None: + """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""" + def _format_declarations(self, qasm_str: str) -> 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""" + def _qasm_ast_to_str(self, qasm_ast: Program) -> str: + """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) -> str | Qasm3Module: - """Convert the module to openqasm3 format + """Convert the module to openqasm3 format. Args: as_str (bool): Flag to indicate if the conversion should be to a string @@ -97,11 +98,11 @@ def to_qasm3(self, as_str: bool = False) -> str | Qasm3Module: qasm_program.version = "3.0" return dumps(qasm_program) if as_str else Qasm3Module(self._name, qasm_program) - def accept(self, visitor): - """Accept a visitor for the module + def accept(self, visitor: QasmVisitor) -> None: + """Accept a visitor for the module. Args: - visitor (QasmVisitor): The visitor to accept + visitor (QasmVisitor): The visitor to accept. """ self._filter_statements() unrolled_stmt_list = visitor.visit_basic_block(self._statements) diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 8ed08d5..974fb61 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -20,6 +20,7 @@ from openqasm3.printer import dumps from pyqasm.modules.base import QasmModule +from pyqasm.visitor import QasmVisitor class Qasm3Module(QasmModule): @@ -32,21 +33,21 @@ class Qasm3Module(QasmModule): statements (list[Statement]): list of openqasm3 Statements. """ - def __init__(self, name: str, program: Program): + def __init__(self, name: str, program: Program) -> None: super().__init__(name, program) self._unrolled_ast = Program(statements=[], version="3.0") - def _qasm_ast_to_str(self, qasm_ast): - """Convert the qasm AST to a string""" + def _qasm_ast_to_str(self, qasm_ast: Program) -> str: + """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 + def accept(self, visitor: QasmVisitor) -> None: + """Accept a visitor for the module. Args: - visitor (QasmVisitor): The visitor to accept + visitor (QasmVisitor): The visitor to accept. """ unrolled_stmt_list = visitor.visit_basic_block(self._statements) final_stmt_list = visitor.finalize(unrolled_stmt_list) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index fd4d0dd..0535564 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -3307,7 +3307,7 @@ def visit_basic_block(self, stmt_list: list[qasm3_ast.Statement]) -> list[qasm3_ result.extend(self.visit_statement(stmt)) return result - def finalize(self, unrolled_stmts): + def finalize(self, unrolled_stmts: list[qasm3_ast.Statement]) -> list[qasm3_ast.Statement]: """Finalize the unrolled statements. Rules: - Remove qubit args from phase operations if ALL qubits are used From 99c9adf5e43bc5f115fc53990686b233656c446f Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Wed, 5 Aug 2026 09:40:44 -0400 Subject: [PATCH 03/21] revert while waiting for Pragma --- src/pyqasm/modules/base.py | 2 +- src/pyqasm/modules/qasm2.py | 2 +- src/pyqasm/modules/qasm3.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 0691029..8f0c61f 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -761,7 +761,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: """Convert the qasm AST to a string.""" @abstractmethod - def accept(self, visitor: QasmVisitor) -> None: + def accept(self, visitor) -> None: """Accept a visitor for the module. Args: diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index 024a83b..9e33b49 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -98,7 +98,7 @@ def to_qasm3(self, as_str: bool = False) -> str | Qasm3Module: qasm_program.version = "3.0" return dumps(qasm_program) if as_str else Qasm3Module(self._name, qasm_program) - def accept(self, visitor: QasmVisitor) -> None: + def accept(self, visitor) -> None: """Accept a visitor for the module. Args: diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 974fb61..7d0b237 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -43,7 +43,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: qasm_ast.version = "3.0" return dumps(qasm_ast) - def accept(self, visitor: QasmVisitor) -> None: + def accept(self, visitor) -> None: """Accept a visitor for the module. Args: From a4a2100af5dc1e937b4d4e655902cfd7d79c7ca7 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Wed, 5 Aug 2026 10:34:58 -0400 Subject: [PATCH 04/21] More docstring work, add to changelog --- CHANGELOG.md | 2 + src/pyqasm/modules/base.py | 36 ++++++++++-- src/pyqasm/modules/qasm2.py | 3 + src/pyqasm/modules/qasm3.py | 3 + src/pyqasm/visitor.py | 114 +++++++++++++++++++++++------------- 5 files changed, 111 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc340e8..8070d9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Types of changes: ### Improved / Modified - Consolidated the hardcoded `"__PYQASM_QUBITS__"` string literals scattered across `visitor.py`, `transformer.py` and `pulse/utils.py` into a single `INTERNAL_QUBIT_REGISTER` constant in `elements.py`, alongside an `is_internal_qubit_register()` helper that is now the one place the internal register is recognised. ([#325](https://github.com/qBraid/pyqasm/pull/325)) +- Added type hinting for `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. ([#346](https://github.com/qBraid/pyqasm/pull/346)) ### Deprecated @@ -35,6 +36,7 @@ Types of changes: - Fixed the `ctrl @` modifier not resolving gate aliases: `ctrl @ toffoli` / `ctrl @ ccnot` (aliases of `ccx`) and `ctrl @ cnot` / `ctrl @ CX` (aliases of `cx`) now escalate controls identically to their canonical gate instead of raising `Unsupported controlled QASM operation`. ([#320](https://github.com/qBraid/pyqasm/pull/320)) - Fixed classical register declarations not being visible inside `box` scope, causing "Missing clbit register declaration" errors for measurement statements inside box blocks. ([#306](https://github.com/qBraid/pyqasm/pull/306)) - Fixed the backend-dependent `dt` duration unit being incorrectly relabeled as `ns` when unrolling `delay` and `box` statements without a `device_cycle_time`. Since `dt` cannot be converted to SI units without a sample rate, it is now preserved as `dt`. ([#317](https://github.com/qBraid/pyqasm/pull/317)) +- Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. ([#346](https://github.com/qBraid/pyqasm/pull/346)) ### Dependencies - Migrated the Linux wheel *build container* from `manylinux2014` to `manylinux_2_28`. NumPy stopped publishing `manylinux2014` (glibc 2.17) wheels for CPython 3.12+, so `pip` fell back to building NumPy from source inside the build container, whose GCC is older than NumPy requires — failing every Linux wheel job for cp312/cp313/cp314. The published wheels are unaffected: auditwheel tags them from the extension's actual symbol requirements, so they remain `manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64` and still install on glibc 2.17 systems. diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 8f0c61f..72b479e 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -145,7 +145,7 @@ def num_clbits(self, value: int) -> None: """Setter for the number of classical bits.""" self._num_clbits = value - def _add_classical_register(self, reg_name: str, num_clbits: int): + def _add_classical_register(self, reg_name: str, num_clbits: int) -> None: """Add classical bits to the module. Args: @@ -173,7 +173,14 @@ def unrolled_ast(self, value: Program) -> None: self._unrolled_ast = value def has_measurements(self) -> bool: - """Check if the module has any measurement operations.""" + """Check if the module has any measurement operations. + + Args: + None + + Returns: + bool: True if the module has measurement operations, False otherwise. + """ if self._has_measurements is None: self._has_measurements = False # try to check in the unrolled version as that will a better indicator of @@ -312,7 +319,7 @@ def depth(self, decompose_native_gates: bool = True) -> int: """Calculate the depth of the unrolled openqasm program. Args: - decompose_native_gates (bool): If True, calculate depth after decomposing gates. + decompose_native_gates (bool): If True, calculate depth after decomposing gates. If False, treat all decompsable gates as a single gate operation. Defaults to True. @@ -348,14 +355,22 @@ def depth(self, decompose_native_gates: bool = True) -> int: def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]) -> None: """Remap the qubits in a register after removing idle qubits and update the operations - using this register accordingly.""" + using this register accordingly. + Args: + reg_name (str): The name of the register to be remapped. + size (int): The size of the register. + idle_indices (list[int]): Indices of idle qubits to be remapped away. + + Returns: + None + """ used_indices = [idx for idx in range(size) if idx not in idle_indices] new_size = size - len(idle_indices) idx_map = {used_indices[i]: i for i in range(new_size)} # old_idx : new_idx # Example - - # reg_name = "q", original_size = 5, idle_indices = [1, 3] + # reg_name = "q", size = 5, idle_indices = [1, 3] # used_indices = [0, 2, 4], new_size = 3 # idx_map = {0: 0, 2: 1, 4: 2} @@ -633,7 +648,7 @@ def unroll(self, **kwargs: Any) -> None: def rebase(self, target_basis_set: BasisSet, in_place: bool = True) -> QasmModule: """Rebase the AST to use a specified target basis set. - Will unroll the module if not already done. + Note: Will unroll the module if not already done. Args: target_basis_set: The target basis set to rebase the module to. @@ -682,6 +697,9 @@ def rebase(self, target_basis_set: BasisSet, in_place: bool = True) -> QasmModul def _get_gate_counts(self) -> dict[str, int]: """Return a dictionary of gate counts in the unrolled program. + Args: + None + Returns: dict[str, int]: A dictionary of gate counts. """ @@ -698,6 +716,9 @@ def compare(self, other_module: QasmModule) -> None: Args: other_module (QasmModule): The module to compare with. + + Returns: + None """ try: # pylint: disable-next=import-outside-toplevel from tabulate import tabulate @@ -766,4 +787,7 @@ def accept(self, visitor) -> None: Args: visitor (QasmVisitor): The visitor to accept. + + Returns: + None """ diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index 9e33b49..3757d48 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -103,6 +103,9 @@ def accept(self, visitor) -> None: Args: visitor (QasmVisitor): The visitor to accept. + + Returns: + None """ self._filter_statements() unrolled_stmt_list = visitor.visit_basic_block(self._statements) diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 7d0b237..97a7e80 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -48,6 +48,9 @@ def accept(self, visitor) -> None: Args: visitor (QasmVisitor): The visitor to accept. + + Returns: + None """ unrolled_stmt_list = visitor.visit_basic_block(self._statements) final_stmt_list = visitor.finalize(unrolled_stmt_list) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 0535564..0137b71 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -202,7 +202,7 @@ def _visit_quantum_register( register (QubitDeclaration): The register name and size. Returns: - None + The list containing the register """ logger.debug("Visiting register '%s'", str(register)) @@ -408,7 +408,7 @@ def _check_variable_type_size( base_type (Any): Base type of the variable. is_const (bool): whether the statement is constant declaration or not. Returns: - Int: size of the variable base type. + int: size of the variable base type. """ base_size = 1 if not isinstance(base_type, qasm3_ast.BoolType): @@ -486,7 +486,12 @@ def _qubit_register_consolidation( Consolidate all quantum registers into a single register '__PYQASM_QUBITS__'. Args: - unrolled_stmts (list): The list of statements to process and modify in-place. + unrolled_stmts (list): The list of non-QubitDeclaration statements to append + after consolidating the quantum registers. + total_qubits (int): The total number of allocated qubits in quantum registers. + + Returns: + A new list of statements with the quantum register statements consolidated. Raises: ValidationError: If the total number of qubits exceeds the available device qubits, @@ -520,13 +525,16 @@ def _qubit_register_consolidation( return _valid_statements def _handle_function_init_expression( - self, expression: Any, init_value: Any + self, expression: qasm3_ast.FunctionCall, init_value: Any ) -> None | qasm3_ast.Expression: """Handle function initialization expression. Args: - statement (Any): The statement to handle function initialization expression. + expression (Any): The statement to handle function initialization expression. init_value (Any): The value to handle function initialization expression. + + Returns: + The resultant expression if """ if isinstance(expression, qasm3_ast.FunctionCall): func_name = expression.name.name @@ -567,7 +575,7 @@ def _visit_measurement( # pylint: disable=too-many-locals,too-many-branches,too statement (qasm3_ast.QuantumMeasurementStatement): The measurement statement to visit. Returns: - None + The list of unrolled measurements. """ logger.debug("Visiting measurement statement '%s'", str(statement)) @@ -605,7 +613,7 @@ def _visit_measurement( # pylint: disable=too-many-locals,too-many-branches,too ) if is_pulse_gate: return [statement] - # # TODO: handle in-function measurements + # TODO: handle in-function measurements source_name: str = ( source.name if isinstance(source, qasm3_ast.Identifier) else source.name.name ) @@ -738,7 +746,7 @@ def _visit_reset(self, statement: qasm3_ast.QuantumReset) -> list[qasm3_ast.Quan statement (qasm3_ast.QuantumReset): The reset statement to visit. Returns: - None + The list of unrolled resets. """ logger.debug("Visiting reset statement '%s'", str(statement)) if self._resolve_unindexed_reset_qubit(statement): @@ -796,7 +804,8 @@ def _expand_barrier_ranges( ) -> list: """Replace RangeDefinition-containing qubits in a barrier with their expanded IndexedIdentifier equivalents so that consolidate_qubit_registers - only sees IntegerLiteral indices.""" + only sees IntegerLiteral indices. + """ consolidated_qubits: list = [] expanded_idx = 0 for op_qubit in barrier.qubits: @@ -834,7 +843,7 @@ def _visit_barrier( # pylint: disable=too-many-locals, too-many-branches statement (qasm3_ast.QuantumBarrier): The barrier statement to visit. Returns: - None + The list containing a single multi-qubit barrier statement. """ valid_open_pulse_qubits = False for op_qubit in barrier.qubits: @@ -962,7 +971,7 @@ def _visit_gate_definition(self, definition: qasm3_ast.QuantumGateDefinition) -> definition (qasm3_ast.QuantumGateDefinition): The gate definition to visit. Returns: - None + An empty list. """ gate_name = definition.name.name if gate_name in self._custom_gates: @@ -1016,8 +1025,9 @@ def _broadcast_gate_operation( all_targets (list[list[qasm3_ast.IndexedIdentifier]]): The list of target qubits. The length of this list indicates the number of time the gate is invoked. + Returns: - List of all executed gates. + The list of all executed gates. """ result = [] if ctrls is None: @@ -1030,7 +1040,7 @@ def _register_physical_qubit(self, name: str) -> int: """Register a physical qubit ``$n`` for depth / count tracking if not already known. Args: - name: The physical qubit identifier string (e.g. ``"$0"``). + name (str): The physical qubit identifier string (e.g. ``"$0"``). Returns: The physical qubit index. @@ -1063,11 +1073,12 @@ def _update_qubit_depth_for_gate( self, all_targets: list[list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]]], ctrls: list[qasm3_ast.IndexedIdentifier], - ): + ) -> None: """Updates the depth of the circuit after applying a broadcasted gate. Args: - all_targes: The list of qubits on which a gate was just added. + all_targets: The list of qubits on which a gate was just added. + ctrls: THe list of control qubits for the gate. Returns: None @@ -1109,7 +1120,7 @@ def _visit_basic_gate_operation( angles. Returns: - None + The list of gates after unrolling. Raises: ValidationError: If the number of qubits is invalid. @@ -1223,7 +1234,7 @@ def _visit_custom_gate_operation( for more clarity. Returns: - None + The list of gates and phase operations after unrolling. """ logger.debug("Visiting custom gate operation '%s'", str(operation)) if ctrls is None: @@ -1398,7 +1409,7 @@ def _visit_phase_operation( inverse (bool): Whether the operation is an inverse operation. Defaults to False. Returns: - list[qasm3_ast.Statement]: The unrolled quantum phase operation. + list[qasm3_ast.QuantumPhase]: The unrolled quantum phase operation. """ logger.debug("Visiting phase operation '%s'", str(operation)) if ctrls is None: @@ -1452,10 +1463,11 @@ def _visit_generic_gate_operation( # pylint: disable=too-many-branches, too-man """Visit a gate operation element. Args: - operation (qasm3_ast.QuantumGate): The gate operation to visit. + operation: The gate operation to visit. + ctrls: An optional list of control qubits on the gate operation. Returns: - None + The list of gates and phase operations after unrolling. """ operation, ctrls = copy.copy(operation), copy.copy(ctrls) negctrls = [] @@ -1617,7 +1629,7 @@ def _visit_constant_declaration( statement (qasm3_ast.ConstantDeclaration): The constant declaration to visit. Returns: - None + list[qasm3_ast.Statement]: The list containing the unrolled statement. """ statements = [] var_name = statement.identifier.name @@ -1733,10 +1745,10 @@ def _visit_classical_declaration( """Visit a classical operation element. Args: - statement (ClassicalType): The classical operation to visit. + statement (qasm3_ast.ClassicalDeclaration): The classical operation to visit. Returns: - None + list[qasm3_ast.Statement]: The list containing the unrolled statement. """ statements = [] var_name = statement.identifier.name @@ -1974,7 +1986,7 @@ def _visit_classical_assignment( statement (qasm3_ast.ClassicalAssignment): The classical assignment to visit. Returns: - list[qasm3_ast.Statement]: The list of statements generated by the assignment. + list[qasm3_ast.Statement]: The list containing the unrolled statement. """ statements = [] lvalue = statement.lvalue @@ -2191,7 +2203,7 @@ def _visit_branching_statement( statement (qasm3_ast.BranchingStatement): The branching statement to visit. Returns: - None + list[qasm3_ast.Statement]: The list of unrolled branch statements. """ self._scope_manager.push_context(Context.BLOCK) self._scope_manager.push_scope({}) @@ -2316,7 +2328,14 @@ def ravel(bit_ind): return result # type: ignore[return-value] def _visit_forin_loop(self, statement: qasm3_ast.ForInLoop) -> list[qasm3_ast.Statement]: - # Compute loop variable values + """Visit a for-in loop statement element. + + Args: + statement (qasm3_ast.ForInLoop): The for-in loop statement to visit. + + Returns: + list[qasm3_ast.Statement]: The list of unrolled branch statements. + """ irange = [] if isinstance(statement.set_declaration, qasm3_ast.RangeDefinition): init_exp = statement.set_declaration.start @@ -2398,10 +2417,10 @@ def _visit_subroutine_definition( Reference: https://openqasm.com/language/subroutines.html#subroutines Args: - statement (qasm3_ast.SubroutineDefinition): The subroutine definition to visit. + statement (SubroutineDefinition | ExternDeclaration): The subroutine definition to visit. Returns: - None + The list containing the statement if it is an ExternDeclaration, otherwise None. """ fn_name = statement.name.name statements = [] @@ -2571,12 +2590,13 @@ def _visit_while_loop(self, statement: qasm3_ast.WhileLoop) -> list[qasm3_ast.St """Visit a while-loop element. Args: - statement (qasm3_ast.WhileLoop) - the while-loop AST node + statement (qasm3_ast.WhileLoop): The while-loop AST node. Returns: - list[qasm3_ast.Statement] - flattened/unrolled statements + list[qasm3_ast.Statement]: The list of unrolled statements from the while-loop. Raises: - ValidationError - if loop condition is non-classical or dynamic - LoopLimitExceededError - if the loop exceeds the maximum limit""" + ValidationError: If loop condition is non-classical or dynamic. + LoopLimitExceededError: if the loop exceeds the maximum limit. + """ result = [] @@ -2631,7 +2651,7 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> list[No statement (qasm3_ast.AliasStatement): The alias statement to visit. Returns: - None + An empty list. """ # pylint: disable=too-many-branches target = statement.target @@ -2828,12 +2848,18 @@ def _evaluate_case(statements): default_stmts = statement.default.statements return _evaluate_case(default_stmts) - def _resolve_duration_unit(self, time_var) -> qasm3_ast.TimeUnit: + def _resolve_duration_unit(self, time_var: qasm3_ast.Expression) -> qasm3_ast.TimeUnit: """Determine the output unit for a duration literal. `dt` is backend-dependent and not convertible to SI units without a known sample rate. Preserve it when the source unit was `dt` (or a device cycle time is set). SI units are already converted to ns by the evaluator. + + Args: + time_var (qasm3_ast.Expression): A DurationLiteral to check. + + Returns: + qasm3_ast.TimeUnit: The unit of the DurationLiteral. """ source_is_dt = ( isinstance(time_var, qasm3_ast.DurationLiteral) @@ -2850,8 +2876,10 @@ def _visit_delay_statement( ) -> list[qasm3_ast.Statement]: """ Visit a DelayInstruction statement. + Args: statement (qasm3_ast.DelayInstruction): The DelayInstruction statement to visit. + Returns: list[qasm3_ast.Statement]: The list of statements generated by the DelayInstruction. """ @@ -2937,10 +2965,12 @@ def _visit_delay_statement( def _visit_box_statement(self, statement: qasm3_ast.Box) -> list[qasm3_ast.Statement]: """ Visit a Box statement. + Args: statement (qasm3_ast.Box): The Box statement node to visit. + Returns: - list[qasm3_ast.Statement]: The list of statements generated by the Box statement. + list[qasm3_ast.Statement]: The list of unrolled statements from the Box statement. """ statements = [] _box_time_var = statement.duration @@ -3009,14 +3039,14 @@ def _visit_box_statement(self, statement: qasm3_ast.Box) -> list[qasm3_ast.State def _visit_calibration_definition( self, statement: qasm3_ast.CalibrationDefinition - ) -> list[Any]: + ) -> list[qasm3_ast.Statement]: """Visit a calibration definition element. Args: statement (qasm3_ast.CalibrationDefinition): The calibration definition to visit. Returns: - None + list[qasm3_ast.Statement]: The list of unrolled statements. """ from openpulse.parser import ( # pylint: disable=import-outside-toplevel OpenPulseParsingError, @@ -3156,14 +3186,16 @@ def _visit_calibration_definition( return [statement] - def _visit_calibration_statement(self, statement: qasm3_ast.CalibrationStatement) -> list[Any]: + def _visit_calibration_statement( + self, statement: qasm3_ast.CalibrationStatement + ) -> list[qasm3_ast.Statement]: """Visit a calibration statement element. Args: statement (qasm3_ast.CalibrationStatement): The calibration statement to visit. Returns: - None + list[qasm3_ast.Statement]: The list of unrolled statements. """ from openpulse.parser import ( # pylint: disable=import-outside-toplevel OpenPulseParsingError, @@ -3232,7 +3264,7 @@ def _visit_calibration_grammar_declaration( statement (qasm3_ast.CalibrationGrammarDeclaration): The calibration grammar declaration Returns: - None + list[qasm3_ast.Statement]: The list of unrolled statements. """ if statement.name != "openpulse": raise_qasm3_error( @@ -3272,7 +3304,7 @@ def visit_statement(self, statement: qasm3_ast.Statement) -> list[qasm3_ast.Stat statement (qasm3_ast.Statement): The statement to visit. Returns: - None + list[qasm3_ast.Statement]: The list of unrolled statements. """ logger.debug("Visiting statement '%s'", str(statement)) result = [] From 1ad40df072e3429fed779ffe599bfb7c5380629e Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Wed, 5 Aug 2026 10:35:14 -0400 Subject: [PATCH 05/21] linting --- src/pyqasm/visitor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 0137b71..583fbf6 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -3187,8 +3187,8 @@ def _visit_calibration_definition( return [statement] def _visit_calibration_statement( - self, statement: qasm3_ast.CalibrationStatement - ) -> list[qasm3_ast.Statement]: + self, statement: qasm3_ast.CalibrationStatement + ) -> list[qasm3_ast.Statement]: """Visit a calibration statement element. Args: From 113884e6800d338d4e56f50791f779bb088deaef Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Thu, 6 Aug 2026 11:56:23 -0400 Subject: [PATCH 06/21] Updates based on suggested changes. --- CHANGELOG.md | 3 +-- src/pyqasm/modules/base.py | 23 ++++++++++++----------- src/pyqasm/modules/qasm2.py | 1 - src/pyqasm/modules/qasm3.py | 1 - src/pyqasm/visitor.py | 25 +++++++++++++++---------- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8070d9f..9719ea1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Types of changes: ### Improved / Modified - Consolidated the hardcoded `"__PYQASM_QUBITS__"` string literals scattered across `visitor.py`, `transformer.py` and `pulse/utils.py` into a single `INTERNAL_QUBIT_REGISTER` constant in `elements.py`, alongside an `is_internal_qubit_register()` helper that is now the one place the internal register is recognised. ([#325](https://github.com/qBraid/pyqasm/pull/325)) -- Added type hinting for `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. ([#346](https://github.com/qBraid/pyqasm/pull/346)) +- Added type hinting for `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` signatures. Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. Fixes incorrect return types in the docstrings of `visitor.py`. ([#346](https://github.com/qBraid/pyqasm/pull/346)) ### Deprecated @@ -36,7 +36,6 @@ Types of changes: - Fixed the `ctrl @` modifier not resolving gate aliases: `ctrl @ toffoli` / `ctrl @ ccnot` (aliases of `ccx`) and `ctrl @ cnot` / `ctrl @ CX` (aliases of `cx`) now escalate controls identically to their canonical gate instead of raising `Unsupported controlled QASM operation`. ([#320](https://github.com/qBraid/pyqasm/pull/320)) - Fixed classical register declarations not being visible inside `box` scope, causing "Missing clbit register declaration" errors for measurement statements inside box blocks. ([#306](https://github.com/qBraid/pyqasm/pull/306)) - Fixed the backend-dependent `dt` duration unit being incorrectly relabeled as `ns` when unrolling `delay` and `box` statements without a `device_cycle_time`. Since `dt` cannot be converted to SI units without a sample rate, it is now preserved as `dt`. ([#317](https://github.com/qBraid/pyqasm/pull/317)) -- Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. ([#346](https://github.com/qBraid/pyqasm/pull/346)) ### Dependencies - Migrated the Linux wheel *build container* from `manylinux2014` to `manylinux_2_28`. NumPy stopped publishing `manylinux2014` (glibc 2.17) wheels for CPython 3.12+, so `pip` fell back to building NumPy from source inside the build container, whose GCC is older than NumPy requires — failing every Linux wheel job for cp312/cp313/cp314. The published wheels are unaffected: auditwheel tags them from the extension's actual symbol requirements, so they remain `manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64` and still install on glibc 2.17 systems. diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 72b479e..613a977 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -22,7 +22,7 @@ from abc import ABC, abstractmethod from collections import Counter from copy import deepcopy -from typing import Any, Callable, Optional, Self +from typing import Any, Callable, Optional, TypeVar import openqasm3.ast as qasm3_ast from openqasm3.ast import BranchingStatement, Program, QuantumGate @@ -35,8 +35,8 @@ from pyqasm.maps.decomposition_rules import DECOMPOSITION_RULES from pyqasm.visitor import QasmVisitor, ScopeManager - -def track_user_operation(func: Callable[..., Any]) -> Callable[..., Any]: +F = TypeVar("F", bound=Callable[..., Any]) +def track_user_operation(func: F) -> F: """Decorator to track user operations on a QasmModule.""" @functools.wraps(func) @@ -120,7 +120,7 @@ def num_qubits(self, value: int) -> None: """Setter for the number of qubits.""" self._num_qubits = value - def _add_qubit_register(self, reg_name: str, num_qubits: int): + def _add_qubit_register(self, reg_name: str, num_qubits: int) -> None: """Add qubits to the module. Args: @@ -320,8 +320,8 @@ def depth(self, decompose_native_gates: bool = True) -> int: Args: decompose_native_gates (bool): If True, calculate depth after decomposing gates. - If False, treat all decompsable gates as a single gate operation. - Defaults to True. + If False, treat all decomposable gates as a single gate operation. + Defaults to True. Returns: int: The depth of the current "unrolled" openqasm program. @@ -356,13 +356,14 @@ def depth(self, decompose_native_gates: bool = True) -> int: def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]) -> None: """Remap the qubits in a register after removing idle qubits and update the operations using this register accordingly. + Args: reg_name (str): The name of the register to be remapped. size (int): The size of the register. idle_indices (list[int]): Indices of idle qubits to be remapped away. - Returns: - None + Returns: + None """ used_indices = [idx for idx in range(size) if idx not in idle_indices] @@ -583,7 +584,7 @@ def reverse_qubit_order(self, in_place: bool = True) -> QasmModule: @track_user_operation def validate(self) -> None: - """Validate the module""" + """Validate the module.""" if self._validated_program is True: return try: @@ -651,7 +652,7 @@ def rebase(self, target_basis_set: BasisSet, in_place: bool = True) -> QasmModul Note: Will unroll the module if not already done. Args: - target_basis_set: The target basis set to rebase the module to. + target_basis_set(BasisSet): The target basis set to rebase the module to. in_place (bool): Flag to indicate if the rebase operation should be done in place. Returns: @@ -773,7 +774,7 @@ def __str__(self) -> str: return self._qasm_ast_to_str(self.unrolled_ast) return self._qasm_ast_to_str(self.original_program) - def copy(self) -> Self: + def copy(self) -> QasmModule: """Return a deep copy of the module.""" return deepcopy(self) diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index 3757d48..caac5e9 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -26,7 +26,6 @@ from pyqasm.exceptions import ValidationError from pyqasm.modules.base import QasmModule from pyqasm.modules.qasm3 import Qasm3Module -from pyqasm.visitor import QasmVisitor class Qasm2Module(QasmModule): diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 97a7e80..a6c7be0 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -20,7 +20,6 @@ from openqasm3.printer import dumps from pyqasm.modules.base import QasmModule -from pyqasm.visitor import QasmVisitor class Qasm3Module(QasmModule): diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 583fbf6..0b9880e 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -202,7 +202,7 @@ def _visit_quantum_register( register (QubitDeclaration): The register name and size. Returns: - The list containing the register + The list containing the register, or an empty list if self._check_only is True. """ logger.debug("Visiting register '%s'", str(register)) @@ -289,7 +289,7 @@ def _get_op_bits( operation (Any): The operation to get qubits for. qubits (bool): Whether the bits are quantum bits or classical bits. Defaults to True. Returns: - The quantum or classical bits for the operation. + The quantum or classical bits for the operation, or an empty list if check_only is true. """ openqasm_bits: list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]] = [] bit_list = [] @@ -530,11 +530,11 @@ def _handle_function_init_expression( """Handle function initialization expression. Args: - expression (Any): The statement to handle function initialization expression. + expression (FunctionCall): The statement to handle function initialization expression. init_value (Any): The value to handle function initialization expression. Returns: - The resultant expression if + The resultant expression if the expression is applied, otherwise None. """ if isinstance(expression, qasm3_ast.FunctionCall): func_name = expression.name.name @@ -551,6 +551,9 @@ def _handle_extern_function_cleanup( Args: statements: List of statements to potentially modify statement: The statement to append if in extern function + + Returns: + None """ if self._in_extern_function: self._in_extern_function = False @@ -575,7 +578,7 @@ def _visit_measurement( # pylint: disable=too-many-locals,too-many-branches,too statement (qasm3_ast.QuantumMeasurementStatement): The measurement statement to visit. Returns: - The list of unrolled measurements. + The list of unrolled measurements, or an empty list if self._check_only is True. """ logger.debug("Visiting measurement statement '%s'", str(statement)) @@ -746,7 +749,7 @@ def _visit_reset(self, statement: qasm3_ast.QuantumReset) -> list[qasm3_ast.Quan statement (qasm3_ast.QuantumReset): The reset statement to visit. Returns: - The list of unrolled resets. + The list of unrolled resets, or an empty list if self._check_only is True. """ logger.debug("Visiting reset statement '%s'", str(statement)) if self._resolve_unindexed_reset_qubit(statement): @@ -1078,7 +1081,7 @@ def _update_qubit_depth_for_gate( Args: all_targets: The list of qubits on which a gate was just added. - ctrls: THe list of control qubits for the gate. + ctrls: The list of control qubits for the gate. Returns: None @@ -2334,7 +2337,7 @@ def _visit_forin_loop(self, statement: qasm3_ast.ForInLoop) -> list[qasm3_ast.St statement (qasm3_ast.ForInLoop): The for-in loop statement to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled branch statements. + list[qasm3_ast.Statement]: The list containing the loop statements. """ irange = [] if isinstance(statement.set_declaration, qasm3_ast.RangeDefinition): @@ -2417,10 +2420,12 @@ def _visit_subroutine_definition( Reference: https://openqasm.com/language/subroutines.html#subroutines Args: - statement (SubroutineDefinition | ExternDeclaration): The subroutine definition to visit. + statement (SubroutineDefinition | ExternDeclaration): + The subroutine definition to visit. Returns: - The list containing the statement if it is an ExternDeclaration, otherwise None. + The list containing the statement if it is an ExternDeclaration, + otherwise an empty list. Returns an empty list if self._check_only is True. """ fn_name = statement.name.name statements = [] From 090c9a416d73d35412d56eb1a9d30dd3c01d2741 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Thu, 6 Aug 2026 11:56:54 -0400 Subject: [PATCH 07/21] linting --- src/pyqasm/modules/base.py | 2 ++ src/pyqasm/visitor.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 613a977..6fa4a4d 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -36,6 +36,8 @@ from pyqasm.visitor import QasmVisitor, ScopeManager F = TypeVar("F", bound=Callable[..., Any]) + + def track_user_operation(func: F) -> F: """Decorator to track user operations on a QasmModule.""" diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 0b9880e..3cf385a 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -551,7 +551,7 @@ def _handle_extern_function_cleanup( Args: statements: List of statements to potentially modify statement: The statement to append if in extern function - + Returns: None """ From bdd18e62862be2f12781525f2467c771523df34a Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Thu, 6 Aug 2026 12:34:53 -0400 Subject: [PATCH 08/21] Progress on updating all docstrings --- src/pyqasm/modules/base.py | 40 ++---- src/pyqasm/modules/qasm2.py | 6 +- src/pyqasm/modules/qasm3.py | 3 - src/pyqasm/visitor.py | 255 ++++++++++++++++++++---------------- 4 files changed, 153 insertions(+), 151 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 6fa4a4d..65160de 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -61,7 +61,7 @@ def wrapper(self, *args, **kwargs): self._user_operations.append(log_message) return func(self, *args, **kwargs) - return wrapper + return wrapper # type: ignore class QasmModule(ABC): # pylint: disable=too-many-instance-attributes, too-many-public-methods @@ -127,9 +127,6 @@ def _add_qubit_register(self, reg_name: str, num_qubits: int) -> None: Args: num_qubits (int): The number of qubits to add to the module. - - Returns: - None """ self._qubit_registers[reg_name] = num_qubits self._num_qubits += num_qubits @@ -152,9 +149,6 @@ def _add_classical_register(self, reg_name: str, num_clbits: int) -> None: Args: num_clbits (int): The number of classical bits to add to the module. - - Returns: - None """ self._classical_registers[reg_name] = num_clbits self._num_clbits += num_clbits @@ -177,9 +171,6 @@ def unrolled_ast(self, value: Program) -> None: def has_measurements(self) -> bool: """Check if the module has any measurement operations. - Args: - None - Returns: bool: True if the module has measurement operations, False otherwise. """ @@ -237,9 +228,6 @@ def remove_measurements(self, in_place: bool = True) -> Optional["QasmModule"]: def has_barriers(self) -> bool: """Check if the module has any barrier operations. - Args: - None - Returns: bool: True if the module has barrier operations, False otherwise. """ @@ -363,9 +351,6 @@ def _remap_qubits(self, reg_name: str, size: int, idle_indices: list[int]) -> No reg_name (str): The name of the register to be remapped. size (int): The size of the register. idle_indices (list[int]): Indices of idle qubits to be remapped away. - - Returns: - None """ used_indices = [idx for idx in range(size) if idx not in idle_indices] @@ -414,8 +399,8 @@ def _get_idle_qubit_indices(self) -> dict[str, list[int]]: """Get the indices of the idle qubits in the module. Returns: - dict[str, list[int]]: A dictionary mapping the register name to the list of idle qubit - indices in that register. + dict[str, list[int]]: A dictionary mapping the register name + to the list of idle qubit indices in that register. """ idle_qubits = [qubit for qubit in self._qubit_depths.values() if qubit.is_idle()] @@ -615,10 +600,10 @@ def unroll(self, **kwargs: Any) -> None: unroll_barriers (bool): If True, barriers will be unrolled. Defaults to True. max_loop_iters (int): Max number of iterations for unrolling loops. Defaults to 1e9. check_only (bool): If True, only check the program without executing it. - Defaults to False. - device_qubits (int): Number of physical qubits available on the target device. - consolidate_qubits (bool): If True, consolidate all quantum registers into - single register. + Defaults to False. + device_qubits (int): Number of physical qubits available on the target device. + consolidate_qubits (bool): If True, consolidate all + quantum registers into a single register. Raises: ValidationError: If the module fails validation during unrolling. @@ -654,7 +639,7 @@ def rebase(self, target_basis_set: BasisSet, in_place: bool = True) -> QasmModul Note: Will unroll the module if not already done. Args: - target_basis_set(BasisSet): The target basis set to rebase the module to. + target_basis_set (BasisSet): The target basis set to rebase the module to. in_place (bool): Flag to indicate if the rebase operation should be done in place. Returns: @@ -700,9 +685,6 @@ def rebase(self, target_basis_set: BasisSet, in_place: bool = True) -> QasmModul def _get_gate_counts(self) -> dict[str, int]: """Return a dictionary of gate counts in the unrolled program. - Args: - None - Returns: dict[str, int]: A dictionary of gate counts. """ @@ -719,9 +701,6 @@ def compare(self, other_module: QasmModule) -> None: Args: other_module (QasmModule): The module to compare with. - - Returns: - None """ try: # pylint: disable-next=import-outside-toplevel from tabulate import tabulate @@ -790,7 +769,4 @@ def accept(self, visitor) -> None: Args: visitor (QasmVisitor): The visitor to accept. - - Returns: - None """ diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index caac5e9..07088cf 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -82,8 +82,7 @@ def to_qasm3(self, as_str: bool = False) -> str | Qasm3Module: Args: as_str (bool): Flag to indicate if the conversion should be to a string - or to a Qasm3Module object. - Default is False. + or to a Qasm3Module object. Default is False. Returns: str | Qasm3Module: The module in openqasm3 format. @@ -102,9 +101,6 @@ def accept(self, visitor) -> None: Args: visitor (QasmVisitor): The visitor to accept. - - Returns: - None """ self._filter_statements() unrolled_stmt_list = visitor.visit_basic_block(self._statements) diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index a6c7be0..617e4d8 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -47,9 +47,6 @@ def accept(self, visitor) -> None: Args: visitor (QasmVisitor): The visitor to accept. - - Returns: - None """ unrolled_stmt_list = visitor.visit_basic_block(self._statements) final_stmt_list = visitor.finalize(unrolled_stmt_list) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 3cf385a..ce07aae 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -26,7 +26,7 @@ from collections import OrderedDict, deque from functools import partial from io import StringIO -from typing import Any, Callable, Optional, Sequence, Union, cast +from typing import Any, Callable, Optional, Sequence, cast import numpy as np import openqasm3.ast as qasm3_ast @@ -202,7 +202,8 @@ def _visit_quantum_register( register (QubitDeclaration): The register name and size. Returns: - The list containing the register, or an empty list if self._check_only is True. + list[QubitDeclaration]: The list containing the register, + or an empty list if self._check_only is True. """ logger.debug("Visiting register '%s'", str(register)) @@ -283,15 +284,16 @@ def _get_op_bits( operation: Any, qubits: bool = True, function_qubit_sizes: Optional[dict[str, int]] = None, - ) -> list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]]: + ) -> list[qasm3_ast.IndexedIdentifier | qasm3_ast.Identifier]: """Get the quantum / classical bits for the operation. Args: operation (Any): The operation to get qubits for. qubits (bool): Whether the bits are quantum bits or classical bits. Defaults to True. Returns: - The quantum or classical bits for the operation, or an empty list if check_only is true. + list[IndexedIdentifier | Identifier]: The quantum or classical bits for the operation, + or an empty list if check_only is true. """ - openqasm_bits: list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]] = [] + openqasm_bits: list[qasm3_ast.IndexedIdentifier | qasm3_ast.Identifier] = [] bit_list = [] if isinstance(operation, qasm3_ast.QuantumMeasurementStatement): @@ -458,8 +460,6 @@ def _check_variable_cast_type( base_type (Any): Base type of the declaration variable. base_size(Any): literal to get the base size of the declaration variable. is_const (bool): whether the statement is constant declaration or not. - Returns: - None """ if not val_type: val_type = base_type @@ -487,7 +487,7 @@ def _qubit_register_consolidation( Args: unrolled_stmts (list): The list of non-QubitDeclaration statements to append - after consolidating the quantum registers. + after consolidating the quantum registers. total_qubits (int): The total number of allocated qubits in quantum registers. Returns: @@ -495,8 +495,8 @@ def _qubit_register_consolidation( Raises: ValidationError: If the total number of qubits exceeds the available device qubits, - or if the reserved register '__PYQASM_QUBITS__' is already declared - in the original QASM program. + or if the reserved register '__PYQASM_QUBITS__' is already declared + in the original QASM program. """ if total_qubits > self._module._device_qubits: # type: ignore raise_qasm3_error( @@ -534,7 +534,7 @@ def _handle_function_init_expression( init_value (Any): The value to handle function initialization expression. Returns: - The resultant expression if the expression is applied, otherwise None. + None | Expression: The resultant expression if the expression is applied, otherwise None. """ if isinstance(expression, qasm3_ast.FunctionCall): func_name = expression.name.name @@ -551,9 +551,6 @@ def _handle_extern_function_cleanup( Args: statements: List of statements to potentially modify statement: The statement to append if in extern function - - Returns: - None """ if self._in_extern_function: self._in_extern_function = False @@ -575,10 +572,11 @@ def _visit_measurement( # pylint: disable=too-many-locals,too-many-branches,too """Visit a measurement statement element. Args: - statement (qasm3_ast.QuantumMeasurementStatement): The measurement statement to visit. + statement (QuantumMeasurementStatement): The measurement statement to visit. Returns: - The list of unrolled measurements, or an empty list if self._check_only is True. + list[QuantumMeasurementStatement]: The list of unrolled measurements, + or an empty list if self._check_only is True. """ logger.debug("Visiting measurement statement '%s'", str(statement)) @@ -717,7 +715,7 @@ def _resolve_unindexed_reset_qubit(self, statement: qasm3_ast.QuantumReset) -> b register slot: a physical qubit ("$n") or the internal pulse register. Args: - statement (qasm3_ast.QuantumReset): The reset statement whose operand + statement (QuantumReset): The reset statement whose operand is being resolved. Renamed in place for OpenPulse programs. Returns: @@ -746,10 +744,10 @@ def _visit_reset(self, statement: qasm3_ast.QuantumReset) -> list[qasm3_ast.Quan """Visit a reset statement element. Args: - statement (qasm3_ast.QuantumReset): The reset statement to visit. + statement (QuantumReset): The reset statement to visit. Returns: - The list of unrolled resets, or an empty list if self._check_only is True. + list[QuantumReset]: The list of unrolled resets, or an empty list if self._check_only is True. """ logger.debug("Visiting reset statement '%s'", str(statement)) if self._resolve_unindexed_reset_qubit(statement): @@ -808,6 +806,13 @@ def _expand_barrier_ranges( """Replace RangeDefinition-containing qubits in a barrier with their expanded IndexedIdentifier equivalents so that consolidate_qubit_registers only sees IntegerLiteral indices. + + Args: + barrier (QuantumBarrier): The barrier with qubits to consolidate. + barrier_qubits (QuantumBarrier): The qubits to replace. + + Returns: + list: The resultant list of consolidated qubits. """ consolidated_qubits: list = [] expanded_idx = 0 @@ -843,10 +848,11 @@ def _visit_barrier( # pylint: disable=too-many-locals, too-many-branches """Visit a barrier statement element. Args: - statement (qasm3_ast.QuantumBarrier): The barrier statement to visit. + statement (QuantumBarrier): The barrier statement to visit. Returns: - The list containing a single multi-qubit barrier statement. + list[QuantumBarrier]: The list containing a single multi-qubit barrier statement, + or an empty list if self._check_only is True. """ valid_open_pulse_qubits = False for op_qubit in barrier.qubits: @@ -947,7 +953,7 @@ def _get_op_parameters(self, operation: qasm3_ast.QuantumGate) -> list[float]: """Get the parameters for the operation. Args: - operation (qasm3_ast.QuantumGate): The operation to get parameters for. + operation (QuantumGate): The operation to get parameters for. Returns: list[float]: The parameters for the operation. @@ -989,16 +995,17 @@ def _visit_gate_definition(self, definition: qasm3_ast.QuantumGateDefinition) -> def _unroll_multiple_target_qubits( self, operation: qasm3_ast.QuantumGate, gate_qubit_count: int - ) -> list[list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]]]: + ) -> list[list[qasm3_ast.IndexedIdentifier | qasm3_ast.Identifier]]: """Unroll the complete list of all qubits that the given operation is applied to. - E.g. this maps 'cx q[0], q[1], q[2], q[3]' to [[q[0], q[1]], [q[2], q[3]]] + E.g. this maps 'cx q[0], q[1], q[2], q[3]' to [[q[0], q[1]], [q[2], q[3]]] Args: - operation (qasm3_ast.QuantumGate): The gate to be applied. + operation (QuantumGate): The gate to be applied. gate_qubit_count (list[int]): The number of qubits that a single gate acts on. Returns: - The list of all targets that the unrolled gate should act on. + list[list[IndexedIdentifier | Identifier]]: The list of all targets that + the unrolled gate should act on. """ op_qubits = self._get_op_bits(operation, qubits=True) if len(op_qubits) <= 0 or len(op_qubits) % gate_qubit_count != 0: @@ -1016,21 +1023,21 @@ def _unroll_multiple_target_qubits( def _broadcast_gate_operation( self, gate_function: Callable, - all_targets: list[list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]]], + all_targets: list[list[qasm3_ast.IndexedIdentifier | qasm3_ast.Identifier]], ctrls: Optional[list[qasm3_ast.IndexedIdentifier]] = None, ) -> list[qasm3_ast.QuantumGate]: """Broadcasts the application of a gate onto multiple sets of target qubits. Args: - gate_function (callable): The gate that should be applied to multiple target qubits. + gate_function (Callable): The gate that should be applied to multiple target qubits. (All arguments of the callable should be qubits, i.e. all non-qubit arguments of the gate should already be evaluated, e.g. using functools.partial). - all_targets (list[list[qasm3_ast.IndexedIdentifier]]): + all_targets (list[list[IndexedIdentifier]]): The list of target qubits. The length of this list indicates the number of time the gate is invoked. Returns: - The list of all executed gates. + list[QuantumGate]: The list of all executed gates. """ result = [] if ctrls is None: @@ -1046,7 +1053,7 @@ def _register_physical_qubit(self, name: str) -> int: name (str): The physical qubit identifier string (e.g. ``"$0"``). Returns: - The physical qubit index. + int: The physical qubit index. """ phys_idx = int(name[1:]) if (name, phys_idx) not in self._module._qubit_depths: @@ -1064,6 +1071,12 @@ def _get_qubit_name_and_id( Physical qubits are represented as ``Identifier("$n")`` and carry their index in the name itself. Virtual qubits are ``IndexedIdentifier`` with an explicit index in ``.indices``. + + Args: + qubit (IndexedIdentifier | Identifier): The qubit to get the name and id of. + + Returns: + tuple[str, int]: A tuple containing the name and id of the qubit. """ if isinstance(qubit, qasm3_ast.Identifier): # Physical qubit: name is "$n", index is n. @@ -1074,17 +1087,15 @@ def _get_qubit_name_and_id( def _update_qubit_depth_for_gate( self, - all_targets: list[list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]]], + all_targets: list[list[qasm3_ast.IndexedIdentifier | qasm3_ast.Identifier]], ctrls: list[qasm3_ast.IndexedIdentifier], ) -> None: """Updates the depth of the circuit after applying a broadcasted gate. Args: - all_targets: The list of qubits on which a gate was just added. - ctrls: The list of control qubits for the gate. - - Returns: - None + all_targets (list[list[IndexedIdentifier | Identifier]]): + The list of qubits on which a gate was just added. + ctrls (list[IndexedIdentifier]): The list of control qubits for the gate. """ if not self._recording_ext_gate_depth: for qubit_subset in all_targets: @@ -1110,20 +1121,17 @@ def _visit_basic_gate_operation( """Visit a gate operation element. Args: - operation (qasm3_ast.QuantumGate): The gate operation to visit. + operation (QuantumGate): The gate operation to visit. inverse (bool): Whether the operation is an inverse operation. Defaults to False. - - - if inverse is True, we apply check for different cases in the - map_qasm_inv_op_to_callable method. - - - Only rotation and S / T gates are affected by this inversion. For S/T - gates we map them to Sdg / Tdg and vice versa. - - - For rotation gates, we map to the same gates but invert the rotation - angles. + If inverse is True, we apply check for different cases in the + map_qasm_inv_op_to_callable method. + Only rotation and S / T gates are affected by this inversion. For S/T + gates we map them to Sdg / Tdg and vice versa. + For rotation gates, we map to the same gates but invert the rotation angles. Returns: - The list of gates after unrolling. + list[QuantumGate]: The list of gates after unrolling, + or an empty list if self._check_only is True. Raises: ValidationError: If the number of qubits is invalid. @@ -1228,24 +1236,24 @@ def _visit_custom_gate_operation( """Visit a custom gate operation element recursively. Args: - operation (qasm3_ast.QuantumGate): The gate operation to visit. + operation (QuantumGate): The gate operation to visit. inverse (bool): Whether the operation is an inverse operation. Defaults to False. - - If True, the gate operation is applied in reverse order and the - inverse modifier is appended to each gate call. - See https://openqasm.com/language/gates.html#inverse-modifier - for more clarity. + If True, the gate operation is applied in reverse order and the + inverse modifier is appended to each gate call. + See https://openqasm.com/language/gates.html#inverse-modifier + for more clarity. Returns: - The list of gates and phase operations after unrolling. + list[QuantumGate | QuantumPhase]: The list of gates and phase operations + after unrolling, or an empty list if self._check_only is True. """ logger.debug("Visiting custom gate operation '%s'", str(operation)) if ctrls is None: ctrls = [] gate_name: str = operation.name.name gate_definition: qasm3_ast.QuantumGateDefinition = self._custom_gates[gate_name] - op_qubits: list[Union[qasm3_ast.IndexedIdentifier, qasm3_ast.Identifier]] = ( - self._get_op_bits(operation, qubits=True) + op_qubits: list[qasm3_ast.IndexedIdentifier | qasm3_ast.Identifier] = self._get_op_bits( + operation, qubits=True ) Qasm3Validator.validate_gate_call(operation, gate_definition, len(op_qubits)) @@ -1334,14 +1342,14 @@ def _visit_external_gate_operation( Args: operation (qasm3_ast.QuantumGate): The external gate operation to visit. inverse (bool): Whether the operation is an inverse operation. Defaults to False. - - If True, the gate operation is applied in reverse order and the - inverse modifier is appended to each gate call. - See https://openqasm.com/language/gates.html#inverse-modifier - for more clarity. + If True, the gate operation is applied in reverse order and the + inverse modifier is appended to each gate call. + See https://openqasm.com/language/gates.html#inverse-modifier + for more clarity. Returns: - list[qasm3_ast.QuantumGate]: The quantum gate that was collected. + list[QuantumGate]: The list containing the quantum gate that was collected, + or an empty list if self._check_only is True. """ logger.debug("Visiting external gate operation '%s'", str(operation)) gate_name: str = operation.name.name @@ -1408,11 +1416,16 @@ def _visit_phase_operation( """Visit a phase operation element. Args: - operation (qasm3_ast.QuantumPhase): The phase operation to visit. + operation (QuantumPhase): The phase operation to visit. inverse (bool): Whether the operation is an inverse operation. Defaults to False. + If True, the gate operation is applied in reverse order and the + inverse modifier is appended to each gate call. + See https://openqasm.com/language/gates.html#inverse-modifier + for more clarity. Returns: - list[qasm3_ast.QuantumPhase]: The unrolled quantum phase operation. + list[QuantumPhase]: The unrolled quantum phase operation, or an empty list + if self._check_only is True. """ logger.debug("Visiting phase operation '%s'", str(operation)) if ctrls is None: @@ -1466,11 +1479,13 @@ def _visit_generic_gate_operation( # pylint: disable=too-many-branches, too-man """Visit a gate operation element. Args: - operation: The gate operation to visit. - ctrls: An optional list of control qubits on the gate operation. + operation (QuantumGate | QuantumPhase): The gate operation to visit. + ctrls (list[IndexedIdentifier]): An optional list of control qubits + on the gate operation. Returns: - The list of gates and phase operations after unrolling. + list[QuantumGate | QuantumPhase]: The list of gates and phase operations + after unrolling, or an empty list if self._check_only is True. """ operation, ctrls = copy.copy(operation), copy.copy(ctrls) negctrls = [] @@ -1629,10 +1644,11 @@ def _visit_constant_declaration( type variables and not arrays. Assignment is mandatory in constant declaration. Args: - statement (qasm3_ast.ConstantDeclaration): The constant declaration to visit. + statement (ConstantDeclaration): The constant declaration to visit. Returns: - list[qasm3_ast.Statement]: The list containing the unrolled statement. + list[Statement]: The list containing the unrolled statement, or an empty + list if self._check_only is True. """ statements = [] var_name = statement.identifier.name @@ -1748,10 +1764,11 @@ def _visit_classical_declaration( """Visit a classical operation element. Args: - statement (qasm3_ast.ClassicalDeclaration): The classical operation to visit. + statement (ClassicalDeclaration): The classical operation to visit. Returns: - list[qasm3_ast.Statement]: The list containing the unrolled statement. + list[Statement]: The list containing the unrolled statement, or an empty list + if self._check_only is True. """ statements = [] var_name = statement.identifier.name @@ -1986,10 +2003,11 @@ def _visit_classical_assignment( """Visit a classical assignment element. Args: - statement (qasm3_ast.ClassicalAssignment): The classical assignment to visit. + statement (ClassicalAssignment): The classical assignment to visit. Returns: - list[qasm3_ast.Statement]: The list containing the unrolled statement. + list[Statement]: The list containing the unrolled statement, or an empty list + if self._check_only is True. """ statements = [] lvalue = statement.lvalue @@ -2161,7 +2179,7 @@ def _evaluate_array_initialization( """Evaluate an array initialization. Args: - array_literal (qasm3_ast.ArrayLiteral): The array literal to evaluate. + array_literal (ArrayLiteral): The array literal to evaluate. dimensions (list[int]): The dimensions of the array. base_type (Any): The base type of the array. @@ -2203,10 +2221,11 @@ def _visit_branching_statement( """Visit a branching statement element. Args: - statement (qasm3_ast.BranchingStatement): The branching statement to visit. + statement (BranchingStatement): The branching statement to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled branch statements. + list[Statement]: The list of unrolled branch statements, , or an empty list + if self._check_only is True. """ self._scope_manager.push_context(Context.BLOCK) self._scope_manager.push_scope({}) @@ -2334,10 +2353,11 @@ def _visit_forin_loop(self, statement: qasm3_ast.ForInLoop) -> list[qasm3_ast.St """Visit a for-in loop statement element. Args: - statement (qasm3_ast.ForInLoop): The for-in loop statement to visit. + statement (ForInLoop): The for-in loop statement to visit. Returns: - list[qasm3_ast.Statement]: The list containing the loop statements. + list[Statement]: The list containing the loop statements, + or an empty list if self._check_only is True. """ irange = [] if isinstance(statement.set_declaration, qasm3_ast.RangeDefinition): @@ -2424,8 +2444,9 @@ def _visit_subroutine_definition( The subroutine definition to visit. Returns: - The list containing the statement if it is an ExternDeclaration, - otherwise an empty list. Returns an empty list if self._check_only is True. + Sequence[None | ExternDeclaration]: The list containing the statement + if it is an ExternDeclaration, otherwise an empty list. + Returns an empty list if self._check_only is True. """ fn_name = statement.name.name statements = [] @@ -2473,10 +2494,15 @@ def _visit_function_call( """Visit a function call element. Args: - statement (qasm3_ast.FunctionCall): The function call to visit. - Returns: - None + statement (FunctionCall): The function call to visit. + Returns: + tuple[Any | None, list[Statement | FunctionCall]]: If the function is undefined, + returns a tuple of None and an empty list. + If the function is defined, returns a tuple of the function's return + value and a list containing the fuction statement. + If self._check_only is True, instead return a tuple of + the function's return value and an empty list. """ fn_name = statement.name.name if fn_name not in self._subroutine_defns and fn_name not in FUNCTION_MAP: @@ -2595,12 +2621,15 @@ def _visit_while_loop(self, statement: qasm3_ast.WhileLoop) -> list[qasm3_ast.St """Visit a while-loop element. Args: - statement (qasm3_ast.WhileLoop): The while-loop AST node. + statement (WhileLoop): The while-loop AST node. + Returns: - list[qasm3_ast.Statement]: The list of unrolled statements from the while-loop. + list[Statement]: The list of unrolled statements from the while-loop, or an + empty list if self._check_only is True. + Raises: ValidationError: If loop condition is non-classical or dynamic. - LoopLimitExceededError: if the loop exceeds the maximum limit. + LoopLimitExceededError: If the loop exceeds the maximum limit. """ result = [] @@ -2653,10 +2682,10 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> list[No """Visit an alias statement element. Args: - statement (qasm3_ast.AliasStatement): The alias statement to visit. + statement (AliasStatement): The alias statement to visit. Returns: - An empty list. + list[None]: An empty list. """ # pylint: disable=too-many-branches target = statement.target @@ -2769,10 +2798,10 @@ def _visit_switch_statement( # type: ignore[return] """Visit a switch statement element. Args: - statement (qasm3_ast.SwitchStatement): The switch statement to visit. + statement (SwitchStatement): The switch statement to visit. Returns: - list[qasm3_ast.Statement]: The list of statements generated by the switch statement. + list[Statement]: The list of statements generated by the switch statement. """ # 1. analyze the target - it should ONLY be int, not casted switch_target = statement.target @@ -2861,10 +2890,10 @@ def _resolve_duration_unit(self, time_var: qasm3_ast.Expression) -> qasm3_ast.Ti time is set). SI units are already converted to ns by the evaluator. Args: - time_var (qasm3_ast.Expression): A DurationLiteral to check. + time_var (Expression): A DurationLiteral to check. Returns: - qasm3_ast.TimeUnit: The unit of the DurationLiteral. + TimeUnit: The unit of the DurationLiteral. """ source_is_dt = ( isinstance(time_var, qasm3_ast.DurationLiteral) @@ -2883,10 +2912,10 @@ def _visit_delay_statement( Visit a DelayInstruction statement. Args: - statement (qasm3_ast.DelayInstruction): The DelayInstruction statement to visit. + statement (DelayInstruction): The DelayInstruction statement to visit. Returns: - list[qasm3_ast.Statement]: The list of statements generated by the DelayInstruction. + list[Statement]: The list of statements generated by the DelayInstruction. """ _delay_time_var = statement.duration global_scope = self._scope_manager.get_global_scope() @@ -2972,10 +3001,11 @@ def _visit_box_statement(self, statement: qasm3_ast.Box) -> list[qasm3_ast.State Visit a Box statement. Args: - statement (qasm3_ast.Box): The Box statement node to visit. + statement (Box): The Box statement node to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled statements from the Box statement. + list[Statement]: The list of unrolled statements from the Box statement, or an + empty list if self._check_only is True. """ statements = [] _box_time_var = statement.duration @@ -3048,10 +3078,11 @@ def _visit_calibration_definition( """Visit a calibration definition element. Args: - statement (qasm3_ast.CalibrationDefinition): The calibration definition to visit. + statement (CalibrationDefinition): The calibration definition to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled statements. + list[Statement]: The list of unrolled statements, or the original statement + in a list if self._check_only is True. """ from openpulse.parser import ( # pylint: disable=import-outside-toplevel OpenPulseParsingError, @@ -3197,10 +3228,11 @@ def _visit_calibration_statement( """Visit a calibration statement element. Args: - statement (qasm3_ast.CalibrationStatement): The calibration statement to visit. + statement (CalibrationStatement): The calibration statement to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled statements. + list[Statement]: The list of unrolled statements, or the original statement + in a list if self._check_only is True. """ from openpulse.parser import ( # pylint: disable=import-outside-toplevel OpenPulseParsingError, @@ -3266,10 +3298,10 @@ def _visit_calibration_grammar_declaration( """Visit a calibration grammar declaration element. Args: - statement (qasm3_ast.CalibrationGrammarDeclaration): The calibration grammar declaration + statement (CalibrationGrammarDeclaration): The calibration grammar declaration Returns: - list[qasm3_ast.Statement]: The list of unrolled statements. + list[Statement]: The list of unrolled statements. """ if statement.name != "openpulse": raise_qasm3_error( @@ -3286,10 +3318,11 @@ def _visit_include(self, include: qasm3_ast.Include) -> list[qasm3_ast.Statement """Visit an include statement element. Args: - include (qasm3_ast.Include): The include statement to visit. + include (Include): The include statement to visit. Returns: - None + list[Statement]: A list containing the include statement, + or an empty list if self._check_only is True. """ filename = include.filename if filename in self._included_files: @@ -3306,10 +3339,10 @@ def visit_statement(self, statement: qasm3_ast.Statement) -> list[qasm3_ast.Stat """Visit a statement element. Args: - statement (qasm3_ast.Statement): The statement to visit. + statement (Statement): The statement to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled statements. + list[Statement]: The list of unrolled statements. """ logger.debug("Visiting statement '%s'", str(statement)) result = [] @@ -3334,10 +3367,10 @@ def visit_basic_block(self, stmt_list: list[qasm3_ast.Statement]) -> list[qasm3_ """Visit a basic block of statements. Args: - stmt_list (list[qasm3_ast.Statement]): The list of statements to visit. + stmt_list (list[Statement]): The list of statements to visit. Returns: - list[qasm3_ast.Statement]: The list of unrolled statements. + list[Statement]: The list of unrolled statements. """ result = [] for stmt in stmt_list: @@ -3351,10 +3384,10 @@ def finalize(self, unrolled_stmts: list[qasm3_ast.Statement]) -> list[qasm3_ast. To add more rules if needed Args: - unrolled_stmts (list[qasm3_ast.Statement]): The list of unrolled statements. + unrolled_stmts (list[Statement]): The list of unrolled statements. Returns: - list[qasm3_ast.Statement]: The list of finalized statements. + list[Statement]: The list of finalized statements. """ # remove the gphase qubits if they use ALL qubits From edc8fb2c170646a548e76a94495a4d24797ee690 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Thu, 6 Aug 2026 22:32:59 -0400 Subject: [PATCH 09/21] push for suggested changes --- src/pyqasm/modules/base.py | 2 +- src/pyqasm/visitor.py | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 65160de..5b7c262 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -720,7 +720,7 @@ def compare(self, other_module: QasmModule) -> None: self_ext_gates_str = "\n".join(self._external_gates) other_ext_gates_str = "\n".join(other_module._external_gates) - table_data = [ + table_data: list[list[Any]] = [ ["Qubits", self.num_qubits, other_module.num_qubits], ["Classical Bits", self.num_clbits, other_module.num_clbits], ["Measurements", self.has_measurements(), other_module.has_measurements()], diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index ce07aae..32c16ed 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -534,7 +534,8 @@ def _handle_function_init_expression( init_value (Any): The value to handle function initialization expression. Returns: - None | Expression: The resultant expression if the expression is applied, otherwise None. + None | Expression: The resultant expression if + the expression is applied, otherwise None. """ if isinstance(expression, qasm3_ast.FunctionCall): func_name = expression.name.name @@ -747,7 +748,8 @@ def _visit_reset(self, statement: qasm3_ast.QuantumReset) -> list[qasm3_ast.Quan statement (QuantumReset): The reset statement to visit. Returns: - list[QuantumReset]: The list of unrolled resets, or an empty list if self._check_only is True. + list[QuantumReset]: The list of unrolled resets, + or an empty list if self._check_only is True. """ logger.debug("Visiting reset statement '%s'", str(statement)) if self._resolve_unindexed_reset_qubit(statement): @@ -2500,7 +2502,7 @@ def _visit_function_call( tuple[Any | None, list[Statement | FunctionCall]]: If the function is undefined, returns a tuple of None and an empty list. If the function is defined, returns a tuple of the function's return - value and a list containing the fuction statement. + value and a list containing the function statement. If self._check_only is True, instead return a tuple of the function's return value and an empty list. """ @@ -2801,7 +2803,8 @@ def _visit_switch_statement( # type: ignore[return] statement (SwitchStatement): The switch statement to visit. Returns: - list[Statement]: The list of statements generated by the switch statement. + list[Statement]: The list of statements generated by the switch statement, + or an empty list if self._check_only is True. """ # 1. analyze the target - it should ONLY be int, not casted switch_target = statement.target @@ -2915,7 +2918,8 @@ def _visit_delay_statement( statement (DelayInstruction): The DelayInstruction statement to visit. Returns: - list[Statement]: The list of statements generated by the DelayInstruction. + list[Statement]: The list of statements generated by the DelayInstruction, + or an empty list if self._check_only is True. """ _delay_time_var = statement.duration global_scope = self._scope_manager.get_global_scope() From 576fcb0a60d55dff0c09e94b4bac32f652ee0dbc Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Thu, 6 Aug 2026 22:35:24 -0400 Subject: [PATCH 10/21] orphaned type comment --- src/pyqasm/visitor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 32c16ed..56bb620 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -2794,9 +2794,9 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> list[No return [] - def _visit_switch_statement( # type: ignore[return] + def _visit_switch_statement( self, statement: qasm3_ast.SwitchStatement - ) -> list[qasm3_ast.Statement]: + ) -> list[qasm3_ast.Statement]: # type: ignore[return] """Visit a switch statement element. Args: From 2375ed69e8d60bbdd1e46d627c90e6726a4bf0ed Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 09:03:36 -0400 Subject: [PATCH 11/21] revert last change --- src/pyqasm/visitor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 56bb620..32c16ed 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -2794,9 +2794,9 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> list[No return [] - def _visit_switch_statement( + def _visit_switch_statement( # type: ignore[return] self, statement: qasm3_ast.SwitchStatement - ) -> list[qasm3_ast.Statement]: # type: ignore[return] + ) -> list[qasm3_ast.Statement]: """Visit a switch statement element. Args: From 28e7028f447841a021447c8111d64f6b4964b120 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 09:15:52 -0400 Subject: [PATCH 12/21] Update qasm3.py --- src/pyqasm/modules/qasm3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 2d88a29..81b24ec 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -22,7 +22,7 @@ from openqasm3.ast import Pragma, Program, QASMNode from openqasm3.printer import Printer, PrinterState -from pyqasm.modules.base import QasmModule +from pyqasm.modules.base import QasmModule, QasmVisitor class Qasm3Printer(Printer): @@ -80,7 +80,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: qasm_ast.version = "3.0" return dumps(qasm_ast) - def accept(self, visitor) -> None: + def accept(self, visitor: QasmVisitor) -> None: """Accept a visitor for the module. Args: From 95d622f7a78d11d3b89d00b114f0a0f35cd8cba0 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 09:22:45 -0400 Subject: [PATCH 13/21] remove qasmvisitor while fixing mypy --- src/pyqasm/modules/base.py | 2 +- src/pyqasm/modules/qasm2.py | 4 ++-- src/pyqasm/modules/qasm3.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index 8531b27..a40c9ee 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -813,7 +813,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: """Convert the qasm AST to a string.""" @abstractmethod - def accept(self, visitor: QasmVisitor) -> None: + def accept(self, visitor) -> None: """Accept a visitor for the module. Args: diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index 13cbc9d..3c4108e 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -24,7 +24,7 @@ from openqasm3.printer import dumps from pyqasm.exceptions import ValidationError, raise_qasm3_error -from pyqasm.modules.base import QasmModule, QasmVisitor +from pyqasm.modules.base import QasmModule from pyqasm.modules.qasm3 import Qasm3Module # the QASM 2.0 production: a gate application, a measurement or a reset. @@ -148,7 +148,7 @@ def to_qasm3(self, as_str: bool = False) -> str | Qasm3Module: qasm_program.version = "3.0" return dumps(qasm_program) if as_str else Qasm3Module(self._name, qasm_program) - def accept(self, visitor: QasmVisitor) -> None: + def accept(self, visitor) -> None: """Accept a visitor for the module. Args: diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 81b24ec..2d88a29 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -22,7 +22,7 @@ from openqasm3.ast import Pragma, Program, QASMNode from openqasm3.printer import Printer, PrinterState -from pyqasm.modules.base import QasmModule, QasmVisitor +from pyqasm.modules.base import QasmModule class Qasm3Printer(Printer): @@ -80,7 +80,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: qasm_ast.version = "3.0" return dumps(qasm_ast) - def accept(self, visitor: QasmVisitor) -> None: + def accept(self, visitor) -> None: """Accept a visitor for the module. Args: From dcc51885f75edaca65880b2e1644fd11928cf05b Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 10:13:08 -0400 Subject: [PATCH 14/21] fix mypy --- src/pyqasm/modules/base.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index a40c9ee..b964fa1 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -89,6 +89,7 @@ def drop_statements(statements: list[StatementT], unwanted: type) -> list[Statem kept.append(stmt) return kept + F = TypeVar("F", bound=Callable[..., Any]) @@ -592,7 +593,8 @@ def reverse_qubit_order(self, in_place: bool = True) -> QasmModule: if not isinstance(bit, qasm3_ast.IndexedIdentifier): continue # physical qubit ("$n"): not part of any register curr_reg_name = bit.name.name - curr_reg_idx = bit.indices[0][0].value + index_node = bit.indices[0][0] # type: ignore[index] + curr_reg_idx = index_node.value # type: ignore[union-attr] new_reg_idx = new_qubit_mappings[curr_reg_name][curr_reg_idx] # make the idx -ve so that this is not touched @@ -600,7 +602,7 @@ def reverse_qubit_order(self, in_place: bool = True) -> QasmModule: # idx -> -1 * idx - 1 as we also have to look at index 0 # which will remain 0 if we just multiply by -1 - bit.indices[0][0].value = -1 * new_reg_idx - 1 + index_node.value = -1 * new_reg_idx - 1 # type: ignore[union-attr] # remove the -ve marker for operation in iter_quantum_statements(qasm_module._unrolled_ast.statements): @@ -608,9 +610,10 @@ def reverse_qubit_order(self, in_place: bool = True) -> QasmModule: for bit in bit_list: if not isinstance(bit, qasm3_ast.IndexedIdentifier): continue - if bit.indices[0][0].value < 0: - bit.indices[0][0].value += 1 - bit.indices[0][0].value *= -1 + index_node = bit.indices[0][0] # type: ignore[index] + if index_node.value < 0: # type: ignore[union-attr] + index_node.value += 1 # type: ignore[union-attr] + index_node.value *= -1 # type: ignore[union-attr] # 3. update the original AST with the unrolled AST qasm_module._statements = qasm_module._unrolled_ast.statements From cdedb54aa7b4e4ca2eed3145f7ebf8a3edc0ed3c Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 10:20:32 -0400 Subject: [PATCH 15/21] qasmvisitor push --- src/pyqasm/modules/base.py | 2 +- src/pyqasm/modules/qasm2.py | 6 +++--- src/pyqasm/modules/qasm3.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pyqasm/modules/base.py b/src/pyqasm/modules/base.py index b964fa1..e954923 100644 --- a/src/pyqasm/modules/base.py +++ b/src/pyqasm/modules/base.py @@ -816,7 +816,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: """Convert the qasm AST to a string.""" @abstractmethod - def accept(self, visitor) -> None: + def accept(self, visitor: QasmVisitor) -> None: """Accept a visitor for the module. Args: diff --git a/src/pyqasm/modules/qasm2.py b/src/pyqasm/modules/qasm2.py index 3c4108e..8a268ba 100644 --- a/src/pyqasm/modules/qasm2.py +++ b/src/pyqasm/modules/qasm2.py @@ -24,7 +24,7 @@ from openqasm3.printer import dumps from pyqasm.exceptions import ValidationError, raise_qasm3_error -from pyqasm.modules.base import QasmModule +from pyqasm.modules.base import QasmModule, QasmVisitor from pyqasm.modules.qasm3 import Qasm3Module # the QASM 2.0 production: a gate application, a measurement or a reset. @@ -148,7 +148,7 @@ def to_qasm3(self, as_str: bool = False) -> str | Qasm3Module: qasm_program.version = "3.0" return dumps(qasm_program) if as_str else Qasm3Module(self._name, qasm_program) - def accept(self, visitor) -> None: + def accept(self, visitor: QasmVisitor) -> None: """Accept a visitor for the module. Args: @@ -158,4 +158,4 @@ def accept(self, visitor) -> None: unrolled_stmt_list = visitor.visit_basic_block(self._statements) final_stmt_list = visitor.finalize(unrolled_stmt_list) - self.unrolled_ast.statements = final_stmt_list + self.unrolled_ast.statements = final_stmt_list # type: ignore[assignment] diff --git a/src/pyqasm/modules/qasm3.py b/src/pyqasm/modules/qasm3.py index 2d88a29..41e3458 100644 --- a/src/pyqasm/modules/qasm3.py +++ b/src/pyqasm/modules/qasm3.py @@ -22,7 +22,7 @@ from openqasm3.ast import Pragma, Program, QASMNode from openqasm3.printer import Printer, PrinterState -from pyqasm.modules.base import QasmModule +from pyqasm.modules.base import QasmModule, QasmVisitor class Qasm3Printer(Printer): @@ -80,7 +80,7 @@ def _qasm_ast_to_str(self, qasm_ast: Program) -> str: qasm_ast.version = "3.0" return dumps(qasm_ast) - def accept(self, visitor) -> None: + def accept(self, visitor: QasmVisitor) -> None: """Accept a visitor for the module. Args: @@ -89,4 +89,4 @@ def accept(self, visitor) -> None: unrolled_stmt_list = visitor.visit_basic_block(self._statements) final_stmt_list = visitor.finalize(unrolled_stmt_list) - self._unrolled_ast.statements = final_stmt_list + self._unrolled_ast.statements = final_stmt_list # type: ignore[assignment] From 423b423ced643ab24cb2e02fee14b6752267a346 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 12:24:20 -0400 Subject: [PATCH 16/21] J1, J2 --- src/pyqasm/pulse/visitor.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pyqasm/pulse/visitor.py b/src/pyqasm/pulse/visitor.py index 9fa5cc1..aee1e55 100644 --- a/src/pyqasm/pulse/visitor.py +++ b/src/pyqasm/pulse/visitor.py @@ -21,7 +21,7 @@ """ import logging -from typing import Any, Optional +from typing import Any, Optional, Sequence import openqasm3.ast as qasm3_ast @@ -754,11 +754,13 @@ def _visit_function_call( # pylint: disable=too-many-branches, too-many-stateme return _return_value, [statement] - def visit_statement(self, statement: qasm3_ast.Statement) -> list[qasm3_ast.Statement]: + def visit_statement( + self, statement: qasm3_ast.Statement | qasm3_ast.Pragma + ) -> list[qasm3_ast.Statement]: """Visit a statement element. Args: - statement (qasm3_ast.Statement): The statement to visit. + statement (Statement | Pragma): The statement to visit. Returns: None @@ -809,13 +811,13 @@ def visit_statement(self, statement: qasm3_ast.Statement) -> list[qasm3_ast.Stat def visit_basic_block( self, - stmt_list: list[qasm3_ast.Statement], + stmt_list: Sequence[qasm3_ast.Statement | qasm3_ast.Pragma], is_def_cal: bool, ) -> list[qasm3_ast.Statement]: """Visit a basic block of statements. Args: - stmt_list (list[qasm3_ast.Statement]): The list of statements to visit. + stmt_list (Sequence[Statement | Pragma]): The list of statements to visit. is_def_cal (bool): is the given statements from def_cal block. Returns: From 327e1a73d54852a82796ad962905db4b8d8f6eff Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 12:38:04 -0400 Subject: [PATCH 17/21] J4 J5 --- src/pyqasm/visitor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 17cdc94..8d90aca 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -820,8 +820,8 @@ def _expand_barrier_ranges( only sees IntegerLiteral indices. Args: - barrier (QuantumBarrier): The barrier with qubits to consolidate. - barrier_qubits (QuantumBarrier): The qubits to replace. + barrier (QuantumBarrier): The barrier whose qubits are being expanded. + barrier_qubits (list[IndexedIdentifier | Identifier]): The expanded qubit operands. Returns: list: The resultant list of consolidated qubits. @@ -2250,7 +2250,7 @@ def _visit_branching_statement( statement (BranchingStatement): The branching statement to visit. Returns: - list[Statement]: The list of unrolled branch statements, , or an empty list + list[Statement]: The list of unrolled branch statements, or an empty list if self._check_only is True. """ self._scope_manager.push_context(Context.BLOCK) From 278b9eb27f7fa3418087a377ee517a42fb50ac1f Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 17:21:55 -0400 Subject: [PATCH 18/21] K1 --- src/pyqasm/pulse/visitor.py | 4 ++-- src/pyqasm/visitor.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pyqasm/pulse/visitor.py b/src/pyqasm/pulse/visitor.py index aee1e55..e52bab4 100644 --- a/src/pyqasm/pulse/visitor.py +++ b/src/pyqasm/pulse/visitor.py @@ -755,8 +755,8 @@ def _visit_function_call( # pylint: disable=too-many-branches, too-many-stateme return _return_value, [statement] def visit_statement( - self, statement: qasm3_ast.Statement | qasm3_ast.Pragma - ) -> list[qasm3_ast.Statement]: + self, statement: qasm3_ast.Statement | qasm3_ast.Pragma + ) -> list[qasm3_ast.Statement]: """Visit a statement element. Args: diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 8d90aca..62f52fa 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -3411,7 +3411,7 @@ def visit_statement( """Visit a statement element. Args: - statement (qasm3_ast.Statement): The statement to visit. + statement (Statement | Pragma): The statement to visit. Returns: list[Statement]: The list of unrolled statements. @@ -3447,7 +3447,7 @@ def visit_basic_block( """Visit a basic block of statements. Args: - stmt_list (list[qasm3_ast.Statement]): The list of statements to visit. + stmt_list (Sequence[Statement | Pragma]): The list of statements to visit. Returns: list[Statement]: The list of unrolled statements. From f5904b9bba2a35372989581c7880cf1bf18c5087 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 17:25:52 -0400 Subject: [PATCH 19/21] Update changelog to cover increased scope --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e91ae..493dd9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ Types of changes: ### Improved / Modified - Consolidated the hardcoded `"__PYQASM_QUBITS__"` string literals scattered across `visitor.py`, `transformer.py` and `pulse/utils.py` into a single `INTERNAL_QUBIT_REGISTER` constant in `elements.py`, alongside an `is_internal_qubit_register()` helper that is now the one place the internal register is recognised. ([#325](https://github.com/qBraid/pyqasm/pull/325)) -- Added type hinting for `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` signatures. Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. Fixes incorrect return types in the docstrings of `visitor.py`. ([#346](https://github.com/qBraid/pyqasm/pull/346)) +- Added / updated type hinting for `base.py`, `qasm2.py`, `qasm3.py`, `visitor.py`, and `pulse/visitor.py` signatures. Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. Fixes incorrect return types in the docstrings of `visitor.py`. ([#346](https://github.com/qBraid/pyqasm/pull/346)) ### Deprecated From 32d9c59de61a892d2b0d05151814c7d2b92408f9 Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 18:30:47 -0400 Subject: [PATCH 20/21] Update src/pyqasm/pulse/visitor.py Co-authored-by: Ryan Hill --- src/pyqasm/pulse/visitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyqasm/pulse/visitor.py b/src/pyqasm/pulse/visitor.py index e52bab4..996a076 100644 --- a/src/pyqasm/pulse/visitor.py +++ b/src/pyqasm/pulse/visitor.py @@ -763,7 +763,7 @@ def visit_statement( statement (Statement | Pragma): The statement to visit. Returns: - None + list[Statement]: The list of resulting statements. """ logger.debug("Visiting statement '%s'", str(statement)) result = [] From 2f22c3917da2ccd332f24a204bdfedb1c23209fb Mon Sep 17 00:00:00 2001 From: Michael Papadopoulos Date: Fri, 7 Aug 2026 18:32:32 -0400 Subject: [PATCH 21/21] Update CHANGELOG.md Co-authored-by: Ryan Hill --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 493dd9c..20919f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ Types of changes: ### Improved / Modified - Consolidated the hardcoded `"__PYQASM_QUBITS__"` string literals scattered across `visitor.py`, `transformer.py` and `pulse/utils.py` into a single `INTERNAL_QUBIT_REGISTER` constant in `elements.py`, alongside an `is_internal_qubit_register()` helper that is now the one place the internal register is recognised. ([#325](https://github.com/qBraid/pyqasm/pull/325)) -- Added / updated type hinting for `base.py`, `qasm2.py`, `qasm3.py`, `visitor.py`, and `pulse/visitor.py` signatures. Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. Fixes incorrect return types in the docstrings of `visitor.py`. ([#346](https://github.com/qBraid/pyqasm/pull/346)) +- Added / updated type hinting for `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` signatures, plus the `visit_statement` / `visit_basic_block` signatures in `pulse/visitor.py`. Fixed grammatical typos in `base.py`, `qasm2.py`, `qasm3.py`, and `visitor.py` docstrings. Fixes incorrect return types in the docstrings of `visitor.py`. ([#346](https://github.com/qBraid/pyqasm/pull/346)) ### Deprecated