Skip to content

Commit

Permalink
Additions and fixes to qiskit synthesis docs and release notes (#11719)…
Browse files Browse the repository at this point in the history
… (#11770)

* add two_qubit_cnot_decompose to docs

* improve TwoQubitWeylDecomposition docs

* update release notes

* add empty lines

* update refs

* improve TwoQubitBasisDecomposer docs

* more docs updates

* minor update to docs

* minor fix

* updates following review

* add docstring to two_qubit_cnot_decompose

* fix return type

* minor fix

* add args to two_qubit_cnot_decopose

* Add backtick

* update back return type

* add two_qubit_cnot_decompose returns

---------

Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
(cherry picked from commit ec53b64)

Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
  • Loading branch information
mergify[bot] and ShellyGarion committed Feb 12, 2024
1 parent e1e88ea commit 30646af
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 39 deletions.
2 changes: 2 additions & 0 deletions qiskit/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@
XXDecomposer
TwoQubitWeylDecomposition
.. autofunction:: two_qubit_cnot_decompose
"""

from .evolution import (
Expand Down
111 changes: 72 additions & 39 deletions qiskit/synthesis/two_qubit/two_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class TwoQubitWeylDecomposition:
.. math::
U = ({K_1}^l \otimes {K_1}^r) \dot e^{(i a XX + i b YY + i c ZZ)} \dot ({K_2}^l \otimes {K_2}^r)
U = ({K_1}^l \otimes {K_1}^r) e^{(i a XX + i b YY + i c ZZ)} ({K_2}^l \otimes {K_2}^r)
where
Expand All @@ -121,10 +121,15 @@ class TwoQubitWeylDecomposition:
Passing non-None fidelity to specializations is treated as an assertion, raising QiskitError if
forcing the specialization is more approximate than asserted.
Reference:
References:
1. Cross, A. W., Bishop, L. S., Sheldon, S., Nation, P. D. & Gambetta, J. M.,
*Validating quantum computers using randomized model circuits*,
`arXiv:1811.12926 [quant-ph] <https://arxiv.org/abs/1811.12926>`_
2. B. Kraus, J. I. Cirac, *Optimal Creation of Entanglement Using a Two-Qubit Gate*,
`arXiv:0011050 [quant-ph] <https://arxiv.org/abs/quant-ph/0011050>`_
3. B. Drury, P. J. Love, *Constructive Quantum Shannon Decomposition from Cartan
Involutions*, `arXiv:0806.4015 [quant-ph] <https://arxiv.org/abs/0806.4015>`_
"""

# The parameters of the decomposition:
Expand Down Expand Up @@ -157,17 +162,17 @@ def __init_subclass__(cls, **kwargs):

@staticmethod
def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9), _unpickling=False):
"""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,
062309 (2001).
"""Perform the Weyl chamber decomposition, and optionally choose a specialized subclass."""

FIXME: There's a cleaner-seeming method based on choosing branch cuts carefully, in Andrew
M. Childs, Henry L. Haselgrove, and Michael A. Nielsen, Phys. Rev. A 68, 052311, but I
wasn't able to get that to work.
# The flip into the Weyl Chamber is described in B. Kraus and J. I. Cirac, Phys. Rev. A 63,
# 062309 (2001).
#
# FIXME: There's a cleaner-seeming method based on choosing branch cuts carefully, in Andrew
# M. Childs, Henry L. Haselgrove, and Michael A. Nielsen, Phys. Rev. A 68, 052311, but I
# wasn't able to get that to work.
#
# The overall decomposition scheme is taken from Drury and Love, arXiv:0806.4015 [quant-ph].

The overall decomposition scheme is taken from Drury and Love, arXiv:0806.4015 [quant-ph].
"""
if _unpickling:
return super().__new__(cls)

Expand Down Expand Up @@ -384,19 +389,19 @@ def __init__(self, unitary_matrix, fidelity=None):
)

def specialize(self):
"""Make changes to the decomposition to comply with any specialization.
"""Make changes to the decomposition to comply with any specialization."""

Do update a, b, c, k1l, k1r, k2l, k2r, _is_flipped_from_original to round to the
specialization. Do not update the global phase, since this gets done in generic
__init__()"""
# Do update a, b, c, k1l, k1r, k2l, k2r, _is_flipped_from_original to round to the
# specialization. Do not update the global phase, since this gets done in generic
# __init__()
raise NotImplementedError

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

simplify, atol arguments are passed to OneQubitEulerDecomposer"""
# simplify, atol arguments are passed to OneQubitEulerDecomposer
if euler_basis is None:
euler_basis = self._default_1q_basis
oneq_decompose = OneQubitEulerDecomposer(euler_basis)
Expand Down Expand Up @@ -424,7 +429,7 @@ def _weyl_gate(self, simplify, circ: QuantumCircuit, atol):
circ.rzz(-self.c * 2, 0, 1)

def actual_fidelity(self, **kwargs) -> float:
"""Calculates the actual fidelity of the decomposed circuit to the input unitary"""
"""Calculates the actual fidelity of the decomposed circuit to the input unitary."""
circ = self.circuit(**kwargs)
trace = np.trace(Operator(circ).data.T.conj() @ self.unitary_matrix)
return trace_to_fid(trace)
Expand Down Expand Up @@ -459,7 +464,8 @@ def __repr__(self):
def from_bytes(
cls, bytes_in: bytes, *, requested_fidelity: float, **kwargs
) -> "TwoQubitWeylDecomposition":
"""Decode bytes into TwoQubitWeylDecomposition. Used by __repr__"""
"""Decode bytes into :class:`.TwoQubitWeylDecomposition`."""
# Used by __repr__
del kwargs # Unused (just for display)
b64 = base64.decodebytes(bytes_in)
with io.BytesIO(b64) as f:
Expand Down Expand Up @@ -854,7 +860,7 @@ def Ud(a, b, c):


def trace_to_fid(trace):
"""Average gate fidelity is :math:`Fbar = (d + |Tr (Utarget \\cdot U^dag)|^2) / d(d+1)`
r"""Average gate fidelity is :math:`Fbar = (d + |Tr (U_{target} \cdot U^{\dag})|^2) / d(d+1)`
M. Horodecki, P. Horodecki and R. Horodecki, PRA 60, 1888 (1999)"""
return (4 + abs(trace) ** 2) / 20

Expand Down Expand Up @@ -1018,8 +1024,10 @@ def __init__(
self._rqc = None

def traces(self, target):
"""Give the expected traces :math:`|Tr(U \\cdot Utarget^dag)|` for different number of
basis gates."""
r"""
Give the expected traces :math:`|Tr(U \cdot U_{target}^{\dag})|` for different number of
basis gates.
"""
# Future gotcha: extending this to non-supercontrolled basis.
# Careful: closest distance between a1,b1,c1 and a2,b2,c2 may be between reflections.
# This doesn't come up if either c1==0 or c2==0 but otherwise be careful.
Expand All @@ -1042,21 +1050,29 @@ def traces(self, target):

@staticmethod
def decomp0(target):
"""Decompose target ~Ud(x, y, z) with 0 uses of the basis gate.
Result Ur has trace:
:math:`|Tr(Ur.Utarget^dag)| = 4|(cos(x)cos(y)cos(z)+ j sin(x)sin(y)sin(z)|`,
which is optimal for all targets and bases"""
r"""
Decompose target :math:`~Ud(x, y, z)` with 0 uses of the basis gate.
Result :math:`Ur` has trace:
.. math::
|Tr(Ur.U_{target}^{\dag})| = 4|(cos(x)cos(y)cos(z)+ j sin(x)sin(y)sin(z)|
which is optimal for all targets and bases
"""

U0l = target.K1l.dot(target.K2l)
U0r = target.K1r.dot(target.K2r)
return U0r, U0l

def decomp1(self, target):
"""Decompose target ~Ud(x, y, z) with 1 uses of the basis gate ~Ud(a, b, c).
Result Ur has trace:
r"""
Decompose target :math:`~Ud(x, y, z)` with 1 uses of the basis gate :math:`~Ud(a, b, c)`.
Result :math:`Ur` has trace:
.. math::
|Tr(Ur.Utarget^dag)| = 4|cos(x-a)cos(y-b)cos(z-c) + j sin(x-a)sin(y-b)sin(z-c)|
|Tr(Ur.U_{target}^{\dag})| = 4|cos(x-a)cos(y-b)cos(z-c) + j sin(x-a)sin(y-b)sin(z-c)|
which is optimal for all targets and bases with z==0 or c==0"""
# FIXME: fix for z!=0 and c!=0 using closest reflection (not always in the Weyl chamber)
Expand All @@ -1068,18 +1084,20 @@ def decomp1(self, target):
return U1r, U1l, U0r, U0l

def decomp2_supercontrolled(self, target):
"""Decompose target ~Ud(x, y, z) with 2 uses of the basis gate.
r"""
Decompose target :math:`~Ud(x, y, z)` with 2 uses of the basis gate.
For supercontrolled basis :math:`~Ud(\pi/4, b, 0)`, all b, result :math:`Ur` has trace
For supercontrolled basis ~Ud(pi/4, b, 0), all b, result Ur has trace
.. math::
|Tr(Ur.Utarget^dag)| = 4cos(z)
|Tr(Ur.U_{target}^{\dag})| = 4cos(z)
which is the optimal approximation for basis of CNOT-class ``~Ud(pi/4, 0, 0)``
or DCNOT-class ``~Ud(pi/4, pi/4, 0)`` and any target.
which is the optimal approximation for basis of CNOT-class :math:`~Ud(\pi/4, 0, 0)`
or DCNOT-class :math:`~Ud(\pi/4, \pi/4, 0)` and any target.
May be sub-optimal for b!=0 (e.g. there exists exact decomposition for any target using B
``B~Ud(pi/4, pi/8, 0)``, but not this decomposition.)
This is an exact decomposition for supercontrolled basis and target ``~Ud(x, y, 0)``.
:math:`B \sim Ud(\pi/4, \pi/8, 0)`, but not this decomposition.)
This is an exact decomposition for supercontrolled basis and target :math:`~Ud(x, y, 0)`.
No guarantees for non-supercontrolled basis.
"""

Expand All @@ -1093,9 +1111,11 @@ def decomp2_supercontrolled(self, target):
return U2r, U2l, U1r, U1l, U0r, U0l

def decomp3_supercontrolled(self, target):
"""Decompose target with 3 uses of the basis.
This is an exact decomposition for supercontrolled basis ~Ud(pi/4, b, 0), all b,
and any target. No guarantees for non-supercontrolled basis."""
r"""
Decompose target with 3 uses of the basis.
This is an exact decomposition for supercontrolled basis :math:`~Ud(\pi/4, b, 0)`, all b,
and any target. No guarantees for non-supercontrolled basis.
"""

U0l = target.K1l.dot(self.u0l)
U0r = target.K1r.dot(self.u0r)
Expand Down Expand Up @@ -1572,3 +1592,16 @@ def num_basis_gates(self, unitary):


two_qubit_cnot_decompose = _LazyTwoQubitCXDecomposer()
"""
This is an instance of :class:`.TwoQubitBasisDecomposer` that always uses
``cx`` as the KAK gate for the basis decomposition. You can use this function
as a quick access to ``cx``-based 2-qubit decompositions.
Args:
unitary (Operator or np.ndarray): The 4x4 unitary to synthesize.
basis_fidelity (float or None): If given the assumed fidelity for applications of :class:`.CXGate`.
approximate (bool): If ``True`` approximate if ``basis_fidelity`` is less than 1.0.
Returns:
QuantumCircuit: The synthesized circuit of the input unitary.
"""
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
---
features:
- |
A new class, :class:`~.TwoQubitWeylDecomposition`, has been added to :mod:`qiskit.synthesis`.
This class allows to apply the Weyl decomposition of two-qubit unitaries.
upgrade:
- |
Removed the ``qiskit.quantum_info.synthesis`` module, which has been deprecated since the 0.46 release.
Expand All @@ -11,3 +16,7 @@ upgrade:
This function was removed, since it has already been deprecated in the 0.46 release:
* ``cnot_rxx_decompose``
These functions were removed, since they have already been deprecated in a previous release:
* ``decompose_clifford`` (use :func:`.qiskit.synthesis.synth_clifford_full` instead)
* ``decompose_cnotdihedral`` (use :func:`.qiskit.synthesis.synth_cnotdihedral_full` instead)

0 comments on commit 30646af

Please sign in to comment.