Der Deutsch-Jozsa-Algorithmus
Für dieses Qiskit-in-Classrooms-Modul müssen Studierende eine funktionierende Python-Umgebung mit den folgenden installierten Paketen haben:
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 verwendete vier 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-aer 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'
Schau dir die Modul-Einführung von Dr. Katie McCormick unten an, oder klicke hier, um sie auf YouTube anzusehen.
Einführung
In den frühen 1980er Jahren hatten Quantenphysiker und Informatiker eine vage Vorstellung davon, dass die Quantenmechanik genutzt werden könnte, um Berechnungen durchzuführen, die weitaus leistungsfähiger sind als das, was klassische Computer leisten können. Ihre Argumentation war folgende: Es ist für einen klassischen Computer schwierig, Quantensysteme zu simulieren, aber ein Quantencomputer sollte dies effizienter können. Und wenn ein Quantencomputer Quantensysteme effizienter simulieren könnte, gäbe es vielleicht auch andere Aufgaben, die er effizienter als ein klassischer Computer ausführen könnte.
Die Logik war stichhaltig, aber die Details mussten noch ausgearbeitet werden. Dies begann 1985, als David Deutsch den ersten „universellen Quantencomputer" beschrieb. In derselben Arbeit lieferte er das erste Beispielproblem, für das ein Quantencomputer etwas effizienter lösen konnte als ein klassischer Computer. Dieses erste Spielzeugbeispiel ist heute als „Deutsch-Algorithmus" bekannt. Die Verbesserung in Deutschs Algorithmus war bescheiden, aber Deutsch arbeitete einige Jahre später mit Richard Jozsa zusammen, um die Lücke zwischen klassischen und Quantencomputern weiter zu vergrößern.
Diese Algorithmen — Deutschs und die Deutsch-Jozsa-Erweiterung — sind nicht besonders nützlich, aber sie sind aus mehreren Gründen dennoch wirklich wichtig:
- Historisch gesehen waren sie einige der ersten Quantenalgorithmen, bei denen nachgewiesen wurde, dass sie ihre klassischen Gegenstücke übertreffen. Sie zu verstehen kann uns helfen zu verstehen, wie sich das Denken der Gemeinschaft über Quantencomputing im Laufe der Zeit entwickelt hat.
- Sie können uns helfen, einige Aspekte der Antwort auf eine überraschend subtile Frage zu verstehen: Was verleiht dem Quantencomputing seine Leistungsfähigkeit? Manchmal werden Quantencomputer mit riesigen, exponentiell skalierenden Parallelprozessoren verglichen. Aber das ist nicht ganz richtig. Während ein Teil der Antwort auf diese Frage im sogenannten „Quantenparallelismus" liegt, ist das Extrahieren möglichst vieler Informationen in einem einzigen Durchlauf eine subtile Kunst. Die Deutsch- und Deutsch-Jozsa-Algorithmen zeigen, wie dies gelingen kann.
In diesem Modul lernen wir den Deutsch-Algorithmus, den Deutsch-Jozsa-Algorithmus und was sie uns über die Leistungsfähigkeit des Quantencomputings lehren.
Quantenparallelismus und seine Grenzen
Ein Teil der Leistungsfähigkeit des Quantencomputings leitet sich vom „Quantenparallelismus" ab, der im Wesentlichen die Fähigkeit ist, Operationen auf mehreren Eingaben gleichzeitig durchzuführen, da die Qubit-Eingabezustände in einer Superposition mehrerer klassisch erlaubter Zustände sein könnten. JEDOCH: Während ein Quantenschaltkreis möglicherweise mehrere Eingabezustände gleichzeitig auswerten kann, ist es unmöglich, all diese Informationen auf einmal zu extrahieren.
Um zu verstehen, was ich hier meine, nehmen wir an, wir haben ein Bit und eine auf dieses Bit angewandte Funktion . Es gibt vier mögliche binäre Funktionen, die ein einzelnes Bit auf ein anderes einzelnes Bit abbilden:
| 0 | 0 | 0 | 1 | 1 |
| 1 | 0 | 1 | 0 | 1 |
Wir möchten herausfinden, welche dieser Funktionen (1-4) unser ist. Klassisch müssten wir die Funktion zweimal ausführen — einmal für , einmal für . Aber sehen wir, ob wir es mit einem Quantenschaltkreis besser machen können. Wir können mit dem folgenden Gate etwas über die Funktion lernen:
Hier berechnet das -Gate , wobei der Zustand von Qubit 0 ist, und wendet das auf Qubit 1 an. Der resultierende Zustand wird also einfach zu , wenn . Dies enthält alle Informationen, die wir benötigen, um die Funktion zu kennen: Qubit 0 sagt uns, was ist, und Qubit 1 sagt uns, was ist. Wenn wir also initialisieren, dann wird der Endzustand beider Qubits sein: . Aber wie greifen wir auf diese Informationen zu?
2.1. Probiere es mit Qiskit:
Mit Qiskit werden wir zufällig eine der vier möglichen Funktionen oben auswählen und den Schaltkreis ausführen. Deine Aufgabe ist es dann, die Messungen des Quantenschaltkreises zu verwenden, um die Funktion mit so wenigen Durchläufen wie möglich zu ermitteln.
In diesem ersten Experiment und im gesamten Modul werden wir ein Framework für Quantencomputing namens „Qiskit Patterns" verwenden, das Arbeitsabläufe in die folgenden Schritte unterteilt:
- Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
- Schritt 2: Problem für die Quantenausführung optimieren
- Schritt 3: Mit Qiskit Runtime Primitives ausführen
- Schritt 4: Nachbearbeitung und klassische Analyse
Beginnen wir mit dem Laden einiger notwendiger Pakete, einschließlich der Qiskit Runtime Primitives. Wir werden auch den am wenigsten ausgelasteten verfügbaren Quantencomputer auswählen.
Es gibt unten Code zum Speichern deiner Anmeldedaten bei der ersten Verwendung. Stelle sicher, dass du diese Informationen nach dem Speichern in deiner Umgebung aus dem Notebook löschst, damit deine Anmeldedaten nicht versehentlich weitergegeben werden, wenn du das Notebook teilst. Siehe IBM Cloud-Konto einrichten und Service in einer nicht vertrauenswürdigen Umgebung initialisieren für weitere Hinweise.
# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService
# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler
# Syntax for first saving your token. Delete these lines after saving your credentials.
# QiskitRuntimeService.save_account(channel='ibm_quantum_platform', instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR_API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')
# Load saved credentials
service = QiskitRuntimeService()
# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
# backend = service.least_busy(operational=True, simulator=False, min_num_qubits = 127)
backend = service.backend("ibm_brisbane")
print(backend.name)
sampler = Sampler(mode=backend)
ibm_brisbane
Die folgende Zelle ermöglicht es dir, im gesamten Notebook zwischen der Verwendung des Simulators und realer Hardware umzuschalten. Wir empfehlen, sie jetzt auszuführen:
# Load the backend sampler
from qiskit.primitives import BackendSamplerV2
# Load the Aer simulator and generate a noise model based on the currently-selected backend.
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel
# Alternatively, load a fake backend with generic properties and define a simulator.
noise_model = NoiseModel.from_backend(backend)
# Define a simulator using Aer, and use it in Sampler.
backend_sim = AerSimulator(noise_model=noise_model)
sampler_sim = BackendSamplerV2(backend=backend_sim)
# You could also define a simulator-based sampler using a generic backend:
# backend_gen = GenericBackendV2(num_qubits=18)
# sampler_gen = BackendSamplerV2(backend=backend_gen)
Nachdem wir die notwendigen Pakete geladen haben, können wir mit dem Qiskit-Patterns-Arbeitsablauf fortfahren. Im folgenden Mapping-Schritt erstellen wir zunächst eine Funktion, die unter den vier möglichen Funktionen auswählt, die ein einzelnes Bit auf ein anderes einzelnes Bit abbilden.
# Step 1: Map
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
def twobit_function(case: int):
"""
Generate a valid two-bit function as a `QuantumCircuit`.
"""
if case not in [1, 2, 3, 4]:
raise ValueError("`case` must be 1, 2, 3, or 4.")
f = QuantumCircuit(2)
if case in [2, 3]:
f.cx(0, 1)
if case in [3, 4]:
f.x(1)
return f
# first, convert oracle circuit (above) to a single gate for drawing purposes. otherwise, the circuit is too large to display
# blackbox = twobit_function(2).to_gate() # you may edit the number inside "twobit_function()" to select among the four valid functions
# blackbox.label = "$U_f$"
qc.h(0)
qc.barrier()
qc.compose(twobit_function(2), inplace=True)
qc.measure_all()
qc.draw("mpl")
Im obigen Schaltkreis bringt das Hadamard-Gate „H" Qubit 0, das anfänglich im Zustand ist, in den Superpositionszustand . Dann wertet die Funktion aus und wendet das auf Qubit 1 an.
Als nächstes müssen wir den Schaltkreis optimieren und transpilieren, um ihn auf dem Quantencomputer auszuführen:
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
Schließlich führen wir unseren transpilierten Schaltkreis auf dem Quantencomputer aus und visualisieren unsere Ergebnisse:
# Step 3: Run the job on a real quantum computer
job = sampler.run([qc_isa], shots=1)
# job = sampler_sim.run([qc_isa],shots=1) # uncomment this line to run on simulator instead
res = job.result()
counts = res[0].data.meas.get_counts()
# Step 4: Visualize and analyze results
## Analysis
from qiskit.visualization import plot_histogram
plot_histogram(counts)
Oben ist ein Histogramm unserer Ergebnisse. Abhängig von der Anzahl der Shots, die du für die Ausführung des Schaltkreises in Schritt 3 oben gewählt hast, könntest du einen oder zwei Balken sehen, die die gemessenen Zustände der beiden Qubits in jedem Shot darstellen. Wie immer bei Qiskit und in diesem Notebook verwenden wir die „Little-Endian"-Notation, was bedeutet, dass die Zustände der Qubits 0 bis n in aufsteigender Reihenfolge von rechts nach links geschrieben werden, sodass Qubit 0 immer ganz rechts steht.
Da Qubit 0 in einem Superpositionszustand war, hat der Schaltkreis also die Funktion für beide und gleichzeitig ausgewertet — etwas, das klassische Computer nicht können! Aber der Haken kommt, wenn wir etwas über die Funktion erfahren wollen — wenn wir die Qubits messen, kollabieren wir ihren Zustand. Wenn du „shots = 1" wählst, um den Schaltkreis nur einmal auszuführen, wirst du nur einen Balken im obigen Histogramm sehen, und deine Informationen über die Funktion werden unvollständig sein.
Überprüfe dein Verständnis
Lies die Frage(n) unten, denke über deine Antwort nach und klicke dann auf das Dreieck, um die Lösung anzuzeigen.
Wie oft müssen wir den obigen Algorithmus ausführen, um die Funktion zu ermitteln? Ist das besser als im klassischen Fall? Hättest du lieber einen klassischen oder einen Quantencomputer, um dieses Problem zu lösen?
Antwort:
Da die Messung die Superposition kollabiert und nur einen Wert zurückgibt, müssen wir den Schaltkreis mindestens zweimal ausführen, um beide Ausgaben der Funktion und zu erhalten. Im besten Fall ist die Leistung genauso gut wie im klassischen Fall, wo wir sowohl als auch in den ersten beiden Abfragen berechnen. Aber es besteht die Chance, dass wir ihn mehr als zweimal ausführen müssen, da die endgültige Messung probabilistisch ist und die ersten beiden Male denselben -Wert zurückgeben könnte. In diesem Fall hätte ich lieber einen klassischen Computer.
Obwohl Quantenparallelismus also bei richtiger Anwendung leistungsfähig sein kann, ist es nicht korrekt zu sagen, dass ein Quantencomputer wie ein massiver, klassischer Parallelprozessor funktioniert. Der Akt der Messung kollabiert die Quantenzustände, sodass wir immer nur auf eine einzelne Ausgabe der Berechnung zugreifen können.
Der Deutsch-Algorithmus
Während Quantenparallelismus allein uns keinen Vorteil gegenüber klassischen Computern verschafft, können wir ihn mit einem anderen Quantenphänomen, der Interferenz, kombinieren, um eine Beschleunigung zu erzielen. Der heute als „Deutsch-Algorithmus" bekannte Algorithmus ist das erste Beispiel eines Algorithmus, der dies erreicht.
Das Problem
Hier war das Problem:
Gegeben ein Eingabebit und eine Eingabefunktion , bestimme, ob die Funktion balanciert oder konstant ist. Das heißt, wenn sie balanciert ist, dann ist die Ausgabe der Funktion die Hälfte der Zeit 0 und die andere Hälfte 1. Wenn sie konstant ist, dann ist die Ausgabe der Funktion entweder immer 0 oder immer 1. Erinnere dich an die Tabelle der vier möglichen Funktionen, die ein einzelnes Bit auf ein anderes einzelnes Bit abbilden:
| 0 | 0 | 0 | 1 | 1 |
| 1 | 0 | 1 | 0 | 1 |
Die erste und die letzte Funktion, und , sind konstant, während die beiden mittleren Funktionen, und , balanciert sind.
Der Algorithmus
Die Art, wie Deutsch dieses Problem anging, war über das „Abfragemodell". Im Abfragemodell ist die Eingabefunktion ( oben) in einer „Black Box" enthalten — wir haben keinen direkten Zugriff auf ihren Inhalt, aber wir können die Black Box abfragen und sie gibt uns die Ausgabe der Funktion. Manchmal sagen wir, dass ein „Orakel" diese Information liefert. Siehe Lektion 1: Quanten-Abfragealgorithmen des Kurses „Grundlagen der Quantenalgorithmen" für mehr zum Abfragemodell.
Um festzustellen, ob ein Quantenalgorithmus effizienter ist als ein klassischer Algorithmus im Abfragemodell, können wir einfach die Anzahl der Abfragen vergleichen, die wir in jedem Fall an die Black Box stellen müssen. Im klassischen Fall müssten wir die Box zweimal abfragen, um zu wissen, ob die in der Black Box enthaltene Funktion balanciert oder konstant ist, um sowohl als auch zu erhalten.
In Deutschs Quantenalgorithmus fand er jedoch einen Weg, die Information mit nur einer Abfrage zu erhalten! Er nahm eine Anpassung am „Quantenparallelismus"-Schaltkreis oben vor, sodass er einen Superpositionszustand auf beiden Qubits vorbereitete, anstatt nur auf Qubit 0. Dann interferierten die beiden Ausgaben der Funktion, und , um 0 zurückzugeben, wenn sie entweder beide 0 oder beide 1 waren (die Funktion war konstant), und 1 zurückzugeben, wenn sie verschieden waren (die Funktion war balanciert). Auf diese Weise konnte Deutsch mit einer einzigen Abfrage zwischen einer konstanten und einer balancierten Funktion unterscheiden.
Hier ist ein Schaltkreisdiagramm von Deutschs Algorithmus:

Um zu verstehen, wie dieser Algorithmus funktioniert, betrachten wir die Quantenzustände der Qubits an den drei im Diagramm oben markierten Punkten. Versuche, die Zustände selbst auszurechnen, bevor du klickst, um die Antworten anzuzeigen:
Überprüfe dein Verständnis
Lies die Fragen unten, denke über deine Antworten nach und klicke dann auf die Dreiecke, um die Lösungen anzuzeigen.
Was ist der Zustand ?
Antwort:
Das Anwenden eines Hadamard transformiert den Zustand zu und den Zustand zu . Der Gesamtzustand wird also:
Was ist der Zustand ?
Antwort:
Bevor wir anwenden, erinnern wir uns daran, was es tut. Es ändert den Zustand von Qubit 1 basierend auf dem Zustand von Qubit 0. Es ist also sinnvoll, den Zustand von Qubit 0 herauszufaktorisieren: . Wenn dann , werden die beiden Terme auf die gleiche Weise transformiert und das relative Vorzeichen zwischen den beiden Termen bleibt positiv, aber wenn , dann nimmt der zweite Term ein Minuszeichen relativ zum ersten Term auf, wodurch der Zustand von Qubit 0 von zu wechselt. Also: