From 4084f872151504cf1662d567e398af716eb67f36 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 14 Sep 2022 15:55:28 -0400 Subject: [PATCH 01/20] Add BackendV2Convert class for treating BackendV1 as BackendV2 This commit adds a new class BackendV2Converter which is a BackendV2 implementation that converts an input BackendV1 object into a BackendV2 implementation. This is useful for users that are supporting working with arbitrary providers so that they can standardize on using the newest access patterns even if a provider is still using BackendV1. Similarly, for qiskit's internal usage, this gives us a path to move all of the transpiler internals to use Target and avoid carrying around duplicate information in the PassManagerConfig for passes that haven't been updated. This will enable us to convert input BackendV1 instances to a target once on initial calling and have the transpiler only ever see a target. Fixes #8611 --- qiskit/providers/__init__.py | 4 + qiskit/providers/backend_compat.py | 250 ++++++++++++++++++ .../backend-converter-05360f12f9042829.yaml | 15 ++ test/python/providers/test_fake_backends.py | 33 ++- 4 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 qiskit/providers/backend_compat.py create mode 100644 releasenotes/notes/backend-converter-05360f12f9042829.yaml diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index e83b145b2f5a..cbdc8dbf482c 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -94,6 +94,8 @@ BackendV1 BackendV2 QubitProperties + BackendV2Converter + convert_to_target Options ------- @@ -677,6 +679,8 @@ def status(self): from qiskit.providers.backend import BackendV1 from qiskit.providers.backend import BackendV2 from qiskit.providers.backend import QubitProperties +from qiskit.providers.backend_compat import BackendV2Converter +from qiskit.providers.backend_compat import convert_to_target from qiskit.providers.options import Options from qiskit.providers.job import Job from qiskit.providers.job import JobV1 diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py new file mode 100644 index 000000000000..1096d0429a4c --- /dev/null +++ b/qiskit/providers/backend_compat.py @@ -0,0 +1,250 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# 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 +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=invalid-name + +"""Backend abstract interface for providers.""" + + +from typing import List, Union, Iterable, Any, Dict + +from qiskit.providers.backend import BackendV1, BackendV2 +from qiskit.transpiler.target import Target, InstructionProperties +from qiskit.providers.backend import QubitProperties +from qiskit.utils.units import apply_prefix +from qiskit.circuit.library.standard_gates import IGate, SXGate, XGate, CXGate, RZGate +from qiskit.circuit.parameter import Parameter +from qiskit.circuit.gate import Gate +from qiskit.circuit.delay import Delay +from qiskit.circuit.measure import Measure +from qiskit.circuit.reset import Reset +from qiskit.providers.models.backendconfiguration import BackendConfiguration +from qiskit.providers.models.backendproperties import BackendProperties +from qiskit.providers.models.pulsedefaults import PulseDefaults +from qiskit.providers.exceptions import BackendPropertyError + + +def convert_to_target( + configuration: BackendConfiguration, + properties: BackendProperties = None, + defaults: PulseDefaults = None, +) -> Target: + """Uses configuration, properties and pulse defaults + to construct and return Target class. + """ + name_mapping = { + "id": IGate(), + "sx": SXGate(), + "x": XGate(), + "cx": CXGate(), + "rz": RZGate(Parameter("λ")), + "reset": Reset(), + } + custom_gates = {} + target = None + # Parse from properties if it exsits + if properties is not None: + qubit_properties = qubit_props_list_from_props(properties=properties) + target = Target(num_qubits=configuration.n_qubits, qubit_properties=qubit_properties) + # Parse instructions + gates: Dict[str, Any] = {} + for gate in properties.gates: + name = gate.gate + if name in name_mapping: + if name not in gates: + gates[name] = {} + elif name not in custom_gates: + custom_gate = Gate(name, len(gate.qubits), []) + custom_gates[name] = custom_gate + gates[name] = {} + + qubits = tuple(gate.qubits) + gate_props = {} + for param in gate.parameters: + if param.name == "gate_error": + gate_props["error"] = param.value + if param.name == "gate_length": + gate_props["duration"] = apply_prefix(param.value, param.unit) + gates[name][qubits] = InstructionProperties(**gate_props) + for gate, props in gates.items(): + if gate in name_mapping: + inst = name_mapping.get(gate) + else: + inst = custom_gates[gate] + target.add_instruction(inst, props) + # Create measurement instructions: + measure_props = {} + for qubit, _ in enumerate(properties.qubits): + measure_props[(qubit,)] = InstructionProperties( + duration=properties.readout_length(qubit), + error=properties.readout_error(qubit), + ) + target.add_instruction(Measure(), measure_props) + # Parse from configuration because properties doesn't exist + else: + target = Target(num_qubits=configuration.n_qubits) + for gate in configuration.gates: + name = gate.name + gate_props = ( + {tuple(x): None for x in gate.coupling_map} # type: ignore[misc] + if hasattr(gate, "coupling_map") + else {None: None} + ) + gate_len = len(gate.coupling_map[0]) if hasattr(gate, "coupling_map") else 0 + if name in name_mapping: + target.add_instruction(name_mapping[name], gate_props) + else: + custom_gate = Gate(name, gate_len, []) + target.add_instruction(custom_gate, gate_props) + target.add_instruction(Measure()) + # parse global configuration properties + if hasattr(configuration, "dt"): + target.dt = configuration.dt + if hasattr(configuration, "timing_constraints"): + target.granularity = configuration.timing_constraints.get("granularity") + target.min_length = configuration.timing_constraints.get("min_length") + target.pulse_alignment = configuration.timing_constraints.get("pulse_alignment") + target.aquire_alignment = configuration.timing_constraints.get("acquire_alignment") + # If a pulse defaults exists use that as the source of truth + if defaults is not None: + inst_map = defaults.instruction_schedule_map + for inst in inst_map.instructions: + for qarg in inst_map.qubits_with_instruction(inst): + sched = inst_map.get(inst, qarg) + if inst in target: + try: + qarg = tuple(qarg) + except TypeError: + qarg = (qarg,) + if inst == "measure": + for qubit in qarg: + target[inst][(qubit,)].calibration = sched + elif qarg in target[inst]: + target[inst][qarg].calibration = sched + if "delay" not in target: + target.add_instruction( + Delay(Parameter("t")), {(bit,): None for bit in range(target.num_qubits)} + ) + return target + + +def qubit_props_list_from_props( + properties: BackendProperties, +) -> List[QubitProperties]: + """Uses BackendProperties to construct + and return a list of IBMQubitProperties. + """ + qubit_props: List[QubitProperties] = [] + for qubit, _ in enumerate(properties.qubits): + try: + t_1 = properties.t1(qubit) + except BackendPropertyError: + t_1 = None + try: + t_2 = properties.t2(qubit) + except BackendPropertyError: + t_2 = None + try: + frequency = properties.frequency(qubit) + except BackendPropertyError: + t_2 = None + qubit_props.append( + QubitProperties( # type: ignore[no-untyped-call] + t1=t_1, + t2=t_2, + frequency=frequency, + ) + ) + return qubit_props + + +class BackendV2Converter(BackendV2): + """A converter class that takes a BackendV1 instance and wraps it in a BackendV2 interface + + This can be used to provide a consistent modern interface when a + + """ + + def __init__( + self, + backend: BackendV1, + ): + """Initialize a BackendV2 based backend + + Args: + backend: The input :class:`~.BackendV1` based backend to wrap in a + :class:`~.BackendV2` interface + """ + self._backend = backend + self._config = self._backend.configuration() + super().__init__( + provider=backend.provider, + name=backend.name(), + description=self._config.description, + online_date=self._config.online_date, + backend_version=self._config.backend_version, + ) + self._options = self._backend._options + self._properties = None + if hasattr(self._backend, "properties"): + self._properties = self._backend.properties() + self._defaults = None + self._target = None + + @property + def target(self): + """A :class:`qiskit.transpiler.Target` object for the backend. + + :rtype: Target + """ + if self._target is None: + if self._defaults is None and hasattr(self._backend, "defaults"): + self._defaults = self._backend.defaults() + if self._properties is None and hasattr(self._backend, "properties"): + self._properties = self._backend.properties() + self._target = convert_to_target(self._config, self._properties, self._defaults) + return self._target + + @property + def max_circuits(self): + return self._config.max_experiments + + @classmethod + def _default_options(cls): + return None + + @property + def dt(self) -> Union[float, None]: + return self.target.dt + + @property + def dtm(self) -> float: + return self._config.dtm + + @property + def meas_map(self) -> List[List[int]]: + return self._config.dt + + def drive_channel(self, qubit: int): + self._config.drive(qubit) + + def measure_channel(self, qubit: int): + self._config.measure(qubit) + + def acquire_channel(self, qubit: int): + self._config.acquire(qubit) + + def control_channel(self, qubits: Iterable[int]): + self._config.control(qubits) + + def run(self, run_input, **options): + return self._backend.run(run_input, **options) diff --git a/releasenotes/notes/backend-converter-05360f12f9042829.yaml b/releasenotes/notes/backend-converter-05360f12f9042829.yaml new file mode 100644 index 000000000000..97695b55d084 --- /dev/null +++ b/releasenotes/notes/backend-converter-05360f12f9042829.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + Added a new class, :class:`~BackendV2Converter`, which is used to wrap + a :class:`~.BackendV1` instance in a :class:`~.BackendV2` interface. It + enables you to have a :class:`~.BackendV2` instance from any + :class:`~.BackendV1`. This enables standardizing access patterns on the + newer :class:`~.BackendV2` interface even if you still support + :class:`~.BackendV1`. + + - | + Added a new function :func:`~.convert_to_target` which is used to take + a :class:`~.BackendConfiguration`, and optionally a + :class:`~.BackendProperties` and :class:`~.PulseDefaults` and create + a :class:`~.Target` object equivalent to the contents of those objects. diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index 79dfc27121fd..6d7fb3871bbf 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -24,7 +24,14 @@ from qiskit.exceptions import QiskitError from qiskit.execute_function import execute from qiskit.test.base import QiskitTestCase -from qiskit.providers.fake_provider import FakeProviderForBackendV2, FakeProvider, FakeMumbaiV2 +from qiskit.providers.fake_provider import ( + FakeProviderForBackendV2, + FakeProvider, + FakeMumbaiV2, + FakeYorktown, +) +from qiskit.providers.backend_compat import BackendV2Converter +from qiskit.providers.backend import BackendV2 from qiskit.utils import optionals FAKE_PROVIDER_FOR_BACKEND_V2 = FakeProviderForBackendV2() @@ -161,3 +168,27 @@ def test_delay_circuit(self): qc.measure_all() res = transpile(qc, backend) self.assertIn("delay", res.count_ops()) + + @data(0, 1, 2, 3) + def test_converter(self, opt_level): + backend = FakeYorktown() + backend_v2 = BackendV2Converter(backend) + self.assertIsInstance(backend_v2, BackendV2) + res = transpile(self.circuit, backend_v2, optimization_level=opt_level) + job = backend_v2.run(res) + result = job.result() + counts = result.get_counts() + max_count = max(counts.items(), key=operator.itemgetter(1))[0] + self.assertEqual(max_count, "11") + + def test_converter_delay_circuit(self): + backend = FakeYorktown() + backend_v2 = BackendV2Converter(backend) + self.assertIsInstance(backend_v2, BackendV2) + qc = QuantumCircuit(2) + qc.delay(502, 0, unit="ns") + qc.x(1) + qc.delay(250, 1, unit="ns") + qc.measure_all() + res = transpile(qc, backend_v2) + self.assertIn("delay", res.count_ops()) From e724e296dd4f8143c278874c3109cdebcd19729d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 14 Sep 2022 17:01:10 -0400 Subject: [PATCH 02/20] Update docstring --- qiskit/providers/backend_compat.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 1096d0429a4c..b31d2143c6d0 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -168,17 +168,20 @@ def qubit_props_list_from_props( class BackendV2Converter(BackendV2): - """A converter class that takes a BackendV1 instance and wraps it in a BackendV2 interface - - This can be used to provide a consistent modern interface when a + """A converter class that takes a :class:`~.BackendV1` instance and wraps it in a + :class:`~.BackendV2` interface. + This class implements the :class:`~.BackendV2` interface and is used to enable + common access patterns between :class:`~.BackendV1` and :class:`~.BackendV2`. This + class should only be used if you need a :class:`~.BackendV2` and still need + compatibility with :class:`~.BackendV1`. """ def __init__( self, backend: BackendV1, ): - """Initialize a BackendV2 based backend + """Initialize a BackendV2 converter instance based on a BackendV1 instance. Args: backend: The input :class:`~.BackendV1` based backend to wrap in a @@ -222,10 +225,6 @@ def max_circuits(self): def _default_options(cls): return None - @property - def dt(self) -> Union[float, None]: - return self.target.dt - @property def dtm(self) -> float: return self._config.dtm From 9e8396e9d52592f99118c5290680cdbaac69278c Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 20 Sep 2022 17:47:13 -0400 Subject: [PATCH 03/20] Return empty options for _default_options --- qiskit/providers/backend_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index b31d2143c6d0..6fec78a8507d 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -223,7 +223,7 @@ def max_circuits(self): @classmethod def _default_options(cls): - return None + return Options() @property def dtm(self) -> float: From 2c8caa970f4aca3967b3cf22731b1ee04e0c8494 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 20 Sep 2022 17:47:37 -0400 Subject: [PATCH 04/20] Remove leftover pylint disable --- qiskit/providers/backend_compat.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 6fec78a8507d..43a5246d7688 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -10,8 +10,6 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=invalid-name - """Backend abstract interface for providers.""" From c30bdd048cb3252019a9b8f4979df42635c77a71 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 20 Sep 2022 18:17:38 -0400 Subject: [PATCH 05/20] Expand standard gate conversions and handle missing basis_gates --- qiskit/providers/backend_compat.py | 66 ++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 43a5246d7688..512e90913eac 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -10,16 +10,17 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Backend abstract interface for providers.""" +# pylint: disable=no-value-for-parameter,unused-wildcard-import,wildcard-import +"""Backend abstract interface for providers.""" -from typing import List, Union, Iterable, Any, Dict +from typing import List, Iterable, Any, Dict from qiskit.providers.backend import BackendV1, BackendV2 from qiskit.transpiler.target import Target, InstructionProperties from qiskit.providers.backend import QubitProperties from qiskit.utils.units import apply_prefix -from qiskit.circuit.library.standard_gates import IGate, SXGate, XGate, CXGate, RZGate +from qiskit.circuit.library.standard_gates import * from qiskit.circuit.parameter import Parameter from qiskit.circuit.gate import Gate from qiskit.circuit.delay import Delay @@ -28,6 +29,7 @@ from qiskit.providers.models.backendconfiguration import BackendConfiguration from qiskit.providers.models.backendproperties import BackendProperties from qiskit.providers.models.pulsedefaults import PulseDefaults +from qiskit.providers.options import Options from qiskit.providers.exceptions import BackendPropertyError @@ -39,13 +41,61 @@ def convert_to_target( """Uses configuration, properties and pulse defaults to construct and return Target class. """ + # Standard gates library mapping, multicontrolled gates not included since they're + # variable width name_mapping = { "id": IGate(), "sx": SXGate(), "x": XGate(), "cx": CXGate(), "rz": RZGate(Parameter("λ")), + "r": RGate(Parameter("ϴ"), Parameter("φ")), "reset": Reset(), + "c3sx": C3SXGate(), + "ccx": CCXGate(), + "dcx": DCXGate(), + "ch": CHGate(), + "cp": CPhaseGate(Parameter("ϴ")), + "crx": CRXGate(Parameter("ϴ")), + "cry": CRYGate(Parameter("ϴ")), + "crz": CRZGate(Parameter("ϴ")), + "cswap": CSwapGate(), + "csx": CSXGate(), + "cu": CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "cu1": CU1Gate(Parameter("λ")), + "cu3": CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "cy": CYGate(), + "cz": CZGate(), + "ccz": CCZGate(), + "h": HGate(), + "p": PhaseGate(Parameter("ϴ")), + "rccx": RCCXGate(), + "rcccx": RC3XGate(), + "rx": RXGate(Parameter("ϴ")), + "rxx": RXXGate(Parameter("ϴ")), + "ry": RYGate(Parameter("ϴ")), + "ryy": RYYGate(Parameter("ϴ")), + "rzz": RZZGate(Parameter("ϴ")), + "rzx": RZXGate(Parameter("ϴ")), + "xx_minus_yy": XXMinusYYGate(Parameter("ϴ")), + "xx_plus_yy": XXPlusYYGate(Parameter("ϴ")), + "ecr": ECRGate(), + "s": SGate(), + "sdg": SdgGate(), + "cs": CSGate(), + "csdg": CSdgGate(), + "swap": SwapGate(), + "iswap": iSwapGate(), + "sxdg": SXdgGate(), + "t": TGate(), + "tdg": TdgGate(), + "u": UGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "u1": U1Gate(Parameter("λ")), + "u2": U2Gate(Parameter("φ"), Parameter("λ")), + "u3": U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "y": YGate(), + "z": ZGate(), + "delay": Delay(), } custom_gates = {} target = None @@ -128,10 +178,12 @@ def convert_to_target( target[inst][(qubit,)].calibration = sched elif qarg in target[inst]: target[inst][qarg].calibration = sched - if "delay" not in target: - target.add_instruction( - Delay(Parameter("t")), {(bit,): None for bit in range(target.num_qubits)} - ) + for op in configuration.basis_gates: + if op not in target and op in name_mapping: + target.add_instruction( + name_mapping[op], {(bit,): None for bit in range(target.num_qubits)} + ) + return target From 21d6c07eee6b527af3a4d590525eb39757e38328 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 20 Sep 2022 18:19:15 -0400 Subject: [PATCH 06/20] Fix copy paste error qubit_props_list_from_props() docstring --- qiskit/providers/backend_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 512e90913eac..ec163cb44911 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -191,7 +191,7 @@ def qubit_props_list_from_props( properties: BackendProperties, ) -> List[QubitProperties]: """Uses BackendProperties to construct - and return a list of IBMQubitProperties. + and return a list of QubitProperties. """ qubit_props: List[QubitProperties] = [] for qubit, _ in enumerate(properties.qubits): From 7e8fe264bbbc3d7742bf23e973867e7771fb8213 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 20 Sep 2022 18:31:18 -0400 Subject: [PATCH 07/20] Add name mapping argument to allow setting custom name-> gate mappings for conversion --- qiskit/providers/backend_compat.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index ec163cb44911..ada520bd5c85 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -14,7 +14,7 @@ """Backend abstract interface for providers.""" -from typing import List, Iterable, Any, Dict +from typing import List, Iterable, Any, Dict, Optional from qiskit.providers.backend import BackendV1, BackendV2 from qiskit.transpiler.target import Target, InstructionProperties @@ -37,6 +37,7 @@ def convert_to_target( configuration: BackendConfiguration, properties: BackendProperties = None, defaults: PulseDefaults = None, + custom_name_mapping: Optional[Dict[str, "Operation"]] = None, ) -> Target: """Uses configuration, properties and pulse defaults to construct and return Target class. @@ -99,6 +100,8 @@ def convert_to_target( } custom_gates = {} target = None + if custom_name_mapping is not None: + name_mapping.update(custom_name_mapping) # Parse from properties if it exsits if properties is not None: qubit_properties = qubit_props_list_from_props(properties=properties) @@ -230,12 +233,19 @@ class should only be used if you need a :class:`~.BackendV2` and still need def __init__( self, backend: BackendV1, + name_mapping: Optional[Dict[str, "Operation"]] = None, ): """Initialize a BackendV2 converter instance based on a BackendV1 instance. Args: backend: The input :class:`~.BackendV1` based backend to wrap in a :class:`~.BackendV2` interface + name_mapping: An optional dictionary that maps custom gate/operation names in + ``backend`` to an :class:`~.Operation` object representing that + gate/operation. By default most standard gates names are mapped to the + standard gate object from :mod:`qiskit.circuit.library` this only needs + to be specified if the input ``backend`` defines gates in names outside + that set. """ self._backend = backend self._config = self._backend.configuration() @@ -252,6 +262,7 @@ def __init__( self._properties = self._backend.properties() self._defaults = None self._target = None + self._name_mapping = name_mapping @property def target(self): @@ -264,7 +275,12 @@ def target(self): self._defaults = self._backend.defaults() if self._properties is None and hasattr(self._backend, "properties"): self._properties = self._backend.properties() - self._target = convert_to_target(self._config, self._properties, self._defaults) + self._target = convert_to_target( + self._config, + self._properties, + self._defaults, + custom_name_mapping=self._name_mapping, + ) return self._target @property From 4b3bf2e137d90d14ea666b9964e16b66c4990494 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 07:50:38 -0400 Subject: [PATCH 08/20] Add missing gamma parameter --- qiskit/providers/backend_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index ada520bd5c85..95626b86f276 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -62,7 +62,7 @@ def convert_to_target( "crz": CRZGate(Parameter("ϴ")), "cswap": CSwapGate(), "csx": CSXGate(), - "cu": CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "cu": CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ"), Parameter("γ")), "cu1": CU1Gate(Parameter("λ")), "cu3": CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), "cy": CYGate(), From d4cb323a8df92a2a83f64a8b201bd59fa7ad6bdb Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 08:57:00 -0400 Subject: [PATCH 09/20] Fix handling of global ops in configuration --- qiskit/providers/backend_compat.py | 9 +++++++-- test/python/providers/test_fake_backends.py | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 95626b86f276..8a628b2b5e46 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -96,7 +96,7 @@ def convert_to_target( "u3": U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), "y": YGate(), "z": ZGate(), - "delay": Delay(), + "delay": Delay(Parameter("t")), } custom_gates = {} target = None @@ -181,7 +181,12 @@ def convert_to_target( target[inst][(qubit,)].calibration = sched elif qarg in target[inst]: target[inst][qarg].calibration = sched - for op in configuration.basis_gates: + combined_global_ops = set() + if configuration.basis_gates: + combined_global_ops.update(configuration.basis_gates) + if configuration.supported_instructions: + combined_global_ops.update(configuration.supported_instructions) + for op in combined_global_ops: if op not in target and op in name_mapping: target.add_instruction( name_mapping[op], {(bit,): None for bit in range(target.num_qubits)} diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index 6d7fb3871bbf..7392994fce2e 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -29,6 +29,7 @@ FakeProvider, FakeMumbaiV2, FakeYorktown, + FakeMumbai, ) from qiskit.providers.backend_compat import BackendV2Converter from qiskit.providers.backend import BackendV2 @@ -182,7 +183,7 @@ def test_converter(self, opt_level): self.assertEqual(max_count, "11") def test_converter_delay_circuit(self): - backend = FakeYorktown() + backend = FakeMumbai() backend_v2 = BackendV2Converter(backend) self.assertIsInstance(backend_v2, BackendV2) qc = QuantumCircuit(2) From 86434cd9cf5cd7e95b0a14951f54996550bf1552 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 09:14:58 -0400 Subject: [PATCH 10/20] Raise exception on custom gates without mapping --- qiskit/providers/backend_compat.py | 39 +++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 8a628b2b5e46..d14b164fb715 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -16,6 +16,8 @@ from typing import List, Iterable, Any, Dict, Optional +from qiskit.exceptions import QiskitError + from qiskit.providers.backend import BackendV1, BackendV2 from qiskit.transpiler.target import Target, InstructionProperties from qiskit.providers.backend import QubitProperties @@ -32,6 +34,11 @@ from qiskit.providers.options import Options from qiskit.providers.exceptions import BackendPropertyError +# Some BackendConfiguration objects contain pulse only instructions in their +# supported_instructions field. Filter these out since they don't go in a +# TARGET +PULSE_INSTRUCTIONS = {"acquire", "shiftf", "setf", "play"} + def convert_to_target( configuration: BackendConfiguration, @@ -98,7 +105,6 @@ def convert_to_target( "z": ZGate(), "delay": Delay(Parameter("t")), } - custom_gates = {} target = None if custom_name_mapping is not None: name_mapping.update(custom_name_mapping) @@ -113,10 +119,11 @@ def convert_to_target( if name in name_mapping: if name not in gates: gates[name] = {} - elif name not in custom_gates: - custom_gate = Gate(name, len(gate.qubits), []) - custom_gates[name] = custom_gate - gates[name] = {} + else: + raise QiskitError( + f"Operation name {name} does not have a known mapping. Use " + "custom_name_mapping to map this name to an Operation object" + ) qubits = tuple(gate.qubits) gate_props = {} @@ -154,8 +161,10 @@ def convert_to_target( if name in name_mapping: target.add_instruction(name_mapping[name], gate_props) else: - custom_gate = Gate(name, gate_len, []) - target.add_instruction(custom_gate, gate_props) + raise QiskitError( + f"Operation name {name} does not have a known mapping. " + "Use custom_name_mapping to map this name to an Operation object" + ) target.add_instruction(Measure()) # parse global configuration properties if hasattr(configuration, "dt"): @@ -186,12 +195,18 @@ def convert_to_target( combined_global_ops.update(configuration.basis_gates) if configuration.supported_instructions: combined_global_ops.update(configuration.supported_instructions) + combined_global_ops -= PULSE_INSTRUCTIONS for op in combined_global_ops: - if op not in target and op in name_mapping: - target.add_instruction( - name_mapping[op], {(bit,): None for bit in range(target.num_qubits)} - ) - + if op not in target: + if op in name_mapping: + target.add_instruction( + name_mapping[op], {(bit,): None for bit in range(target.num_qubits)} + ) + else: + raise QiskitError( + f"Operation name '{op}' does not have a known mapping. Use " + "custom_name_mapping to map this name to an Operation object" + ) return target From f2803ad2bebfac1780ad5b6ee4563cc6a58ee79e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 09:29:55 -0400 Subject: [PATCH 11/20] Move name mapping to standard gates module --- .../library/standard_gates/__init__.py | 68 +++++++++++++++++++ qiskit/providers/backend_compat.py | 67 +----------------- 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/__init__.py b/qiskit/circuit/library/standard_gates/__init__.py index d1345e13d8f2..f6553979df6b 100644 --- a/qiskit/circuit/library/standard_gates/__init__.py +++ b/qiskit/circuit/library/standard_gates/__init__.py @@ -103,3 +103,71 @@ from .z import ZGate, CZGate, CCZGate from .multi_control_rotation_gates import mcrx, mcry, mcrz + + +def get_standard_gate_name_mapping(): + """Return a dictionary mapping the name of standard gates and instructions to an object for + that name.""" + from qiskit.circuit.parameter import Parameter + from qiskit.circuit.measure import Measure + from qiskit.circuit.delay import Delay + from qiskit.circuit.reset import Reset + + # Standard gates library mapping, multicontrolled gates not included since they're + # variable width + name_mapping = { + "id": IGate(), + "sx": SXGate(), + "x": XGate(), + "cx": CXGate(), + "rz": RZGate(Parameter("λ")), + "r": RGate(Parameter("ϴ"), Parameter("φ")), + "reset": Reset(), + "c3sx": C3SXGate(), + "ccx": CCXGate(), + "dcx": DCXGate(), + "ch": CHGate(), + "cp": CPhaseGate(Parameter("ϴ")), + "crx": CRXGate(Parameter("ϴ")), + "cry": CRYGate(Parameter("ϴ")), + "crz": CRZGate(Parameter("ϴ")), + "cswap": CSwapGate(), + "csx": CSXGate(), + "cu": CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ"), Parameter("γ")), + "cu1": CU1Gate(Parameter("λ")), + "cu3": CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "cy": CYGate(), + "cz": CZGate(), + "ccz": CCZGate(), + "h": HGate(), + "p": PhaseGate(Parameter("ϴ")), + "rccx": RCCXGate(), + "rcccx": RC3XGate(), + "rx": RXGate(Parameter("ϴ")), + "rxx": RXXGate(Parameter("ϴ")), + "ry": RYGate(Parameter("ϴ")), + "ryy": RYYGate(Parameter("ϴ")), + "rzz": RZZGate(Parameter("ϴ")), + "rzx": RZXGate(Parameter("ϴ")), + "xx_minus_yy": XXMinusYYGate(Parameter("ϴ")), + "xx_plus_yy": XXPlusYYGate(Parameter("ϴ")), + "ecr": ECRGate(), + "s": SGate(), + "sdg": SdgGate(), + "cs": CSGate(), + "csdg": CSdgGate(), + "swap": SwapGate(), + "iswap": iSwapGate(), + "sxdg": SXdgGate(), + "t": TGate(), + "tdg": TdgGate(), + "u": UGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "u1": U1Gate(Parameter("λ")), + "u2": U2Gate(Parameter("φ"), Parameter("λ")), + "u3": U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + "y": YGate(), + "z": ZGate(), + "delay": Delay(Parameter("t")), + "measure": Measure(), + } + return name_mapping diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index d14b164fb715..2c82a8b13d15 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -22,12 +22,8 @@ from qiskit.transpiler.target import Target, InstructionProperties from qiskit.providers.backend import QubitProperties from qiskit.utils.units import apply_prefix -from qiskit.circuit.library.standard_gates import * -from qiskit.circuit.parameter import Parameter -from qiskit.circuit.gate import Gate -from qiskit.circuit.delay import Delay +from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping from qiskit.circuit.measure import Measure -from qiskit.circuit.reset import Reset from qiskit.providers.models.backendconfiguration import BackendConfiguration from qiskit.providers.models.backendproperties import BackendProperties from qiskit.providers.models.pulsedefaults import PulseDefaults @@ -51,60 +47,7 @@ def convert_to_target( """ # Standard gates library mapping, multicontrolled gates not included since they're # variable width - name_mapping = { - "id": IGate(), - "sx": SXGate(), - "x": XGate(), - "cx": CXGate(), - "rz": RZGate(Parameter("λ")), - "r": RGate(Parameter("ϴ"), Parameter("φ")), - "reset": Reset(), - "c3sx": C3SXGate(), - "ccx": CCXGate(), - "dcx": DCXGate(), - "ch": CHGate(), - "cp": CPhaseGate(Parameter("ϴ")), - "crx": CRXGate(Parameter("ϴ")), - "cry": CRYGate(Parameter("ϴ")), - "crz": CRZGate(Parameter("ϴ")), - "cswap": CSwapGate(), - "csx": CSXGate(), - "cu": CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ"), Parameter("γ")), - "cu1": CU1Gate(Parameter("λ")), - "cu3": CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), - "cy": CYGate(), - "cz": CZGate(), - "ccz": CCZGate(), - "h": HGate(), - "p": PhaseGate(Parameter("ϴ")), - "rccx": RCCXGate(), - "rcccx": RC3XGate(), - "rx": RXGate(Parameter("ϴ")), - "rxx": RXXGate(Parameter("ϴ")), - "ry": RYGate(Parameter("ϴ")), - "ryy": RYYGate(Parameter("ϴ")), - "rzz": RZZGate(Parameter("ϴ")), - "rzx": RZXGate(Parameter("ϴ")), - "xx_minus_yy": XXMinusYYGate(Parameter("ϴ")), - "xx_plus_yy": XXPlusYYGate(Parameter("ϴ")), - "ecr": ECRGate(), - "s": SGate(), - "sdg": SdgGate(), - "cs": CSGate(), - "csdg": CSdgGate(), - "swap": SwapGate(), - "iswap": iSwapGate(), - "sxdg": SXdgGate(), - "t": TGate(), - "tdg": TdgGate(), - "u": UGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), - "u1": U1Gate(Parameter("λ")), - "u2": U2Gate(Parameter("φ"), Parameter("λ")), - "u3": U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), - "y": YGate(), - "z": ZGate(), - "delay": Delay(Parameter("t")), - } + name_mapping = get_standard_gate_name_mapping() target = None if custom_name_mapping is not None: name_mapping.update(custom_name_mapping) @@ -134,10 +77,7 @@ def convert_to_target( gate_props["duration"] = apply_prefix(param.value, param.unit) gates[name][qubits] = InstructionProperties(**gate_props) for gate, props in gates.items(): - if gate in name_mapping: - inst = name_mapping.get(gate) - else: - inst = custom_gates[gate] + inst = name_mapping[gate] target.add_instruction(inst, props) # Create measurement instructions: measure_props = {} @@ -157,7 +97,6 @@ def convert_to_target( if hasattr(gate, "coupling_map") else {None: None} ) - gate_len = len(gate.coupling_map[0]) if hasattr(gate, "coupling_map") else 0 if name in name_mapping: target.add_instruction(name_mapping[name], gate_props) else: From be7aeafb29c7ecc5e1ab36b49443c2f1caef4f7a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 10:30:21 -0400 Subject: [PATCH 12/20] Fix lint and docs --- qiskit/providers/backend_compat.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 2c82a8b13d15..c1fac2c94858 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -19,7 +19,6 @@ from qiskit.exceptions import QiskitError from qiskit.providers.backend import BackendV1, BackendV2 -from qiskit.transpiler.target import Target, InstructionProperties from qiskit.providers.backend import QubitProperties from qiskit.utils.units import apply_prefix from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping @@ -40,8 +39,10 @@ def convert_to_target( configuration: BackendConfiguration, properties: BackendProperties = None, defaults: PulseDefaults = None, - custom_name_mapping: Optional[Dict[str, "Operation"]] = None, + custom_name_mapping: Optional[Dict[str, Any]] = None, ) -> Target: + from qiskit.transpiler.target import Target, InstructionProperties # pylint: disable=cyclic-import + """Uses configuration, properties and pulse defaults to construct and return Target class. """ @@ -192,7 +193,7 @@ class should only be used if you need a :class:`~.BackendV2` and still need def __init__( self, backend: BackendV1, - name_mapping: Optional[Dict[str, "Operation"]] = None, + name_mapping: Optional[Dict[str, Any]] = None, ): """Initialize a BackendV2 converter instance based on a BackendV1 instance. From bffe0f30e72b1dce51dfe2ff40de18fa403ee52b Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 16:40:11 -0400 Subject: [PATCH 13/20] Use gate object name attribute to build name mapping Co-authored-by: Jake Lishman --- .../library/standard_gates/__init__.py | 111 +++++++++--------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/__init__.py b/qiskit/circuit/library/standard_gates/__init__.py index f6553979df6b..2203edbcaf21 100644 --- a/qiskit/circuit/library/standard_gates/__init__.py +++ b/qiskit/circuit/library/standard_gates/__init__.py @@ -115,59 +115,60 @@ def get_standard_gate_name_mapping(): # Standard gates library mapping, multicontrolled gates not included since they're # variable width - name_mapping = { - "id": IGate(), - "sx": SXGate(), - "x": XGate(), - "cx": CXGate(), - "rz": RZGate(Parameter("λ")), - "r": RGate(Parameter("ϴ"), Parameter("φ")), - "reset": Reset(), - "c3sx": C3SXGate(), - "ccx": CCXGate(), - "dcx": DCXGate(), - "ch": CHGate(), - "cp": CPhaseGate(Parameter("ϴ")), - "crx": CRXGate(Parameter("ϴ")), - "cry": CRYGate(Parameter("ϴ")), - "crz": CRZGate(Parameter("ϴ")), - "cswap": CSwapGate(), - "csx": CSXGate(), - "cu": CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ"), Parameter("γ")), - "cu1": CU1Gate(Parameter("λ")), - "cu3": CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), - "cy": CYGate(), - "cz": CZGate(), - "ccz": CCZGate(), - "h": HGate(), - "p": PhaseGate(Parameter("ϴ")), - "rccx": RCCXGate(), - "rcccx": RC3XGate(), - "rx": RXGate(Parameter("ϴ")), - "rxx": RXXGate(Parameter("ϴ")), - "ry": RYGate(Parameter("ϴ")), - "ryy": RYYGate(Parameter("ϴ")), - "rzz": RZZGate(Parameter("ϴ")), - "rzx": RZXGate(Parameter("ϴ")), - "xx_minus_yy": XXMinusYYGate(Parameter("ϴ")), - "xx_plus_yy": XXPlusYYGate(Parameter("ϴ")), - "ecr": ECRGate(), - "s": SGate(), - "sdg": SdgGate(), - "cs": CSGate(), - "csdg": CSdgGate(), - "swap": SwapGate(), - "iswap": iSwapGate(), - "sxdg": SXdgGate(), - "t": TGate(), - "tdg": TdgGate(), - "u": UGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), - "u1": U1Gate(Parameter("λ")), - "u2": U2Gate(Parameter("φ"), Parameter("λ")), - "u3": U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), - "y": YGate(), - "z": ZGate(), - "delay": Delay(Parameter("t")), - "measure": Measure(), - } + gates = [ + IGate(), + SXGate(), + XGate(), + CXGate(), + RZGate(Parameter("λ")), + RGate(Parameter("ϴ"), Parameter("φ")), + Reset(), + C3SXGate(), + CCXGate(), + DCXGate(), + CHGate(), + CPhaseGate(Parameter("ϴ")), + CRXGate(Parameter("ϴ")), + CRYGate(Parameter("ϴ")), + CRZGate(Parameter("ϴ")), + CSwapGate(), + CSXGate(), + CUGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ"), Parameter("γ")), + CU1Gate(Parameter("λ")), + CU3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + CYGate(), + CZGate(), + CCZGate(), + HGate(), + PhaseGate(Parameter("ϴ")), + RCCXGate(), + RC3XGate(), + RXGate(Parameter("ϴ")), + RXXGate(Parameter("ϴ")), + RYGate(Parameter("ϴ")), + RYYGate(Parameter("ϴ")), + RZZGate(Parameter("ϴ")), + RZXGate(Parameter("ϴ")), + XXMinusYYGate(Parameter("ϴ")), + XXPlusYYGate(Parameter("ϴ")), + ECRGate(), + SGate(), + SdgGate(), + CSGate(), + CSdgGate(), + SwapGate(), + iSwapGate(), + SXdgGate(), + TGate(), + TdgGate(), + UGate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + U1Gate(Parameter("λ")), + U2Gate(Parameter("φ"), Parameter("λ")), + U3Gate(Parameter("ϴ"), Parameter("φ"), Parameter("λ")), + YGate(), + ZGate(), + Delay(Parameter("t")), + Measure(), + ] + name_mapping = {gate.name for gate in gates} return name_mapping From aff11df44c59085529636da979e723ca13338d0f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 21 Sep 2022 17:12:37 -0400 Subject: [PATCH 14/20] Fix lint --- qiskit/circuit/library/standard_gates/__init__.py | 2 +- qiskit/providers/backend_compat.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/__init__.py b/qiskit/circuit/library/standard_gates/__init__.py index 2203edbcaf21..3dca1abb6201 100644 --- a/qiskit/circuit/library/standard_gates/__init__.py +++ b/qiskit/circuit/library/standard_gates/__init__.py @@ -170,5 +170,5 @@ def get_standard_gate_name_mapping(): Delay(Parameter("t")), Measure(), ] - name_mapping = {gate.name for gate in gates} + name_mapping = {gate.name: gate for gate in gates} return name_mapping diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index c1fac2c94858..8ad1eec11d6c 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -10,10 +10,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=no-value-for-parameter,unused-wildcard-import,wildcard-import - """Backend abstract interface for providers.""" +from __future__ import annotations + from typing import List, Iterable, Any, Dict, Optional from qiskit.exceptions import QiskitError @@ -40,12 +40,15 @@ def convert_to_target( properties: BackendProperties = None, defaults: PulseDefaults = None, custom_name_mapping: Optional[Dict[str, Any]] = None, -) -> Target: - from qiskit.transpiler.target import Target, InstructionProperties # pylint: disable=cyclic-import - +) -> "Target": """Uses configuration, properties and pulse defaults to construct and return Target class. """ + from qiskit.transpiler.target import ( + Target, + InstructionProperties, + ) # pylint: disable=cyclic-import + # Standard gates library mapping, multicontrolled gates not included since they're # variable width name_mapping = get_standard_gate_name_mapping() From 2635c4cde09de3b1cc5f373c9e4800fb6682b816 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 22 Sep 2022 07:10:44 -0400 Subject: [PATCH 15/20] Fix pylint cyclic-import error --- qiskit/providers/backend_compat.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 8ad1eec11d6c..24d6c69c3b36 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -40,14 +40,15 @@ def convert_to_target( properties: BackendProperties = None, defaults: PulseDefaults = None, custom_name_mapping: Optional[Dict[str, Any]] = None, -) -> "Target": +): """Uses configuration, properties and pulse defaults to construct and return Target class. """ + # pylint: disable=cyclic-import from qiskit.transpiler.target import ( Target, InstructionProperties, - ) # pylint: disable=cyclic-import + ) # Standard gates library mapping, multicontrolled gates not included since they're # variable width From 25bd63737b591496c0fc364acb1fa234991f6b3d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 27 Sep 2022 04:15:28 -0400 Subject: [PATCH 16/20] Update qiskit/providers/backend_compat.py Co-authored-by: Naoki Kanazawa --- qiskit/providers/backend_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 24d6c69c3b36..a73079aaff1c 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -173,7 +173,7 @@ def qubit_props_list_from_props( try: frequency = properties.frequency(qubit) except BackendPropertyError: - t_2 = None + frequency = None qubit_props.append( QubitProperties( # type: ignore[no-untyped-call] t1=t_1, From a4db6669a279b6d3d0535ad89c49c0718524c5f0 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 27 Sep 2022 10:41:32 -0400 Subject: [PATCH 17/20] Remove supported_instructions and add option for adding delay The supported instructions field is underdocumented and it's not clear that the instructions it contains are something that we want to support directly. Since the only reason we added support for it in the target generation was to handle the delay for ibm backends (mostly in tests as the IBM provider will be using BackendV2 natively soon) this commit removes its usage and adds a new flag for explicitly adding delay to the backend's target. --- qiskit/providers/backend_compat.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index a73079aaff1c..4f4081f6ac49 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -29,17 +29,13 @@ from qiskit.providers.options import Options from qiskit.providers.exceptions import BackendPropertyError -# Some BackendConfiguration objects contain pulse only instructions in their -# supported_instructions field. Filter these out since they don't go in a -# TARGET -PULSE_INSTRUCTIONS = {"acquire", "shiftf", "setf", "play"} - def convert_to_target( configuration: BackendConfiguration, properties: BackendProperties = None, defaults: PulseDefaults = None, custom_name_mapping: Optional[Dict[str, Any]] = None, + add_delay: bool = False, ): """Uses configuration, properties and pulse defaults to construct and return Target class. @@ -137,9 +133,6 @@ def convert_to_target( combined_global_ops = set() if configuration.basis_gates: combined_global_ops.update(configuration.basis_gates) - if configuration.supported_instructions: - combined_global_ops.update(configuration.supported_instructions) - combined_global_ops -= PULSE_INSTRUCTIONS for op in combined_global_ops: if op not in target: if op in name_mapping: @@ -151,6 +144,10 @@ def convert_to_target( f"Operation name '{op}' does not have a known mapping. Use " "custom_name_mapping to map this name to an Operation object" ) + if add_delay and "delay" not in target: + target.add_instruction( + name_mapping["delay"], {(bit,): None for bit in range(target.num_qubits)} + ) return target @@ -198,6 +195,7 @@ def __init__( self, backend: BackendV1, name_mapping: Optional[Dict[str, Any]] = None, + add_delay: bool = False, ): """Initialize a BackendV2 converter instance based on a BackendV1 instance. @@ -210,6 +208,9 @@ def __init__( standard gate object from :mod:`qiskit.circuit.library` this only needs to be specified if the input ``backend`` defines gates in names outside that set. + add_delay: If set to true a :class:`~qiskit.circuit.Delay` operation + will be added to the target as a supported operation for all + qubits """ self._backend = backend self._config = self._backend.configuration() @@ -227,6 +228,7 @@ def __init__( self._defaults = None self._target = None self._name_mapping = name_mapping + self._add_delay = add_delay @property def target(self): @@ -244,6 +246,7 @@ def target(self): self._properties, self._defaults, custom_name_mapping=self._name_mapping, + add_delay=self._add_delay ) return self._target From 8cb7c12def7106860824cfd26e00caaa3420715c Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 27 Sep 2022 10:44:59 -0400 Subject: [PATCH 18/20] Add mention of converter class to API changes section --- qiskit/providers/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index cbdc8dbf482c..e96d5635fe00 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -668,6 +668,9 @@ def status(self): operation on a given qubit is used to model the readout length. However, a :obj:`~BackendV2` can implement multiple measurement types and list them separately in a :class:`~qiskit.transpiler.Target`. + +There is also a :class:`~.BackendV2Converter` class available that enables you +to wrap a :class:`~.BackendV1` object with a :class:`~.BackendV2` interface. """ import pkgutil From e3e266b19bcf74fb5e0c1c843a6f00903798b22f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 27 Sep 2022 10:46:21 -0400 Subject: [PATCH 19/20] Add missing flag usage to delay test --- test/python/providers/test_fake_backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index 7392994fce2e..5c0da0f35dbf 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -184,7 +184,7 @@ def test_converter(self, opt_level): def test_converter_delay_circuit(self): backend = FakeMumbai() - backend_v2 = BackendV2Converter(backend) + backend_v2 = BackendV2Converter(backend, add_delay=True) self.assertIsInstance(backend_v2, BackendV2) qc = QuantumCircuit(2) qc.delay(502, 0, unit="ns") From aeb9784a32a2472b5493611b68808925988551d1 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 27 Sep 2022 10:49:41 -0400 Subject: [PATCH 20/20] Run black --- qiskit/providers/backend_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index 4f4081f6ac49..60889ff22c70 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -246,7 +246,7 @@ def target(self): self._properties, self._defaults, custom_name_mapping=self._name_mapping, - add_delay=self._add_delay + add_delay=self._add_delay, ) return self._target