Expression de programmes quantiques en Q# et OpenQASM
Avec la popularité croissante de l'informatique quantique au cours des dernières années, les cadres à source ouverte pour le développement de programmes quantiques arrivent à maturité et forment des écosystèmes substantiels autour d'eux. Le Quantum Development Kit (QDK) de Microsoft et Qiskit d'IBM sont deux cadres open-source majeurs. Ils présentent des approches et des outils différents pour développer des algorithmes quantiques et les exécuter sur des simulateurs ou du matériel quantique. Toutefois, étant donné que leur objectif et leur public se recoupent, les deux frameworks présentent de nombreuses similitudes. Ce billet de blog se concentre sur les principaux langages de programmation quantique des deux frameworks : Q# (QDK) et OpenQASM (Qiskit).
Le langage Q# fait partie de la famille .NET (C# et F#) et est un langage spécifique pour la programmation sur les ordinateurs quantiques. Il s'agit d'un langage fortement typé qui inclut des types, des déclarations et des opérations quantiques particuliers, tels que Qubit
et Mesures
ou en spécifiant si une opération peut être inversée ou contrôlée par d'autres qubits. Le langage OpenQASM est souvent décrit comme le langage d'assemblage quantique ouvert (comparable au Verilog classique) et est le principal format exportable du code Qiskit en Python. Généralement, les utilisateurs du cadre Qiskit n'écrivent pas eux-mêmes le code OpenQASM (bien que cela soit possible) mais utilisent les paquets Python qui mettent en œuvre une partie de la logique d'OpenQASM. OpenQASM 2.0 a été annoncé en 2017, et depuis, il a été adopté et développé. La préversion d'OpenQASM 3.0 est déjà disponible et est en cours d'intégration dans le framework Qiskit.
Dans ce billet, nous soulignons certains aspects de l'expression des programmes quantiques et présentons comment ils sont implémentés dans Q# par rapport aux programmes OpenQASM. En effet, le pouvoir d'expression d'un langage est limité par sa grammaire et ses règles additionnelles. Les spécifications grammaticales d'OpenQASM 3.0 et de Q# sont toutes deux définies avec le format ANTLR v4, ce qui constitue la première similitude entre les deux langages. La principale différence réside dans le fait qu'OpenQASM est un langage de bas niveau destiné à servir de représentation intermédiaire, tandis que Q# est un langage de haut niveau pour le développement d'algorithmes quantiques. Sauf indication contraire, "OpenQASM" désigne ici OpenQASM 3.0, car de nombreux points abordés ci-dessous n'ont été ajoutés à OpenQASM que dans la version 3.0.
1. Portes de base
L'une des pierres angulaires de l'informatique quantique est la possibilité de construire chaque porte unitaire à partir d'un ensemble prédéfini de portes (un ensemble de portes universel). OpenQASM 2.0 définit un ensemble minimaliste de portes quantiques universelles composé de deux portes : la porte unitaire générique à un qubit U(θ, φ, λ)
et la porte X contrôlée par deux qubits CX
. Ce sont les seules portes prédéfinies dans OpenQASM 2.0. Toutes les autres portes du langage sont explicitement dérivées de ces deux portes. Par exemple, considérons l'extrait suivant d'une porte .qasm
dans lequel le fichier personnalisé (mais largement utilisé) ry
et échange
sont définies :
// Defining gates in OpenQASM
gate ry(theta) a {
U(theta, 0, 0) a;
}
échange de portes a, b {
CX a, b;
CX b, a;
CX a, b;
}
De nombreux programmes OpenQASM utilisent des portes standard provenant de bibliothèques d'extension communes. L'ajout de ces portes est dirigé par include "qelib1.inc"
dans le .qasm
par exemple.
En Q#, la bibliothèque standard comprend la bibliothèque "Intrinsic" (ouvrir Microsoft.Quantum.Intrinsic
) et "Canonical" (ouvrir Microsoft.Quantum.Canon
), qui définissent de nombreuses opérations quantiques de base et avancées. Certaines opérations de base sont les portes de Pauli standard (X
, Y
, Z
), Paulis conditionnel (CNOT
, CCNOT
, CY
, CZ
), les rotations (R
), tandis que les opérations plus avancées contiennent QFT
, Plus grand que
et ApplyCNOTChain
.
Dans OpenQASM, un langage de niveau inférieur, les bibliothèques d'extension n'incluent généralement pas les portes de haut niveau comme QFT. Ces portes et circuits sont implémentés par des wrappers Python de niveau supérieur qui exportent l'implémentation dans OpenQASM.
2. Modificateurs de porte
Supposons que vous ayez une opération/porte que vous voulez appliquer à certains qubits cibles conditionnés par les valeurs d'autres qubits. Cette porte composite est l'analogue quantique de l'énoncé classique "si". Un autre scénario courant consiste à effectuer l'inverse d'une porte quantique (l'"adjoint" U† d'une opération unitaire U). Cette technique est particulièrement utile dans les techniques de non calcul. Les deux exemples mentionnés créent une nouvelle porte à partir d'une porte donnée - les "modificateurs de porte". Heureusement, Q# et OpenQASM 3.0 permettent aux utilisateurs d'étendre élégamment les portes existantes pour en créer de nouvelles.
Q# définit le Adjoint
et Contrôlé
foncteursqui peut étendre les portes. Les développeurs ne peuvent appliquer les foncteurs qu'aux opérations effectuées avec la fonction Adj
et Ctl
respectivement. Si la signature d'une opération Q# comprend est Adj
- cela signifie que l'opération est adjuvante - on peut l'inverser avec la fonction Adjoint
foncteur. Il en va de même pour est Ctl
et Contrôlé
. Ces caractéristiques constituent un élément essentiel du système de typage Q#. Notez qu'une opération peut être à la fois adjointe et contrôlable (est Adj + Ctl
).
OpenQASM 3.0 introduit l'équivalent modificateurs de porte concept : inv @
, ctrl @
, negctrl @
et pow @
. Chaque modificateur peut être appliqué à une porte, et il est même possible d'utiliser plusieurs modificateurs dans la même déclaration.
Quelques exemples sont donnés dans le tableau ci-dessous :
Modificateur | OpenQASM3 | Q# |
---|---|---|
Inverse | inv @ op q[0], q[1] |
Adjoint op(q[0], q[1]) |
Portail contrôlé | ctrl(2) @ op cont[0], cont[1], q[0], q[1] |
Op contrôlé([cont[0], cont[1]], q) |
Contrôle négatif | negctrl(2) @ op cont[0], cont[1], q[0], q[1] |
(*) |
Puissance | pow(k) @ op q[0], q[1] |
OperationPow(op, k)(q[0], q[1]) (**) |
op
est une porte OpenQASM ou une opération Q# (op : 'T => L'unité est Adj + Ctl
). L'entrée de op
est de deux qubits.
Par exemple, la déclaration inv @ op q[0], q[1]
applique le inverse porte de op
(inv @ op
) aux qubits q[0]
et q[1]
.
* Une implémentation possible du contrôle négatif est (extrait d'un document de travail) .qs
) :
// Negatively controlled operation in Q#
utiliser q = Qubit[2] ;
utiliser t = Qubit();
within {
ApplyToEachA(X, q);
}
apply {
Controlled op(q, t);
}
L'expression dans le à l'intérieur
est appliqué avant le bloc appliquer
et son bloc Adjoint
est appliquée par la suite.
Notez qu'il existe des options plus concises pour exprimer le même programme, comme l'utilisation de l'option ApplyControlledOnBitString
opération.
** En Q#, le OpérationPow
ne prend en charge que les puissances entières.
3. Programmes paramétrés
L'opération principale d'un projet Q# est marquée par l'icône @EntryPoint()
avant sa signature. Considérons le programme Q# paramétré suivant (.qs
) :
// An example for a Q# program that accepts parameters
namespace Quantum.Parametrized {
ouvert Microsoft.Quantum.Canon ;
ouvert Microsoft.Quantum.Intrinsic ;
ouvert Microsoft.Quantum.Measurement ; // for MultiM
@EntryPoint()
opération QuantumProg(numQubits : Int, angles : Double[]) : Result[] {
use qubits = Qubit[numQubits];
// Implement the actual logic
let res = MultiM(qubits) ;
retour res ;
}
}
Le programme quantique peut être appelé à partir d'un programme hôte classique Python ou C#. Le programme hôte peut envoyer les paramètres d'entrée et obtenir les sorties. Le programme hôte peut optimiser les paramètres et modifier le programme quantique via son interface, ce qui permet un schéma spécifique de calcul hybride quantique-classique.
OpenQASM 3.0 définit également une interface E/S pour les paramètres via la fonction entrée
et sortie
modificateurs :
// An OpenQASM 3.0 program with input and output
OPENQASM 3;
entrée int num_repetitions ;
sortie mors résultat ;
qubit q ;
// Implement the logic here
résultat = mesure q ;
Le code OpenQASM peut être intégré à Python, ce qui est utile pour les algorithmes variationnels.
4. Logique classique
OpenQASM3 et Q# prennent en charge les déclarations nécessaires au calcul classique. pour
et alors que
Les boucles sont prises en charge dans les deux cas, de même que le typage et les fonctions classiques, et permettent la composition de programmes quantiques sophistiqués. Sans logique classique à l'intérieur du code quantique, le programme quantique est statique et décrit comme un circuit quantique. Au contraire, les programmes quantiques dynamiques modifient l'exécution sur le processeur quantique en temps réel. Le contrôle classique ouvre la voie à la mise en œuvre de programmes quantiques-classiques concurrents.
5. Gestion de la mémoire quantique
L'une des fonctionnalités les plus puissantes du langage Q# est la suivante l'allocation de qubits auxiliaires à l'intérieur d'une opération et d'autres instructions de bloc. Il est possible d'allouer explicitement un qubit propre (initialisé à 0) via la commande utiliser
déclaration :
// Allocate a clean qubit in Q#
utiliser qubit = Qubit() ;
Ou un qubit "sale" par l'intermédiaire du emprunter
déclaration :
// Allocate a dirty qubit in Q#
borrow qubit = Qubit();
Dans tous les cas, le développeur Q# doit libérer les qubits alloués tels qu'ils ont été donnés. En d'autres termes, les qubits propres doivent être dans l'état 0 à la fin de leur champ d'application (par une mesure ou une réinitialisation, par exemple), et les qubits sales doivent être dans le même état que lorsqu'ils ont été empruntés.
Dans OpenQASM (2.0 et 3.0), les qubits sont déclarée au niveau mondial. Tous les qubits doivent être alloués dans le programme principal d'OpenQASM plutôt que dans les portes.
Un changement important entre les versions d'OpenQASM est l'état dans lequel les qubits sont initialisés. Dans OpenQASM 2.0, tous les qubits d'un registre quantique sont initialisés à l'état 0. Dans OpenQASM 3.0, les qubits alloués sont initialisés à un état non défini. Il convient d'utiliser l'option réinitialiser
pour garantir l'état 0 des qubits fraîchement alloués dans OpenQASM 3.0.
// Allocate and initialize qubits to 0 in OpenQASM 3.0
qubit[3] q;
reset q;
Notez qu'il existe des cas d'utilisation de qubits "sales", de sorte que le qubit "sale" ne soit pas utilisé. réinitialiser
n'est pas un code complètement banal dans OpenQASM 3.0.
En se concentrant sur les qubits propres, l'exigence de réinitialisation dans Q# et OpenQASM3 est vraiment très similaire, bien que distincte et assez subtile. Les qubits sont réinitialisés à la fin de leur portée (Q#) ou au début (OpenQASM3). Il peut sembler que le choix de l'endroit où la réinitialisation a lieu soit une particularité arbitraire de chaque langage. Cependant, il est essentiel de se rappeler qu'en Q#, le même qubit peut être réutilisé (deux fois ou plus) tout au long du programme avec un nom différent. Par conséquent, si les qubits sont réinitialisés dans Q# juste après leur allocation, la réinitialisation pourrait affecter inconsciemment l'état des autres qubits. Cela découle du concept d'intrication, l'une des profondes subtilités de l'informatique quantique. Il est donc obligatoire de réinitialiser les qubits Q# avant leur désallocation automatique à la fin de leur portée.
Une caractéristique distinctive d'OpenQASM 3.0 est l'accès aux qubits physiques à l'aide de $i
où $i
est le i + 1
Le qubit physique du 0
à n - 1
où n
est le nombre de qubits physiques).
Résumé
Dans ce billet, nous nous sommes concentrés sur les similitudes entre les langages de programmation Q# et OpenQASM, et nous avons laissé de côté le contrôle du matériel de niveau inférieur et certains concepts plus avancés. Il est également essentiel de se rappeler que la plupart des idées présentées ici ne sont entrées dans OpenQASM que dans la version 3.0, qui est encore en cours d'adoption, mais dont l'avenir semble prometteur. Nous espérons que cet article vous aidera dans votre parcours de développement quantique.
Références :
- OpenQASM 2 : https://arxiv.org/abs/1707.03429
- OpenQASM 3 : https://arxiv.org/abs/2104.14722
- Q# : https://arxiv.org/abs/1803.00652
- Spécification OpenQASM 3 : https://qiskit.github.io/openqasm/
- Référence API Q# : https://docs.microsoft.com/en-us/qsharp/api/qsharp/
Ce billet a été rédigé dans le cadre du calendrier de l'Avent Q# 2021.