Sari la conținutul principal

Implementare Qiskit

În această secțiune, vom analiza câteva implementări Qiskit ale conceptelor introduse în această lecție. Dacă dorești să rulezi aceste implementări tu însuți, ceea ce este recomandat cu insistență, consultă pagina Instalare Qiskit din IBM Quantum Documentation pentru detalii despre cum să configurezi Qiskit.

Trebuie înțeles că Qiskit este în continuă dezvoltare și este axat în principal pe maximizarea performanței calculatoarelor cuantice pe care le operează, care la rândul lor continuă să evolueze. Ca urmare, Qiskit este supus unor modificări care pot duce ocazional la deprecierea codului. Ținând cont de acest lucru, vom executa întotdeauna comenzile de mai jos înainte de a prezenta exemple de cod Qiskit în acest curs, pentru a fi clar care versiune de Qiskit a fost folosită. Începând cu Qiskit v1.0, aceasta este o modalitate simplă de a vedea ce versiune de Qiskit este instalată în prezent.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import __version__

print(__version__)
2.1.1

Dacă rulezi acest cod într-un mediu Python bazat pe cloud, este posibil să fie nevoie să instalezi unele dintre pachetele de mai jos:

#!pip install qiskit
#!pip install jupyter
#!pip install sympy
#!pip install matplotlib
#!pip install pylatexenc

Vectori și matrice în Python

Qiskit utilizează limbajul de programare Python, deci înainte de a discuta specific despre Qiskit, poate fi util să discutăm foarte pe scurt despre calculele cu matrice și vectori în Python.

În Python, calculele cu matrice și vectori pot fi efectuate folosind clasa array din biblioteca NumPy, care oferă funcționalitate pentru multe calcule numerice și științifice. Codul următor încarcă această bibliotecă, definește doi vectori coloană, ket0 și ket1, corespunzând vectorilor de stare qubit 0\vert 0\rangle și 1,\vert 1\rangle, și apoi afișează media lor.

import numpy as np

ket0 = np.array([[1], [0]])
ket1 = np.array([[0], [1]])

print(ket0 / 2 + ket1 / 2)
[[0.5]
[0.5]]

Putem folosi de asemenea array pentru a crea matrice care pot reprezenta operații.

M1 = np.array([[1, 1], [0, 0]])
M2 = np.array([[1, 0], [0, 1]])
M = M1 / 2 + M2 / 2
print(M)
[[1.  0.5]
[0. 0.5]]

Te rog să reții că tot codul care apare într-o lecție dată din acest curs trebuie rulat secvențial. Prin urmare, nu este nevoie să importăm NumPy din nou aici, deoarece a fost deja importat.

Înmulțirea matricelor, inclusiv înmulțirea matrice-vector ca un caz special, poate fi efectuată folosind funcția matmul din NumPy.

print(np.matmul(M1, ket1))
print(np.matmul(M1, M2))
print(np.matmul(M, M))
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

Această formatare a rezultatelor lasă de dorit din punct de vedere vizual. O soluție, pentru situațiile care necesită ceva mai estetic, este să folosești funcția array_to_latex din Qiskit, din modulul qiskit.visualization. Reține că, în codul care urmează, folosim funcția generică display din Python. Prin contrast, comportamentul specific al lui print poate varia în funcție de ce este afișat, cum se întâmplă pentru array-urile definite de NumPy.

from qiskit.visualization import array_to_latex

display(array_to_latex(np.matmul(M1, ket1)))
display(array_to_latex(np.matmul(M1, M2)))
display(array_to_latex(np.matmul(M, M)))
[10] \begin{bmatrix} 1 \\ 0 \\ \end{bmatrix} [1100] \begin{bmatrix} 1 & 1 \\ 0 & 0 \\ \end{bmatrix} [134014] \begin{bmatrix} 1 & \frac{3}{4} \\ 0 & \frac{1}{4} \\ \end{bmatrix}

Stări, măsurători și operații

Qiskit include mai multe clase care permit crearea și manipularea stărilor, măsurătorilor și operațiilor — deci nu este nevoie să programezi tot ce este necesar pentru a simula stări cuantice, măsurători și operații în Python. Mai jos sunt incluse câteva exemple pentru a te ajuta să începi.

Definirea și afișarea vectorilor de stare

Clasa Statevector din Qiskit oferă funcționalitate pentru definirea și manipularea vectorilor de stare cuantică. În codul care urmează, clasa Statevector este importată și câțiva vectori sunt definiți. (Importăm de asemenea funcția sqrt din biblioteca NumPy pentru a calcula o rădăcină pătrată. Această funcție ar putea fi apelată alternativ ca np.sqrt cu condiția că NumPy a fost deja importat, ceea ce s-a întâmplat mai sus; aceasta este pur și simplu o altă modalitate de a importa și utiliza doar această funcție specifică.)

from qiskit.quantum_info import Statevector
from numpy import sqrt

u = Statevector([1 / sqrt(2), 1 / sqrt(2)])
v = Statevector([(1 + 2.0j) / 3, -2 / 3])
w = Statevector([1 / 3, 2 / 3])

Clasa Statevector include o metodă draw pentru afișarea vectorilor de stare în diverse moduri, inclusiv text pentru text simplu, latex pentru LaTeX redat și latex_source pentru cod LaTeX, care poate fi util pentru a copia și lipi în documente. (Folosește print în loc de display pentru a afișa cod LaTeX cu cele mai bune rezultate.)

display(u.draw("text"))
display(u.draw("latex"))
print(u.draw("latex_source"))
[0.70710678+0.j,0.70710678+0.j]

220+221\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

Clasa Statevector include de asemenea metoda is_valid, care verifică dacă un vector dat este un vector de stare cuantică valid (cu alte cuvinte, că are norma euclidiană egală cu 1):

display(u.is_valid())
display(w.is_valid())
True
False

Simularea măsurătorilor folosind Statevector

În continuare, vom vedea o modalitate prin care măsurătorile stărilor cuantice pot fi simulate în Qiskit, folosind metoda measure din clasa Statevector. Să folosim același vector de stare qubit v definit anterior.

display(v.draw("latex"))

(13+2i3)0231(\frac{1}{3} + \frac{2 i}{3}) |0\rangle- \frac{2}{3} |1\rangle

Rularea metodei measure simulează o măsurătoare în baza standard. Aceasta returnează rezultatul acelei măsurători, plus noul vector de stare cuantică al sistemului după măsurătoare. (Aici folosim funcția print din Python cu prefixul f pentru afișare formatată cu expresii incluse.)

outcome, state = v.measure()
print(f"Measured: {outcome}\nPost-measurement state:")
display(state.draw("latex"))
Measured: 1
Post-measurement state:

1- |1\rangle

Rezultatele măsurătorilor sunt probabilistice, deci această metodă poate returna rezultate diferite la rulări multiple. Pentru exemplul particular al vectorului v definit mai sus, metoda measure definește vectorul de stare cuantică după ce are loc măsurătoarea ca fiind

(1+2i5)0\biggl(\frac{1 + 2i}{\sqrt{5}}\biggr) \vert 0\rangle

(în loc de 0\vert 0\rangle) sau

1- \vert 1\rangle

(în loc de 1\vert 1\rangle), în funcție de rezultatul măsurătorii. În ambele cazuri, alternativele la 0\vert 0\rangle și 1\vert 1\rangle sunt, de fapt, echivalente cu acești vectori de stare; se spune că sunt echivalente până la o fază globală deoarece unul este egal cu celălalt înmulțit cu un număr complex de pe cercul unitate. Această problemă este discutată mai detaliat în lecția Circuite cuantice și poate fi ignorată cu siguranță pentru moment.

Statevector va genera o eroare dacă metoda measure este aplicată unui vector de stare cuantică invalid.

Statevector vine de asemenea cu o metodă sample_counts care permite simularea oricărui număr de măsurători pe sistem, de fiecare dată pornind cu o copie proaspătă a stării. De exemplu, codul următor arată rezultatul măsurării vectorului v de 10001000 de ori, ceea ce (cu probabilitate ridicată) duce la rezultatul 00 de aproximativ 55 din fiecare 99 ori (sau aproximativ 556556 din cele 10001000 de încercări) și rezultatul 11 de aproximativ 44 din fiecare 99 ori (sau aproximativ 444444 din cele 10001000 de încercări). Codul care urmează demonstrează de asemenea funcția plot_histogram din modulul qiskit.visualization pentru vizualizarea rezultatelor.

from qiskit.visualization import plot_histogram

statistics = v.sample_counts(1000)
plot_histogram(statistics)

Output of the previous code cell

Rularea acestui cod de mai multe ori pe cont propriu, cu numere diferite de eșantioane în locul lui 1000,1000, poate fi utilă pentru a dezvolta o intuiție despre cum influențează numărul de încercări de câte ori apare fiecare rezultat. Cu din ce în ce mai multe eșantioane, fracțiunea de eșantioane pentru fiecare posibilitate este probabil să se apropie tot mai mult de probabilitatea corespunzătoare. Acest fenomen, vorbind mai general, este cunoscut sub numele de legea numerelor mari în teoria probabilității.

Efectuarea operațiilor cu Operator și Statevector

Operațiile unitare pot fi definite în Qiskit folosind clasa Operator, ca în exemplul care urmează. Această clasă include o metodă draw cu argumente similare cu Statevector. Reține că opțiunea latex produce rezultate echivalente cu array_from_latex.

from qiskit.quantum_info import Operator

Y = Operator([[0, -1.0j], [1.0j, 0]])
H = Operator([[1 / sqrt(2), 1 / sqrt(2)], [1 / sqrt(2), -1 / sqrt(2)]])
S = Operator([[1, 0], [0, 1.0j]])
T = Operator([[1, 0], [0, (1 + 1.0j) / sqrt(2)]])

display(T.draw("latex"))
[10022+2i2] \begin{bmatrix} 1 & 0 \\ 0 & \frac{\sqrt{2}}{2} + \frac{\sqrt{2} i}{2} \\ \end{bmatrix}

Putem aplica o operație unitară unui vector de stare folosind metoda evolve.

v = Statevector([1, 0])

v = v.evolve(H)
v = v.evolve(T)
v = v.evolve(H)
v = v.evolve(S)
v = v.evolve(Y)

display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

O previzualizare a Circuit-urilor cuantice

Circuit-urile cuantice nu vor fi introduse formal până la lecția Circuite cuantice, care este a treia lecție din acest curs, dar putem totuși experimenta cu compunerea operațiilor unitare qubit folosind clasa QuantumCircuit din Qiskit. În special, putem defini un Circuit cuantic (care, în acest caz, va fi pur și simplu o secvență de operații unitare efectuate pe un singur qubit) după cum urmează.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(1)

circuit.h(0)
circuit.t(0)
circuit.h(0)
circuit.s(0)
circuit.y(0)

display(circuit.draw(output="mpl"))

Output of the previous code cell

Aici folosim metoda draw din clasa QuantumCircuit cu renderer-ul mpl (prescurtare pentru Matplotlib, o bibliotecă de vizualizare Python). Acesta este singurul renderer pe care îl vom folosi pentru Circuit-urile cuantice în acest curs, dar există și alte opțiuni, inclusiv un renderer bazat pe text și unul bazat pe LaTeX.

Operațiile sunt aplicate secvențial, începând din stânga și terminând în dreapta în diagramă. O modalitate practică de a obține matricea unitară corespunzătoare acestui Circuit este să folosești metoda from_circuit din clasa Operator.

display(Operator.from_circuit(circuit).draw("latex"))
[0.14644660940.3535533906i0.8535533906+0.3535533906i0.3535533906+0.8535533906i0.3535533906+0.1464466094i] \begin{bmatrix} 0.1464466094 - 0.3535533906 i & 0.8535533906 + 0.3535533906 i \\ -0.3535533906 + 0.8535533906 i & 0.3535533906 + 0.1464466094 i \\ \end{bmatrix}

Putem de asemenea inițializa un vector de stare cuantică de pornire și apoi evolua acea stare conform secvenței de operații descrise de Circuit.

ket0 = Statevector([1, 0])
v = ket0.evolve(circuit)
display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

Codul următor simulează un experiment în care starea obținută din Circuit-ul de mai sus este măsurată cu o măsurătoare în baza standard de 4000 de ori (folosind o copie proaspătă a stării de fiecare dată).

statistics = v.sample_counts(4000)
display(plot_histogram(statistics))

Output of the previous code cell