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 Folgendes:

  • 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: Diagramm eines Quantum Fourier Transform Circuit.

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

Erstellen einer neuen Q# Datei

  1. Wählen Sie in VS Code die Option "Neue > Textdatei speichern" aus.
  2. Speichern Sie die Datei als QFTcircuit.qs. Diese Datei enthält den Q# Code für Ihr Programm.
  3. Ö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 Tutorials werden Messfunktionen hinzugefügt und die Zustände vor und nach der Messung der Qubits miteinander verglichen.

Der Vorgang wird Schritt für Schritt aufgebaut. 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 wird der Vorgang geändert, um ein Array von Messergebnissen zurückzugeben.

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();

Genau wie bei echten Quantenberechnungen kann mit Q# nicht direkt auf Qubitzustände zugegriffen werden. DumpMachine Der Vorgang druckt jedoch den aktuellen Zustand des target Computers, sodass er wertvolle Einblicke für das Debuggen und Lernen liefern kann, wenn er in Verbindung 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:

Diagramm mit einem Schaltkreis für drei Qubit-QFT bis zum ersten Hadamard.

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):

Diagramm mit einem Schaltkreis für drei Qubit Quantum Fourier Transform über den ersten Qubit.

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

Nach dem Anwenden der relevanten H Vorgänge und gesteuerten Drehungen auf die zweite und dritte Qubits 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. Dies ist notwendig, weil die Natur der Quanten-Fourier-Transformation die Qubits in umgekehrter Reihenfolge ausgibt, so dass die Vertauschungen eine nahtlose Integration der Subroutine in größere Algorithmen ermöglichen.

SWAP(qs[2], qs[0]);

Die Vorgänge auf Qubit-Ebene der Quantum-Fourier-Transformation in Ihrem Q#-Vorgang sind damit fertig geschrieben:

Diagramm mit einem Schaltkreis für drei Qubit Quantum Fourier Transform.

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. Darüber hinaus wird dadurch sichergestellt, dass sie nicht mit anderen Qubits im System verschränkt sind. Wenn das Zurücksetzen nicht am Ende eines use-Zuordnungsblocks durchgeführt wird, 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.

  1. Ü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 target Profil nicht auf "Uneingeschränkt" festgelegt ist, wird beim Ausführen des Programms eine Fehlermeldung angezeigt.
  2. Um Ihr Programm auszuführen, wählen Sie in der Dropdownliste "Wiedergabesymbol" die Option "Datei ausführenQ#" oben rechts aus, oder drücken Sie STRG+F5. Das Programm führt den Main() Vorgang im Standardsimulator aus.
  3. Die Message Ausgaben und DumpMachine Ausgaben werden in der Debugkonsole angezeigt.

Wenn Sie sich für die Auswirkungen auf andere Eingangszustände interessieren, können Sie gerne mit der Anwendung anderer Qubit-Vorgänge vor der Transformation experimentieren.

Hinzufügen von Maßen zum QFT-Schaltkreis

Die Anzeige der DumpMachine-Funktion enthielt zwar die Ergebnisse des Vorgangs, leider besagt jedoch ein Eckpunkt der Quantenmechanik, dass ein echtes 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 M-Vorgang, der einen Result-Typ zurückgibt, um Messungen innerhalb eines Q#-Programms zu implementieren.

Ändern Sie zunächst den Vorgang Main so, dass anstelle von Unit ein Array von Messergebnissen (Result[]) 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) {
    set 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 die Schleife for verwendet, um die einzelnen Qubits nacheinander mithilfe der Anweisung M(qs[i]) 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 hinzugefügt wurden, können die Qubits wie zuvor zurückgesetzt und freigegeben werden. 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) {
        set 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.

  1. Ü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 target Profil nicht auf "Uneingeschränkt" festgelegt ist, wird beim Ausführen des Programms eine Fehlermeldung angezeigt.
  2. Wenn Sie Ihr Programm ausführen möchten, wählen Sie in der Dropdownliste "Wiedergabesymbol" oben rechts die Option "Datei ausführenQ#" aus, oder drücken Sie STRG+5. Das Programm führt den Main() Vorgang im Standardsimulator aus.
  3. Die Message Ausgaben und DumpMachine 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:

  1. Vergleicht man das zurückgegebene Ergebnis mit der VormessungDumpMachine, so zeigt 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.
  2. 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 diesen Vorgang viele Male wiederholen, bildet die Ergebnisstatistik die gleichgewichtige Superposition des Post-QFT-Zustands ab, die bei jedem Shot ein zufälliges Ergebnis liefert. 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 sehen, dass relative Phasen auftreten, wenn die QFT eine komplexere Eingabe erhält als $\ket{000}$.

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.

  1. Ersetzen Sie alles von der ersten H Operation bis einschließlich durch SWAP :

    ApplyQFT(qs);
    
  2. 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) {
            set resultArray w/= i <- M(qs[i]);
        }
    
        Message("After measurement: ");
        DumpMachine();
    
        ResetAll(qs);
        Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
        return resultArray;
    
    }
    
  3. Führen Sie das Q# Programm erneut aus, und beachten Sie, dass die Ausgabe wie zuvor identisch ist.

  4. 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.

Sehen Sie sich weitere Q#-Tutorials an: