From 61323662a7b7349aa1d47fc0d39f3bf04fdbc931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Fri, 31 May 2024 14:43:10 +0200 Subject: [PATCH] Update `transpile()` and `generate_preset_pass_manager` to convert loose input of constraints to a `Target` with `Target.from_configuration()` (#12185) * Update transpile() to convert BackendV1 inputs to BackendV2 with BackendV2Converter * Rework use of instruction durations, move logic from transpile function to individual passes. * Convert loose constraints to target inside transpile. Cover edge cases where num_qubits and/or basis_gates is None. * Fix basis gates oversights, characterize errors in test_transpiler.py and comment out failing cases (temporary). * Fix pulse oversights * Add backend instructions to name mapping by default * Fix edge case 3: Add control flow to default basis gates * Add path for regular cases and path for edge cases that skips target construction. * Complete edge case handling, fix tests. * Update reno * Apply suggestion from Ray's code review Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> * * Migrate full target-building capabilities from transpile to generate_preset_pass_manager. * Apply Matt's suggestions for custom mapping and control flow op names. * Extend docstring, fix typos. * Fix lint, update reno * Create new reno for 1.2 instead of modifying the 1.1 one. --------- Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> --- qiskit/compiler/transpiler.py | 119 ++------ .../preset_passmanagers/__init__.py | 280 +++++++++++++++++- qiskit/transpiler/target.py | 7 +- ...n-generate-preset-pm-5215e00d22d0205c.yaml | 14 + test/python/compiler/test_transpiler.py | 8 +- test/python/transpiler/test_sabre_swap.py | 2 +- .../python/transpiler/test_stochastic_swap.py | 2 +- 7 files changed, 314 insertions(+), 118 deletions(-) create mode 100644 releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index 95c583ceeaad..9dd316839a0f 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -13,7 +13,6 @@ # pylint: disable=invalid-sequence-index """Circuit transpile function""" -import copy import logging from time import time from typing import List, Union, Dict, Callable, Any, Optional, TypeVar @@ -30,11 +29,10 @@ from qiskit.transpiler import Layout, CouplingMap, PropertySet from qiskit.transpiler.basepasses import BasePass from qiskit.transpiler.exceptions import TranspilerError, CircuitTooWideForTarget -from qiskit.transpiler.instruction_durations import InstructionDurations, InstructionDurationsType +from qiskit.transpiler.instruction_durations import InstructionDurationsType from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager -from qiskit.transpiler.timing_constraints import TimingConstraints -from qiskit.transpiler.target import Target, target_to_backend_properties +from qiskit.transpiler.target import Target logger = logging.getLogger(__name__) @@ -335,73 +333,32 @@ def callback_func(**kwargs): UserWarning, ) - _skip_target = False - _given_inst_map = bool(inst_map) # check before inst_map is overwritten - # If a target is specified, have it override any implicit selections from a backend - if target is not None: - if coupling_map is None: - coupling_map = target.build_coupling_map() - if basis_gates is None: - basis_gates = list(target.operation_names) - if instruction_durations is None: - instruction_durations = target.durations() - if inst_map is None: - inst_map = target.instruction_schedule_map() - if dt is None: - dt = target.dt - if timing_constraints is None: - timing_constraints = target.timing_constraints() - if backend_properties is None: - backend_properties = target_to_backend_properties(target) - # If target is not specified and any hardware constraint object is - # manually specified, do not use the target from the backend as - # it is invalidated by a custom basis gate list, custom coupling map, - # custom dt or custom instruction_durations - elif ( - basis_gates is not None # pylint: disable=too-many-boolean-expressions - or coupling_map is not None - or dt is not None - or instruction_durations is not None - or backend_properties is not None - or timing_constraints is not None - ): - _skip_target = True - else: - target = getattr(backend, "target", None) + if not ignore_backend_supplied_default_methods: + if scheduling_method is None and hasattr(backend, "get_scheduling_stage_plugin"): + scheduling_method = backend.get_scheduling_stage_plugin() + if translation_method is None and hasattr(backend, "get_translation_stage_plugin"): + translation_method = backend.get_translation_stage_plugin() initial_layout = _parse_initial_layout(initial_layout) - coupling_map = _parse_coupling_map(coupling_map, backend) approximation_degree = _parse_approximation_degree(approximation_degree) - output_name = _parse_output_name(output_name, circuits) - inst_map = _parse_inst_map(inst_map, backend) + coupling_map = _parse_coupling_map(coupling_map) _check_circuits_coupling_map(circuits, coupling_map, backend) - timing_constraints = _parse_timing_constraints(backend, timing_constraints) - instruction_durations = _parse_instruction_durations(backend, instruction_durations, dt) - - if _given_inst_map and inst_map.has_custom_gate() and target is not None: - # Do not mutate backend target - target = copy.deepcopy(target) - target.update_from_instruction_schedule_map(inst_map) - - if not ignore_backend_supplied_default_methods: - if scheduling_method is None and hasattr(backend, "get_scheduling_stage_plugin"): - scheduling_method = backend.get_scheduling_stage_plugin() - if translation_method is None and hasattr(backend, "get_translation_stage_plugin"): - translation_method = backend.get_translation_stage_plugin() - + # Edge cases require using the old model (loose constraints) instead of building a target, + # but we don't populate the passmanager config with loose constraints unless it's one of + # the known edge cases to control the execution path. pm = generate_preset_pass_manager( optimization_level, - backend=backend, target=target, + backend=backend, basis_gates=basis_gates, - inst_map=inst_map, coupling_map=coupling_map, instruction_durations=instruction_durations, backend_properties=backend_properties, timing_constraints=timing_constraints, + inst_map=inst_map, initial_layout=initial_layout, layout_method=layout_method, routing_method=routing_method, @@ -414,14 +371,15 @@ def callback_func(**kwargs): hls_config=hls_config, init_method=init_method, optimization_method=optimization_method, - _skip_target=_skip_target, + dt=dt, ) + out_circuits = pm.run(circuits, callback=callback, num_processes=num_processes) + for name, circ in zip(output_name, out_circuits): circ.name = name end_time = time() _log_transpile_time(start_time, end_time) - if arg_circuits_list: return out_circuits else: @@ -451,31 +409,20 @@ def _log_transpile_time(start_time, end_time): logger.info(log_msg) -def _parse_inst_map(inst_map, backend): - # try getting inst_map from user, else backend - if inst_map is None and backend is not None: - inst_map = backend.target.instruction_schedule_map() - return inst_map - - -def _parse_coupling_map(coupling_map, backend): - # try getting coupling_map from user, else backend - if coupling_map is None and backend is not None: - coupling_map = backend.coupling_map - +def _parse_coupling_map(coupling_map): # coupling_map could be None, or a list of lists, e.g. [[0, 1], [2, 1]] - if coupling_map is None or isinstance(coupling_map, CouplingMap): - return coupling_map if isinstance(coupling_map, list) and all( isinstance(i, list) and len(i) == 2 for i in coupling_map ): return CouplingMap(coupling_map) - else: + elif isinstance(coupling_map, list): raise TranspilerError( "Only a single input coupling map can be used with transpile() if you need to " "target different coupling maps for different circuits you must call transpile() " "multiple times" ) + else: + return coupling_map def _parse_initial_layout(initial_layout): @@ -491,22 +438,6 @@ def _parse_initial_layout(initial_layout): return initial_layout -def _parse_instruction_durations(backend, inst_durations, dt): - """Create a list of ``InstructionDuration``s. If ``inst_durations`` is provided, - the backend will be ignored, otherwise, the durations will be populated from the - backend. - """ - final_durations = InstructionDurations() - if not inst_durations: - backend_durations = InstructionDurations() - if backend is not None: - backend_durations = backend.instruction_durations - final_durations.update(backend_durations, dt or backend_durations.dt) - else: - final_durations.update(inst_durations, dt or getattr(inst_durations, "dt", None)) - return final_durations - - def _parse_approximation_degree(approximation_degree): if approximation_degree is None: return None @@ -549,13 +480,3 @@ def _parse_output_name(output_name, circuits): ) else: return [circuit.name for circuit in circuits] - - -def _parse_timing_constraints(backend, timing_constraints): - if isinstance(timing_constraints, TimingConstraints): - return timing_constraints - if backend is None and timing_constraints is None: - timing_constraints = TimingConstraints() - elif backend is not None: - timing_constraints = backend.target.timing_constraints() - return timing_constraints diff --git a/qiskit/transpiler/preset_passmanagers/__init__.py b/qiskit/transpiler/preset_passmanagers/__init__.py index 8d653ed3a1a9..37b284a4680f 100644 --- a/qiskit/transpiler/preset_passmanagers/__init__.py +++ b/qiskit/transpiler/preset_passmanagers/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2019. +# (C) Copyright IBM 2017, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -21,7 +21,8 @@ for the transpiler. The preset pass managers are instances of :class:`~.StagedPassManager` which are used to execute the circuit transformations as part of Qiskit's compiler inside the -:func:`~.transpile` function at the different optimization levels. +:func:`~.transpile` function at the different optimization levels, but +can also be used in a standalone manner. The functionality here is divided into two parts, the first includes the functions used generate the entire pass manager which is used by :func:`~.transpile` (:ref:`preset_pass_manager_generators`) and the @@ -56,13 +57,21 @@ .. autofunction:: generate_scheduling .. currentmodule:: qiskit.transpiler.preset_passmanagers """ +import copy -import warnings +from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES +from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping +from qiskit.providers.backend_compat import BackendV2Converter + +from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passmanager_config import PassManagerConfig -from qiskit.transpiler.target import target_to_backend_properties +from qiskit.transpiler.target import Target, target_to_backend_properties from qiskit.transpiler import CouplingMap +from qiskit.transpiler.exceptions import TranspilerError + from .level0 import level_0_pass_manager from .level1 import level_1_pass_manager from .level2 import level_2_pass_manager @@ -91,16 +100,43 @@ def generate_preset_pass_manager( hls_config=None, init_method=None, optimization_method=None, + dt=None, *, _skip_target=False, ): """Generate a preset :class:`~.PassManager` - This function is used to quickly generate a preset pass manager. A preset pass - manager are the default pass managers used by the :func:`~.transpile` + This function is used to quickly generate a preset pass manager. Preset pass + managers are the default pass managers used by the :func:`~.transpile` function. This function provides a convenient and simple method to construct a standalone :class:`~.PassManager` object that mirrors what the transpile + function internally builds and uses. + + The target constraints for the pass manager construction can be specified through a :class:`.Target` + instance, a `.BackendV1` or `.BackendV2` instance, or via loose constraints (``basis_gates``, + ``inst_map``, ``coupling_map``, ``backend_properties``, ``instruction_durations``, + ``dt`` or ``timing_constraints``). + The order of priorities for target constraints works as follows: if a ``target`` + input is provided, it will take priority over any ``backend`` input or loose constraints. + If a ``backend`` is provided together with any loose constraint + from the list above, the loose constraint will take priority over the corresponding backend + constraint. This behavior is independent of whether the ``backend`` instance is of type + :class:`.BackendV1` or :class:`.BackendV2`, as summarized in the table below. The first column + in the table summarizes the potential user-provided constraints, and each cell shows whether + the priority is assigned to that specific constraint input or another input + (`target`/`backend(V1)`/`backend(V2)`). + ============================ ========= ======================== ======================= + User Provided target backend(V1) backend(V2) + ============================ ========= ======================== ======================= + **basis_gates** target basis_gates basis_gates + **coupling_map** target coupling_map coupling_map + **instruction_durations** target instruction_durations instruction_durations + **inst_map** target inst_map inst_map + **dt** target dt dt + **timing_constraints** target timing_constraints timing_constraints + **backend_properties** target backend_properties backend_properties + ============================ ========= ======================== ======================= Args: optimization_level (int): The optimization level to generate a @@ -126,16 +162,57 @@ def generate_preset_pass_manager( and ``backend_properties``. basis_gates (list): List of basis gate names to unroll to (e.g: ``['u1', 'u2', 'u3', 'cx']``). - inst_map (InstructionScheduleMap): Mapping object that maps gate to schedules. + inst_map (InstructionScheduleMap): Mapping object that maps gates to schedules. If any user defined calibration is found in the map and this is used in a circuit, transpiler attaches the custom gate definition to the circuit. This enables one to flexibly override the low-level instruction implementation. coupling_map (CouplingMap or list): Directed graph represented a coupling - map. - instruction_durations (InstructionDurations): Dictionary of duration - (in dt) for each instruction. + map. Multiple formats are supported: + + #. ``CouplingMap`` instance + #. List, must be given as an adjacency matrix, where each entry + specifies all directed two-qubit interactions supported by backend, + e.g: ``[[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]`` + + instruction_durations (InstructionDurations or list): Dictionary of duration + (in dt) for each instruction. If specified, these durations overwrite the + gate lengths in ``backend.properties``. Applicable only if ``scheduling_method`` + is specified. + The format of ``instruction_durations`` must be as follows: + They must be given as an :class:`.InstructionDurations` instance or a list of tuples + + ``` + [(instruction_name, qubits, duration, unit), ...]. + | [('cx', [0, 1], 12.3, 'ns'), ('u3', [0], 4.56, 'ns')] + | [('cx', [0, 1], 1000), ('u3', [0], 300)] + ``` + + If ``unit`` is omitted, the default is ``'dt'``, which is a sample time depending on backend. + If the time unit is ``'dt'``, the duration must be an integer. + dt (float): Backend sample time (resolution) in seconds. + If provided, this value will overwrite the ``dt`` value in ``instruction_durations``. + If ``None`` (default) and a backend is provided, ``backend.dt`` is used. timing_constraints (TimingConstraints): Hardware time alignment restrictions. + A quantum computer backend may report a set of restrictions, namely: + + - granularity: An integer value representing minimum pulse gate + resolution in units of ``dt``. A user-defined pulse gate should have + duration of a multiple of this granularity value. + - min_length: An integer value representing minimum pulse gate + length in units of ``dt``. A user-defined pulse gate should be longer + than this length. + - pulse_alignment: An integer value representing a time resolution of gate + instruction starting time. Gate instruction should start at time which + is a multiple of the alignment value. + - acquire_alignment: An integer value representing a time resolution of measure + instruction starting time. Measure instruction should start at time which + is a multiple of the alignment value. + + This information will be provided by the backend configuration. + If the backend doesn't have any restriction on the instruction time allocation, + then ``timing_constraints`` is None and no adjustment will be performed. + initial_layout (Layout | List[int]): Initial position of virtual qubits on physical qubits. layout_method (str): The :class:`~.Pass` to use for choosing initial qubit @@ -205,8 +282,74 @@ def generate_preset_pass_manager( ValueError: if an invalid value for ``optimization_level`` is passed in. """ - if coupling_map is not None and not isinstance(coupling_map, CouplingMap): - coupling_map = CouplingMap(coupling_map) + if backend is not None and getattr(backend, "version", 0) <= 1: + # This is a temporary conversion step to allow for a smoother transition + # to a fully target-based transpiler pipeline while maintaining the behavior + # of `transpile` with BackendV1 inputs. + backend = BackendV2Converter(backend) + + # Check if a custom inst_map was specified before overwriting inst_map + _given_inst_map = bool(inst_map) + # If there are no loose constraints => use backend target if available + _no_loose_constraints = ( + basis_gates is None + and coupling_map is None + and dt is None + and instruction_durations is None + and backend_properties is None + and timing_constraints is None + ) + # If it's an edge case => do not build target + _skip_target = ( + target is None + and backend is None + and (basis_gates is None or coupling_map is None or instruction_durations is not None) + ) + + # Resolve loose constraints case-by-case against backend constraints. + # The order of priority is loose constraints > backend. + dt = _parse_dt(dt, backend) + instruction_durations = _parse_instruction_durations(backend, instruction_durations, dt) + timing_constraints = _parse_timing_constraints(backend, timing_constraints) + inst_map = _parse_inst_map(inst_map, backend) + # The basis gates parser will set _skip_target to True if a custom basis gate is found + # (known edge case). + basis_gates, name_mapping, _skip_target = _parse_basis_gates( + basis_gates, backend, inst_map, _skip_target + ) + coupling_map = _parse_coupling_map(coupling_map, backend) + + if target is None: + if backend is not None and _no_loose_constraints: + # If a backend is specified without loose constraints, use its target directly. + target = backend.target + elif not _skip_target: + # Only parse backend properties when the target isn't skipped to + # preserve the former behavior of transpile. + backend_properties = _parse_backend_properties(backend_properties, backend) + # Build target from constraints. + target = Target.from_configuration( + basis_gates=basis_gates, + num_qubits=backend.num_qubits if backend is not None else None, + coupling_map=coupling_map, + # If the instruction map has custom gates, do not give as config, the information + # will be added to the target with update_from_instruction_schedule_map + inst_map=inst_map if inst_map and not inst_map.has_custom_gate() else None, + backend_properties=backend_properties, + instruction_durations=instruction_durations, + concurrent_measurements=( + backend.target.concurrent_measurements if backend is not None else None + ), + dt=dt, + timing_constraints=timing_constraints, + custom_name_mapping=name_mapping, + ) + + # Update target with custom gate information. Note that this is an exception to the priority + # order (target > loose constraints), added to handle custom gates for scheduling passes. + if target is not None and _given_inst_map and inst_map.has_custom_gate(): + target = copy.deepcopy(target) + target.update_from_instruction_schedule_map(inst_map) if target is not None: if coupling_map is None: @@ -262,6 +405,119 @@ def generate_preset_pass_manager( return pm +def _parse_basis_gates(basis_gates, backend, inst_map, skip_target): + name_mapping = {} + standard_gates = get_standard_gate_name_mapping() + # Add control flow gates by default to basis set + default_gates = {"measure", "delay", "reset"}.union(CONTROL_FLOW_OP_NAMES) + + try: + instructions = set(basis_gates) + for name in default_gates: + if name not in instructions: + instructions.add(name) + except TypeError: + instructions = None + + if backend is None: + # Check for custom instructions + if instructions is None: + return None, name_mapping, skip_target + + for inst in instructions: + if inst not in standard_gates or inst not in default_gates: + skip_target = True + break + + return list(instructions), name_mapping, skip_target + + instructions = instructions or backend.operation_names + name_mapping.update( + {name: backend.target.operation_from_name(name) for name in backend.operation_names} + ) + + # Check for custom instructions before removing calibrations + for inst in instructions: + if inst not in standard_gates or inst not in default_gates: + skip_target = True + break + + # Remove calibrated instructions, as they will be added later from the instruction schedule map + if inst_map is not None and not skip_target: + for inst in inst_map.instructions: + for qubit in inst_map.qubits_with_instruction(inst): + entry = inst_map._get_calibration_entry(inst, qubit) + if entry.user_provided and inst in instructions: + instructions.remove(inst) + + return list(instructions) if instructions else None, name_mapping, skip_target + + +def _parse_inst_map(inst_map, backend): + # try getting inst_map from user, else backend + if inst_map is None and backend is not None: + inst_map = backend.target.instruction_schedule_map() + return inst_map + + +def _parse_backend_properties(backend_properties, backend): + # try getting backend_props from user, else backend + if backend_properties is None and backend is not None: + backend_properties = target_to_backend_properties(backend.target) + return backend_properties + + +def _parse_dt(dt, backend): + # try getting dt from user, else backend + if dt is None and backend is not None: + dt = backend.target.dt + return dt + + +def _parse_coupling_map(coupling_map, backend): + # try getting coupling_map from user, else backend + if coupling_map is None and backend is not None: + coupling_map = backend.coupling_map + + # coupling_map could be None, or a list of lists, e.g. [[0, 1], [2, 1]] + if coupling_map is None or isinstance(coupling_map, CouplingMap): + return coupling_map + if isinstance(coupling_map, list) and all( + isinstance(i, list) and len(i) == 2 for i in coupling_map + ): + return CouplingMap(coupling_map) + else: + raise TranspilerError( + "Only a single input coupling map can be used with generate_preset_pass_manager()." + ) + + +def _parse_instruction_durations(backend, inst_durations, dt): + """Create a list of ``InstructionDuration``s. If ``inst_durations`` is provided, + the backend will be ignored, otherwise, the durations will be populated from the + backend. + """ + final_durations = InstructionDurations() + if not inst_durations: + backend_durations = InstructionDurations() + if backend is not None: + backend_durations = backend.instruction_durations + final_durations.update(backend_durations, dt or backend_durations.dt) + else: + final_durations.update(inst_durations, dt or getattr(inst_durations, "dt", None)) + return final_durations + + +def _parse_timing_constraints(backend, timing_constraints): + if isinstance(timing_constraints, TimingConstraints): + return timing_constraints + if backend is None and timing_constraints is None: + timing_constraints = TimingConstraints() + elif backend is not None: + timing_constraints = backend.target.timing_constraints() + return timing_constraints + + __all__ = [ "level_0_pass_manager", "level_1_pass_manager", diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 5af9e8686583..f7b5227c0266 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -426,7 +426,9 @@ def add_instruction(self, instruction, properties=None, name=None): f"of qubits in the properties dictionary: {qarg}" ) if qarg is not None: - self.num_qubits = max(self.num_qubits, max(qarg) + 1) + self.num_qubits = max( + self.num_qubits if self.num_qubits is not None else 0, max(qarg) + 1 + ) qargs_val[qarg] = properties[qarg] self._qarg_gate_map[qarg].add(instruction_name) self._gate_map[instruction_name] = qargs_val @@ -987,7 +989,8 @@ def instruction_properties(self, index): def _build_coupling_graph(self): self._coupling_graph = rx.PyDiGraph(multigraph=False) - self._coupling_graph.add_nodes_from([{} for _ in range(self.num_qubits)]) + if self.num_qubits is not None: + self._coupling_graph.add_nodes_from([{} for _ in range(self.num_qubits)]) for gate, qarg_map in self._gate_map.items(): if qarg_map is None: if self._gate_name_map[gate].num_qubits == 2: diff --git a/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml b/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml new file mode 100644 index 000000000000..4857bb1bda12 --- /dev/null +++ b/releasenotes/notes/use-target-in-generate-preset-pm-5215e00d22d0205c.yaml @@ -0,0 +1,14 @@ +--- +features_transpiler: + - | + A new ``dt`` argument has been added to :func:`.generate_preset_pass_manager` to match + the set of arguments of :func:`.transpile`. This will allow for the internal conversion + of transpilation constraints to a :class:`.Target` representation. + +upgrade_transpiler: + - | + The :func:`.generate_preset_pass_manager` function has been upgraded to, when possible, + internally convert transpiler constraints into a :class:`.Target` instance. + If a `backend` input of type :class:`.BackendV1` is provided, it will be + converted to :class:`.BackendV2` to expose its :class:`.Target`. This change does + not require any user action. diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 6166528f8868..30ba83440c9e 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1829,7 +1829,7 @@ def test_synthesis_translation_method_with_single_qubit_gates(self, optimization @data(0, 1, 2, 3) def test_synthesis_translation_method_with_gates_outside_basis(self, optimization_level): - """Test that synthesis translation works for circuits with single gates outside bassis""" + """Test that synthesis translation works for circuits with single gates outside basis""" qc = QuantumCircuit(2) qc.swap(0, 1) res = transpile( @@ -2781,12 +2781,14 @@ def test_backend_and_custom_gate(self, opt_level): backend = GenericBackendV2( num_qubits=5, coupling_map=[[0, 1], [1, 0], [1, 2], [1, 3], [2, 1], [3, 1], [3, 4], [4, 3]], + seed=42, ) inst_map = InstructionScheduleMap() inst_map.add("newgate", [0, 1], pulse.ScheduleBlock()) newgate = Gate("newgate", 2, []) circ = QuantumCircuit(2) circ.append(newgate, [0, 1]) + tqc = transpile( circ, backend, @@ -2797,8 +2799,8 @@ def test_backend_and_custom_gate(self, opt_level): ) self.assertEqual(len(tqc.data), 1) self.assertEqual(tqc.data[0].operation, newgate) - qubits = tuple(tqc.find_bit(x).index for x in tqc.data[0].qubits) - self.assertIn(qubits, backend.target.qargs) + for x in tqc.data[0].qubits: + self.assertIn((tqc.find_bit(x).index,), backend.target.qargs) @ddt diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index 5315c4b8e018..fbe4e1fbf74c 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -1329,9 +1329,9 @@ def setUpClass(cls): super().setUpClass() cls.backend = Fake27QPulseV1() cls.backend.configuration().coupling_map = MUMBAI_CMAP + cls.backend.configuration().basis_gates += ["for_loop", "while_loop", "if_else"] cls.coupling_edge_set = {tuple(x) for x in cls.backend.configuration().coupling_map} cls.basis_gates = set(cls.backend.configuration().basis_gates) - cls.basis_gates.update(["for_loop", "while_loop", "if_else"]) def assert_valid_circuit(self, transpiled): """Assert circuit complies with constraints of backend.""" diff --git a/test/python/transpiler/test_stochastic_swap.py b/test/python/transpiler/test_stochastic_swap.py index 5b924e590a54..8c96150ae8ff 100644 --- a/test/python/transpiler/test_stochastic_swap.py +++ b/test/python/transpiler/test_stochastic_swap.py @@ -1489,9 +1489,9 @@ class TestStochasticSwapRandomCircuitValidOutput(QiskitTestCase): def setUpClass(cls): super().setUpClass() cls.backend = Fake27QPulseV1() + cls.backend.configuration().basis_gates += ["for_loop", "while_loop", "if_else"] cls.coupling_edge_set = {tuple(x) for x in cls.backend.configuration().coupling_map} cls.basis_gates = set(cls.backend.configuration().basis_gates) - cls.basis_gates.update(["for_loop", "while_loop", "if_else"]) def assert_valid_circuit(self, transpiled): """Assert circuit complies with constraints of backend."""