From d7cbfd417b275165247c19946754ef54035f65ec Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 28 Feb 2024 19:41:13 +0000 Subject: [PATCH] Fix transpilation of control flow to no hardware (#11907) Previously we treated `basis_gates=None` the same as `basis_gates=()` in the hardware check; that is, we treated it as meaning "nothing is allowed", when the intention (and how it's treated elsewhere) is more that "everything is allowed." (cherry picked from commit d47a227d982710e7933d744dfd95f6550ff07ed2) --- .../transpiler/preset_passmanagers/common.py | 7 +++++-- ...rol-flow-no-hardware-7c00ad733a569bb9.yaml | 7 +++++++ test/python/compiler/test_transpiler.py | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/fix-transpile-control-flow-no-hardware-7c00ad733a569bb9.yaml diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 8fc1402d322..d59a6271cbf 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -86,9 +86,12 @@ class _InvalidControlFlowForBackend: def __init__(self, basis_gates=(), target=None): if target is not None: self.unsupported = [op for op in CONTROL_FLOW_OP_NAMES if op not in target] - else: - basis_gates = set(basis_gates) if basis_gates is not None else set() + elif basis_gates is not None: + basis_gates = set(basis_gates) self.unsupported = [op for op in CONTROL_FLOW_OP_NAMES if op not in basis_gates] + else: + # Pass manager without basis gates or target; assume everything's valid. + self.unsupported = [] def message(self, property_set): """Create an error message for the given property set.""" diff --git a/releasenotes/notes/fix-transpile-control-flow-no-hardware-7c00ad733a569bb9.yaml b/releasenotes/notes/fix-transpile-control-flow-no-hardware-7c00ad733a569bb9.yaml new file mode 100644 index 00000000000..f306362c404 --- /dev/null +++ b/releasenotes/notes/fix-transpile-control-flow-no-hardware-7c00ad733a569bb9.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + The preset pass managers of :func:`.transpile` will no longer fail on circuits + with control flow, if no hardware target or basis-gate set is specified. They + will now treat such abstract targets as permitting all control-flow operations. + Fixed `#11906 `__. diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 6aebaab832a..954eb016e6d 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1646,6 +1646,27 @@ def test_target_ideal_gates(self, opt_level): self.assertEqual(Operator.from_circuit(result), Operator.from_circuit(qc)) + @data(0, 1, 2, 3) + def test_transpile_control_flow_no_backend(self, opt_level): + """Test `transpile` with control flow and no specified hardware constraints.""" + qc = QuantumCircuit(QuantumRegister(1, "q"), ClassicalRegister(1, "c")) + qc.h(0) + qc.measure(0, 0) + with qc.if_test((qc.clbits[0], False)): + qc.x(0) + with qc.while_loop((qc.clbits[0], True)): + qc.x(0) + with qc.for_loop(range(2)): + qc.x(0) + with qc.switch(qc.cregs[0]) as case: + with case(case.DEFAULT): + qc.x(0) + qc.measure(0, 0) + + transpiled = transpile(qc, optimization_level=opt_level) + # There's nothing that can be optimized here. + self.assertEqual(qc, transpiled) + @data(0, 1, 2, 3) def test_transpile_with_custom_control_flow_target(self, opt_level): """Test transpile() with a target and constrol flow ops."""