Struktur eines Q# Programms

In diesem Artikel werden die allgemeinen Komponenten untersucht, aus denen ein Q# Programm besteht. Beachten Sie, dass Q# in Jupyter Notebooks geschriebene Programme einige dieser Komponenten nicht verwenden. Diese Unterschiede werden in jedem Abschnitt beschrieben.

Sehen Sie sich das folgende Q#-Programm an:

namespace Superposition {

    @EntryPoint()
    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);      
        // Now we measure the qubit in Z-basis.
        let result = M(q);
        // We reset the qubit before releasing it.
        Reset(q);
        // Finally, we return the result of the measurement.
        return result;
    }
}

Durch das Lesen der Kommentare (//) können Sie feststellen, dass dieses Programm ein Qubit zuordnet, einen Vorgang anwendet, um es in eine Überlagerung zu versetzen, den Zustand des Qubits misst, es dann zurücksetzt und das Ergebnis zurückgibt.

Informationen zum Ausführen dieses Programms in Visual Studio Code finden Sie unter Erste Schritte mit Q# Programmen und VS Code.

Benutzernamespaces

Q# Programme beginnen in der Regel mit einem benutzernamenten Namespace, z. B.

namespace Superposition {
    // Your code goes here.
}

Namespaces helfen bei der Strukturierung zusammengehöriger Funktionen. Namespaces sind Benutzernamen, und pro qsharp (*.qs)-Datei kann es nur einen namespace geben.

Die Q# Standardbibliothek verfügt über vordefinierte Namespaces, die Funktionen und Vorgänge enthalten, die Sie in Quantenprogrammen verwenden können. Weitere Informationen finden Sie unter Integrierte Namespaces.

Jupyter Notebooks verwenden keine Benutzernamespaces.

EntryPoint()

Das @EntryPoint() -Attribut teilt dem Compiler mit Q# , wo mit der Ausführung des Programms begonnen werden soll. In Programmen mit mehreren Funktions- und Vorgangsdefinitionen kann die @EntryPoint() platziert werden, bevor eine der Funktionen oder Vorgänge und der Programmfluss von dort aus beginnt und sequenziell fortgesetzt wird.

    ...
    @EntryPoint()
    operation MeasureOneQubit() : Result {
        ...

Jupyter Notebooks verwenden keine Einstiegspunkte.

Der %%qsharp-Befehl

Standardmäßig Q# verwenden Programme in Jupyter Notebooks den Python-Kernel ipykernel . Um einer Notebookzelle Code hinzuzufügen Q# , müssen Sie den Befehl verwenden, der %%qsharp mit dem qsharp Python-Paket aktiviert ist. Der vorherige Beispielcode in einer Jupyter Notebook sieht beispielsweise wie folgt aus:

import qsharp
%%qsharp

    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);      
        // Now we measure the qubit in Z-basis.
        let result = M(q);
        // We reset the qubit before releasing it.
        Reset(q);
        // Display the result
        Message($"Result is {result}");
        // Finally, we return the result of the measurement.
        return result;
    
    }
    MeasureOneQubit();

Beachten Sie das Fehlen eines Benutzernamespaces oder eines @EntryPoint(), die für Jupyter Notebooks nicht benötigt werden. Anstelle eines Einstiegspunkts wird der Vorgang direkt in der letzten Zeile aufgerufen. Beachten Sie auch, dass dem Jupyter Notebook Code eine Message Anweisung hinzugefügt wurde, um das Ergebnis anzuzeigen. Wenn Sie das frühere Q# Programm in VS Code ausführen, zeigt der integrierte Simulator standardmäßig das Ergebnis an.

Wenn Sie den %%qsharp Befehl verwenden:

  • Sie müssen zuerst import qsharp ausführen, um den %%qsharp-Befehl zu aktivieren.
  • Der %%qsharp Befehl ist auf die gesamte Zelle ausgerichtet, in der er angezeigt wird.
  • Der Q#-Code, der dem Befehl folgt, muss die Standardcodierungssyntax Q# einhalten. Beispielsweise bezeichnen Sie Kommentare mit anstelle // von innerhalb %%qsharp von # Zellen, und Codezeilen müssen mit einem Semikolon ;enden.
  • Der Befehl %%qsharp darf sich nicht vor oder nach einer Python-Anweisung in seiner Zelle befinden.

Ein Beispiel für die Arbeit mit einem Jupyter Notebook-Programm finden Sie unter Erste Schritte mit Q# Programmen und VS Code.

Typen

Q# stellt viele integrierte Typen bereit, die für die meisten Sprachen üblich sind, einschließlich Int, Double, Boolund String, sowie Typen, die für Quantencomputing spezifisch sind. Der Typ stellt beispielsweise Result das Ergebnis einer beliebigen Qubitmessung dar und kann einen von zwei möglichen definierten Werten aufweisen: One und Zero. Im Beispielprogramm erwartet der Vorgang MeasureOneQubit() einen Rückgabetyp von Result , und der M Vorgang misst das Qubit und gibt den Resultzurück.

...
// operation definition expecting a return type of Result
operation MeasureOneQubit() : Result {
    ...
    // Now we measure the qubit in Z-basis, returning a Result type
    let result = M(q);
    ...
}

Q# bietet auch Typen, die Bereiche, Arrays und Tupel definieren. Sie können sogar eigene benutzerdefinierte Typen definieren.

Zuordnen von Qubits

In Q# werden Qubits mithilfe des Schlüsselworts use zugeordnet.

In unserem Beispiel wird ein einzelnes Qubit definiert:

// Allocate a qubit.
use q = Qubit();
...

Sie können aber auch mehrere Qubits zuordnen und über den zugehörigen Index auf jedes zugreifen:

...
use qubits = Qubit[2];
X(qubits[1]);
H(qubits[0]);
...

Standardmäßig beginnt jedes Qubit, das Sie mit dem Schlüsselwort use zuordnen, im Zustand null. Jedes Qubit muss zurück auf den Nullzustand zurückgesetzt werden, bevor es am Ende des Programms veröffentlicht wird. Wenn ein Qubit nicht zurückgesetzt wird, wird ein Laufzeitfehler ausgelöst.

// Reset a qubit.
Reset(q);
...

Quantenvorgänge

Nach der Zuordnung kann ein Qubit an Operationen und Funktionen übergeben werden, die auch als aufrufbare Aufrufe bezeichnet werden. Vorgänge sind die Grundbausteine eines Q#-Programms. Ein Vorgang Q# ist eine Quantenunterroutine. Das heißt, es handelt sich um eine aufrufbare Routine, die Quantenvorgänge enthält, mit denen der Zustand des Qubit-Registers geändert wird.

Um einen Q#-Vorgang zu definieren, geben Sie einen Namen für den Vorgang und dessen Ein- und Ausgabe an. In unserem Beispiel ist der einzelne Vorgang im Wesentlichen das gesamte Programm. Es benötigt keine Parameter und erwartet einen Rückgabetyp von Result:

operation MeasureOneQubit() : Result {
    ...
}

Hier sehen Sie ein einfaches Beispiel, das keine Parameter akzeptiert und keinen Rückgabewert erwartet. Der Unit Wert entspricht NULL in anderen Sprachen.

operation SayHelloQ() : Unit {
    Message("Hello quantum world!");
}

Die Q# Standardbibliothek bietet auch Vorgänge, die Sie in Ihren Programmen verwenden können, z. B. hadamard oder der H Vorgang, der im Beispielprogramm verwendet wird. Bei einem Qubit auf Z-Basis versetzt der H-Vorgang das Qubit in eine ausgeglichene Superposition. Wenn sich das Qubit in Superposition befindet, ist die Wahrscheinlichkeit jeweils 50 Prozent, dass eine Messung null oder eins ergibt.

Messen von Qubits

Es gibt viele Arten von Quantenmessungen, konzentriert sich jedoch Q# auf projektive Messungen an einzelnen Qubits, auch bekannt als Pauli-Messungen. Bei der Messung in einer bestimmten Basis (etwa in der Berechnungsbasis $\ket{0},\ket{1}$) wird der Qubit-Zustand auf den gemessenen Basiszustand projiziert und damit die Superposition zwischen den beiden zerstört.

Unser Beispielprogramm verwendet den M Vorgang, der eine Messung eines einzelnen Qubits auf der Pauli Z-Basis durchführt und einen Result Typ zurückgibt.

Integrierte Namespaces

Die Standardbibliothek Q# verwendet integrierte Namespaces, die Funktionen und Vorgänge enthalten, die Sie in Quantenprogrammen verwenden können. Beispielsweise enthält der Namespace Microsoft.Quantum.Intrinsic häufig verwendete Vorgänge und Funktionen wie M, um Ergebnisse zu messen, und Message, um Benutzernachrichten an einer beliebigen Stelle im Programm anzuzeigen.

Sie können eine Funktion oder einen Vorgang aufrufen, indem Sie den vollständigen Namespace angeben, oder eine open Anweisung verwenden, um alle Funktionen und Vorgänge für diesen Namespace verfügbar zu machen und den Code leichter lesbar zu machen. In diesen beiden Beispielen wird derselbe Vorgang aufgerufen:

 Microsoft.Quantum.Intrinsic.Message("Hello quantum world!");
open Microsoft.Quantum.Intrinsic;
Message("Hello quantum world!");

Beachten Sie, dass im Beispielprogramm keine open Anweisungen oder Aufrufe mit vollständigen Namespaces vorhanden sind. Das liegt daran, dass die Q# Entwicklungsumgebung standardmäßig automatisch zwei Namespaces lädt – Microsoft.Quantum.Core und Microsoft.Quantum.Intrinsic – die häufig verwendete Funktionen und Vorgänge enthalten.

Sie können den Microsoft.Quantum.Measurement Namespace nutzen und den MResetZ Vorgang verwenden, um den Code im Beispielprogramm zu optimieren. MResetZ kombiniert die Mess- und Zurücksetzungsvorgänge in einem Schritt, wie im folgenden Beispiel gezeigt:

namespace Superposition {

    // open the namespace for the MResetZ operation
    open Microsoft.Quantum.Measurement;

    @EntryPoint()
    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);   
        // Measure and reset the qubit, and return the result value   
        return MResetZ(q);
    }
    
}