Grover’s Search Algorithm

Grover’s algorithm [7] provides a quadratic speedup for searching an unstructured database of size \(N\). It works by iteratively rotating a quantum state vector toward a “marked” solution state using a phase oracle and a diffusion operator.

Overview

Grover’s algorithm is a fundamental quantum algorithm that finds the unique input to a black-box function that produces a particular output value. The implementation consists of two main components:

  • The Oracle: A phase oracle that flips the sign of the amplitude for the “marked” states passed by the user.

  • The Diffusion Operator: This operator flips the current state about its previous state.

The algorithm requires an optimal number of iterations, approximately \(\frac{\pi}{4}\sqrt{\frac{N}{M}}\), where \(N=2^n\) is the size of the search space and \(M\) is the number of marked states.

Constructing the Algorithm

In this module, you can construct the full Grover search circuit using the grover() function. To make the process easier, a utility function grover_oracle() is provided to generate standard phase-flip oracles if the marked states are known.

The grover() function requires the following:

Argument

Description

oracle

A QubitCircuit or Gate representing the phase oracle.

search_qubits

List of qubit indices (or integer count) to run the search on.

num_solutions

Integer representing the number of marked states \(M\).

num_iterations

Optional integer for manual control over the rotation count.

num_qubits

Optional total number of qubits when searching on a subset of a larger system.

Example: Searching for Multiple Targets

Let’s simulate a search on 3 qubits (\(N=8\)) where two states are marked: \(|011\rangle\) (index 3) and \(|101\rangle\) (index 5).

First, we import the necessary components and define our search space.

>>> import matplotlib.pyplot as plt
>>> from qutip import basis, tensor
>>> from qutip_qip.algorithms.grover import grover, grover_oracle
>>> n_qubits = 3
>>> marked = [3, 5]

We then use the utility function grover_oracle() to create a phase-flip oracle for our marked states.

>>> oracle = grover_oracle(n_qubits, marked)

Using the grover() function, we construct the full circuit. Since we provide the mandatory num_solutions, the algorithm automatically calculates the optimal number of iterations [7].

>>> qc = grover(oracle, n_qubits, num_solutions=len(marked))

We can also visually render the circuit:

>>> qc.draw()

(png, hires.png, pdf)

_images/qip-grover-1.png

We can now simulate the circuit by computing the full unitary and applying it to the initial state.

>>> U_grover = qc.compute_unitary()
>>> psi0 = tensor([basis(2, 0)] * n_qubits)
>>> psi_final = U_grover * psi0

To visualize the results, we calculate the measurement probabilities for all possible states and plot them.

>>> probabilities = [float(abs(psi_final.overlap(basis(2**n_qubits, i)))**2) for i in range(2**n_qubits)]
>>> states = [format(i, f'0{n_qubits}b') for i in range(2**n_qubits)]
>>> plt.bar(states, probabilities)
<...>
>>> plt.title("Grover Search Results (2 Targets)")
Text(...)
>>> plt.xlabel("States")
Text(...)
>>> plt.ylabel("Probability")
Text(...)
>>> plt.show()

(png, hires.png, pdf)

_images/qip-grover-2.png