# The original version of the following code is sourced from Qiskit circuit
# library and involve the following files for dependency decoupling:
# https://github.com/Qiskit/qiskit/blob/stable/2.3/qiskit/circuit/library/grover_operator.py#L27-L285
#
# 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):
# - Dependency decoupling: import the local Diagonal class to avoid circular imports from
# external repo.
# - Refactored to expose unified program interfaces:
# - We adopt the functional interface style for benchmark registration, as it focuses more on the functionality
# being provided rather than the inherent class structure with other components.
# - `def grover_operator()` -> `class GroverOperator(QuantumCircuit)`
# - Refine the statevector oracle handling to ensure compatibility with numpy arrays.
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# 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.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""The Grover operator."""
from __future__ import annotations
from typing import List, Optional, Union
import numpy
from qiskit.circuit import QuantumCircuit, AncillaQubit
from qiskit.exceptions import QiskitError
from qiskit.quantum_info import Statevector, Operator, DensityMatrix
from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit.library import MCXGate # This is a basic gate dependency and is kept here
import numpy as np
from ..diagonal import Diagonal
# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
@register_benchmark(
Path(__file__).stem,
family=Path(__file__).resolve().parent.name,
description="Construct the Grover operator circuit",
class_name="GroverOperator",
source={
"repo": "https://github.com/Qiskit/qiskit/tree/f14e0b29a484795034447ea5bfb637fe845c194f",
"file": "qiskit/circuit/library/grover_operator.py",
"sdk": "Qiskit",
"available_doc": True
},
testability_refactoring=[
"Dependency decoupling",
"Structure reorganization"
],
)
def create_grover_operator(
oracle: QuantumCircuit | Statevector,
state_preparation: QuantumCircuit | None = None,
zero_reflection: QuantumCircuit | DensityMatrix | Operator | None = None,
reflection_qubits: List[int] | None = None,
) -> QuantumCircuit:
return GroverOperator(
oracle=oracle,
state_preparation=state_preparation,
zero_reflection=zero_reflection,
reflection_qubits=reflection_qubits,
)
[docs]
class GroverOperator(QuantumCircuit):
r"""Grover operator circuit."""
def __init__(
self,
oracle: QuantumCircuit | Statevector,
state_preparation: QuantumCircuit | None = None,
zero_reflection: QuantumCircuit | DensityMatrix | Operator | None = None,
reflection_qubits: list[int] | None = None,
insert_barriers: bool = False,
name: str = "Q",
):
r"""
Args:
oracle: The phase oracle implementing a reflection about the bad state. Note that this
is not a bitflip oracle, see the docstring for more information.
state_preparation: The operator preparing the good and bad state.
For Grover's algorithm, this is a n-qubit Hadamard gate and for amplitude
amplification or estimation the operator :math:`\mathcal{A}`.
If ``None``, a layer of Hadamard gates is used on ``reflection_qubits``.
zero_reflection: The reflection about the zero state, :math:`\mathcal{S}_0`.
If ``None``, a default implementation is used.
reflection_qubits: Qubits on which the zero reflection acts on.
If `None`, all qubits of the oracle are used.
insert_barriers: Whether barriers should be inserted between the reflections and A.
name: The name of the circuit.
"""
super().__init__(name=name)
# store inputs
self._oracle = oracle
self._reflection_qubits = reflection_qubits
self._state_preparation = state_preparation
self._insert_barriers = insert_barriers
self._zero_reflection = zero_reflection
self.name = name
# build circuit
self._build()
def _build(self) -> None:
# We inherit the ancillas/qubits structure from the oracle, if it is given as circuit.
if isinstance(self._oracle, QuantumCircuit):
circuit = self._oracle.copy_empty_like(name=self.name, vars_mode="drop")
else:
circuit = QuantumCircuit(self._oracle.num_qubits, name=self.name)
# (1) Add the oracle.
# If the oracle is given as statevector, turn it into a circuit that implements the
# reflection about the state.
# Append a quantum gate -> compose to the circuit
if isinstance(self._oracle, Statevector):
diagonal = Diagonal((-1) ** self._oracle.data) # type: ignore
circuit.compose(diagonal, inplace=True)
else:
circuit.compose(self._oracle, inplace=True)
if self._insert_barriers:
circuit.barrier()
# (2) Add the inverse state preparation.
# For this we need to know the target qubits that we apply the zero reflection to.
# If the reflection qubits are not given, we assume they are the qubits that are not
# of type ``AncillaQubit`` in the oracle.
if self._reflection_qubits is None:
self.reflection_qubits = [
i for i, qubit in enumerate(circuit.qubits) if not isinstance(qubit, AncillaQubit)
]
if self._state_preparation is None:
circuit.h(self.reflection_qubits) # H is self-inverse
else:
circuit.compose(self._state_preparation.inverse(), inplace=True)
if self._insert_barriers:
circuit.barrier()
# (3) Add the zero reflection.
if self._zero_reflection is None:
num_reflection = len(self.reflection_qubits)
circuit.x(self.reflection_qubits)
if num_reflection == 1:
circuit.z(
self.reflection_qubits[0]
) # MCX does not support 0 controls, hence this is separate
else:
mcx = MCXGate(num_reflection - 1)
circuit.h(self.reflection_qubits[-1])
circuit.append(mcx, self.reflection_qubits)
circuit.h(self.reflection_qubits[-1])
circuit.x(self.reflection_qubits)
elif isinstance(self._zero_reflection, (Operator, DensityMatrix)):
diagonal = Diagonal(self._zero_reflection.data.diagonal()) # type: ignore
circuit.compose(diagonal, inplace=True)
else:
circuit.compose(self._zero_reflection, inplace=True)
if self._insert_barriers:
circuit.barrier()
# (4) Add the state preparation.
if self._state_preparation is None:
circuit.h(self.reflection_qubits)
else:
circuit.compose(self._state_preparation, inplace=True)
# minus sign
circuit.global_phase = numpy.pi
# Compose built circuit into self
self.add_register(*circuit.qregs)
self.compose(circuit, inplace=True)