Shor-Algorithmus
Für dieses Qiskit-in-Classrooms-Modul benötigen Studierende eine funktionierende Python-Umgebung mit den folgenden installierten Paketen:
qiskitv2.1.0 oder neuerqiskit-ibm-runtimev0.40.1 oder neuerqiskit-aerv0.17.0 oder neuerqiskit.visualizationnumpypylatexenc
Zum Einrichten und Installieren der oben genannten Pakete siehe die Anleitung Qiskit installieren. Um Jobs auf echten Quantencomputern auszuführen, müssen Studierende ein Konto bei IBM Quantum® einrichten, indem sie den Schritten in der Anleitung IBM Cloud-Konto einrichten folgen.
Dieses Modul wurde getestet und verbrauchte drei Sekunden QPU-Zeit. Dies ist nur eine Schätzung. Dein tatsächlicher Verbrauch kann abweichen.
# 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'
Einführung
In den frühen 1990er Jahren wuchs die Begeisterung über das Potenzial von Quantencomputern, Probleme zu lösen, die für klassische Computer schwierig waren. Einige talentierte Informatiker hatten Algorithmen entwickelt, die die Leistungsfähigkeit des Quantencomputings für einige Nischen- und konstruierte Probleme demonstrierten, aber niemand hatte eine einzige „Killeranwendung" des Quantencomputings gefunden, die das Feld sicher revolutionieren würde. Das änderte sich 1994, als Peter Shor das entwickelte, was heute als Shor-Algorithmus zur Faktorisierung großer Zahlen bekannt ist.
Es war damals allgemein bekannt, dass das Finden der Primfaktoren einer großen Zahl für einen klassischen Computer extrem schwierig war. Tatsächlich beruhten die Internet-Sicherheitsprotokolle auf dieser Schwierigkeit. Shor fand einen Weg, diese Faktoren exponentiell effizienter zu finden, indem er einige der schwierigeren Schritte auf einen theoretischen, zukünftigen Quantencomputer auslagerte.
In diesem Modul werden wir den Shor-Algorithmus erkunden. Zunächst geben wir etwas mehr Kontext zum Algorithmus, formalisieren das Problem, das er löst, und erklären die Relevanz für die Cybersicherheit. Dann geben wir eine Einführung in die modulare Mathematik und wie man sie auf das Faktorisierungsproblem anwendet, wobei wir zeigen, wie sich die Faktorisierung auf ein anderes Problem namens „Ordnungsfindung" reduziert. Wir zeigen, wie die Quanten-Fourier-Transformation und die Quanten-Phasenschätzung, die wir in einem früheren Modul kennengelernt haben, ins Spiel kommen und wie man sie zur Lösung des Ordnungsfindungsproblems verwendet.
Schließlich werden wir den Shor-Algorithmus auf einem echten Quantencomputer ausführen! Bedenke jedoch, dass dieser Algorithmus wirklich erst nützlich sein wird, wenn wir einen großen, fehlertoleranten Quantencomputer haben, was noch einige Jahre entfernt ist. Daher werden wir nur eine kleine Zahl faktorisieren, um die Funktionsweise des Algorithmus zu demonstrieren.
Das Faktorisierungsproblem
Das Ziel des Faktorisierungsproblems ist es, die Primfaktoren einer Zahl zu finden. Für einige Zahlen ist das ziemlich einfach. Wenn zum Beispiel gerade ist, wird einer seiner Primfaktoren 2 sein. Wenn eine Primzahlpotenz ist, also für eine Primzahl , ist es auch recht einfach, zu finden: Wir approximieren einfach die -Wurzel von und suchen nach nahegelegenen Primzahlen, die sein könnten.
Wo klassische Computer jedoch Schwierigkeiten haben, ist wenn ungerade und keine Primzahlpotenz ist. Dies ist der Fall, den der Shor-Algorithmus behandelt. Der Algorithmus findet zwei Faktoren und , sodass . Er kann rekursiv angewendet werden, bis alle Faktoren Primzahlen sind. In den nächsten Abschnitten werden wir sehen, wie dieses Problem angegangen wird.
Relevanz für die Cybersicherheit
Viele kryptografische Verfahren wurden auf der Grundlage der Tatsache entwickelt, dass die Faktorisierung großer Zahlen schwierig ist, darunter eines, das heute häufig verwendet wird, namens RSA. Bei RSA wird ein öffentlicher Schlüssel erstellt, indem zwei große Primzahlen miteinander multipliziert werden, um zu erhalten. Dann kann jeder diesen öffentlichen Schlüssel verwenden, um Daten zu verschlüsseln. Aber nur jemand mit dem privaten Schlüssel, und , kann diese Daten entschlüsseln.
Wenn leicht zu faktorisieren wäre, könnte jeder bestimmen, was und sind, und die Verschlüsselung brechen. Aber das ist es nicht. Dies ist ein bekanntermaßen schwieriges Problem. Tatsächlich wurden die Primfaktoren einer Zahl namens RSA1024, die 1024 Binärstellen und 309 Dezimalstellen lang ist, noch immer nicht gefunden, obwohl bereits 1991 ein Preisgeld von $100.000 für ihre Faktorisierung ausgesetzt wurde.
Shors Lösung
Im Jahr 1994 erkannte Peter Shor, dass ein Quantencomputer eine große Zahl exponentiell effizienter faktorisieren könnte als ein klassischer Computer. Seine Erkenntnis beruhte auf der Beziehung zwischen diesem Faktorisierungsproblem und der modularen Arithmetik. Wir werden eine kurze Einführung in die modulare Arithmetik geben und dann sehen, wie wir sie nutzen können, um zu faktorisieren.
Modulare Arithmetik
Modulare Arithmetik ist ein Zählsystem, das zyklisch ist, was bedeutet, dass das Zählen zwar auf die übliche Weise beginnt, mit den ganzen Zahlen 0, 1, 2, usw., aber irgendwann, nach einer Periode , das Zählen von vorne beginnt. Sehen wir uns an, wie das mit einem Beispiel funktioniert. Unsere Periode sei 5. Dann beginnen wir beim Zählen dort, wo wir normalerweise 5 erreichen würden, wieder bei 0:
Das liegt daran, dass in der „Modulo-5"-Welt 5 äquivalent zu 0 ist. Wir sagen, dass . Tatsächlich sind alle Vielfachen von 5 äquivalent zu .
Überprüfe dein Verständnis
Verwende modulare Arithmetik, um das folgende Problem zu lösen:
Du startest eine lange, transkontinentale Zugfahrt um 8 Uhr morgens. Die Zugfahrt dauert 60 Stunden. Wie spät ist es, wenn du ankommst?
Answer
Die Periode ist 24, da ein Tag 24 Stunden hat. Dieses Problem kann also in modularer Arithmetik geschrieben werden als:
Du würdest also um 20:00 Uhr an deinem Ziel ankommen.
und
Es ist oft nützlich, zwei Mengen einzuführen, und . ist einfach die Menge der Zahlen, die in einer „Modulo-"-Welt existieren. Zum Beispiel, als wir modulo-5 gezählt haben, wäre die Menge . Ein weiteres Beispiel: . Wir können Addition und Multiplikation (modulo ) auf den Elementen in durchführen, und das Ergebnis jeder dieser Operationen ist ebenfalls ein Element in , was zu einem mathematischen Objekt namens Ring macht.
Es gibt eine spezielle Teilmenge von , die für den Shor-Algorithmus von besonderem Interesse ist. Das ist die Teilmenge der Zahlen in , bei denen der größte gemeinsame Teiler zwischen jedem Element und gleich 1 ist, jedes Element also „teilerfremd" zu ist. Wenn wir die Menge dieser Zahlen zusammen mit der modularen Multiplikationsoperation nehmen, bildet dies ein weiteres mathematisches Objekt, eine sogenannte Gruppe. Wir nennen diese Gruppe . Es stellt sich heraus, dass bei (und endlichen Gruppen im Allgemeinen), wenn wir ein beliebiges Element wählen und wiederholt mit sich selbst multiplizieren, wir immer irgendwann die Zahl erhalten. Die minimale Anzahl, wie oft man mit sich selbst multiplizieren muss, um zu erhalten, wird als Ordnung von bezeichnet. Diese Tatsache wird für unsere Diskussion über die Faktorisierung von Zahlen weiter unten sehr wichtig sein.
Überprüfe dein Verständnis
Was ist ?
Answer
Wir haben die folgenden Zahlen ausgeschlossen:
Was ist die Ordnung jedes Elements in ?
Answer
Die Ordnung ist die kleinste Zahl, für die für jedes Element gilt.
Beachte, dass wir zwar die Ordnung der Zahlen in finden konnten, dies aber im Allgemeinen für größere KEINE einfache Aufgabe ist. Dies ist der Kern des Faktorisierungsproblems und der Grund, warum wir einen Quantencomputer brauchen. Wir werden sehen warum, wenn wir den Rest des Notebooks durcharbeiten.
Modulare Arithmetik auf das Faktorisierungsproblem anwenden
Der Schlüssel zum Finden der Faktoren und mit liegt darin, eine andere ganze Zahl zu finden, sodass
und
Wie hilft uns das Finden von , die Faktoren und zu finden? Gehen wir das Argument jetzt durch. Da , bedeutet das, dass . Mit anderen Worten, ist ein Vielfaches von . Also gilt für eine ganze Zahl :
Wir können faktorisieren und erhalten:
Aus unseren anfänglichen Annahmen wissen wir, dass , also teilt weder noch ohne Rest. Die beiden Faktoren von , und , müssen also jeweils und teilen. Entweder ist ein Faktor von und ein Faktor von , oder umgekehrt. Wenn wir also die größten gemeinsamen Teiler (GGT) zwischen und sowohl als auch berechnen, erhalten wir die Faktoren und . Die Berechnung des GGT zweier Zahlen ist eine klassisch einfache Aufgabe, die zum Beispiel mit dem Euklidischen Algorithmus durchgeführt werden kann.
Überprüfe dein Verständnis
Es kann schwierig sein, jeden logischen Schritt oben zu verstehen, also versuche, es mit einem Beispiel durchzuarbeiten. Verwende und . Überprüfe zunächst, dass und . Überprüfe dann weiterhin jeden Schritt. Berechne schließlich und überprüfe, dass es sich um die Faktoren von handelt.
Answer
, was ist, also .
, was nicht äquivalent zu ist.
, was nicht äquivalent zu ist.
Nun wissen wir, dass für eine ganze Zahl . Dies wird bestätigt, wenn wir und einsetzen: mit .
Nun müssen wir und berechnen.
Wir haben also unsere Faktoren von gefunden!
Der Algorithmus
Nachdem wir gesehen haben, wie das Finden einer ganzen Zahl mit uns hilft, zu faktorisieren, können wir den Shor-Algorithmus durchgehen. Er läuft im Wesentlichen darauf hinaus, zu finden:
- Wähle eine zufällige ganze Zahl Wähle eine zufällige ganze Zahl mit .
- Berechne klassisch.
- Falls , hast du bereits einen Faktor gefunden. Stopp.
- Andernfalls fahre fort.
-
Finde die Ordnung von modulo Finde die kleinste positive ganze Zahl , die erfüllt.
-
Überprüfe, ob die Ordnung gerade ist
- Falls ungerade ist, gehe zurück zu Schritt 1 und wähle ein neues .
- Falls gerade ist, fahre mit Schritt 4 fort.
- Berechne
- Überprüfe, dass und .
- Falls , gehe zurück zu Schritt 1 und wähle ein neues .
- Andernfalls berechne die GGTs, um die Faktoren zu extrahieren:
Diese werden nichttriviale Faktoren von sein.
- Rekursiv faktorisieren, falls nötig
- Falls und/oder nicht prim sind, wende den Algorithmus rekursiv an, um sie vollständig zu faktorisieren.
- Sobald alle Faktoren Primzahlen sind, ist die Faktorisierung abgeschlossen.
Basierend auf diesem Verfahren ist es vielleicht nicht offensichtlich, warum ein Quantencomputer benötigt wird, um diese Aufgabe zu erledigen. Er ist notwendig, weil Schritt 2, das Finden der Ordnung von modulo , klassisch ein sehr schwieriges Problem ist. Die Komplexität skaliert exponentiell mit der Zahl . Aber mit einem Quantencomputer müssen wir nur die Quanten-Phasenschätzung verwenden, um es zu lösen. Schritt 4, das Finden des GGT zweier ganzer Zahlen, ist klassisch eigentlich recht einfach. Der einzige Schritt, der tatsächlich die Leistung eines Quantencomputers benötigt, ist der Ordnungsfindungsschritt. Wir sagen, dass sich das Faktorisierungsproblem auf das Ordnungsfindungsproblem „reduziert".
Der schwierige Teil: Ordnungsfindung
Nun gehen wir durch, wie wir einen Quantencomputer bei der Ordnungsfindung einsetzen können. Zunächst klären wir, was wir mit „Ordnung" meinen. Natürlich habe ich dir bereits erklärt, was die Ordnung mathematisch bedeutet: Es ist die erste von Null verschiedene ganze Zahl , sodass Aber sehen wir, ob wir etwas mehr Intuition für dieses Konzept gewinnen können.
Für genügend kleines können wir die Ordnung einfach bestimmen, indem wir jede Potenz von berechnen, den Modulus dieser Zahl nehmen und dann aufhören, wenn wir die Potenz finden, die erfüllt. Das haben wir mit unserem Beispiel oben gemacht. Schauen wir uns einige Graphen dieser modularen Potenzen für einige Beispielwerte von und an:
Fällt dir etwas auf? Das sind periodische Funktionen! Und die Ordnung ist gleich der Periode! Ordnungsfindung ist also äquivalent zur Periodenfindung.
Quantencomputer sind sehr gut geeignet, die Periode von Funktionen zu finden. Dafür können wir eine algorithmische Unterroutine namens Quanten-Phasenschätzung verwenden. Wir haben QPE und ihre Beziehung zur Quanten-Fourier-Transformation im vorherigen Modul besprochen. Für eine ausführliche Auffrischung besuche das QFT-Modul oder John Watrous' Lektion zur Quanten-Phasenschätzung in seinem Kurs über Quantenalgorithmen. Wir gehen nun das Wesentliche des Verfahrens durch:
Bei der Quanten-Phasenschätzung (QPE) beginnen wir mit einem Unitären und einem Eigenzustand dieses Unitären . Dann verwenden wir QPE, um den entsprechenden Eigenwert zu approximieren, der, da der Operator unitär ist, die Form hat. Das Finden des Eigenwerts ist also äquivalent zum Finden des Werts von in der periodischen Funktion. Der Circuit sieht so aus:

wobei die Anzahl der Kontroll-Qubits (die oberen Qubits in der Abbildung oben) die Genauigkeit der Approximation bestimmt.
Im Shor-Algorithmus verwenden wir QPE auf den unitären Operator :
Hier bezeichnet einen Rechenbasis-Zustand des Multi-Qubit-Registers, wobei der Binärwert der Qubits der ganzen Zahl entspricht. Zum Beispiel, wenn und , dann wird durch den Vier-Qubit-Basiszustand dargestellt, da vier Qubits benötigt werden, um Zahlen bis 15 zu kodieren. (Falls dieses Konzept unbekannt ist, siehe das einführende Qiskit-in-Classrooms-Modul für eine Auffrischung zur Binärkodierung von Quantenzuständen.)
Nun müssen wir einen Eigenzustand dieses Unitären bestimmen. Wenn wir im Zustand starten, können wir sehen, dass jede aufeinanderfolgende Anwendung von den Zustand unseres Registers mit multipliziert, und nach Anwendungen wieder zum Zustand zurückkehrt. Zum Beispiel mit und :
Superpositionen der Zustände in diesem Zyklus () der Form:
sind alles Eigenzustände von . (Es gibt mehr Eigenzustände als nur diese. Aber wir interessieren uns nur für die der obigen Form.)
Überprüfe dein Verständnis
Finde einen Eigenzustand des Unitären, der und entspricht.
Answer
Die Ordnung ist also . Die Eigenzustände, die uns interessieren, sind eine gleichmäßige Superposition aller Zustände, die oben durchlaufen wurden, mit verschiedenen Phasen:
Nehmen wir an, wir wären in der Lage, unseren Qubit-Zustand in einen dieser Eigenzustände zu initialisieren (Spoiler — das können wir nicht. Oder zumindest nicht einfach. Wir werden gleich erklären warum und was wir stattdessen tun können). Dann könnten wir QPE verwenden, um den entsprechenden Eigenwert zu schätzen, wobei . Dann können wir die Ordnung durch die einfache Gleichung bestimmen:
Aber denke daran, ich habe gesagt, dass QPE schätzt — es gibt uns keinen exakten Wert. Wir brauchen eine Schätzung, die gut genug ist, um zwischen und zu unterscheiden. Je mehr Kontroll-Qubits wir haben, desto besser wird die Schätzung. In den Aufgaben am Ende der Lektion wirst du aufgefordert, das minimale zu bestimmen, das zum Faktorisieren einer Zahl benötigt wird.
Nun müssen wir ein Problem in Einklang bringen. Die gesamte obige Erklärung, wie man findet, beginnt mit der Vorbereitung des Eigenzustands . Aber wir wissen nicht, wie wir das tun sollen, ohne bereits zu wissen, was ist. Die Logik ist zirkulär. Wir brauchen eine Möglichkeit, den Eigenwert zu schätzen, ohne den Eigenzustand zu initialisieren.
Anstatt mit einem Eigenzustand von zu beginnen, können wir den Anfangszustand in den -Qubit-Zustand präparieren, der in Binärdarstellung entspricht (also ). Obwohl dieser Zustand offensichtlich kein Eigenzustand von ist, ist er eine Superposition über alle Eigenzustände :
Überprüfe dein Verständnis
Überprüfe, dass äquivalent zur Superposition über die Eigenzustände ist, die du für und in der vorherigen Verständnisfrage gefunden hast.
Answer
Die vier Eigenzustände waren:
Also:
Wie ermöglicht uns das, die Ordnung zu finden? Da der Ausgangszustand eine Superposition über alle Eigenzustände der oben aufgeführten Form ist, schätzt der QPE-Algorithmus gleichzeitig alle , die diesen Eigenzuständen entsprechen. Die Messung der Kontroll-Qubits am Ende liefert also eine Approximation des Werts , wobei einer der zufällig ausgewählten Eigenwerte ist. Wenn wir diesen Circuit einige Male wiederholen und einige Stichproben mit verschiedenen Werten von erhalten, können wir schnell ableiten.
Implementierung in Qiskit
Wie bereits erwähnt, ist unsere Hardware noch nicht an dem Punkt, an dem wir riesige Zahlen wie RSA1024 faktorisieren können. Wir werden nur eine kleine Zahl faktorisieren, um die Funktionsweise des Algorithmus zu demonstrieren. Für diese Demo verwenden wir eine vereinfachte Version des Codes aus dem Shor-Algorithmus-Tutorial. Für weitere Details besuche bitte das Tutorial.
Wir werden den Algorithmus mit unserem Standardrahmenwerk zum Lösen von Quantenproblemen ausführen, dem sogenannten Qiskit-Patterns-Framework. Dieses besteht aus vier Schritten:
- Abbildung deines Problems auf einen Quanten-Circuit
- Optimierung des Circuits für die Ausführung auf Quantenhardware
- Ausführung deines Circuits auf dem Quantencomputer
- Nachbearbeitung der Messungen
1. Abbildung
Faktorisieren wir und wählen als unsere teilerfremde ganze Zahl.
Zuerst müssen wir den Circuit konstruieren, der den modularen Multiplikationsunitären implementiert. Das ist tatsächlich der kniffligste Teil der gesamten Implementierung und kann je nach Umsetzung sehr rechenaufwändig sein. Dafür schummeln wir ein wenig: Wir wissen, dass wir im Zustand starten, und aus einer früheren Verständnisfrage:
Wir werden also einen Unitären konstruieren, der die korrekten Operationen auf diesen vier Zuständen durchführt, aber alle anderen Zustände unverändert lässt. Das ist Schummeln, weil wir unser Wissen über die Ordnung von verwenden, um den Unitären zu vereinfachen. Wenn wir tatsächlich versuchen würden, eine Zahl zu faktorisieren, deren Faktoren uns unbekannt wären, könnten wir das nicht tun.
Überprüfe dein Verständnis
Konstruiere mit deinem Wissen darüber, wie der -Operator die obigen Zustände transformiert, den Operator aus einer Reihe von SWAP-Gates, die die Zustände zweier Qubits austauschen. (Hinweis: Es hilft, jeden Zustand in Binärdarstellung zu schreiben.)
Answer
Schreiben wir die Wirkung von auf die Zustände in Binärdarstellung um:
Jede dieser Aktionen kann mit einem einfachen SWAP erreicht werden. wird durch Vertauschen der Zustände von Qubit und erreicht. wird durch Vertauschen der Zustände von Qubit und erreicht. Und so weiter. Wir können also die -Matrix in die folgende Reihe von SWAP-Gates zerlegen:
Da Operatoren von rechts nach links wirken, überprüfen wir, dass dies die gewünschte Wirkung auf jeden der Zustände hat:
Wir können nun den Circuit, der diesem Operator entspricht, in Qiskit programmieren.
Zuerst importieren wir die notwendigen Pakete:
# 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
Dann erstellen wir den -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)
Der QPE-Algorithmus verwendet ein kontrolliertes--Gate. Da wir nun einen -Circuit haben, müssen wir daraus einen kontrollierten--Circuit machen:
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)
Nun haben wir unser kontrolliertes--Gate. Aber um den Quanten-Phasenschätzungsalgorithmus auszuführen, benötigen wir kontrolliertes-, kontrolliertes-, bis zu kontrolliertes-, wobei die Anzahl der Qubits ist, die zur Phasenschätzung verwendet werden. Je mehr Qubits, desto präziser wird die Phasenschätzung. Wir verwenden Kontroll-Qubits für unser Phasenschätzungsverfahren. Wir benötigen also:
wobei der Index mit dem Kontroll-Qubit entspricht. Berechnen wir nun für jeden Wert von :
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]
Da für , sind alle entsprechenden Operatoren ( und höher) äquivalent zur Identität. Wir müssen also nur noch eine weitere Matrix konstruieren,
Hinweis: Diese Vereinfachung funktioniert hier nur, weil die Ordnung von gleich ist. Sobald (also ), ist jede nachfolgende Potenz des Operators die Identität. Im Allgemeinen kann man für größere Zahlen oder andere Wahlen von die Konstruktion der höheren Potenzen nicht überspringen. Dies ist einer der Gründe, warum dies als Spielzeugbeispiel betrachtet wird: Die kleinen Zahlen ermöglichen Abkürzungen, die bei größeren Fällen nicht funktionieren würden.
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)
Und wie zuvor machen wir daraus einen kontrollierten--Operator:
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)
Nun können wir alles zusammensetzen, um die Ordnung von mit einem Quanten-Circuit unter Verwendung der Phasenschätzung zu finden:
# 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. Optimierung
Nachdem wir unseren Circuit abgebildet haben, ist der nächste Schritt, ihn für die Ausführung auf einem bestimmten Quantencomputer zu optimieren. Zuerst müssen wir das Backend laden.
service = QiskitRuntimeService()
backend = service.backend("ibm_marrakesh")
Falls du keine verfügbare Zeit auf deinem Konto hast oder aus irgendeinem Grund einen Simulator verwenden möchtest, kannst du die untenstehende Zelle ausführen, um einen Simulator einzurichten, der das oben ausgewählte Quantengerät nachahmt:
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. Ausführung
# 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))

Wir sehen vier deutliche Spitzen bei 00000000, 01000000, 10000000 und 11000000, mit einigen Zählungen in anderen Bitstrings aufgrund von Rauschen im Quantencomputer. Wir ignorieren diese und behalten nur die dominanten vier, indem wir einen Schwellenwert festlegen: Nur Zählungen oberhalb dieses Schwellenwerts werden als echtes Signal über dem Rauschen betrachtet.
# 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. Nachbearbeitung
Beim Shor-Algorithmus wird ein Großteil des Algorithmus klassisch durchgeführt. Wir legen also den Rest in den „Nachbearbeitungs"-Schritt, nachdem wir unsere Messungen vom Quantencomputer erhalten haben. Jede der obigen Messungen kann in ganze Zahlen umgewandelt werden, die nach Division durch unsere Approximationen für sind, wobei jedes Mal zufällig ist.
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 ***
Fazit
Nachdem du dieses Modul durchgearbeitet hast, wirst du vielleicht eine neue Wertschätzung für die Brillanz von Peter Shor entwickelt haben, der einen so cleveren Algorithmus erdacht hat. Aber hoffentlich hast du auch ein neues Verständnisniveau für dessen trügerische Einfachheit erreicht. Obwohl der Algorithmus beeindruckend (oder einschüchternd) komplex erscheinen mag — wenn du ihn in jeden logischen Schritt zerlegst und langsam durchgehst, wirst auch du in der Lage sein, den Shor-Algorithmus auszuführen.
Wir sind zwar weit davon entfernt, diesen Algorithmus zur Faktorisierung von Zahlen wie RSA1024 zu verwenden, aber unsere Quantencomputer werden jeden Tag besser, und sobald eine Schwelle namens Fehlertoleranz erreicht ist, werden Algorithmen wie diese bald folgen. Es ist eine aufregende Zeit, um etwas über Quantencomputing zu lernen!
Aufgaben
Schlüsselkonzepte:
- Moderne kryptografische Systeme beruhen auf der klassischen Schwierigkeit, große ganze Zahlen zu faktorisieren.
- Modulare Arithmetik — einschließlich der Strukturen und — liefert die mathematische Grundlage für den Shor-Algorithmus.
- Das Problem der Faktorisierung einer ganzen Zahl kann auf das Problem der Ordnungsfindung einer Zahl modulo reduziert werden.
- Die quantenmechanische Ordnungsfindung nutzt Quanten-Phasenschätzungstechniken, um die Periode der Funktion zu bestimmen.
- Der Shor-Algorithmus besteht aus einem klassisch-quantenmechanischen hybriden Workflow, der eine Basis wählt, quantenmechanische Ordnungsfindung durchführt und dann klassisch die Faktoren aus dem Ergebnis berechnet.
Wahr/Falsch:
- W/F Die Effizienz des Shor-Algorithmus bedroht die Sicherheit der RSA-Verschlüsselung.
- W/F Der Shor-Algorithmus kann auf jedem modernen Quantencomputer effizient ausgeführt werden.
- W/F Der Shor-Algorithmus verwendet die Quanten-Phasenschätzung (QPE) als zentrale Unterroutine.
- W/F Der klassische Teil des Shor-Algorithmus beinhaltet die Berechnung des größten gemeinsamen Teilers (GGT).
- W/F Der Shor-Algorithmus funktioniert nur für die Faktorisierung gerader Zahlen.
- W/F Ein erfolgreicher Durchlauf des Shor-Algorithmus garantiert immer die korrekten Faktoren.
Kurzantworten:
- Warum wird der Shor-Algorithmus als potenzielle zukünftige Bedrohung für die RSA-Verschlüsselung angesehen?
- Warum ist das Finden der Periode oder Ordnung einer modularen Exponentialfunktion hilfreich für die Faktorisierung einer Zahl im Shor-Algorithmus?
Herausforderungsaufgaben:
-
Wie viele Kontroll-Qubits benötigen wir für eine gegebene Zahl , die wir zu faktorisieren versuchen, um die nötige Genauigkeit in der QPE zu erhalten, um den korrekten Wert der Ordnung zu finden?
-
Versuche nun, dem Verfahren folgend, das wir hier zum Faktorisieren von 15 beschrieben haben, die Zahl 21 zu faktorisieren.