Les programmes d'exemples sur Azure Quantum Coding
Maintenant que nous avons les bases en ayant survolé la synthaxe de Q#, il est temps de programmer en Q#. En tant que novice en la matière, il est clair que je ne sais pas du tout par ou commencer, et que le prisme de la programmation quantique m'est tout à fait inconnu. Heureusement Azure Quantum Coding nous met à disposition des programmes pré-établit sur lesquels nous pouvons nous baser pour apprendre les rouages de la programmation quantiques. Sur cet article nous allons nous intéresser à deux algorithmes : Le générateur de nombre aléatoire et les états de Bell.
Commençons avec le générateur de nombre aléatoire que j'ai revisité à ma manière en m'inspirant du code disponible sur Azure Quantum Code et la documentation de microsoft.
I. Premier Algorithme : Générateur de nombre aléatoire

Avant toute chose, il faut savoir que les ordianateur classique ne sont pas capable de générer des nombres aléatoire à proprement parler. Il génèrent plutôt des nombres pseudo-aléatoire.
La différence réside dans le principe qu'on a vu dans les articles précédents : L'aspect deterministe des algorithmes classiques.
En effet, un algorithme déterministe qui génère des suites de nombres ne peuvent pas satisfaire complètement les critères mathématiques d'une suites aléatoire car ils sont déterminés par des règles précises et reproductibles.
Grâce aux ordinateurs quantiques, on peut générer des suites de nombres qui sont réelement aléatoires grâce au principe de superposition qui se base sur un système probabiliste. Voyons plus en détail ce principe car il s'agit là de la base de l'algorithme.
Rappelons nous qu'un qubit est une unité de soit la valeur 0 soit la v'information quantique. Le générateur quantique va reposer sur la succession de mesures des qubits qui peuvent prendre la valeur 1 , 0 ou les deux à la fois .
Le générateur va utiliser l'opération quantique Hadamard pour mettre un qubit dans un état ou le qubit sera la combinaison des deux états possibles. Ainsi avant la mesure de ce qubit, ce dernier aura une superposition des deux états possibles : 1 ou 0, et chacuns de ces états ont une amplitude de probabilité égale. C'est à dire qu'après la mesure du qubit, ce dernier se retrouve soit dans l'état "0" soit dans l'état "1" avec une probabilité de 50 % pour chaque résultat. Cela signifie que la mesure du qubit fournit un résultat aléatoire, car avant la mesure, le qubit était dans une superposition, ce qui implique une incertitude quant au résultat de la mesure.
C'est grâce à ce comportement répété plusieurs fois qu'on peut obtenir une suite de chiffres binaires totalement aléatoire. La concaténation de ces bits peut donner naissance à des nombres aléatoire de la taille qu'on souhaite.
La logique
Avant de passer à l'analyse du code, faisons un travail de logique et pensons à la mécanique de l'algorithme.
Si nous voulons déterminer un nombre aléatoirement, il faudrait d'abord déterminer la longueur en bit de ce dernier. En determinant le nombre de bit maximum (noté nbitsMax), on définit implicitement un range dans lequel le générateur travaillera.
Une fois le nombre de bit max définit, on peut générer une chaîne de bits aléatoire dont la longueur correspont à nbitsMax.
Enfin une fois la chaîne générée on retourne la chaîne sous forme d'entier.
Passons maintenant à l'analyse du code.
Le code
Générateur de bits
En suivant la logique qui a été rédigée ci-dessus, il nous faut d'abord générer une suite de bits pour ensuite les convertir en entier.
Jettons un oeil sur un générateur de bit simple GenerateRandomBit()
:
operation GenerateRandomBit() : Result {
// Allocate a qubit.
use q = Qubit();
// Set the qubit into superposition of 0 and 1 using the Hadamard
H(q);
// At this point the qubit `q` has 50% chance of being measured in the
// |0〉 state and 50% chance of being measured in the |1〉 state.
// Measure the qubit value using the `M` operation, and store the
// measurement value in the `result` variable.
let result = M(q);
// Reset qubit to the |0〉 state.
// Qubits must be in the |0〉 state by the time they are released.
Reset(q);
// Return the result of the measurement.
return result;
}
L'opération GenerateRandomBit()
retourne une valeur de type Result
qui correspond au résultat d'une mesure (1 ou 0).
Dans un premier temps on utilise la méthode Hadamard notée H
sur un qubit préalablement alloué. Le mot clé use
est utilisé pour alloué un qubit (vulgairement parlant, c'est comme si on instanciait un qubit). C'est grâce à l'opération :
H(q);
que le qubit est mit dans un état de superposition.
C'est ensuite dans la variable result
qu'on initialise grâce au mot clé let
, qu'on stock la mesure du qubit q
.
Après avoir fait la mesure, on réitinialise le qubit :
Reset(q);
puis on retourne le résultat.
Cette méthode ne retourne qu'un seul bit. Notre objectif est de générer un nombre aléatoirement qui peut prendre plusieurs bits.
Voici la version du générateur plus complète :
operation GenerateNRandomBits(nBits : Int) : Result[] {
// Allocate N qubits.
use register = Qubit[nBits];
for qubit in register {
H(qubit);
}
let results = MResetEachZ(register);
return results;
}
La grosse différence, ici, réside dans l'utilisation d'un array. La méthode prend en paramètre un nombre de bit qu'on veut pour génerer le nombre. Cette valeur sera la taille du tableau qu'on initialise au début dans lequel on alloue nBits Qubits.
use register = Qubit[nBits];
registrer est donc un tableau de qubit alloués et qui seront par la suite mis dans un état de superposition dans la boucle suivante :
for qubit in register {
H(qubit);
}
On peut ainsi en une ligne les mesurer et les réinitialiser automatiquement grâce à la méthode MResetEachZ()
qui prend le tableau de qubit en paramètre. Les résultats sont ensuite retournés
Maintenant qu'on a une méthode pour générer des bits, il faut à présent les convertir en entier !
Générer un entier
Pour convertir la chaine de bits que nous retourne la méthode c'est simple, on a juste besoin de la méthode suivante :
ResultArrayAsInt()
Ainsi, dans l'opération principale de mon code, il suffit de 3 lignes pour générer le nombre aléatoire :
@EntryPoint()
operation Main() : Int {
let nBits = 5;
let tableauBits = GenerateNRandomBits(nBits);
return ResultArrayAsInt(tableauBits);
}
On définit le nombre de bits qu'on veut dans la sortie du nombre genéré, puis on le met en paramètre en de la méthode expliquée précédemment pour générer une chaîne de bits retournée dans un array. Grâce à ResultArrayAsInt()
, on convertit la chaîne en Int.
return ResultArrayAsInt(tableauBits);
Voici le résultat quand on éxecute le code :

Le nombre genéré est dans la case Result . En cliquant successivement sur Run, j'ai eu les résultat suivant :
- 22
- 14
- 19
- 6
- 10
Passons à l'algorithme des états de Bell
II. Deuxième Algorithme : États de bell
Que sont les états de Bell ?
L'intrication quantique
Avant de rentrer dans le vif du sujet, il faut voir plus en détail ce qu'est le concept d'intrication quantique. Par définition,
l'intrication est un phénomème dans lequel deux particules forment un système lié et présentent des états quantiques dépendant l'un de l'autre quelle que soit la distance qui les sépare
Selon la vidéo de la chaîne youtube Antoine Vs Science
En se basant sur cette définition, si deux particules se croisent à un instant t, alors ces deux particules seront à jamais relié après cet instant. Ainsi en considérant une particule A étant intriquée avec une particule B. Si l'état de la particul A change, alors l'état de la particule B change aussi instantanément.
Ce phénomène est un des phénomène les plus fascinant puisqu'il a été utilisé pour transférer de l'information d'un système A vers un système B sans que ces deux systèmes soient liés physiquement. On parle ici de tléportation quantique.
Les états de Bell
Les états de Bell sont sont en informatique quantique les états d'intrication maximale de deux qbits. On trouve un total de quatres états de Bell. Les voicis :

- PhiPlus noté Φ+
- PhiMinus noté Φ-
- PsiPlus Ψ+
- PsiMinus Ψ-
Visualiser un Qubit
Avant de passer au code, il est important de prendre connaissance de l'outil de visualisation d'un qubit.
La sphère de Bloch
La sphère de Bloch est un outil graphique largement utilisé en informatique quantique pour représenter l'état d'un qubit. Grâce à la sphère de Bloch on peut non seulement représenter l'état d'un qubit de manière intuitive, mais aussi on peut y observer l'évolution de ces états en applicant des portes quantiques qui agissent comme des rotations sur le plan.
Dans la représentation de la sphère de Bloch, les états d'un qubit sont représentés par les points situés sur la surface de la sphère.

Si un qubit est mesuré et trouvé dans l'état ∣0⟩, son état final sur la sphère de Bloch sera le pôle nord. Si le qubit est mesuré et trouvé dans l'état ∣1⟩, son état final sur la sphère de Bloch sera le pôle sud.
Si un qubit est dans un état de superposition, cela signifie qu'il est dans une combinaison linéaire des états de base ∣0⟩ et ∣1⟩. Graphiquement, sur la sphère de Bloch, cela se traduit par un point qui se trouve quelque part entre les pôles nord et sud, c'est-à-dire sur l'équateur de la sphère.
Passons au code
Le but de l'algorithme est de créer et mesurer les quatre états de Bell différents sur deux qubits. Voici à quoi ressemble le code que je vais devoir décortiquer :
namespace Sample {
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Measurement;
@EntryPoint()
operation BellStates() : (Result, Result)[] {
use register = Qubit[2];
let bellStateTuples = [
("|Φ+〉", PreparePhiPlus),
("|Φ-〉", PreparePhiMinus),
("|Ψ+〉", PreparePsiPlus),
("|Ψ-〉", PreparePsiMinus)
];
mutable measurements = [];
for (label, prepare) in bellStateTuples {
prepare(register);
Message($"Bell state {label}:");
DumpMachine();
set measurements += [(MResetZ(register[0]), MResetZ(register[1]))];
}
return measurements;
}
operation PreparePhiPlus(register : Qubit[]) : Unit {
ResetAll(register); // |00〉
H(register[0]); // |+0〉
CNOT(register[0], register[1]); // 1/sqrt(2)(|00〉 + |11〉)
}
operation PreparePhiMinus(register : Qubit[]) : Unit {
ResetAll(register); // |00〉
H(register[0]); // |+0〉
Z(register[0]); // |-0〉
CNOT(register[0], register[1]); // 1/sqrt(2)(|00〉 - |11〉)
}
operation PreparePsiPlus(register : Qubit[]) : Unit {
ResetAll(register); // |00〉
H(register[0]); // |+0〉
X(register[1]); // |+1〉
CNOT(register[0], register[1]); // 1/sqrt(2)(|01〉 + |10〉)
}
operation PreparePsiMinus(register : Qubit[]) : Unit {
ResetAll(register); // |00〉
H(register[0]); // |+0〉
Z(register[0]); // |-0〉
X(register[1]); // |-1〉
CNOT(register[0], register[1]); // 1/sqrt(2)(|01〉 - |10〉)
}
}
Allocation des Qubits
Dans un premier temps on alloue d'abord deux qubits qu'on stockera dans un tableau de qubit nommé result
:
use register = Qubit[2];
Préparation des états de Bell
Ensuite on définit les opérations pour préparer les quatres états Bells qu'on a vu précedemment. Chaque état à son opération qui portent le nom des portes correspondantes. Dans chacune de ces fonctions on applique les portes nécessaire pour avoir les qubits dans l'état recherché.
Avant d'expliquer comment on prépare chacun de ces états voyons ce que font les portes quantiques qui seront impliquées.
Les portes quantiques sont les opération qui font évoluer des éléments quantiques d'un état à un autre.
Porte CNOT
la porte CNOT effectue une inversion de l'état du qubit cible uniquement lorsque le qubit de contrôle est dans l'état 1, et ne fait rien lorsque le qubit de contrôle est dans l'état 0
Porte Z
Mathématiquement, la porte Z peut être représentée par la matrice suivante :

En prenant la sphère de Bloch, cette porte effectue une rotation de π autour de l'axe Z dans la sphère de Bloch, inversant donc la phase de l'état ∣1⟩ sans affecter la probabilité d'observation. Cela signifie que si le qubit cible est dans l'état ∣1⟩, il deviendra −∣1⟩ après l'application de la porte Z, tandis que s'il est dans l'état ∣0⟩, il restera ∣0⟩ inchangé.
Porte H
La porte Hadamard comme vu dans le précédent algorithme, transforme les états de base ∣0⟩ et ∣1⟩ en états superposés. Plus précisément, elle effectue la transformation suivante :

Les opérations pour préparer les états
- L'opération Φ+ (PhiPlus) :
operation PreparePhiPlus(register : Qubit[]) : Unit {
ResetAll(register);
H(register[0]);
CNOT(register[0], register[1]);
}
On reset d'abord les qubits qui sont passés en paramètre de l'opération, pour ensuite appliquer la porte Hadamard au premier qubit. Enfin, elle applique une porte CNOT entre le premier et le deuxième qubit, créant ainsi l'état entrelacé
- L'opération Φ+ (PhiMinus) :
operation PreparePhiMinus(register : Qubit[]) : Unit {
ResetAll(register);
H(register[0]);
Z(register[0]);
CNOT(register[0], register[1]);
}
Ici, on reset les qubits passés en paramètre de l'opération, pour appliquer successivement les portes H et Z au premier qubit du tableau register
pour enfin appliquer la porte CNOT créant ainsi l'état entrelacé.
- L'opération Ψ+ (PsiPlus) :
operation PreparePsiPlus(register : Qubit[]) : Unit {
ResetAll(register);
H(register[0]);
X(register[1]);
CNOT(register[0], register[1]);
}
Cette opération reset dans un premier temps les qubits du tableau passé en paramètre, applique la porte H au premier qubit du tableau register
puis la porte X au deuxième qubit. Pour enfin Appliquer la porte CNOT.
- L'opération Ψ- (PsiMinus) :
operation PreparePsiMinus(register : Qubit[]) : Unit {
ResetAll(register);
H(register[0]);
Z(register[0]);
X(register[1]);
CNOT(register[0], register[1]);
}
En suivant le même pattern que les précédentes opérations, ici on reset les qubits du tableau register
puis on applique successivement la porte H et Z au premier qubit, puis a porte X au deuxième Qubit. Pour enfin appliquer la porte CNOT aux deux qubits comme les trois autres opérations.
Créer un tableau de tuple
Une fois les états de bell préparés, on peut retourner dans la partie du code principale, ou on y instancie un tableau de tuples contenant des étiquettes des états de Bell et les opérations de préparation associées pour chaque état de Bell qu'on vient de rédiger :
let bellStateTuples = [
("|Φ+〉", PreparePhiPlus),
("|Φ-〉", PreparePhiMinus),
("|Ψ+〉", PreparePsiPlus),
("|Ψ-〉", PreparePsiMinus)
];
Variable pour stocker le résultat
On initialise ensuite une variable mutable (car on veut pouvoir le modifier par la suite) "measurements" pour stocker les résultats des mesures :
mutable measurements = [];
Mesurer les qubits en appliquant chacun des états de Bell
Une fois l'essentiel instancié on peut appliquer chacune des préparation pour appliquer les états de Bell aux deux qubits qu'on a alloué. Pour cela, on parcour le tableau de tuple de la manière qui suit ;
for (label, prepare) in bellStateTuples {
Dans cette boucle, on appelle la fonction de préparation associée à l'état de Bell actuel sur le registre de qubits :
prepare(register);
Ensuite, on affiche un message décrivant l'état de Bell actuel, puis affiche l'état complet de la machine quantique :
Message($"Bell state {label}:");
DumpMachine();
On a donc appliqué l'état actuel sur les qubits, mais l'objectif est de mesurer les qubits une fois l'état appliqué et de les stocker ensuite dans le tableau instancié à son effet :
set measurements += [(MResetZ(register[0]), MResetZ(register[1]))];
Pour finir, on retourne le tableau des résultats des mesures :
return measurements;
Voici le résultat lorsqu'on exécute le code ;

Conclusion
J'espère que vous avez appréciez les lectures de mes articles au cours de ces 6 dernières semaines. J'espère que vous en sortez moins confus et moins brouillé vis à vis de tous ces concepts. Malheurement un j'espère ne suffira pas pour m'assurer que ma propre compréhension de la matière vous ait éclairé d'une quelquonque manière.
À vrai dire, pour tout à fait être honnête, je ne suis pas sûr d'avoir compris ces concepts. Je pensais au début qu'il me fallait de bien meilleur bases en physique et en mathématiques pour avoir une meilleur introduction au monde quantique.
Mais Richard Feynman, l'un des physicien les plus influent de la seconde partie du XXe siècle grâce notamment à ses travaux sur l'électrodynamique quantique, disait :
Je crois pouvoir affirmer que personne ne comprend vraiment la physique quantique.
C'est en tombant sur cette citation que je me suis rendu compte d'une chose très intéressante. Même en étant un expert en la matière, nos certitudes sur celle-ci ne seront jamais assez fondées tant qu'on ait pas fait d'avancée conséquente.
Tant que ça ne sera pas le cas, la confusion persistera, et les oppositions d'interprétations continueront de subsister dans la sphère quantique.
Ainsi la dimension philosophique continuera de planer. En effet, depuis que les bases probabilistes des mesures ont été posées, on a assisté à une opposition réaliste de certains physiciens attachés à une vision dite réaliste de la physique. Pour ces derniers la physique se doit de décrire le comportement d'entités physiques réelles, et non se contenter de prédire des résultats. Quand Einstein disait :
Je suis persuadé que Dieu ne joue pas aux dés.

C'était je pense, le symptôme d'une physique quantique touchée par l'incertitude sur la base même du concept général.
À mon humble avis, quand la métaphysique se voit être un facteur important dans l'interprétation d'un concept, c'est que ce concept a encore beaucoup de zone grises qu'il faudrait démystifier.
Ceci dit, quand on pense à toutes les avancées qu'on ait pu faire grâce à la physique quantique, imaginez ce qu'on sera capable de faire quand tout cet univers ne sera plus un mystère pour nous.
Si je ne suis pas sûr d'avoir saisit le concept de la physique et la mécanique quantique, il y a bien une chose pour laquelle je n'ai aucun doute :
La physique quantique continuera toujours à nous étonner et à ouvrir de vastes horizons, tant dans les aspects concrets de notre existence que dans les aspects métaphysiques.
C'est pour cette raison que j'aimerai approffondir cette science, et qui sait ! Peut être que dans trois ou quatre ans, je relierai mes articles avec un rire méprisant sur la qualité de mes recherches. Ou peut être pas...
Nous le saurons dans quatre ans.
Références
Learn quantum computing with Azure Quantum, https://quantum.microsoft.com/en-us/experience/quantum-coding (Page consultée le 21 mars 2024)
Tutoriel : Implémenter un générateur de nombres aléatoires quantique en Q#, https://learn.microsoft.com/fr-fr/azure/quantum/tutorial-qdk-quantum-random-number-generator?tabs=tabid-copilot (Page consultée le 21 mars 2024)
Tutoriel : Explorer l’intrication avec Q#, https://learn.microsoft.com/fr-fr/azure/quantum/tutorial-qdk-explore-entanglement?pivots=ide-azure-copilot (Page consultée le 21 mars 2024)
États de Bell, https://fr.wikipedia.org/wiki/%C3%89tats_de_Bell#:~:text=Les%20%C3%A9tats%20de%20Bell%20sont,%C3%A9tats%20quantiques%20aux%20propri%C3%A9t%C3%A9s%20int%C3%A9ressantes. (Page consultée le 22 mars 2024)
Téléportation quantique https://fr.wikipedia.org/wiki/T%C3%A9l%C3%A9portation_quantique (Page consultée le 22 mars 2024)
Sphère de Bloch, https://fr.wikipedia.org/wiki/Sph%C3%A8re_de_Bloch (Page consultée le 22 mars 2024)
Les portes quantiques à 1 qubit, https://www.utc.fr/~wschon/sr06/demonstrateur-algorithmes-quantiques-master/website/_site/portes_un_qubit.html (Page consultée le 22 mars 2024)
Les nouveaux défis de la physique quantique, https://www.pourlascience.fr/sd/physique/les-nouveaux-defis-de-la-physique-quantique-17059.php (Page consultée le 21 mars 2024)
Variable cachée, https://fr.wikipedia.org/wiki/Variable_cach%C3%A9e (Page consultée le 22 mars 2024)
Was Einstein or Bohr right?, https://www.quora.com/Was-Einstein-or-Bohr-right (Page consultée le 22 mars 2024)
Téléportation quantique : une équipe chinoise rapporte un nouveau record de vitesse, https://www.science-et-vie.com/sciences-fondamentales/teleportation-quantique-une-equipe-chinoise-rapporte-un-nouveau-record-de-vitesse-116734.html (Page consultée le 22 mars 2024)
Commentaires