##############################
## IMPORTS ##
##############################
# greatest common divisor
from math import gcd
# Needed for pi and a few other things
import numpy as np
# For the continued fractions
from fractions import Fraction
# Needed to build the quantum circuit
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
# To simulate running the quantum circuit, transpile any built gates, and
# visualuze the results of the simulation as a histogram
from qiskit_aer import AerSimulator
from qiskit import transpile
from qiskit.visualization import plot_histogram
# Needed to get the inverse QFT
from qiskit.circuit.library import QFT
Shor’s Factoring Algorithm
This code is modified from the Qiskit Textbook Shor’s Algorithm Code
##############################
## U 7MOD15 ##
##############################
def U_7mod15(power):
"""
Presented withouth proof, this gate is used to factor the number 15
if a = 7.
Inputs:
power (an int): The number of times the gate is to be applied to the
circuit
Returns:
c_U (a qiskit quantum circuit): A controlled version of the U gate needed
by Shor's algorithm
"""
# Define a
= 7
a # This gate will need more than one qubit, in this case the gate needs four qubits
= QuantumCircuit(4)
U # Apply the gate as many times as needed, the gate consists of swapping the four qubits
# and then applying NOT gates
for iteration in range(power):
0,1)
U.swap(1,2)
U.swap(2,3)
U.swap(for q in range(4):
U.x(q)# Take the finished quantum circuit and make it into a gate
= U.to_gate()
U # Give the circuit a name to be displayed in the circuit diagram
# Note that this can also be accomplished using the name argument in
# the QuantumCircuit functions
= f"{a}^{power} mod 15"
U.name # Make the gate a controlled gate and then return it
= U.control()
c_U return c_U
##############################
## CREATE QUANTUM CIRCUIT ##
##############################
# Specify variables
= 8 # number of counting qubits
counting_qubits = 15 # The number of factor
N = 7 # A number such that 1<a<N and gcd(a,N) = 1
a
# Create QuantumCircuit with specified counting qubits
# plus 4 qubits for U to act on (the U gate needs 4 qubits)
= QuantumRegister(counting_qubits + 4)
q = ClassicalRegister(counting_qubits)
c = QuantumCircuit(q,c)
qc
# Initialize counting qubits
# in state |+>
for q in range(counting_qubits):
qc.h(q)
# And auxiliary register in state |1>
qc.x(counting_qubits)
# Do controlled-U operations
for q in range(counting_qubits):
# Control qubit + Target qubits (the last for qubits)
2**q), [q] + [i+counting_qubits for i in range(4)])
qc.append(U_7mod15(
# Do inverse-QFT
= qc.compose(QFT(counting_qubits, inverse=True), range(counting_qubits))
qc
# Measure circuit
range(counting_qubits), range(counting_qubits))
qc.measure(
# Draw the circuit
="mpl", fold=-1) # -1 means 'do not fold' qc.draw(output
##############################
## SIMULATE QUANTUM CIRCUIT ##
##############################
# Define the simulator, transpile the circuit, simulate the circuit, and then print
# a histogram of the results
= AerSimulator()
aer_sim = transpile(qc, aer_sim)
t_qc = aer_sim.run(t_qc).result().get_counts()
results plot_histogram(results)
##############################
## CONVERT TO DECIMAL ##
##############################
def convert_to_decimal(b):
"""
Converts a binary string to an decimal using the quantum phase estimation
algorithm
Inputs:
b (a binary string)
Returns:
sum (a float): the binary string converted to a decimal
"""
sum = 0
for i in range(len(b)):
sum += int(b[i])/2**(i+1)
return sum
# Note that unlike previous iterations of this function, we do not want to convert
# the result of the QPE algorithm to an angle, so we do not multiply by 2*pi
##########################################
## CONVERT SIMULATION RESULTS TO ANSWER ##
##########################################
# Sort the returned dictionary in order of counts of each result
= sorted(results, key=results.get, reverse=True)
sorted_results
# For each possible result, convert it to a decimal
= [convert_to_decimal(i) for i in sorted_results]
s_over_r_decimal
# For each possible decimal approximation of s/r, use continued fractions to
# get a more usable representation. Note that the limit_denominator function
# will limit the denomonator of the returned fraction to equal to or below the
# given number (r must be less than 15).
= [Fraction(i).limit_denominator(N) for i in s_over_r_decimal]
s_over_r
# Extract the denominator (or r) from all of the fraction approximations to
# s.r
= [f.denominator for f in s_over_r]
r_guess
# r must be even, so keep only the even values
= [r for r in r_guess if r % 2 == 0]
r_guess
# Define a^(r/2), int because the gcd function must have an int input
= [int(a**(r/2)) for r in r_guess]
a_r_2
# The possible factors of N can be found using a, r, and the gcd function
= [gcd(num+1,15) for num in a_r_2]
factor1 = [gcd(num-1,15) for num in a_r_2]
factor2
# Print the guessed r values as well as the factors found with each r value
# and with each equation
print(r_guess)
print(factor1)
print(factor2)
[2, 4, 4]
[1, 5, 5]
[3, 3, 3]