Source code for qolumbina.programs.quantum_counting.counting

# This code is refactored by the Jupyter Notebook from
# https://github.com/Qiskit/textbook/blob/aebdd2bc86ddb7a79dd8441d52c839d312ffafbb/notebooks/ch-algorithms/quantum-counting.ipynb
#
# The corresponding repository is
# https://github.com/Qiskit/textbook?tab=Apache-2.0-1-ov-file
#
# This program is adapted for use as a benchmark in controlled software testing experiments.
# Modifications made to the original code include (for Apache License 2.0):
# - Refactored to unify the inherent class as QuantumCircuit:
#   - Unify into the QuantumCircuit structure.
#   - Modify code to make the grover operator scalable.
#   - Automatically determine the number of searching qubits from the oracle.
# - Dependency decoupling: use the local QFT implementation to avoid circular imports from 
#   
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

 
# importing Qiskit
import math
from qiskit import QuantumCircuit 
from qiskit.quantum_info import Statevector

# Use the local QFT and GroverOperator
from ..quantum_fourier_transform import QFT
from ..grover import GroverOperator


# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
@register_benchmark(
    Path(__file__).stem,
    family=Path(__file__).resolve().parent.name,
    description="Estimate the number of solutions to a search problem using Grover's operator and inverse QFT.",
    class_name="QuantumCounting",
    source={
        "repo": "https://github.com/Qiskit/textbook?tab=Apache-2.0-1-ov-file",
        "file": "notebooks/ch-algorithms/quantum-counting.ipynb",
        "sdk": "Qiskit",
        "available_doc": True
    },
    testability_refactoring=[
        "Structure reorganization",
        "Dependency decoupling"
    ],
)
def create_quantum_counting(
    oracle: QuantumCircuit | Statevector,
    counting_qubits: int
) -> QuantumCircuit:
    return QuantumCounting(
        oracle=oracle, 
        counting_qubits=counting_qubits
    )

[docs] class QuantumCounting(QuantumCircuit): r""" Quantum Counting Algorithm Circuit. This class implements the quantum counting algorithm using Grover's operator and inverse Quantum Fourier Transform (QFT). """ def __init__( self, oracle: QuantumCircuit | Statevector, counting_qubits: int, name: str | None = None, ): r""" Args: oracle: The oracle circuit that marks the solutions. This argument derermines the number of searching qubits, i.e., :math:`n`. Besides, be sure that the oracle is a phase oracle. Otherwise, the algorithm will not work as expected. counting_qubits: The number of counting qubits, i.e., :math:`t`. name: Optional name of the circuit. Raises: TypeError: If the oracle is not a QuantumCircuit or Statevector. """ self._oracle = oracle self._counting_qubits = counting_qubits # Determine number of searching qubits from oracle if isinstance(oracle, QuantumCircuit): self._searching_qubits = int(oracle.num_qubits) elif isinstance(oracle, Statevector): self._searching_qubits = int(math.log2(len(oracle))) else: raise TypeError("Oracle must be a QuantumCircuit or Statevector.") super().__init__( self._counting_qubits + self._searching_qubits, name=name or "QuantumCounting" ) # Build the circuit self._build()
[docs] def grover_operator(self, n_iterations: int): r"""Grover iteration circuit for oracle Args: n_iterations: Number of times to repeat the circuit. Returns: Gate that implements ``n_iterations`` of the Grover operator """ grover_it = GroverOperator(self._oracle).repeat(n_iterations).to_gate() grover_it.label = f"Grover$^{n_iterations}$" return grover_it
def _build(self): t = self._counting_qubits n = self._searching_qubits # Initialize all qubits to |+> for qubit in range(t + n): self.h(qubit) # Begin controlled Grover iterations n_iterations = 1 for qubit in range(t): # Apply controlled Grover iterations in a gate form cgrit = self.grover_operator(n_iterations).control() self.append(cgrit, [qubit] + list(range(t, n+t))) n_iterations *= 2 # Do inverse QFT on counting qubits # Inverse QFT on counting register self.append(QFT(t, inverse=True), range(t))
# # Measure counting qubits # self.measure(range(t), range(t))