Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

shor_medium

March 10, 2024

0.1 https://medium.com/qiskit/applying-shors-algorithm-bbdfd6f05f7d
0.1.1 Shor’s Algorithm
Shor’s algorithm is quantum algorithm used to find the period of cyclic or periodic functions. By
representing a product of two prime numbers, called the coprime, as a periodic function using the
modulo operator, and converting this equation into a form that a quantum computer can process,
Shor’s algorithm can determine the period of that function. Interestingly, using the period of this
function, a quantum computer could factor the coprime number. Using a quantum computer to
factor the extremely large numbers used in RSA is decades away and will require an error-corrected
device with many qubits— but today, we can at least use it to factor very small coprimes…like 15.
You review and write out each step from the notes:
Pick an integer, a, such that 1 < a < N and gcd(a, N) = 1.

0.1.2 Find the period of f(x) = a^x (mod N), where x is the function’s period
This is when you connect to your quantum computer and begin your period-finding circuit.

0.1.3 Second, you see U gates applying a unitary operator, U(x) = a^x (mod N), on
the target qubits controlled by the measurement qubits, which in your case is
U(x) = a^x mod 15
Third, you perform an inverse quantum Fourier transform on the measurement qubits. Fourth,
you measure the measurement qubits to hopefully return an exponent, x, which satisfies f(x) = a^x
(mod N).
Finally, you assemble the circuit, run it, and return possible exponents for period finding.

0.2 Factoring N
Now, you sort through the possible exponents, finding those which satisfy two constraints:

1
the exponent, x, must be even and a^(x/2) + 1 � 0 (mo
Using an applicable period, x, you can find nontrivial factors, P and Q , of N with gcd(a^(x/2) ±
1, N) .
Decrypting 213 This is the moment you’ve been waiting for! Quickly, you use the factors P and Q
to restore the incomplete private key.d N)

[1]: # imports for RSA


from numpy import gcd
from numpy.random import seed, randint
# imports for Shor
from qiskit import QuantumCircuit, Aer, execute
from qiskit.visualization import plot_histogram
from qiskit.circuit.library import QFT

[2]: code = 213


N = 15
print(f"You know 3 things:\n\t1. a company is going to report high earnings,",␣
↪f"\n\t2. that company's encrypted stock listing is `{code}`, and", f"\n\t3.␣

↪the key's coprime, N, is {N}.")

You know 3 things:


1. a company is going to report high earnings,
2. that company's encrypted stock listing is `213`, and
3. the key's coprime, N, is 15.

[3]: def rsa(P, Q):


N = P * Q # modulus <-- the hard number to crack!

if N % 2 == 0:
val = P if P % 2 == 0 else Q
raise ValueError(f"{N} can not be divisible by 2.",
f"{P} and {Q} are incompatible with Shor's Algorithm.")

L = (Q - 1) * (P - 1) # number of non-common factors (1, N)

for E in range(2, L): # between [2, L)


if gcd(L, E) * gcd(N, E) == 1: # coprime with both L and N
break # E is public value

D = 1
while True:
if D * E % L == 1 and D != E and D != N:
break # D is private value
D += 1

return ((E, N), (D, N))

2
def dec(code, key):
D, N = key
return "".join([chr(((d**D) % N) + ord('A'))
for d in [int(d) for d in str(code)]])

[4]: P = Q = 0

print("Guesses:")
for i in range(0, 5, 2):
print(f"\t{i//2 + 1}. {dec(code, (i, N))}")

Guesses:
1. BBB
2. EBJ
3. BBG

[5]: N = 15

[6]: seed(1)

a = randint(2, N) # 1 < a < N

if gcd(a, N) == 1: # a shares no factors


print(f"{1} < {a} < {N}, {1 < a < N}")
else: # a shares a factor
P = gcd(a, N)
Q = N // gcd(a, N)
print(f"P = {P}\nQ = {Q}\n\n",
f"{P} x {Q} = {N}, {P * Q == N}\n")
print("You got lucky! You can skip to the Decypting 213 section, I guess.␣
↪�")

1 < 7 < 15, True

[7]: def initialize_qubits(qc, n, m):


qc.h(range(n)) # apply hadamard gates
qc.x(n+m-1) # set qubit to 1

[8]: print(f"Which in your case is\n\tU(x) = a^x mod {N}")


def a_x_mod15(a, x):
if a not in [2,7,8,11,13]:
raise ValueError("'a' must be 2,7,8,11 or 13")
U = QuantumCircuit(4)
for iteration in range(x):
if a in [2,13]:
U.swap(0,1)
U.swap(1,2)
U.swap(2,3)

3
if a in [7,8]:
U.swap(2,3)
U.swap(1,2)
U.swap(0,1)
if a == 11:
U.swap(1,3)
U.swap(0,2)
if a in [7,11,13]:
for q in range(4):
U.x(q)
U = U.to_gate()
U.name = f"U({x})"
c_U = U.control()
return c_U
def modular_exponentiation(qc, n, m, a):
for x in range(n):
exponent = 2**x
qc.append(a_x_mod15(a, exponent),
[x] + list(range(n, n+m)))

Which in your case is


U(x) = a^x mod 15

[9]: def apply_iqft(qc, measurement_qubits):


qc.append(QFT(len(measurement_qubits),
do_swaps=False).inverse(),
measurement_qubits)

[10]: def measure(qc, n):


qc.measure(n, n)

[11]: def period_finder(n, m, a):

# set up quantum circuit


qc = QuantumCircuit(n+m, n)

# initialize the qubits


initialize_qubits(qc, n, m)
qc.barrier()

# apply modular exponentiation


modular_exponentiation(qc, n, m, a)
qc.barrier()

# apply inverse QFT


apply_iqft(qc, range(n))
qc.barrier()

4
# measure the n measurement qubits
measure(qc, range(n))

return qc

[12]: n = 4; m = 4

qc = period_finder(n, m, a)
qc.draw(output='mpl')

C:\Users\Chaitanya\anaconda3\Lib\site-
packages\qiskit\visualization\circuit\matplotlib.py:266: FutureWarning: The
default matplotlib drawer scheme will be changed to "iqp" in a following
release. To silence this warning, specify the current default explicitly as
style="clifford", or the new default as style="iqp".
self._style, def_font_ratio = load_style(self._style)
[12]:

[13]: simulator = Aer.get_backend('qasm_simulator')


counts = execute(qc, backend=simulator).result().get_counts(qc)

plot_histogram(counts)

C:\Users\Chaitanya\AppData\Local\Temp\ipykernel_134132\2714375574.py:1:
DeprecationWarning: The 'qiskit.Aer' entry point is deprecated and will be
removed in Qiskit 1.0. You should use 'qiskit_aer.Aer' directly instead.

5
simulator = Aer.get_backend('qasm_simulator')
C:\Users\Chaitanya\AppData\Local\Temp\ipykernel_134132\2714375574.py:2:
DeprecationWarning: The function ``qiskit.execute_function.execute()`` is
deprecated as of qiskit 0.46.0. It will be removed in the Qiskit 1.0 release.
This function combines ``transpile`` and ``backend.run``, which is covered by
``Sampler`` :mod:`~qiskit.primitives`. Alternatively, you can also run
:func:`.transpile` followed by ``backend.run()``.
counts = execute(qc, backend=simulator).result().get_counts(qc)
[13]:

[14]: # convert and add binary periods to list


counts_dec = sorted([int(measured_value[::-1], 2)
for measured_value in counts])

print("Measured periods:", end='\t')


for measured_value in counts_dec:
print(measured_value, end='\t')

Measured periods: 0 4 5 6 7 8 9
10 11 12 13 14 15

[15]: factors = set()

6
for x in counts_dec:
guesses = [gcd(int((a ** (measured_value/2))) + 1, N),
gcd(int((a ** (measured_value/2))) - 1, N)]
for guess in guesses:
# ignore trivial factors
if guess != 1 and guess != N and N % guess == 0:
factors.add(guess)

if len(factors):
P = factors.pop()
Q = factors.pop() if len(factors) else N // P
print(f"P = {P}\nQ = {Q}\n\n",
f"{P} x {Q} = {N}, {P * Q == N}")
else:
print("Shor's Algorithm Failed. Choose a different 'a'.")

P = 3
Q = 5

3 x 5 = 15, True

[16]: _, priv_key = rsa(P, Q)


print("Using RSA and Shor's Algorithm,",
f"you determine the private key to be:\n\t{priv_key}")

Using RSA and Shor's Algorithm, you determine the private key to be:
(23, 15)

[17]: dec_str = dec(code, priv_key)


print(f"You learn that the decrypted listing is {dec_str}!")

You learn that the decrypted listing is IBM!

[ ]:

You might also like