Source code for qrisp.circuit.pass_management.passes.convert_to_cx
"""********************************************************************************
* 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
from collections.abc import Callable
from qrisp.circuit.pass_management.circuit_pass import CircuitPass
from qrisp.circuit.quantum_circuit import QuantumCircuit
[docs]
def convert_to_cx(
strict: bool = False,
) -> Callable[[QuantumCircuit], QuantumCircuit]:
"""Create a pass that converts two-qubit gates to CX-based decompositions.
This pass converts CZ, CY, and SWAP gates to their CX-based equivalents
using single-qubit gate decompositions. CX gates are native to many
quantum computing platforms.
Two-qubit gates that are already CX or barrier instructions are always left
unchanged. Any other two-qubit gate that has no known CX decomposition is
either passed through silently (``strict=False``, the default) or causes the
pass to raise an exception (``strict=True``).
.. note::
This pass does **not** decompose composite (wrapped) gates. Use the
:func:`decompose` pass to expand composite gates into elementary
operations before applying this pass.
Parameters
----------
strict : bool, optional
When ``True``, an :class:`Exception` is raised if a two-qubit gate with
no known CX decomposition is encountered. When ``False`` (the default),
such gates are left unchanged in the output circuit.
Returns
-------
Callable[[QuantumCircuit], QuantumCircuit]
A pass function suitable for :meth:`PassManager.add_pass`.
Decompositions
--------------
- CZ(control, target) -> H(target), CX(control, target), H(target)
- CY(control, target) -> S†(target), CX(control, target), S(target)
- SWAP(a, b) -> three CX gates
Examples
--------
Convert a CZ gate to H—CX—H::
>>> from qrisp import QuantumCircuit, PassManager
>>> from qrisp import convert_to_cx
>>> qc = QuantumCircuit(2)
>>> qc.cz(0, 1)
>>> print(qc)
<BLANKLINE>
qb_69: ──■──
│
qb_70: ──■──
>>> pm = PassManager()
>>> pm += convert_to_cx()
>>> optimized_qc = pm.run(qc)
>>> print(optimized_qc)
qb_69: ──────■──────
┌───┐ │ ┌───┐
qb_70: ┤ H ├─■─┤ H ├
└───┘ └───┘
"""
@CircuitPass
def _convert_to_cx(qc: QuantumCircuit) -> QuantumCircuit:
qc_new = qc.clearcopy()
for instr in qc.data:
op = instr.op
if op.num_qubits == 2 and op.name not in ("cx", "barrier"):
if op.name == "cz":
qc_new.h(instr.qubits[1])
qc_new.cx(instr.qubits[0], instr.qubits[1])
qc_new.h(instr.qubits[1])
elif op.name == "cy":
qc_new.s_dg(instr.qubits[1])
qc_new.cx(instr.qubits[0], instr.qubits[1])
qc_new.s(instr.qubits[1])
elif op.name == "swap":
qc_new.cx(instr.qubits[0], instr.qubits[1])
qc_new.cx(instr.qubits[1], instr.qubits[0])
qc_new.cx(instr.qubits[0], instr.qubits[1])
elif strict:
raise ValueError(f"Don't know how to convert two-qubit gate {op.name!r} to CX")
else:
qc_new.append(instr)
else:
qc_new.append(instr)
return qc_new
return _convert_to_cx