270 lines
4.0 KiB
Markdown
270 lines
4.0 KiB
Markdown
|
|
||
|
```python
|
||
|
#!/usr/bin/env python3
|
||
|
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
"""
|
||
|
|
||
|
Created on Tue Jul 27 20:31:40 2021
|
||
|
|
||
|
@author: sollyboukman
|
||
|
|
||
|
"""
|
||
|
|
||
|
import matplotlib.pyplot as plt
|
||
|
|
||
|
import numpy as np
|
||
|
|
||
|
from qiskit import QuantumCircuit, Aer, transpile, assemble
|
||
|
|
||
|
from qiskit.visualization import plot_histogram
|
||
|
|
||
|
from math import gcd
|
||
|
|
||
|
from numpy.random import randint
|
||
|
|
||
|
import pandas as pd
|
||
|
|
||
|
from fractions import Fraction
|
||
|
|
||
|
from math import gcd # greatest common divisor
|
||
|
|
||
|
def c_amod15(a, power):
|
||
|
|
||
|
"""Controlled multiplication by a mod 15"""
|
||
|
|
||
|
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(power):
|
||
|
|
||
|
if a in [2,13]:
|
||
|
|
||
|
U.swap(0,1)
|
||
|
|
||
|
U.swap(1,2)
|
||
|
|
||
|
U.swap(2,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 = "%i^%i mod 15" % (a, power)
|
||
|
|
||
|
c_U = U.control()
|
||
|
|
||
|
return c_U
|
||
|
|
||
|
n_count = 8 # number of counting qubits
|
||
|
|
||
|
a = 7
|
||
|
|
||
|
def qft_dagger(n):
|
||
|
|
||
|
"""n-qubit QFTdagger the first n qubits in circ"""
|
||
|
|
||
|
qc = QuantumCircuit(n)
|
||
|
|
||
|
# Don't forget the Swaps!
|
||
|
|
||
|
for qubit in range(n//2):
|
||
|
|
||
|
qc.swap(qubit, n-qubit-1)
|
||
|
|
||
|
for j in range(n):
|
||
|
|
||
|
for m in range(j):
|
||
|
|
||
|
qc.cp(-np.pi/float(2**(j-m)), m, j)
|
||
|
|
||
|
qc.h(j)
|
||
|
|
||
|
qc.name = "QFT†"
|
||
|
|
||
|
return qc
|
||
|
|
||
|
# Create QuantumCircuit with n_count counting qubits
|
||
|
|
||
|
# plus 4 qubits for U to act on
|
||
|
|
||
|
qc = QuantumCircuit(n_count + 4, n_count)
|
||
|
|
||
|
# Initialize counting qubits
|
||
|
|
||
|
# in state |+>
|
||
|
|
||
|
for q in range(n_count):
|
||
|
|
||
|
qc.h(q)
|
||
|
|
||
|
# And auxiliary register in state |1>
|
||
|
|
||
|
qc.x(3+n_count)
|
||
|
|
||
|
# Do controlled-U operations
|
||
|
|
||
|
for q in range(n_count):
|
||
|
|
||
|
qc.append(c_amod15(a, 2**q),
|
||
|
|
||
|
[q] + [i+n_count for i in range(4)])
|
||
|
|
||
|
# Do inverse-QFT
|
||
|
|
||
|
qc.append(qft_dagger(n_count), range(n_count))
|
||
|
|
||
|
# Measure circuit
|
||
|
|
||
|
qc.measure(range(n_count), range(n_count))
|
||
|
|
||
|
qc.draw(fold=-1) # -1 means 'do not fold'
|
||
|
|
||
|
aer_sim = Aer.get_backend('aer_simulator')
|
||
|
|
||
|
t_qc = transpile(qc, aer_sim)
|
||
|
|
||
|
qobj = assemble(t_qc)
|
||
|
|
||
|
results = aer_sim.run(qobj).result()
|
||
|
|
||
|
counts = results.get_counts()
|
||
|
|
||
|
plot_histogram(counts)
|
||
|
|
||
|
def a2jmodN(a, j, N):
|
||
|
|
||
|
"""Compute a^{2^j} (mod N) by repeated squaring"""
|
||
|
|
||
|
for i in range(j):
|
||
|
|
||
|
a = np.mod(a**2, N)
|
||
|
|
||
|
return a
|
||
|
|
||
|
N = 15
|
||
|
|
||
|
np.random.seed(1) # This is to make sure we get reproduceable results
|
||
|
|
||
|
a = randint(2, 15)
|
||
|
|
||
|
print(a)
|
||
|
|
||
|
gcd(a, N)
|
||
|
|
||
|
def qpe_amod15(a):
|
||
|
|
||
|
n_count = 8
|
||
|
|
||
|
qc = QuantumCircuit(4+n_count, n_count)
|
||
|
|
||
|
for q in range(n_count):
|
||
|
|
||
|
qc.h(q) # Initialize counting qubits in state |+>
|
||
|
|
||
|
qc.x(3+n_count) # And auxiliary register in state |1>
|
||
|
|
||
|
for q in range(n_count): # Do controlled-U operations
|
||
|
|
||
|
qc.append(c_amod15(a, 2**q),
|
||
|
|
||
|
[q] + [i+n_count for i in range(4)])
|
||
|
|
||
|
qc.append(qft_dagger(n_count), range(n_count)) # Do inverse-QFT
|
||
|
|
||
|
qc.measure(range(n_count), range(n_count))
|
||
|
|
||
|
# Simulate Results
|
||
|
|
||
|
aer_sim = Aer.get_backend('aer_simulator')
|
||
|
|
||
|
# Setting memory=True below allows us to see a list of each sequential reading
|
||
|
|
||
|
t_qc = transpile(qc, aer_sim)
|
||
|
|
||
|
obj = assemble(t_qc, shots=1)
|
||
|
|
||
|
result = aer_sim.run(qobj, memory=True).result()
|
||
|
|
||
|
readings = result.get_memory()
|
||
|
|
||
|
print("Register Reading: " + readings[0])
|
||
|
|
||
|
phase = int(readings[0],2)/(2**n_count)
|
||
|
|
||
|
print("Corresponding Phase: %f" % phase)
|
||
|
|
||
|
return phase
|
||
|
|
||
|
phase = qpe_amod15(a) # Phase = s/r
|
||
|
|
||
|
Fraction(phase).limit_denominator(15) # Denominator should (hopefully!) tell us r
|
||
|
|
||
|
frac = Fraction(phase).limit_denominator(15)
|
||
|
|
||
|
s, r = frac.numerator, frac.denominator
|
||
|
|
||
|
print(r)
|
||
|
|
||
|
guesses = [gcd(a**(r//2)-1, N), gcd(a**(r//2)+1, N)]
|
||
|
|
||
|
print(guesses)
|
||
|
|
||
|
a = 7
|
||
|
|
||
|
factor_found = False
|
||
|
|
||
|
attempt = 0
|
||
|
|
||
|
while not factor_found:
|
||
|
|
||
|
attempt += 1
|
||
|
|
||
|
print("\nAttempt %i:" % attempt)
|
||
|
|
||
|
phase = qpe_amod15(a) # Phase = s/r
|
||
|
|
||
|
frac = Fraction(phase).limit_denominator(N) # Denominator should (hopefully!) tell us r
|
||
|
|
||
|
r = frac.denominator
|
||
|
|
||
|
print("Result: r = %i" % r)
|
||
|
|
||
|
if phase != 0:
|
||
|
|
||
|
# Guesses for factors are gcd(x^{r/2} ±1 , 15)
|
||
|
|
||
|
guesses = [gcd(a**(r//2)-1, N), gcd(a**(r//2)+1, N)]
|
||
|
|
||
|
print("Guessed Factors: %i and %i" % (guesses[0], guesses[1]))
|
||
|
|
||
|
for guess in guesses:
|
||
|
|
||
|
if guess not in [1,N] and (N % guess) == 0: # Check to see if guess is a factor
|
||
|
|
||
|
print("*** Non-trivial factor found: %i ***" % guess)
|
||
|
|
||
|
factor_found = True
|
||
|
```
|