Sari la conținutul principal

Începe cu Qiskit în sala de clasă

Pentru acest modul Qiskit în Săli de Clasă, studenții trebuie să aibă un mediu Python funcțional cu următoarele pachete instalate:

  • qiskit v2.1.0 sau mai nou
  • qiskit-ibm-runtime v0.40.1 sau mai nou
  • qiskit-aer v0.17.0 sau mai nou
  • qiskit.visualization
  • numpy
  • pylatexenc

Pentru a configura și instala pachetele de mai sus, consultă ghidul Instalează Qiskit. Pentru a rula joburi pe calculatoare cuantice reale, studenții vor trebui să își creeze un cont IBM Quantum® urmând pașii din ghidul Configurează-ți contul IBM Cloud®.

Acest modul a fost testat și a folosit 2 secunde de timp QPU pe un procesor Heron v2. Aceasta este doar o estimare. Utilizarea ta reală poate varia.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'

Introducere

În modulele Qiskit în Sala de Clasă, vei avea ocazia să folosești un calculator cuantic pentru a explora diverse concepte din domenii adiacente calculului cuantic, cum ar fi mecanica cuantică, informatica, chimia și altele. Acest modul este o condiție prealabilă pentru celelalte — el introduce fundamentele calculului cuantic și modul în care se folosește Qiskit pentru a rula circuite cuantice.

Mai întâi îți vom oferi o scurtă prezentare a modului în care funcționează un calculator clasic, apoi îți vom arăta cum sunt adaptate aceste concepte pentru paradigma calculului cuantic. În final, îți vom arăta cum să combini aceste concepte pentru a construi și executa primul tău circuit cuantic.

Calculatoare clasice

Probabil că ești familiarizat cu bazele modului în care funcționează calculatoarele clasice, dar aici vom evidenția câteva dintre caracteristicile cheie, astfel încât să putem face apoi o comparație cu calculatoarele cuantice.

Unitățile de bază ale informației: biții

Calculatoarele clasice procesează informație clasică, iar unitatea fundamentală a informației clasice este bitul. Un singur bit poate stoca răspunsul la o întrebare de tipul „da/nu". De obicei, reprezentăm cele două stări binare ale unui bit ca „0" și „1".

Recapitulare numerele binare

Combinarea biților îți permite să stochezi mai multă informație. De exemplu, dacă vrei să stochezi un număr de la 0 la 15, poți face acest lucru cu patru biți în felul următor:

0 = 00004 = 01008 = 100012 = 1100
1 = 00015 = 01019 = 100113 = 1101
2 = 00106 = 011010 = 101014 = 1110
3 = 00117 = 011111 = 101115 = 1111

În general, pentru a converti un număr binar de NN biți într-un număr familiar în baza 10, înmulțești bitul cel mai puțin semnificativ (cel mai din dreapta) cu 20=12^0 = 1, următorul bit din stânga cu 21=22^1 = 2, apoi pe următorul cu 22=42^2 = 4, și tot așa, până ajungi la bitul cel mai semnificativ (cel mai din stânga), pe care îl înmulțești cu 2N12^{N-1}.

Deci, asta înseamnă că NN biți pot fi în una dintre 2N2^N stări posibile diferite.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă triunghiul pentru a dezvălui soluția.

De câți biți ai nevoie pentru a reprezenta numărul 86? Scrie șirul de biți care codifică acest număr în binar.

Răspuns:

Reține că NN biți îți permit să reprezinți numerele de la 00 la 2N12^N - 1, deci folosind șase biți am ajunge până la 261=632^6 - 1 = 63. Nu este destul. Mai adăugăm un bit pentru a ajunge la 271=1272^7 - 1 = 127. Acum să descompunem 86 în puteri ale lui 2:

86=64+16+4+2=26×1+25×0+24×1+23×0+22×1+21×1+20×0=1010110\begin{aligned} 86 &= 64 + 16 + 4 + 2 \\ &= 2^6 \times 1 + 2^5 \times 0 + 2^4 \times 1 + 2^3 \times 0 + 2^2 \times 1 + 2^1 \times 1 + 2^0 \times 0 \\ &= 1010110 \end{aligned}

Operații fundamentale: Gate-uri

Acum, un calculator trebuie să poată face ceva cu biții pentru a putea, ei bine, calcula. Gate-urile binare sunt operațiile care formează blocurile de construcție fundamentale ale tuturor algoritmilor și codurilor mai complexe.

Gate cu un singur bit:

NOT

Când ai un singur bit, există o singură modalitate de a-i transforma starea: inversezi starea din 0 în 1 sau din 1 în 0. Aceasta se numește poarta „NOT". Efectul acestei porți — și al celorlalte porți pe care le vom discuta mai jos — poate fi reprezentat într-un așa-numit „tabel de adevăr", cu coloane pentru stările de intrare și de ieșire ale qubiților. Tabelul de adevăr pentru poarta NOT este:

IntrareIeșire
01
10

Porți multi-bit:

AND

AND este o poartă cu doi biți care primește doi biți de intrare și produce un singur bit de ieșire. Produce 1 dacă ambii biți de intrare sunt 1, și 0 în caz contrar:

IntrareIeșire
000
010
100
111

OR

OR este o altă poartă cu doi biți și un singur bit de ieșire. Produce 1 dacă oricare dintre biți este 1:

IntrareIeșire
000
011
101
111

XOR

XOR vine de la „OR exclusiv" și seamănă cu poarta OR, dar produce 1 dacă doar unul dintre biții de intrare este 1. Produce 0 dacă ambii sunt 1 sau ambii sunt 0:

IntrareIeșire
000
011
101
110

Măsurători:

De obicei, când înveți despre calculul clasic, nu se acordă prea multă atenție procesului de citire a stării biților. Acest lucru se datorează faptului că nu este foarte complex din punct de vedere conceptual. Poți măsura biții oricând — înainte, în timpul sau după o calcul — fără a afecta rezultatul. Acesta nu este cazul în calculul cuantic, după cum vom discuta mai jos.

Circuite:

Combinând porțile de mai sus, poți efectua orice fel de operație pe un calculator. Să luăm un exemplu simplu: folosind porțile AND și XOR, poți construi circuitul semi-sumator, care calculează suma a doi biți. Aceasta este reprezentată într-o diagramă de circuit logic, unde firele reprezintă biții, iar porțile care operează asupra biților sunt afișate ca simboluri pe firele corespunzătoare:

Diagrama circuitului clasic pentru circuitul semi-sumator. O poartă XOR generează bitul de ieșire Sumă, iar o poartă AND generează bitul de ieșire Transport.

Astfel, cei doi biți sunt copiați și trecuți prin ambele porți, AND și XOR. Rezultatul porții XOR este „bitul de sumă" (S), care rămâne în locul unităților din numărul binar, iar rezultatul porții AND este „bitul de transport" (C), care reprezintă valoarea cifrei imediat mai semnificative din numărul binar. Iată tabelul de adevăr:

AABBSumă (ABA \oplus B)Transport (ABA \wedge B)
0000
0110
1010
1101

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă pe triunghi pentru a dezvălui soluția.

Verifică că tabelul de adevăr de mai sus produce soluția corectă pentru un circuit sumator. Adică, pentru fiecare dintre cele patru combinații ale lui A și B, verifică că A+B=S+2×CA+B=S+2 \times C.

Răspuns:

0+0=0+0=0 0+1=1+0=1 1+0=1+0=1 1+1=0+2=2 \begin{aligned} 0+0 &= 0+0 = 0 ~\checkmark \\ 0+1 &= 1+0 = 1 ~\checkmark \\ 1+0 &= 1+0 = 1 ~\checkmark \\ 1+1 &= 0+2 = 2 ~\checkmark \\ \end{aligned}

Calculatoare cuantice

Biți \rightarrow qubiți

Așa cum biții sunt unitățile fundamentale ale informației clasice, biții cuantici, sau „qubiții", sunt unitățile fundamentale ale informației cuantice. Ca și bitul clasic, starea unui qubit poate fi fie 0, fie 1, pe care le notăm de obicei cu 0\vert 0\rangle și 1\vert 1\rangle. Dar, spre deosebire de bitul clasic, un bit cuantic poate fi și într-o suprapunere a ambelor stări 0\vert 0\rangle și 1\vert 1\rangle în același timp. În general, un qubit poate fi în orice stare ψ\vert \psi\rangle de forma:

ψ=c00+c11\vert \psi\rangle = c_0 \vert 0\rangle + c_1 \vert 1\rangle

unde c0c_0 și c1c_1 sunt amplitudini complexe cu c02+c12=1\vert c_0 \vert ^2+\vert c_1\vert ^2=1.

Faza cuantică

Deoarece c0c_0 și c1c_1 sunt numere complexe, fiecare poate fi scris ca ci=cieiϕic_i = \vert c_i\vert e^{i\phi_i}, unde ϕi\phi_i se numește faza. Dacă înmulțim întreaga stare cu același factor de fază global, nimic nu se schimbă fizic — aceasta se numește fază globală și nu are consecințe observabile.

Din acest motiv, este convențional să „scoatem în factor" eiϕ0e^{i\phi_0}, obținând:

ψ=c00+c1eiϕ1\vert \psi\rangle = \vert c_0\vert \vert 0\rangle + \vert c_1\vert e^{i\phi}\vert 1\rangle

unde ϕ=ϕ1ϕ0\phi = \phi_1-\phi_0 este faza relativă a stării cuantice, care are consecințe observabile.

Această fază joacă un rol foarte important în calculul cuantic și vei explora consecințele ei în modulele Qiskit in the Classroom ulterioare.

Qubiți multipli

În timp ce starea mai multor biți clasici se poate exprima simplu ca un șir de 0-uri și 1-uri, starea mai multor qubiți devine un pic mai complicată datorită principiilor superpoziției și entanglementului.

Amintește-ți că NN biți clasici pot fi într-una din 2N2^N stări posibile, de la numărul binar 000...000 la 111...111. Dar acum, datorită principiului superpoziției, NN qubiți pot fi într-o superpoziție a tuturor acestor stări simultan!

Aceasta se poate exprima ca

ψN=i=02N1cii\psi_N = \sum_{i=0}^{2^N-1} c_i \vert i\rangle

unde, ca și în cazul clasic, starea i\vert i\rangle corespunde stării în care fiecare qubit se află în combinația potrivită de 0-uri și 1-uri pentru a produce numărul binar ii. Acestea sunt cunoscute drept „stările bazei computaționale" ale sistemului cuantic. De exemplu, o stare de trei qubiți poate fi scrisă ca o superpoziție a celor opt stări ale bazei computaționale:

ψ3=c0000+c1001+c2010+c3011+c4100+c5101+c6110+c7111\psi_3 = c_0 \vert 000\rangle + c_1 \vert 001\rangle + c_2 \vert 010\rangle + c_3 \vert 011\rangle + c_4 \vert 100\rangle + c_5 \vert 101\rangle + c_6 \vert 110\rangle + c_7 \vert 111\rangle

Fiecare qubit din sistem este notat cu un index 00 până la N1N-1. Convenția este să citești stările qubiților de la dreapta la stânga, astfel încât starea qubitului 00 este cea mai din dreapta, iar starea qubitului N1N-1 este cea mai din stânga. Aceasta este cunoscută drept notație „little-endian" și poate părea neintuitivă la început, deoarece suntem obișnuiți să citim de la stânga la dreapta.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi dă clic pe triunghi pentru a dezvălui soluția.

La prima vedere, poate părea neintuitivă ordonarea qubiților de la dreapta la stânga, ca în notația little-endian, dar este de fapt un lucru foarte logic! Explică de ce. (Amintește-ți discuția noastră de mai sus despre conversia din binar în baza 10.)

Răspuns:

Dacă ordonăm qubiții de la dreapta la stânga, astfel încât qubit 0 este cel mai din dreapta și qubit N-1 este cel mai din stânga, este logic să asociem qubit 00 cu bitul cel mai puțin semnificativ, care se înmulțește cu 202^0, și qubit N1N-1 cu bitul cel mai semnificativ, care se înmulțește cu 2N12^{N-1}.

Entanglementul

Așa cum am menționat mai devreme, o altă caracteristică esențială a qubiților este că pot fi entanglați unul cu celălalt. Să luăm exemplul unei stări de doi qubiți, unde c0=c3=12c_0 = c_3 = \frac{1}{\sqrt{2}} și c1=c2=0c_1 = c_2 = 0:

ψ=12(00+11)\vert \psi\rangle = \frac{1}{\sqrt{2}}(\vert 00\rangle + \vert 11\rangle)

Astfel, starea qubitului 0 poate fi fie 0\vert 0\rangle, fie 1\vert 1\rangle cu probabilitate egală, la fel și starea qubitului 1. Dar aceste probabilități nu mai sunt independente una de cealaltă. Dacă găsim starea qubitului 0 ca fiind 0\vert 0\rangle, atunci știm că și qubit 1 va fi în 0\vert 0\rangle. Acest lucru este valabil indiferent cât de departe sunt unul de celălalt, motiv pentru care actul de măsurare a unei stări entanglate este uneori numit „acțiune fantomatică la distanță".

Entanglementul poate lua și alte forme. De exemplu, starea

ψ=12(01+10)\vert \psi\rangle = \frac{1}{\sqrt{2}}(\vert 01\rangle + \vert 10\rangle)

produce rezultate opuse de fiecare dată: dacă un qubit este măsurat ca 0\vert 0\rangle, celălalt este garantat să fie găsit în starea 1\vert 1\rangle.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi dă clic pe triunghi pentru a dezvălui soluția.

Este starea ψ=11\vert \psi\rangle = \vert 11\rangle entanglată? De ce sau de ce nu?

Răspuns:

Nu este entanglată. Deși rezultatele sunt mereu aceleași când măsori ambii qubiți, aceasta se datorează doar faptului că fiecare qubit este mereu fixat în starea 1\vert 1\rangle. Rezultatul măsurării unui qubit nu depinde de fapt de celălalt — ambii sunt pur și simplu mereu 1\vert 1\rangle.

În general, dacă poți descrie starea fiecărui qubit separat și apoi le poți înmulți împreună astfel:

ψ=ψ1ψ0\vert \psi\rangle = \vert \psi_1\rangle \vert \psi_0\rangle

Atunci aceasta este cunoscută ca o „stare produs" și nu este entanglată.

Notația vectorială

Este adesea util să folosești vectori și matrice pentru a vedea cum se transformă starea cuantică sub diferite operații. În această reprezentare, stările noastre cuantice vor fi vectori, iar porțile noastre cuantice (discutate în secțiunea următoare) vor fi matrice care transformă vectorii.

Pentru un singur qubit, forma vectorială a stărilor este aleasă astfel: 0=(10)\vert 0\rangle = \begin{pmatrix}1 \\ 0\end{pmatrix} 1=(01)\vert 1\rangle = \begin{pmatrix}0 \\ 1\end{pmatrix} În acest fel, o stare arbitrară ψ=a0+b1\vert \psi\rangle = a\vert 0\rangle+b\vert 1\rangle poate fi scrisă ca ψ=(ab)\vert \psi\rangle =\begin{pmatrix}a \\ b\end{pmatrix}

Pentru o stare generală de nn qubiți, vom avea nevoie de un vector de dimensiune 2n2^n, cu stările bazei ordonate după cum te-ai aștepta, în valoare binară crescătoare:

0000=(1000),0001=1110=(0010),1111=(0001)\vert 0 \dots 000\rangle = \begin{pmatrix}1 \\ 0 \\ 0 \\ \vdots \\ 0\end{pmatrix}, \vert 0 \dots 001 \rangle = \vert 1 \dots 110\rangle = \begin{pmatrix}0 \\ \vdots \\ 0 \\ 1 \\ 0\end{pmatrix}, \vert 1 \dots 111 \rangle = \begin{pmatrix}0 \\ \vdots \\ 0 \\ 0\\ 1\end{pmatrix}

Ținând cont de această alegere a notației vectoriale, putem introduce porțile cuantice necesare, efectele lor asupra stărilor cuantice și formele lor matriceale.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă pe triunghi pentru a vedea soluția.

Există patru stări ale bazei computaționale pentru un sistem cu doi qubiți. Scrie fiecare dintre ele atât în notație ket, cât și în notație vectorială.

Răspuns:

00=(1000),01=(0100),,10=(0010),11=(0001)\vert 00\rangle = \begin{pmatrix}1 \\ 0 \\ 0 \\ 0\end{pmatrix}, \vert 01 \rangle = \begin{pmatrix}0 \\ 1 \\ 0 \\ 0\end{pmatrix}, \dots, \vert 10\rangle = \begin{pmatrix}0 \\ 0 \\ 1 \\ 0\end{pmatrix}, \vert 11 \rangle = \begin{pmatrix}0 \\ 0 \\ 0\\ 1\end{pmatrix}

Porți \rightarrow porți cuantice

La fel cum porțile clasice precum NOT, AND, OR și XOR pot fi combinate pentru a construi circuite clasice arbitrare, porțile cuantice joacă același rol în calculul cuantic. Deoarece qubiții au caracteristici mecanice cuantice suplimentare, porțile cuantice sunt corespunzător mai bogate. Deși putem în continuare să le descriem acțiunea asupra stărilor de bază 0|0\rangle și 1|1\rangle cu o tabelă de adevăr, aceasta nu surprinde întreaga imagine. Pentru porțile cuantice, este adesea mai natural să folosim o reprezentare matriceală, deoarece ele acționează și asupra superpozițiilor de stări de bază.

Mai jos vom prezenta cele mai comune porți cuantice și modul în care transformă qubiții cu care interacționează. Acolo unde este cazul, le vom conecta cu porțile clasice familiare.

Porți cu un singur qubit

Poarta XX: Aceasta este echivalentul cuantic al operației NOT. Tabela sa de adevăr arată exact ca poarta clasică NOT:

IntrareIeșire
0\vert 0\rangle1\vert 1\rangle
1\vert 1\rangle0\vert 0\rangle

Și reprezentarea matriceală:

X=(0110)X=\begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}

În Qiskit, crearea unui Circuit cu o poartă XX arată astfel:

from qiskit import QuantumCircuit

qc = QuantumCircuit(1)
qc.x(0)
qc.draw("mpl")

Output of the previous code cell

În această diagramă de Circuit foarte simplă, qubitul este reprezentat printr-un fir, linia orizontală neagră, iar poarta apare ca o casetă pe acel fir.

Poarta Hadamard: Creează o stare de superpozitie. Tabela de adevăr:

IntrareIeșire
0\vert 0\rangle12(0+1)\frac{1}{\sqrt{2}}\left(\vert 0\rangle+\vert 1\rangle\right)
1\vert 1\rangle12(01)\frac{1}{\sqrt{2}}\left(\vert 0\rangle-\vert 1\rangle\right)

Reprezentarea matriceală: H=12(1111)H=\frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}

Un Circuit cu o poartă Hadamard se creează astfel:

from qiskit import QuantumCircuit

qc = QuantumCircuit(1)
qc.h(0)
qc.draw("mpl")

Output of the previous code cell

Poarta ZZ: Adaugă o schimbare de fază de Δϕ=π\Delta \phi = \pi stării 1|1\rangle:

IntrareIeșire
0\vert 0\rangle0\vert 0\rangle
1\vert 1\rangle1-\vert 1\rangle

Z=(1001)Z=\begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}

În Qiskit, crearea unui Circuit cu o poartă ZZ arată astfel:

qc = QuantumCircuit(1)
qc.z(0)
qc.draw("mpl")

Output of the previous code cell

Poarta TT: Adaugă o schimbare de fază de Δϕ=π/4\Delta \phi = \pi/4 stării 1|1\rangle:

IntrareIeșire
0\vert 0\rangle0\vert 0\rangle
1\vert 1\rangleeiπ/41e^{i\pi/4}\vert 1\rangle

T=(100eiπ/4)T=\begin{pmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{pmatrix}

În Qiskit, crearea unui Circuit cu o poartă TT arată astfel:

qc = QuantumCircuit(1)
qc.t(0)
qc.draw("mpl")

Output of the previous code cell

Porți cu mai mulți qubiți

Porțile cu doi qubiți pot semăna cu porțile clasice cu două biți, dar cu o rezervă importantă: toate porțile cuantice trebuie să fie reversibile. În termeni de algebră liniară, aceasta înseamnă că sunt reprezentate de matrice unitare. Astfel, doi qubiți de intrare se mapează întotdeauna la doi qubiți de ieșire, iar operația poate, în principiu, să fie anulată. Aceasta contrastează cu porțile clasice pe care le-am văzut mai sus, precum AND sau OR, care pierd informație și sunt ireversibile — dat fiind un rezultat, nu poți determina în mod unic intrarea.

Poarta CNOT (Controlled-NOT): Cei doi qubiți de intrare se numesc qubiți „de control" și „țintă". Qubitul de control rămâne neschimbat, dar starea sa dictează ce se întâmplă cu qubitul țintă. Dacă qubitul de control se află în starea 1\vert 1\rangle, atunci se aplică o poartă XX acelei ținte; dacă starea qubitului de control este 0\vert 0\rangle, nu se face nicio modificare. În notația de mai jos, presupunem că qubitul AA (qubitul cel mai din dreapta) este controlul, iar qubitul BB (qubitul cel mai din stânga) este ținta. Mai jos, notația folosită este CNOT(qcontrol,qtarget)BA.CNOT(q_{control},q_{target})\vert BA\rangle.

CNOT(A,B)BAinput=BAoutputCNOT(A,B)\vert BA\rangle_{input} = \vert BA\rangle_{output}

IntrareIeșire
00\vert 00\rangle00\vert 00\rangle
01\vert 01\rangle11\vert 11\rangle
10\vert 10\rangle10\vert 10\rangle
11\vert 11\rangle01\vert 01\rangle

Astfel, matricea care reprezintă această acțiune este:

CNOT=(1000000100100100)CNOT=\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix}

qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.draw("mpl")

Rezultatul celulei de cod anterioare

Acesta este primul Circuit diagram pe care îl vedem cu doi qubiți, reprezentați de cele două fire. Poarta CNOT este implementată între cei doi qubiți, cu q0q_0 ca control și q1q_1 ca țintă.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă pe triunghi pentru a dezvălui soluția.

Majoritatea porților au aceeași formă matriceală în Qiskit ca oriunde altundeva. Dar poarta CNOT acționează pe doi qubiți, și astfel convențiile de ordonare ale qubiților devin brusc o problemă. Textele care ordonează qubiții q0,q1,...\vert q_0,q_1,...\rangle vor arăta o formă matriceală diferită pentru porțile lor CNOT. Verifică prin înmulțire matriceală explicită că matricea CNOT de mai sus are acțiunea corectă asupra stării 01.\vert 01\rangle.

Răspuns:

CNOT01=(1000000100100100)(0100)=(0001)=11CNOT\vert 01\rangle =\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix}\begin{pmatrix}0 \\ 1 \\ 0 \\0\end{pmatrix} = \begin{pmatrix}0 \\ 0 \\ 0 \\1\end{pmatrix} = \vert 11\rangle

Poarta SWAP: Această poartă schimbă stările a doi qubiți. Tabelul de adevăr:

IntrareIeșire
00\vert 00\rangle00\vert 00\rangle
01\vert 01\rangle10\vert 10\rangle
10\vert 10\rangle01\vert 01\rangle
11\vert 11\rangle11\vert 11\rangle

Astfel, matricea care reprezintă această acțiune este:

SWAP=(1000001001000001)SWAP=\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1\end{pmatrix}

qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.draw("mpl")

Rezultatul celulei de cod anterioare

Poarta SWAP poate fi de fapt construită din trei CNOTuri. Pentru a vedea cum, putem decompose() poarta cu Qiskit:

qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")

Rezultatul celulei de cod anterioare

Aici vedem pentru prima dată cum sunt afișate mai multe porți într-un Circuit diagram. Îl citim de la stânga la dreapta, deci poarta cea mai din stânga se aplică prima.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă pe triunghi pentru a dezvălui soluția.

Verifică faptul că combinația de CNOT-uri de mai sus dă ca rezultat o poartă SWAP. Poți face asta prin înmulțire de matrice sau prin orice altă metodă.

Răspuns:

Prin înmulțire de matrice:

(1000000100100100)(1000010000010010)(1000000100100100)=(1000001001000001)=SWAP \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix} \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0\end{pmatrix} \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1\end{pmatrix} = SWAP ~\checkmark

Folosind un tabel de adevăr pentru a vedea cum se modifică stările cu fiecare CNOT. În ultima coloană, stările ar trebui să fie echivalente cu coloana „ieșire" a tabelului de adevăr SWAP:

IntrareCNOT(A,B)CNOT(B,A)CNOT(A,B)
00\vert 00\rangle00\vert 00\rangle00\vert 00\rangle00\vert 00\rangle \checkmark
01\vert 01\rangle11\vert 11\rangle10\vert 10\rangle10\vert 10\rangle \checkmark
10\vert 10\rangle10\vert 10\rangle11\vert 11\rangle01\vert 01\rangle \checkmark
11\vert 11\rangle01\vert 01\rangle01\vert 01\rangle11\vert 11\rangle \checkmark

Poarta Toffoli (sau „controlled-controlled-NOT" (CCNOT)): Aceasta este o poartă cu trei qubiți. Denumirea „controlled-controlled-NOT" îți poate deja sugera cum funcționează: există doi qubiți de control și un qubit țintă, iar starea qubitului țintă este inversată numai dacă ambii qubiți de control se află în starea 1\vert 1\rangle. Păstrăm convenția de ordonare folosită la CNOT:

CCNOT(ControlA,ControlB,TargetC)CBACCNOT(Control A, Control B, Target C)\vert CBA\rangle

Astfel, tabelul de adevăr este:

IntrareIeșire
000\vert 000\rangle000\vert 000\rangle
001\vert 001\rangle001\vert 001\rangle
010\vert 010\rangle010\vert 010\rangle
011\vert 011\rangle111\vert 111\rangle
100\vert 100\rangle100\vert 100\rangle
101\vert 101\rangle101\vert 101\rangle
110\vert 110\rangle110\vert 110\rangle
111\vert 111\rangle011\vert 011\rangle

Iar matricea care reprezintă această acțiune este:

CCNOT=(1000000001000000001000000000000100001000000001000000001000010000)CCNOT=\begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\end{pmatrix}
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.draw("mpl")

Output of the previous code cell

Poarta Toffoli poate fi și ea descompusă în CNOT-uri, împreună cu alte porți. Cu toate acestea, este semnificativ mai complicată decât descompunerea porții SWAP, astfel că va fi lăsată ca exercițiu opțional la sfârșitul modulului pentru a explora și verifica această descompunere.

Măsurători

Măsurătorile joacă un rol special în calculul cuantic — unul care nu are un corespondent în calculul clasic. În timp ce în calculul clasic poți verifica biții în orice moment al algoritmului, în calculul cuantic trebuie să fii foarte selectiv în privința momentului în care privești qubitul, deoarece măsurătoarea colapsează starea acestuia și distruge superpozița care conferă qubiților complexitatea lor computațională.

În particular, dat fiind un stat cuantic cu NN biți ψ=i=02N1cii\vert \psi\rangle = \sum_{i=0}^{2^N-1} c_i \vert i\rangle, o măsurătoare va colpasa starea la una dintre funcțiile de bază i\vert i\rangle cu o probabilitate egală cu ci2\vert c_i\vert ^2.

Dar acest efect distructiv al unei măsurători nu este întotdeauna un impediment. Este de fapt o resursă cheie în anumiți algoritmi și protocoale, cum ar fi teleportarea cuantică și distribuția cuantică a cheilor.

În Qiskit, atunci când se efectuează o măsurătoare, aceasta este trimisă într-un registru clasic unde este stocată ca bit clasic. Crearea unui Circuit cu o măsurătoare arată astfel:

qc = QuantumCircuit(
1, 1
) # the second number is the number of classical bits in the circuit
qc.measure(0, 0)
qc.draw("mpl")

Output of the previous code cell

Circuite

Acum că știm cum funcționează qubiții, porțile și măsurătorile, hai să creăm și să executăm propriul nostru Circuit cuantic! Pentru aceasta, va trebui să-ți prezentăm un flux de lucru util numit Qiskit patterns.

Framework-ul Qiskit patterns

Framework-ul Qiskit patterns este o procedură generală pentru abordarea și rezolvarea problemelor cu un calculator cuantic. Constă din patru pași:

  1. Maparea problemei noastre pe Circuite și operatori cuantici
  2. Optimizarea Circuit-ului pentru hardware-ul țintă
  3. Executarea pe hardware-ul țintă
  4. Post-procesarea rezultatelor noastre

Pentru a ilustra acești pași, vom implementa o versiune cuantică a Circuit-ului half-adder discutat mai sus.

1. Mapare

Circuitul clasic de adunare folosește o poartă XOR și o poartă AND pentru a calcula, respectiv, bitul de sumă și bitul de transport. Putem adapta aceste porți în contextul cuantic pentru a construi adunătorul cuantic pe jumătate. Mai întâi, reamintind că porțile cuantice sunt reversibile, nu putem pur și simplu să suprascriem intrările. În schimb, introducem doi qubiți auxiliari inițializați la 0\vert 0\rangle pentru a stoca ieșirile de sumă și transport. Astfel, starea cuantică completă va consta din qubiții AA și BB, precum și qubiții de sumă și transport, pe care îi vom nota SS și CC:

ψ=CSBA\vert \psi\rangle = \vert C S B A\rangle

Acum avem nevoie de porți cuantice care să realizeze ceea ce au făcut porțile XOR și AND în circuitul clasic.

Sumă:

Pentru XOR, aplicăm două porți CNOT, fiecare cu qubiții de control AA și BB și qubitul țintă SS pentru ambele. Dacă AA și BB sunt diferiți, atunci una dintre porțile CNOT va inversa SS la starea 1\vert 1\rangle. Dacă AA și BB sunt amândoi 0\vert 0\rangle, atunci nu se întâmplă nimic cu SS și rămâne în starea 0\vert 0\rangle. Dacă AA și BB sunt amândoi 1\vert 1\rangle, atunci starea lui SS va fi inversată de două ori, aducând-o înapoi la starea 0\vert 0\rangle.

Transport:

Pentru bitul de transport, avem nevoie de ceva care funcționează ca poarta AND clasică.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă pe triunghi pentru a vedea soluția.

Uită-te înapoi la porțile pe care le-am discutat pentru a vedea dacă poți ghici ce poartă cuantică vom folosi în locul porții AND clasice:

Răspuns:

Este poarta Toffoli! Reamintind, poarta Toffoli, sau poarta controlled-controlled-NOT, inversează starea țintă dacă și numai dacă qubitul de control 0 ȘI qubitul de control 1 sunt ambii 1\vert 1\rangle. Deci, dacă qubitul țintă începe în starea 0\vert 0\rangle, atunci are aceeași acțiune ca poarta AND.

Deci, acum avem toate ingredientele necesare pentru a construi circuitul cuantic:

# qubits: a, b, sum, carry
qc = QuantumCircuit(4)

# Choose values for A and B:
a = 0
b = 0

# Prepare A and B qubits according to selected values:
if a:
qc.x(0)
if b:
qc.x(1)

# XOR (sum) into qubit 2
qc.cx(0, 2)
qc.cx(1, 2)

# AND (carry) into qubit 3
qc.ccx(0, 1, 3) # a AND b

# measure
qc.measure_all()

qc.draw("mpl")

Ieșirea celulei de cod anterioare

Mai sus este diagrama circuitului pentru circuitul cuantic al adunătorului pe jumătate. Așa cum s-a menționat anterior, firele reprezintă qubiții de la 00 la 33 ordonați de sus în jos, iar registrul de biți clasici este firul dublu de la bază. Apoi, citind de la stânga la dreapta, vedem cum se aplică porțile fiecărui qubit observând unde apar casetele pe firele corespunzătoare. În final, măsurătorile sunt afișate la sfârșit. Măsurătorile prăbușesc stările qubiților în valori definite 00 sau 11, iar rezultatele sunt trimise într-un registru clasic.

O subtilitate: deși diagrama circuitului este desenată de la stânga la dreapta, atunci când scriem expresia matriceală corespunzătoare trebuie să o citim de la dreapta la stânga. Aceasta deoarece în înmulțirea matriceală, operatorul cel mai apropiat de vectorul de stare acționează primul. Astfel, de exemplu, circuitul de mai sus (ignorând măsurătorile) ar fi scris ca:

CCNOT(q0,q1,q3)CNOT(q1,q2)CNOT(q0,q2)q3q2q1q0CCNOT(q_0,q_1,q_3)CNOT(q_1, q_2)CNOT(q_0,q_2)\vert q_3 q_2 q_1 q_0\rangle

2. Optimizarea:

Apoi, trebuie să optimizăm circuitul pentru a fi rulat pe hardware-ul cuantic. Această optimizare se realizează prin transpiler, care traduce circuitul abstract prezentat mai sus în instrucțiuni pe care calculatorul cuantic le va înțelege. Acesta atribuie qubiții logici de mai sus unor qubiți fizici reali de pe procesor și rescrie porțile în funcție de setul propriu de porți native, optimizate pentru a rula pe calculatorul cuantic. În cele din urmă, transpilerul implementează și ceea ce se numește „suprimarea și mitigarea erorilor", pentru a minimiza efectul erorilor asupra rezultatului. Acest lucru nu este atât de important pentru circuitul nostru foarte simplu, dar dacă îți continui parcursul în domeniul informaticii cuantice și ajungi să rulezi circuite mai complexe, vei vedea curând valoarea suprimării și mitigării erorilor. Dacă vrei să afli mai multe despre acest subiect, consultă cursul lui Olivia Lane, Quantum Computing in Practice.

Mai întâi, încărcăm pachetele necesare pentru a comunica cu calculatoarele cuantice IBM® și selectăm un backend pe care să rulăm. Putem fie să alegem backend-ul cel mai puțin ocupat, fie să selectăm un backend specific ale cărui proprietăți le cunoaștem.

Mai jos există cod pentru salvarea acreditivelor la prima utilizare. Asigură-te că ștergi aceste informații din notebook după ce le-ai salvat în mediul tău, pentru ca acreditivele să nu fie partajate accidental atunci când distribui notebook-ul. Consultă Configurarea contului tău IBM Cloud și Inițializarea serviciului într-un mediu nesigur pentru mai multe îndrumări.

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

# Load the Qiskit Runtime service

# Syntax for first saving your token. Delete these lines after saving your credentials.
# QiskitRuntimeService.save_account(channel='ibm_quantum_platform', instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR-API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')

# Load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)
ibm_fez

Acum folosim transpilerul pentru a optimiza circuitul. Putem alege nivelul de optimizare de la 0 (fără optimizare) la 3 (optimizare maximă). Pentru a vedea ce presupune fiecare nivel, vizitează ghidul Setarea nivelului de optimizare al transpilerului. Circuitul rezultat va arăta semnificativ diferit față de circuitul logic creat în pasul de mapare.

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Rezultatul celulei de cod anterioare

Un „Sampler" este o primitivă concepută pentru a eșantiona stările posibile rezultate dintr-un Circuit cuantic și a colecta statistici despre ce stări ar putea fi măsurate și cu ce probabilitate. Importăm aici Sampler-ul Qiskit Runtime:

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

Dacă ai epuizat timpul alocat pe calculatoare cuantice reale sau dacă nu ai conexiune la internet, s-ar putea să preferi să folosești un simulator. Pentru a face acest lucru, rulează celula de mai jos și decomentează linia asociată în pasul „Execute".

# Load the backend sampler
from qiskit.primitives import BackendSamplerV2

# Load the Aer simulator and generate a noise model based on the currently-selected backend.
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel

noise_model = NoiseModel.from_backend(backend)

# Define a simulator using Aer, and use it in Sampler.
backend_sim = AerSimulator(noise_model=noise_model)
sampler_sim = BackendSamplerV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# sampler_gen = BackendSamplerV2(backend=backend_gen)

3. Executarea

După pregătirea circuitului, îl putem rula acum pe calculatorul cuantic!

job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

4. Post-procesare

Acum suntem gata să ne vizualizăm rezultatele! Vom afișa o histogramă a celor 100 de eșantioane ale circuitului.

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)
counts =  {'0000': 90, '0100': 4, '1100': 3, '0010': 3}

Rezultatul celulei de cod anterioare

Histograma de mai sus arată rezultatele măsurătorilor tuturor celor patru qubiți la sfârșitul circuitului. Un calculator cuantic ideal, fără zgomot, ar fi măsurat qubiții cu aceleași valori de fiecare dată, dar în realitate, zgomotul va face ca unele dintre rulări să producă erori.

Verifică-ți înțelegerea

Citește întrebarea (întrebările) de mai jos, gândește-te la răspuns, apoi apasă pe triunghi pentru a dezvălui soluția.

Folosind șirul de biți cu cele mai multe numărări ca valori pentru AA, BB, SS și CC, verifică că circuitul cuantic adunător a funcționat.

Răspuns:

Trebuie să verificăm că A+B=S+2×CA+B = S+2 \times C. Reține că ordinea șirului de biți urmează notația little-endian, deci se citește CSBA.

Din histograma de mai sus, vedem că șirul de biți 0000 este cel dominant.

0+0=0+0×2=0 0 + 0 = 0 + 0 \times 2 = 0 ~\checkmark

Întoarce-te și schimbă valorile lui AA și BB la A=1A=1 și B=1B=1 și parcurge din nou pașii Qiskit patterns pentru a rerula circuitul. Verifică că circuitul adunător a funcționat din nou.

Răspuns:

Ar trebui să obții o histogramă cu șirul de biți dominant 1011:

1+1=0+1×2=2 1 + 1 = 0 + 1 \times 2 = 2 ~\checkmark

Una dintre caracteristicile suplimentare ale jumătății de adunător cuantic față de cel clasic este că poate rula cu intrări cuantice. Adică poate „aduna" qubiții AA și BB chiar dacă se află în stări de superpoziție. În secțiunea Întrebări provocatoare de mai jos, ți se va cere să pregătești qubiții în superpoziții și să vezi ce se întâmplă!

Concluzie

Acest modul a fost conceput pentru a-ți oferi o înțelegere solidă a principiilor de bază din spatele calculului cuantic, comparându-l cu calculul clasic. Am analizat circuitul clasic al jumătății de adunător și apoi ți-am arătat cum să adaptezi circuitul pentru a rula cu qubiți pe un calculator cuantic. Acum ești pregătit(ă) să explorezi celelalte module Qiskit in the Classroom!

Concepte esențiale:

  • Spre deosebire de biții clasici, care pot lua doar valorile 0 și 1, qubiții pot fi și în stări de superpoziție ale lui 0 și 1.
  • Mai mulți qubiți pot fi în superpoziție față de șirurile de biți clasic permise, numite stări de bază computațională.
  • Mai mulți qubiți pot fi întrețesați astfel încât starea unuia depinde de starea celuilalt.
  • Convenția Qiskit este de a folosi notația little-endian, care plasează qubit-ul cel mai puțin semnificativ, q0q_0, în poziția cea mai din dreapta și qubit-ul cel mai semnificativ, qNq_N, la stânga.
  • Porțile cuantice sunt operații reversibile reprezentate de matrice unitare care acționează asupra vectorilor de stare cuantică. În această notație, matricea cea mai apropiată de vector (cea mai din dreapta) acționează prima.
  • Măsurătorile prăbușesc o stare de superpoziție cuantică într-una dintre stările sale clasic permise, cu o probabilitate egală cu pătratul amplitudinii stării de bază computaționale corespunzătoare din superpoziție.
  • Circuitele cuantice sunt adesea reprezentate folosind diagrame de circuit cuantic, unde qubiții sunt descriși ca fire orizontale, iar porțile cuantice apar de-a lungul acestor fire de la stânga la dreapta.
  • Pentru a rula un circuit cuantic, folosim cei patru pași din fluxul de lucru Qiskit patterns: Map, Optimize, Execute, Post-process.

Întrebări

Întrebări adevărat/fals

  1. Un singur bit dintr-un calculator clasic poate conține doar valoarea 0 sau 1.

  2. Întrețesarea înseamnă că starea unui qubit este independentă de starea altuia.

  3. Porțile cuantice sunt în general operații ireversibile.

  4. Convenția Qiskit plasează qubit-ul cel mai puțin semnificativ, q0q_0, în poziția cea mai din stânga.

  5. Măsurarea unei stări cuantice dă întotdeauna același rezultat dacă este repetată de mai multe ori.

  6. Poarta Hadamard creează superpoziție într-un singur qubit.

  7. Circuitele cuantice pot include operații de măsurare care prăbușesc starea de superpoziție într-una dintre stările clasic permise.

  8. Numărul de stări clasice posibile pentru NN biți este 2N2N.

  9. Probabilitățile rezultatelor pentru măsurătorile cuantice sunt date de amplitudinile la pătrat ale stărilor de bază măsurabile clasic.

Întrebări cu răspuns scurt

  1. Care sunt câteva dintre principalele diferențe dintre un bit și un qubit?

  2. Ce se întâmplă cu o stare cuantică atunci când este măsurată?

  3. De ce folosim notația little-endian în Qiskit?

  4. Care sunt cei patru pași din fluxul de lucru Qiskit patterns?

Întrebări provocatoare:

  1. În modul, am folosit adunătorul doar pentru a aduna stări clasic permise pentru AA și BB. Dar putem pregăti și AA și BB în superpoziții! Modifică codul pentru a pregăti fiecare qubit într-o superpoziție egală de 0 și 1, apoi rulează noul circuit și obține o nouă histogramă. Ce observi? Explică ce se întâmplă.

  2. Descompunerea porții Toffoli. Folosește decompose() pentru a arăta cum este descompusă poarta Toffoli în porți cu un singur qubit și cu doi qubiți, apoi verifică această construcție prin înmulțire de matrice. Reține că, deși diagramele de circuit se citesc de la stânga la dreapta, matricele se aplică stărilor cuantice de la dreapta la stânga!