diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4cea9c..eda63714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,23 @@ barrier q3[0]; ``` ### Improved / Modified +- Improved the error messages for the parameter mismatch errors in basic quantum gates ([#169](https://github.com/qBraid/pyqasm/issues/169)). Following error is raised on parameter count mismatch - +```python +In [1]: import pyqasm + ...: + ...: qasm = """ + ...: OPENQASM 3; + ...: include "stdgates.inc"; + ...: qubit[2] q; + ...: rx(0.5, 1) q[1]; + ...: """ + ...: program = pyqasm.loads(qasm) + ...: program.validate() + +...... +ValidationError: Expected 1 parameter for gate 'rx', but got 2 +``` ### Deprecated ### Removed diff --git a/src/pyqasm/maps/gates.py b/src/pyqasm/maps/gates.py index b07a0b12..de3ee055 100644 --- a/src/pyqasm/maps/gates.py +++ b/src/pyqasm/maps/gates.py @@ -1091,6 +1091,39 @@ def two_qubit_gate_op( "c4x": c4x_gate, } +PARAMS_OP_SET = { + 1: { + "rx", + "ry", + "rz", + "phaseshift", + "p", + "gpi", + "gpi2", + "xx", + "rxx", + "yy", + "ryy", + "zz", + "rzz", + "xy", + "pswap", + "cp", + "cu1", + "crx", + "cry", + "crz", + "cphaseshift", + "cp10", + "cphaseshift01", + "cp01", + "cp00", + "cphaseshift00", + }, + 2: {"xx_plus_yy", "u2", "U2"}, + 3: {"ms", "cu3", "u", "U", "u3", "U3"}, + 4: {"cu"}, +} BASIS_GATE_MAP = { # default basis set is the gate set of the stdgates.inc library file BasisSet.DEFAULT: { @@ -1116,6 +1149,25 @@ def two_qubit_gate_op( } +def map_qasm_op_num_params(op_name: str) -> int: + """ + Map a basic QASM operation to the number of parameters it takes. + + Args: + op_name (str): The QASM operation name. + + Returns: + int: The number of parameters the operation takes. + + Raises: + ValidationError: If the QASM operation is unsupported or undeclared. + """ + for num_params, op_set in PARAMS_OP_SET.items(): + if op_name in op_set: + return num_params + return 0 + + def map_qasm_op_to_callable(op_name: str) -> tuple[Callable, int]: """ Map a QASM operation to a callable. diff --git a/src/pyqasm/visitor.py b/src/pyqasm/visitor.py index 4c9ec5c9..47836fa2 100644 --- a/src/pyqasm/visitor.py +++ b/src/pyqasm/visitor.py @@ -36,6 +36,7 @@ from pyqasm.maps.gates import ( map_qasm_ctrl_op_to_callable, map_qasm_inv_op_to_callable, + map_qasm_op_num_params, map_qasm_op_to_callable, ) from pyqasm.subroutines import Qasm3SubroutineProcessor @@ -769,14 +770,21 @@ def _visit_basic_gate_operation( # pylint: disable=too-many-locals qasm_func, op_qubit_count, inverse_action = map_qasm_inv_op_to_callable( operation.name.name ) - op_parameters = [] + actual_num_params = map_qasm_op_num_params(operation.name.name) if len(operation.arguments) > 0: # parametric gate op_parameters = self._get_op_parameters(operation) if inverse_action == InversionOp.INVERT_ROTATION: op_parameters = [-1 * param for param in op_parameters] + if len(op_parameters) != actual_num_params: + raise_qasm3_error( + f"Expected {op_qubit_count} parameter{'s' if op_qubit_count > 1 else ''}" + f" for gate '{operation.name.name}', but got {len(op_parameters)}", + span=operation.span, + ) + result = [] unrolled_targets = self._unroll_multiple_target_qubits(operation, op_qubit_count) unrolled_gate_function = partial(qasm_func, *op_parameters) diff --git a/tests/qasm3/resources/gates.py b/tests/qasm3/resources/gates.py index 20ca43a5..f15d36d9 100644 --- a/tests/qasm3/resources/gates.py +++ b/tests/qasm3/resources/gates.py @@ -340,7 +340,7 @@ def test_fixture(): """, "Unsupported / undeclared QASM operation: custom_gate", ), - "parameter_mismatch": ( + "parameter_mismatch_1": ( """ OPENQASM 3; include "stdgates.inc"; @@ -355,6 +355,20 @@ def test_fixture(): """, "Parameter count mismatch for gate custom_gate: expected 2 arguments, but got 1 instead.", ), + "parameter_mismatch_2": ( + """ + OPENQASM 3; + include "stdgates.inc"; + + qubit[2] q; + + rx(0.5) q[1]; + + // too many parameters + rz(0.5, 0.0) q[0]; + """, + "Expected 1 parameter for gate 'rz', but got 2", + ), "qubit_mismatch": ( """ OPENQASM 3;