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.

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 * X(0)), wire_order=range(2))
U @= qp.matrix(qp.exp(-1j * 0.5 * Y(1)), wire_order=range(2))
ops = qp.ops.two_qubit_decomposition(U, range(2))
circ = qp.tape.QuantumScript(ops, [])
assert np.allclose(U, qp.matrix(circ, wire_order=range(2)))

print(qp.drawer.tape_text(circ))
0: ──RZ──RY──RZ─┤  
1: ──RZ──RY──RZ─┤  

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))

ops = qp.ops.two_qubit_decomposition(U, range(2))
circ = qp.tape.QuantumScript(ops, [])

print(qp.drawer.tape_text(circ))
0: ──RZ──RY──RZ─╭X──RZ─╭●─────╭X──RZ──RY──RZ─┤  
1: ──RZ──RY──RZ─╰●──RY─╰X──RY─╰●──RZ──RY──RZ─┤ 

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 * X(0) @ Y(1)), wire_order=range(2))
ops = qp.ops.two_qubit_decomposition(U, range(2))
circ = qp.tape.QuantumScript(ops, [])
assert np.allclose(U, qp.matrix(circ, wire_order=range(2)))

print(qp.drawer.tape_text(circ))
0: ──RZ──RY──RZ─╭X──RZ─╭X──RZ──RY──RZ─┤  
1: ──RZ──RY──RZ─╰●──RX─╰●──RZ──RY──RZ─┤ 

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)

>>> U = qp.matrix(circ, wire_order=range(2))()
>>> print("Input circuit:\n")
>>> print(qp.draw(circ)(), "\n")
Input circuit:

0: ─╭●──RX(0.50)─┤  
1: ─╰X──RY(0.50)─┤   

>>> ops = qp.ops.two_qubit_decomposition(U, range(2))
>>> circ_re = qp.tape.QuantumScript(ops, [])
>>> assert np.allclose(U, qp.matrix(circ_re, wire_order=range(2)))

>>> print("Synthesized circuit:\n")
>>> print(qp.drawer.tape_text(circ_re, decimals=2))
Synthesized circuit:

0: ──RZ(2.01)──RY(0.00)──RZ(9.44)─╭●──RZ(8.96)──RY(0.50)──RZ(11.00)─┤  
1: ──RZ(4.71)──RY(1.02)──RZ(1.57)─╰X──RZ(1.00)──RY(1.09)──RZ(11.28)─┤ 

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.

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