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))