shor.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. """Shor's algorithm and helper functions.
  2. Todo:
  3. * Get the CMod gate working again using the new Gate API.
  4. * Fix everything.
  5. * Update docstrings and reformat.
  6. """
  7. import math
  8. import random
  9. from sympy.core.mul import Mul
  10. from sympy.core.singleton import S
  11. from sympy.functions.elementary.exponential import log
  12. from sympy.functions.elementary.miscellaneous import sqrt
  13. from sympy.core.numbers import igcd
  14. from sympy.ntheory import continued_fraction_periodic as continued_fraction
  15. from sympy.utilities.iterables import variations
  16. from sympy.physics.quantum.gate import Gate
  17. from sympy.physics.quantum.qubit import Qubit, measure_partial_oneshot
  18. from sympy.physics.quantum.qapply import qapply
  19. from sympy.physics.quantum.qft import QFT
  20. from sympy.physics.quantum.qexpr import QuantumError
  21. class OrderFindingException(QuantumError):
  22. pass
  23. class CMod(Gate):
  24. """A controlled mod gate.
  25. This is black box controlled Mod function for use by shor's algorithm.
  26. TODO: implement a decompose property that returns how to do this in terms
  27. of elementary gates
  28. """
  29. @classmethod
  30. def _eval_args(cls, args):
  31. # t = args[0]
  32. # a = args[1]
  33. # N = args[2]
  34. raise NotImplementedError('The CMod gate has not been completed.')
  35. @property
  36. def t(self):
  37. """Size of 1/2 input register. First 1/2 holds output."""
  38. return self.label[0]
  39. @property
  40. def a(self):
  41. """Base of the controlled mod function."""
  42. return self.label[1]
  43. @property
  44. def N(self):
  45. """N is the type of modular arithmetic we are doing."""
  46. return self.label[2]
  47. def _apply_operator_Qubit(self, qubits, **options):
  48. """
  49. This directly calculates the controlled mod of the second half of
  50. the register and puts it in the second
  51. This will look pretty when we get Tensor Symbolically working
  52. """
  53. n = 1
  54. k = 0
  55. # Determine the value stored in high memory.
  56. for i in range(self.t):
  57. k += n*qubits[self.t + i]
  58. n *= 2
  59. # The value to go in low memory will be out.
  60. out = int(self.a**k % self.N)
  61. # Create array for new qbit-ket which will have high memory unaffected
  62. outarray = list(qubits.args[0][:self.t])
  63. # Place out in low memory
  64. for i in reversed(range(self.t)):
  65. outarray.append((out >> i) & 1)
  66. return Qubit(*outarray)
  67. def shor(N):
  68. """This function implements Shor's factoring algorithm on the Integer N
  69. The algorithm starts by picking a random number (a) and seeing if it is
  70. coprime with N. If it is not, then the gcd of the two numbers is a factor
  71. and we are done. Otherwise, it begins the period_finding subroutine which
  72. finds the period of a in modulo N arithmetic. This period, if even, can
  73. be used to calculate factors by taking a**(r/2)-1 and a**(r/2)+1.
  74. These values are returned.
  75. """
  76. a = random.randrange(N - 2) + 2
  77. if igcd(N, a) != 1:
  78. return igcd(N, a)
  79. r = period_find(a, N)
  80. if r % 2 == 1:
  81. shor(N)
  82. answer = (igcd(a**(r/2) - 1, N), igcd(a**(r/2) + 1, N))
  83. return answer
  84. def getr(x, y, N):
  85. fraction = continued_fraction(x, y)
  86. # Now convert into r
  87. total = ratioize(fraction, N)
  88. return total
  89. def ratioize(list, N):
  90. if list[0] > N:
  91. return S.Zero
  92. if len(list) == 1:
  93. return list[0]
  94. return list[0] + ratioize(list[1:], N)
  95. def period_find(a, N):
  96. """Finds the period of a in modulo N arithmetic
  97. This is quantum part of Shor's algorithm. It takes two registers,
  98. puts first in superposition of states with Hadamards so: ``|k>|0>``
  99. with k being all possible choices. It then does a controlled mod and
  100. a QFT to determine the order of a.
  101. """
  102. epsilon = .5
  103. # picks out t's such that maintains accuracy within epsilon
  104. t = int(2*math.ceil(log(N, 2)))
  105. # make the first half of register be 0's |000...000>
  106. start = [0 for x in range(t)]
  107. # Put second half into superposition of states so we have |1>x|0> + |2>x|0> + ... |k>x>|0> + ... + |2**n-1>x|0>
  108. factor = 1/sqrt(2**t)
  109. qubits = 0
  110. for arr in variations(range(2), t, repetition=True):
  111. qbitArray = list(arr) + start
  112. qubits = qubits + Qubit(*qbitArray)
  113. circuit = (factor*qubits).expand()
  114. # Controlled second half of register so that we have:
  115. # |1>x|a**1 %N> + |2>x|a**2 %N> + ... + |k>x|a**k %N >+ ... + |2**n-1=k>x|a**k % n>
  116. circuit = CMod(t, a, N)*circuit
  117. # will measure first half of register giving one of the a**k%N's
  118. circuit = qapply(circuit)
  119. for i in range(t):
  120. circuit = measure_partial_oneshot(circuit, i)
  121. # Now apply Inverse Quantum Fourier Transform on the second half of the register
  122. circuit = qapply(QFT(t, t*2).decompose()*circuit, floatingPoint=True)
  123. for i in range(t):
  124. circuit = measure_partial_oneshot(circuit, i + t)
  125. if isinstance(circuit, Qubit):
  126. register = circuit
  127. elif isinstance(circuit, Mul):
  128. register = circuit.args[-1]
  129. else:
  130. register = circuit.args[-1].args[-1]
  131. n = 1
  132. answer = 0
  133. for i in range(len(register)/2):
  134. answer += n*register[i + t]
  135. n = n << 1
  136. if answer == 0:
  137. raise OrderFindingException(
  138. "Order finder returned 0. Happens with chance %f" % epsilon)
  139. #turn answer into r using continued fractions
  140. g = getr(answer, 2**t, N)
  141. return g