Ausdruck von Quantenprogrammen in Q# und OpenQASM
Mit der zunehmenden Popularität des Quantencomputings in den letzten Jahren reifen Open-Source-Frameworks für die Entwicklung von Quantenprogrammen heran und bilden umfangreiche Ökosysteme um sie herum. Das Quantum Development Kit (QDK) von Microsoft und Qiskit von IBM sind zwei wichtige Open-Source-Frameworks. Sie bieten unterschiedliche Ansätze und Werkzeuge für die Entwicklung von Quantenalgorithmen und deren Ausführung auf Simulatoren oder Quantenhardware. Da sich ihre Ziele und Zielgruppen jedoch überschneiden, haben die beiden Frameworks viele Gemeinsamkeiten. Dieser Blogbeitrag konzentriert sich auf die wichtigsten Quantenprogrammiersprachen der beiden Frameworks: Q# (QDK) und OpenQASM (Qiskit).
Die Sprache Q# ist Teil der .NET-Familie (C# und F#) und ist eine domänenspezifische Sprache für die Programmierung auf Quantencomputern. Es handelt sich um eine stark typisierte Sprache, die bestimmte Quantentypen, Anweisungen und Operationen enthält, wie z. B. Qubit
und Messung
oder die Angabe, ob eine Operation invertiert oder von anderen Qubits kontrolliert werden kann. Die OpenQASM-Sprache wird oft als offene Quanten-Assemblersprache beschrieben (vergleichbar mit dem klassischen Verilog) und ist das primäre exportierbare Format von Qiskits Code in Python. Normalerweise schreiben die Benutzer des Qiskit-Frameworks nicht selbst OpenQASM-Code (obwohl dies möglich ist), sondern verwenden die Python-Pakete, die einen Teil der Logik in OpenQASM implementieren. OpenQASM 2.0 wurde 2017 angekündigt, und seitdem wurde es übernommen und weiterentwickelt. Die Vorabversion von OpenQASM 3.0 ist bereits verfügbar und wird in das Qiskit-Framework integriert.
In diesem Beitrag beleuchten wir einige Aspekte des Ausdrucks von Quantenprogrammen und zeigen, wie sie in Q# im Vergleich zu OpenQASM-Programmen implementiert sind. Die volle Ausdruckskraft einer Sprache wird durch ihre Grammatik und zusätzliche Regeln begrenzt. Die Grammatikspezifikationen von OpenQASM 3.0 und Q# sind beide im ANTLR v4 Format definiert, was die erste Ähnlichkeit zwischen den beiden Sprachen darstellt. Ein zentraler Unterschied ist, dass OpenQASM eine Low-Level-Sprache ist, die als Zwischendarstellung gedacht ist, während Q# eine High-Level-Sprache zur Entwicklung von Quantenalgorithmen ist. Wenn nicht anders angegeben, ist mit "OpenQASM" hier OpenQASM 3.0 gemeint, da viele der unten beschriebenen Punkte erst in Version 3.0 zu OpenQASM hinzugefügt wurden.
1. Basis Gates
Ein Eckpfeiler der Quanteninformatik ist die Möglichkeit, jedes unitäre Gatter aus einer vordefinierten Menge von Gattern (einem universellen Gatterset) zu bauen. OpenQASM 2.0 definiert einen minimalistischen universellen Quantengattersatz aus zwei Gattern: das generische unitäre Ein-Qubit-Gatter U(θ, φ, λ)
und das Zwei-Qubit-gesteuerte X-Gatter CX
. Dies sind die einzigen vordefinierten Gatter in OpenQASM 2.0. Jedes andere Gatter in der Sprache wird explizit von diesen beiden abgeleitet. Betrachten Sie zum Beispiel den folgenden Ausschnitt aus einem .qasm
Datei, in der die benutzerdefinierte (aber weit verbreitete) ry
und tauschen.
Gates definiert sind:
// Defining gates in OpenQASM
Tor ry(theta) a {
U(theta, 0, 0) a;
}
Gattertausch a, b {
CX a, b;
CX b, a;
CX a, b;
}
Viele OpenQASM-Programme verwenden Standardgatter aus gängigen Erweiterungsbibliotheken. Das Hinzufügen dieser Gatter wird gesteuert durch include "qelib1.inc"
im .qasm
Datei, zum Beispiel.
In Q# enthält die Standardbibliothek das "Intrinsic" (Microsoft.Quantum.Intrinsic öffnen
) und "Kanonisch" (Microsoft.Quantum.Canon öffnen
), die viele grundlegende und fortgeschrittene Quantenoperationen definieren. Einige grundlegende Operationen sind die Standard-Pauli-Gatter (X
, Y
, Z
), bedingte Paulis (CNOT
, CCNOT
, CY
, CZ
), Drehungen (R
), während die fortgeschritteneren Operationen Folgendes enthalten QFT
, GrößerAls
und ApplyCNOTChain
.
In OpenQASM, einer Sprache mit niedrigerem Niveau, enthalten die Erweiterungsbibliotheken in der Regel keine High-Level-Gatter wie QFT. Diese Gatter und Schaltungen werden von Python-Wrappern auf höherer Ebene implementiert, die die Implementierung in OpenQASM exportieren.
2. Gate-Modifikatoren
Angenommen, Sie haben eine Operation/ein Gatter, das Sie auf einige Ziel-Qubits anwenden wollen, die von den Werten anderer Qubits abhängig sind. Dieses zusammengesetzte Gatter ist das Quanten-Analogon der klassischen "Wenn"-Anweisung. Ein weiteres weit verbreitetes Szenario ist die Umkehrung eines Quantengatters (der "Adjungierte" U† einer unitären Operation U). Dies ist insbesondere bei Unberechnungsmethoden nützlich. Die beiden genannten Beispiele erzeugen ein neues Tor aus einem gegebenen Tor - "Tor-Modifikatoren". Glücklicherweise erlauben sowohl Q# als auch OpenQASM 3.0 den Benutzern, bestehende Gatter auf elegante Weise zu neuen Gattern zu erweitern.
Q# definiert die Adjoint
und Kontrolliert
Funktoren, die Gatter erweitern können. Die Entwickler können die Funktoren nur auf Operationen mit der Adj
und Ctl
bzw. Merkmale. Wenn die Signatur einer Q#-Operation Folgendes enthält ist Adj
- bedeutet dies, dass die Operation anschlussfähig ist - man kann sie mit der Adjoint
Funktor. Das Gleiche gilt für ist Ctl
und Kontrolliert
. Diese Eigenschaften sind ein wesentlicher Bestandteil des Q#-Typisierungssystems. Beachten Sie, dass eine Operation sowohl anschlussfähig als auch kontrollierbar sein kann (ist Adj + Ctl
).
OpenQASM 3.0 führt das Äquivalent Gate-Modifikatoren Konzept: inv @
, ctrl @
, negctrl @
und pow @
. Jeder Modifikator kann auf ein Tor angewendet werden, und man kann sogar mehrere Modifikatoren in derselben Anweisung verwenden.
Einige Beispiele sind in der folgenden Tabelle aufgeführt:
Modifikator | OpenQASM3 | Q# |
---|---|---|
Umgekehrt | inv @ op q[0], q[1] |
Adjunkte op(q[0], q[1]) |
Kontrolliertes Tor | ctrl(2) @ op cont[0], cont[1], q[0], q[1] |
Gesteuertes op([cont[0], cont[1]], q) |
Negative Kontrolle | negctrl(2) @ op cont[0], cont[1], q[0], q[1] |
(*) |
Strom | pow(k) @ op q[0], q[1] |
OperationPow(op, k)(q[0], q[1]) (**) |
op
ist ein OpenQASM-Gatter oder eine Q#-Operation (op : 'T => Einheit ist Adj + Ctl
). Die Eingabe von op
ist zwei Qubits.
Zum Beispiel, die Aussage inv @ op q[0], q[1]
wendet die Invers Tor von op
(inv @ op
) zu den Qubits q[0]
und q[1]
.
* Eine mögliche Implikation der negativen Kontrolle ist (Auszug aus einem .qs
Datei):
// Negatively controlled operation in Q#
verwenden. q = Qubit[2];
verwenden. t = Qubit();
within {
ApplyToEachA(X, q);
}
apply {
Controlled op(q, t);
}
Der Ausdruck in der innerhalb
Block wird vor der Anwendung der anwenden.
Block, und sein Adjoint
wird danach angewendet.
Beachten Sie, dass es prägnantere Möglichkeiten gibt, das gleiche Programm auszudrücken, wie z.B. die Verwendung der ApplyControlledOnBitString
Betrieb.
** In Q# wird die OperationPow
Funktion unterstützt nur ganzzahlige Potenzen.
3. Parametrisierte Programme
Die Hauptoperation eines Q#-Projekts wird durch das @Eintrittspunkt()
Attribut vor seiner Signatur. Betrachten wir das folgende parametrisierte Q#-Programm (.qs
Datei):
// An example for a Q# program that accepts parameters
Namespace Quantum.Parametrized {
öffnen Microsoft.Quantum.Canon;
öffnen Microsoft.Quantum.Intrinsic;
öffnen Microsoft.Quantum.Measurement; // for MultiM
@Eintrittspunkt()
Operation QuantumProg(numQubits : Int, Winkel : Doppelter[]) : Result[] {
use qubits = Qubit[numQubits];
// Implement the actual logic
let res = MultiM(qubits);
return res;
}
}
Das Quantenprogramm kann von einem klassischen Python- oder C#-Hostprogramm aufgerufen werden. Das Host-Programm kann die Eingabeparameter senden und die Ausgaben erhalten. Das Host-Programm kann die Parameter optimieren und das Quantenprogramm über seine Schnittstelle ändern, was ein spezielles Schema für hybride quantenklassische Berechnungen ermöglicht.
Auch OpenQASM 3.0 definiert eine I/O-Schnittstelle für Parameter über die Eingabe
und Ausgabe
Modifikatoren:
// An OpenQASM 3.0 program with input and output
OPENQASM 3;
Eingabe int num_repetitions;
Ausgabe bit Ergebnis;
Q-Bit q;
// Implement the logic here
Ergebnis = Maßnahme q;
Der OpenQASM-Code kann in Python verpackt werden, was für Variationsalgorithmen nützlich ist.
4. Klassische Logik
OpenQASM3 und Q# unterstützen notwendige Anweisungen für klassische Berechnungen. für
und während
Schleifen werden in beiden unterstützt, zusammen mit der korrekten klassischen Typisierung und Funktionen, und ermöglichen die Komposition von anspruchsvollen Quantenprogrammen. Ohne klassische Logik innerhalb des Quantencodes ist das Quantenprogramm statisch und wird als Quantenschaltung beschrieben. Im Gegensatz dazu ändern dynamische Quantenprogramme die Ausführung auf dem Quantenprozessor in Echtzeit. Die klassische Steuerung öffnet den Weg zur Implementierung nebenläufiger quantenklassischer Programme.
5. Verwaltung des Quantenspeichers
Eine der leistungsfähigsten Funktionen der Q#-Sprache ist Zuweisung von Hilfsqubits innerhalb einer Operation und anderer Blockanweisungen. Man kann explizit ein sauberes Qubit (initialisiert auf 0) über die verwenden.
Erklärung:
// Allocate a clean qubit in Q#
verwenden. Qubit = Qubit();
Oder ein "schmutziges" Qubit über die ausleihen
Erklärung:
// Allocate a dirty qubit in Q#
borrow qubit = Qubit();
In jedem Fall muss der Q#-Entwickler die zugewiesenen Qubits so freigeben, wie sie übergeben wurden. Das heißt, saubere Qubits müssen am Ende ihres Geltungsbereichs (z. B. durch eine Messung oder einen Reset) den Zustand 0 haben, und schmutzige Qubits müssen denselben Zustand haben wie bei der Ausleihe.
In OpenQASM (2.0 und 3.0) sind Qubits weltweit deklariert. Alle Qubits müssen im OpenQASM-Hauptprogramm und nicht in Gattern zugewiesen werden.
Eine entscheidende Änderung zwischen den Versionen von OpenQASM ist der Zustand, in den die Qubits initialisiert werden. In OpenQASM 2.0 sind alle Qubits eines Quantenregisters auf den Zustand 0 initialisiert. In OpenQASM 3.0 werden zugewiesene Qubits auf einen undefinierten Zustand initialisiert. Man sollte die zurücksetzen
Anweisung, um einen 0-Zustand von frisch zugewiesenen Qubits in OpenQASM 3.0 zu gewährleisten.
// Allocate and initialize qubits to 0 in OpenQASM 3.0
qubit[3] q;
reset q;
Beachten Sie, dass es einige Anwendungsfälle für "schmutzige" Qubits gibt, so dass die zurücksetzen
ist in OpenQASM 3.0 kein vollständiger Boilerplate-Code.
Wenn man sich auf saubere Q-Bits konzentriert, ist die Rücksetzanforderung in Q# und OpenQASM3 wirklich sehr ähnlich, wenn auch unterschiedlich und ziemlich subtil. Die Qubits werden am Ende ihres Bereichs (Q#) oder am Anfang (OpenQASM3) zurückgesetzt. Es mag den Anschein erwecken, dass die Wahl des Rücksetzungszeitpunkts eine willkürliche Eigenheit der jeweiligen Sprache ist. Es ist jedoch wichtig, sich daran zu erinnern, dass in Q# ein und dasselbe Qubit im gesamten Programm unter einem anderen Namen wiederverwendet werden kann (zweimal oder öfter). Wenn also die Q-Bits in Q# kurz nach ihrer Zuweisung zurückgesetzt werden, könnte das Zurücksetzen den Zustand der anderen Q-Bits unbemerkt beeinflussen. Dies ergibt sich aus dem Konzept der Verschränkung, einer der tiefgreifenden Feinheiten der Quanteninformatik. Daher ist es zwingend erforderlich, Q#-Qubits zurückzusetzen, bevor sie am Ende ihres Anwendungsbereichs automatisch freigegeben werden.
Eine Besonderheit von OpenQASM 3.0 ist der Zugriff auf die physikalischen Qubits mit $i
, wobei $i
ist die i + 1
Das physikalische Qubit (von 0
zu n - 1
, wobei n
ist die Anzahl der physikalischen Qubits).
Zusammenfassung
In diesem Beitrag haben wir uns auf die Gemeinsamkeiten zwischen den Programmiersprachen Q# und OpenQASM konzentriert und die Hardwaresteuerung auf unterer Ebene sowie einige fortgeschrittenere Konzepte außer Acht gelassen. Es ist auch wichtig, sich daran zu erinnern, dass die meisten der hier vorgestellten Ideen erst in Version 3.0 in OpenQASM eingeflossen sind, das sich noch in der Entwicklung befindet, aber die Zukunft sieht rosig aus. Wir hoffen, dass dieser Beitrag Sie auf Ihrer Reise zur Quantenentwicklung unterstützen wird.
Referenzen:
- OpenQASM 2: https://arxiv.org/abs/1707.03429
- OpenQASM 3: https://arxiv.org/abs/2104.14722
- Q#: https://arxiv.org/abs/1803.00652
- OpenQASM 3-Spezifikation: https://qiskit.github.io/openqasm/
- Q# API-Referenz: https://docs.microsoft.com/en-us/qsharp/api/qsharp/
Dieser Beitrag wurde als Teil des Q#-Adventskalenders 2021 geschrieben.