PennyLane
Install
Install
  1. Compilation/
  2. Diagonal unitary decomposition

Diagonal unitary decomposition

OverviewDetailsResources

Diagonal unitary operators appear in various compilation scenarios, most frequently in the context of unitary synthesis and state preparation.

To decompose a diagonal unitary operator (without auxiliary qubits), we may use a recursive decomposition into multiplexed R_Z gates, which is called SelectPauliRot in PennyLane. This is shown in [1], citing earlier work [2]:

Recursive decomposition of a diagonal unitary operator into multiplexed rotations SelectPauliRot

Here, \Delta denotes a diagonal operator and the half-filled control nodes indicate that the R_Z rotations are controlled uniformly, or multiplexed. See the Select-U(2) compilation page for a decomposition of these multiplexers.

Alternatively, if we have a clean auxiliary qubit at our disposal, we may leverage phase kickback for a decomposition into a single multiplexed R_Z gate, or SelectPauliRot:

Decomposition of a diagonal unitary operator into a single multiplexed rotation SelectPauliRot via phase kickback

This decomposition is particularly useful in the context of phase gradient decompositions, because decomposing a single SelectPauliRot that has one more control is notably cheaper (one 2^n-entry QROM and one adder),in this framework than decomposing n-1 smaller multiplexers and an R_Z rotation (n-1 QROMs with 2^{n-1}, 2^{n-2}\dots 2 entries and n adders).

Inputs

  • Diagonal unitary with length N=2^n for n qubits.

Outputs

  • Circuit consisting either of
    • n-1 multiplexed R_Z rotations (qp.SelectPauliRot), one each with k controls for 1\leq k\leq n-1,
    • 1 single-qubit R_Z rotation (qp.RZ), and
    • a global phase (qp.GlobalPhase). or, if we have a clean auxiliary qubit available,
    • a single multiplexed R_Z rotation (qp.SelectPauliRot) with n controls.

Example

Consider a diagonal unitary operator acting on three qubits, consisting of some random phases:

import pennylane as qp
import numpy as np
qp.decomposition.enable_graph()
n = 3
wires = list(range(n))
np.random.seed(211)
D = np.exp(1j * np.random.random(2**n))

@qp.qnode(qp.device("reference.qubit", wires=n))
def qnode():
    qp.DiagonalQubitUnitary(D, wires=wires)
    return qp.expval(qp.X(0))
>>> print(qp.draw(qnode, show_matrices=False)())
0: ─╭U(M0)─┤  <X>
1: ─├U(M0)─┤     
2: ─╰U(M0)─┤     

We can decompose the DiagonalQubitUnitary iteratively, which we can control via the max_expansion keyword argument of qp.decompose. In the first step, we obtain a SelectPauliRot gate with two controls and a two-qubit diagonal unitary operator:

gate_set = {"SelectPauliRot", "RZ", "GlobalPhase"}
dec_node = qp.transforms.decompose(qnode, gate_set=gate_set, max_expansion=1)
>>> print(qp.draw(dec_node, show_matrices=False)())
0: ─╭U(M0)─╭SelectPauliRot(M1)─┤  <X>
1: ─╰U(M0)─├SelectPauliRot(M1)─┤     
2: ────────╰SelectPauliRot(M1)─┤     

In the next step, the two-qubit diagonal unitary operator is decomposed into a single-qubit diagonal unitary and a SelectPauliRot gate with one control:

dec_node = qp.transforms.decompose(qnode, gate_set=gate_set, max_expansion=2)
>>> print(qp.draw(dec_node, show_matrices=False)())
0: ──U(M0)─╭SelectPauliRot(M1)─╭SelectPauliRot(M2)─┤  <X>
1: ────────╰SelectPauliRot(M1)─├SelectPauliRot(M2)─┤     
2: ────────────────────────────╰SelectPauliRot(M2)─┤     

In the last step, the single-qubit diagonal unitary is broken up into a global phase and a qp.RZ gate:

dec_node = qp.transforms.decompose(qnode, gate_set=gate_set, max_expansion=3)
>>> print(qp.draw(dec_node, show_matrices=False)())
0: ─╭GlobalPhase(-0.69)──RZ(0.14)─╭SelectPauliRot(M0)─╭SelectPauliRot(M1)─┤  <X>
1: ─├GlobalPhase(-0.69)───────────╰SelectPauliRot(M0)─├SelectPauliRot(M1)─┤     
2: ─╰GlobalPhase(-0.69)───────────────────────────────╰SelectPauliRot(M1)─┤ 

If we instead provide a (zeroed) work wire to decompose, we see the decomposition into a single multiplexer instead:

dec_node = qp.transforms.decompose(qnode, gate_set=gate_set, num_work_wires=1)
>>> print(qp.draw(dec_node, show_matrices=False)())
            0: ───────────╭SelectPauliRot(M0)─────────────┤  <X>
            1: ───────────├SelectPauliRot(M0)─────────────┤     
            2: ───────────├SelectPauliRot(M0)─────────────┤     
<DynamicWire>: ──Allocate─╰SelectPauliRot(M0)──Deallocate─┤  

Typical usage

This decomposition can be useful as part of a compilation routine that splits off diagonal operators to commute them through control/multiplexing structures. It is also used implicitly in MottonenStapePreparation, both in a version with qp.RY rotations to create the correct amplitudes, and as seen above with qp.RZ rotations to apply phases.

As a concrete example, Select operators with a single target qubit can be decomposed using arbitrary diagonal unitary operators; for details, see the Select-U(2) compilation page. In turn, this page will also show how to decompose SelectPauliRot obtained from the decomposition presented here.

References

[1] "Synthesis of quantum logic circuits", Vivek Shende, Stephen Bullock, Igor Markov, quant-ph/0406176, 2004.

[2] "Smaller circuits for arbitrary n-qubit diagonal computations", Stephen S. Bullock, Igor L. Markov, quant-ph/0303039, 2003.

Cite this page

@misc{PennyLane-diagonal-unitary-decomp,
    title = "Diagonal unitary decomposition",
    author = "David Wierichs",
    year = "2025",
    howpublished = "\url{https://pennylane.ai/compilation/diagonal-unitary-decomp}"
}

Page author(s)

David Wierichs
David Wierichs

David Wierichs

I like to think about representations and compilation of quantum programs, and I enjoy coding up research ideas and useful features for anyone to use in PennyLane.

PennyLane

PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Built by researchers, for research. Created with ❤️ by Xanadu.

Research

  • Research
  • Performance
  • Hardware & Simulators
  • Demos
  • Compilation Hub
  • Quantum Datasets

Education

  • Teach
  • Learn
  • Codebook
  • Coding Challenges
  • Videos
  • Glossary

Software

  • Install PennyLane
  • Features
  • Documentation
  • Catalyst Compilation Docs
  • Development Guide
  • API
  • GitHub
Stay updated with our newsletter

© Copyright 2026 | Xanadu | All rights reserved

TensorFlow, the TensorFlow logo and any related marks are trademarks of Google Inc.

Privacy Policy|Terms of Service|Cookie Policy|Code of Conduct