Skip to content

[Demo] Add: Unitary synthesis with recursive KAK decompositions #1372

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

Open
wants to merge 38 commits into
base: master
Choose a base branch
from

Conversation

dwierichs
Copy link
Contributor

@dwierichs dwierichs commented May 19, 2025

Title:
Unitary synthesis with recursive KAK decompositions

Summary:
Unitary synthesis, the process of compiling a unitary matrix into a quantum circuit, has been a topic of study for over
25 years. Three interesting techniques for unitary synthesis, namely the first, the (probably) most widely known, and the minimal-CNOT-count ("the best"), all use the same recursive KAK decomposition under the hood, as we recently
elaborated in a paper.

This demo builds on our demo on KAK decompositions (added in #1227) and explains how such decompositions can be applied recursively to construct a many-qubit circuit and how the three mentioned techniques all are powered by the same recursion.
It defers some optimization details to the literature but provides a brief gate count overview and showcases a simplified numerical implementation of all three techniques.

Relevant references:
Quite a few, see metadata file.

Possible Drawbacks:
N/A

Related GitHub Issues:

[sc-87548]

To do

  • Redo circuit diagrams
  • Add .tex files to repo
  • add examples/code

If you are writing a demonstration, please answer these questions to facilitate the marketing process.

  • GOALS — Why are we working on this now?
  • AUDIENCE — Who is this for?
  • Researchers in quantum compilation and quantum information
  • Practicioners that want to learn where the complexity of their operations comes from
  • KEYWORDS — What words should be included in the marketing post?
  • (Recursive) KAK decomposition
  • Quantum Shannon decomposition
  • Block-ZXZ decomposition
  • Unitary synthesis
  • Compilation
  • Which of the following types of documentation is most similar to your file?
    (more details here)
  • Tutorial
  • Demo
  • How-to

@dwierichs dwierichs marked this pull request as draft May 19, 2025 13:40
Copy link

👋 Hey, looks like you've updated some demos!

🐘 Don't forget to update the dateOfLastModification in the associated metadata files so your changes are reflected in Glass Onion (search and recommendations).

Please hide this comment once the field(s) are updated. Thanks!

@dwierichs dwierichs requested a review from Qottmann May 20, 2025 12:24
@dwierichs dwierichs marked this pull request as ready for review May 20, 2025 12:33
Copy link

github-actions bot commented May 20, 2025

Thank you for opening this pull request.

You can find the built site at this link.

Deployment Info:

  • Pull Request ID: 1372
  • Deployment SHA: 71803400d5ef3c960f0ecbfb37ed6c764bd15fe3
    (The Deployment SHA refers to the latest commit hash the docs were built from)

Note: It may take several minutes for updates to this pull request to be reflected on the deployed site.

Copy link
Collaborator

@Qottmann Qottmann left a comment

Choose a reason for hiding this comment

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

First look and this looks great, learned a lot !

I think in the first part some numerical or analytic examples could help

I dont know how much you planned for the gate counts, stating the logic of counting and then the results should be sufficient imo but feel free to go wild 😆 🚀

Comment on lines +43 to +46
.. figure:: ../_static/demonstration_assets/unitary_synthesis_kak/KAK_generic.png
:align: center
:width: 70%
:alt: Quantum circuit of a generic KAK decomposition.
Copy link
Collaborator

Choose a reason for hiding this comment

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

any idea whats with the vertical line?
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is a screenshot artifact :D Sorry!

I'll redo all the circuit diagrams for sure, because something will change, so they are a bit in rough shape here and there 😬

@dwierichs dwierichs requested review from Qottmann May 22, 2025 16:00
@Qottmann
Copy link
Collaborator

No preview :(

TypeError: In-place matrix multiplication is not (yet) supported. Use 'a = a @ b' instead of 'a @= b'.

Copy link
Collaborator

@Qottmann Qottmann left a comment

Choose a reason for hiding this comment

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

My main remaining concern is the Khaneja Glaser section which I'd consider hard to parse for a non-expert audience. My suggestion would be to make it as explicit as possible without bloating the section too much.

Comment on lines 220 to 234
# Qubit count
n = 5
# Get a random unitary on n qubits
U = unitary_group.rvs(2**n, random_state=214)
# Compute AIII decomposition
(u_1, u_2), theta, (v_1, v_2) = aiii_decomposition(U)

# Check that the decomposition is valid
zero = np.zeros_like(u_1)
K_1 = np.block([[u_1, zero], [zero, u_2]])
A = qml.matrix(qml.Select([qml.RY(2 * th, 0) for th in theta], control=range(1, n)), wire_order=range(n))
K_2 = np.block([[v_1, zero], [zero, v_2]])

reconstructed_U = K_1 @ A @ K_2
print(np.allclose(reconstructed_U, U))
Copy link
Collaborator

Choose a reason for hiding this comment

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

super neat example!!!! 🚀

Comment on lines 379 to 389
U_1, phi, U_2 = demultiplex(u_1, u_2)

######################################################################
# The demultiplexed matrices make up :math:`K_1=u_1\oplus u_2` from above:
#

demultiplex_A = qml.matrix(qml.Select([qml.RZ(-2 * p, 0) for p in phi], control=range(1, n)), wire_order=range(n))
demultiplex_K_1 = np.block([[U_1, zero], [zero, U_1]])
demultiplex_K_2 = np.block([[U_2, zero], [zero, U_2]])
reconstructed_K_1 = demultiplex_K_1 @ demultiplex_A @ demultiplex_K_2
print(np.allclose(reconstructed_K_1, K_1))
Copy link
Collaborator

Choose a reason for hiding this comment

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

super neat 👌

# via :math:`E` on the last two qubits, and from the Pauli :math:`Z` into the Pauli :math:`X` basis
# on the :math:`n-3` remaining qubits.
#
# For the second part in each recursion step, the type-A Cartan decomposition, the Cartan subalgebra
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the recursion steps are not necessarily clear to all readers, would also make it more explicit what the second part in each recursion step refers to

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I rephrased this a bit. In particular, I try not to mention the "two parts" of a recursion step any longer, because this only makes sense in the context of the original paper, but not so much in the phrasing of the demo.

Comment on lines +527 to +530
# .. figure:: ../_static/demonstration_assets/unitary_synthesis_kak/recursive_QSD.png
# :align: center
# :width: 100%
# :alt: Recursively applied Quantum Shannon decomposition of the unitary group on n qubits.
Copy link
Collaborator

Choose a reason for hiding this comment

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

on the rhs, the lower wire misses the n-2 collection symbol

Copy link
Collaborator

Choose a reason for hiding this comment

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

now it has a gray line on the left 🥲

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be fixed.

Comment on lines +512 to +513
# beginning. They are captured by multiplexed single-qubit rotations about the Pauli :math:`Y`
# axis and Pauli :math:`Z` axis, respectively, with the first of the qubits (on which the recursion
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is it pauli Y and pauli Z alternating? is that because cant do the same twice? would be nice if you can mention why that is

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a brief comment that this results from the different Cartan subalgebras used in the two types of decompositions :) Is that the level you had in mind? :)

# each type-AIII Cartan decomposition. We showcase the transformation into the correct block
# structure in form of a circuit diagram:
#
# .. figure:: ../_static/demonstration_assets/unitary_synthesis_kak/block_zxz_transformations.png
Copy link
Collaborator

Choose a reason for hiding this comment

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

image missing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added, sorry about that!

Comment on lines 559 to 561
# Afterwards, the block-diagonal matrices can be decomposed with the type-A decompositions, and
# optimizations from the QSD can be applied, complemented by the merger of two CZ gates into
# the control structure of a multiplexer (see Sec. 5 of [#Krol_BlockZXZ]_).
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you show that with one or two circuit diagrams or would it bloat it too much? Images/diagrams are always easier to follow

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think on could do that, more or less reproducing Sec. 5.2 here. Would that be helpful? Unfortunately, it takes quite some drawing space and/or words to get those two gates to cancel 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Included in new gate counts appendix :)

Copy link
Collaborator

@Qottmann Qottmann left a comment

Choose a reason for hiding this comment

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

Great demo! 🚀

Still got some nitpicky comments here and there and would suggest to find a nicer conclusion for for the KG and BlockZXZ sections.

# \mathfrak{a}'_{\text{A}}(n)
# =\operatorname{span}_{i\mathbb{R}}
# \{Z\otimes \{\mathbb{I},X\}^{\otimes (n-3)}\otimes \mathcal{S}\}.
#
Copy link
Collaborator

Choose a reason for hiding this comment

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

The KG section still feels a bit incomplete, I think a quick sentence just stating the recursion steps would help conclude the section in a satisfying way. Perhaps also recap the $\mathfrak{k}$ (they are the same as above I guess?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a wrap-up circuit diagram that combines everything into one step that leads to n-1 qubit unitaries. I think that's also useful for the gate counting appendix. Let me know whether this is a sufficient conclusion! :)

Comment on lines 561 to 573
# .. adminition:: Implementing a multiplexed rotation
#
# As a subroutine, all decompositions require us to perform a multiplexed, also called
# uniformly controlled or Select-applied, single-qubit rotation, :math:`UCR_P(\vec{\theta})`
# about some Pauli word :math:`P`. A possible implementation of :math:`UCR_P` is given in
# the following circuit diagram:
#
# .. figure:: ../_static/demonstration_assets/unitary_synthesis_kak/multiplexer_decomp.png
# :align: center
# :width: 75%
# :alt: Decomposition of a multiplexer on 3 qubits.
#
# It takes :math:`2^k` CNOT gates and :math:`2^k` rotation gates for :math:`k` control qubits.
Copy link
Collaborator

Choose a reason for hiding this comment

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

this doesnt render 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typo: adminition 🤦 Sorry about that!

Copy link
Collaborator

Choose a reason for hiding this comment

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

still not seeing it in the latest preview 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

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

edit: ah you didnt update it yet, sorry :D

Comment on lines +527 to +530
# .. figure:: ../_static/demonstration_assets/unitary_synthesis_kak/recursive_QSD.png
# :align: center
# :width: 100%
# :alt: Recursively applied Quantum Shannon decomposition of the unitary group on n qubits.
Copy link
Collaborator

Choose a reason for hiding this comment

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

now it has a gray line on the left 🥲

Comment on lines +619 to +625
# Afterwards, the block-diagonal matrices can be decomposed with the type-A decompositions,
# which is done with the same generic technique as for the QSD.
# Crucially, the transformation above appears to simplify the circuit structure by turning
# some blocks into the identity, but this has no effect on the CNOT count. The enhanced
# optimization performed by Krol and Al-Ars also applies to the more generic form after the
# recursive Cartan decomposition
# (see our gate counts appendix and Sec. 5 of [#Krol_BlockZXZ]_).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bit unsatisfying conclusion in the sense that I'm not sure at this point what the section on its own is trying to convey? Does that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be honest, I am not sure how to improve this :D
The block-ZXZ decomp is the same as the QSD, except for a tiny optimization trick in the appendix, and all the block stuff is pretty unnecessary.
I reworded the paragraph a bit, do you have a suggestion how to make it more conclusive?

# c_n^{\text{QSD}} &= \frac{9}{16} 4^n - 3\cdot 2^{n-1},\\
# r_n^{\text{QSD}} &= \frac{21}{16} 4^n - 3\cdot 2^{n-1}.
#
# Shende et al. then perform the following two optimizations:
Copy link
Collaborator

Choose a reason for hiding this comment

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

citation after shende et al perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, also added the analogous spots for KG and Block-ZXZ 👍

# leaves the rotation count unchanged. The solution for the CNOT count is
# :math:`c_n = \tfrac{13}{24} 4^n -3\cdot 2^{n-1} + \tfrac{1}{3}`.
#
# Second, Shende et al. consider the stage at the recursion just before decomposing the two-qubit
Copy link
Collaborator

Choose a reason for hiding this comment

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

also a citation here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that would be a bit repetitive for my taste, no strong preference though.

Comment on lines 802 to 810
# Note that all two-qubit blocks in the QSD are separated by multiplexer controls, which commute
# with diagonal matrices. Therefore we can start at the right-most block, decompose it into the
# above form, and pull the diagonal :math:`\Delta` to the left to absorb it into the second two-qubit
# block from the right. This block can then be decomposed into the above form again, and the
# diagonal contribution can be merged into the third block from the right. Continuing this,
# we find that we can decompose all two-qubit blocks into 2 CNOTs and 14 rotation gates,
# except for the left-most block which is decomposed in the conventional manner into 3 CNOTs and
# 15 rotations. As there are :math:`4^{n-2}` two-qubit blocks, we save :math:`4^{n-2}-1`
# CNOTs and rotation gates, arriving at
Copy link
Collaborator

Choose a reason for hiding this comment

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

a bit hard to follow without circuit diagrams 😅 could at them since this is appendix anyway, but dont have to - gonna leave it up to you

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a figure :)

Comment on lines 893 to 903
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
for c_n, r_n, label, color in zip(all_c_n, all_r_n, labels, colors):
axs[0].plot(n, c_n / 4**n, label=label, color=color, lw=2)
ls = ":" if label in ["QSD", "Block-ZXZ"] else "-"
axs[1].plot(n, r_n / 4**n, label=label, color=color, ls=ls, lw=2)

ylabels = ["Number of CNOT gates ($/4^n$)", "Number of rotations ($/4^n$)"]
for ax, ylabel in zip(axs, ylabels):
ax.legend()
ax.set_xlabel("Number of qubits $n$")
ax.set_ylabel(ylabel)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nice touch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants