Traktor/myenv/Lib/site-packages/sympy/physics/quantum/shor.py
2024-05-26 05:12:46 +02:00

174 lines
5.4 KiB
Python

"""Shor's algorithm and helper functions.
Todo:
* Get the CMod gate working again using the new Gate API.
* Fix everything.
* Update docstrings and reformat.
"""
import math
import random
from sympy.core.mul import Mul
from sympy.core.singleton import S
from sympy.functions.elementary.exponential import log
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.core.numbers import igcd
from sympy.ntheory import continued_fraction_periodic as continued_fraction
from sympy.utilities.iterables import variations
from sympy.physics.quantum.gate import Gate
from sympy.physics.quantum.qubit import Qubit, measure_partial_oneshot
from sympy.physics.quantum.qapply import qapply
from sympy.physics.quantum.qft import QFT
from sympy.physics.quantum.qexpr import QuantumError
class OrderFindingException(QuantumError):
pass
class CMod(Gate):
"""A controlled mod gate.
This is black box controlled Mod function for use by shor's algorithm.
TODO: implement a decompose property that returns how to do this in terms
of elementary gates
"""
@classmethod
def _eval_args(cls, args):
# t = args[0]
# a = args[1]
# N = args[2]
raise NotImplementedError('The CMod gate has not been completed.')
@property
def t(self):
"""Size of 1/2 input register. First 1/2 holds output."""
return self.label[0]
@property
def a(self):
"""Base of the controlled mod function."""
return self.label[1]
@property
def N(self):
"""N is the type of modular arithmetic we are doing."""
return self.label[2]
def _apply_operator_Qubit(self, qubits, **options):
"""
This directly calculates the controlled mod of the second half of
the register and puts it in the second
This will look pretty when we get Tensor Symbolically working
"""
n = 1
k = 0
# Determine the value stored in high memory.
for i in range(self.t):
k += n*qubits[self.t + i]
n *= 2
# The value to go in low memory will be out.
out = int(self.a**k % self.N)
# Create array for new qbit-ket which will have high memory unaffected
outarray = list(qubits.args[0][:self.t])
# Place out in low memory
for i in reversed(range(self.t)):
outarray.append((out >> i) & 1)
return Qubit(*outarray)
def shor(N):
"""This function implements Shor's factoring algorithm on the Integer N
The algorithm starts by picking a random number (a) and seeing if it is
coprime with N. If it is not, then the gcd of the two numbers is a factor
and we are done. Otherwise, it begins the period_finding subroutine which
finds the period of a in modulo N arithmetic. This period, if even, can
be used to calculate factors by taking a**(r/2)-1 and a**(r/2)+1.
These values are returned.
"""
a = random.randrange(N - 2) + 2
if igcd(N, a) != 1:
return igcd(N, a)
r = period_find(a, N)
if r % 2 == 1:
shor(N)
answer = (igcd(a**(r/2) - 1, N), igcd(a**(r/2) + 1, N))
return answer
def getr(x, y, N):
fraction = continued_fraction(x, y)
# Now convert into r
total = ratioize(fraction, N)
return total
def ratioize(list, N):
if list[0] > N:
return S.Zero
if len(list) == 1:
return list[0]
return list[0] + ratioize(list[1:], N)
def period_find(a, N):
"""Finds the period of a in modulo N arithmetic
This is quantum part of Shor's algorithm. It takes two registers,
puts first in superposition of states with Hadamards so: ``|k>|0>``
with k being all possible choices. It then does a controlled mod and
a QFT to determine the order of a.
"""
epsilon = .5
# picks out t's such that maintains accuracy within epsilon
t = int(2*math.ceil(log(N, 2)))
# make the first half of register be 0's |000...000>
start = [0 for x in range(t)]
# Put second half into superposition of states so we have |1>x|0> + |2>x|0> + ... |k>x>|0> + ... + |2**n-1>x|0>
factor = 1/sqrt(2**t)
qubits = 0
for arr in variations(range(2), t, repetition=True):
qbitArray = list(arr) + start
qubits = qubits + Qubit(*qbitArray)
circuit = (factor*qubits).expand()
# Controlled second half of register so that we have:
# |1>x|a**1 %N> + |2>x|a**2 %N> + ... + |k>x|a**k %N >+ ... + |2**n-1=k>x|a**k % n>
circuit = CMod(t, a, N)*circuit
# will measure first half of register giving one of the a**k%N's
circuit = qapply(circuit)
for i in range(t):
circuit = measure_partial_oneshot(circuit, i)
# Now apply Inverse Quantum Fourier Transform on the second half of the register
circuit = qapply(QFT(t, t*2).decompose()*circuit, floatingPoint=True)
for i in range(t):
circuit = measure_partial_oneshot(circuit, i + t)
if isinstance(circuit, Qubit):
register = circuit
elif isinstance(circuit, Mul):
register = circuit.args[-1]
else:
register = circuit.args[-1].args[-1]
n = 1
answer = 0
for i in range(len(register)/2):
answer += n*register[i + t]
n = n << 1
if answer == 0:
raise OrderFindingException(
"Order finder returned 0. Happens with chance %f" % epsilon)
#turn answer into r using continued fractions
g = getr(answer, 2**t, N)
return g