Bits, portes et circuits quantiques
Kifumi Numata (19 Apr 2024)
Clique ici pour télécharger le PDF du cours original. Note que certains extraits de code peuvent être obsolètes car il s'agit d'images statiques.
Le temps QPU approximatif pour exécuter cette expérience est de 5 secondes.
1. Introduction
Les bits, les portes et les circuits sont les blocs de construction fondamentaux du calcul quantique. Tu apprendras le calcul quantique avec le modèle de circuits en utilisant des bits quantiques et des portes, et tu reverras également la superposition, la mesure et l'intrication.
Dans cette leçon, tu apprendras :
- Les portes à qubit unique
- La sphère de Bloch
- La superposition
- La mesure
- Les portes à deux qubits et l'état d'intrication
À la fin de ce cours, tu apprendras la profondeur de circuit, qui est essentielle pour le calcul quantique à l'échelle utilitaire.
2. Le calcul comme un diagramme
Lorsqu'on utilise des qubits ou des bits, il faut les manipuler pour transformer les entrées dont on dispose en les sorties dont on a besoin. Pour les programmes les plus simples comportant très peu de bits, il est utile de représenter ce processus sous la forme d'un diagramme connu sous le nom de diagramme de circuit.
La figure en bas à gauche est un exemple de circuit classique, et la figure en bas à droite est un exemple de circuit quantique. Dans les deux cas, les entrées sont à gauche et les sorties à droite, tandis que les opérations sont représentées par des symboles. Les symboles utilisés pour les opérations sont appelés « portes », principalement pour des raisons historiques.
3. Porte quantique à qubit unique
3.1 État quantique et sphère de Bloch
L'état d'un qubit est représenté comme une superposition de et . Un état quantique arbitraire est représenté par
où et sont des nombres complexes tels que .
et sont des vecteurs dans l'espace vectoriel complexe à deux dimensions :
Par conséquent, un état quantique arbitraire est également représenté par
De là, on peut voir que l'état d'un bit quantique est un vecteur unitaire dans un espace produit intérieur complexe à deux dimensions, avec une base orthonormale formée de et . Il est normalisé à 1.
|\psi\rangle =\begin\{pmatrix\} \alpha \\ \beta \end\{pmatrix\} est également appelé le vecteur d'état (statevector).
Un état quantique à qubit unique est aussi représenté par
où et sont les angles de la sphère de Bloch dans la figure suivante.
Dans les prochaines cellules de code, nous allons construire des calculs de base à partir d'éléments constitutifs dans Qiskit. Nous construirons un circuit vide, puis ajouterons des opérations quantiques, en discutant des portes et en visualisant leurs effets au fur et à mesure.
Tu peux exécuter la cellule avec "Shift" + "Entrée". Commence par importer les bibliothèques.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# Import the qiskit library
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
from qiskit_ibm_runtime import Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.visualization import plot_histogram
Préparer le circuit quantique
Nous allons créer et dessiner un circuit à qubit unique.
# Create the single-qubit quantum circuit
qc = QuantumCircuit(1)
# Draw the circuit
qc.draw("mpl")
Porte X
La porte X est une rotation de autour de l'axe de la sphère de Bloch. Appliquer la porte X à donne , et appliquer la porte X à donne ; c'est donc une opération similaire à la porte NOT classique, également connue sous le nom de bit flip. La représentation matricielle de la porte X est la suivante.
qc = QuantumCircuit(1) # Prepare the single-qubit quantum circuit
# Apply a X gate to qubit 0
qc.x(0)
# Draw the circuit
qc.draw("mpl")
Dans IBM Quantum®, l'état initial est , donc le circuit quantique ci-dessus en représentation matricielle est
Ensuite, exécutons ce circuit en utilisant un simulateur de vecteur d'état.
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))
Le vecteur vertical est affiché comme vecteur ligne, avec des nombres complexes (la partie imaginaire est indexée par ).
Porte H
La porte Hadamard est une rotation de autour d'un axe à mi-chemin entre les axes et sur la sphère de Bloch. L'application de la porte H à crée un état de superposition tel que . La représentation matricielle de la porte H est la suivante.
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit
# Apply an Hadamard gate to qubit 0
qc.h(0)
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.70710678+0.j, 0.70710678+0.j],
dims=(2,))
C'est
Cet état de superposition est si courant et important qu'il possède son propre symbole :
En appliquant la porte à , nous avons créé une superposition de et où une mesure dans la base computationnelle (le long de z dans la représentation de la sphère de Bloch) donnerait chaque état avec des probabilités égales.
État
Tu as peut-être deviné qu'il existe un état correspondant :
Pour créer cet état, applique d'abord une porte X pour obtenir , puis applique une porte H.
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit
# Apply a X gate to qubit 0
qc.x(0)
# Apply an Hadamard gate to qubit 0
qc.h(0)
# draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([ 0.70710678+0.j, -0.70710678+0.j],
dims=(2,))
C'est
Appliquer la porte à donne une superposition égale de et , mais le signe de est négatif.
3.2 État quantique à qubit unique et évolution unitaire
Les actions de toutes les portes que nous avons vues jusqu'ici sont unitaires, ce qui signifie qu'elles peuvent être représentées par un opérateur unitaire. En d'autres termes, l'état de sortie peut être obtenu en appliquant une matrice unitaire à l'état initial :
Une matrice unitaire est une matrice satisfaisant
En termes d'opération d'ordinateur quantique, on dirait qu'appliquer une porte quantique au qubit fait évoluer l'état quantique. Les portes à qubit unique courantes sont les suivantes.
Portes de Pauli :
où le produit extérieur a été calculé comme suit :
Autres portes à qubit unique typiques :
La signification et l'utilisation de ces portes sont décrites plus en détail dans le cours Bases de l'information quantique.
Exercice 1
Utilise Qiskit pour créer des circuits quantiques qui préparent les états décrits ci-dessous. Ensuite, exécute chaque circuit en utilisant le simulateur de vecteur d'état et affiche l'état résultant sur la sphère de Bloch. En bonus, essaie d'anticiper quel devrait être l'état final en te basant sur ton intuition des portes et des rotations sur la sphère de Bloch.
(1)
(2)
(3)
Conseil : la porte Z peut être utilisée avec
qc.z(0)
Solution :
### (1) XX|0> ###
# Create the single-qubit quantum circuit
qc = QuantumCircuit(1) ##your code goes here##
# Add a X gate to qubit 0
qc.x(0) ##your code goes here##
# Add a X gate to qubit 0
qc.x(0) ##your code goes here##
# Draw a circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))
3.3 Mesure
La mesure est théoriquement un sujet très complexe. Mais en pratique, effectuer une mesure le long de (comme le font tous les ordinateurs quantiques IBM®) force simplement l'état du qubit soit vers soit vers et nous observons le résultat.
- est la probabilité d'obtenir lors de la mesure.
- est la probabilité d'obtenir lors de la mesure.
Ainsi, et sont appelés amplitudes de probabilité. (voir la « règle de Born »)
Par exemple, a une probabilité égale de devenir ou lors de la mesure. a 75 % de chance de devenir .
Simulateur Qiskit Aer
Ensuite, mesurons un circuit qui prépare la superposition de probabilité égale ci-dessus. Nous devons ajouter les portes de mesure, car le simulateur Qiskit Aer simule par défaut un matériel quantique idéal (sans bruit). Note : le simulateur Aer peut également appliquer un modèle de bruit basé sur un vrai ordinateur quantique. Nous reviendrons sur les modèles de bruit plus tard.
# Create a new circuit with one qubits (first argument) and one classical bits (second argument)
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0) # Add the measurement gate
qc.draw(output="mpl")
Nous sommes maintenant prêts à exécuter notre circuit sur le simulateur Aer. Dans cet exemple, nous appliquerons la valeur par défaut shots=1024, ce qui signifie que nous mesurerons 1024 fois. Ensuite, nous représenterons ces comptages dans un histogramme.
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'0': 521, '1': 503}
On constate que 0 et 1 ont été mesurés avec une probabilité de presque 50 % chacun. Bien que le bruit n'ait pas été simulé ici, les états restent probabilistes. Ainsi, bien qu'on s'attende à une distribution approximativement 50-50, on la trouvera rarement exactement ainsi. Tout comme 100 lancers d'une pièce donneraient rarement exactement 50 fois chaque face.
4. Porte quantique multi-qubit et intrication
4.1 Circuit quantique multi-qubit
Nous pouvons créer un circuit quantique à deux qubits avec le code suivant. Nous allons appliquer une porte H à chaque qubit.
# Create the two qubits quantum circuit
qc = QuantumCircuit(2)
# Apply an H gate to qubit 0
qc.h(0)
# Apply an H gate to qubit 1
qc.h(1)
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j],
dims=(2, 2))
Note : Ordre des bits dans Qiskit
Qiskit utilise la notation Little Endian pour ordonner les qubits et les bits, ce qui signifie que le qubit 0 est le bit le plus à droite dans les chaînes de bits. Exemple : signifie que q0 est et q1 est . Fais attention car certaines références en calcul quantique utilisent la notation Big Endian (le qubit 0 est le bit le plus à gauche), et une grande partie de la littérature en mécanique quantique aussi.
Une autre chose à noter est que lors de la représentation d'un circuit quantique, est toujours placé en haut du circuit. En gardant cela à l'esprit, l'état quantique du circuit ci-dessus peut s'écrire comme un produit tensoriel d'états quantiques à qubit unique.
( )
L'état initial de Qiskit est , donc en appliquant à chaque qubit, il passe à un état de superposition égale.
La règle de mesure est également la même que pour un qubit unique : la probabilité de mesurer est .
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Ensuite, mesurons ce circuit.
# Create a new circuit with two qubits (first argument) and two classical bits (second argument)
qc = QuantumCircuit(2, 2)
# Apply the gates
qc.h(0)
qc.h(1)
# Add the measurement gates
qc.measure(0, 0) # Measure qubit 0 and save the result in bit 0
qc.measure(1, 1) # Measure qubit 1 and save the result in bit 1
# Draw the circuit
qc.draw(output="mpl")
Maintenant, nous allons utiliser à nouveau un simulateur Aer pour vérifier expérimentalement que les probabilités relatives de tous les états de sortie possibles sont à peu près égales.
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'10': 262, '01': 246, '00': 265, '11': 251}
Comme prévu, les états , , , ont été mesurés à presque 25 % chacun.
4.2 Portes quantiques multi-qubit
Porte CNOT
Une porte CNOT (« NOT contrôlé » ou CX) est une porte à deux qubits, ce qui signifie que son action implique deux qubits à la fois : le qubit de contrôle et le qubit cible. Une porte CNOT inverse le qubit cible uniquement lorsque le qubit de contrôle est .
| Entrée (cible, contrôle) | Sortie (cible, contrôle) |
|---|---|
| 00 | 00 |
| 01 | 11 |
| 10 | 10 |
| 11 | 01 |
Simulons d'abord l'action de cette porte à deux qubits lorsque q0 et q1 sont tous les deux , et obtenons le vecteur d'état de sortie. La syntaxe Qiskit utilisée est qc.cx(qubit de contrôle, qubit cible).
# Create a circuit with two quantum registers and two classical registers
qc = QuantumCircuit(2, 2)
# Apply the CNOT (cx) gate to a |00> state.
qc.cx(0, 1) # Here the control is set to q0 and the target is set to q1.
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
dims=(2, 2))
Comme prévu, appliquer une porte CNOT sur n'a pas modifié l'état, puisque le qubit de contrôle était dans l'état . Revenons à notre opération CNOT. Cette fois, nous allons appliquer une porte CNOT à et voir ce qui se passe.
qc = QuantumCircuit(2, 2)
# q0=1, q1=0
qc.x(0) # Apply a X gate to initialize q0 to 1
qc.cx(0, 1) # Set the control bit to q0 and the target bit to q1.
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
dims=(2, 2))
En appliquant une porte CNOT, l'état est maintenant devenu .
Vérifions ces résultats en exécutant le circuit sur un simulateur.
# Add measurements
qc.measure(0, 0)
qc.measure(1, 1)
# Draw the circuit
qc.draw(output="mpl")
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'11': 1024}
Les résultats devraient montrer que a été mesuré avec une probabilité de 100 %.
4.3 Intrication quantique et exécution sur un vrai dispositif quantique
Commençons par présenter un état intriqué particulier qui est particulièrement important en calcul quantique, puis nous définirons le terme « intriqué » :
et cet état est appelé un état de Bell.
Un état intriqué est un état composé d'états quantiques et qui ne peut pas être représenté par un produit tensoriel d'états quantiques individuels.
Si ci-dessous a deux états et ;
le produit tensoriel de ces deux états est le suivant
mais il n'existe aucun coefficient et pour satisfaire ces deux équations. Par conséquent, n'est pas représenté par un produit tensoriel d'états quantiques individuels, et , et cela signifie que est un état intriqué.
Créons l'état de Bell et exécutons-le sur un vrai ordinateur quantique. Nous allons maintenant suivre les quatre étapes pour écrire un programme quantique, appelées Qiskit patterns :
- Mapper le problème sur des circuits et des opérateurs quantiques
- Optimiser pour le matériel cible
- Exécuter sur le matériel cible
- Post-traiter les résultats
Étape 1. Mapper le problème sur des circuits et des opérateurs quantiques
Dans un programme quantique, les circuits quantiques sont le format natif pour représenter les instructions quantiques. Lors de la création d'un circuit, tu créeras généralement un nouvel objet QuantumCircuit, puis tu y ajouteras des instructions en séquence.
La cellule de code suivante crée un circuit qui produit un état de Bell, l'état intriqué à deux qubits spécifique mentionné ci-dessus.
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw("mpl")
Étape 2. Optimiser pour le matériel cible
Qiskit convertit les circuits abstraits en circuits QISA (Quantum Instruction Set Architecture) qui respectent les contraintes du matériel cible et optimise les performances du circuit. Avant l'optimisation, nous allons donc spécifier le matériel cible.
Si tu n'as pas qiskit-ibm-runtime, tu devras d'abord l'installer. Pour plus d'informations sur Qiskit Runtime, consulte la référence de l'API.
# Install
# !pip install qiskit-ibm-runtime
Nous allons spécifier le matériel cible.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
# You can specify the device
# backend = service.backend('ibm_kingston')
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
La transpilation du circuit est un processus complexe supplémentaire. En bref, cela réécrit le circuit en un circuit logiquement équivalent en utilisant des « portes natives » (des portes qu'un ordinateur quantique particulier peut implémenter) et mappe les qubits de ton circuit sur des qubits réels optimaux sur l'ordinateur quantique cible. Pour en savoir plus sur la transpilation, consulte cette documentation.
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
target_circuit = pm.run(qc)
target_circuit.draw("mpl", idle_wires=False)
Tu peux voir qu'au cours de la transpilation, le circuit a été réécrit en utilisant de nouvelles portes. Pour plus d'informations, consulte la documentation ECRGate.
Étape 3. Exécuter le circuit cible
Maintenant, nous allons exécuter le circuit cible sur le vrai dispositif.
sampler = Sampler(backend)
job_real = sampler.run([target_circuit])
job_id = job_real.job_id()
print("job id:", job_id)
L'exécution sur le vrai dispositif peut nécessiter une attente dans une file d'attente, car les ordinateurs quantiques sont des ressources précieuses très demandées. Le job_id est utilisé pour vérifier le statut d'exécution et les résultats du travail ultérieurement.
# Check the job status (replace the job id below with your own)
job_real.status(job_id)
Tu peux également vérifier le statut du travail depuis ton tableau de bord IBM Quantum:https://quantum.cloud.ibm.com/workloads
# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
job_real = service.job(job_id) # Input your job-id between the quotations
job_real.status()
# Execute after job has successfully run
result_real = job_real.result()
print(result_real[0].data.c.get_counts())
Étape 4. Post-traiter les résultats
Enfin, nous devons post-traiter nos résultats pour créer des sorties dans le format attendu, comme des valeurs ou des graphiques.
plot_histogram(result_real[0].data.c.get_counts())
Comme tu peux le voir, et sont les plus fréquemment observés. Il y a quelques résultats autres que les données attendues, et ils sont dus au bruit et à la décohérence des qubits. Nous en apprendrons davantage sur les erreurs et le bruit dans les ordinateurs quantiques dans les leçons ultérieures de ce cours.
4.4 État GHZ
Le concept d'intrication peut être étendu aux systèmes de plus de deux qubits. L'état GHZ (état de Greenberger-Horne-Zeilinger) est un état maximalement intriqué de trois qubits ou plus. L'état GHZ pour trois qubits est défini comme
Il peut être créé avec le circuit quantique suivant.
qc = QuantumCircuit(3, 3)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
qc.measure(0, 0)
qc.measure(1, 1)
qc.measure(2, 2)
qc.draw("mpl")
La « profondeur » d'un circuit quantique est une métrique utile et courante pour décrire les circuits quantiques. Trace un chemin à travers le circuit quantique, de gauche à droite, en ne changeant de qubit que lorsqu'ils sont connectés par une porte multi-qubit. Compte le nombre de portes le long de ce chemin. Le nombre maximum de portes pour tout chemin de ce type à travers un circuit est la profondeur. Dans les ordinateurs quantiques bruités modernes, les circuits de faible profondeur ont moins d'erreurs et sont susceptibles de retourner de bons résultats. Les circuits très profonds ne le sont pas.
En utilisant QuantumCircuit.depth(), nous pouvons vérifier la profondeur de notre circuit quantique. La profondeur du circuit ci-dessus est 4. Le qubit du dessus n'a que trois portes, mesure incluse. Mais il existe un chemin depuis le qubit du dessus jusqu'au qubit 1 ou au qubit 2 qui implique une porte CNOT supplémentaire.
qc.depth()
4
Exercice 2
L'état GHZ d'un système à 8 qubits est
Écris le code pour préparer cet état avec le circuit le moins profond possible. La profondeur du circuit quantique le moins profond est 5, portes de mesure incluses.
Solution :
# Step 1
qc = QuantumCircuit(8, 8)
##your code goes here##
qc.h(0)
qc.cx(0, 4)
qc.cx(4, 6)
qc.cx(6, 7)
qc.cx(4, 5)
qc.cx(0, 2)
qc.cx(2, 3)
qc.cx(0, 1)
qc.barrier() # for visual separation
# measure
for i in range(8):
qc.measure(i, i)
qc.draw("mpl")
# print(qc.depth())
print(qc.depth())
5
from qiskit.visualization import plot_histogram
# Step 2
# For this exercise, the circuit and operators are simple, so no optimizations are needed.
# Step 3
# Run the circuit on a simulator to get the results
backend = AerSimulator()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=1024)
result = job.result()
counts = result[0].data.c.get_counts()
print(counts)
# Step 4
# Plot the counts in a histogram
plot_histogram(counts)
{'11111111': 535, '00000000': 489}
5. Résumé
Tu as appris le calcul quantique avec le modèle de circuits en utilisant des bits quantiques et des portes, et tu as revu la superposition, la mesure et l'intrication. Tu as également appris la méthode pour exécuter le circuit quantique sur le vrai dispositif quantique.
Dans l'exercice final de création d'un circuit GHZ, tu as essayé de réduire la profondeur du circuit, ce qui est un facteur important pour obtenir une solution à l'échelle utilitaire dans un ordinateur quantique bruité. Dans les leçons ultérieures de ce cours, tu apprendras en détail le bruit et les méthodes d'atténuation des erreurs. Dans cette leçon, à titre d'introduction, nous avons envisagé de réduire la profondeur du circuit sur un dispositif idéal, mais en réalité, il faut tenir compte des contraintes du vrai dispositif, telles que la connectivité des qubits. Tu en apprendras davantage à ce sujet dans les leçons suivantes de ce cours.
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'