qft.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. """An implementation of qubits and gates acting on them.
  2. Todo:
  3. * Update docstrings.
  4. * Update tests.
  5. * Implement apply using decompose.
  6. * Implement represent using decompose or something smarter. For this to
  7. work we first have to implement represent for SWAP.
  8. * Decide if we want upper index to be inclusive in the constructor.
  9. * Fix the printing of Rk gates in plotting.
  10. """
  11. from sympy.core.expr import Expr
  12. from sympy.core.numbers import (I, Integer, pi)
  13. from sympy.core.symbol import Symbol
  14. from sympy.functions.elementary.exponential import exp
  15. from sympy.matrices.dense import Matrix
  16. from sympy.functions import sqrt
  17. from sympy.physics.quantum.qapply import qapply
  18. from sympy.physics.quantum.qexpr import QuantumError, QExpr
  19. from sympy.matrices import eye
  20. from sympy.physics.quantum.tensorproduct import matrix_tensor_product
  21. from sympy.physics.quantum.gate import (
  22. Gate, HadamardGate, SwapGate, OneQubitGate, CGate, PhaseGate, TGate, ZGate
  23. )
  24. from sympy.functions.elementary.complexes import sign
  25. __all__ = [
  26. 'QFT',
  27. 'IQFT',
  28. 'RkGate',
  29. 'Rk'
  30. ]
  31. #-----------------------------------------------------------------------------
  32. # Fourier stuff
  33. #-----------------------------------------------------------------------------
  34. class RkGate(OneQubitGate):
  35. """This is the R_k gate of the QTF."""
  36. gate_name = 'Rk'
  37. gate_name_latex = 'R'
  38. def __new__(cls, *args):
  39. if len(args) != 2:
  40. raise QuantumError(
  41. 'Rk gates only take two arguments, got: %r' % args
  42. )
  43. # For small k, Rk gates simplify to other gates, using these
  44. # substitutions give us familiar results for the QFT for small numbers
  45. # of qubits.
  46. target = args[0]
  47. k = args[1]
  48. if k == 1:
  49. return ZGate(target)
  50. elif k == 2:
  51. return PhaseGate(target)
  52. elif k == 3:
  53. return TGate(target)
  54. args = cls._eval_args(args)
  55. inst = Expr.__new__(cls, *args)
  56. inst.hilbert_space = cls._eval_hilbert_space(args)
  57. return inst
  58. @classmethod
  59. def _eval_args(cls, args):
  60. # Fall back to this, because Gate._eval_args assumes that args is
  61. # all targets and can't contain duplicates.
  62. return QExpr._eval_args(args)
  63. @property
  64. def k(self):
  65. return self.label[1]
  66. @property
  67. def targets(self):
  68. return self.label[:1]
  69. @property
  70. def gate_name_plot(self):
  71. return r'$%s_%s$' % (self.gate_name_latex, str(self.k))
  72. def get_target_matrix(self, format='sympy'):
  73. if format == 'sympy':
  74. return Matrix([[1, 0], [0, exp(sign(self.k)*Integer(2)*pi*I/(Integer(2)**abs(self.k)))]])
  75. raise NotImplementedError(
  76. 'Invalid format for the R_k gate: %r' % format)
  77. Rk = RkGate
  78. class Fourier(Gate):
  79. """Superclass of Quantum Fourier and Inverse Quantum Fourier Gates."""
  80. @classmethod
  81. def _eval_args(self, args):
  82. if len(args) != 2:
  83. raise QuantumError(
  84. 'QFT/IQFT only takes two arguments, got: %r' % args
  85. )
  86. if args[0] >= args[1]:
  87. raise QuantumError("Start must be smaller than finish")
  88. return Gate._eval_args(args)
  89. def _represent_default_basis(self, **options):
  90. return self._represent_ZGate(None, **options)
  91. def _represent_ZGate(self, basis, **options):
  92. """
  93. Represents the (I)QFT In the Z Basis
  94. """
  95. nqubits = options.get('nqubits', 0)
  96. if nqubits == 0:
  97. raise QuantumError(
  98. 'The number of qubits must be given as nqubits.')
  99. if nqubits < self.min_qubits:
  100. raise QuantumError(
  101. 'The number of qubits %r is too small for the gate.' % nqubits
  102. )
  103. size = self.size
  104. omega = self.omega
  105. #Make a matrix that has the basic Fourier Transform Matrix
  106. arrayFT = [[omega**(
  107. i*j % size)/sqrt(size) for i in range(size)] for j in range(size)]
  108. matrixFT = Matrix(arrayFT)
  109. #Embed the FT Matrix in a higher space, if necessary
  110. if self.label[0] != 0:
  111. matrixFT = matrix_tensor_product(eye(2**self.label[0]), matrixFT)
  112. if self.min_qubits < nqubits:
  113. matrixFT = matrix_tensor_product(
  114. matrixFT, eye(2**(nqubits - self.min_qubits)))
  115. return matrixFT
  116. @property
  117. def targets(self):
  118. return range(self.label[0], self.label[1])
  119. @property
  120. def min_qubits(self):
  121. return self.label[1]
  122. @property
  123. def size(self):
  124. """Size is the size of the QFT matrix"""
  125. return 2**(self.label[1] - self.label[0])
  126. @property
  127. def omega(self):
  128. return Symbol('omega')
  129. class QFT(Fourier):
  130. """The forward quantum Fourier transform."""
  131. gate_name = 'QFT'
  132. gate_name_latex = 'QFT'
  133. def decompose(self):
  134. """Decomposes QFT into elementary gates."""
  135. start = self.label[0]
  136. finish = self.label[1]
  137. circuit = 1
  138. for level in reversed(range(start, finish)):
  139. circuit = HadamardGate(level)*circuit
  140. for i in range(level - start):
  141. circuit = CGate(level - i - 1, RkGate(level, i + 2))*circuit
  142. for i in range((finish - start)//2):
  143. circuit = SwapGate(i + start, finish - i - 1)*circuit
  144. return circuit
  145. def _apply_operator_Qubit(self, qubits, **options):
  146. return qapply(self.decompose()*qubits)
  147. def _eval_inverse(self):
  148. return IQFT(*self.args)
  149. @property
  150. def omega(self):
  151. return exp(2*pi*I/self.size)
  152. class IQFT(Fourier):
  153. """The inverse quantum Fourier transform."""
  154. gate_name = 'IQFT'
  155. gate_name_latex = '{QFT^{-1}}'
  156. def decompose(self):
  157. """Decomposes IQFT into elementary gates."""
  158. start = self.args[0]
  159. finish = self.args[1]
  160. circuit = 1
  161. for i in range((finish - start)//2):
  162. circuit = SwapGate(i + start, finish - i - 1)*circuit
  163. for level in range(start, finish):
  164. for i in reversed(range(level - start)):
  165. circuit = CGate(level - i - 1, RkGate(level, -i - 2))*circuit
  166. circuit = HadamardGate(level)*circuit
  167. return circuit
  168. def _eval_inverse(self):
  169. return QFT(*self.args)
  170. @property
  171. def omega(self):
  172. return exp(-2*pi*I/self.size)