Exact CNOT and Depth Minimization with Breadth-First Search
Every gate on a real quantum device adds noise, and every extra layer of depth gives qubits more time to decohere. Quantum circuit compilation and synthesis is the craft of rewriting a circuit into an equivalent one that is as small and shallow as possible. In its exact form it is a discrete, combinatorial optimization problem.
In this post we solve a complete, concrete instance. We synthesize every linear reversible circuit on 4 qubits (all 20,160 of them) with the provably minimum number of CNOT gates and the provably minimum depth. We then compare the optimum against a standard heuristic, Gauss–Jordan elimination.
1. The problem
A circuit built only from CNOT gates acts linearly on computational basis states over the field $\mathbb{F}_2$:
$$
|x_1, \dots, x_n\rangle ;\mapsto; |M\mathbf{x}\rangle, \qquad M \in GL(n, \mathbb{F}_2).
$$
A $\mathrm{CNOT}(c \to t)$ gate is the elementary row operation
$$
x_t \leftarrow x_t \oplus x_c .
$$
Applying the gates $g_1, \dots, g_k$ in sequence multiplies the matrix by the corresponding elementary matrices:
$$
M(C) = E_{g_k} \cdots E_{g_2} E_{g_1}.
$$
For $n=4$ the number of distinct circuits is the order of the group

Problem A (gate count). Given a target matrix $M$, find a gate sequence $C$ solving
$$
\min_{C} ; |C| \quad \text{subject to} \quad M(C) = M .
$$
Problem B (depth). Let $L$ be a layer of CNOTs acting on pairwise disjoint qubits, and let $\mathcal{L}$ be the set of all such layers. Find
$$
\min_{(L_1,\dots,L_d)} ; d \quad \text{subject to} \quad M(L_d)\cdots M(L_1) = M, \quad L_i \in \mathcal{L}.
$$
Both are shortest-path problems on the Cayley graph of $GL(4,\mathbb{F}_2)$. The vertices are the 20,160 matrices, and the edges are CNOT gates (Problem A) or parallel CNOT layers (Problem B). Because the graph is small, breadth-first search (BFS) from the identity gives the exact optimum for every target matrix at once.
As a baseline, we implement the classical Gauss–Jordan heuristic, which reduces $M$ to the identity row by row. Its worst-case gate count is $O(n^2)$, but it is not optimal.
2. Source code
1 | import itertools |
3. Code walkthrough
Representing a circuit as a tuple of integers
Each row of $M$ is stored as a 4-bit integer, and the whole matrix is a tuple of four integers. This makes the matrix hashable, so it can be a dictionary key, and a CNOT becomes a single XOR:
1 | rows[t] ^= rows[c] |
The identity matrix is (1, 2, 4, 8). Representing matrices as bit masks instead of NumPy arrays is the main speed optimization. Each BFS expansion costs a few integer operations rather than a matrix multiplication plus conversion to a hashable key, so both exhaustive searches finish in a fraction of a second. No further acceleration is needed.
Two search spaces, one BFS
The function bfs(moves) is a plain breadth-first search from the identity. The only difference between the two optimization problems is the move set:
GATE_MOVEScontains the 12 single CNOT gates $(c,t)$ with $c \neq t$. BFS distance equals the minimum gate count.LAYER_MOVEScontains the 12 single gates plus all pairs of CNOTs acting on four distinct qubits, which can run simultaneously. There are 12 such pairs: 3 ways to split four qubits into two pairs, times 2 orientations for each pair. BFS distance equals the minimum depth.
Because every edge has unit cost, the first time BFS reaches a matrix is guaranteed to be via a shortest path. The parent dictionary stores a back-pointer, so reconstruct recovers an explicit optimal circuit for any target.
Greedy baseline
greedy_synth is the Gauss–Jordan procedure. For each column $i$ it makes sure the pivot $M_{ii}$ is 1, swapping in a row below through an XOR if necessary, and then clears every other 1 in that column. Each row operation is a CNOT. The recorded operations reduce $M$ to the identity, so reversing the list gives a circuit that builds $M$. The function asserts the reduction succeeded.
Self-verification
The code is written to be self-checking:
assert len(all_mats) == 20160confirms BFS reached exactly the order of the group.simulatereplays any gate list, and the assertions confirm each synthesized circuit reproduces its target matrix.- The reconstructed optimal circuits for the first 200 matrices are replayed in the same way, for both the gate-count and depth searches.
Depth of a gate list
circuit_depth schedules gates as early as possible. A gate placed on qubits $c$ and $t$ lands one level after the later of the two qubits’ current levels, and the circuit depth is the maximum level reached.
4. Console output
BFS finished in 1.16 s | |GL(4,2)| = 20160
Optimal CNOT count : max = 9, mean = 5.685
Greedy CNOT count : max = 15, mean = 7.638
Optimal depth : max = 6, mean = 4.173
Greedy depth : max = 14, mean = 7.011
Greedy is already optimal for 16.5% of all circuits
Optimal gate count | #circuits | mean greedy count
0 | 1 | 0.000
1 | 12 | 1.000
2 | 96 | 2.250
3 | 542 | 3.738
4 | 2058 | 5.407
5 | 5316 | 6.970
6 | 7530 | 8.197
7 | 4058 | 9.017
8 | 541 | 9.571
9 | 6 | 10.333
Worst-case example (rows of M as bit masks): (8, 3, 5, 9)
Optimal circuit (4 CNOTs, depth 4): [(0, 1), (0, 2), (0, 3), (3, 0)]
Greedy circuit (14 CNOTs, depth 13): [(3, 2), (3, 1), (2, 3), (2, 1), (2, 0), (3, 2), (1, 3), (1, 2), (1, 0), (2, 1), (0, 3), (0, 2), (0, 1), (1, 0)]
5. Output figure

6. Results and discussion
The optimum is dramatically smaller than the heuristic
Over all 20,160 circuits, exhaustive search shows that no 4-qubit CNOT circuit ever needs more than 9 gates, and the average optimal circuit needs only about 5.7 gates. Gauss–Jordan elimination, in contrast, reaches 15 gates in the worst case and averages about 7.6. It matches the true optimum for only about 16.5% of the circuits.
The population by optimal gate count is a bell-shaped distribution:
| Optimal CNOTs | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Circuits | 1 | 12 | 96 | 542 | 2058 | 5316 | 7530 | 4058 | 541 | 6 |
Almost all of the group sits at 5–7 gates, and only 6 matrices are as hard as 9 gates. The counts grow by roughly a factor of 8 per level at the start, then saturate once most of the group has been visited. This is the expected shape for BFS on a finite Cayley graph.
Reading each panel
Top left: gate-count histogram. The green bars (optimal) are concentrated between 4 and 7 gates, while the red bars (greedy) are shifted to the right and have a long tail up to 15. Every gate in that tail is wasted noise on real hardware.
Top middle: optimal vs. greedy heat map. Each cell counts the circuits with a given (optimal, greedy) pair. Any circuit on the diagonal is solved optimally by the heuristic, and everything above the diagonal is overhead. The mass lies well above the diagonal, and the gap widens for the easiest circuits. A circuit that needs only 4 CNOTs is often compiled into 5, 6 or many more.
Top right: 3D bar chart. The $x$-axis is the optimal gate count, the $y$-axis is the optimal depth, and the $z$-axis is the number of circuits. Depth can never exceed gate count, because a circuit with $k$ gates can always be scheduled in at most $k$ layers. The bars therefore lie on and below the diagonal of the $(x,y)$ plane. The tall ridge shows that the typical circuit needs about 6 gates but fewer layers, because parallelism lets disjoint CNOTs share a layer. The optimal depth never exceeds 6, and its mean is about 4.2. The greedy method reaches a depth of 14.
Bottom left: average overhead. For each optimal gate count $k$, the red curve shows the mean greedy gate count. The shaded band between the red and dashed green curves is the average overhead, and it is positive at every level above zero. For circuits of optimal size 2, the heuristic already averages 2.25 gates. For optimal size 4 it averages about 5.4 gates.
Bottom middle and right: the worst case. The program picks the circuit with the largest gap between the two methods. For the matrix with rows (8, 3, 5, 9), the exact search finds a circuit with just 4 CNOTs (depth 4), whereas Gauss–Jordan returns 14 CNOTs (depth 13). That is $3.5\times$ more gates and more than $3\times$ the depth, for the very same unitary. A blind row-by-row elimination ignores shared structure, such as one CNOT that clears several entries at once, which an exhaustive search finds immediately.
Scaling and what comes next
The approach scales poorly in the number of qubits because $|GL(n,\mathbb{F}_2)|$ grows as roughly $2^{n^2}$:
$$
|GL(4,\mathbb{F}_2)| = 20160, \qquad |GL(5,\mathbb{F}_2)| = 9{,}999{,}360, \qquad |GL(6,\mathbb{F}_2)| \approx 2\times 10^{10}.
$$
Four qubits is trivial for BFS, five qubits is feasible with a compact encoding, and six or more calls for heuristics such as meet-in-the-middle search, SAT/ILP encodings, or the Patel–Markov–Hayes algorithm, which achieves $O(n^2/\log n)$ gates. Even then, exact tables for small blocks remain useful. Compilers can partition a large circuit into 3- or 4-qubit blocks and replace each one with its provably optimal implementation from a precomputed table like the one we just built. This pattern is called peephole optimization.
7. Conclusion
We cast gate-count and depth minimization as shortest-path problems on the group $GL(4,\mathbb{F}_2)$ and solved them exactly for all 20,160 linear reversible circuits with two breadth-first searches. The key findings are:
- Every 4-qubit CNOT circuit can be implemented with at most 9 CNOTs and at most 6 layers.
- A standard Gauss–Jordan compiler needs up to 15 CNOTs and is optimal for only about one circuit in six.
- In the worst case the heuristic is $3.5\times$ larger than the optimum (14 vs. 4 gates).
Small as it is, this example shows why circuit synthesis matters. An optimization that happens entirely at compile time can remove most of the noisy operations from a circuit before it ever runs on hardware.



















