diff --git a/qiskit/circuit/library/generalized_gates/permutation.py b/qiskit/circuit/library/generalized_gates/permutation.py index aa27a6b20fdc..0dd8e0f9df7f 100644 --- a/qiskit/circuit/library/generalized_gates/permutation.py +++ b/qiskit/circuit/library/generalized_gates/permutation.py @@ -186,7 +186,7 @@ def inverse(self, annotated: bool = False) -> PermutationGate: return PermutationGate(pattern=_inverse_pattern(self.pattern)) - def _qasm2_decomposition(self): + def _qasm_decomposition(self): # pylint: disable=cyclic-import from qiskit.synthesis.permutation import synth_permutation_basic diff --git a/qiskit/circuit/library/generalized_gates/unitary.py b/qiskit/circuit/library/generalized_gates/unitary.py index 40271ed4f594..a1550d1a7acb 100644 --- a/qiskit/circuit/library/generalized_gates/unitary.py +++ b/qiskit/circuit/library/generalized_gates/unitary.py @@ -202,7 +202,7 @@ def control( ) return gate - def _qasm2_decomposition(self): + def _qasm_decomposition(self): """Return an unparameterized version of ourselves, so the OQ2 exporter doesn't choke on the non-standard things in our `params` field.""" out = self.definition.to_gate() diff --git a/qiskit/qasm2/export.py b/qiskit/qasm2/export.py index 7c655dfd432b..fbe9b533ea9a 100644 --- a/qiskit/qasm2/export.py +++ b/qiskit/qasm2/export.py @@ -314,8 +314,8 @@ def _define_custom_operation(operation, gates_to_define): # definition, but still continue to return the given object as the call-site object. if operation.base_class in known_good_parameterized: parameterized_operation = type(operation)(*_FIXED_PARAMETERS[: len(operation.params)]) - elif hasattr(operation, "_qasm2_decomposition"): - new_op = operation._qasm2_decomposition() + elif hasattr(operation, "_qasm_decomposition"): + new_op = operation._qasm_decomposition() parameterized_operation = operation = new_op.copy(name=_escape_name(new_op.name, "gate_")) else: parameterized_operation = operation diff --git a/qiskit/qasm3/exporter.py b/qiskit/qasm3/exporter.py index 8997770c14e7..b08b2a9719ef 100644 --- a/qiskit/qasm3/exporter.py +++ b/qiskit/qasm3/exporter.py @@ -1180,13 +1180,16 @@ def build_gate_call(self, instruction: CircuitInstruction): This will also push the gate into the symbol table (if required), including recursively defining the gate blocks.""" - ident = self.symbols.get_gate(instruction.operation) + operation = instruction.operation + if hasattr(operation, "_qasm_decomposition"): + operation = operation._qasm_decomposition() + ident = self.symbols.get_gate(operation) if ident is None: - ident = self.define_gate(instruction.operation) + ident = self.define_gate(operation) qubits = [self._lookup_bit(qubit) for qubit in instruction.qubits] parameters = [ ast.StringifyAndPray(self._rebind_scoped_parameters(param)) - for param in instruction.operation.params + for param in operation.params ] if not self.disable_constants: for parameter in parameters: diff --git a/releasenotes/notes/fix-qasm-3-unitary-2da190be6ba25bbd.yaml b/releasenotes/notes/fix-qasm-3-unitary-2da190be6ba25bbd.yaml new file mode 100644 index 000000000000..3cd59ff5e427 --- /dev/null +++ b/releasenotes/notes/fix-qasm-3-unitary-2da190be6ba25bbd.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix a bug in :class:`.qasm3.Exporter` that caused the exporter to crash when + handling a unitary gate due to incorrect processing of its ``params`` field. diff --git a/test/python/qasm3/test_export.py b/test/python/qasm3/test_export.py index acae1d7d3cd7..1b9c24ca5410 100644 --- a/test/python/qasm3/test_export.py +++ b/test/python/qasm3/test_export.py @@ -2665,6 +2665,23 @@ def test_switch_v1_expr_target(self): test = dumps(qc, experimental=ExperimentalFeatures.SWITCH_CASE_V1) self.assertEqual(test, expected) + def test_circuit_with_unitary(self): + """Test that circuits with `unitary` gate are correctly handled""" + matrix = [[0, 1], [1, 0]] + qc = QuantumCircuit(1) + qc.unitary(matrix, [0]) + expected = """\ +OPENQASM 3.0; +include "stdgates.inc"; +gate unitary _gate_q_0 { + U(pi, -pi, 0) _gate_q_0; +} +qubit[1] q; +unitary q[0]; +""" + test = dumps(qc) + self.assertEqual(test, expected) + @ddt class TestQASM3ExporterFailurePaths(QiskitTestCase):