From 4bb889f95be2c7e1b6f0ca0c983ed0c2c6f57971 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 1 Dec 2021 08:27:23 -0500 Subject: [PATCH] Fix arguments for RZXCalibrationBuilder and EchoRZXWeylDecomposition The RZXCalibrationBuilder and EchoRZXWeylDecomposition transpiler passes were previously taking BaseBackend instances as arguments. Besides that being a deprecated class which will be removed soon, it also is incorrect because backend objects are not guaranteed to be pickleable so when a pass manager runs in parallel processes this will cause an error. This was never caught because these passes aren't part of any default pass managers and their tests don't include running them as part of transpile(). To fix this passes typically take the properties of a backend they require. This is being reworked for BackendV2 in #5885 so a target object can be used to encapsulate the model of a backend so we have a single data structure to pass around, but until that is the minimum backend version we need to also support taking the individual components for BackendV1 and BaseBackend. This commit changes the pass constructors to take only use the parameters from the backend object used in the pass internals and stop requiring a backend object be used to construct an instance of either pass. --- .../transpiler/passes/calibration/builders.py | 24 ++++++++++--------- .../echo_rzx_weyl_decomposition.py | 6 ++--- test/python/pulse/test_calibrationbuilder.py | 5 +++- .../test_echo_rzx_weyl_decomposition.py | 15 ++++++------ 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/qiskit/transpiler/passes/calibration/builders.py b/qiskit/transpiler/passes/calibration/builders.py index 9c328bd9dd8f..76e4241bd851 100644 --- a/qiskit/transpiler/passes/calibration/builders.py +++ b/qiskit/transpiler/passes/calibration/builders.py @@ -22,7 +22,6 @@ from qiskit.circuit.library.standard_gates import RZXGate from qiskit.dagcircuit import DAGCircuit from qiskit.exceptions import QiskitError -from qiskit.providers import basebackend from qiskit.pulse import ( Play, Delay, @@ -103,26 +102,29 @@ class RZXCalibrationBuilder(CalibrationBuilder): angle. Additional details can be found in https://arxiv.org/abs/2012.11660. """ - def __init__(self, backend: basebackend): + def __init__( + self, + instruction_schedule_map: InstructionScheduleMap, + qubit_channel_mapping: List[List[str]], + ): """ Initializes a RZXGate calibration builder. Args: - backend: Backend for which to construct the gates. + instruction_schedule_map: The :obj:`InstructionScheduleMap` object representing the + default pulse calibrations for the target backend + qubit_channel_mapping: The list mapping qubit indices to the list of + channel names that apply on that qubit. Raises: QiskitError: if open pulse is not supported by the backend. """ super().__init__() - if not backend.configuration().open_pulse: - raise QiskitError( - "Calibrations can only be added to Pulse-enabled backends, " - "but {} is not enabled with Pulse.".format(backend.name()) - ) + if instruction_schedule_map is None or qubit_channel_mapping is None: + raise QiskitError("Calibrations can only be added to Pulse-enabled backends") - self._inst_map = backend.defaults().instruction_schedule_map - self._config = backend.configuration() - self._channel_map = backend.configuration().qubit_channel_mapping + self._inst_map = instruction_schedule_map + self._channel_map = qubit_channel_mapping def supported(self, node_op: CircuitInst, qubits: List) -> bool: """Determine if a given node supports the calibration. diff --git a/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py b/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py index 76e0ecc13ac9..2b896fe5d351 100644 --- a/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py +++ b/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py @@ -24,8 +24,6 @@ from qiskit.dagcircuit import DAGCircuit from qiskit.converters import circuit_to_dag -from qiskit.providers import basebackend - import qiskit.quantum_info as qi from qiskit.quantum_info.synthesis.two_qubit_decompose import TwoQubitControlledUDecomposer @@ -38,10 +36,10 @@ class EchoRZXWeylDecomposition(TransformationPass): Each pair of RZXGates forms an echoed RZXGate. """ - def __init__(self, backend: basebackend): + def __init__(self, instruction_schedule_map: "InstructionScheduleMap"): """EchoRZXWeylDecomposition pass.""" - self._inst_map = backend.defaults().instruction_schedule_map super().__init__() + self._inst_map = instruction_schedule_map def _is_native(self, qubit_pair: Tuple) -> bool: """Return the direction of the qubit pair that is native, i.e. with the shortest schedule.""" diff --git a/test/python/pulse/test_calibrationbuilder.py b/test/python/pulse/test_calibrationbuilder.py index 3637bb06136b..b6b449ad0032 100644 --- a/test/python/pulse/test_calibrationbuilder.py +++ b/test/python/pulse/test_calibrationbuilder.py @@ -57,7 +57,10 @@ def test_rzx_calibration_builder(self): self.assertEqual(rzx_qc.calibrations, {}) # apply the RZXCalibrationBuilderNoEcho. - pass_ = RZXCalibrationBuilderNoEcho(self.backend) + pass_ = RZXCalibrationBuilderNoEcho( + self.backend.defaults().instruction_schedule_map, + self.backend.configuration().qubit_channel_mapping, + ) cal_qc = PassManager(pass_).run(rzx_qc) rzx_qc_duration = schedule(cal_qc, self.backend).duration diff --git a/test/python/transpiler/test_echo_rzx_weyl_decomposition.py b/test/python/transpiler/test_echo_rzx_weyl_decomposition.py index b9a38871bbad..410a1441ffcf 100644 --- a/test/python/transpiler/test_echo_rzx_weyl_decomposition.py +++ b/test/python/transpiler/test_echo_rzx_weyl_decomposition.py @@ -38,6 +38,7 @@ class TestEchoRZXWeylDecomposition(QiskitTestCase): def setUp(self): super().setUp() self.backend = FakeParis() + self.inst_map = self.backend.defaults().instruction_schedule_map def assertRZXgates(self, unitary_circuit, after): """Check the number of rzx gates""" @@ -74,7 +75,7 @@ def test_rzx_number_native_weyl_decomposition(self): unitary_circuit = qi.Operator(circuit).data - after = EchoRZXWeylDecomposition(self.backend)(circuit) + after = EchoRZXWeylDecomposition(self.inst_map)(circuit) unitary_after = qi.Operator(after).data @@ -96,11 +97,11 @@ def test_h_number_non_native_weyl_decomposition_1(self): circuit_non_native.rzz(theta, qr[1], qr[0]) dag = circuit_to_dag(circuit) - pass_ = EchoRZXWeylDecomposition(self.backend) + pass_ = EchoRZXWeylDecomposition(self.inst_map) after = dag_to_circuit(pass_.run(dag)) dag_non_native = circuit_to_dag(circuit_non_native) - pass_ = EchoRZXWeylDecomposition(self.backend) + pass_ = EchoRZXWeylDecomposition(self.inst_map) after_non_native = dag_to_circuit(pass_.run(dag_non_native)) circuit_rzx_number = self.count_gate_number("rzx", after) @@ -126,11 +127,11 @@ def test_h_number_non_native_weyl_decomposition_2(self): circuit_non_native.swap(qr[1], qr[0]) dag = circuit_to_dag(circuit) - pass_ = EchoRZXWeylDecomposition(self.backend) + pass_ = EchoRZXWeylDecomposition(self.inst_map) after = dag_to_circuit(pass_.run(dag)) dag_non_native = circuit_to_dag(circuit_non_native) - pass_ = EchoRZXWeylDecomposition(self.backend) + pass_ = EchoRZXWeylDecomposition(self.inst_map) after_non_native = dag_to_circuit(pass_.run(dag_non_native)) circuit_rzx_number = self.count_gate_number("rzx", after) @@ -165,7 +166,7 @@ def test_weyl_decomposition_gate_angles(self): unitary_circuit = qi.Operator(circuit).data dag = circuit_to_dag(circuit) - pass_ = EchoRZXWeylDecomposition(self.backend) + pass_ = EchoRZXWeylDecomposition(self.inst_map) after = dag_to_circuit(pass_.run(dag)) dag_after = circuit_to_dag(after) @@ -220,7 +221,7 @@ def test_weyl_unitaries_random_circuit(self): unitary_circuit = qi.Operator(circuit).data dag = circuit_to_dag(circuit) - pass_ = EchoRZXWeylDecomposition(self.backend) + pass_ = EchoRZXWeylDecomposition(self.inst_map) after = dag_to_circuit(pass_.run(dag)) unitary_after = qi.Operator(after).data