Lernprogramm: Implementieren der Quanten fouriertransformation in Q#
In diesem Tutorial erfahren Sie, wie Sie ein einfaches Quantenprogramm schreiben und simulieren, das mit einzelnen Qubits arbeitet.
Q# wurde zwar in erster Linie als Programmiersprache auf hoher Ebene für umfangreiche Quantenprogramme entwickelt, eignet sich aber genauso gut zur Erkundung der unteren Ebene von Quantenprogrammen: die direkte Adressierung bestimmter Qubits. In diesem Tutorial wird insbesondere die Quanten-Fourier-Transformation (QFT) näher betrachtet – einer Unterroutine, die in viele größere Quantenalgorithmen integriert ist.
In diesem Tutorial lernen Sie, wie Sie:
- Definieren von Quantenvorgängen in Q#.
- Schreiben des Quantum Fourier Transform Circuit
- Simulieren Sie einen Quantenvorgang von der Qubitzuordnung bis zur Messausgabe.
- Beobachten Sie, wie sich die simulierte Wellenfunktion des Quantensystems während des gesamten Vorgangs weiterentwickelt.
Hinweis
Diese Betrachtung der Verarbeitung von Quanteninformationen auf niedrigerer Ebene wird häufig in Form von Quantenschaltungen beschrieben, die die sequenzielle Anwendung von Gates (oder Vorgängen) auf bestimmte Qubits eines Systems darstellen. Daher können die sequenziell angewendeten Einzel- und Multi-Qubit-Vorgänge problemlos in Schaltungsdiagrammen dargestellt werden. Die in diesem Lernprogramm verwendete vollständige Drei-Qubit-Quanten fouriertransformation weist beispielsweise die folgende Darstellung als Schaltkreis auf:
Tipp
Wenn Sie Ihre Quantum Computing-Reise beschleunigen möchten, schauen Sie sich Code mit Azure Quantum an, einem einzigartigen Feature der Azure Quantum-Website. Hier können Sie integrierte Q# Beispiele oder Eigene Q# Programme ausführen, neuen Q# Code aus Ihren Eingabeaufforderungen generieren, Ihren Code in VS Code für das Web mit nur einem Klick öffnen und ausführen und Copilot fragen.
Voraussetzungen
Die neueste Version von Visual Studio Code oder öffnen Sie VS Code im Web.
Die neueste Version der Azure Quantum Development Kit (QDK) Erweiterung. Details zur Installation finden Sie unter Einrichten der QDK-Erweiterung.
Wenn Sie Jupyter-Notizbücher verwenden möchten, müssen Sie auch Python
qsharp
. Öffnen Sie dazu ein Terminal, und führen Sie den folgenden Befehl aus:$ pip install --upgrade qsharp
Erstellen einer neuen Q# Datei
- Wählen Sie in VS Code die Option "Neue > Textdatei speichern" aus.
- Speichern Sie die Datei als QFTcircuit.qs. Diese Datei enthält den Q# Code für Ihr Programm.
- Öffnen Sie QFTcircuit.qs.
Schreiben eines QFT-Schaltkreises in Q#
Der erste Teil dieses Tutorials besteht aus der Definition des Q#Vorgangs Main
, der die Fourier-Quantentransformation für drei Qubits ausführt. Die DumpMachine
-Funktion wird verwendet, um zu beobachten, wie sich die simulierte Wellenfunktion des Drei-Qubit-Systems im Laufe des Vorgangs entwickelt. Im zweiten Teil des Lernprogramms fügen Sie Messfunktionen hinzu und vergleichen die Vor- und Nachmessungszustände der Qubits.
Sie erstellen den Vorgang schritt für Schritt. Kopieren Sie den Code in den folgenden Abschnitten, und fügen Sie ihn in die Datei QFTcircuit.qs ein.
Sie können den vollständigen Q# Code für diesen Abschnitt als Referenz anzeigen.
Importieren erforderlicher Q# Bibliotheken
Importieren Sie in Ihrer Q# Datei die relevanten Microsoft.Quantum.*
Namespaces.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
// operations go here
Definieren von Vorgängen mit Argumenten und Rückgaben
Definieren Sie als Nächstes den Vorgang Main
:
operation Main() : Unit {
// do stuff
}
Der Main()
Vorgang verwendet niemals Argumente und gibt nun ein Unit
Objekt zurück, das analog zur Rückgabe void
in C# oder einem leeren Tupel Tuple[()]
in Python ist.
Später ändern Sie den Vorgang so, dass ein Array von Messergebnissen zurückgegeben wird.
Zuordnen von Qubits
Weisen Sie innerhalb des Q# Vorgangs ein Register von drei Qubits mit dem use
Schlüsselwort zu. Bei use
werden die Qubits automatisch im $\ket {0} $-Zustand zugeordnet.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
Wie bei realen Quantenberechnungen erlaubt Q# nicht, direkt auf qubit-Zustände zuzugreifen. Der DumpMachine
-Vorgang druckt jedoch den aktuellen Zustand der target-Maschine, sodass er wertvolle Einblicke für das Debuggen und Lernen liefern kann, wenn er zusammen mit dem vollständigen Zustandssimulator verwendet wird.
Anwenden von Einzel-Qubit- und kontrollierten Vorgängen
Als Nächstes wenden Sie die Vorgänge an, die den Main
Vorgang selbst umfassen. Q# enthält bereits viele dieser und andere grundlegende Quantenvorgänge im Microsoft.Quantum.Intrinsic
Namespace.
Hinweis
Beachten Sie, dass Microsoft.Quantum.Intrinsic
im früheren Codeausschnitt mit den anderen Namespaces nicht importiert wurde, da er automatisch vom Compiler für alle Q# Programme geladen wird.
Als Erstes wird der H
-Vorgang (Hadamard) auf das erste Qubit angewendet:
Verwenden Sie die Standardindexnotation, um einen Vorgang auf ein bestimmtes Qubit aus einem Register anzuwenden – etwa ein einzelnes Qubit
aus einem Array (Qubit[]
).
Wenn Sie also den H
-Vorgang auf das erste Qubit des Registers qs
anwenden, sieht das wie folgt aus:
H(qs[0]);
Neben der Anwendung des H
-Vorgangs auf einzelne Qubits besteht die QFT-Schaltung in erster Linie aus gesteuerten R1
-Rotationen. Ein R1(θ, <qubit>)
Vorgang im Allgemeinen lässt die $\ket{0}$-Komponente des Qubits unverändert, während eine Drehung von $e^{i\theta}$ auf die $\ket{1}$-Komponente angewendet wird.
Mit Q# ist es einfach, die Ausführung eines Vorgangs auf einzelne oder mehrere Steuerqubits zu konditionieren. Im Allgemeinen wird dem Aufruf Controlled
vorangestellt, und die Vorgangsargumente ändern sich wie folgt:
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
Beachten Sie, dass es sich bei den Steuerqubits um ein Array handeln muss. Das gilt auch im Falle eines einzelnen Qubits.
Die kontrollierten Vorgänge im QFT sind die R1
Vorgänge, die auf dem ersten Qubit (und von den zweiten und dritten Qubits gesteuert werden):
Rufen Sie diese Vorgänge in Ihrer Q#-Datei mit den folgenden Anweisungen auf:
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
Die Funktion PI()
dient zum Definieren der Rotationen im Hinblick auf Pi Radiant.
SWAP-Vorgang anwenden
Nachdem Sie die relevanten H
Vorgänge und kontrollierte Drehungen auf die zweiten und dritten Qubits angewendet haben, sieht der Schaltkreis wie folgt aus:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
Schließlich wenden Sie einen SWAP
Vorgang auf die ersten und dritten Qubits an, um den Schaltkreis abzuschließen. Dieser Vorgang ist notwendig, weil die Quanten-Fourier-Transformation die Qubits in umgekehrter Reihenfolge ausgibt. Die Swaps ermöglichen dadurch eine nahtlose Integration der Subroutine in größere Algorithmen.
SWAP(qs[2], qs[0]);
Die Vorgänge auf Qubit-Ebene der Quantum-Fourier-Transformation in Ihrem Q#-Vorgang sind damit fertig geschrieben:
Qubits freigeben
Der letzte Schritt besteht darin, erneut DumpMachine()
aufzurufen, um den Zustand nach dem Vorgang anzuzeigen und die Zuordnung der Qubits aufzuheben. Die Qubits befanden sich bei der Zuordnung im Zustand $\ket{0}$ und müssen mithilfe des ResetAll
-Vorgangs wieder in ihren ursprünglichen Zustand zurückversetzt werden.
Alle Qubits müssen explizit auf $\ket{0}$ zurückgesetzt werden, ist ein grundlegendes Feature von Q#, da andere Vorgänge ihren Zustand genau erkennen können, wenn sie mit der Verwendung derselben Qubits (einer knappen Ressource) beginnen. Außerdem wird durch das Zurücksetzen sichergestellt, dass sie nicht mit anderen Qubits im System verschränkt sind. Wird das Zurücksetzen nicht am Ende eines use
Zuweisungsblocks durchgeführt, kann ein Laufzeitfehler ausgelöst werden.
Fügen Sie Ihrer Q#-Datei folgende Zeilen hinzu:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
Der vollständige QFT-Vorgang
Das Q# Programm ist abgeschlossen. Ihre Datei "QFTcircuit.qs " sollte jetzt wie folgt aussehen:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
Ausführen des QFT-Schaltkreises
Derzeit gibt der Main
Vorgang keinen Wert zurück – der Vorgang gibt den Wert zurück Unit
. Später ändern Sie den Vorgang so, dass ein Array von Messergebnissen (Result[]
) zurückgegeben wird.
- Überprüfen Sie vor dem Ausführen des Programms in der Statusleiste am unteren Rand von VS-Code, dass das target Profil auf Q#: Uneingeschränkt festgelegt ist. Um das target Profil zu ändern, wählen Sie das target Profil in der Statusleiste aus, und wählen Sie im Dropdownmenü "Uneingeschränkt" aus. Wenn das Profil target nicht auf Unbeschränkt festgelegt ist, erhalten Sie einen Fehler, wenn Sie das Programm ausführen.
- Um Ihr Programm auszuführen, wählen Sie Q# ausführen" oben rechts aus, oder drücken Sie STRG+F5. Das Programm führt den
Main()
Vorgang im Standardsimulator aus. - Die
Message
Ausgaben undDumpMachine
Ausgaben werden in der Debugkonsole angezeigt.
Wenn Sie neugierig sind, wie andere Eingabezustände betroffen sind, sollten Sie vor der Transformation mit der Anwendung anderer Qubit-Vorgänge experimentieren.
Hinzufügen von Maßen zum QFT-Schaltkreis
Die Darstellung aus der DumpMachine
-Funktion zeigte die Ergebnisse des Vorgangs, aber leider besagt ein Eckpfeiler der Quantenmechanik, dass ein reales Quantensystem keine solche DumpMachine
-Funktion haben kann.
Stattdessen werden die Informationen durch Messungen extrahiert, was im Allgemeinen keine Informationen zum vollständigen Quantenzustand liefert und auch das System selbst drastisch verändern kann.
Es gibt viele Arten von Quantenmessungen. Im vorliegenden Beispiel wird jedoch die grundlegendste Option verwendet: projektive Messungen für einzelne Qubits. Nach der Messung in einer bestimmten Basis (z B. der Rechenbasis { \ket{0}, \ket{1} } $) wird der Qubit-Zustand auf den gemessenen Basiszustand projiziert und damit jegliche Superposition zwischen den beiden zerstört.
Ändern des QFT-Vorgangs
Verwenden Sie den Q#-Vorgang, der einen M
-Typ zurückgibt, um Messungen innerhalb eines Result
-Programms zu implementieren.
Ändern Sie zunächst den Vorgang Main
so, dass anstelle von Result[]
ein Array von Messergebnissen (Unit
) zurückgegeben wird.
operation Main() : Result[] {
Definieren und Initialisieren einer Result[]
Array
Deklarieren und binden Sie vor dem Zuordnen von Qubits ein Array mit drei Elementen (eines Result
für jedes Qubit):
mutable resultArray = [Zero, size = 3];
Das Schlüsselwort mutable
vor resultArray
sorgt dafür, dass die Variable später im Code geändert werden kann – etwa beim Hinzufügen der Messergebnisse.
Messungen in einer for
-Schleife durchführen und Ergebnisse zum Array hinzufügen
Fügen Sie nach den QFT-Transformationsvorgängen den folgenden Code ein:
for i in IndexRange(qs) {
resultArray w/= i <- M(qs[i]);
}
Wenn die IndexRange
-Funktion für ein Array (z. B. für das Qubit-Array qs
) aufgerufen wird, wird ein Bereich über die Indizes des Arrays zurückgegeben.
Hier wird es in der for
-Schleife verwendet, um mit der M(qs[i])
-Anweisung nacheinander jedes Qubit zu messen.
Jeder gemessene Result
-Typ (entweder Zero
oder One
) wird dann resultArray
mit einer Update-and-Reassign-Anweisung an der entsprechenden Indexposition in hinzugefügt.
Hinweis
Die Syntax dieser Anweisung ist einzigartig in Q#, entspricht aber der ähnlichen Variablenneuzuweisung resultArray[i] <- M(qs[i])
in anderen Sprachen wie F# und R.
Das Schlüsselwort wird immer verwendet, um Variablen neu zu set
zuweisen, die mithilfe von gebunden mutable
sind.
resultArray
zurückgeben
Nachdem alle drei Qubits gemessen und die Ergebnisse zu resultArray
addiert wurden, können Sie die Qubits wie zuvor zurücksetzen und freigeben. Um die Messungen zurückzugeben, geben Sie Folgendes ein:
return resultArray;
Führen Sie den QFT-Schaltkreis mit den Messungen aus.
Ändern Sie nun die Platzierung der DumpMachine
-Funktionen, um den Zustand vor und nach den Messungen auszugeben.
Der fertige Q#-Code sollte wie folgt aussehen:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
Tipp
Denken Sie daran, Ihre Datei jedes Mal zu speichern, wenn Sie eine Änderung am Code einführen, bevor Sie sie erneut ausführen.
- Überprüfen Sie vor dem Ausführen des Programms in der Statusleiste am unteren Rand von VS-Code, dass das target Profil auf Q#: Uneingeschränkt festgelegt ist. Um das target Profil zu ändern, wählen Sie das target Profil in der Statusleiste aus, und wählen Sie im Dropdownmenü "Uneingeschränkt" aus. Wenn das Profil target nicht auf Unbeschränkt festgelegt ist, erhalten Sie einen Fehler, wenn Sie das Programm ausführen.
- Wenn Sie Ihr Programm ausführen möchten, wählen Sie Q# ausführen" aus, oder drücken Sie STRG+5. Das Programm führt den
Main()
Vorgang im Standardsimulator aus. - Die
Message
Ausgaben undDumpMachine
Ausgaben werden in der Debugkonsole angezeigt.
Die zugehörige Ausgabe sollte etwa wie folgt aussehen:
Before measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
After measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|010⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[Zero, One, Zero]
Diese Ausgabe veranschaulicht einige verschiedene Dinge:
- Wenn Sie das zurückgegebene Ergebnis mit der Messung vor der Messung
DumpMachine
vergleichen, veranschaulicht es eindeutig nicht die Post-QFT-Überlagerung über Basiszustände. Eine Messung liefert nur einen einzigen Basiszustand mit einer Wahrscheinlichkeit, die durch die Amplitude dieses Zustands in der Wellenfunktion des Systems bestimmt wird. - Die Nachmessung
DumpMachine
zeigt, dass die Messung den Zustand verändert, indem sie ihn von der anfänglichen Superposition über Basiszustände auf den einzigen Basiszustand projiziert, der dem gemessenen Wert entspricht.
Wenn Sie diese Operation viele Male wiederholen, sehen Sie, dass die Ergebnisstatistiken die gleichgewichtige Überlagerung des Post-QFT-Zustands zu veranschaulichen beginnen, die bei jedem Schuss zu einem Zufallsergebnis führt. Dies wäre jedochnicht nur ineffizient und immer noch unvollkommen, sondern würde auch nur die relativen Amplituden der Basiszustände wiedergeben, nicht aber die relativen Phasen zwischen ihnen. Letzteres ist in diesem Beispiel kein Problem, aber Sie würden relative Phasen sehen, wenn Sie der QFT eine komplexere Eingabe als $\ket{000}$ geben würden.
Verwenden der Q# Vorgänge zur Vereinfachung des QFT-Schaltkreises
Wie in der Einführung erwähnt, liegt einer der großen Vorteile von Q# darin, dass sich damit die Probleme im Zusammenhang mit der Verwendung einzelner Qubits abstrahieren lassen.
In der Tat, wenn Sie vollwertige, anwendbare Quantenprogramme entwickeln wollen, würde die Sorge darüber, ob ein H
-Vorgang vor oder nach einer bestimmten Rotation stattfindet, Sie nur ausbremsen. Azure Quantum stellt den Vorgang bereit, den ApplyQFT
Sie für eine beliebige Anzahl von Qubits verwenden und anwenden können.
Ersetzen Sie alles von der ersten
H
Operation bis einschließlich durchSWAP
:ApplyQFT(qs);
Ihr Code sollte nun wie folgt aussehen:
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Arrays.*; operation Main() : Result[] { mutable resultArray = [Zero, size = 3]; use qs = Qubit[3]; //QFT: //first qubit: ApplyQFT(qs); Message("Before measurement: "); DumpMachine(); for i in IndexRange(qs) { resultArray w/= i <- M(qs[i]); } Message("After measurement: "); DumpMachine(); ResetAll(qs); Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: "); return resultArray; }
Führen Sie das Q# Programm erneut aus, und beachten Sie, dass die Ausgabe wie zuvor identisch ist.
Um den tatsächlichen Vorteil der Verwendung Q# von Vorgängen zu sehen, ändern Sie die Anzahl der Qubits auf etwas anderes als
3
:
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
So können Sie den richtigen QFT für jede bestimmte Anzahl von Qubits anwenden, ohne sich Gedanken über das Hinzufügen neuer H
Vorgänge und Drehungen auf jedem Qubit machen zu müssen.
Zugehöriger Inhalt
Sehen Sie sich weitere Q#-Tutorials an:
- Der Quantum random number generator zeigt, wie ein Q# Programm geschrieben wird, das Zufallszahlen aus Qubits in Superposition generiert.
- Q# von Grover verwendet.
- Quantenanglement zeigt, wie ein Q# Programm geschrieben wird, das Qubits bearbeitet und misst und die Auswirkungen von Superposition und Veranglement veranschaulicht.
- Die Quantum Katas sind selbstgesteuerte Lernprogramme und Programmierübungen, die darauf abzielen, die Elemente von Quantencomputing und Q# Programmierung gleichzeitig zu unterrichten.