Skip to content

Commit

Permalink
Fix arguments for RZXCalibrationBuilder and EchoRZXWeylDecomposition (#…
Browse files Browse the repository at this point in the history
…7331)

* 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.

* Deprecate backend for RZXCalibrationBuilder instead of removing
  • Loading branch information
mtreinish authored Dec 1, 2021
1 parent 3694fff commit 6b6c683
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 22 deletions.
49 changes: 39 additions & 10 deletions qiskit/transpiler/passes/calibration/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@

from abc import abstractmethod
from typing import List, Union
import warnings

import math
import numpy as np

from qiskit.providers.basebackend import BaseBackend
from qiskit.providers.backend import BackendV1
from qiskit.circuit import Instruction as CircuitInst
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,
Expand Down Expand Up @@ -103,26 +105,53 @@ class RZXCalibrationBuilder(CalibrationBuilder):
angle. Additional details can be found in https://arxiv.org/abs/2012.11660.
"""

def __init__(self, backend: basebackend):
def __init__(
self,
backend: Union[BaseBackend, BackendV1] = None,
instruction_schedule_map: InstructionScheduleMap = None,
qubit_channel_mapping: List[List[str]] = None,
):
"""
Initializes a RZXGate calibration builder.
Args:
backend: Backend for which to construct the gates.
backend: DEPRECATED a backend object to build the calibrations for.
Use of this argument is deprecated in favor of directly
specifying ``instruction_schedule_map`` and
``qubit_channel_map``.
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 backend is not None:
warnings.warn(
"Passing a backend object directly to this pass (either as the first positional "
"argument or as the named 'backend' kwarg is deprecated and will no long be "
"supported in a future release. Instead use the instruction_schedule_map and "
"qubit_channel_mapping kwargs.",
DeprecationWarning,
stacklevel=2,
)

self._inst_map = backend.defaults().instruction_schedule_map
self._config = backend.configuration()
self._channel_map = backend.configuration().qubit_channel_mapping
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())
)
self._inst_map = backend.defaults().instruction_schedule_map
self._channel_map = backend.configuration().qubit_channel_mapping

else:
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 = 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
features:
- |
The constructor of :class:`~qiskit.transpiler.passes.RZXCalibrationBuilder`
has two new kwargs ``instruction_schedule_map`` and ``qubit_channel_mapping``
which take a :class:`~qiskit.pulse.InstructionScheduleMap` and list of
channel name lists for each qubit respectively. These new arguments are used
to directly specify the information needed from a backend target. They should
be used instead of passing a :class:`~qiskit.providers.BaseBackend` or
:class:`~qiskit.providers.BackendV1` object directly to the pass with the
``backend`` argument.
deprecations:
- |
For the constructor of the
:class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` passing a backend
either as the first positional argument or with the named ``backend`` kwarg
is deprecated and will no longer work in a future release. Instead a
a :class:`~qiskit.pulse.InstructionScheduleMap` should be passed directly to
the ``instruction_schedule_map`` kwarg and a list of channel name lists for
each qubit should be passed directly to ``qubit_channel_mapping``. For example,
if you were calling the pass like::
from qiskit.transpiler.passes import RZXCalibrationBuilder
from qiskit.test.mock import FakeMumbai
backend = FakeMumbai()
cal_pass = RZXCalibrationBuilder(backend)
instead you should call it like::
from qiskit.transpiler.passes import RZXCalibrationBuilder
from qiskit.test.mock import FakeMumbai
backend = FakeMumbai()
inst_map = backend.defaults().instruction_schedule_map
channel_map = self.backend.configuration().qubit_channel_mapping
cal_pass = RZXCalibrationBuilder(
instruction_schedule_map=inst_map,
qubit_channel_mapping=channel_map,
)
This change is necessary because as a general rule backend objects are not
pickle serializeable and it would break when it was used with multiple
processes inside of :func:`~qiskit.compiler.transpile` when compliing
multiple circuits at once.
5 changes: 4 additions & 1 deletion test/python/pulse/test_calibrationbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ def test_rzx_calibration_builder(self):
self.assertEqual(rzx_qc.calibrations, {})

# apply the RZXCalibrationBuilderNoEcho.
pass_ = RZXCalibrationBuilderNoEcho(self.backend)
pass_ = RZXCalibrationBuilderNoEcho(
instruction_schedule_map=self.backend.defaults().instruction_schedule_map,
qubit_channel_mapping=self.backend.configuration().qubit_channel_mapping,
)
cal_qc = PassManager(pass_).run(rzx_qc)
rzx_qc_duration = schedule(cal_qc, self.backend).duration

Expand Down
15 changes: 8 additions & 7 deletions test/python/transpiler/test_echo_rzx_weyl_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 6b6c683

Please sign in to comment.