Grover’s Algorithm And Its Variant Algorithm

An Introduction to Grover’s Algorithm

Grover’s algorithm is a quantum search algorithm for an unstructured database, which was devised by Lov Grover in 1996 (https://arxiv.org/pdf/quant-ph/9605043.pdf). Grover’s algorithm finds the unique input to a black box function that produce a particular output value with high probability, using just \(O(\sqrt{N})\) black boxes. Any classical algorithm cannot solve the search problem in fewer than \(O(N)\) black boxes. In 1997, Bennett, Bernstein, Brassard, and Vazirani proved that any quantum algorithm needs to use black boxes \(\Omega(\sqrt{N})\) times (https://arxiv.org/pdf/quant-ph/9701001.pdf), which means Grover’s algorithm is asymptotically optimal.

Main steps of Grover’s algorithm

Consider an unstructured database with \(N\) entries. There are \(N=2^n\) quantum basis states \(|1\rangle,|2\rangle, \ldots, |N\rangle\), and only the target state \(|\tau\rangle\) is the solution of search problem. Grover’s algorithm consists of following steps:

  1. Initialization. Apply Walsh-Hadamard transformation \(W=H^{\otimes n}\) on the first \(n\) qubits and get the initial state

    \[|\psi_0\rangle=W|0\rangle=\sqrt{1/N}\sum_{i=1}^n|i\rangle.\]
  2. Perform Grover iteration \(O(\sqrt{N})\) times, measure the first \(n\) qubits and get \(|\tau\rangle\) with high probability. The Grover iteration contains four steps: > Step 1. Flip the phase of target state \(|\tau\rangle\), i.e., apply

    \[I_{\tau}=I-2|\tau\rangle\langle \tau |.\]

    > > Step 2. Apply Walsh-Hadamard transformation \(W=H^{\otimes n}\) on the first \(n\) qubits. > > Step 3. Flip the phase of initial states \(|0\rangle^{\otimes n}\), i.e., apply

    \[I_0=2|0\rangle^{\otimes n~n \otimes}\langle 0|-I.\]

    > > Step 4. Apply Walsh-Hadamard transformation \(W=H^{\otimes n}\) on the first \(n\) qubits.

The quantum circuit of Grover’s algorithm is:

../_images/Grovers_algorithm.png

Realization of Grover’s Algorithm on HiQ

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 import math

 from projectq.ops import H, Z, X, Measure, All, Ph
 from projectq.meta import Loop, Compute, Uncompute, Control

 from projectq.cengines import (MainEngine,
                             AutoReplacer,
                             LocalOptimizer,
                             TagRemover,
                             DecompositionRuleSet)
 from hiq.projectq.cengines import GreedyScheduler, HiQMainEngine
 from hiq.projectq.backends import SimulatorMPI
 import projectq.setups.decompositions

 from mpi4py import MPI

 """
 To run example use the command line:
 $ mpirun -np 4 python ./grover_mpi.py
 """


 def run_grover(eng, n, oracle):
     """
     Runs Grover's algorithm on n qubit using the provided quantum oracle.

     Args:
         eng (MainEngine): Main compiler engine to run Grover on.
         n (int): Number of bits in the solution.
         oracle (function): Function accepting the engine, an n-qubit register,
             and an output qubit which is flipped by the oracle for the correct
             bit string.

     Returns:
         solution (list<int>): Solution bit-string.
     """
     x = eng.allocate_qureg(n)

     # start in uniform superposition
     All(H) | x

     # number of iterations we have to run:
     num_it = int(math.pi / 4. * math.sqrt(1 << n))
     print("Number of iterations we have to run: {}".format(num_it))

     # prepare the oracle output qubit (the one that is flipped to indicate the
     # solution. start in state 1/sqrt(2) * (|0> - |1>) s.t. a bit-flip turns
     # into a (-1)-phase.
     oracle_out = eng.allocate_qubit()
     X | oracle_out
     H | oracle_out

     # run num_it iterations
     with Loop(eng, num_it):
         # oracle adds a (-1)-phase to the solution
         oracle(eng, x, oracle_out)

         # reflection across uniform superposition
         with Compute(eng):
             All(H) | x
             All(X) | x

         with Control(eng, x[0:-1]):
             Z | x[-1]

         Uncompute(eng)

         All(Ph(math.pi / n)) | x

     All(Measure) | x
     Measure | oracle_out

     eng.flush()
     # return result
     return [int(qubit) for qubit in x]


 def alternating_bits_oracle(eng, qubits, output):
     """
     Marks the solution string 0,1,0,..... by flipping the

     qubit,
     conditioned on qubits being equal to the alternating bit-string.

     Args:
         eng (MainEngine): Main compiler engine the algorithm is being run on.
         qubits (Qureg): n-qubit quantum register Grover search is run on.
         output (Qubit): Output qubit to flip in order to mark the solution.
     """
     with Compute(eng):
         X | qubits[1]
     with Control(eng, qubits):
         X | output
     Uncompute(eng)


 if __name__ == "__main__":
     # create a main compiler engine with a simulator backend:
     backend = SimulatorMPI(gate_fusion=True,num_local_qubits=20)

     cache_depth = 10
     rule_set = DecompositionRuleSet(modules=[projectq.setups.decompositions])
     engines = [TagRemover()
             , LocalOptimizer(cache_depth)
             , AutoReplacer(rule_set)
             , TagRemover()
             , LocalOptimizer(cache_depth)
             , GreedyScheduler()
             ]

     eng = HiQMainEngine(backend, engines)

     if MPI.COMM_WORLD.Get_rank() == 0:
         print("=====================================================================")
         print("= This is the Grover algorithm demo")
         print("= Change the list to search by modifying alternating_bits_oracle")
         print("= The chosen list is: [0, 1, 0, ....]")
         print("= The second element is marked")
         print("= Calling Grover algorithm to find the marked element now...")

     # run Grover search to find a 7-bit solution
     N = 7
     mark_bits = run_grover(eng, N, alternating_bits_oracle)

     if MPI.COMM_WORLD.Get_rank() == 0:
         found = False
         for i in range(len(mark_bits)):
             if mark_bits[i] == 0:
                 print("= Marked element is found, its index is: {}".format(i))
                 found = True
         if not found:
             print("= Cannot find the marked element!")
         print("=====================================================================")

An Introduction to Exact Grover’s Algorithm

Standard Grover’s algorithm can only return the solution of search problem with high probability. In 2001, Lov proposed a modified Grover’s algorithm which can output the solution with probability 1 (https://arxiv.org/pdf/quant-ph/0106071.pdf).

The standard Grover iteration is

\[G=-H^{\otimes n}(I-2|0\rangle\langle 0|)H^{\otimes n}(I-2|\tau\rangle \langle \tau|),\]

where \(\tau\) is the solution of search problem.

The Grover iteration of exact Grover’s algorithm is described as

\[G(\theta, \phi)=-H^{\otimes n}(I+(e^{i\theta}-1)|0\rangle\langle 0|)H^{\otimes n}(I+(e^{i\phi}-1)|\tau\rangle \langle \tau|).\]

Here, \(\theta\) and \(\phi\) satisfy:

\[\cot ((2m+1)\beta)=e^{i\phi}\sin(2\beta)(-\cos(2\beta)+i\cot(\theta/2))^{-1},\]

where \(m=\pi 2^{n-2}\) and \(\sin^2(\beta)=1/2^{n}\).

Realization of Exact Grover’s Algorithm on HiQ

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
 import math

 from projectq.ops import H, Z, X, Measure, All, Ph, Rz
 from projectq.meta import Loop, Compute, Uncompute, Control

 from projectq.backends import CircuitDrawer, CommandPrinter
 from projectq.cengines import (MainEngine,
                             AutoReplacer,
                             LocalOptimizer,
                             TagRemover,
                             DecompositionRuleSet)
 import projectq.setups.decompositions

 from hiq.projectq.backends import SimulatorMPI
 from hiq.projectq.cengines import GreedyScheduler, HiQMainEngine

 from mpi4py import MPI

 def run_exactgrover(eng, n, oracle, oracle_modified):
     """
     Runs exact Grover's algorithm on n qubit using the two kind of provided
     quantum oracles (oracle and oracle_modified).
     This is an algorithm which can output solution with probability 1.

     Args:
         eng (MainEngine): Main compiler engine to run Grover on.
         n (int): Number of bits in the solution.
         oracle (function): Function accepting the engine, an n-qubit register,
             and an output qubit which is flipped by the oracle for the correct
             bit string.

     Returns:
         solution (list<int>): Solution bit-string.
     """
     x = eng.allocate_qureg(n)

     # start in uniform superposition
     All(H) | x

     # number of iterations we have to run:
     num_it = int(math.pi/4.*math.sqrt(1 << n))

     #phi is the parameter of modified oracle
     #varphi is the parameter of reflection across uniform superposition
     theta=math.asin(math.sqrt(1/(1 << n)))
     phi=math.acos(-math.cos(2*theta)/(math.sin(2*theta)*math.tan((2*num_it+1)*theta)))
     varphi=math.atan(1/(math.sin(2*theta)*math.sin(phi)*math.tan((2*num_it+1)*theta)))*2

     # prepare the oracle output qubit (the one that is flipped to indicate the
     # solution. start in state 1/sqrt(2) * (|0> - |1>) s.t. a bit-flip turns
     # into a (-1)-phase.
     oracle_out = eng.allocate_qubit()
     X | oracle_out
     H | oracle_out

     # run num_it iterations
     with Loop(eng, num_it):
         # oracle adds a (-1)-phase to the solution
         oracle(eng, x, oracle_out)

         # reflection across uniform superposition
         with Compute(eng):
             All(H) | x
             All(X) | x

         with Control(eng, x[0:-1]):
             Z | x[-1]

         Uncompute(eng)

     # prepare the oracle output qubit (the one that is flipped to indicate the
     # solution. start in state |1> s.t. a bit-flip turns into a e^(i*phi)-phase.
     H | oracle_out
     oracle_modified(eng, x, oracle_out, phi)

     with Compute(eng):
         All(H) | x
         All(X) | x

     with Control(eng, x[0:-1]):
         Rz(varphi) | x[-1]
         Ph(varphi/2) | x[-1]

     Uncompute(eng)

     All(Measure) | x
     Measure | oracle_out

     eng.flush()
     # return result
     return [int(qubit) for qubit in x]


 def alternating_bits_oracle(eng, qubits, output):
     """
     Marks the solution string 0, 1, 0,...,0 by flipping the

     qubit,
     conditioned on qubits being equal to the alternating bit-string.

     Args:
         eng (MainEngine): Main compiler engine the algorithm is being run on.
         qubits (Qureg): n-qubit quantum register Grover search is run on.
         output (Qubit): Output qubit to flip in order to mark the solution.
     """
     with Compute(eng):
         X | qubits[1]
     with Control(eng, qubits):
         X | output
     Uncompute(eng)

 def alternating_bits_oracle_modified(eng, qubits, output, phi):
     """
     Marks the solution string 0,1,0,... by applying phase gate to the output bits,
     conditioned on qubits being equal to the alternating bit-string.

     Args:
         eng (MainEngine): Main compiler engine the algorithm is being run on.
         qubits (Qureg): n-qubit quantum register Grover search is run on.
         output (Qubit): Output qubit to mark the solution by a phase gate with parameter phi.
     """
     with Compute(eng):
         All(X) | qubits[1::2]
     with Control(eng, qubits):
         Rz(phi) | output
         Ph(phi/2) | output

     Uncompute(eng)

 if __name__ == "__main__":
     # create a main compiler engine with a simulator backend:
     backend = SimulatorMPI(gate_fusion=True, num_local_qubits=20)
     # backend = CircuitDrawer()
     # locations = {}
     # for i in range(module.nqubits):
     #     locations[i] = i
     # backend.set_qubit_locations(locations)

     cache_depth = 10
     rule_set = DecompositionRuleSet(modules=[projectq.setups.decompositions])
     engines = [TagRemover(),
             LocalOptimizer(cache_depth),
             AutoReplacer(rule_set),
             TagRemover(),
             LocalOptimizer(cache_depth)
             #,CommandPrinter()
         , GreedyScheduler()
             ]

     eng = HiQMainEngine(backend, engines)
     # run Grover search to find a 7-bit solution

     print("=====================================================================")
     print("= This is the Grover algorithm demo")
     print("= Change the list to search by modifying alternating_bits_oracle")
     print("= The chosen list is: [0, 1, 0, ....]")
     print("= The second element is marked")
     print("= Calling ExactGrover algorithm to find the marked element now...")
     # run Grover search to find a 7-bit solution
     N = 12
     mark_bits = run_exactgrover(eng, N, alternating_bits_oracle, alternating_bits_oracle_modified)
     found = False
     for i in range(len(mark_bits)):
         if mark_bits[i] == 0:
             print("= Marked element is found, it index is: {}".format(i))
             found = True
     if not found:
         print("= Cannot found the marked element!")
     print("=====================================================================")

References:

[1] Grover L K. A fast quantum mechanical algorithm for database search[C]// Proceedings of the twenty-eighth annual ACM symposium on Theory of computing. ACM, 1996: 212-219.
[2] Brassard G, Hoyer P, Mosca M, et al. Quantum amplitude amplification and estimation[J]. Contemporary Mathematics, 2002, 305: 53-74.