Aller au contenu principal

Bits, portes et circuits quantiques

remarque

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.

"circuit logique classique et circuit quantique"

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 0|0\rangle et 1|1\rangle. Un état quantique arbitraire est représenté par

ψ=α0+β1|\psi\rangle =\alpha|0\rangle+ \beta|1\rangle

α\alpha et β\beta sont des nombres complexes tels que α2+β2=1|\alpha|^2+|\beta|^2=1.

0|0\rangle et 1|1\rangle sont des vecteurs dans l'espace vectoriel complexe à deux dimensions :

0=(10),1=(01)|0\rangle = \begin{pmatrix} 1 \\0 \end{pmatrix}, |1\rangle = \begin{pmatrix} 0\\1 \end{pmatrix}

Par conséquent, un état quantique arbitraire est également représenté par

ψ=α(10)+β(01)=(αβ)|\psi\rangle = \alpha\begin{pmatrix} 1 \\ 0 \end{pmatrix} + \beta\begin{pmatrix}0\\ 1 \end{pmatrix} = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}

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 0|0\rangle et 1|1\rangle. Il est normalisé à 1.

ψψ=(αβ)(αβ)=1\langle\psi|\psi\rangle = \begin{pmatrix} \alpha^* & \beta^* \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix} = 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

ψ=cosθ20+eiφsinθ21=((cosθ2eiφsinθ2))|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle =\left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix}\right)

θ\theta et φ\varphi sont les angles de la sphère de Bloch dans la figure suivante.

Sphère de Bloch 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")

Sortie de la cellule de code précédente

Porte X

La porte X est une rotation de π\pi autour de l'axe xx de la sphère de Bloch. Appliquer la porte X à 0|0\rangle donne 1|1\rangle, et appliquer la porte X à 1|1\rangle donne 0|0\rangle ; 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.

X=(0110)X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix}
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")

Sortie de la cellule de code précédente

Dans IBM Quantum®, l'état initial est 0|0\rangle, donc le circuit quantique ci-dessus en représentation matricielle est

X0=(0110)(10)=(01)=1X|0\rangle= \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} =\begin{pmatrix} 0 \\ 1 \end{pmatrix} = |1\rangle

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,))

Sortie de la cellule de code précédente

Le vecteur vertical est affiché comme vecteur ligne, avec des nombres complexes (la partie imaginaire est indexée par jj ).

Porte H

La porte Hadamard est une rotation de π\pi autour d'un axe à mi-chemin entre les axes xx et zz sur la sphère de Bloch. L'application de la porte H à 0|0\rangle crée un état de superposition tel que 0+12\frac{|0\rangle + |1\rangle}{\sqrt{2}}. La représentation matricielle de la porte H est la suivante.

H=12(1111)H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \\ \end{pmatrix}
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")

Sortie de la cellule de code précédente

# 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,))

Sortie de la cellule de code précédente

C'est

H0=12(1111)(10)=12(11)=(0.7070.707)=12(0+1)H|0\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 1 \\0 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\ 1 \end{pmatrix} =\begin{pmatrix} 0.707 \\ 0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)

Cet état de superposition est si courant et important qu'il possède son propre symbole :

+12(0+1).|+\rangle \equiv \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle).

En appliquant la porte HH à 0|0\rangle, nous avons créé une superposition de 0|0\rangle et 1|1\rangle 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 |-\rangle

Tu as peut-être deviné qu'il existe un état |-\rangle correspondant :

012.|-\rangle \equiv \frac{|0\rangle -|1\rangle}{\sqrt{2}}.

Pour créer cet état, applique d'abord une porte X pour obtenir 1|1\rangle, 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")

Sortie de la cellule de code précédente

# 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,))

Sortie de la cellule de code précédente

C'est

H1=12(11 11)(0 1)=12(1 1)=(0.707 0.707)=12(01)=H|1\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\\ 1 & -1 \end{pmatrix} \begin{pmatrix} 0 \\\ 1 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\\ -1 \end{pmatrix} =\begin{pmatrix} 0.707 \\\ -0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle) = |-\rangle

Appliquer la porte HH à 1|1\rangle donne une superposition égale de 0|0\rangle et 1|1\rangle, mais le signe de 1|1\rangle 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 :

ψ=Uψ|\psi^{'}\rangle = U|\psi\rangle

Une matrice unitaire est une matrice satisfaisant

UU=UU=I.U^{\dagger}U =U U^{\dagger} = I.

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 :

X=(0110)=01+10X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix} = |0\rangle \langle 1|+|1\rangle \langle 0| Y=(0ii0)=i01+i10Y = \begin{pmatrix} 0 & -i \\ i & 0 \\ \end{pmatrix} = -i|0\rangle \langle 1|+i|1\rangle \langle 0| Z=(1001)=0011Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \\ \end{pmatrix} = |0\rangle \langle 0|-|1\rangle \langle 1|

où le produit extérieur a été calculé comme suit :

00=[10][10]=[1000],10=[01][10]=[0010],|0\rangle \langle 0|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 1 & 0 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 0|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 1 & 0 \\ \end{bmatrix}, \quad 01=[10][01]=[0100],11=[01][01]=[0001],|0\rangle \langle 1|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 1 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 1|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 0 & 1 \\ \end{bmatrix}, \quad

Autres portes à qubit unique typiques :

H=12[1111],S=[100i],T=[100exp(iπ/4)]H= \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \\ \end{bmatrix},\quad S = \begin{bmatrix} 1 & 0 \\ 0 & i \\ \end{bmatrix}, \quad T = \begin{bmatrix} 1 & 0 \\ 0 & exp(i\pi/4) \\ \end{bmatrix} Rx(θ)=eiθX/2=cosθ2Iisinθ2X=[cosθ2isinθ2isinθ2cosθ2]R_x(\theta) = e^{-i\theta X/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}X = \begin{bmatrix} cos\frac{\theta}{2} & -i sin \frac{\theta}{2} \\ -i sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Ry(θ)=eiθY/2=cosθ2Iisinθ2Y=[cosθ2sinθ2sinθ2cosθ2]R_y(\theta) = e^{-i\theta Y/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Y = \begin{bmatrix} cos\frac{\theta}{2} & - sin \frac{\theta}{2} \\ sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Rz(θ)=eiθZ/2=cosθ2Iisinθ2Z=[eiθ/200eiθ/2]R_z(\theta) = e^{-i\theta Z/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Z = \begin{bmatrix} e^{-i\theta /2} & 0 \\ 0 & e^{i\theta /2} \\ \end{bmatrix}

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) XX0XX|0\rangle

(2) HH0HH|0\rangle

(3) HZH0HZH|0\rangle

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")

Sortie de la cellule de code précédente

# 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,))

Sortie de la cellule de code précédente

### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")

Sortie de la cellule de code précédente

# 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,))

Sortie de la cellule de code précédente

### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")

Sortie de la cellule de code précédente

# 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,))

Sortie de la cellule de code précédente

3.3 Mesure

La mesure est théoriquement un sujet très complexe. Mais en pratique, effectuer une mesure le long de zz (comme le font tous les ordinateurs quantiques IBM®) force simplement l'état du qubit α0+β1(s.t.α2+β2=1)\alpha|0\rangle+\beta|1\rangle \quad (s.t.|\alpha|^2+|\beta|^2=1) soit vers 0|0\rangle soit vers 1,|1\rangle, et nous observons le résultat.

  • α2|\alpha|^2 est la probabilité d'obtenir 0|0\rangle lors de la mesure.
  • β2|\beta|^2 est la probabilité d'obtenir 1|1\rangle lors de la mesure.

Ainsi, α\alpha et β\beta sont appelés amplitudes de probabilité. (voir la « règle de Born »)

Par exemple, 220+221\frac{\sqrt{2}}{2}|0\rangle+\frac{\sqrt{2}}{2}|1\rangle a une probabilité égale de devenir 0|0\rangle ou 1|1\rangle lors de la mesure. 32012i1\frac{\sqrt{3}}{2}|0\rangle-\frac{1}{2}i|1\rangle a 75 % de chance de devenir 0|0\rangle.

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")

Sortie de la cellule de code précédente

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}

Sortie de la cellule de code précédente

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")

Sortie de la cellule de code précédente

# 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 : 01|01\rangle signifie que q0 est 1|1\rangle et q1 est 0|0\rangle. 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, q0|q_0\rangle 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.

q1q0=(a0+b1)(c0+d1)|q1\rangle \otimes|q0\rangle = (a|0\rangle+b|1\rangle) \otimes (c|0\rangle+d|1\rangle)

=ac00+ad01+bc10+bd11= ac|0\rangle|0\rangle+ad|0\rangle|1\rangle+bc|1\rangle|0\rangle+bd|1\rangle|1\rangle

=ac00+ad01+bc10+bd11= ac|00\rangle+ad|01\rangle+bc|10\rangle+bd|11\rangle

( ac2+ad2+bc2+bd2=1|ac|^2+ |ad|^2+ |bc|^2+ |bd|^2=1 )

L'état initial de Qiskit est 00=00|0\rangle|0\rangle=|00\rangle, donc en appliquant HH à chaque qubit, il passe à un état de superposition égale.

H0H0=12(0+1)12(0+1)=12(00+01+10+11)H|0\rangle \otimes H|0\rangle=\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) \otimes \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) = \frac{1}{2}(|00\rangle+|01\rangle+|10\rangle+|11\rangle)

=12((11)(11))=12(1111)=12((1000)+(0100)+(0010)+(0001))=\frac{1}{2}\left( \begin{pmatrix} 1 \\ 1 \end{pmatrix} \otimes \begin{pmatrix} 1 \\ 1 \end{pmatrix}\right) = \frac{1}{2}\begin{pmatrix} 1 \\ 1 \\ 1 \\ 1 \end{pmatrix}=\frac{1}{2}\left(\begin{pmatrix} 1 \\ 0 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 1 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix}\right)

La règle de mesure est également la même que pour un qubit unique : la probabilité de mesurer 00|00\rangle est ac2|ac|^2.

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Sortie de la cellule de code précédente

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")

Sortie de la cellule de code précédente

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}

Sortie de la cellule de code précédente

Comme prévu, les états 00|00\rangle, 01|01\rangle, 10|10\rangle, 11|11\rangle 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 1|1\rangle.

Entrée (cible, contrôle)Sortie (cible, contrôle)
0000
0111
1010
1101

Simulons d'abord l'action de cette porte à deux qubits lorsque q0 et q1 sont tous les deux 0|0\rangle, 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")

Sortie de la cellule de code précédente

# 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 00|00\rangle n'a pas modifié l'état, puisque le qubit de contrôle était dans l'état 0|0\rangle. Revenons à notre opération CNOT. Cette fois, nous allons appliquer une porte CNOT à 01|01\rangle 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")

Sortie de la cellule de code précédente

# 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 01|01\rangle est maintenant devenu 11|11\rangle.

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")

Sortie de la cellule de code précédente

# 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}

Sortie de la cellule de code précédente

Les résultats devraient montrer que 11|11\rangle 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é » :

1200+1211\frac{1}{\sqrt{2}}|00\rangle + \frac{1}{\sqrt{2}}|11\rangle

et cet état est appelé un état de Bell.

Un état intriqué est un état ψAB|\psi_{AB}\rangle composé d'états quantiques ψA|\psi_A\rangle et ψB|\psi_B\rangle qui ne peut pas être représenté par un produit tensoriel d'états quantiques individuels.

Si ψAB|\psi_{AB}\rangle ci-dessous a deux états ψA|\psi\rangle_A et ψB|\psi\rangle_B ;

ψAB=12(00+11)=12(0A0B+1A1B)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) = \frac{1}{\sqrt{2}}(|0\rangle_A|0\rangle_B +|1\rangle_A|1\rangle_B) ψA=a00+a11|\psi\rangle_A = a_0|0\rangle+a_1|1\rangle ψB=b00+b11|\psi\rangle_B = b_0|0\rangle+b_1|1\rangle

le produit tensoriel de ces deux états est le suivant

ψAψB=a0b000+a0b101+a1b010+a1b111|\psi\rangle _A\otimes |\psi\rangle _B = a_0 b_0|00\rangle+a_0 b_1|01\rangle+a_1 b_0|10\rangle+a_1 b_1|11\rangle

mais il n'existe aucun coefficient a0,a1,b0,a_0, a_1, b_0, et b1b_1 pour satisfaire ces deux équations. Par conséquent, ψAB|\psi_{AB}\rangle n'est pas représenté par un produit tensoriel d'états quantiques individuels, ψA|\psi\rangle_A et ψB|\psi\rangle_B, et cela signifie que ψAB=12(00+11)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) 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 :

  1. Mapper le problème sur des circuits et des opérateurs quantiques
  2. Optimiser pour le matériel cible
  3. Exécuter sur le matériel cible
  4. 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")

Sortie de la cellule de code précédente

É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, 00|00\rangle et 11|11\rangle 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

12(000+111)\frac{1}{\sqrt 2}(|000\rangle + |111\rangle)

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")

Sortie de la cellule de code précédente

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

12(00000000+11111111)\frac{1}{\sqrt 2}(|00000000\rangle + |11111111\rangle)

É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())

Sortie de la cellule de code précédente

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}

Sortie de la cellule de code précédente

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'