-
Notifications
You must be signed in to change notification settings - Fork 0
/
shor.py
166 lines (130 loc) · 4.28 KB
/
shor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
from math import log2, gcd
from random import Random
from time import time
from datetime import datetime, timezone, timedelta
def is_prime(n: int) -> bool:
"""
Primality test using 6k+-1 optimization.
Font: https://en.wikipedia.org/wiki/Primality_test#Simple_methods
"""
if n <= 3:
return n > 1
if not n % 2 or not n % 3:
return False
i = 5
stop = int(n**0.5)
while i <= stop:
if not n % i or not n % (i + 2):
return False
i += 6
return True
def _factor_pow_a_b(N):
n = N.bit_length()
y = int(log2(N))
for b in range(2, n+1):
x = y/b
u1 = int(2**x)
u2 = u1+1
if u1**b == N:
return u1
elif u2**b == N:
return u2
def _quantum_subroutine(N, x):
from functools import reduce
from ket import quant, H, measure, adj
from ket.lib import qft
from ket.plugins import pown
n = N.bit_length()
def subroutine():
reg1 = H(quant(n))
reg2 = pown(x, reg1, N)
measure(reg2)
adj(qft, reg1)
return measure(reg1).value
r = reduce(gcd, [subroutine() for _ in range(n)])
return 2**n//r
def shor(N: int, quantum_subroutine=_quantum_subroutine, quantum: bool = False, seed=None, verbose=False, _timezone=-3) -> int:
"""
Shor's factorization algorithm
The `quantum_subroutine` must have the signature:
def quantum_subroutine(N: int, x: int) -> int:
...
Args:
N: Number to factor.
quantum_subroutine: Order-finding quantum subroutine to find the order of f(j) = x^j % N.
quantum: If True checks whether N is trivially factorable AND force use the quantum subroutine.
seed: RNG seed. Not used in quantum simulation.
verbose: If True print the result and execution time.
Returns:
A factor of the number N.
Raises:
ValueError: If quantum is True and N is trivially factorable OR N is prime.
RuntimeError: If the algorithm fails to factor N.
"""
if N < 4:
raise ValueError(f"{N} is prime")
if N % 2 == 0:
if quantum:
raise RuntimeError(f'{N} is trivially factorable')
else:
return 2
factor = _factor_pow_a_b(N)
if factor is not None:
if quantum:
raise RuntimeError(f'{N} is trivially factorable')
else:
return factor
rng = Random(seed)
factor = None
begin = time()
for _ in range(N.bit_length()):
while True:
x = rng.randint(2, N-1)
gcd_x_N = gcd(x, N)
if gcd_x_N > 1 and not quantum:
return gcd_x_N
elif gcd_x_N == 1:
break
r = quantum_subroutine(N, x)
if r % 2 == 0 and pow(x, r//2) != -1 % N:
p = gcd(x**(r//2)-1, N)
if p != 1 and p != N and p*N//p == N:
end = time()
factor = p
break
q = gcd(x**(r//2)+1, N)
if q != 1 and q != N and q*N//q == N:
end = time()
factor = q
break
if factor is not None:
if verbose:
total = end-begin
min = int(total // 60)
s = total % 60
date = datetime.now(timezone(timedelta(hours=_timezone)))
date = date.strftime('%d/%m/%Y %H:%M:%S')
print(
f"Shor's algorithm: {N}={factor}x{N//factor}\t({min}min {s:.2f}s; {date})")
return factor
elif is_prime(N):
raise ValueError(f"{N} is prime")
else:
raise RuntimeError(f"fails to factor {N}")
if __name__ == '__main__':
from itertools import combinations
prime_list = [5, 11, 17, 23, 29, 41, 47, 53, 59, 71, 83, 89, 101, 107,
113, 131, 137, 149, 167, 173, 179, 191, 197, 227, 233, 239, 251, 257, 263]
# import sys
# if sys.argv[1]:
# try:
# f = shor(int(sys.argv[1]), quantum=True, verbose=True)
# except Exception as e:
# print(e)
for N in sorted([p*q for p, q in combinations(prime_list, 2)]):
try:
f = shor(N, quantum=True, verbose=True)
except Exception as e:
print(e)
continue
assert (N == f*(N//f))