Source code for shor.algorithms.shor
import math
import random
from typing import List, Tuple
from shor.gates import CNOT, CSWAP, QFT, H, Rx, X
from shor.layers import Qubits
from shor.quantum import Circuit
[docs]def factor(N: int) -> Tuple[int, int]:
"""WIP not currently functioning.
The classical part seems to be correct, but the quantum period finding function is incorrect
at the moment.
"""
while True:
a = random.randint(2, N - 1)
common_divisor = gcd(a, N)
if common_divisor > 1:
# Lucky guess
return common_divisor, N / common_divisor
r = find_period(a, N)
a_pow_r_o_2 = a ^ r // 2
if r & 1 == 1 or (a_pow_r_o_2 + 1) % N == 0:
continue
factor = max(gcd(a_pow_r_o_2 + 1, N), gcd(a_pow_r_o_2 - 1, N))
return factor, N // factor
[docs]def find_period(a, N):
"""WIP: Quantum subroutine for shor's algorithm.
Finds the period of a function of the form:
f(x) = a^x % N
This uses the quantum fourier transform.
"""
circuit = Circuit()
# circuit.add(Qubits(5))
# circuit.add(QFT(0, 1, 2, 3))
# circuit.add(X(4))
# circuit.add(quantum_amod_15(a))
# circuit.add(QFT(0, 1, 2, 3))
circuit.add(Qubits(5))
circuit.add(H(0)).add(H(1)).add(H(2)).add(H(3))
circuit.add(X(4))
circuit.add(quantum_amod_15(a))
circuit.add(QFT(3, 2, 1, 0)) # Inverse Quantum Fourier transform
job = circuit.run(1024)
result = job.result
# TODO: This is still broken, likely due to differences between Qiskit and our library
# See: https://github.com/shor-team/shor/issues/39
# from shor.utils.visual import plot_results
# plot_results(result)
most_common = result.counts.most_common()
if len(most_common) > 1:
return gcd(most_common[0][0], most_common[1][0])
return 1
[docs]def quantum_amod_15(a: int) -> Circuit:
qc = Circuit()
if a == 2:
qc.add(CSWAP(4, 3, 2))
qc.add(CSWAP(4, 2, 1))
qc.add(CSWAP(4, 1, 0))
if a == 4 or a == 11 or a == 14:
qc.add(CSWAP(4, 2, 0))
qc.add(CSWAP(4, 3, 1))
qc.add(CNOT(4, 3))
qc.add(CNOT(4, 2))
qc.add(CNOT(4, 1))
qc.add(CNOT(4, 0))
if a == 7:
qc.add(CSWAP(4, 1, 0))
qc.add(CSWAP(4, 2, 1))
qc.add(CSWAP(4, 3, 2))
qc.add(CNOT(4, 3))
qc.add(CNOT(4, 2))
qc.add(CNOT(4, 1))
qc.add(CNOT(4, 0))
if a == 8:
qc.add(CSWAP(4, 1, 0))
qc.add(CSWAP(4, 2, 1))
qc.add(CSWAP(4, 3, 2))
if a == 13:
qc.add(CSWAP(4, 3, 2))
qc.add(CSWAP(4, 2, 1))
qc.add(CSWAP(4, 1, 0))
qc.add(CNOT(4, 3))
qc.add(CNOT(4, 2))
qc.add(CNOT(4, 1))
qc.add(CNOT(4, 0))
return qc
[docs]def qft(qubits: List[int]) -> Circuit:
qc = Circuit()
for i in range(len(qubits)):
for k in range(i):
qc.add(Rx(qubits[i], qubits[k], angle=math.pi / float(2 ** (i - k))))
qc.add(H(qubits[i]))
return qc
[docs]def gcd(a: int, b: int) -> int:
"""Finds greatest common divisor between positive integers a and b
Efficient, using Euclidean algorithm.
Example:
>>> greatest_common_divisor(15, 25)
5
"""
assert a > 0 and b > 0, "Inputs should be positive integers"
to_divide, remainder = max(a, b), min(a, b) # initial divisor
divisor = 1
while remainder != 0:
divisor = remainder
remainder = to_divide
remainder = to_divide % divisor
to_divide = divisor
return divisor