grover.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. """Grover's algorithm and helper functions.
  2. Todo:
  3. * W gate construction (or perhaps -W gate based on Mermin's book)
  4. * Generalize the algorithm for an unknown function that returns 1 on multiple
  5. qubit states, not just one.
  6. * Implement _represent_ZGate in OracleGate
  7. """
  8. from sympy.core.numbers import pi
  9. from sympy.core.sympify import sympify
  10. from sympy.functions.elementary.integers import floor
  11. from sympy.functions.elementary.miscellaneous import sqrt
  12. from sympy.matrices.dense import eye
  13. from sympy.core.numbers import NegativeOne
  14. from sympy.physics.quantum.qapply import qapply
  15. from sympy.physics.quantum.qexpr import QuantumError
  16. from sympy.physics.quantum.hilbert import ComplexSpace
  17. from sympy.physics.quantum.operator import UnitaryOperator
  18. from sympy.physics.quantum.gate import Gate
  19. from sympy.physics.quantum.qubit import IntQubit
  20. __all__ = [
  21. 'OracleGate',
  22. 'WGate',
  23. 'superposition_basis',
  24. 'grover_iteration',
  25. 'apply_grover'
  26. ]
  27. def superposition_basis(nqubits):
  28. """Creates an equal superposition of the computational basis.
  29. Parameters
  30. ==========
  31. nqubits : int
  32. The number of qubits.
  33. Returns
  34. =======
  35. state : Qubit
  36. An equal superposition of the computational basis with nqubits.
  37. Examples
  38. ========
  39. Create an equal superposition of 2 qubits::
  40. >>> from sympy.physics.quantum.grover import superposition_basis
  41. >>> superposition_basis(2)
  42. |0>/2 + |1>/2 + |2>/2 + |3>/2
  43. """
  44. amp = 1/sqrt(2**nqubits)
  45. return sum([amp*IntQubit(n, nqubits=nqubits) for n in range(2**nqubits)])
  46. class OracleGate(Gate):
  47. """A black box gate.
  48. The gate marks the desired qubits of an unknown function by flipping
  49. the sign of the qubits. The unknown function returns true when it
  50. finds its desired qubits and false otherwise.
  51. Parameters
  52. ==========
  53. qubits : int
  54. Number of qubits.
  55. oracle : callable
  56. A callable function that returns a boolean on a computational basis.
  57. Examples
  58. ========
  59. Apply an Oracle gate that flips the sign of ``|2>`` on different qubits::
  60. >>> from sympy.physics.quantum.qubit import IntQubit
  61. >>> from sympy.physics.quantum.qapply import qapply
  62. >>> from sympy.physics.quantum.grover import OracleGate
  63. >>> f = lambda qubits: qubits == IntQubit(2)
  64. >>> v = OracleGate(2, f)
  65. >>> qapply(v*IntQubit(2))
  66. -|2>
  67. >>> qapply(v*IntQubit(3))
  68. |3>
  69. """
  70. gate_name = 'V'
  71. gate_name_latex = 'V'
  72. #-------------------------------------------------------------------------
  73. # Initialization/creation
  74. #-------------------------------------------------------------------------
  75. @classmethod
  76. def _eval_args(cls, args):
  77. # TODO: args[1] is not a subclass of Basic
  78. if len(args) != 2:
  79. raise QuantumError(
  80. 'Insufficient/excessive arguments to Oracle. Please ' +
  81. 'supply the number of qubits and an unknown function.'
  82. )
  83. sub_args = (args[0],)
  84. sub_args = UnitaryOperator._eval_args(sub_args)
  85. if not sub_args[0].is_Integer:
  86. raise TypeError('Integer expected, got: %r' % sub_args[0])
  87. if not callable(args[1]):
  88. raise TypeError('Callable expected, got: %r' % args[1])
  89. return (sub_args[0], args[1])
  90. @classmethod
  91. def _eval_hilbert_space(cls, args):
  92. """This returns the smallest possible Hilbert space."""
  93. return ComplexSpace(2)**args[0]
  94. #-------------------------------------------------------------------------
  95. # Properties
  96. #-------------------------------------------------------------------------
  97. @property
  98. def search_function(self):
  99. """The unknown function that helps find the sought after qubits."""
  100. return self.label[1]
  101. @property
  102. def targets(self):
  103. """A tuple of target qubits."""
  104. return sympify(tuple(range(self.args[0])))
  105. #-------------------------------------------------------------------------
  106. # Apply
  107. #-------------------------------------------------------------------------
  108. def _apply_operator_Qubit(self, qubits, **options):
  109. """Apply this operator to a Qubit subclass.
  110. Parameters
  111. ==========
  112. qubits : Qubit
  113. The qubit subclass to apply this operator to.
  114. Returns
  115. =======
  116. state : Expr
  117. The resulting quantum state.
  118. """
  119. if qubits.nqubits != self.nqubits:
  120. raise QuantumError(
  121. 'OracleGate operates on %r qubits, got: %r'
  122. % (self.nqubits, qubits.nqubits)
  123. )
  124. # If function returns 1 on qubits
  125. # return the negative of the qubits (flip the sign)
  126. if self.search_function(qubits):
  127. return -qubits
  128. else:
  129. return qubits
  130. #-------------------------------------------------------------------------
  131. # Represent
  132. #-------------------------------------------------------------------------
  133. def _represent_ZGate(self, basis, **options):
  134. """
  135. Represent the OracleGate in the computational basis.
  136. """
  137. nbasis = 2**self.nqubits # compute it only once
  138. matrixOracle = eye(nbasis)
  139. # Flip the sign given the output of the oracle function
  140. for i in range(nbasis):
  141. if self.search_function(IntQubit(i, nqubits=self.nqubits)):
  142. matrixOracle[i, i] = NegativeOne()
  143. return matrixOracle
  144. class WGate(Gate):
  145. """General n qubit W Gate in Grover's algorithm.
  146. The gate performs the operation ``2|phi><phi| - 1`` on some qubits.
  147. ``|phi> = (tensor product of n Hadamards)*(|0> with n qubits)``
  148. Parameters
  149. ==========
  150. nqubits : int
  151. The number of qubits to operate on
  152. """
  153. gate_name = 'W'
  154. gate_name_latex = 'W'
  155. @classmethod
  156. def _eval_args(cls, args):
  157. if len(args) != 1:
  158. raise QuantumError(
  159. 'Insufficient/excessive arguments to W gate. Please ' +
  160. 'supply the number of qubits to operate on.'
  161. )
  162. args = UnitaryOperator._eval_args(args)
  163. if not args[0].is_Integer:
  164. raise TypeError('Integer expected, got: %r' % args[0])
  165. return args
  166. #-------------------------------------------------------------------------
  167. # Properties
  168. #-------------------------------------------------------------------------
  169. @property
  170. def targets(self):
  171. return sympify(tuple(reversed(range(self.args[0]))))
  172. #-------------------------------------------------------------------------
  173. # Apply
  174. #-------------------------------------------------------------------------
  175. def _apply_operator_Qubit(self, qubits, **options):
  176. """
  177. qubits: a set of qubits (Qubit)
  178. Returns: quantum object (quantum expression - QExpr)
  179. """
  180. if qubits.nqubits != self.nqubits:
  181. raise QuantumError(
  182. 'WGate operates on %r qubits, got: %r'
  183. % (self.nqubits, qubits.nqubits)
  184. )
  185. # See 'Quantum Computer Science' by David Mermin p.92 -> W|a> result
  186. # Return (2/(sqrt(2^n)))|phi> - |a> where |a> is the current basis
  187. # state and phi is the superposition of basis states (see function
  188. # create_computational_basis above)
  189. basis_states = superposition_basis(self.nqubits)
  190. change_to_basis = (2/sqrt(2**self.nqubits))*basis_states
  191. return change_to_basis - qubits
  192. def grover_iteration(qstate, oracle):
  193. """Applies one application of the Oracle and W Gate, WV.
  194. Parameters
  195. ==========
  196. qstate : Qubit
  197. A superposition of qubits.
  198. oracle : OracleGate
  199. The black box operator that flips the sign of the desired basis qubits.
  200. Returns
  201. =======
  202. Qubit : The qubits after applying the Oracle and W gate.
  203. Examples
  204. ========
  205. Perform one iteration of grover's algorithm to see a phase change::
  206. >>> from sympy.physics.quantum.qapply import qapply
  207. >>> from sympy.physics.quantum.qubit import IntQubit
  208. >>> from sympy.physics.quantum.grover import OracleGate
  209. >>> from sympy.physics.quantum.grover import superposition_basis
  210. >>> from sympy.physics.quantum.grover import grover_iteration
  211. >>> numqubits = 2
  212. >>> basis_states = superposition_basis(numqubits)
  213. >>> f = lambda qubits: qubits == IntQubit(2)
  214. >>> v = OracleGate(numqubits, f)
  215. >>> qapply(grover_iteration(basis_states, v))
  216. |2>
  217. """
  218. wgate = WGate(oracle.nqubits)
  219. return wgate*oracle*qstate
  220. def apply_grover(oracle, nqubits, iterations=None):
  221. """Applies grover's algorithm.
  222. Parameters
  223. ==========
  224. oracle : callable
  225. The unknown callable function that returns true when applied to the
  226. desired qubits and false otherwise.
  227. Returns
  228. =======
  229. state : Expr
  230. The resulting state after Grover's algorithm has been iterated.
  231. Examples
  232. ========
  233. Apply grover's algorithm to an even superposition of 2 qubits::
  234. >>> from sympy.physics.quantum.qapply import qapply
  235. >>> from sympy.physics.quantum.qubit import IntQubit
  236. >>> from sympy.physics.quantum.grover import apply_grover
  237. >>> f = lambda qubits: qubits == IntQubit(2)
  238. >>> qapply(apply_grover(f, 2))
  239. |2>
  240. """
  241. if nqubits <= 0:
  242. raise QuantumError(
  243. 'Grover\'s algorithm needs nqubits > 0, received %r qubits'
  244. % nqubits
  245. )
  246. if iterations is None:
  247. iterations = floor(sqrt(2**nqubits)*(pi/4))
  248. v = OracleGate(nqubits, oracle)
  249. iterated = superposition_basis(nqubits)
  250. for iter in range(iterations):
  251. iterated = grover_iteration(iterated, v)
  252. iterated = qapply(iterated)
  253. return iterated