Skip to content

Commit 46b4e3d

Browse files
committed
finish gate counts. plot
1 parent 3447d73 commit 46b4e3d

File tree

1 file changed

+60
-12
lines changed

1 file changed

+60
-12
lines changed

demonstrations/tutorial_unitary_synthesis_kak.py

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -690,14 +690,9 @@ def demultiplex(U, V):
690690
# Appendix: Gate counts
691691
# ---------------------
692692
#
693-
# TODO: VERIFY ROTATION GATE COUNTS
694-
#
695693
# Before concluding, let us count the gates in the three decompositions we discussed above.
696694
# We will start with the decompositions as they are and then will comment on optimizations
697695
# that reduce the CNOT count further.
698-
# We will consider :math:`U(d)` even if the authors of the original decompositions worked with
699-
# :math:`SU(d)`, and we put the global phase into the very first Cartan subgroup of the
700-
# recursive decompositions.
701696
# We will denote the CNOT (rotation gate) cost for a special unitary on :math:`n` qubits as
702697
# :math:`c_n` (:math:`r_n`).
703698
#
@@ -712,7 +707,7 @@ def demultiplex(U, V):
712707
# part into three CNOTs and three rotation gates.
713708
# Overall, we thus have :math:`c_2^{\text{KG}}=3` and :math:`r_2^{\text{KG}}=15`.
714709
#
715-
# For an arbitrary number of qubits :math:`n`, they decompose one special unitary
710+
# For an arbitrary number of qubits :math:`n`, Khaneja and Glaser decompose one special unitary
716711
# :math:`U\in SU(2^{n})` into four special unitaries on :math:`n-1` qubits, three
717712
# :math:`(n-1)`-multiplexed :math:`R_Z` or :math:`R_X` rotations and a number of basis change
718713
# rotation gates. The multiplexers take :math:`2^{n-1}` CNOT gates and equally many rotation gates
@@ -760,8 +755,7 @@ def demultiplex(U, V):
760755
# Without any optimizations, the QSD decomposes a unitary on :math:`n` qubits into four unitaries
761756
# on :math:`n-1` qubits and three :math:`(n-1)`-multiplexed single-qubit :math:`R_Z`
762757
# and :math:`R_Y` rotations. That is, we find the same behaviour as for the Khaneja-Glaser
763-
# decomposition if we had merged the basis rotations into the unitary blocks that are decomposed
764-
# in the subsequent step.
758+
# decomposition if we had merged the basis rotations into the smaller unitary blocks.
765759
# Correspondingly, we get the recursion relation :math:`x_n=4 x_{n-1} +3\cdot 2^{n-1}` for both,
766760
# the CNOT count and the number of rotation gates. Using the optimal two-qubit decomposition then
767761
# leads to
@@ -781,7 +775,7 @@ def demultiplex(U, V):
781775
# :alt: Decomposition of a multiplexed RY rotation on 3 qubits using CZ gates.
782776
#
783777
# As the last ``CZ`` gate is (block) diagonal, it can be absorbed into the multiplexed
784-
# :math:`SU(2^{n-1})` operation on the right before decomposing it with the type-A decomposition.
778+
# :math:`SU(2^{n-1})` operation on the right, before decomposing it with the type-A decomposition.
785779
# This changes the recursion relation to :math:`c_n = 4 c_{n-1} + 3\cdot 2^{n-1} - 1` and
786780
# leaves the rotation count unchanged. The solution for the CNOT count is
787781
# :math:`c_n = \tfrac{13}{24} 4^n -3\cdot 2^{n-1} + \tfrac{1}{3}`.
@@ -795,9 +789,9 @@ def demultiplex(U, V):
795789
# :width: 75%
796790
# :alt: Decomposition of a two-qubit unitary with a diagonal and two CNOTs.
797791
#
798-
# Note that all two-qubit blocks are separated by multiplexer controls, which commute with
799-
# diagonal matrices. Therefore we can start at the right-most block, decompose it into the
800-
# above form, and pull the diagonal through to the left to absorb it into the second two-qubit
792+
# Note that all two-qubit blocks in the QSD are separated by multiplexer controls, which commute
793+
# with diagonal matrices. Therefore we can start at the right-most block, decompose it into the
794+
# above form, and pull the diagonal to the left to absorb it into the second two-qubit
801795
# block from the right. This block can then be decomposed into the above form again, and the
802796
# diagonal contribution can be merged into the third block from the right. Continuing this,
803797
# we find that we can decompose all two-qubit blocks into 2 CNOTs and 14 rotation gates,
@@ -845,6 +839,60 @@ def demultiplex(U, V):
845839
#
846840
# c_n^{\text{ZXZ,opt}} &= \tfrac{22}{48} 4^n -3\cdot 2^{n-1} + \tfrac{5}{3},\\
847841
# r_n^{\text{ZXZ,opt}} &= \tfrac{5}{4} 4^n - 3\cdot 2^{n-1} + 1.
842+
#
843+
# We note once more that the transformation into the special block form that is eponymous to
844+
# the Block-ZXZ decomposition does not yield any CNOT or rotation gate reductions. This is
845+
# because the controlled gates obtained through the transformation are decomposed like
846+
# multiplexers, leading to the same cost as before the transformation.
847+
#
848+
# To conclude, we plot the CNOT and rotation gate counts, comparing them to their respective
849+
# lower bounds
850+
#
851+
# .. math::
852+
#
853+
# c_n^{\text{min}} &= \tfrac{1}{4} (4^n - 3n - 1),\\
854+
# r_n^{\text{min}} &= 4^n - 1.
855+
#
856+
# The former is derived by counting the independent degrees of freedom one can add per CNOT
857+
# gate in a circuit and equating this with the dimension of the group, :math:`4^n-1`.
858+
# This dimension then also is the lower bound for the rotation count.
859+
#
860+
861+
n = np.arange(2, 11)
862+
c_n_KG = 11/16 * 4 ** n - 3 * 2**(n-1) - 2
863+
r_n_KG = 21/16 * 4 ** n - 3 * 2**(n-1)
864+
865+
c_n_QSD = 9/16 * 4 ** n - 3 * 2**(n-1)
866+
r_n_QSD = 21/16 * 4 ** n - 3 * 2**(n-1)
867+
868+
c_n_QSD_opt = 23/48 * 4 ** n - 3 * 2**(n-1) + 4/3
869+
r_n_QSD_opt = 5 / 4 * 4 ** n - 3 * 2**(n-1) + 1
870+
871+
c_n_ZXZ = 11/24 * 4 ** n - 3 * 2**(n-1) + 5/3
872+
r_n_ZXZ = 5 / 4 * 4 ** n - 3 * 2**(n-1) + 1
873+
874+
c_n_min = 1/4 * (4**n - 3 * n - 1)
875+
r_n_min = 4**n - 1
876+
877+
all_c_n = [c_n_KG, c_n_QSD, c_n_QSD_opt, c_n_ZXZ, c_n_min]
878+
all_r_n = [r_n_KG, r_n_QSD, r_n_QSD_opt, r_n_ZXZ, r_n_min]
879+
labels = ["KG", "QSD", "QSD, opt", "Block-ZXZ", "Lower bound"]
880+
colors = ["#DE8900", "#4D53C8", "#44ACE8", "#D7333B", "#007D33"]
881+
882+
883+
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
884+
for c_n, r_n, label, color in zip(all_c_n, all_r_n, labels, colors):
885+
axs[0].plot(n, c_n / 4**n, label=label, color=color, lw=2)
886+
ls = ":" if label in ["QSD", "Block-ZXZ"] else "-"
887+
axs[1].plot(n, r_n / 4**n, label=label, color=color, ls=ls, lw=2)
888+
889+
ylabels = ["Number of CNOT gates ($/4^n$)", "Number of rotations ($/4^n$)"]
890+
for ax, ylabel in zip(axs, ylabels):
891+
ax.legend()
892+
ax.set_xlabel("Number of qubits $n$")
893+
ax.set_ylabel(ylabel)
894+
895+
######################################################################
848896
#
849897
# About the author
850898
# ----------------

0 commit comments

Comments
 (0)