from qiskit import QuantumCircuit
from qiskit.visualization import plot_bloch_multivector
from qiskit.quantum_info import Statevector
Algorytmy kwantowe - przegląd
Deutsch Algorithm
Wyobraźmy sobie, ze ktoś ukrywa dwie monety w rękach. Poniewaz kazda moneta ma dwie strony (niezalezne) dwie ręcę mogą ukryć 4 mozliwe sytuacje. Jeśli w obu rękach mamy te same strony mozemy powiedziec o stałej funkcji
, która przyjmuje lewą rękę a zwraca prawą. Jeśli wynik ulega zmianie mozemy powiedziec o funkcji zbalansowanej
.
W przypadku monet, nawet jeśli odsłonimy monetę w lewej ręce nie jesteśmy w stanie stwierdzić, bez sprawdzenia, co jest w prawej ręce. A teraz wyobraź sobie 100 takich rąk. Aby mieć pewność czy masz doczynienia z funkcją stałą musisz (czy chcesz czy nie) sprawdzić wszystkie mozliwości - wystarczy jedna róznica, aby funkcja była zbalansowana.
Co powiesz jeśli stwierdzę, ze kwantowy komputer moze rozwiązać problem otwierając wszystkie ręcę za jednym razem?
Dla funkcji boolowskiej \(f:\{0,1\} \rightarrow \{0, 1\}\) mówimy, ze \(f\) jest zbalansowana jeśli \(f(0) \neq f(1)\) lub stała jeśli \(f(0) = f(1)\).
\[ f(0) = 0 = \begin{pmatrix} 1 \\ 0 \end{pmatrix} \] \[ f(1) = 0 = \begin{pmatrix} 1 \\ 0 \end{pmatrix} \] Taka funkcja moze być reprezentowana jako macierz: \[ f = \begin{pmatrix} 1 \,\, 1 \\0 \,\, 0 \end{pmatrix} \]
Jednak, aby była uzyteczna dla obliczeń kwantowych musi być odwracalna i unitarna.
Aby sprawdzić i jednoznacznie stwierdzić czy funkcja, którą się posługujemy jest stała czy zbalansowana w klasycznym przypadku musimy sprawdzić oba wyniki \(f(0)\) i \(f(1)\).
Bardzo często tego typu modelowanie (funkcję) nazywa się black box
lub oracle
.
Tworzymy dwa rejestry kwantowe - input \(|x\rangle\) i output \(|y\rangle\)
Definiujemy rejestr outputu jako wynik działania XOR na rejestrze inputu: \[ U_f: |x\rangle ,|y\rangle \to |x\rangle, |y \oplus f(x)\rangle \] \[ U^{-1}_f: |x\rangle, |y \oplus f(x)\rangle \to |x\rangle ,|y\rangle \]
Krok 1: tworzymy obwod kwantowy dla dwóch kubitów: - pierwszy w stanie 0 - drugi w stanie 1
# krok 1 2 kubitowy circuit
= QuantumCircuit(2,1)
qc 0)
qc.i(1)
qc.x(='mpl') qc.draw(output
Krok 2: hadamard na oba kubity
# krok 2 hadamard na oba kubity - tworzymy superpozycje aby działać na 4 stanach jednocześnie
0)
qc.h(1)
qc.h(='mpl') qc.draw(output
= Statevector(qc)
state =True)) display(plot_bloch_multivector(state, reverse_bits
'latex') state.draw(
\[\frac{1}{2} |00\rangle+\frac{1}{2} |01\rangle- \frac{1}{2} |10\rangle- \frac{1}{2} |11\rangle\]
Po zastosowaniu bramek hadamarda, otrzymujemy: \[ \frac{1}{2}(|00\rangle + |01\rangle - |10\rangle - |11\rangle) \,\,\,\,\,\,\,\, Eq. 1 \]
Zauwazmy ze drugi kubit jest w stanie - (\(H|1\rangle\)), mozemy zdefiniować powyzsze rownanie jako:
\[ \frac{(-1)^{f(0)}|0\rangle+(-1)^{f(1)}|1\rangle}{\sqrt{2}}\cdot{\frac{(|0\rangle-|1\rangle)}{\sqrt{2}}}\]
Jeśli \(f\) jest stała, wtedy \(f(0)=0\) i \((-1)^0=1\) oraz \(f(1)=0\)
otrzymujemy:
\[\pm{\frac{(|0\rangle+|1\rangle)}{\sqrt{2}}}{\frac{(|0\rangle-|1\rangle)}{\sqrt{2}}}\]
W przeciwnym przypadku (\(f\) jest zbalansowana): \[\pm{\frac{(|0\rangle-|1\rangle)}{\sqrt{2}}}{\frac{(|0\rangle-|1\rangle)}{\sqrt{2}}}\]
Dla stałej funkcji pierwszy kubit jest ustawiony jako: \[{\frac{(|0\rangle+|1\rangle)}{\sqrt{2}}}\] ponowne zastosowanie bramki H zwroci nam stan \(|0\rangle\).
Analogicznie, dla zbalansowanej funkcji dostaniemy stan \(|1\rangle\).
Na podstawie tej informacji, mierząc TYLKO pierwszy kubit (po zastosowaniu bramki H) otrzymamy stan \(|0\rangle\) albo \(|1\rangle\) co daje jednoznaczą odpowiedź z jaką funkcją mamy do czynienia.
Następnym krokiem jest zastosowanie bramki działającej na dwóch kubitach. \[
U_f: \ket{x} ,\ket{y} \to \ket{x}, \ket{y \oplus f(x)}
\] Zakładając \(f(x)=x\) wyrocznię (oracle) mozna zrealizować jako bramkę CNOT
.
Jeśli \(f(x)=0\) wyrocznią jest bramka Id
.
0,1)
qc.cx(='mpl') qc.draw(output
0)
qc.h(1)
qc.h(='mpl') qc.draw(output
= Statevector(qc)
state =True)) display(plot_bloch_multivector(state, reverse_bits
'latex') state.draw(
\[ |11\rangle\]
from qiskit import execute, Aer
0,0)
qc.measure(='mpl'))
display(qc.draw(output
= Aer.get_backend('qasm_simulator')
backend = execute(qc, backend, shots=1000).result()
result = result.get_counts(qc) counts
from qiskit.visualization import plot_histogram
display(plot_histogram(counts))
= QuantumCircuit(2,1)
qc2 0)
qc2.h(1)
qc2.x(0)
qc2.h(1)
qc2.h(0,0)
qc2.measure(='mpl'))
display(qc2.draw(output= Aer.get_backend('qasm_simulator')
backend = execute(qc2, backend, shots=1000).result()
result2 = result2.get_counts(qc2)
counts2 display(plot_histogram(counts2))
Szyfrowanie
BB84 - Kawantowa dystrybucja klucza
Kwantowa dystrybucja klucza z ang. Quantum Key Distribution – QKD.
Pytania nad którymi warto się zastanowić:
- Jak dzielić klucz bez jego fizycznego wysyłania do odbiorcy przez sieć?
- Jak zakodować tekst z uzyciem klucza (To juz znamy z XOR)?
- Dlaczego QKD działa?
- Jak zasymulować algorytm BB84 w qiskit?
Rozwazmy klasyczną Alicję i Boba, którzy chcą podzielić się poufną informacją. Zawartość przekazywanej wiadomości nie ma znaczenia.
Tworzenie klucza
Alicja i Bob wymieniają się inforamcją o bazach i patrzą tylko na te kubity dla których pomiary wykonywane były w tej samej bazie. Prawdopodobieństwo, ze ich bazy się zgadzają wynosi 1/2.
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
= QuantumRegister(10)
q = ClassicalRegister(10)
c
= QuantumCircuit(q,c)
qc
qc.h(q)
qc.barrier()
qc.measure(q,c)'mpl') qc.draw(
= execute(qc, Aer.get_backend('qasm_simulator'), shots=1)
job = job.result().get_counts()
counts counts
{'1100001100': 1}
= list(counts)[0]
wiadomosc = wiadomosc[::-1] wiadomosc_odw
Moze przekazać je bezpośrednio do Boba “kablem”. Jednak w trakcie przekazywania informacji moze przejąć ją kazdy (Eve), kto tylko podłączy się “gdzieś w środku”. Przejmie bity i ich kopię prześle do Boba.
Poniewaz znamy juz zasady kwantowego generowania obwodów zobaczmy czy dodanie bramki hadamarda cos zmieni. Alicja po wygenerowaniu losowej informacji przetwarza kazdy pojedynczy bit bramką Hadamarda. Bob podczas odczytu korzysta z faktu, iz ponowne zastosowanie bramki Hadamarda pozwoli odzyskać zakodowany bit.
from random import randrange
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute, Aer
= QuantumRegister(10)
q = ClassicalRegister(10)
c
= QuantumCircuit(q,c)
qc
for i, bit in enumerate(wiadomosc_odw):
if bit == '1':
qc.x(q[i])else:
id(q[i])
qc.
qc.h(q)
qc.barrier()
qc.h(q)
qc.barrier()
qc.measure(q,c)'mpl'))
display(qc.draw(
= execute(qc, Aer.get_backend('qasm_simulator'), shots=1)
job = job.result().get_counts()
counts print(f"Alicja: {wiadomosc}, Bob: {counts}")
Alicja: 1100001100, Bob: {'1100001100': 1}
Skoro Bob w prosty sposób moze odzyskać informację Eve równiez moze zastosować analogiczny proces. Najpierw zastosuje bramkę H, odczyta informację i wygeneruje kopię informacji klasycznej przemnozoną przez bramki Hadamarda. Bob nadal odbierze prawidłową inforamcję.
Czy istnieje jakaś mozliwosc zabezpieczenia sie przed odzyskaniem informacji przez Eve? Pokazaliśmy właśnie, ze sama bramka H nie wystarczy.
Pomysł Alicji jest następujący:
- Zastosujmy bramki hadamarda do losowo wybranych bitów informacji.
Czy widzisz analogię z losowaniem bazy ??
Postępując w ten sposób Eve nie ma mozliwosci zdecydowac dla których bitów powinna zastosować H a dla ktorych nie. A to znaczy, ze rowniez nie ma mozliwosci wysłać skopiowanej informacji dla Boba. Stanu kubitu nie da się sklonować.
Pytanie co ma zrobić Bob. Przekazanie informacji, które bity zostały potraktowane bramką H, w sposób klasyczny nie ma sensu.
Odpowiedzią jest schemat znany jako BB84 zaproponowany przez Chales’a Bennett’a i Gilles’a Brassard’a w 1984.
Załózmy, ze kazdy (Alicja i Bob) mają własne generatory losowego przypisywania bramek H.
= QuantumRegister(10)
q = ClassicalRegister(10)
c
= QuantumCircuit(q,c)
qc # Alicja
for i, bit in enumerate(wiadomosc_odw):
if bit == '1':
qc.x(q[i])else:
id(q[i])
qc.
for i in range(10):
if randrange(2) == 0:
qc.h(q[i])
qc.barrier()# Bob
= []
bob_pattern for i in range(10):
if randrange(2) == 0:
qc.h(q[i])'H')
bob_pattern.append(else:
'-')
bob_pattern.append(
qc.barrier()
qc.measure(q,c)'mpl'))
display(qc.draw(
= execute(qc, Aer.get_backend('qasm_simulator'), shots=1)
job = job.result().get_counts()
counts print(f"Alicja: {wiadomosc}, Bob: {counts}")
print("bob's hadamard pattern",bob_pattern)
Alicja: 1100001100, Bob: {'1100001101': 1}
bob's hadamard pattern ['H', '-', '-', 'H', 'H', '-', 'H', 'H', '-', '-']
Jakie mozliwosci się pojawiają
- Oboje sosują H do swoich bitów albo oboje ich nie stosują - dzięki temu Bob odzyskuje prawidłowy Bit.
- Jedno stosuje a drugie nie stosuje bramki H. Tutaj niezaleznie, od opcji Bob zawsze zmierzy losowo 0 lub 1.
Prawdopodobieństwo, ze oboje wybrali to samo = \(1/2\)
Trochę inne podejście:
Alicja genruje dwa losowe stringi bitowe (zawierają tylko 0 i 1). - pierwszy string koduje dwie bazy: 0 to baza obliczeniowa a 1 to baza Hadamarda. - drugi koduje stany kubitów: w bazie hadamarda 0 to stan “+” a 1 to stan “-”, w bazie obliczeniowej – wiadomo.
Alicja przesyła do Boba 10 kubitów.
from qiskit import QuantumCircuit, execute, Aer
from qiskit.visualization import plot_histogram, plot_bloch_multivector
from numpy.random import randint
import numpy as np
from qiskit.providers.aer import QasmSimulator
%matplotlib inline
= 10
num_qubits = np.random.randint(2, size=num_qubits)
alice_basis = np.random.randint(2, size=num_qubits)
alice_state = np.random.randint(2, size=num_qubits)
bob_basis
print(f"Alice's State:\t {np.array2string(alice_state)}")
print(f"Alice's Bases:\t {np.array2string(alice_basis)}")
print(f"Bob's Bases:\t {np.array2string(bob_basis)}")
Alice's State: [1 0 1 0 0 1 1 0 0 1]
Alice's Bases: [1 0 1 0 0 0 0 0 1 1]
Bob's Bases: [1 0 0 1 0 1 1 1 0 1]
- zawsze gdy chcemy przeslac 1 w kubicie alicja aplikuje bramkę X do odpowiedniego kubitu. Dla 0 nie musi podejmować zadnego działania (lub id).
- Jeśli ma zakodować coś w bazie Hadamarda uzywa bramki Hadamarda na odpowiedni kubit
- wysyła przygotowane kubity do Boba
- Bob mierzy kubity zgodnie z przygotowanym przez siebie stringiem baz. Jeśli ma zmierzyć coś w bazie Hadamarda aplikuje bramkę H. Jeśli w bazie obliczeniowej nie robi niczego.
def bb84_circuit(state, basis, measurement_basis):
#state: array of 0s and 1s denoting the state to be encoded
#basis: array of 0s and 1s denoting the basis to be used for encoding
#0 -> Computational Basis
#1 -> Hadamard Basis
#meas_basis: array of 0s and 1s denoting the basis to be used for measurement
#0 -> Computational Basis
#1 -> Hadamard Basis
= len(state)
num_qubits
= QuantumCircuit(num_qubits)
circuit
# Sender prepares qubits
for i in range(len(basis)):
if state[i] == 1:
circuit.x(i)if basis[i] == 1:
circuit.h(i)
# Measuring action performed by Bob
for i in range(len(measurement_basis)):
if measurement_basis[i] == 1:
circuit.h(i)
circuit.measure_all()
return circuit
= bb84_circuit(alice_state, alice_basis, bob_basis)
circuit = execute(circuit.reverse_bits(),backend=QasmSimulator(),shots=1).result().get_counts().most_frequent()
key = ''
encryption_key for i in range(num_qubits):
if alice_basis[i] == bob_basis[i]:
+= str(key[i])
encryption_key print(f"Key: {encryption_key}")
Key: 1001
Other, a nice version from book
import random
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, Aer, execute
= 100
NUMBER_OF_CIRCUITS = True
DOES_EVE_EXIST = u'\u2713'
CHECK_MARK
def create_circuits(how_many, does_eve_exist):
= []
circuits for i in range(how_many):
circuits.append(make_new_circuit(does_eve_exist))return circuits
def make_new_circuit(eve_exists):
= create_registers(eve_exists)
circ = circ.qubits[0]
alice_q = circ.qubits[1]
bob_q = circ.clbits[0]
bob_c = setup_alice(circ)
circ
circ.swap(alice_q, bob_q)if eve_exists:
= setup_eve(circ)
circ = setup_bob(circ)
circ return circ
def create_registers(eve_exists):
= QuantumRegister(1, 'alice_q')
alice_q = QuantumRegister(1, 'bob_q')
bob_q = ClassicalRegister(1, 'bob_c')
bob_c if eve_exists:
= ClassicalRegister(1, 'eve_c')
eve_c = QuantumCircuit(alice_q, bob_q, bob_c, eve_c)
circ else:
= QuantumCircuit(alice_q, bob_q, bob_c)
circ return circ
def setup_alice(circ):
= circ.qubits[0]
alice_q if random.getrandbits(1):
circ.x(alice_q)if random.getrandbits(1):
circ.h(alice_q)return circ
def setup_bob(circ):
= circ.qubits[1]
bob_q = circ.clbits[0]
bob_c if random.getrandbits(1):
circ.h(bob_q)
circ.measure(bob_q, bob_c)return circ
def setup_eve(circ):
= circ.qubits[1]
bob_q = circ.clbits[1]
eve_c
circ.barrier()
circ.measure(bob_q, eve_c)
circ.barrier()return circ
def run_the_job(circuits):
= Aer.get_backend('qasm_simulator')
device = execute(circuits, backend=device, shots=1, memory=True)
job return job.result()
def print_alice_bits(circuits):
print('alice bits: ', end='')
for circ in circuits:
= 1 if 'x' in circ.count_ops() else 0
bit print(bit, end='')
print('')
def print_bob_bits(circuits, result):
print('bob bits : ', end='')
for circ in circuits:
= result.get_memory(circ)
memory print(bob_bit_value(circ, memory), end='')
print('')
def bob_bit_value(circ, memory):
return memory[0][0]
def had_agreement(circ):
= circ.count_ops()
gate_counts return not ('h' in gate_counts and gate_counts['h'] == 1)
def print_had_agreements(circuits):
= 0
number_of_agreements print('hads agree? ', end='')
for circ in circuits:
if had_agreement(circ):
print(CHECK_MARK, end='')
+= 1
number_of_agreements else:
print(' ', end='')
print('')
return number_of_agreements
def print_bit_agreements(circuits, result,number_of_agreements):
= 0
number_tested = False
is_eve_detected = 0
i print('bits agree? ', end='')
while number_tested < number_of_agreements // 2:
if had_agreement(circuits[i]):
if bit_value_agreement(circuits[i], result):
print(CHECK_MARK, end='')
+= 1
number_tested else:
= True
is_eve_detected print('X')
break
else:
print(' ', end='')
+= 1
i print()
return i, is_eve_detected
def bit_value_agreement(circ, result):
= result.get_memory(circ)
memory return alice_bit_value(circ) == int(
bob_bit_value(circ, memory))
def alice_bit_value(circ):
return 1 if 'x' in circ.count_ops() else 0
def print_key(circuits, number_of_circuits, how_many_for_testing):
print('key :', end='')
for i in range(how_many_for_testing + 1):
print(' ', end='')
for i in range(i, NUMBER_OF_CIRCUITS):
if had_agreement(circuits[i]):
print(alice_bit_value(circuits[i]), end='')
else:
print(' ', end='')
= create_circuits(NUMBER_OF_CIRCUITS, DOES_EVE_EXIST) # 1
circuits = run_the_job(circuits) # 2
result # 3
print_alice_bits(circuits) # 4
print_bob_bits(circuits, result) = print_had_agreements(circuits) # 5
number_of_agreements = print_bit_agreements(circuits, result,number_of_agreements) # 6
how_many_for_testing, is_eve_detected if is_eve_detected: # 7
print('INTRUDER ALERT!')
else:
print_key (circuits, NUMBER_OF_CIRCUITS, how_many_for_testing)
alice bits: 0101111111011100100000010011000110111101101111000111010101011011110010001001011100010001011010110000
bob bits : 0100110000110001000000010010001010000101111111001111110111001011101011001001011001010100001000100010
hads agree? ✓✓✓✓ ✓✓ ✓✓ ✓ ✓✓✓✓✓ ✓✓ ✓ ✓✓ ✓✓ ✓ ✓✓✓✓ ✓ ✓✓ ✓ ✓ ✓✓ ✓✓ ✓✓✓ ✓ ✓ ✓✓ ✓✓ ✓✓✓✓ ✓ ✓✓
bits agree? ✓✓✓X
INTRUDER ALERT!
Kwantowa teleportacja
Quantum teleportation czyli kwantowa teleportacja to technika przekazu informacji (kwantowej) między wysyłającym i odbierającym. Jak poprzednio mozemy przyjąć, ze Alicja wysyła wiadomość do Boba. Informacja, którą chce przesłać Alicja to stan \(|\psi\rangle=\alpha |0\rangle +\beta |1\rangle\).
Zgodnie z twierdzeniem o zakazie klonowania, nie mozna wykonac dokładnej kopii dowolnego stanu kwantowego. Oznacza to, ze Alicja nie moze przygotować swojego stanu i go sklonować w celu wysłania go do Boba.
Uzyjemy dwóch klasycznych bitów informacji oraz splątanej pary kubitów. Ponadto trzeba pamiętać, ze w chwili kiedy Alicja wysyła swój Kubit niszczy jego stan u siebie.
The Quantum Teleportation Protocol
import numpy as np
import random
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute, Aer
from qiskit.result import marginal_counts
Potrzebujemy obwodu składającego się z 3 kubitów: 1. qubit Alicji 2. qubit splatany 1 (Eva-Alicja) 3. qubit splatany 2 (Eva-Bob)
i 3 klasycznych rejestrów.
def create_registers():
= QuantumRegister(1, 'alice (q)')
alice_q = QuantumRegister(1, 'eva/alice (q)')
eva_alice_q = QuantumRegister(1, 'eva/bob (q)')
eva_bob_q = ClassicalRegister(3, 'bob (c)')
bob_c return QuantumCircuit(alice_q, eva_alice_q, eva_bob_q, bob_c)
= create_registers()
qc 'mpl') qc.draw(
Przygotujmy losowy stan Alicji, który prześlemy
def generate_amplitudes():
= np.sqrt(random.uniform(0,1))
alpha = np.sqrt(1 - alpha**2)
beta return alpha, beta
generate_amplitudes()
(0.5455988535218319, 0.8380464730763221)
Dodajemy bramki
def add_gates(circ, alpha, beta):
# stan alicji q0
0)
circ.initialize([alpha, beta],
circ.barrier()# stan splątany q1 i q2
1)
circ.h(1,2)
circ.cnot(
circ.barrier()# alicja stosuje bramki łacząc swoj kubit i otrzymany kubit od evy
0,1)
circ.cnot(0)
circ.h(
circ.barrier()# alicja mierzy kubity i przesyla info do Boba
0,0)
circ.measure(1,1)
circ.measure(# realizacja boba
with circ.if_test((1, 1)):
2)
circ.x(with circ.if_test((0, 1)):
2)
circ.z(2, 2)
circ.measure(return circ
= generate_amplitudes()
alpha, beta print(f'alpha = {alpha:.4f}, beta = {beta:.4f}')
= create_registers()
circ = add_gates(circ, alpha, beta)
circ 'mpl', cregbundle=False)) display(circ.draw(
alpha = 0.7765, beta = 0.6301
= Aer.get_backend("qasm_simulator")
device = 1000
shots = device.run(circ, shots=shots)
job = job.result()
result = result.get_counts(circ)
counts = marginal_counts(counts, [2])
counts_m = counts_m.get('0')
number_of_0s = counts_m.get('1')
number_of_1s = np.sqrt(number_of_0s / shots)
alpha = np.sqrt(number_of_1s / shots)
beta print("stan = ({:.4f}, {:.4f})".format(alpha, beta))
stan = (0.7880, 0.6156)
Step 4: Bob dekoder
Bob, który posiada kubit 3 q2 (wcześniej splątany) stosuje następujące bramki w zaleznosci od klasycznej informacji, którą orzymał:
\[00 \to Identity \]
\[01 \to Apply 𝑋 gate \]
\[ 10 \to Apply Z gate \]
\[ 11 \to Apply 𝑍𝑋 gate \]
Shor
def initialize_qubits(given_circuit, n, m):
range(n))
given_circuit.h(+m-1)
given_circuit.x(n
from qiskit import QuantumCircuit
def c_amod15(a, x):
if a not in [2,7,8,11,13]:
raise ValueError("'a' must be 2,7,8,11,13")
= QuantumCircuit(4)
U for iteration in range(x):
if a in [2,13]:
0,1)
U.swap(1,2)
U.swap(2,3)
U.swap(if a in [7,8]:
2,3)
U.swap(1,2)
U.swap(0,1)
U.swap(if a == 11:
1,3)
U.swap(0,2)
U.swap(if a in [7,11,13]:
for q in range(4):
U.x(q)= U.to_gate()
U = "%i^%i mod 15" % (a, x)
U.name = U.control()
c_U return c_U
def modular_exponentiation(circuit, n, m, a):
for x in range(n):
= 2**x
exponent + list(range(n, n+m)))
circuit.append(c_amod15(a, exponent), [x]
from qiskit.circuit.library import QFT
def inverse_qft(circuit, measurement_qubits):
len(measurement_qubits), do_swaps=False).inverse(), measurement_qubits)
circuit.append(QFT(
def shors_algorithm(n, m, a):
= QuantumCircuit(n+m, n)
qc
initialize_qubits(qc, n, m)
qc.barrier()
modular_exponentiation(qc, n, m, a)
qc.barrier()range(n))
inverse_qft(qc, range(n), range(n))
qc.measure(return qc
= 4; m = 4; a = 7
n = shors_algorithm(n, m, a)
final_circuit 'mpl') final_circuit.draw(
= Aer.get_backend('qasm_simulator')
simulator = execute(final_circuit, backend=simulator, shots=1000).result().get_counts(final_circuit) counts
for measured_value in counts:
print(f"{int(measured_value[::-1], 2)}")
9
14
10
15
11
13
6
12
8
7
0
4
5
from math import gcd
for i in counts:
= int(i[::-1], 2)
measured_value if measured_value % 2 != 0:
print("Measured value not even")
continue #measured value should be even as we are doing a^(r/2) mod N and r/2 should be int
= int((a ** (measured_value/2)) % 15)
x if (x + 1) % 15 == 0:
continue
= gcd(x + 1, 15), gcd(x - 1, 15) #we saw earlier that a^(r/2)+1 or a^(r/2)-1 should be a factor of 15
factors print(factors)
Measured value not even
(1, 3)
(1, 3)
Measured value not even
Measured value not even
Measured value not even
(1, 3)
(5, 3)
(1, 15)
Measured value not even
(1, 15)
(5, 3)
Measured value not even