Sari la conținutul principal

Qubiți cuantici, porți și circuite

notă

Kifumi Numata (19 Apr 2024)

Apasă aici pentru a descărca pdf-ul lecției originale. Reține că unele fragmente de cod ar putea fi deprecate, deoarece acestea sunt imagini statice.

Timpul aproximativ de QPU pentru a rula acest experiment este de 5 secunde.

1. Introducere

Biții, porțile și circuitele sunt elementele de bază ale computației cuantice. Vei învăța computația cuantică cu modelul de circuit, folosind biți cuantici și porți, și vei recapitula superpozița, măsurarea și entanglementul.

În această lecție vei învăța:

  • Porți cu un singur qubit
  • Sfera Bloch
  • Superpozița
  • Măsurarea
  • Porți cu doi qubiți și starea de entanglement

La finalul acestei lecții, vei învăța despre adâncimea circuitului, care este esențială pentru computația cuantică la scară utilă.

2. Computația ca diagramă

Atunci când folosim qubiți sau biți, trebuie să îi manipulăm pentru a transforma intrările pe care le avem în ieșirile de care avem nevoie. Pentru programele cele mai simple cu foarte puțini biți, este util să reprezentăm acest proces printr-o diagramă numită diagramă de circuit.

Figura din stânga jos este un exemplu de circuit clasic, iar figura din dreapta jos este un exemplu de circuit cuantic. În ambele cazuri, intrările sunt la stânga și ieșirile la dreapta, iar operațiile sunt reprezentate prin simboluri. Simbolurile folosite pentru operații se numesc „porți" (gates), în mare parte din motive istorice.

"circuit logic clasic și circuit cuantic"

3. Poarta cuantică cu un singur qubit

3.1 Starea cuantică și sfera Bloch

Starea unui qubit este reprezentată ca o superpozție de 0|0\rangle și 1|1\rangle. O stare cuantică arbitrară este reprezentată astfel:

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

unde α\alpha și β\beta sunt numere complexe cu proprietatea că α2+β2=1|\alpha|^2+|\beta|^2=1.

0|0\rangle și 1|1\rangle sunt vectori în spațiul vectorial complex bidimensional:

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

Prin urmare, o stare cuantică arbitrară este de asemenea reprezentată astfel:

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

Din aceasta putem vedea că starea unui bit cuantic este un vector unitar într-un spațiu vectorial complex cu produs intern bidimensional, cu o bază ortonormată formată din 0|0\rangle și 1|1\rangle. Este normalizat la 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\} se mai numește și statevector.

O stare cuantică cu un singur qubit se mai poate reprezenta și astfel:

ψ=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)

unde θ\theta și φ\varphi sunt unghiurile sferei Bloch din figura de mai jos.

Sfera Bloch În următoarele celule de cod vom construi calcule de bază din componente elementare în Qiskit. Vom crea un circuit gol și vom adăuga operații cuantice, discutând porțile și vizualizând efectele lor pe parcurs. Poți rula celula cu „Shift" + „Enter". Importă mai întâi bibliotecile.

# 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

Pregătirea circuitului cuantic

Vom crea și desena un circuit cu un singur qubit.

# Create the single-qubit quantum circuit
qc = QuantumCircuit(1)

# Draw the circuit
qc.draw("mpl")

Output of the previous code cell

Poarta X

Poarta X este o rotație de π\pi în jurul axei xx a sferei Bloch. Aplicând poarta X pe 0|0\rangle se obține 1|1\rangle, iar aplicând poarta X pe 1|1\rangle se obține 0|0\rangle, deci este o operație similară cu poarta clasică NOT și mai este cunoscută și ca bit flip (inversare de bit). Reprezentarea matriceală a porții X este de mai jos.

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

Output of the previous code cell În IBM Quantum®, starea inițială este setată la 0|0\rangle, deci circuitul cuantic de mai sus în reprezentare matriceală este:

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

Hai să rulăm acum acest circuit folosind un simulator de statevector.

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

Output of the previous code cell

Vectorul vertical este afișat ca vector linie, cu numere complexe (partea imaginară este indexată prin jj).

Poarta H

Poarta Hadamard este o rotație de π\pi în jurul unei axe situate la jumătatea distanței dintre axele xx și zz pe sfera Bloch. Aplicând poarta H pe 0|0\rangle se creează o stare de superpozție, cum ar fi 0+12\frac{|0\rangle + |1\rangle}{\sqrt{2}}. Reprezentarea matriceală a porții H este de mai jos.

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

Output of the previous code cell

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

Output of the previous code cell

Aceasta este:

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)

Această stare de superpozție este atât de frecventă și importantă, încât are propriul său simbol:

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

Prin aplicarea porții HH pe 0|0\rangle, am creat o superpozție de 0|0\rangle și 1|1\rangle, unde măsurarea în baza computațională (de-a lungul axei z în imaginea sferei Bloch) ți-ar da fiecare stare cu probabilități egale.

Starea |-\rangle

Probabil ai ghicit că există o stare corespunzătoare |-\rangle:

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

Pentru a crea această stare, aplică mai întâi o poartă X pentru a obține 1|1\rangle, apoi aplică o poartă 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")

Output of the previous code cell

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

Output of the previous code cell

Aceasta este:

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

Aplicând poarta HH pe 1|1\rangle se obține o superpozție egală de 0|0\rangle și 1|1\rangle, dar semnul lui 1|1\rangle este negativ.

3.2 Starea cuantică cu un singur qubit și evoluția unitară

Acțiunile tuturor porților pe care le-am văzut până acum au fost unitare, ceea ce înseamnă că pot fi reprezentate de un operator unitar. Cu alte cuvinte, starea de ieșire se poate obține acționând asupra stării inițiale cu o matrice unitară:

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

O matrice unitară este o matrice care satisface:

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

În termeni de operare a calculatorului cuantic, am spune că aplicarea unei porți cuantice pe qubit evoluează starea cuantică. Porțile comune cu un singur qubit includ următoarele.

Porți 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|

unde produsul extern a fost calculat astfel:

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

Alte porți tipice cu un singur qubit:

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}

Semnificația și utilizarea acestora sunt descrise mai detaliat în cursul Bazele informației cuantice.

Exercițiul 1

Folosește Qiskit pentru a crea circuite cuantice care pregătesc stările descrise mai jos. Apoi rulează fiecare circuit cu simulatorul de statevector și afișează starea rezultată pe sfera Bloch. Ca bonus, încearcă să anticipezi care ar trebui să fie starea finală pe baza intuiției despre porți și rotații în sfera Bloch.

(1) XX0XX|0\rangle

(2) HH0HH|0\rangle

(3) HZH0HZH|0\rangle

Indicație: Poarta Z poate fi folosită astfel:

qc.z(0)

Soluție:

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

Output of the previous code cell

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

Output of the previous code cell

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

Output of the previous code cell

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

Output of the previous code cell

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

Output of the previous code cell

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

Output of the previous code cell

3.3 Măsurarea

Măsurarea este teoretic un subiect foarte complex. Dar în termeni practici, efectuarea unei măsurători de-a lungul axei zz (cum fac toate calculatoarele cuantice IBM®) forțează pur și simplu starea qubitului α0+β1(s.t.α2+β2=1)\alpha|0\rangle+\beta|1\rangle \quad (s.t.|\alpha|^2+|\beta|^2=1) fie la 0|0\rangle, fie la 1|1\rangle, și observăm rezultatul.

  • α2|\alpha|^2 este probabilitatea de a obține 0|0\rangle atunci când măsurăm.
  • β2|\beta|^2 este probabilitatea de a obține 1|1\rangle atunci când măsurăm.

Prin urmare, α\alpha și β\beta sunt numite amplitudini de probabilitate. (vezi „regula Born")

De exemplu, 220+221\frac{\sqrt{2}}{2}|0\rangle+\frac{\sqrt{2}}{2}|1\rangle are o probabilitate egală de a deveni 0|0\rangle sau 1|1\rangle la măsurare. 32012i1\frac{\sqrt{3}}{2}|0\rangle-\frac{1}{2}i|1\rangle are o șansă de 75% de a deveni 0|0\rangle.

Simulatorul Qiskit Aer

Acum, hai să măsurăm un circuit care pregătește superpozția cu probabilitate egală de mai sus. Trebuie să adăugăm porțile de măsurare, deoarece simulatorul Qiskit Aer simulează un hardware cuantic ideal (fără zgomot) în mod implicit. Notă: Simulatorul Aer poate aplica și un model de zgomot bazat pe un calculator cuantic real. Ne vom întoarce la modelele de zgomot mai târziu.

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

Output of the previous code cell

Suntem acum gata să rulăm circuitul nostru pe simulatorul Aer. În acest exemplu, vom aplica shots=1024 implicit, ceea ce înseamnă că vom măsura de 1024 de ori. Apoi vom reprezenta acele rezultate într-o histogramă.

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

Output of the previous code cell

Vedem că 0 și 1 au fost măsurate cu o probabilitate de aproape 50% fiecare. Deși zgomotul nu a fost simulat aici, stările sunt în continuare probabilistice. Deci, deși ne așteptăm la o distribuție de aproximativ 50-50, rareori o vom găsi exact astfel. La fel cum 100 de aruncări de monedă ar da rareori exact 50 de rezultate pentru fiecare față.

4. Poarta cuantică cu mai mulți qubiți și entanglementul

4.1 Circuitul cuantic cu mai mulți qubiți

Putem crea un circuit cuantic cu doi qubiți cu codul următor. Vom aplica o poartă H fiecărui 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")

Output of the previous code cell

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

Notă: Ordinea biților în Qiskit

Qiskit folosește notația Little Endian la ordonarea qubiților și biților, ceea ce înseamnă că qubit 0 este bitul cel mai din dreapta în șirurile de biți. Exemplu: 01|01\rangle înseamnă că q0 este 1|1\rangle și q1 este 0|0\rangle. Fii atent, deoarece unele lucrări din computația cuantică folosesc notația Big Endian (qubit 0 este bitul cel mai din stânga), la fel ca o mare parte din literatura de mecanică cuantică.

Un alt lucru de observat este că atunci când reprezentăm un circuit cuantic, q0|q_0\rangle este întotdeauna plasat în partea de sus a circuitului. Ținând cont de aceasta, starea cuantică a circuitului de mai sus poate fi scrisă ca un produs tensorial de stări cuantice cu un singur qubit.

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 )

Starea inițială a Qiskit este 00=00|0\rangle|0\rangle=|00\rangle, deci prin aplicarea HH pe fiecare qubit, se modifică la o stare de superpozție egală.

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)

Regula de măsurare este aceeași ca în cazul unui singur qubit; probabilitatea de a măsura 00|00\rangle este ac2|ac|^2.

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Output of the previous code cell

Acum, hai să măsurăm acest 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")

Output of the previous code cell

Acum vom folosi din nou simulatorul Aer pentru a verifica experimental că probabilitățile relative ale tuturor stărilor de ieșire posibile sunt aproximativ egale.

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

Output of the previous code cell

Conform așteptărilor, stările 00|00\rangle, 01|01\rangle, 10|10\rangle, 11|11\rangle au fost măsurate cu aproximativ 25% fiecare.

4.2 Porțile cuantice cu mai mulți qubiți

Poarta CNOT

O poartă CNOT (din engleza „controlled NOT" sau CX) este o poartă cu doi qubiți, ceea ce înseamnă că acțiunea ei implică doi qubiți simultan: qubit-ul de control și qubit-ul țintă. CNOT inversează qubit-ul țintă numai când qubit-ul de control este 1|1\rangle.

Intrare (țintă, control)Ieșire (țintă, control)
0000
0111
1010
1101

Hai să simulăm mai întâi acțiunea acestei porți cu doi qubiți când q0 și q1 sunt ambii 0|0\rangle și să obținem statevector-ul de ieșire. Sintaxa Qiskit folosită este qc.cx(qubit control, qubit țintă).

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

Output of the previous code cell

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

Conform așteptărilor, aplicarea unei porți CNOT pe 00|00\rangle nu a schimbat starea, deoarece qubit-ul de control era în starea 0|0\rangle. Hai să revenim la operația noastră CNOT. De data aceasta vom aplica o poartă CNOT pe 01|01\rangle și să vedem ce se întâmplă.

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

Output of the previous code cell

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

Prin aplicarea unei porți CNOT, starea 01|01\rangle a devenit acum 11|11\rangle.

Hai să verificăm aceste rezultate rulând circuitul pe un simulator.

# Add measurements
qc.measure(0, 0)
qc.measure(1, 1)

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

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

Output of the previous code cell

Rezultatele ar trebui să îți arate că 11|11\rangle a fost măsurat cu o probabilitate de 100%.

4.3 Entanglementul cuantic și execuția pe un dispozitiv cuantic real

Hai să începem prin a introduce o stare entanglată specifică care este deosebit de importantă în computația cuantică, după care vom defini termenul „entanglat":

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

și această stare se numește stare Bell.

O stare entanglată este o stare ψAB|\psi_{AB}\rangle formată din stările cuantice ψA|\psi_A\rangle și ψB|\psi_B\rangle care nu poate fi reprezentată ca un produs tensorial al stărilor cuantice individuale. Dacă ψAB|\psi_{AB}\rangle de mai jos are două stări ψA|\psi\rangle_A și ψ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

produsul tensorial al acestor două stări este următorul:

ψ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

dar nu există coeficienți a0,a1,b0a_0, a_1, b_0 și b1b_1 care să satisfacă aceste două ecuații. Prin urmare, ψAB|\psi_{AB}\rangle nu este reprezentat ca un produs tensorial al stărilor cuantice individuale ψA|\psi\rangle_A și ψB|\psi\rangle_B, iar aceasta înseamnă că ψAB=12(00+11)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) este o stare entanglată.

Hai să creăm starea Bell și să o rulăm pe un calculator cuantic real. Vom urmări acum cei patru pași pentru scrierea unui program cuantic, numiți Qiskit patterns (tipare Qiskit):

  1. Mapează problema pe circuite și operatori cuantici
  2. Optimizează pentru hardware-ul țintă
  3. Execută pe hardware-ul țintă
  4. Post-procesează rezultatele

Pasul 1. Mapează problema pe circuite și operatori cuantici

Într-un program cuantic, circuitele cuantice sunt formatul nativ în care se reprezintă instrucțiunile cuantice. Când creezi un circuit, vei crea de obicei un nou obiect QuantumCircuit, apoi vei adăuga instrucțiuni la acesta în secvență.

Celula de cod de mai jos creează un circuit care produce o stare Bell, starea specifică entanglată cu doi qubiți de mai sus.

qc = QuantumCircuit(2, 2)

qc.h(0)
qc.cx(0, 1)

qc.measure(0, 0)
qc.measure(1, 1)

qc.draw("mpl")

Output of the previous code cell

Pasul 2. Optimizează pentru hardware-ul țintă

Qiskit convertește circuitele abstracte în circuite QISA (Quantum Instruction Set Architecture) care respectă constrângerile hardware-ului țintă și optimizează performanța circuitului. Deci, înainte de optimizare, vom specifica hardware-ul țintă. Dacă nu ai qiskit-ibm-runtime, va trebui să îl instalezi mai întâi. Pentru mai multe informații despre Qiskit Runtime, consultă referința API.

# Install
# !pip install qiskit-ibm-runtime

Vom specifica hardware-ul țintă.

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)

Transpilarea circuitului este un alt proces complex. Pe scurt, aceasta rescrie circuitul într-unul echivalent logic folosind „porți native" (porți pe care un anumit calculator cuantic le poate implementa) și mapează qubiții din circuitul tău pe qubiții reali optimi de pe calculatorul cuantic țintă. Pentru mai multe informații despre transpilare, consultă această documentație.

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

Poți vedea că în transpilare circuitul a fost rescris folosind noi porți. Pentru mai multe informații, consultă documentația ECRGate.

Pasul 3. Execută circuitul țintă

Acum vom rula circuitul țintă pe dispozitivul real.

sampler = Sampler(backend)
job_real = sampler.run([target_circuit])

job_id = job_real.job_id()
print("job id:", job_id)

Execuția pe dispozitivul real poate necesita așteptarea în coadă, deoarece calculatoarele cuantice sunt resurse valoroase și foarte solicitate. job_id este folosit pentru a verifica statusul execuției și rezultatele jobului mai târziu.

# Check the job status (replace the job id below with your own)
job_real.status(job_id)

Poți verifica și statusul jobului din dashboard-ul 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())

Pasul 4. Post-procesează rezultatele

În final, trebuie să post-procesăm rezultatele pentru a crea ieșiri în formatul așteptat, cum ar fi valori sau grafice.

plot_histogram(result_real[0].data.c.get_counts())

După cum poți vedea, 00|00\rangle și 11|11\rangle sunt cele mai frecvent observate. Există câteva rezultate altele decât datele așteptate, și acestea se datorează zgomotului și decoherenței qubiților. Vom afla mai multe despre erori și zgomot în calculatoarele cuantice în lecțiile ulterioare ale acestui curs.

4.4 Starea GHZ

Conceptul de entanglement poate fi extins la sisteme cu mai mult de doi qubiți. Starea GHZ (starea Greenberger-Horne-Zeilinger) este o stare maximal entanglată de trei sau mai mulți qubiți. Starea GHZ pentru trei qubiți este definită astfel:

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

Aceasta poate fi creată cu următorul circuit cuantic.

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

Output of the previous code cell

„Adâncimea" unui circuit cuantic este o metrică utilă și des folosită pentru a descrie circuitele cuantice. Trasează o cale prin circuitul cuantic, mergând de la stânga la dreapta, schimbând qubiții numai atunci când aceștia sunt conectați printr-o poartă cu mai mulți qubiți. Numără numărul de porți de-a lungul acelei căi. Numărul maxim de porți pentru orice astfel de cale printr-un circuit reprezintă adâncimea. În calculatoarele cuantice moderne cu zgomot, circuitele cu adâncime mică au mai puține erori și sunt mai susceptibile să returneze rezultate bune. Circuitele foarte adânci nu sunt.

Folosind QuantumCircuit.depth(), putem verifica adâncimea circuitului nostru cuantic. Adâncimea circuitului de mai sus este 4. Qubit-ul de sus are doar trei porți, inclusiv măsurarea. Dar există o cale de la qubit-ul de sus în jos la qubit-ul 1 sau qubit-ul 2 care implică o altă poartă CNOT.

qc.depth()
4

Exercițiul 2

Starea GHZ a unui sistem de 8 qubiți este:

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

Scrie cod pentru a pregăti această stare cu cel mai superficial circuit posibil. Adâncimea celui mai superficial circuit cuantic este 5, inclusiv porțile de măsurare.

Soluție:

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

Output of the previous code cell

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}

Output of the previous code cell

5. Rezumat

Ai învățat computația cuantică cu modelul de circuit, folosind biți cuantici și porți, și ai recapitulat superpozția, măsurarea și entanglementul. Ai aflat și metoda de executare a circuitului cuantic pe dispozitivul cuantic real.

În exercițiul final de creare a unui circuit GHZ, ai încercat să reduci adâncimea circuitului, care este un factor important pentru obținerea unei soluții la scară utilă într-un calculator cuantic cu zgomot. În lecțiile ulterioare ale acestui curs, vei învăța despre zgomot și despre metodele de mitigare a erorilor în detaliu. În această lecție, ca introducere, am considerat reducerea adâncimii circuitului pe un dispozitiv ideal, dar în realitate trebuie să luăm în considerare constrângerile dispozitivului real, cum ar fi conectivitatea qubiților. Vei afla mai multe despre acest lucru în lecțiile ulterioare ale acestui curs.

# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'