PennyLane
Install
Install
  1. Compilation/
  2. Two-qubit Synthesis

Two-qubit Synthesis

OverviewDetailsResources

Two-qubit gate synthesis takes in a 4 \times 4 unitary matrix U and synthesizes a circuit with an optimal \text{CNOT} gate count. This pass is implemented as qp.ops.two_qubit_decomposition in PennyLane, which can also be accessed via qp.transforms.unitary_to_rot.

The resulting circuits can take one of the following four forms, depending on how many CNOT gates are necessary.

CNOTs0123
Circuit -A-
-B-
-C--╭●--A-
-D--╰X--B-
-A--╭X--RZ(α)--╭X--C-
-B--╰●--RX(β)--╰●--D-
-C--╭X--RZ(δ)--╭●---------╭X--A-
-D--╰●--RY(β)--╰X--RY(α)--╰●--B-

A, B, C, D \in \text{SU}(2) are general single-qubit gates and, together with the angles \alpha, \beta, \delta \in \mathbb{R}, can be determined algebraically directly from the input matrix U (the exact process is described in the details tab).

In a NISQ setting, where costs for CNOT gates dominate, applying this pass to two-qubit gates may reduce the overall CNOT gate count.

Inputs

  • Unitary matrix U \in \mathbb{C}^{4\times 4}

Outputs

  • Decomposed circuit with an optimal \text{CNOT} gate count

Example

A tensor product of two operators requires 0 CNOT gates.

import pennylane as qp
import numpy as np

U = qp.matrix(qp.exp(-1j * 0.5 * qp.X(0)), wire_order=range(2))
U @= qp.matrix(qp.exp(-1j * 0.5 * qp.Y(1)), wire_order=range(2))

@qp.transforms.unitary_to_rot
@qp.qnode(qp.device("default.qubit"))
def circuit(U):
    qp.QubitUnitary(U, wires=range(2))
    return qp.expval(qp.Z(0)), qp.expval(qp.Z(1))
>>> print(qp.draw(circuit, level="device")(U))
0: ──RZ(1.57)──RY(1.00)──RZ(11.00)─┤  <Z>
1: ──RZ(0.00)──RY(1.00)──RZ(0.00)──┤  <Z>

Note that a ZYZ decomposition is used to represent the \text{SU}(2) gate on each wire. This leads to a redundancy because the input matrix on wire 1 is already a R_Y rotation.

A general \text{SU}(4) element requires 3 CNOT gates.

>>> SU4_generators = list(qp.pauli.pauli_group(2))
>>> arbitrary_op = qp.dot(np.arange(16), SU4_generators)
>>> U = qp.matrix(qp.exp(-1j * arbitrary_op))
>>> print(qp.draw(circuit, level="device", max_length=80)(U))
0: ──RZ(11.64)──RY(1.86)──RZ(2.97)─╭X──RZ(-0.03)─╭●───────────╭X──RZ(11.57) ···
1: ──RZ(11.68)──RY(2.10)──RZ(5.12)─╰●──RY(1.42)──╰X──RY(1.69)─╰●──RZ(0.04)─ ···

0: ··· ──RY(1.30)──RZ(0.81)─╭GlobalPhase(-0.79)─┤  <Z>
1: ··· ──RY(1.07)──RZ(7.03)─╰GlobalPhase(-0.79)─┤  <Z>

There are intermediate cases with 1 and 2 CNOTs, respectively. Here is an example where 2 CNOT gates are required.

>>> U = qp.matrix(qp.exp(-1j * 0.5 * qp.X(0) @ qp.Y(1)), wire_order=range(2))
>>> print(qp.draw(circuit, level="device")(U))
0: ──RZ(12.57)──RY(1.57)──RZ(11.00)─╭X──RZ(1.00)─╭X──RZ(11.00)──RY(1.57)──RZ(3.14)──┤  <Z>
1: ──RZ(11.00)──RY(1.57)──RZ(1.57)──╰●──RX(0.00)─╰●──RZ(1.57)───RY(1.57)──RZ(11.00)─┤  <Z>

While the CNOT count is optimal, the overall decomposition may not be. This is because this pass always targets a fixed ansatz structure and may introduce some redundant single-qubit gates, as illustrated by this example.

def circ():
    qp.CNOT((0, 1))
    qp.RX(0.5, 0)
    qp.RY(0.5, 1)

>>> print(qp.draw(circ)()) # input circuit
0: ─╭●──RX(0.50)─┤
1: ─╰X──RY(0.50)─┤
>>> U = qp.matrix(circ, wire_order=range(2))()
>>> assert np.allclose(U, qp.matrix(circuit, wire_order=range(2))(U))
>>> print(qp.draw(circuit, level="device", max_length=100)(U)) # synthesized circuit
0: ──RZ(12.14)──RY(0.00)──RZ(11.46)─╭●──RZ(3.10)───RY(0.50)──RZ(11.00)─┤  <Z>
1: ──RZ(1.57)───RY(1.79)──RZ(11.00)─╰X──RZ(11.51)──RY(1.76)──RZ(1.68)──┤  <Z>

Typical usage

This pass is necessary when the input unitary is provided as a matrix rather than a gate decomposition. In a NISQ setting, where costs for CNOT gates dominate, applying this pass to two-qubit gates may reduce the overall CNOT gate count.

References

[1] "Minimal Universal Two-qubit Quantum Circuits", Vivek V. Shende, Igor L. Markov, Stephen S. Bullock, arxiv:quant-ph/0308033, 2003

[2] "Recognizing Small-Circuit Structure in Two-Qubit Operators and Timing Hamiltonians to Compute Controlled-Not Gates", Vivek V. Shende, Stephen S. Bullock, Igor L. Markov, arXiv:quant-ph/0308045, 2003

[3] "Finding Small Two-Qubit Circuits", Vivek V. Shende, Igor L. Markov, and Stephen S. Bullock, preprint, 2004

Cite this page

@misc{PennyLane-2QSynthesis,
    title = "Two-qubit Synthesis",
    author = "Korbinian Kottmann",
    year = "2025",
    howpublished = "\url{https://pennylane.ai/compilation/two-qubit-synthesis}"
}

Page author(s)

Korbinian Kottmann
Korbinian Kottmann

Korbinian Kottmann

Korbinian likes simulating quantum systems, whether it be via tensor network methods during his PhD, or with quantum computers at Xanadu. Currently, he works on quantum compilation to make simulation algorithms go brrr.

Never miss a milestone

Get the latest quantum updates delivered to your inbox.

Join the list
PennyLane

PennyLane is an open-source quantum software platform for quantum computing, quantum machine learning, and quantum chemistry. Create meaningful quantum algorithms, from inspiration to implementation.

Created with ❤️ by Xanadu.

Research

  • Research

  • Performance

  • Hardware and simulators

  • Demos library

  • Compilation hub

  • Quantum datasets

Education

  • Teach

  • Learn

  • Codebook

  • Coding challenges

  • Videos

  • Glossary

Software

  • Install

  • Features

  • PennyLane documentation

  • Catalyst documentation

  • Development guide

  • How-to guides

  • API

  • GitHub


Xanadu

© Copyright 2026 | Xanadu | All rights reserved

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

Privacy policyTerms of serviceCookies policyCode of conduct