Archived
1
0
This repository has been archived on 2023-07-24. You can view files and clone it, but cannot push or open issues or pull requests.
qalg/bench-qiskit/shor_algorithm.py

377 lines
11 KiB
Python

# https://qiskit.org/documentation/tutorials/circuits/3_summary_of_quantum_operations.html
# https://github.com/olekssy/qiskit-shors/blob/master/shors.py
# https://quantumcomputinguk.org/tutorials/shors-algorithm-with-code
from math import pow,gcd,floor,isinf,ceil,log
from numpy import zeros,pi
from fractions import Fraction
from array import array
from qiskit import QuantumRegister,ClassicalRegister,QuantumCircuit
from qiskit import IBMQ
from quantum_runner import setup_IBM,quantum_simulate
def check_if_power(N):
b=2
while (2**b) <= N:
a = 1
c = N
while (c-a) >= 2:
m = int( (a+c)/2 )
if (m**b) < (N+1):
p = int( (m**b) )
else:
p = int(N+1)
if int(p) == int(N):
print('N is {0}^{1}'.format(int(m),int(b)) )
return True
if p<N:
a = int(m)
else:
c = int(m)
b=b+1
return False
def create_QFT(circuit,up_reg,n,with_swaps):
i=n-1
while i>=0:
circuit.h(up_reg[i])
j=i-1
while j>=0:
if (pi)/(pow(2,(i-j))) > 0:
circuit.crz( (pi)/(pow(2,(i-j))) , up_reg[i] , up_reg[j] )
j=j-1
i=i-1
if with_swaps==1:
i=0
while i < ((n-1)/2):
circuit.swap(up_reg[i], up_reg[n-1-i])
i=i+1
def create_inverse_QFT(circuit,up_reg,n,with_swaps):
if with_swaps==1:
i=0
while i < ((n-1)/2):
circuit.swap(up_reg[i], up_reg[n-1-i])
i=i+1
i=0
while i<n:
circuit.h(up_reg[i])
if i != n-1:
j=i+1
y=i
while y>=0:
if (pi)/(pow(2,(j-y))) > 0:
circuit.crz( - (pi)/(pow(2,(j-y))) , up_reg[j] , up_reg[y] )
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('modular inverse does not exist')
else:
return x % m
def get_factors(x_value,t_upper,N,a):
if x_value<=0:
print('x_value is <= 0, there are no continued fractions\n')
return False
# print('Running continued fractions for this case\n')
T = pow(2,t_upper)
x_over_T = x_value/T
i=0
b = array('i')
t = array('f')
b.append(floor(x_over_T))
t.append(x_over_T - b[i])
while i>=0:
if i>0:
b.append( floor( 1 / (t[i-1]) ) )
t.append( ( 1 / (t[i-1]) ) - b[i] )
aux = 0
j=i
while j>0:
aux = 1 / ( b[j] + aux )
j = j-1
aux = aux + b[0]
frac = Fraction(aux).limit_denominator()
den=frac.denominator
print('Approximation number {0} of continued fractions:'.format(i+1))
print("Numerator:{0} \t\t Denominator: {1}\n".format(frac.numerator,frac.denominator))
""" Increment i for next iteration """
i=i+1
if (den%2) == 1:
if i>=15:
print('Returning because have already done too much tries')
return False
print('Odd denominator, will try next iteration of continued fractions\n')
continue
""" If denominator even, try to get factors of N """
""" Get the exponential a^(r/2) """
exponential = 0
if den<1000:
exponential=pow(a , (den/2))
""" Check if the value is too big or not """
if isinf(exponential)==1 or exponential>1000000000:
print('Denominator of continued fraction is too big!\n')
aux_out = input('Input number 1 if you want to continue searching, other if you do not: ')
if aux_out != '1':
return False
else:
continue
putting_plus = int(exponential + 1)
putting_minus = int(exponential - 1)
one_factor = gcd(putting_plus,N)
other_factor = gcd(putting_minus,N)
""" Check if the factors found are trivial factors or are the desired
factors """
if one_factor==1 or one_factor==N or other_factor==1 or other_factor==N:
print('Found just trivial factors, not good enough\n')
if t[i-1]==0:
print('The continued fractions found exactly x_final/(2^(2n)) , leaving funtion\n')
return False
else:
""" Return if already too much tries and numbers are huge """
print('Returning because have already done too many tries\n')
return False
else:
print('The factors of {0} are {1} and {2}\n'.format(N,one_factor,other_factor))
print('Found the desired factors!\n')
return True
def getAngles(a,N):
s=bin(int(a))[2:].zfill(N)
angles=zeros([N])
for i in range(0, N):
for j in range(i,N):
if s[j]=='1':
angles[N-i-1]+=pow(2, -(j-i))
angles[N-i-1]*=pi
return angles
def ccphase(circuit,angle,ctl1,ctl2,tgt):
circuit.crz(angle/2,ctl1,tgt)
circuit.cx(ctl2,ctl1)
circuit.crz(-angle/2,ctl1,tgt)
circuit.cx(ctl2,ctl1)
circuit.crz(angle/2,ctl2,tgt)
def phiADD(circuit,q,a,N,inv):
angle=getAngles(a,N)
for i in range(0,N):
if inv==0:
circuit.p(angle[i],q[i])
else:
circuit.p(-angle[i],q[i])
def cphiADD(circuit,q,ctl,a,n,inv):
angle=getAngles(a,n)
for i in range(0,n):
if inv==0:
circuit.crz(angle[i],ctl,q[i])
else:
circuit.crz(-angle[i],ctl,q[i])
def ccphiADD(circuit,q,ctl1,ctl2,a,n,inv):
angle=getAngles(a,n)
for i in range(0,n):
if inv==0:
ccphase(circuit,angle[i],ctl1,ctl2,q[i])
else:
ccphase(circuit,-angle[i],ctl1,ctl2,q[i])
def ccphiADDmodN(circuit, q, ctl1, ctl2, aux, a, N, n):
ccphiADD(circuit, q, ctl1, ctl2, a, n, 0)
phiADD(circuit, q, N, n, 1)
create_inverse_QFT(circuit, q, n, 0)
circuit.cx(q[n-1],aux)
create_QFT(circuit,q,n,0)
cphiADD(circuit, q, aux, N, n, 0)
ccphiADD(circuit, q, ctl1, ctl2, a, n, 1)
create_inverse_QFT(circuit, q, n, 0)
circuit.x(q[n-1])
circuit.cx(q[n-1], aux)
circuit.x(q[n-1])
create_QFT(circuit,q,n,0)
ccphiADD(circuit, q, ctl1, ctl2, a, n, 0)
def ccphiADDmodN_inv(circuit, q, ctl1, ctl2, aux, a, N, n):
ccphiADD(circuit, q, ctl1, ctl2, a, n, 1)
create_inverse_QFT(circuit, q, n, 0)
circuit.x(q[n-1])
circuit.cx(q[n-1],aux)
circuit.x(q[n-1])
create_QFT(circuit, q, n, 0)
ccphiADD(circuit, q, ctl1, ctl2, a, n, 0)
cphiADD(circuit, q, aux, N, n, 1)
create_inverse_QFT(circuit, q, n, 0)
circuit.cx(q[n-1], aux)
create_QFT(circuit, q, n, 0)
phiADD(circuit, q, N, n, 0)
ccphiADD(circuit, q, ctl1, ctl2, a, n, 1)
def cMULTmodN(circuit, ctl, q, aux, a, N, n):
create_QFT(circuit,aux,n+1,0)
for i in range(0, n):
ccphiADDmodN(circuit, aux, q[i], ctl, aux[n+1], (2**i)*a % N, N, n+1)
create_inverse_QFT(circuit, aux, n+1, 0)
for i in range(0, n):
circuit.cswap(ctl,q[i],aux[i])
a_inv = modinv(a, N)
create_QFT(circuit, aux, n+1, 0)
i = n-1
while i >= 0:
ccphiADDmodN_inv(circuit, aux, q[i], ctl, aux[n+1], pow(2,i)*a_inv % N, N, n+1)
i -= 1
create_inverse_QFT(circuit, aux, n+1, 0)
def get_value_a(N):
a=2
while gcd(a,N)!=1:
a=a+1
return a
def factorize_number(N):
if N==1 or N==0:
print('Please put an N different from 0 and from 1')
exit()
""" Check if N is even """
if (N%2)==0:
print('N is even, so does not make sense!')
exit()
""" Check if N can be put in N=p^q, p>1, q>=2 """
""" Try all numbers for p: from 2 to sqrt(N) """
if check_if_power(N)==True:
exit()
# print('Not an easy case, using the quantum circuit is necessary\n')
setup_IBM()
""" Get an integer a that is coprime with N """
a = get_value_a(N)
# """ If user wants to force some values, he can do that here, please make sure to update the print and that N and a are coprime"""
# print('Forcing N=15 and a=4 because its the fastest case, please read top of source file for more info')
# N=15
# a=4
print("help !!!")
""" Get n value used in Shor's algorithm, to know how many qubits are used """
n = ceil(log(N,2))
# print('Total number of qubits used: {0}\n'.format(4*n+2))
aux = QuantumRegister(n+2)
up_reg = QuantumRegister(2*n)
down_reg = QuantumRegister(n)
up_classic = ClassicalRegister(2*n)
circuit = QuantumCircuit(down_reg , up_reg , aux, up_classic)
circuit.h(up_reg)
circuit.x(down_reg[0])
""" Apply the multiplication gates as showed in the report in order to create the exponentiation """
for i in range(0, 2*n):
cMULTmodN(circuit, up_reg[i], down_reg, aux, int(pow(a, pow(2, i))), N, n)
""" Apply inverse QFT """
create_inverse_QFT(circuit, up_reg, 2*n ,1)
""" Measure the top qubits, to get x value"""
circuit.measure(up_reg,up_classic)
""" Simulate the created Quantum Circuit """
# print(transpile(circuit,BasicAer.get_backend('qasm_simulator')))
#simulation = execute(circuit, backend=BasicAer.get_backend('qasm_simulator'),shots=number_shots)
provider = IBMQ.get_provider(hub='ibm-q', group='open', project='main')
simulation = quantum_simulate(circuit,provider.get_backend('ibmq_lima'),True)
i=0
while i < len(simulation):
print('Result \"{0}\" happened {1} times out of {2}'.format(list(simulation.keys())[i],list(simulation.values())[i]))
i=i+1
""" An empty print just to have a good display in terminal """
print(' ')
""" Initialize this variable """
# prob_success=0
""" For each simulation result, print proper info to user and try to calculate the factors of N"""
# i=0
# while i < len(counts_result):
#""" Get the x_value from the final state qubits """
# output_desired = list(sim_result.get_counts().keys())[i]
# x_value = int(output_desired, 2)
# prob_this_result = 100 * ( int( list(sim_result.get_counts().values())[i] ) ) / (number_shots)
#print("------> Analysing result {0}. This result happened in {1:.4f} % of all cases\n".format(output_desired,prob_this_result))
#""" Print the final x_value to user """
# print('In decimal, x_final value for this result is: {0}\n'.format(x_value))
#""" Get the factors using the x value obtained """
# success=get_factors(int(x_value),int(2*n),int(N),int(a))
#if success==True:
# prob_success = prob_success + prob_this_result
#i=i+1
# print("\nUsing a={0}, found the factors of N={1} in {2:.4f} % of the cases\n".format(a,N,prob_success))
N = 33
factorize_number(N)