Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Pulse-efficient Transpiler Passes #6653

Open
wants to merge 78 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 76 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8df6123
Added the class RZXCalibrationBuilderNoEcho
catornow Apr 26, 2021
51a3caf
Fixed style and lint error in line 20
catornow Apr 27, 2021
aad1389
Fixed style and lint errors
catornow Apr 27, 2021
7ae864a
Corrected code by implementing new filter_funcs
catornow Apr 28, 2021
271093e
Implemented Daniel's suggestions
catornow Apr 29, 2021
b0e1527
Added tests for the RZXCalibrationBuilderNoEcho
catornow May 5, 2021
d37ee2a
Fixed style and lint error
catornow May 5, 2021
28ea533
Formatted the code
catornow May 5, 2021
e31cc56
Reformatted calibration_creators.py
catornow May 6, 2021
1f825ac
Merge branch 'main' into CalibrationBuilderNoEcho
catornow May 6, 2021
226ca84
Apply suggestions from code review
catornow May 6, 2021
63ed1e9
Reformatted the code
catornow May 11, 2021
a2ed887
Fixed Pylint no-member error
catornow May 19, 2021
e7a2445
Merge branch 'main' into CalibrationBuilderNoEcho
catornow May 19, 2021
b078705
Merge branch 'main' into CalibrationBuilderNoEcho
eggerdj May 21, 2021
1b5cf50
Added NativeCXDirection Transpiler Pass, added RZX decompositions of …
catornow Jun 16, 2021
6eca945
Merge branch 'CalibrationBuilderNoEcho' of github.com:catornow/qiskit…
catornow Jun 16, 2021
448f94f
Added NativeCXDirection Transpiler Pass.
catornow Jun 16, 2021
b589c86
Added EchoRZXWeylDecomposition Transpiler Pass.
catornow Jun 20, 2021
d704b5a
Small change
catornow Jun 21, 2021
60f5b41
Removed changes to Equivalence Library
catornow Jun 21, 2021
c17c534
Minor changes
catornow Jun 22, 2021
b4d81d7
Removed bug due to simplified=True and modified _weyl_gate in class T…
catornow Jun 22, 2021
ec5df30
Changed name of NativeCXGateDirection to NativeCRGateDirection
catornow Jun 22, 2021
23d3ca7
Merge branch 'main' into CalibrationBuilderNoEcho
eggerdj Jun 22, 2021
aa970c6
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 22, 2021
2d27040
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 22, 2021
992a158
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 22, 2021
d64a7f8
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 22, 2021
9dd97c2
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 22, 2021
2b0c698
Update qiskit/transpiler/passes/utils/native_cr_direction.py
catornow Jun 22, 2021
2ea238f
Update qiskit/transpiler/passes/utils/native_cr_direction.py
catornow Jun 22, 2021
764d0ee
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 22, 2021
2c0aee8
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 22, 2021
d944a47
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 22, 2021
90f0f35
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 22, 2021
ee2f6bf
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 22, 2021
882b178
Update qiskit/transpiler/passes/utils/native_cr_direction.py
catornow Jun 22, 2021
9615d24
Minor changes
catornow Jun 24, 2021
2c3c4ca
Added "backend" to passmanager_config and transpiler to use it in pas…
catornow Jun 24, 2021
ed5c0aa
Added new pulse-efficient preset pass manager
catornow Jun 24, 2021
b501631
Added new pulse-efficient preset pass manager
catornow Jun 24, 2021
a4e28d5
Added tests for the EchoRZXWeylDecomposition pass and the TwoQubitWey…
catornow Jun 24, 2021
f38fad2
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 25, 2021
b67fb0a
Update test/python/transpiler/test_echo_rzx_weyl_decomposition.py
catornow Jun 25, 2021
4aa7da9
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 25, 2021
8dd4556
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 25, 2021
8e01158
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 25, 2021
33439a9
Update qiskit/transpiler/preset_passmanagers/pulse_efficient.py
catornow Jun 25, 2021
5922469
Use instruction schedule map as variable instead of backend, added _a…
catornow Jun 26, 2021
e335416
Added instruction schedule map as variable to pass manager config and…
catornow Jun 26, 2021
65262d7
Merge branch 'CalibrationBuilderNoEcho' of github.com:catornow/qiskit…
catornow Jun 26, 2021
67690da
Use instruction schedule map in pass manager, modification in pulse-e…
catornow Jun 26, 2021
093ab18
Allow for str expression as optimization_level
catornow Jun 27, 2021
b274b84
Added instruction schedule map as variable for RZXCalibrationBuilder
catornow Jun 27, 2021
e00eeac
Added tests for pulse-efficient transpiler pass and pass manager
catornow Jun 27, 2021
3cbb40f
black
catornow Jun 27, 2021
81cd58d
Minor change
catornow Jun 27, 2021
a8f9ea5
Update test/python/transpiler/test_echo_rzx_weyl_decomposition.py
catornow Jun 28, 2021
5d9d582
Update test/python/transpiler/test_echo_rzx_weyl_decomposition.py
catornow Jun 28, 2021
4c85977
Update test/python/transpiler/test_echo_rzx_weyl_decomposition.py
catornow Jun 28, 2021
70d5c52
Update test/python/transpiler/test_echo_rzx_weyl_decomposition.py
catornow Jun 28, 2021
b0f8700
Update test/python/transpiler/test_pulse_efficient_passmanager.py
catornow Jun 28, 2021
7f4b2df
Update qiskit/transpiler/preset_passmanagers/pulse_efficient.py
catornow Jun 28, 2021
b79aed7
Update test/python/transpiler/test_pulse_efficient_passmanager.py
catornow Jun 28, 2021
17ea8a8
Update test/python/transpiler/test_pulse_efficient_passmanager.py
catornow Jun 28, 2021
261ba12
Update test/python/transpiler/test_pulse_efficient_passmanager.py
catornow Jun 28, 2021
a34a297
Update qiskit/transpiler/preset_passmanagers/pulse_efficient.py
catornow Jun 28, 2021
f0e03c4
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jun 28, 2021
598f7ec
Implement suggestions of Luciano
catornow Jun 28, 2021
2f675ed
Included optimization_level 3 in pulse efficient pass manager
catornow Jun 28, 2021
6b54311
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 28, 2021
7f909ad
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 28, 2021
fa5e961
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 28, 2021
45ad417
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jun 28, 2021
e58d5b2
Merge branch 'main' into CalibrationBuilderNoEcho
catornow Jun 28, 2021
48a3a53
Added function specialize in class TwoQubitWeylEchoRZX again
catornow Jul 21, 2021
176b228
Merge branch 'CalibrationBuilderNoEcho' of github.com:catornow/qiskit…
catornow Jul 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import (
level_0_pass_manager,
pulse_efficient_pass_manager,
level_1_pass_manager,
level_2_pass_manager,
level_3_pass_manager,
Expand All @@ -58,7 +59,8 @@ def transpile(
dt: Optional[float] = None,
approximation_degree: Optional[float] = None,
seed_transpiler: Optional[int] = None,
optimization_level: Optional[int] = None,
inst_map: Dict[str, Dict[Tuple[int], Schedule]] = None,
optimization_level: Union[Optional[int], Optional[str]] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the algorithms, that create circuits and execute them, this is presently done via a QuantumInstance that wraps a backend and has common circuit support for the algorithms, that also allows an end user to set circuit aspects such as transpiler options. See here https://github.com/Qiskit/qiskit-terra/blob/3bb72d340fcf9552d4a6bd54b675399578c726db/qiskit/utils/quantum_instance.py#L71
So if this is introduced as a string I think the above should follow suit and be changed likewise. As the code just stores and passes this on I think its a minor change there just for the typehint.

As a very minor comment I think the typehint can be written more simply as Optional[Union[int, str]] = None

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think a better interface for this is the FullPassManager being introduced in: #6403 with for the extra passes the user just builds a post-optimization pass manager that runs these additional passes and sets that on level 3. I don't think we want to use optimization level like this.

pass_manager: Optional[PassManager] = None,
callback: Optional[Callable[[BasePass, DAGCircuit, float, PropertySet, int], Any]] = None,
output_name: Optional[Union[str, List[str]]] = None,
Expand Down Expand Up @@ -186,6 +188,7 @@ def callback_func(**kwargs):

output_name: A list with strings to identify the output circuits. The length of
the list should be exactly the length of the ``circuits`` parameter.
inst_map: Instruction schedule map.

Returns:
The transpiled circuit(s).
Expand Down Expand Up @@ -221,6 +224,7 @@ def callback_func(**kwargs):
translation_method=translation_method,
approximation_degree=approximation_degree,
backend=backend,
inst_map=inst_map,
)

warnings.warn(
Expand Down Expand Up @@ -260,6 +264,7 @@ def callback_func(**kwargs):
dt,
approximation_degree,
seed_transpiler,
inst_map,
optimization_level,
callback,
output_name,
Expand Down Expand Up @@ -352,8 +357,12 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua
pass_manager = level_2_pass_manager(pass_manager_config)
elif level == 3:
pass_manager = level_3_pass_manager(pass_manager_config)
elif level == "pulse_efficient":
pass_manager = pulse_efficient_pass_manager(pass_manager_config)
else:
raise TranspilerError("optimization_level can range from 0 to 3.")
raise TranspilerError(
"optimization_level can range from 0 to 3, or can be 'pulse_efficient'."
)

result = pass_manager.run(
circuit, callback=transpile_config["callback"], output_name=transpile_config["output_name"]
Expand Down Expand Up @@ -440,6 +449,7 @@ def _parse_transpile_args(
dt,
approximation_degree,
seed_transpiler,
inst_map,
optimization_level,
callback,
output_name,
Expand Down Expand Up @@ -475,6 +485,7 @@ def _parse_transpile_args(
translation_method = _parse_translation_method(translation_method, num_circuits)
approximation_degree = _parse_approximation_degree(approximation_degree, num_circuits)
seed_transpiler = _parse_seed_transpiler(seed_transpiler, num_circuits)
inst_map = _parse_inst_map(inst_map, backend, num_circuits)
optimization_level = _parse_optimization_level(optimization_level, num_circuits)
output_name = _parse_output_name(output_name, circuits)
callback = _parse_callback(callback, num_circuits)
Expand All @@ -499,6 +510,7 @@ def _parse_transpile_args(
durations,
approximation_degree,
seed_transpiler,
inst_map,
optimization_level,
output_name,
callback,
Expand All @@ -518,12 +530,13 @@ def _parse_transpile_args(
instruction_durations=args[8],
approximation_degree=args[9],
seed_transpiler=args[10],
inst_map=args[11],
),
"optimization_level": args[11],
"output_name": args[12],
"callback": args[13],
"backend_num_qubits": args[14],
"faulty_qubits_map": args[15],
"optimization_level": args[12],
"output_name": args[13],
"callback": args[14],
"backend_num_qubits": args[15],
"faulty_qubits_map": args[16],
}
list_transpile_args.append(transpile_args)

Expand Down Expand Up @@ -770,6 +783,16 @@ def _parse_seed_transpiler(seed_transpiler, num_circuits):
return seed_transpiler


def _parse_inst_map(inst_map, backend, num_circuits):
if inst_map is None:
if backend.defaults():
backend_defaults = backend.defaults()
inst_map = backend_defaults.instruction_schedule_map
if not isinstance(inst_map, list):
inst_map = [inst_map] * num_circuits
return inst_map


def _parse_optimization_level(optimization_level, num_circuits):
if not isinstance(optimization_level, list):
optimization_level = [optimization_level] * num_circuits
Expand Down
73 changes: 69 additions & 4 deletions qiskit/quantum_info/synthesis/two_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import io
import base64
import warnings
from typing import ClassVar, Optional
from typing import ClassVar, Optional, Tuple

import logging

Expand Down Expand Up @@ -129,12 +129,12 @@ def __init_subclass__(cls, **kwargs):

Make explicitly-instantiated subclass __new__ call base __new__ with fidelity=None"""
super().__init_subclass__(**kwargs)
cls.__new__ = lambda cls, *a, fidelity=None, **k: TwoQubitWeylDecomposition.__new__(
cls, *a, fidelity=None, **k
cls.__new__ = lambda cls, *a, fidelity=None, inst_map=None, qubit_pair=None, **k: TwoQubitWeylDecomposition.__new__(
cls, *a, fidelity=None, inst_map=None, qubit_pair=None, **k
)

@staticmethod
def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9)):
def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9), inst_map=None, qubit_pair=None):
"""Perform the Weyl chamber decomposition, and optionally choose a specialized subclass.

The flip into the Weyl Chamber is described in B. Kraus and J. I. Cirac, Phys. Rev. A 63,
Expand Down Expand Up @@ -544,6 +544,71 @@ def specialize(self):
self.K2r = np.asarray(RYGate(k2rtheta)) @ np.asarray(RXGate(k2rlambda))


class TwoQubitWeylEchoRZX(TwoQubitWeylGeneral):
"""Decompose two-qubit unitary in terms of echoed cross-resonance gates."""

def __init__(self, unitary, inst_map, qubit_pair: Tuple):
self.inst_map = inst_map
self.qubit_pair = qubit_pair
super().__init__(unitary)


@staticmethod
def _apply_rzx(circ: QuantumCircuit, angle: float):
"""Echoed RZX gate"""
circ.rzx(-angle, 0, 1)
circ.x(0)
circ.rzx(angle, 0, 1)
circ.x(0)

@staticmethod
def _apply_reverse_rzx(circ: QuantumCircuit, angle: float):
"""Reverse direction of the echoed RZX gate"""
circ.h(0)
circ.h(1)
circ.rzx(-angle, 1, 0)
circ.x(1)
circ.rzx(angle, 1, 0)
circ.x(1)
circ.h(0)
circ.h(1)

def is_native_cx(self, qubit_pair: Tuple) -> bool:
"""Check that a CX for a qubit pair is native."""
cx1 = self.inst_map.get("cx", qubit_pair)
cx2 = self.inst_map.get("cx", qubit_pair[::-1])
return cx1.duration < cx2.duration

def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
"""Appends Ud(a, b, c) to the circuit."""
del simplify
circ.h(0)
if abs(self.a) > atol:
if self.is_native_cx(self.qubit_pair):
self._apply_rzx(circ, self.a)
else:
self._apply_reverse_rzx(circ, self.a)
circ.h(0)
circ.sdg(0)
circ.h(0)
circ.sdg(1)
if abs(self.b) > atol:
if self.is_native_cx(self.qubit_pair):
self._apply_rzx(circ, self.b)
else:
self._apply_reverse_rzx(circ, self.b)
circ.h(0)
circ.s(0)
circ.s(1)
circ.h(1)
if abs(self.c) > atol:
if self.is_native_cx(self.qubit_pair):
self._apply_rzx(circ, self.c)
else:
self._apply_reverse_rzx(circ, self.c)
circ.h(1)


class TwoQubitWeylMirrorControlledEquiv(TwoQubitWeylDecomposition):
"""U ~ Ud(𝜋/4, 𝜋/4, α) ~ SWAP . Ctrl-U

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# 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.

"""Weyl decomposition of two-qubit gates in terms of echoed cross-resonance gates."""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError

from qiskit.dagcircuit import DAGCircuit
from qiskit.converters import circuit_to_dag

from qiskit.transpiler.layout import Layout

import qiskit.quantum_info as qi

from qiskit.quantum_info.synthesis.two_qubit_decompose import TwoQubitWeylEchoRZX


class EchoRZXWeylDecomposition(TransformationPass):
"""Rewrite two-qubit gates using the Weyl decomposition.

This transpiler pass rewrites two-qubit gates in terms of echoed cross-resonance gates according
to the Weyl decomposition. A two-qubit gate will be replaced with at most six non-echoed RZXGates.
Each pair of RZXGates forms an echoed RZXGate.
"""

def __init__(self, inst_map):
"""EchoRZXWeylDecomposition pass."""
self.inst_map = inst_map
super().__init__()

def run(self, dag):
"""Run the EchoRZXWeylDecomposition pass on `dag`.

Rewrites two-qubit gates in an arbitrary circuit in terms of echoed cross-resonance
gates by computing the Weyl decomposition of the corresponding unitary. Modifies the
input dag.

Args:
dag (DAGCircuit): DAG to map.

Returns:
DAGCircuit: The rearranged dag.

Raises:
TranspilerError: If the circuit cannot be mapped.
"""

if len(dag.qregs) > 1:
raise TranspilerError(
"EchoRZXWeylDecomposition expects a single qreg input DAG,"
f"but input DAG had qregs: {dag.qregs}."
)

trivial_layout = Layout.generate_trivial_layout(*dag.qregs.values())

for node in dag.two_qubit_ops():
if node.type == "op":
control = node.qargs[0]
target = node.qargs[1]

physical_q0 = trivial_layout[control]
physical_q1 = trivial_layout[target]

qubit_pair = (physical_q0, physical_q1)

unitary = qi.Operator(node.op).data
dag_weyl = circuit_to_dag(
TwoQubitWeylEchoRZX(
unitary, inst_map=self.inst_map, qubit_pair=qubit_pair
).circuit()
)
dag.substitute_node_with_dag(node, dag_weyl)

return dag
34 changes: 23 additions & 11 deletions qiskit/transpiler/passes/scheduling/calibration_creators.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Calibration creators."""

import math
from typing import List, Union
from typing import List, Union, Optional, Dict, Tuple
from abc import abstractmethod
import numpy as np

Expand All @@ -28,7 +28,7 @@
)
from qiskit.pulse.instructions.instruction import Instruction
from qiskit.exceptions import QiskitError
from qiskit.providers import basebackend
from qiskit.providers import BaseBackend
from qiskit.dagcircuit import DAGNode
from qiskit.circuit.library.standard_gates import RZXGate
from qiskit.transpiler.basepasses import TransformationPass
Expand Down Expand Up @@ -64,7 +64,7 @@ def run(self, dag):

schedule = self.get_calibration(params, qubits)

dag.add_calibration(node.op, qubits, schedule, params=params)
dag.add_calibration(node.op.name, qubits, schedule, params=params)

return dag

Expand All @@ -82,26 +82,37 @@ class RZXCalibrationBuilder(CalibrationCreator):
angle. Additional details can be found in https://arxiv.org/abs/2012.11660.
"""

def __init__(self, backend: basebackend):
def __init__(
self, backend: Optional[BaseBackend] = None,
inst_map: Optional[Dict[str, Dict[Tuple[int], Schedule]]] = None
):
"""
Initializes a RZXGate calibration builder.

Args:
inst_map: Instruction schedule map.
backend: Backend for which to construct the gates.

Raises:
QiskitError: if open pulse is not supported by the backend.
"""

super().__init__()
if not backend.configuration().open_pulse:

if backend is not None:
self._inst_map = backend.defaults().instruction_schedule_map
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())
)
elif inst_map is not None:
self._inst_map = inst_map
else:
raise QiskitError(
"Calibrations can only be added to Pulse-enabled backends, "
"but {} is not enabled with Pulse.".format(backend.name())
)
"Either a backend or an instruction schedule map must be specified.")

self._inst_map = backend.defaults().instruction_schedule_map
self._config = backend.configuration()
self._channel_map = backend.configuration().qubit_channel_mapping
# self._inst_map = inst_map

def supported(self, node_op: DAGNode) -> bool:
"""
Expand Down Expand Up @@ -328,6 +339,7 @@ def get_calibration(self, params: List, qubits: List) -> Schedule:
support the specified direction of the cx.
"""
theta = params[0]

q1, q2 = qubits[0], qubits[1]

if not self._inst_map.has("cx", qubits):
Expand Down
3 changes: 3 additions & 0 deletions qiskit/transpiler/passmanager_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(
backend_properties=None,
approximation_degree=None,
seed_transpiler=None,
inst_map=None,
):
"""Initialize a PassManagerConfig object

Expand All @@ -54,6 +55,7 @@ def __init__(
(1.0=no approximation, 0.0=maximal approximation)
seed_transpiler (int): Sets random seed for the stochastic parts of
the transpiler.
inst_map (Dict[str, Dict[Tuple[int], Schedule]]): Schedule instructions
"""
self.initial_layout = initial_layout
self.basis_gates = basis_gates
Expand All @@ -66,3 +68,4 @@ def __init__(
self.backend_properties = backend_properties
self.approximation_degree = approximation_degree
self.seed_transpiler = seed_transpiler
self.inst_map = inst_map
Loading