From 92b4ee1e8b5842ab82ff7a90a18ced89a5394fc2 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Sun, 16 Jul 2023 18:04:32 +0200 Subject: [PATCH 1/8] raise an exception with a custom gate with no qubits or with one-or-more clbits --- qiskit/circuit/quantumcircuit.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index e6cb4c4638d7..88c0efe64309 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -5089,6 +5089,15 @@ def _qasm2_define_custom_operation(operation, existing_gate_names, gates_to_defi ) else: parameters_qasm = "" + + # Gate definitions with 0 qubits or with any classical bits are not allowed. + if operation.num_qubits == 0 or operation.num_clbits != 0: + raise CircuitError( + "OpenQASM 2 does not allow gate definitions with no qubits or with any " + f"classical bit: '{operation.name}' has {operation.num_qubits} qubits " + f"and {operation.num_clbits} clbits" + ) + qubits_qasm = ",".join(f"q{i}" for i in range(parameterized_operation.num_qubits)) parameterized_definition = getattr(parameterized_operation, "definition", None) if parameterized_definition is None: From fc0d3be7aeffddef5b8a7ffb6c7ec41b2ee5732e Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Sun, 23 Jul 2023 18:28:42 +0200 Subject: [PATCH 2/8] QASM2ExportError --- qiskit/circuit/quantumcircuit.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 329f9b11a480..9889b47bb866 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -5088,8 +5088,10 @@ def _qasm2_define_custom_operation(operation, existing_gate_names, gates_to_defi parameters_qasm = "" # Gate definitions with 0 qubits or with any classical bits are not allowed. + from qiskit.qasm2 import QASM2ExportError # pylint: disable=cyclic-import + if operation.num_qubits == 0 or operation.num_clbits != 0: - raise CircuitError( + raise QASM2ExportError( "OpenQASM 2 does not allow gate definitions with no qubits or with any " f"classical bit: '{operation.name}' has {operation.num_qubits} qubits " f"and {operation.num_clbits} clbits" From b4e8ce81b65cf00342ba337774882118d977c008 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Mon, 24 Jul 2023 07:59:41 +0200 Subject: [PATCH 3/8] test_circuit_raises_invalid_custom_gate_1 --- test/python/circuit/test_circuit_qasm.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 32f12e2c89ef..222e62fdba98 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -657,6 +657,15 @@ def test_circuit_raises_on_single_bit_condition(self): with self.assertRaisesRegex(QasmError, "OpenQASM 2 can only condition on registers"): qc.qasm() + def test_circuit_raises_invalid_custom_gate_1(self): + """OpenQASM 2 exporter of custom gates with no qubits.""" + legit_circuit = QuantumCircuit(5, name="legit_circuit") + empty_circuit = QuantumCircuit(name="empty_circuit") + legit_circuit.append(empty_circuit) + + with self.assertRaisesRegex(QasmError, "gate definitions with no qubits"): + legit_circuit.qasm() + def test_circuit_qasm_with_permutations(self): """Test circuit qasm() method with Permutation gates.""" From a8127baddb93bec2b6a662973f7e4f04e1767085 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Mon, 24 Jul 2023 07:59:55 +0200 Subject: [PATCH 4/8] test_circuit_raises_invalid_custom_gate_2 --- test/python/circuit/test_circuit_qasm.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 222e62fdba98..848b39169fea 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -666,6 +666,19 @@ def test_circuit_raises_invalid_custom_gate_1(self): with self.assertRaisesRegex(QasmError, "gate definitions with no qubits"): legit_circuit.qasm() + def test_circuit_raises_invalid_custom_gate_2(self): + """OpenQASM 2 exporter of custom instruction.""" + instruction = QuantumCircuit(2, 2, name="inst") + instruction.cx(0, 1) + instruction.measure_all() + custom_instruction = instruction.to_instruction() + + qc = QuantumCircuit(2, 2) + qc.append(custom_instruction, [0, 1], [0, 1]) + + with self.assertRaisesRegex(QasmError, "gate definitions with no qubits"): + qc.qasm() + def test_circuit_qasm_with_permutations(self): """Test circuit qasm() method with Permutation gates.""" From a03841e01e2535c035acc7a1b3c00b4cd9525e8d Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Mon, 24 Jul 2023 08:19:09 +0200 Subject: [PATCH 5/8] reno --- .../qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml | 7 +++++++ test/python/circuit/test_circuit_qasm.py | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml diff --git a/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml b/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml new file mode 100644 index 000000000000..743883b52286 --- /dev/null +++ b/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Some of the custom operator that Qiskit is available to express are not expressible in OpenQASM 2. + When exporting those gates using :meth:`qiskit.QuantumCircuit.qasm`, this sort of operators are now + raising. See `#7351 `__ and + `#10435 `__. diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 848b39169fea..12521fd3f22c 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -658,7 +658,8 @@ def test_circuit_raises_on_single_bit_condition(self): qc.qasm() def test_circuit_raises_invalid_custom_gate_1(self): - """OpenQASM 2 exporter of custom gates with no qubits.""" + """OpenQASM 2 exporter of custom gates with no qubits. + See: https://github.com/Qiskit/qiskit-terra/issues/10435""" legit_circuit = QuantumCircuit(5, name="legit_circuit") empty_circuit = QuantumCircuit(name="empty_circuit") legit_circuit.append(empty_circuit) @@ -667,7 +668,8 @@ def test_circuit_raises_invalid_custom_gate_1(self): legit_circuit.qasm() def test_circuit_raises_invalid_custom_gate_2(self): - """OpenQASM 2 exporter of custom instruction.""" + """OpenQASM 2 exporter of custom instruction. + See: https://github.com/Qiskit/qiskit-terra/issues/7351""" instruction = QuantumCircuit(2, 2, name="inst") instruction.cx(0, 1) instruction.measure_all() From 69a4f96773a25940848c86c4f66b9376fd62ac4b Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 24 Jul 2023 11:08:00 +0100 Subject: [PATCH 6/8] Split error messages to be more specific --- qiskit/circuit/quantumcircuit.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 9889b47bb866..8727d78b810e 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -5025,7 +5025,9 @@ def _qasm2_define_custom_operation(operation, existing_gate_names, gates_to_defi Returns a potentially new :class:`.Instruction`, which should be used for the :meth:`~.Instruction.qasm` call (it may have been renamed).""" - from qiskit.circuit import library as lib # pylint: disable=cyclic-import + # pylint: disable=cyclic-import + from qiskit.circuit import library as lib + from qiskit.qasm2 import QASM2ExportError if operation.name in existing_gate_names: return operation @@ -5087,14 +5089,14 @@ def _qasm2_define_custom_operation(operation, existing_gate_names, gates_to_defi else: parameters_qasm = "" - # Gate definitions with 0 qubits or with any classical bits are not allowed. - from qiskit.qasm2 import QASM2ExportError # pylint: disable=cyclic-import - - if operation.num_qubits == 0 or operation.num_clbits != 0: + if operation.num_qubits == 0: + raise QASM2ExportError( + f"OpenQASM 2 cannot represent '{operation.name}, which acts on zero qubits." + ) + if operation.num_clbits != 0: raise QASM2ExportError( - "OpenQASM 2 does not allow gate definitions with no qubits or with any " - f"classical bit: '{operation.name}' has {operation.num_qubits} qubits " - f"and {operation.num_clbits} clbits" + f"OpenQASM 2 cannot represent '{operation.name}', which acts on {operation.num_clbits}" + " classical bits." ) qubits_qasm = ",".join(f"q{i}" for i in range(parameterized_operation.num_qubits)) From b842b921d962b0526771970d5258cdbc6f19b201 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 24 Jul 2023 11:08:45 +0100 Subject: [PATCH 7/8] Fix testing bug --- test/python/circuit/test_circuit_qasm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 12521fd3f22c..83e1d7999a47 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -657,28 +657,28 @@ def test_circuit_raises_on_single_bit_condition(self): with self.assertRaisesRegex(QasmError, "OpenQASM 2 can only condition on registers"): qc.qasm() - def test_circuit_raises_invalid_custom_gate_1(self): + def test_circuit_raises_invalid_custom_gate_no_qubits(self): """OpenQASM 2 exporter of custom gates with no qubits. See: https://github.com/Qiskit/qiskit-terra/issues/10435""" legit_circuit = QuantumCircuit(5, name="legit_circuit") empty_circuit = QuantumCircuit(name="empty_circuit") legit_circuit.append(empty_circuit) - with self.assertRaisesRegex(QasmError, "gate definitions with no qubits"): + with self.assertRaisesRegex(QasmError, "acts on zero qubits"): legit_circuit.qasm() - def test_circuit_raises_invalid_custom_gate_2(self): + def test_circuit_raises_invalid_custom_gate_clbits(self): """OpenQASM 2 exporter of custom instruction. See: https://github.com/Qiskit/qiskit-terra/issues/7351""" instruction = QuantumCircuit(2, 2, name="inst") instruction.cx(0, 1) - instruction.measure_all() + instruction.measure([0, 1], [0, 1]) custom_instruction = instruction.to_instruction() qc = QuantumCircuit(2, 2) qc.append(custom_instruction, [0, 1], [0, 1]) - with self.assertRaisesRegex(QasmError, "gate definitions with no qubits"): + with self.assertRaisesRegex(QasmError, "acts on 2 classical bits"): qc.qasm() def test_circuit_qasm_with_permutations(self): From ac2ebc3d195e62b7b4e8dd14c3939709709de3d1 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 24 Jul 2023 11:10:47 +0100 Subject: [PATCH 8/8] Fixup release note --- .../qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml b/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml index 743883b52286..545032c17a17 100644 --- a/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml +++ b/releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml @@ -1,7 +1,8 @@ --- fixes: - | - Some of the custom operator that Qiskit is available to express are not expressible in OpenQASM 2. - When exporting those gates using :meth:`qiskit.QuantumCircuit.qasm`, this sort of operators are now - raising. See `#7351 `__ and + Qiskit can represent custom instructions that act on zero qubits, or on a non-zero number of + classical bits. These cannot be exported to OpenQASM 2, but previously :meth:`.QuantumCircuit.qasm` + would try, and output invalid OpenQASM 2. Instead, a :exc:`.QASM2ExportError` will now correctly + be raised. See `#7351 `__ and `#10435 `__.