Source code for uniqc.algorithms.core.circuits.qft

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

This module follows the *circuit fragment* design (see
the design notes in the project README):

- ``qft_circuit(n_qubits, qubits=None, swaps=True) -> Circuit``
  is the canonical fragment-style API and returns a fresh
  :class:`uniqc.circuit_builder.qcircuit.Circuit`.
- ``qft_circuit(circuit, qubits=...)`` is kept as a deprecated in-place
  shim that emits :class:`DeprecationWarning`.
"""

__all__ = ["qft_circuit"]

import math

from uniqc._error_hints import format_enriched_message
from uniqc.algorithms._compat import dispatch_circuit_fragment
from uniqc.circuit_builder import Circuit


def _build_qft_fragment(
    *,
    n_qubits: int,
    qubits: list[int] | None = None,
    swaps: bool = True,
) -> Circuit:
    """Pure fragment builder: returns a fresh ``Circuit`` containing QFT."""
    if qubits is None:
        qubits = list(range(n_qubits))

    n = len(qubits)
    if n < 1:
        raise ValueError(format_enriched_message("qft_circuit requires at least 1 qubit", "circuit_validation"))

    fragment = Circuit()
    for j in range(n):
        fragment.h(qubits[j])
        for k in range(j + 1, n):
            angle = math.pi / (2 ** (k - j))
            # Controlled-Rz emulated with CNOT decomposition
            fragment.rz(qubits[k], angle / 2)
            fragment.cnot(qubits[j], qubits[k])
            fragment.rz(qubits[k], -angle / 2)
            fragment.cnot(qubits[j], qubits[k])

    if swaps:
        for i in range(n // 2):
            fragment.swap(qubits[i], qubits[n - 1 - i])
    return fragment


[docs] def qft_circuit(first_arg=None, qubits: list[int] | None = None, swaps: bool = True): r"""Build (or apply) a Quantum Fourier Transform fragment. Two calling conventions are supported: .. code-block:: python # Fragment style (recommended): qft = qft_circuit(n_qubits=3) # returns a fresh Circuit qft = qft_circuit(3, qubits=[2, 3, 4]) # explicit qubit layout # Legacy in-place style (deprecated, emits DeprecationWarning): c = Circuit(3) qft_circuit(c, qubits=[0, 1, 2]) # mutates c, returns None The QFT maps :math:`|j\rangle` to :math:`\frac{1}{\sqrt{N}} \sum_{k=0}^{N-1} e^{2\pi i jk / N} |k\rangle`. Args: first_arg: Either an integer ``n_qubits`` (fragment mode) or a :class:`Circuit` (deprecated in-place mode). May be ``None`` if ``qubits`` is given. qubits: Qubit indices to operate on. ``None`` defaults to ``range(n_qubits)``. swaps: Whether to append the SWAP layer that reverses qubit order so the output follows the standard big-endian convention. Returns: A fresh :class:`Circuit` in fragment mode; ``None`` in legacy mode. Raises: ValueError: Fewer than 1 qubit specified. """ return dispatch_circuit_fragment( name="qft_circuit", fragment_builder=_build_qft_fragment, first_arg=first_arg, legacy_qubits=qubits, extra_kwargs={"swaps": swaps}, )
[docs] def qft_example() -> Circuit: """Return a 3-qubit QFT circuit, used by docs and tests as a smoke example.""" return qft_circuit(3)