Source code for uniqc.algorithmics.circuits.qft

"""Quantum Fourier Transform (QFT) circuit."""

__all__ = ["qft_circuit"]

from typing import List, Optional
import math

from uniqc.circuit_builder import Circuit


[docs] def qft_circuit( circuit: Circuit, qubits: Optional[List[int]] = None, swaps: bool = True, ) -> None: r"""Apply the Quantum Fourier Transform (QFT) to the given qubits. The QFT maps the computational basis state :math:`|j\rangle` to: .. math:: \frac{1}{\sqrt{N}} \sum_{k=0}^{N-1} e^{2\pi i\, jk/N} |k\rangle where :math:`N = 2^n` and *n* is the number of qubits. The circuit applies, for each qubit *j* (from most-significant to least-significant): 1. A Hadamard gate on qubit *j*. 2. Controlled phase rotations :math:`R_k` from every later qubit *k* > *j* with angle :math:`\pi / 2^{k-j}`. If *swaps* is ``True`` (the default), a layer of SWAP gates is appended to reverse the qubit order so that the output follows the standard big-endian convention. To obtain the inverse QFT, use ``circuit.dagger()`` on a copy of the QFT sub-circuit, or apply ``qft_circuit`` and then call ``circuit.dagger()`` on the relevant gates. Args: circuit: Quantum circuit to operate on (mutated in-place). qubits: Qubit indices to apply QFT on. ``None`` means all qubits of *circuit* (``list(range(circuit.qubit_num))``). swaps: Whether to append SWAP gates to reverse qubit order. Defaults to ``True``. Raises: ValueError: Fewer than 1 qubit is specified. Example: >>> from uniqc.circuit_builder import Circuit >>> from uniqc.algorithmics.circuits import qft_circuit >>> c = Circuit(3) >>> qft_circuit(c, qubits=[0, 1, 2]) """ if qubits is None: qubits = list(range(circuit.qubit_num)) n = len(qubits) if n < 1: raise ValueError("qft_circuit requires at least 1 qubit") for j in range(n): # Hadamard on qubit j circuit.h(qubits[j]) # Controlled phase rotations from qubits k > j for k in range(j + 1, n): angle = math.pi / (2 ** (k - j)) # Controlled-Rz using CNOT decomposition: # CRz(θ) = Rz(θ/2) on target, CNOT, Rz(-θ/2) on target, CNOT # Equivalently, we can use controlled-phase via CNOT + Rz circuit.rz(qubits[k], angle / 2) circuit.cnot(qubits[j], qubits[k]) circuit.rz(qubits[k], -angle / 2) circuit.cnot(qubits[j], qubits[k]) if swaps: for i in range(n // 2): circuit.swap(qubits[i], qubits[n - 1 - i])