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

EchoRZXWeylDecomposition Transpiler Pass #6784

Merged
merged 66 commits into from
Oct 1, 2021
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
55514cd
Added new EchoRZXWeylDecomposition transpiler pass
catornow Jul 21, 2021
b60171d
Adapted two_qubit_decompose and calibration_creators and added tests
catornow Jul 21, 2021
73de71b
Black
catornow Jul 21, 2021
8f4ebf7
Small modification
catornow Jul 21, 2021
add5413
Adapted two_qubit_decompose to match latest upstream/main version
catornow Jul 23, 2021
76936f6
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
294804a
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jul 25, 2021
c13538d
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jul 25, 2021
8bd3c22
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Jul 25, 2021
a8fc088
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
e71d098
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
f93d5e5
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
43de98b
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
5b7523e
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
713b536
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Jul 25, 2021
a484c9c
Implemented Daniel's suggestions
catornow Jul 25, 2021
d600452
Small modifications in calibration_creators
catornow Jul 25, 2021
2bcb15e
Style and lint errors
catornow Jul 26, 2021
1aa8f80
Rewrote TwoQubitWeylEchoRZX to remove lint error
catornow Jul 26, 2021
e1cefd0
Black
catornow Jul 26, 2021
9bcb060
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
eggerdj Jul 28, 2021
0e9f637
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
eggerdj Jul 28, 2021
4139093
Implemented additional tests
catornow Jul 28, 2021
e7d06e2
Black
catornow Jul 28, 2021
c148aaa
Small modifications of the tests
catornow Jul 28, 2021
ae7df3b
Added release note
catornow Jul 28, 2021
562af6a
Added the class TwoQubitControlledUDecomposer to generalize TwoQubitW…
catornow Aug 18, 2021
148e8e9
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 24, 2021
2210b73
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 24, 2021
e5a4370
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 24, 2021
7ed3b1a
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 24, 2021
d996963
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 24, 2021
6903d22
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Aug 24, 2021
97e0221
Update qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposit…
catornow Aug 24, 2021
49bc0de
Modified TwoQubitControlledUDecomposer to work for CPhaseGate, CRZGat…
catornow Aug 27, 2021
60316e6
Added tests for TwoQubitControlledUDecomposer
catornow Aug 27, 2021
b359a4d
Moved is_native to EchoRZXWeylDecomposition transpiler pass
catornow Aug 27, 2021
2c26d6f
Removed TwoQubitWeylEchoRZX tests (since this class does not exist an…
catornow Aug 27, 2021
6f76d18
Merge branch 'main' into EchoRZXWeylDecomposition
catornow Aug 27, 2021
a7018a4
Small modification
catornow Aug 27, 2021
8051753
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 27, 2021
5cf54da
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 27, 2021
2dd8097
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 27, 2021
cb33ef5
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Aug 27, 2021
a2f52d5
Fixed style and lint error
catornow Aug 27, 2021
69bf841
Removed ```node.type == "op"``` in EchoRZXWeylDecomposition transpile…
catornow Aug 27, 2021
3537478
Merge branch 'main' into EchoRZXWeylDecomposition
catornow Aug 28, 2021
a684107
Merge branch 'main' of github.com:Qiskit/qiskit-terra into EchoRZXWey…
catornow Sep 1, 2021
2b2fb01
Merge branch 'EchoRZXWeylDecomposition' of github.com:catornow/qiskit…
catornow Sep 1, 2021
fe50279
Merge branch 'main' of github.com:Qiskit/qiskit-terra into EchoRZXWey…
catornow Sep 1, 2021
8dff106
Changed ```gate=node.op.name``` back to ```gate=node.op``` in Calibra…
catornow Sep 1, 2021
e0f0c2e
Changed the argument of EchoRZXWeylDecomposition from inst_map to bac…
catornow Sep 1, 2021
4fe00f8
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Sep 6, 2021
4630b11
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Sep 6, 2021
c328dd0
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Sep 6, 2021
a778fd3
Update qiskit/quantum_info/synthesis/two_qubit_decompose.py
catornow Sep 6, 2021
f3a0888
Fixed bug and slightly modified correct decomposition check
catornow Sep 6, 2021
3506c53
Removed (raise-missing-from) pylint error
catornow Sep 7, 2021
382ab9a
Adapted tests.
catornow Sep 7, 2021
9cb9b6c
Black
catornow Sep 7, 2021
de3622b
Modified docstrings
catornow Sep 29, 2021
bd41b3f
Modified and added tests to also check the RZX gate angles
catornow Sep 30, 2021
949fa01
Merge branch 'main' into EchoRZXWeylDecomposition
catornow Sep 30, 2021
1e777a3
Merge branch 'main' into EchoRZXWeylDecomposition
catornow Oct 1, 2021
84b4af7
Merge branch 'main' into EchoRZXWeylDecomposition
eggerdj Oct 1, 2021
9d3bdaa
Merge branch 'main' into EchoRZXWeylDecomposition
eggerdj Oct 1, 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
89 changes: 89 additions & 0 deletions qiskit/quantum_info/synthesis/two_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,95 @@ def specialize(self):
self.K2r = np.asarray(RYGate(k2rtheta)) @ np.asarray(RXGate(k2rlambda))


class TwoQubitWeylEchoRZX:
eggerdj marked this conversation as resolved.
Show resolved Hide resolved
"""Decompose two-qubit unitary in terms of echoed cross-resonance gates."""

_default_1q_basis = "XYX"

def __init__(self, unitary, is_native: bool):
"""Initialize the KAK decomposition.

Args:
is_native: If True then the CX schedule on qubits (q0, q1)
is shorter than the schedule on (q1, q0).
"""
self.is_native = is_native

self.decomposer = TwoQubitWeylDecomposition(unitary)

def circuit(self, *, euler_basis: Optional[str] = None, atol=DEFAULT_ATOL) -> QuantumCircuit:
"""Returns Weyl decomposition in circuit form.

simplify, atol arguments are passed to OneQubitEulerDecomposer"""
eggerdj marked this conversation as resolved.
Show resolved Hide resolved
if euler_basis is None:
euler_basis = self._default_1q_basis
oneq_decompose = OneQubitEulerDecomposer(euler_basis)
catornow marked this conversation as resolved.
Show resolved Hide resolved
c1l, c1r, c2l, c2r = (
oneq_decompose(k, atol=atol)
for k in (
self.decomposer.K1l,
self.decomposer.K1r,
self.decomposer.K2l,
self.decomposer.K2r,
)
)
circ = QuantumCircuit(2, global_phase=self.decomposer.global_phase)
circ.compose(c2r, [0], inplace=True)
circ.compose(c2l, [1], inplace=True)
self._weyl_gate(circ, atol)
circ.compose(c1r, [0], inplace=True)
circ.compose(c1l, [1], inplace=True)
return circ

@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 _weyl_gate(self, circ: QuantumCircuit, atol=1.0e-13):
"""Appends Ud(a, b, c) to the circuit."""
circ.h(0)
if abs(self.decomposer.a) > atol:
if self.is_native:
self._apply_rzx(circ, self.decomposer.a)
else:
self._apply_reverse_rzx(circ, self.decomposer.a)
circ.h(0)
circ.sdg(0)
circ.h(0)
circ.sdg(1)
if abs(self.decomposer.b) > atol:
if self.is_native:
self._apply_rzx(circ, self.decomposer.b)
else:
self._apply_reverse_rzx(circ, self.decomposer.b)
circ.h(0)
circ.s(0)
circ.s(1)
circ.h(1)
if abs(self.decomposer.c) > atol:
if self.is_native:
self._apply_rzx(circ, self.decomposer.c)
else:
self._apply_reverse_rzx(circ, self.decomposer.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,90 @@
# 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 typing import Tuple

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__()

catornow marked this conversation as resolved.
Show resolved Hide resolved
def _is_native(self, qubit_pair: Tuple) -> bool:
"""Return the direction of the qubit pair that is native, i.e. with the shortest schedule."""
cx1 = self.inst_map.get("cx", qubit_pair)
cx2 = self.inst_map.get("cx", qubit_pair[::-1])
return cx1.duration < cx2.duration

def run(self, dag: DAGCircuit):
"""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.
catornow marked this conversation as resolved.
Show resolved Hide resolved

Returns:
DAGCircuit: The rearranged dag.
catornow marked this conversation as resolved.
Show resolved Hide resolved

Raises:
TranspilerError: If the circuit cannot be mapped.
catornow marked this conversation as resolved.
Show resolved Hide resolved
"""

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())
eggerdj marked this conversation as resolved.
Show resolved Hide resolved

catornow marked this conversation as resolved.
Show resolved Hide resolved
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]

is_native = self._is_native((physical_q0, physical_q1))
eggerdj marked this conversation as resolved.
Show resolved Hide resolved

unitary = qi.Operator(node.op).data
dag_weyl = circuit_to_dag(
TwoQubitWeylEchoRZX(unitary, is_native=is_native).circuit()
catornow marked this conversation as resolved.
Show resolved Hide resolved
)
dag.substitute_node_with_dag(node, dag_weyl)

return dag
41 changes: 25 additions & 16 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,35 @@ 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:
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._config = backend.configuration()
self._channel_map = backend.configuration().qubit_channel_mapping
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("Either a backend or an instruction schedule map must be specified.")

def supported(self, node_op: DAGNode) -> bool:
"""
Expand Down Expand Up @@ -272,10 +281,10 @@ class RZXCalibrationBuilderNoEcho(RZXCalibrationBuilder):

The ``RZXCalibrationBuilderNoEcho`` is a variation of the
:class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` pass
that creates calibrations for the cross-resonance pulses without inserting
as it creates calibrations for the cross-resonance pulses without inserting
the echo pulses in the pulse schedule. This enables exposing the echo in
the cross-resonance sequence as gates so that the transpiler can simplify them.
The ``RZXCalibrationBuilderNoEcho`` only supports the hardware-native direction
The RZXCalibrationBuilderNoEcho only supports the hardware-native direction
of the CX gate.
"""

Expand All @@ -289,7 +298,7 @@ def _filter_control(inst: (int, Union["Schedule", Instruction])) -> bool:

Returns:
match: True if the instruction is a Play instruction with
a Gaussian square pulse on the ControlChannel.
a Gaussian square pulse on the ControlChannel.
"""
if isinstance(inst[1], Play):
if isinstance(inst[1].pulse, GaussianSquare) and isinstance(
Expand All @@ -309,7 +318,7 @@ def _filter_drive(inst: (int, Union["Schedule", Instruction])) -> bool:

Returns:
match: True if the instruction is a Play instruction with
a Gaussian square pulse on the DriveChannel.
a Gaussian square pulse on the DriveChannel.
"""
if isinstance(inst[1], Play):
if isinstance(inst[1].pulse, GaussianSquare) and isinstance(
Expand Down
Loading