Algorytm Shora
Na potrzeby tego modułu Qiskit in Classrooms uczniowie muszą mieć działające środowisko Python z zainstalowanymi następującymi pakietami:
qiskitw wersji 2.1.0 lub nowszejqiskit-ibm-runtimew wersji 0.40.1 lub nowszejqiskit-aerw wersji 0.17.0 lub nowszejqiskit.visualizationnumpypylatexenc
Aby skonfigurować i zainstalować powyższe pakiety, zapoznaj się z przewodnikiem Instalacja Qiskit. Aby uruchamiać zadania na prawdziwych komputerach kwantowych, uczniowie muszą założyć konto w IBM Quantum®, postępując zgodnie z instrukcjami w przewodniku Konfiguracja konta IBM Cloud.
Ten moduł był testowany i wykorzystał trzy sekundy czasu QPU. To jedynie szacunek. Twoje rzeczywiste zużycie może być inne.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'
Wprowadzenie
Na początku lat 90. XX wieku narastał entuzjazm związany z potencjałem komputerów kwantowych do rozwiązywania problemów trudnych dla klasycznych komputerów. Kilku utalentowanych informatyków opracowało algorytmy demonstrujące moc obliczeń kwantowych dla wybranych, niszowych problemów, lecz nikomu nie udało się znaleźć jednej, przełomowej „zabójczej aplikacji" dla obliczeń kwantowych, która zrewolucjonizowałaby tę dziedzinę. Tak było aż do 1994 roku, kiedy Peter Shor wymyślił to, co dziś nazywamy algorytmem Shora – służący do faktoryzacji dużych liczb.
W tamtych czasach powszechnie wiadomo było, że znalezienie czynników pierwszych dużej liczby jest niezwykle trudne dla klasycznego komputera. Właśnie na tej trudności opierały się protokoły bezpieczeństwa w internecie. Shor odkrył sposób na wykładniczo bardziej efektywne znajdowanie tych czynników, przenosząc najtrudniejsze etapy obliczeń na teoretyczny, przyszły komputer kwantowy.
W tym module przyjrzymy się algorytmowi Shora. Najpierw zarysujemy szerszy kontekst, formalizując problem, który rozwiązuje, i wyjaśniając jego znaczenie dla cyberbezpieczeństwa. Następnie przedstawimy wprowadzenie do matematyki modularnej i pokażemy, jak zastosować ją do problemu faktoryzacji — wyjaśniając, jak faktoryzacja sprowadza się do innego zagadnienia, zwanego „znajdowaniem rzędu". Zobaczymy, jak Kwantowa Transformata Fouriera i Kwantowe Szacowanie Fazy, poznane w poprzednim module, wchodzą do gry, oraz jak ich użyć do rozwiązania problemu znajdowania rzędu.
Na koniec uruchomimy algorytm Shora na prawdziwym komputerze kwantowym! Pamiętaj jednak, że algorytm ten będzie naprawdę użyteczny dopiero wtedy, gdy będziemy dysponować dużym, odpornym na błędy komputerem kwantowym — co wciąż jest kwestią kilku najbliższych lat. Dlatego po prostu rozłożymy małą liczbę na czynniki, żeby pokazać, jak algorytm działa.
Problem faktoryzacji
Celem problemu faktoryzacji jest znalezienie czynników pierwszych liczby . Dla niektórych wartości jest to dość proste. Na przykład, jeśli jest parzyste, jednym z jego czynników pierwszych będzie 2. Jeśli jest potęgą liczby pierwszej, tzn. dla pewnej liczby pierwszej , znalezienie jest również stosunkowo łatwe: wystarczy przybliżyć -ty pierwiastek z i poszukać pobliskich liczb pierwszych, które mogłyby być wartością .
Klasyczne komputery mają jednak problemy w sytuacji, gdy jest nieparzyste i nie jest potęgą liczby pierwszej. To właśnie ten przypadek rozwiązuje algorytm Shora. Algorytm znajduje dwa czynniki i takie, że . Można go stosować rekurencyjnie, aż wszystkie czynniki będą pierwsze. W kolejnych sekcjach zobaczymy, jak podchodzi się do tego problemu.
Znaczenie dla cyberbezpieczeństwa
Wiele schematów kryptograficznych zostało zbudowanych w oparciu o fakt, że faktoryzacja dużych liczb jest trudna — w tym jeden powszechnie stosowany dziś schemat o nazwie RSA. W RSA klucz publiczny powstaje przez pomnożenie dwóch dużych liczb pierwszych, dając . Każdy może użyć tego klucza publicznego do szyfrowania danych. Jednak tylko ktoś posiadający klucz prywatny, i , może je odszyfrować.
Gdyby dało się łatwo rozłożyć na czynniki, każdy mógłby ustalić wartości i i złamać szyfrowanie. Ale tak nie jest. To słynnie trudny problem. W rzeczywistości czynniki pierwsze liczby RSA1024 — mającej 1024 cyfry w zapisie binarnym i 309 cyfr w zapisie dziesiętnym — nie zostały znalezione do dziś, mimo że za jej faktoryzację jeszcze w 1991 roku zaoferowano nagrodę w wysokości $100 000.
Rozwiązanie Shora
W 1994 roku Peter Shor zdał sobie sprawę, że komputer kwantowy mógłby rozkładać dużą liczbę na czynniki wykładniczo efektywniej niż klasyczny komputer. Jego spostrzeżenie opierało się na związku między problemem faktoryzacji a arytmetyką modularną. Przedstawimy krótkie wprowadzenie do arytmetyki modularnej, a następnie zobaczymy, jak można jej użyć do faktoryzacji .
Arytmetyka modularna
Arytmetyka modularna to cykliczny system liczenia, w którym liczenie zaczyna się w zwykły sposób — od liczb całkowitych 0, 1, 2 itd. — lecz po pewnym czasie, po upłynięciu okresu , zaczyna się od nowa. Zobaczmy, jak to działa na przykładzie. Niech nasz okres wynosi 5. Wtedy podczas liczenia, w miejscu, gdzie normalnie pojawiłoby się 5, zaczynamy od nowa od 0:
Wynika to stąd, że w „świecie modulo 5" liczba 5 jest równoważna 0. Mówimy, że . W istocie wszystkie wielokrotności 5 będą równoważne .
Sprawdź swoją wiedzę
Przeczytaj poniższe pytania, zastanów się nad odpowiedzią, a następnie kliknij trójkąt, aby odkryć rozwiązanie.
Użyj arytmetyki modularnej, aby rozwiązać następujący problem:
Wyruszasz w długą, transkontynentalną podróż pociągiem o godzinie 8:00. Podróż trwa 60 godzin. O której godzinie przyjeżdżasz?
Odpowiedź:
Okres wynosi 24, ponieważ doba ma 24 godziny. Zadanie można więc zapisać w arytmetyce modularnej jako:
Dotrzesz więc do celu o godzinie 20:00, czyli o 20:00 wieczorem.
i
Przydatne jest wprowadzenie dwóch zbiorów: i . to po prostu zbiór liczb istniejących w „świecie modulo ". Na przykład, gdy liczyliśmy modulo 5, zbiór ten to . Inny przykład: . Na elementach można wykonywać dodawanie i mnożenie (modulo ), a wynik każdej z tych operacji również należy do , co czyni obiektem matematycznym zwanym pierścieniem.
Dla algorytmu Shora szczególnie interesujący jest pewien podzbiór . Jest to podzbiór liczb z , dla których największy wspólny dzielnik każdego elementu i wynosi 1, tzn. każdy element jest „wzajemnie pierwszym" z . Jeśli weźmiemy zbiór tych liczb wraz z operacją mnożenia modularnego, otrzymamy inny obiekt matematyczny zwany grupą. Grupę tę oznaczamy . Okazuje się, że w (i skończonych grupach w ogólności), jeśli wybierzemy dowolny element i będziemy wielokrotnie mnożyć przez siebie, zawsze w końcu otrzymamy liczbę . Minimalna liczba mnożeń przez siebie potrzebna do uzyskania nosi nazwę rzędu elementu . Fakt ten będzie bardzo ważny w naszej dyskusji o tym, jak faktoryzować liczby.
Sprawdź swoją wiedzę
Przeczytaj poniższe pytania, zastanów się nad odpowiedzią, a następnie kliknij trójkąt, aby odkryć rozwiązanie.
Czym jest ?
Odpowiedź:
Wykluczyliśmy następujące liczby:
Jaki jest rząd każdego z elementów ?
Odpowiedź:
Rząd to najmniejsza liczba taka, że dla każdego elementu .
Zauważ, że choć byliśmy w stanie wyznaczyć rząd liczb w , w ogólności — dla większych — NIE jest to łatwe zadanie. To sedno problemu faktoryzacji i powód, dla którego potrzebujemy komputera kwantowego. Zobaczymy dlaczego, gdy będziemy przechodzić przez dalszą część tego notesu.
Zastosowanie arytmetyki modularnej do problemu faktoryzacji
Kluczem do znalezienia czynników i takich, że , jest znalezienie pewnej innej liczby całkowitej spełniającej
i
Jak znalezienie pomaga nam wyznaczyć czynniki i ? Przeprowadźmy teraz to rozumowanie. Ponieważ , oznacza to, że . Innymi słowy, jest wielokrotnością . A zatem dla pewnej liczby całkowitej :
Możemy rozłożyć na czynniki:
Z naszych początkowych założeń wiemy, że , więc nie dzieli równo ani , ani . Zatem dwa czynniki , czyli i , muszą każdy dzielić odpowiednio i . Albo jest czynnikiem , a czynnikiem , albo odwrotnie. Dlatego obliczając największe wspólne dzielniki (NWD) między a i , otrzymamy czynniki i . Wyznaczenie NWD dwóch liczb jest klasycznie łatwym zadaniem, które można wykonać np. za pomocą algorytmu Euklidesa.
Sprawdź swoją wiedzę
Przeczytaj poniższe pytania, zastanów się nad odpowiedzią, a następnie kliknij trójkąt, aby odkryć rozwiązanie.
Zrozumienie każdego kroku powyższego rozumowania może być niełatwe, dlatego spróbuj prześledzić je na przykładzie. Użyj i . Najpierw zweryfikuj, że i . Następnie sprawdź każdy kolejny krok. Na koniec oblicz i zweryfikuj, że są to czynniki liczby .
Odpowiedź:
, co wynosi , zatem .
, co nie jest równoważne .
, co nie jest równoważne .
Wiemy teraz, że dla pewnej liczby całkowitej . Weryfikujemy to, podstawiając i : dla .
Teraz musimy obliczyć i .
Znaleźliśmy czynniki liczby !
The algorithm
Teraz, gdy wiemy już, jak znalezienie pewnej liczby całkowitej spełniającej warunek pomaga nam rozłożyć na czynniki, możemy przejść przez algorytm Shora. Sprowadza się on w zasadzie do znalezienia :
- Wybierz losową liczbę całkowitą Wybierz losową liczbę całkowitą taką, że .
- Oblicz klasycznie.
- Jeśli , znalazłeś już czynnik. Zakończ.
- W przeciwnym razie kontynuuj.
-
Znajdź rząd liczby modulo Znajdź najmniejszą dodatnią liczbę całkowitą spełniającą .
-
Sprawdź, czy rząd jest parzysty
- Jeśli jest nieparzyste, wróć do kroku 1 i wybierz nowe .
- Jeśli jest parzyste, przejdź do kroku 4.
- Oblicz
- Sprawdź, czy oraz .
- Jeśli , wróć do kroku 1 i wybierz nowe .
- W przeciwnym razie oblicz NWD, aby wyłuskać czynniki:
Będą to nietrywialnie czynniki .
- W razie potrzeby rozłóż rekurencyjnie
- Jeśli i/lub nie są liczbami pierwszymi, zastosuj algorytm rekurencyjnie, aby je w pełni rozłożyć.
- Gdy wszystkie czynniki są pierwsze, rozkład jest kompletny.
Na podstawie tej procedury może nie być oczywiste, dlaczego do wykonania tego zadania potrzebny jest komputer kwantowy. Jest to konieczne, ponieważ krok 2 — znalezienie rzędu modulo — jest klasycznie bardzo trudnym problemem. Złożoność rośnie wykładniczo względem . Jednak przy użyciu komputera kwantowego wystarczy skorzystać z Kwantowej Estymacji Fazy (Quantum Phase Estimation), aby go rozwiązać. Krok 4, czyli znalezienie NWD dwóch liczb całkowitych, jest natomiast klasycznie dość prostym zadaniem. Mówimy zatem, że problem rozkładu na czynniki „redukuje się" do problemu wyznaczania rzędu.
The hard part: order finding
Teraz omówimy, jak można użyć komputera kwantowego do wyznaczania rzędu. Najpierw wyjaśnijmy dokładniej, co rozumiemy przez „rząd". Oczywiście podałem już jego matematyczne znaczenie: to pierwsza niezerowa liczba całkowita taka, że . Spróbujmy jednak nieco lepiej wyczuć intuicję kryjącą się za tym pojęciem.
Dla wystarczająco małego możemy po prostu wyznaczyć rząd, obliczając kolejne potęgi , biorąc modulus każdego wyniku, a następnie zatrzymując się, gdy znajdziemy potęgę spełniającą . Tak właśnie zrobiliśmy w powyższym przykładzie z . Przyjrzyjmy się wykresom tych potęg modularnych dla kilku przykładowych wartości i :
Zauważasz coś? To są funkcje periodyczne! A rząd jest równy okresowi! Wyznaczanie rzędu jest zatem równoważne z wyznaczaniem okresu.
Komputery kwantowe doskonale nadają się do znajdowania okresu funkcji. W tym celu możemy skorzystać z podprocedury algorytmicznej zwanej Kwantową Estymacją Fazy (Quantum Phase Estimation). Omawialiśmy QPE i jej związek z Kwantową Transformatą Fouriera w poprzednim module. Szczegółowe przypomnienie znajdziesz w module QFT lub w lekcji Johna Watrouca dotyczącej Kwantowej Estymacji Fazy w jego kursie algorytmów kwantowych. Teraz omówimy ogólny zarys procedury:
W Kwantowej Estymacji Fazy (QPE) zaczynamy od unitarnego operatora oraz własnego stanu tego unitarnego . Następnie używamy QPE do przybliżenia odpowiedniej wartości własnej, która — ponieważ operator jest unitarny — ma postać . Znalezienie wartości własnej jest zatem równoważne ze znalezieniem wartości w funkcji periodycznej. Układ (Circuit) wygląda następująco:

gdzie liczba Qubitów sterujących (górne Qubitów na powyższym rysunku) wyznacza dokładność przybliżenia.
W algorytmie Shora stosujemy QPE na unitarnym operatorze :
Tutaj oznacza stan z bazy obliczeniowej wieloQubitowego rejestru, gdzie binarna wartość Qubitów odpowiada liczbie całkowitej . Na przykład, jeśli i , to jest reprezentowane przez czteroQubitowy stan bazowy , ponieważ do zakodowania liczb do 15 potrzebne są cztery Qubity. (Jeśli to pojęcie jest Ci nieznane, zajrzyj do wstępnego modułu Qiskit w Classrooms, gdzie znajdziesz przypomnienie o binarnym kodowaniu stanów kwantowych.)
Musimy teraz znaleźć stan własny tego operatora unitarnego. Jeśli zaczęlibyśmy w stanie , każde kolejne zastosowanie przemnożyłoby stan naszego rejestru przez , a po zastosowaniach wrócimy do stanu . Na przykład dla i :
Zatem superpozycje stanów w tym cyklu () postaci:
są stanami własnymi . (Istnieje więcej stanów własnych niż tylko te. Ale interesują nas wyłącznie stany powyższej postaci.)
Check your understanding
Przeczytaj poniższe pytanie(-a), zastanów się nad odpowiedzią, a następnie kliknij trójkąt, aby ujawnić rozwiązanie.
Znajdź stan własny operatora unitarnego odpowiadającego i .
Odpowiedź:
Zatem rząd . Interesujące nas stany własne będą równą superpozycją wszystkich stanów, przez które przechodził powyższy cykl, z różnymi fazami:
Powiedzmy, że bylibyśmy w stanie zainicjować stan naszego Qubita w jednym z tych stanów własnych (uwaga — nie jesteśmy. Przynajmniej nie w prosty sposób. Za chwilę wyjaśnimy, dlaczego i co można zamiast tego zrobić). Wtedy moglibyśmy użyć QPE do oszacowania odpowiedniej wartości własnej, , gdzie . Następnie będziemy mogli wyznaczyć rząd z prostego równania:
Pamiętaj jednak, że QPE jedynie przybliża — nie podaje dokładnej wartości. Potrzebujemy, aby przybliżenie było wystarczająco dobre, by odróżnić od . Im więcej Qubitów sterujących , tym lepsze będzie przybliżenie. W zadaniach na końcu lekcji zostaniesz poproszony(-a) o wyznaczenie minimalnej wartości potrzebnej do rozłożenia liczby na czynniki.
Musimy teraz rozwiązać pewien problem. Całe powyższe wyjaśnienie, jak znaleźć , zaczyna się od przygotowania stanu własnego . Nie wiemy jednak, jak to zrobić bez uprzedniej znajomości . Rozumowanie jest kołowe. Potrzebujemy sposobu na oszacowanie wartości własnej bez inicjowania stanu własnego.
Zamiast zaczynać od stanu własnego , możemy przygotować stan początkowy jako -Qubitowy stan odpowiadający w zapisie binarnym (tzn. ). Choć ten stan sam w sobie oczywiście nie jest stanem własnym , jest superpozycją wszystkich stanów własnych :
Check your understanding
Przeczytaj poniższe pytanie(-a), zastanów się nad odpowiedzią, a następnie kliknij trójkąt, aby ujawnić rozwiązanie.
Sprawdź, że jest równoważne superpozycji stanów własnych, które znalazłeś(-aś) dla i w poprzednim pytaniu sprawdzającym.
Odpowiedź:
Cztery stany własne to:
Zatem:
Jak to pozwala nam znaleźć rząd ? Ponieważ stan początkowy jest superpozycją wszystkich stanów własnych powyższej postaci, algorytm QPE jednocześnie szacuje każde z odpowiadających tym stanom własnym. Pomiar Qubitów sterujących na końcu da zatem przybliżenie wartości , gdzie jest losowo wybraną wartością własną. Jeśli kilkukrotnie powtórzymy ten obwód i uzyskamy kilka próbek z różnymi wartościami , szybko będziemy mogli wyznaczyć .
Implementacja w Qiskit
Jak wspomnieliśmy wcześniej, nasze urządzenia sprzętowe nie są jeszcze w stanie faktoryzować ogromnych liczb, takich jak RSA1024. Sfaktoryzujemy więc małą liczbę, aby zademonstrować, jak działa algorytm. W tym demo posłużymy się uproszczoną wersją kodu przedstawionego w samouczku dotyczącym algorytmu Shora. Jeśli chcesz poznać więcej szczegółów, odwiedź tamten samouczek.
Uruchomimy algorytm przy użyciu naszego standardowego frameworku do rozwiązywania problemów kwantowych, zwanego frameworkiem wzorców Qiskit (ang. Qiskit patterns framework). Składa się on z czterech kroków:
- Odwzorowanie problemu na obwód kwantowy
- Optymalizacja Circuit do uruchomienia na sprzęcie kwantowym
- Wykonanie Circuit na komputerze kwantowym
- Przetwarzanie końcowe pomiarów
1. Odwzorowanie
Sfaktoryzujemy , wybierając jako naszą liczbę pierwszą względem .
Najpierw musimy zbudować Circuit implementujący unitarny operator mnożenia modularnego . To faktycznie najtrudniejsza część całej implementacji i może być bardzo kosztowna obliczeniowo, w zależności od sposobu realizacji. W tym celu trochę „oszukamy": wiemy, że startujemy ze stanu , a z wcześniejszego pytania sprawdzającego wiemy, że:
Zbudujemy więc operator unitarny, który wykonuje prawidłowe operacje na tych czterech stanach, a wszystkie pozostałe stany pozostawia bez zmian. To jest właśnie „oszustwo", bo korzystamy z naszej wiedzy o rzędzie , aby uprościć operator unitarny. Gdybyśmy faktycznie próbowali faktoryzować liczbę, której czynniki są nam nieznane, nie moglibyśmy tego zrobić.
Sprawdź swoje rozumienie
Przeczytaj poniższe pytanie/pytania, zastanów się nad odpowiedzią, a następnie kliknij trójkąt, aby ujawnić rozwiązanie.
Znając działanie operatora na powyższe stany, skonstruuj ten operator z szeregu bramek SWAP, które zamieniają stany dwóch Qubitów. (Wskazówka: pomaga zapisanie każdego stanu w reprezentacji binarnej.)
Odpowiedź:
Przepiszmy działanie na stanach w reprezentacji binarnej:
Każde z tych działań można zrealizować za pomocą prostej bramki SWAP. uzyskujemy, zamieniając stany Qubitu i . uzyskujemy, zamieniając stany Qubitu i . I tak dalej. Możemy więc rozłożyć macierz na następującą serię bramek SWAP:
Pamiętając, że operatory działają od prawej do lewej, sprawdźmy, że daje to oczekiwany efekt na każdym ze stanów:
Możemy teraz zakodować Circuit równoważny temu operatorowi w Qiskit.
Najpierw importujemy niezbędne pakiety:
# Import necessary packages
import numpy as np
from fractions import Fraction
from math import floor, gcd, log
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.library import QFTGate
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import plot_histogram
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
Następnie tworzymy operator :
def M2mod15():
"""
M2 (mod 15)
"""
b = 2
U = QuantumCircuit(4)
U.swap(2, 3)
U.swap(1, 2)
U.swap(0, 1)
U = U.to_gate()
U.name = f"M_{b}"
return U
# Get the M2 operator
M2 = M2mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(4)
circ.compose(M2, inplace=True)
circ.decompose(reps=2).draw(output="mpl", fold=-1)
Algorytm QPE używa bramki kontrolowanej-. Skoro mamy już Circuit z , musimy zbudować Circuit kontrolowany-:
def controlled_M2mod15():
"""
Controlled M2 (mod 15)
"""
b = 2
U = QuantumCircuit(4)
U.swap(2, 3)
U.swap(1, 2)
U.swap(0, 1)
U = U.to_gate()
U.name = f"M_{b}"
c_U = U.control()
return c_U
# Get the controlled-M2 operator
controlled_M2 = controlled_M2mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(5)
circ.compose(controlled_M2, inplace=True)
circ.decompose(reps=1).draw(output="mpl", fold=-1)
Mamy teraz naszą bramkę kontrolowaną-. Aby jednak uruchomić algorytm Quantum Phase Estimation, będziemy potrzebować kontrolowanego-, kontrolowanego-, aż do kontrolowanego-, gdzie to liczba Qubitów użytych do estymacji fazy. Im więcej Qubitów, tym dokładniejsza będzie estymacja fazy. Do naszej procedury estymacji fazy użyjemy Qubitów kontrolnych. Potrzebujemy więc:
gdzie indeks , dla , odpowiada Qubitowi kontrolnemu. Obliczmy teraz dla każdej wartości :
def a2kmodN(a, k, N):
"""Compute a^{2^k} (mod N) by repeated squaring"""
for _ in range(k):
a = int(np.mod(a**2, N))
return a
k_list = range(8)
b_list = [a2kmodN(2, k, 15) for k in k_list]
print(b_list)
[2, 4, 1, 1, 1, 1, 1, 1]
Ponieważ dla , wszystkie odpowiadające im operatory ( i wyższe) są równoważne operatorowi tożsamości. Musimy więc skonstruować tylko jedną dodatkową macierz,
Uwaga: To uproszczenie działa tutaj tylko dlatego, że rząd wynosi . Gdy (czyli ), każda kolejna potęga operatora jest operatorem tożsamości. Ogólnie rzecz biorąc, dla większych lub innych wyborów nie można pominąć konstruowania wyższych potęg. To właśnie jeden z powodów, dla których jest to uważane za przykład zabawkowy: małe liczby pozwalają na skróty, które nie działałyby w przypadkach o większej skali.
def M4mod15():
"""
M4 (mod 15)
"""
b = 4
U = QuantumCircuit(4)
U.swap(1, 3)
U.swap(0, 2)
U = U.to_gate()
U.name = f"M_{b}"
return U
# Get the M4 operator
M4 = M4mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(4)
circ.compose(M4, inplace=True)
circ.decompose(reps=2).draw(output="mpl", fold=-1)
I podobnie jak poprzednio, tworzymy z niego operator kontrolowany-:
def controlled_M4mod15():
"""
Controlled M4 (mod 15)
"""
b = 4
U = QuantumCircuit(4)
U.swap(1, 3)
U.swap(0, 2)
U = U.to_gate()
U.name = f"M_{b}"
c_U = U.control()
return c_U
# Get the controlled-M4 operator
controlled_M4 = controlled_M4mod15()
# Add it to a circuit and plot
circ = QuantumCircuit(5)
circ.compose(controlled_M4, inplace=True)
circ.decompose(reps=1).draw(output="mpl", fold=-1)
Teraz możemy złożyć to wszystko razem, aby znaleźć rząd za pomocą Circuit kwantowego, korzystając z estymacji fazy:
# Order finding problem for N = 15 with a = 2
N = 15
a = 2
# Number of qubits
num_target = floor(log(N - 1, 2)) + 1 # for modular exponentiation operators
num_control = 2 * num_target # for enough precision of estimation
# List of M_b operators in order
k_list = range(num_control)
b_list = [a2kmodN(2, k, 15) for k in k_list]
# Initialize the circuit
control = QuantumRegister(num_control, name="C")
target = QuantumRegister(num_target, name="T")
output = ClassicalRegister(num_control, name="out")
circuit = QuantumCircuit(control, target, output)
# Initialize the target register to the state |1>
circuit.x(num_control)
# Add the Hadamard gates and controlled versions of the
# multiplication gates
for k, qubit in enumerate(control):
circuit.h(k)
b = b_list[k]
if b == 2:
circuit.compose(
M2mod15().control(), qubits=[qubit] + list(target), inplace=True
)
elif b == 4:
circuit.compose(
M4mod15().control(), qubits=[qubit] + list(target), inplace=True
)
else:
continue # M1 is the identity operator
# Apply the inverse QFT to the control register
circuit.compose(QFTGate(num_control).inverse(), qubits=control, inplace=True)
# Measure the control register
circuit.measure(control, output)
circuit.draw("mpl", fold=-1)
2. Optymalizacja
Teraz, gdy zmapowaliśmy nasz obwód, kolejnym krokiem jest jego optymalizacja pod kątem uruchomienia na konkretnym komputerze kwantowym. Najpierw musimy wczytać Backend.
service = QiskitRuntimeService()
backend = service.backend("ibm_marrakesh")
Jeśli nie masz dostępnego czasu na swoim koncie lub z jakiegokolwiek powodu chcesz użyć symulatora, możesz uruchomić poniższą komórkę, aby skonfigurować symulator naśladujący wybrane przez nas urządzenie kwantowe:
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
transpiled_circuit = pm.run(circuit)
print(f"2q-depth: {transpiled_circuit.depth(lambda x: x.operation.num_qubits==2)}")
print(f"2q-size: {transpiled_circuit.size(lambda x: x.operation.num_qubits==2)}")
print(f"Operator counts: {transpiled_circuit.count_ops()}")
transpiled_circuit.draw(output="mpl", fold=-1, style="clifford", idle_wires=False)
2q-depth: 188
2q-size: 281
Operator counts: OrderedDict({'sx': 548, 'rz': 380, 'cz': 281, 'measure': 8, 'x': 6})

3. Wykonanie
# Sampler primitive to obtain the probability distribution
sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
# Enable gate twirling
sampler.options.twirling.enable_gates = True
pub = transpiled_circuit
job = sampler.run([pub], shots=1024)
result = job.result()[0]
counts = result.data["out"].get_counts()
plot_histogram(counts, figsize=(35, 5))

Widzimy cztery wyraźne piki przy 00000000, 01000000, 10000000 i 11000000, z pewną liczbą wyników w innych bitstrings spowodowanych szumem komputera kwantowego. Zignorujemy je i zachowamy jedynie dominującą czwórkę, narzucając próg: tylko zliczenia powyżej tego progu są uznawane za rzeczywisty sygnał ponad szumem.
# Dictionary of bitstrings and their counts to keep
counts_keep = {}
# Threshold to filter
threshold = np.max(list(counts.values())) / 2
for key, value in counts.items():
if value > threshold:
counts_keep[key] = value
print(counts_keep)
4. Post-processing
W algorytmie Shora znaczna część algorytmu jest wykonywana klasycznie. Dlatego resztę umieścimy w kroku „post-processingu", po uzyskaniu pomiarów z komputera kwantowego. Każdy z powyższych pomiarów można przekształcić w liczby całkowite, które po podzieleniu przez stanowią nasze przybliżenia , gdzie jest za każdym razem losowe.
a = 2
N = 15
FACTOR_FOUND = False
num_attempt = 0
while not FACTOR_FOUND:
print(f"\nATTEMPT {num_attempt}:")
# Here, we get the bitstring by iterating over outcomes
# of a previous hardware run with multiple shots.
# Instead, we can also perform a single-shot measurement
# here in the loop.
bitstring = list(counts_keep.keys())[num_attempt]
num_attempt += 1
# Find the phase from measurement
decimal = int(bitstring, 2)
phase = decimal / (2**num_control) # phase = k / r
print(f"Phase: theta = {phase}")
# Guess the order from phase
frac = Fraction(phase).limit_denominator(N)
r = frac.denominator # order = r
print(f"Order of {a} modulo {N} estimated as: r = {r}")
if phase != 0:
# Guesses for factors are gcd(a^{r / 2} ± 1, 15)
if r % 2 == 0:
x = pow(a, r // 2, N) - 1
d = gcd(x, N)
if d > 1:
FACTOR_FOUND = True
print(f"*** Non-trivial factor found: {x} ***")
ATTEMPT 0:
Phase: theta = 0.0
Order of 2 modulo 15 estimated as: r = 1
ATTEMPT 1:
Phase: theta = 0.75
Order of 2 modulo 15 estimated as: r = 4
*** Non-trivial factor found: 3 ***
Wnioski
Po przejściu przez ten moduł być może ogarnął cię nowy podziw dla geniuszu Petera Shora, który wpadł na tak sprytny algorytm. Mamy jednak nadzieję, że osiągnąłeś też nowy poziom zrozumienia jego zwodniczej prostoty. Choć algorytm może wydawać się imponująco (lub przytłaczająco) złożony, jeśli rozłożysz go na poszczególne kroki i przejdziesz przez nie powoli, ty również będziesz w stanie uruchomić algorytm Shora.
Choć jesteśmy daleko od wykorzystania tego algorytmu do faktoryzacji liczb takich jak RSA1024, nasze komputery kwantowe poprawiają się z dnia na dzień, a gdy zostanie osiągnięty próg zwany odpornością na błędy, takie algorytmy będą wkrótce gotowe do użycia. To ekscytujący czas, by uczyć się o obliczeniach kwantowych!
Zadania
Kluczowe pojęcia:
- Nowoczesne systemy kryptograficzne opierają się na klasycznej trudności faktoryzacji dużych liczb całkowitych.
- Arytmetyka modularna — w tym struktury i — stanowi matematyczny fundament algorytmu Shora.
- Problem faktoryzacji liczby całkowitej można sprowadzić do problemu znalezienia rzędu liczby modulo .
- Kwantowe wyznaczanie rzędu wykorzystuje techniki kwantowej estymacji fazy do wyznaczenia okresu funkcji .
- Algorytm Shora składa się z klasyczno-kwantowego przepływu pracy, który wybiera podstawę, wykonuje kwantowe wyznaczanie rzędu, a następnie klasycznie oblicza czynniki z uzyskanego wyniku.
Prawda/Fałsz:
- P/F Wydajność algorytmu Shora zagraża bezpieczeństwu szyfrowania RSA.
- P/F Algorytm Shora można efektywnie wykonać na dowolnym współczesnym komputerze kwantowym.
- P/F Algorytm Shora wykorzystuje kwantową estymację fazy (QPE) jako kluczową procedurę pomocniczą.
- P/F Klasyczna część algorytmu Shora polega na obliczeniu największego wspólnego dzielnika (NWD).
- P/F Algorytm Shora działa wyłącznie dla faktoryzacji liczb parzystych.
- P/F Pomyślne uruchomienie algorytmu Shora zawsze gwarantuje znalezienie właściwych czynników.
Krótkie odpowiedzi:
- Dlaczego algorytm Shora jest uważany za potencjalne przyszłe zagrożenie dla szyfrowania RSA?
- Dlaczego wyznaczenie okresu, czyli rzędu, modularnej funkcji wykładniczej pomaga w faktoryzacji liczby w algorytmie Shora?
Zadania dla zaawansowanych:
-
Ile kubitów sterujących potrzebujemy dla danej liczby , którą chcemy faktoryzować, aby uzyskać w QPE precyzję wystarczającą do znalezienia właściwej wartości rzędu ?
-
Stosując opisaną tutaj procedurę faktoryzacji liczby 15, spróbuj teraz sfaktoryzować liczbę 21.