grover.py 10 KB

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