diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index fe582540a647..7a4d7a6a1ebd 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -25,6 +25,10 @@ from qiskit.circuit.measure import Measure from qiskit.providers.models.backendconfiguration import BackendConfiguration from qiskit.providers.models.backendproperties import BackendProperties +<<<<<<< HEAD +======= +from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES +>>>>>>> 65ab96508 (Fix handling of control flow instructions in convert_to_target() (#11877)) from qiskit.providers.models.pulsedefaults import PulseDefaults from qiskit.providers.options import Options from qiskit.providers.exceptions import BackendPropertyError @@ -58,12 +62,48 @@ def convert_to_target( InstructionProperties, ) +<<<<<<< HEAD # Standard gates library mapping, multicontrolled gates not included since they're # variable width name_mapping = get_standard_gate_name_mapping() target = None if custom_name_mapping is not None: name_mapping.update(custom_name_mapping) +======= + required = ["measure", "delay"] + + # Load Qiskit object representation + qiskit_inst_mapping = get_standard_gate_name_mapping() + if custom_name_mapping: + qiskit_inst_mapping.update(custom_name_mapping) + + qiskit_control_flow_mapping = { + "if_else": IfElseOp, + "while_loop": WhileLoopOp, + "for_loop": ForLoopOp, + "switch_case": SwitchCaseOp, + } + + in_data = {"num_qubits": configuration.n_qubits} + + # Parse global configuration properties + if hasattr(configuration, "dt"): + in_data["dt"] = configuration.dt + if hasattr(configuration, "timing_constraints"): + in_data.update(configuration.timing_constraints) + + # Create instruction property placeholder from backend configuration + basis_gates = set(getattr(configuration, "basis_gates", [])) + supported_instructions = set(getattr(configuration, "supported_instructions", [])) + gate_configs = {gate.name: gate for gate in configuration.gates} + all_instructions = set.union( + basis_gates, set(required), supported_instructions.intersection(CONTROL_FLOW_OP_NAMES) + ) + + inst_name_map = {} # type: Dict[str, Instruction] + + faulty_ops = set() +>>>>>>> 65ab96508 (Fix handling of control flow instructions in convert_to_target() (#11877)) faulty_qubits = set() # Parse from properties if it exsits if properties is not None: diff --git a/releasenotes/notes/fix-control-flow-convert-to-target-ae838418a7ad2a20.yaml b/releasenotes/notes/fix-control-flow-convert-to-target-ae838418a7ad2a20.yaml new file mode 100644 index 000000000000..be29520fad89 --- /dev/null +++ b/releasenotes/notes/fix-control-flow-convert-to-target-ae838418a7ad2a20.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed an issue with the :func:`.convert_to_target` where the converter + would incorrectly ignore control flow instructions if they were specified + in the :attr:`.BackendConfiguration.supported_instructions` attribute which + is the typical location that control flow instructions are specified in a + :class:`.BackendConfiguration` object. + Fixed `#11872 `__. diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index efa1dd3ebf2f..d33c0b184957 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -37,7 +37,7 @@ FakeSherbrooke, FakePrague, ) -from qiskit.providers.backend_compat import BackendV2Converter +from qiskit.providers.backend_compat import BackendV2Converter, convert_to_target from qiskit.providers.models.backendproperties import BackendProperties from qiskit.providers.backend import BackendV2 from qiskit.utils import optionals @@ -635,3 +635,59 @@ def test_faulty_full_path_transpile_connected_cmap(self, opt_level): tqc = transpile(qc, v2_backend, seed_transpiler=433, optimization_level=opt_level) connections = [tuple(sorted(tqc.find_bit(q).index for q in x.qubits)) for x in tqc.data] self.assertNotIn((0, 1), connections) + + def test_convert_to_target_control_flow(self): + backend = Fake27QPulseV1() + properties = backend.properties() + configuration = backend.configuration() + configuration.supported_instructions = [ + "cx", + "id", + "delay", + "measure", + "reset", + "rz", + "sx", + "x", + "if_else", + "for_loop", + "switch_case", + ] + defaults = backend.defaults() + target = convert_to_target(configuration, properties, defaults) + self.assertTrue(target.instruction_supported("if_else", ())) + self.assertFalse(target.instruction_supported("while_loop", ())) + self.assertTrue(target.instruction_supported("for_loop", ())) + self.assertTrue(target.instruction_supported("switch_case", ())) + + def test_convert_unrelated_supported_instructions(self): + backend = Fake27QPulseV1() + properties = backend.properties() + configuration = backend.configuration() + configuration.supported_instructions = [ + "cx", + "id", + "delay", + "measure", + "reset", + "rz", + "sx", + "x", + "play", + "u2", + "u3", + "u1", + "shiftf", + "acquire", + "setf", + "if_else", + "for_loop", + "switch_case", + ] + defaults = backend.defaults() + target = convert_to_target(configuration, properties, defaults) + self.assertTrue(target.instruction_supported("if_else", ())) + self.assertFalse(target.instruction_supported("while_loop", ())) + self.assertTrue(target.instruction_supported("for_loop", ())) + self.assertTrue(target.instruction_supported("switch_case", ())) + self.assertFalse(target.instruction_supported("u3", (0,)))