Source code for qrisp.circuit.pass_management.passes.gray_synth_toffoli
"""********************************************************************************
* Copyright (c) 2026 the Qrisp authors
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************
"""
from __future__ import annotations
import functools
import numpy as np
from qrisp.circuit.operation import ControlledOperation
from qrisp.circuit.pass_management.circuit_pass import CircuitPass
from qrisp.circuit.quantum_circuit import QuantumCircuit
# The gray-synthesis Toffoli circuit is built lazily (on first call) rather than
# at module level. Building it at import time triggers QuantumCircuit.append →
# convert_to_qb_list → "from qrisp import QuantumArray", which hits a circular
# import because qrisp.circuit is itself not yet fully initialised when
# qrisp.__init__ imports qrisp.circuit.passes. The lru_cache ensures the circuit
# is constructed only once and reused on every subsequent call.
@functools.lru_cache(maxsize=1)
def _get_gray_toffoli_qc() -> QuantumCircuit:
"""Build and return the cached gray-synthesis Toffoli circuit."""
qc = QuantumCircuit(3)
qc.t(0)
qc.t(1)
qc.h(2)
qc.t(2)
qc.cx(1, 2)
qc.t_dg(2)
qc.cx(0, 2)
qc.t(2)
qc.cx(1, 2)
qc.t_dg(2)
qc.cx(0, 2)
qc.h(2)
qc.rzz(-np.pi / 4, 0, 1)
return qc
def is_toffoli(op) -> bool:
"""Return True if *op* is a Toffoli (doubly-controlled X) gate.
Parameters
----------
op : Operation
The operation to check.
Returns
-------
bool
``True`` if *op* is a :class:`~qrisp.circuit.ControlledOperation`
with two controls whose base operation is an ``x`` gate.
Examples
--------
>>> from qrisp import QuantumCircuit
>>> from qrisp import is_toffoli
>>>
>>> qc = QuantumCircuit(3)
>>> qc.ccx(0, 1, 2)
>>>
>>> is_toffoli(qc.data[0].op)
True
"""
return isinstance(op, ControlledOperation) and len(op.controls) == 2 and op.base_operation.name == "x"
[docs]
@CircuitPass
def gray_synth_toffoli(qc: QuantumCircuit) -> QuantumCircuit:
"""Replace Toffoli gates with a gray-synthesis decomposition.
Qrisp's default Toffoli implementation is optimised for T-depth, since
the majority of Qrisp algorithms target fault-tolerant execution where
T gates dominate cost. The default decomposition does not have a
higher CNOT count than the gray-synthesis variant, but it requires
more SWAP gates when routed onto linear nearest-neighbour connectivity.
The gray-synthesis decomposition uses 6 CNOT gates and does not
inherently satisfy linear connectivity either, but the router
consistently resolves the gap with a single SWAP whose constituent
CNOTs partially cancel with a Toffoli CNOT — resulting in 7 physical
CNOTs and a displaced target qubit.
Parameters
----------
qc : QuantumCircuit
The input quantum circuit.
Returns
-------
QuantumCircuit
A new circuit with every Toffoli replaced by the gray-synthesis
decomposition.
Examples
--------
We showcase the distinction in Toffoli decompositions.
>>> from qrisp import QuantumCircuit, PassManager
>>> from qrisp import gray_synth_toffoli, decompose
>>> qc = QuantumCircuit(3)
>>> qc.ccx(0, 1, 2)
>>> print(qc)
<BLANKLINE>
qb_95: ──■──
│
qb_96: ──■──
┌─┴─┐
qb_97: ┤ X ├
└───┘
>>> pm_0 = PassManager()
>>> pm_0 += decompose()
>>> decomposed_qc = pm_0.run(qc)
>>> print(decomposed_qc)
┌─────┐
qb_95: ┤ Tdg ├───────■─────────■────■───────────────────────■──
├─────┤┌───┐ │ ┌───┐┌─┴─┐ │ ┌─────┐┌───┐ ┌───┐ ┌─┴─┐
qb_96: ┤ Tdg ├┤ X ├──┼──┤ T ├┤ X ├──┼──┤ Tdg ├┤ X ├─┤ T ├─┤ X ├
└┬───┬┘└─┬─┘┌─┴─┐├───┤└───┘┌─┴─┐└─────┘└─┬─┘┌┴───┴┐├───┤
qb_97: ─┤ H ├───■──┤ X ├┤ T ├─────┤ X ├─────────■──┤ Tdg ├┤ H ├
└───┘ └───┘└───┘ └───┘ └─────┘└───┘
While this implementation has only a T-depth of 4, the CX gates
essentially "cycle" through the connectivity requirements. On
a linear chain connectivity, several swaps would be required.
>>> pm_1 = PassManager()
>>> pm_1 += gray_synth_toffoli
>>> pm_1 += decompose()
>>> optimized_qc = pm_1.run(qc)
>>> print(optimized_qc)
┌───┐ ┌────────┐
qb_95: ┤ T ├───────────────────■─────────────────────■────■───┤ gphase ├──■──
├───┤ │ │ ┌─┴─┐┌┴────────┤┌─┴─┐
qb_96: ┤ T ├───────■───────────┼─────────■───────────┼──┤ X ├┤ P(-π/4) ├┤ X ├
├───┤┌───┐┌─┴─┐┌─────┐┌─┴─┐┌───┐┌─┴─┐┌─────┐┌─┴─┐├───┤└─────────┘└───┘
qb_97: ┤ H ├┤ T ├┤ X ├┤ Tdg ├┤ X ├┤ T ├┤ X ├┤ Tdg ├┤ X ├┤ H ├────────────────
└───┘└───┘└───┘└─────┘└───┘└───┘└───┘└─────┘└───┘└───┘
This implementation has T-depth 5 but the first 4 CX gates can be implemented
swap-free on a linear chain connectivity. After this, a single SWAP (that can
be fused with one of the CX) is suffificient to execute the remaining CX.
"""
qc_new = qc.clearcopy()
for instr in qc.data:
op = instr.op
if is_toffoli(op):
# Imported here to avoid a circular import: alg_primitives imports
# from qrisp.core, which is not yet available when qrisp.circuit.passes
# is first loaded.
from qrisp.alg_primitives.mcx_algs import ctrl_state_wrap
assert isinstance(op, ControlledOperation)
new_op = op.copy()
new_op.definition = ctrl_state_wrap(_get_gray_toffoli_qc(), op.ctrl_state)
qc_new.append(new_op, instr.qubits, instr.clbits)
else:
qc_new.append(instr)
return qc_new