Sari la conținutul principal

Codificarea datelor

Introducere și notații

Pentru a folosi un algoritm cuantic, datele clasice trebuie aduse cumva într-un circuit cuantic. Acest proces este denumit de obicei codificarea datelor, dar mai este numit și încărcarea datelor. Reamintește-ți din lecțiile anterioare noțiunea de mapare a caracteristicilor, adică o mapare a trăsăturilor de date dintr-un spațiu în altul. Simpla transferare a datelor clasice pe un calculator cuantic este un tip de mapare și ar putea fi numită mapare a caracteristicilor. În practică, mapările de caracteristici încorporate în Qiskit (cum ar fi z_Feature Map și ZZ Feature Map) vor include de obicei straturi de rotație și straturi de împletire care extind starea la multe dimensiuni în spațiul Hilbert. Acest proces de codificare este o parte critică a algoritmilor de învățare automată cuantică și le afectează direct capacitățile computaționale.

Unele dintre tehnicile de codificare de mai jos pot fi simulate eficient clasic; acest lucru este deosebit de ușor de observat în metodele de codificare care produc stări produs (adică nu împletesc qubiți). Și ține minte că utilitatea cuantică este cel mai probabil să apară acolo unde complexitatea de tip cuantic a setului de date este bine potrivită cu metoda de codificare. Astfel, este foarte probabil să ajungi să îți scrii propriile circuite de codificare. Aici prezentăm o varietate largă de strategii posibile de codificare doar pentru ca tu să le poți compara și contrasta, și să vezi ce este posibil. Există câteva afirmații foarte generale care pot fi făcute despre utilitatea tehnicilor de codificare. De exemplu, efficient_su2 (vezi mai jos) cu o schemă completă de împletire este mult mai probabil să capteze trăsăturile cuantice ale datelor decât metodele care produc stări produs (cum ar fi z_feature_map). Dar asta nu înseamnă că efficient_su2 este suficientă sau suficient de bine adaptată setului tău de date pentru a produce o accelerare cuantică. Asta necesită o analiză atentă a structurii datelor modelate sau clasificate. Există și un echilibru de găsit în privința adâncimii circuitului, deoarece multe mapări de caracteristici care împletesc complet qubiții dintr-un circuit produc circuite foarte adânci, prea adânci pentru a obține rezultate utilizabile pe calculatoarele cuantice actuale.

Notații

Un set de date este un ansamblu de MM vectori de date: X={x(j)j[M]}\text{X} = \{\vec{x}^{(j)}\,|\,j\in [M]\}, unde fiecare vector este NN-dimensional, adică x(j)=(x1(j),,xN(j))RN\vec{x}^{(j)}=(\vec{x}^{(j)}_1,\ldots,\vec{x}^{(j)}_N)\in\mathbb{R}^N. Aceasta ar putea fi extins la trăsături de date complexe. În această lecție, putem folosi ocazional aceste notații pentru setul complet (X),(\text{X}), și elementele sale specifice, cum ar fi x(j)\vec{x}^{(j)}. Dar ne vom referi în principal la încărcarea unui singur vector din setul nostru de date la un moment dat și vom face adesea referire pur și simplu la un singur vector de NN trăsături ca x\vec{x}.

În plus, este obișnuit să se utilizeze simbolul Φ(x)\Phi(\vec{x}) pentru a face referire la maparea de caracteristici Φ\Phi a vectorului de date x\vec{x}. Specific în informatica cuantică, este obișnuit să se facă referire la mapările din informatica cuantică folosind U(x),U(\vec{x}), o notație care subliniază natura unitară a acestor operații. Cineva ar putea folosi corect același simbol pentru ambele; ambele sunt mapări de caracteristici. Pe parcursul acestui curs, tindem să folosim:

  • Φ(x)\Phi(\vec{x}) când discutăm mapările de caracteristici în învățarea automată, în general, și
  • U(x)U(\vec{x}) când discutăm implementările circuitelor de mapări de caracteristici.

Normalizare și pierdere de informații

În învățarea automată clasică, trăsăturile datelor de antrenament sunt adesea „normalizate" sau rescalate, ceea ce îmbunătățește adesea performanța modelului. O modalitate obișnuită de a face acest lucru este utilizarea normalizării min-max sau standardizarea. În normalizarea min-max, coloanele de trăsături ale matricei de date X\text{X} (să spunem trăsătura kk) sunt normalizate:

xk(i)=xk(i)min{xk(j)x(j)[X]}max{xk(j)x(j)[X]}min{xk(j)x(j)[X]}x^{'(i)}_k = \frac{x^{(i)}_k - \text{min}\{x^{(j)}_k\,|\,\vec{x}^{(j)}\in [\text{X}]\}}{\text{max}\{x^{(j)}_k\,|\,\vec{x}^{(j)}\in [\text{X}]\}-\text{min}\{x^{(j)}_k\,|\,\vec{x}^{(j)}\in [\text{X}]\}}

unde min și max se referă la minimul și maximul trăsăturii kk pe cei MM vectori de date din setul de date X\text{X}. Toate valorile trăsăturilor cad apoi în intervalul unitar: xk(i)[0,1]x^{'(i)}_k \in [0,1] pentru toți i[M]i\in [M], k[N]k\in[N].

Normalizarea este, de asemenea, un concept fundamental în mecanica cuantică și informatica cuantică, dar este ușor diferită de normalizarea min-max. Normalizarea în mecanica cuantică impune ca lungimea (în contextul informaticii cuantice, norma-2) a unui vector de stare ψ|\psi\rangle să fie egală cu unitatea: ψ=ψψ=1\|\psi\|=\sqrt{\langle\psi|\psi\rangle} = 1, asigurând că probabilitățile de măsurare se sumează la 1. Starea este normalizată prin împărțirea la norma-2; adică prin rescalare:

ψψ1ψ|\psi\rangle\rightarrow\|\psi\|^{-1}|\psi\rangle

În informatica cuantică și mecanica cuantică, aceasta nu este o normalizare impusă de oameni asupra datelor, ci o proprietate fundamentală a stărilor cuantice. În funcție de schema de codificare, această constrângere poate afecta modul în care datele tale sunt rescalate. De exemplu, în codificarea pe amplitudine (vezi mai jos), vectorul de date este normalizat x(j)=1\vert\vec{x}^{(j)}\vert = 1 conform cerinței din mecanica cuantică, iar aceasta afectează scalarea datelor care urmează să fie codificate. În codificarea pe fază, se recomandă rescalarea valorilor trăsăturilor astfel încât xi(j)(0,2π]\vec{x}^{(j)}_i \in (0,2\pi] pentru a nu exista pierderi de informații din cauza efectului modulo-2π2\pi al codificării pe unghiul de fază al unui qubit[1,2].

Metode de codificare

În următoarele câteva secțiuni, vom face referire la un mic set de date clasic exemplu Xex\text{X}_\text{ex} format din M=5M=5 vectori de date, fiecare cu N=3N=3 trăsături:

Xex={(4,8,5),(9,8,6),(2,9,2),(5,7,0),(3,7,5)}\text{X}_{\text{ex}}=\{(4,8,5),(9,8,6),(2,9,2),(5,7,0),(3,7,5)\}

În notația introdusă mai sus, am putea spune că a 1a1^\text{a} trăsătură a celui de-al 4lea4^\text{lea} vector de date din setul nostru Xex\text{X}_{\text{ex}} este x1(4)=5,\vec{x}^{(4)}_1 = 5, de exemplu.

Codificarea pe bază

Codificarea pe bază codifică un șir clasic de PP biți într-o stare de bază computațională a unui sistem de PP qubiți. Luăm ca exemplu x3(1)=5=0(23)+1(22)+0(21)+1(20).\vec{x}^{(1)}_3 = 5 = 0(2^3)+1(2^2)+0(2^1)+1(2^0). Aceasta poate fi reprezentată ca un șir de 44 biți ca (0101)(0101), și de un sistem de 44 qubiți ca starea cuantică 0101|0101\rangle. Mai general, pentru un șir de PP biți: xk(j)=(b1,b2,...,bP)\vec{x}^{(j)}_k = (b_1, b_2, ... , b_P), starea corespunzătoare de PP qubiți este xk(j)=b1,b2,...,bP|x^{(j)}_k\rangle = | b_1, b_2, ... , b_P \rangle cu bn{0,1}b_n \in \{0,1\} pentru n=1,,Pn = 1 , \dots , P. Rețineți că aceasta este doar pentru o singură trăsătură.

Codificarea pe bază în informatica cuantică reprezintă fiecare bit clasic ca un qubit separat, mapând direct reprezentarea binară a datelor pe stările cuantice din baza computațională. Când mai multe trăsături trebuie codificate, fiecare trăsătură este mai întâi convertită în forma sa binară și apoi atribuită unui grup distinct de qubiți — câte un grup per trăsătură — unde fiecare qubit reflectă un bit în reprezentarea binară a acelei trăsături.

Ca exemplu, să codificăm vectorul (5, 7, 0).

Presupunem că toate trăsăturile sunt stocate în patru biți (mai mult decât avem nevoie, dar suficient pentru a reprezenta orice număr întreg cu o singură cifră în baza 10):

5 → binar 0101

7 → binar 0111

0 → binar 0000

Aceste șiruri de biți sunt atribuite la trei seturi de câte patru qubiți, deci starea de bază globală de 12 qubiți este:

010101110000∣0101 0111 0000⟩

Aici, primii patru qubiți reprezintă prima trăsătură, următorii patru qubiți a doua trăsătură și ultimii patru qubiți a treia trăsătură. Codul de mai jos convertește vectorul de date (5,7,0) într-o stare cuantică și este generalizat pentru a face același lucru pentru alte trăsături cu o singură cifră.

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

# Data point to encode
x = 5 # binary: 0101
y = 7 # binary: 0111
z = 0 # binary: 0000

# Convert each to 4-bit binary list
x_bits = [int(b) for b in format(x, "04b")] # [0,1,0,1]
y_bits = [int(b) for b in format(y, "04b")] # [0,1,1,1]
z_bits = [int(b) for b in format(z, "04b")] # [0,0,0,0]

# Combine all bits
all_bits = x_bits + y_bits + z_bits # [0,1,0,1,0,1,1,1,0,0,0,0]

# Initialize a 12-qubit quantum circuit
qc = QuantumCircuit(12)

# Apply x-gates where the bit is 1
for idx, bit in enumerate(all_bits):
if bit == 1:
qc.x(idx)

qc.draw("mpl")

Output of the previous code cell

Verifică-ți înțelegerea

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

Scrie cod pentru a codifica primul vector din setul nostru de date exemplu Xex\text{X}_{\text{ex}}:

x(1)=(4,8,5)\vec{x}^{(1)}=(4,8,5)

folosind codificarea pe bază.

Răspuns:

import math
from qiskit import QuantumCircuit

# Data point to encode
x = 4 # binary: 0100
y = 8 # binary: 1000
z = 5 # binary: 0101

# Convert each to 4-bit binary list
x_bits = [int(b) for b in format(x, '04b')] # [0,1,0,0]
y_bits = [int(b) for b in format(y, '04b')] # [1,0,0,0]
z_bits = [int(b) for b in format(z, '04b')] # [0,1,0,1]

# Combine all bits
all_bits = x_bits + y_bits + z_bits # [0,1,0,0,1,0,0,0,0,1,0,1]

# Initialize a 12-qubit quantum circuit
qc = QuantumCircuit(12)

# Apply x-gates where the bit is 1
for idx, bit in enumerate(all_bits):
if bit == 1:
qc.x(idx)

qc.draw('mpl')

Codificarea pe amplitudine

Codificarea pe amplitudine codifică datele în amplitudinile unei stări cuantice. Reprezintă un vector de date clasic normalizat NN-dimensional, x(j)\vec{x}^{(j)}, ca amplitudinile unei stări cuantice de nn qubiți, ψx|\psi_x\rangle:

ψx(j)=1αi=1Nxi(j)i|\psi^{(j)}_x\rangle = \frac{1}{\alpha}\sum_{i=1}^N x^{(j)}_i |i\rangle

unde NN este aceeași dimensiune a vectorilor de date ca înainte, xi(j)\vec{x}^{(j)}_i este al ii-lea element al lui x(j)\vec{x}^{(j)} și i|i\rangle este a ii-a stare de bază computațională. Aici, α\alpha este o constantă de normalizare care urmează să fie determinată din datele ce urmează să fie codificate. Aceasta este condiția de normalizare impusă de mecanica cuantică:

i=1Nxi(j)2=α2.\sum_{i=1}^N \left|x^{(j)}_i\right|^2 = \left|\alpha\right|^2.

În general, aceasta este o condiție diferită de normalizarea min/max utilizată pentru fiecare trăsătură pe toți vectorii de date. Modul precis în care se navighează această situație va depinde de problema ta. Dar nu există nicio cale de a ocoli condiția de normalizare din mecanica cuantică de mai sus.

În codificarea pe amplitudine, fiecare trăsătură dintr-un vector de date este stocată ca amplitudinea unei stări cuantice diferite. Deoarece un sistem de nn qubiți oferă 2n2^n amplitudini, codificarea pe amplitudine a NN trăsături necesită nlog2(N)n \ge \mathrm{log}_2(N) qubiți.

Ca exemplu, să codificăm primul vector din setul de date exemplu Xex\text{X}_\text{ex}, x(1)=(4,8,5)\vec{x}^{(1)} = (4,8,5) folosind codificarea pe amplitudine. Normalizând vectorul rezultat, obținem:

i=1Nxi(1)2=42+82+52=105=α2α=105\sum_{i=1}^N \left|x^{(1)}_i\right|^2 = 4^2+8^2+5^2 = 105 = \left|\alpha\right|^2 \rightarrow \alpha = \sqrt{105}

și starea cuantică rezultată de 2 qubiți ar fi:

ψ(x(1))=1105(400+801+510+011)|\psi(\vec{x}^{(1)})\rangle = \frac{1}{\sqrt{105}}(4|00\rangle+8|01\rangle+5|10\rangle+0|11\rangle)

În exemplul de mai sus, numărul de trăsături din vector N=3N=3 nu este o putere a lui 2. Când NN nu este o putere a lui 2, pur și simplu alegem o valoare pentru numărul de qubiți nn astfel încât 2nN2^n\geq N și completăm vectorul de amplitudini cu constante neinformative (aici, un zero).

La fel ca în codificarea pe bază, odată ce calculăm ce stare va codifica setul nostru de date, în Qiskit putem folosi funcția initialize pentru a o pregăti:

import math

desired_state = [
1 / math.sqrt(105) * 4,
1 / math.sqrt(105) * 8,
1 / math.sqrt(105) * 5,
1 / math.sqrt(105) * 0,
]

qc = QuantumCircuit(2)
qc.initialize(desired_state, [0, 1])

qc.decompose(reps=5).draw(output="mpl")

Output of the previous code cell

Un avantaj al codificării pe amplitudine este cerința menționată anterior de doar log2(N)\mathrm{log}_2(N) qubiți pentru codificare. Cu toate acestea, algoritmii următori trebuie să opereze pe amplitudinile unei stări cuantice, iar metodele de pregătire și măsurare a stărilor cuantice tind să nu fie eficiente.

Verifică-ți înțelegerea

Citește întrebările de mai jos, gândește-te la răspunsuri, apoi apasă pe triunghiuri pentru a vedea soluțiile.

Scrie starea normalizată pentru codificarea următorului vector (alcătuit din doi vectori din setul nostru de date exemplu):

x=(9,8,6,2,9,2)\vec{x}=(9,8,6,2,9,2)

folosind codificarea pe amplitudine.

Răspuns:

Pentru a codifica 6 numere, va trebui să avem cel puțin 6 stări disponibile ale căror amplitudini le putem folosi pentru codificare. Aceasta va necesita 3 qubiți. Folosind un factor de normalizare necunoscut α\alpha, putem scrie aceasta ca:

ψ=α(9000+8001+6010+2011+9100+2101+0110+0111)|\psi\rangle = \alpha(9|000\rangle+8|001\rangle+6|010\rangle+2|011\rangle+9|100\rangle+2|101\rangle+0|110\rangle+0|111\rangle)

Rețineți că

ψψ=α2×(92+82+62+22+92+22+02+02)=α2×(270)=1α=1270\langle \psi|\psi\rangle = |\alpha|^2\times(9^2+8^2+6^2+2^2+9^2+2^2+0^2+0^2) = |\alpha|^2\times(270)=1 \rightarrow \alpha = \frac{1}{\sqrt{270}}

Deci, în final,

ψ=1270(9000+8001+6010+2011+9100+2101+0110+0111)|\psi\rangle = \frac{1}{\sqrt{270}}(9|000\rangle+8|001\rangle+6|010\rangle+2|011\rangle+9|100\rangle+2|101\rangle+0|110\rangle+0|111\rangle)

Pentru același vector de date x=(9,8,6,2,9,2),\vec{x}=(9,8,6,2,9,2), scrie cod pentru a crea un circuit care încarcă aceste trăsături de date folosind codificarea pe amplitudine.

Răspuns:

desired_state = [
9 / math.sqrt(270),
8 / math.sqrt(270),
6 / math.sqrt(270),
2 / math.sqrt(270),
9 / math.sqrt(270),
2 / math.sqrt(270),
0,
0,
]

print(desired_state)

qc = QuantumCircuit(3)
qc.initialize(desired_state, [0, 1, 2])
qc.decompose(reps=8).draw(output="mpl")

[0.5477225575051662, 0.48686449556014766, 0.36514837167011077, 0.12171612389003691, 0.5477225575051662, 0.12171612389003691, 0, 0]

"Output of the previous code cell"

S-ar putea să fie nevoie să lucrezi cu vectori de date foarte mari. Consideră vectorul

x=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5).\vec{x}=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5).

Scrie cod pentru a automatiza normalizarea și generează un circuit cuantic pentru codificarea pe amplitudine.

Răspuns:

Există multe răspunsuri posibile. Iată un cod care afișează câțiva pași intermediari:

import numpy as np
from math import sqrt

init_list = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0, 3, 7, 5]
qubits = round(np.log(len(init_list)) / np.log(2) + 0.4999999999)
need_length = 2**qubits
pad = need_length - len(init_list)
for i in range(0, pad):
init_list.append(0)

init_array = np.array(init_list) # Unnormalized data vector
length = sqrt(
sum(init_array[i] ** 2 for i in range(0, len(init_array)))
) # Vector length
norm_array = init_array / length # Normalized array
print("Normalized array:")
print(norm_array)
print()

qubit_numbers = []
for i in range(0, qubits):
qubit_numbers.append(i)
print(qubit_numbers)

qc = QuantumCircuit(qubits)
qc.initialize(norm_array, qubit_numbers)
qc.decompose(reps=7).draw(output="mpl")

Normalized array: [0.17342199 0.34684399 0.21677749 0.39019949 0.34684399 0.26013299 0.086711 0.39019949 0.086711 0.21677749 0.30348849 0. 0.1300665 0.30348849 0.21677749 0. ]

[0, 1, 2, 3]

"Output of the previous code cell"

Observi avantaje ale codificării pe amplitudine față de codificarea pe bază? Dacă da, explică.

Răspuns:

Pot exista mai multe răspunsuri. Un răspuns este că, dată fiind ordinea fixă a stărilor de bază, această codificare pe amplitudine păstrează ordinea numerelor codificate. Adesea va fi și mai densă.

Un beneficiu al codificării pe amplitudine este că sunt necesari doar log2(N)\log_2(N) qubiți pentru un vector de date NN-dimensional (NN trăsături) xx\vec{x}\rightarrow|\vec{x}\rangle. Cu toate acestea, codificarea pe amplitudine este în general o procedură ineficientă care necesită pregătirea arbitrară a stării, care este exponențială în numărul de porți CNOT. Altfel spus, pregătirea stării are o complexitate de timp de execuție polinomială de O(N)\mathcal O(N) în numărul de dimensiuni, unde N=2nN = 2^n, iar nn este numărul de qubiți. Codificarea pe amplitudine „oferă o economie exponențială de spațiu cu prețul unei creșteri exponențiale a timpului"[3]; totuși, creșteri ale timpului de execuție la O(logN)\mathcal O(\log N) sunt realizabile în anumite cazuri[4]. Pentru o accelerare cuantică end-to-end, trebuie luată în considerare complexitatea timpului de execuție al încărcării datelor.

Codificarea pe unghi

Codificarea pe unghi prezintă interes în multe modele QML care utilizează mapări de caracteristici Pauli, cum ar fi mașinile vectoriale de suport cuantice (QSVM) și circuitele cuantice variaționale (VQC), printre altele. Codificarea pe unghi este strâns legată de codificarea pe fază și codificarea densă pe unghi, care sunt prezentate mai jos. Aici vom folosi „codificarea pe unghi" pentru a face referire la o rotație în θ\theta, adică o rotație departe de axa zz realizată, de exemplu, printr-o poartă RXR_X sau o poartă RYR_Y[1,3]. De fapt, se pot codifica date în orice rotație sau combinație de rotații. Dar RYR_Y este obișnuită în literatură, deci o subliniem aici.

Când este aplicată unui singur qubit, codificarea pe unghi imprimă o rotație pe axa Y proporțională cu valoarea datei. Considerăm codificarea unei singure trăsături (a kk-a trăsătură) din al jj-lea vector de date dintr-un set de date, xk(j)\vec{x}^{(j)}_k:

xk(j)=RY(θ=xk(j))0=cos(xk(j)2)0+sin(xk(j)2)1.|\vec{x}^{(j)}_k\rangle = R_Y(\theta=\vec{x}^{(j)}_k)|0\rangle = \textstyle\cos\left(\frac{\vec{x}^{(j)}_k}{2}\right)|0\rangle + \sin\left(\frac{\vec{x}^{(j)}_k}{2}\right)|1\rangle.

Alternativ, codificarea pe unghi poate fi realizată folosind porți RX(θ)R_X(\theta), deși starea codificată ar avea o fază relativă complexă față de RY(θ)R_Y(\theta).

Codificarea pe unghi diferă de cele două metode anterioare discutate în mai multe moduri. În codificarea pe unghi:

  • Fiecare valoare a trăsăturii este mapată unui qubit corespunzător, xk(j)Qk\vec{x}^{(j)}_k \rightarrow Q_k, lăsând qubiții într-o stare produs.
  • O singură valoare numerică este codificată la un moment dat, în loc de un set întreg de trăsături dintr-un punct de date.
  • Sunt necesari nn qubiți pentru NN trăsături de date, unde nNn\leq N. Adesea se aplică egalitatea. Vom vedea cum este posibil n<Nn<N în secțiunile următoare.
  • Circuitul cuantic rezultat are o adâncime constantă (de obicei adâncimea este 1 înainte de transpilare).

Circuitul cuantic cu adâncime constantă îl face deosebit de potrivit pentru hardware-ul cuantic actual. O caracteristică suplimentară a codificării datelor noastre folosind θ\theta (și, în mod specific, alegerea noastră de a utiliza codificarea pe unghi pe axa Y) este că creează stări cuantice cu valori reale care pot fi utile pentru anumite aplicații. Pentru rotația pe axa Y, datele sunt mapate cu o poartă de rotație pe axa Y RY(θ)R_Y(\theta) printr-un unghi real θ(0,2π]\theta \in (0, 2\pi] (Qiskit RYGate). Ca și în cazul codificării pe fază (vezi mai jos), recomandăm rescalarea datelor astfel încât xk(j)(0,2π]\vec{x}^{(j)}_k \in (0,2\pi], prevenind pierderea de informații și alte efecte nedorite.

Următorul cod Qiskit rotește un singur qubit dintr-o stare inițială 0|0\rangle pentru a codifica o valoare de date xk(j)=12π\vec{x}^{(j)}_k=\frac{1}{2}\pi.

from qiskit.quantum_info import Statevector
from math import pi

qc = QuantumCircuit(1)
state1 = Statevector.from_instruction(qc)
qc.ry(pi / 2, 0) # Phase gate rotates by an angle pi/2
state2 = Statevector.from_instruction(qc)
states = state1, state2

Vom defini o funcție pentru a vizualiza acțiunea asupra vectorului de stare. Detaliile definiției funcției nu sunt importante, dar capacitatea de a vizualiza vectorii de stare și modificările lor este importantă.

import numpy as np
from qiskit.visualization.bloch import Bloch
from qiskit.visualization.state_visualization import _bloch_multivector_data

def plot_Nstates(states, axis, plot_trace_points=True):
"""This function plots N states to 1 Bloch sphere"""
bloch_vecs = [_bloch_multivector_data(s)[0] for s in states]

if axis is None:
bloch_plot = Bloch()
else:
bloch_plot = Bloch(axes=axis)

bloch_plot.add_vectors(bloch_vecs)

if len(states) > 1:

def rgba_map(x, num):
g = (0.95 - 0.05) / (num - 1)
i = 0.95 - g * num
y = g * x + i
return (0.0, y, 0.0, 0.7)

num = len(states)
bloch_plot.vector_color = [rgba_map(x, num) for x in range(1, num + 1)]

bloch_plot.vector_width = 3
bloch_plot.vector_style = "simple"

if plot_trace_points:

def trace_points(bloch_vec1, bloch_vec2):
# bloch_vec = (x,y,z)
n_points = 15
thetas = np.arccos([bloch_vec1[2], bloch_vec2[2]])
phis = np.arctan2(
[bloch_vec1[1], bloch_vec2[1]], [bloch_vec1[0], bloch_vec2[0]]
)
if phis[1] < 0:
phis[1] = phis[1] + 2 * pi
angles0 = np.linspace(phis[0], phis[1], n_points)
angles1 = np.linspace(thetas[0], thetas[1], n_points)

xp = np.cos(angles0) * np.sin(angles1)
yp = np.sin(angles0) * np.sin(angles1)
zp = np.cos(angles1)
pnts = [xp, yp, zp]
bloch_plot.add_points(pnts)
bloch_plot.point_color = "k"
bloch_plot.point_size = [4] * len(bloch_plot.points)
bloch_plot.point_marker = ["o"]

for i in range(len(bloch_vecs) - 1):
trace_points(bloch_vecs[i], bloch_vecs[i + 1])

bloch_plot.sphere_alpha = 0.05
bloch_plot.frame_alpha = 0.15
bloch_plot.figsize = [4, 4]

bloch_plot.render()

plot_Nstates(states, axis=None, plot_trace_points=True)

Output of the previous code cell

Aceasta a fost doar o singură trăsătură a unui singur vector de date. Când se codifică NN trăsături în unghiurile de rotație ale nn qubiți, să spunem pentru al jj-lea vector de date x(j)=(x1,...,xN),\vec{x}^{(j)} = (x_1,...,x_N), starea produs codificată va arăta astfel:

x(j)=k=1Ncos(xk(j))0+sin(xk(j))1|\vec{x}^{(j)}\rangle = \bigotimes^N_{k=1} \cos(\vec{x}^{(j)}_k)|0\rangle + \sin(\vec{x}^{(j)}_k)|1\rangle

Remarcăm că aceasta este echivalentă cu

x(j)=k=1NRY(2xk(j))0.|\vec{x}^{(j)}\rangle = \bigotimes^N_{k=1} R_Y(2\vec{x}^{(j)}_k)|0\rangle.

Verifică-ți înțelegerea

Citește întrebările de mai jos, gândește-te la răspunsuri, apoi apasă pe triunghiuri pentru a vedea soluțiile.

Codifică vectorul de date x=(0,π/4,π/2)\vec{x} = (0, \pi/4, \pi/2) folosind codificarea pe unghi, așa cum este descrisă mai sus.

Răspuns:

qc = QuantumCircuit(3)
qc.ry(0, 0)
qc.ry(2 * math.pi / 4, 1)
qc.ry(2 * math.pi / 2, 2)
qc.draw(output="mpl")

&quot;Output of the previous code cell&quot;

Folosind codificarea pe unghi descrisă mai sus, câți qubiți sunt necesari pentru a codifica 5 trăsături?

Răspuns: 5

Codificarea pe fază

Codificarea pe fază este foarte similară cu codificarea pe unghi descrisă mai sus. Unghiul de fază al unui qubit este un unghi real ϕ\phi în jurul axei zz față de axa +xx. Datele sunt mapate cu o rotație de fază, P(ϕ)=eiϕ/2RZ(ϕ)P(\phi) = e^{i\phi/2}R_Z(\phi), unde ϕ(0,2π]\phi \in (0,2\pi] (vezi Qiskit PhaseGate pentru mai multe informații). Se recomandă rescalarea datelor astfel încât xk(j)(0,2π]\vec{x}^{(j)}_k \in (0,2\pi]. Aceasta previne pierderea de informații și alte efecte potențial nedorite[1,2].

Un qubit este adesea inițializat în starea 0|0\rangle, care este o stare proprie a operatorului de rotație de fază, ceea ce înseamnă că starea qubitului trebuie mai întâi rotită pentru ca codificarea pe fază să fie implementată. Prin urmare, are sens să se inițializeze starea cu o poartă Hadamard: H0=+=12(0+1)H|0\rangle = |+\rangle = \textstyle\frac{1}{\sqrt{2}}(|0\rangle + |1\rangle). Codificarea pe fază pe un singur qubit înseamnă imprimarea unei faze relative proporționale cu valoarea datei:

xk(j)=P(ϕ=xk(j))+=12(0+eixk(j)1).|\vec{x}^{(j)}_k\rangle = P(\phi=\vec{x}^{(j)}_k)|+\rangle = \textstyle\frac{1}{\sqrt{2}}\big(|0\rangle + e^{i\vec{x}^{(j)}_k}|1\rangle\big).

Procedura de codificare pe fază mapează fiecare valoare a trăsăturii la faza unui qubit corespunzător, xk(j)Qk\vec{x}^{(j)}_k \rightarrow Q_k. În total, codificarea pe fază are o adâncime a circuitului de 2, incluzând stratul Hadamard, ceea ce o face o schemă de codificare eficientă. Starea cu mai mulți qubiți codificată pe fază (nn qubiți pentru N=nN=n trăsături) este o stare produs:

x(j)=k=1NPk(ϕ=xk(j))+N=12Nk=1N(0+eixk(j)1).|\vec{x}^{(j)}\rangle = \bigotimes_{k=1}^{N} P_k(\phi = \vec{x}^{(j)}_k)|+\rangle^{\otimes N} = {\textstyle\frac{1}{\sqrt{2^N}}} \bigotimes_{k=1}^{N}\big(|0\rangle + e^{i\vec{x}^{(j)}_k}|1\rangle\big).

Următorul cod Qiskit pregătește mai întâi starea inițială a unui singur qubit rotindu-l cu o poartă Hadamard, apoi îl rotește din nou folosind o poartă de fază pentru a codifica o trăsătură de date xk(j)=12π\vec{x}^{(j)}_k=\frac{1}{2}\pi.

qc = QuantumCircuit(1)
qc.h(0) # Hadamard gate rotates state down to Bloch equator
state1 = Statevector.from_instruction(qc)

qc.p(pi / 2, 0) # Phase gate rotates by an angle pi/2
state2 = Statevector.from_instruction(qc)

states = state1, state2

qc.draw("mpl", scale=1)

Output of the previous code cell

Putem vizualiza rotația în ϕ\phi folosind funcția plot_Nstates pe care am definit-o.

plot_Nstates(states, axis=None, plot_trace_points=True)

Output of the previous code cell

Graficul sferei Bloch arată rotația pe axa Z +P(12π)+|+\rangle \rightarrow P(\frac{1}{2}\pi)|+\rangle unde xk(j)=12π\vec{x}^{(j)}_k=\frac{1}{2}\pi. Săgeata verde deschis arată starea finală.

Codificarea pe fază este folosită în multe mapări de caracteristici cuantice, în special mapările de caracteristici ZZ și ZZZZ, și mapările de caracteristici Pauli generale, printre altele.

Verifică-ți înțelegerea

Citește întrebările de mai jos, gândește-te la răspunsuri, apoi apasă pe triunghiuri pentru a vedea soluțiile.

Câți qubiți sunt necesari pentru a folosi codificarea pe fază, așa cum este descrisă mai sus, pentru a stoca 8 trăsături?

Răspuns: 8

Scrie cod pentru a codifica vectorul x(1)=(4,8,5,9,8,6,2,9,2,5,7,0)\vec{x}^{(1)}=(4,8,5,9,8,6,2,9,2,5,7,0) folosind codificarea pe fază.

Răspuns:

Pot exista mai multe răspunsuri. Iată un exemplu:

phase_data = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0]
qc = QuantumCircuit(len(phase_data))
for i in range(0, len(phase_data)):
qc.h(i)
qc.rz(phase_data[i] * 2 * math.pi / float(max(phase_data)), i)
qc.draw(output="mpl")

&quot;Output of the previous code cell&quot;

Codificarea densă pe unghi

Codificarea densă pe unghi (DAE) este o combinație a codificării pe unghi și a codificării pe fază. DAE permite codificarea a două valori de trăsături într-un singur qubit: una cu un unghi de rotație pe axa Y și cealaltă cu un unghi de rotație pe axa zz: xk(j),\vec{x}^{(j)}_k, x(j)θ,ϕ\vec{x}^{(j)}_\ell \rightarrow \theta, \phi. Codifică două trăsături astfel:

xk(j),x(j)=RZ(ϕ=x(j))RY(θ=xk(j))0=cos(xk(j)2)0+eix(j)sin(xk(j)2)1.|\vec{x}^{(j)}_k,\vec{x}^{(j)}_\ell\rangle = R_Z(\phi=\vec{x}^{(j)}_\ell) R_Y(\theta=\vec{x}^{(j)}_k)|0\rangle = \cos\left(\frac{\vec{x}^{(j)}_k}{2}\right)|0\rangle + e^{i\vec{x}^{(j)}_\ell} \sin\left(\frac{\vec{x}^{(j)}_k}{2}\right)|1\rangle.

Codificarea a două trăsături de date pe un qubit duce la o reducere de 2×2\times a numărului de qubiți necesari pentru codificare. Extinzând aceasta la mai multe trăsături, vectorul de date x=(x1,...,xN)\vec{x} = (x_1,...,x_N) poate fi codificat ca:

x=k=1N/2cos(x2k1)0+eix2ksin(x2k1)1|\vec{x}\rangle = \bigotimes_{k=1}^{N/2} \cos(x_{2k-1})|0\rangle + e^{i x_{2k}}\sin(x_{2k-1})|1\rangle

DAE poate fi generalizată la funcții arbitrare ale celor două trăsături în locul funcțiilor sinusoidale utilizate aici. Aceasta se numește codificare generală pe qubit[7].

Ca exemplu de DAE, codul de mai jos codifică și vizualizează codificarea trăsăturilor x1=θ=3π/8x_1=\theta = 3\pi/8 și x2=ϕ=7π/4x_2=\phi = 7\pi/4.

qc = QuantumCircuit(1)
state1 = Statevector.from_instruction(qc)
qc.ry(3 * pi / 8, 0)
state2 = Statevector.from_instruction(qc)
qc.rz(7 * pi / 4, 0)
state3 = Statevector.from_instruction(qc)
states = state1, state2, state3

plot_Nstates(states, axis=None, plot_trace_points=True)

Output of the previous code cell

Verifică-ți înțelegerea

Citește întrebările de mai jos, gândește-te la răspunsuri, apoi apasă pe triunghiuri pentru a vedea soluțiile.

Ținând cont de tratamentul de mai sus, câți qubiți sunt necesari pentru a codifica 6 trăsături folosind codificarea densă?

Răspuns: 3

Scrie cod pentru a încărca vectorul x(1)=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5)\vec{x}^{(1)}=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5) folosind codificarea densă pe unghi.

Răspuns:

Rețineți că am completat lista cu un „0" pentru a evita problema existenței unui singur parametru nefolosit în schema noastră de codificare.

dense_data = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0, 3, 7, 5, 0]
qc = QuantumCircuit(int(len(dense_data) / 2))
entry = 0
for i in range(0, int(len(dense_data) / 2)):
qc.ry(dense_data[entry] * 2 * math.pi / float(max(dense_data)), i)
entry = entry + 1
qc.rz(dense_data[entry] * 2 * math.pi / float(max(dense_data)), i)
entry = entry + 1
qc.draw(output="mpl")

&quot;Output of the previous code cell&quot;

Codificare cu hărți de caracteristici încorporate

Codificare în puncte arbitrare

Codificarea unghiulară, codificarea de fază și codificarea densă au pregătit stări produs cu o caracteristică codificată pe fiecare qubit (sau două caracteristici pe qubit). Aceasta diferă de codificarea în bază și codificarea de amplitudine, deoarece acele metode utilizează stări entanglate. Nu există o corespondență 1:1 între caracteristica datelor și qubit. În codificarea de amplitudine, de exemplu, o caracteristică poate fi amplitudinea stării 01|01\rangle, iar alta poate fi amplitudinea stării 10|10\rangle. În general, metodele care codifică în stări produs produc circuite mai puțin adânci și pot stoca 1 sau 2 caracteristici pe fiecare qubit. Metodele care folosesc entanglarea și asociază o caracteristică cu o stare, nu cu un qubit, duc la circuite mai adânci și pot stoca în medie mai multe caracteristici pe qubit.

Dar codificarea nu trebuie să fie în întregime în stări produs sau în întregime în stări entanglate, cum ar fi codificarea de amplitudine. Într-adevăr, multe scheme de codificare încorporate în Qiskit permit codificarea atât înainte, cât și după un strat de entangalare, nu doar la început. Aceasta se numește „reîncărcarea datelor" (data reuploading). Pentru lucrări conexe, consultă referințele [5] și [6].

În această secțiune, vom folosi și vizualiza câteva dintre schemele de codificare încorporate. Toate metodele din această secțiune codifică NN caracteristici ca rotații pe NN porți parametrizate pe nn qubiți, unde nNn \leq N. Reține că maximizarea încărcării datelor pentru un număr dat de qubiți nu este singurul criteriu. În multe cazuri, adâncimea circuitului poate fi o considerație chiar mai importantă decât numărul de qubiți.

Efficient SU2

Un exemplu comun și util de codificare cu entanglare este circuitul efficient_su2 din Qiskit. Remarcabil, acest circuit poate, de exemplu, codifica 8 caracteristici pe doar 2 qubiți. Să vedem acest lucru și apoi să încercăm să înțelegem cum este posibil.

from qiskit.circuit.library import efficient_su2

circuit = efficient_su2(num_qubits=2, reps=1, insert_barriers=True)
circuit.decompose().draw(output="mpl")

Output of the previous code cell

Pe măsură ce scriem starea noastră, vom folosi convenția Qiskit conform căreia qubiții cei mai puțin semnificativi sunt ordonați la dreapta, ca în q2,q1,q0|q_2,q_1,q_0\rangle sau q2q1q0.|q_2\rangle\otimes|q_1\rangle\otimes|q_0\rangle. Aceste stări pot deveni foarte complicate foarte repede, iar acest exemplu rar poate ajuta la explicarea motivului pentru care astfel de stări sunt rareori scrise explicit.

Sistemul nostru pornește din starea 00.|00\rangle. Până la prima barieră (un punct pe care îl etichetăm b1b1), stările noastre sunt:

ψb1=(cos(θ12)0+sin(θ12)eiθ31)(cos(θ02)0+sin(θ02)eiθ21)|\psi\rangle_{b1} = \left(\cos\left(\frac{\theta_1}{2}\right)|0\rangle+\sin\left(\frac{\theta_1}{2}\right)e^{i\theta_3}|1\rangle\right)\otimes\left(\cos\left(\frac{\theta_0}{2}\right)|0\rangle+\sin\left(\frac{\theta_0}{2}\right)e^{i\theta_2}|1\rangle\right)

Aceasta este doar codificarea densă, pe care am văzut-o înainte. Acum, după poarta CNOT, la a doua barieră (b2b2), starea noastră este

ψb2=cos(θ12)cos(θ02)00+cos(θ12)sin(θ02)eiθ211+sin(θ12)cos(θ02)eiθ310+sin(θ12)sin(θ02)eiθ2eiθ301\begin{aligned} |\psi\rangle_{b2} = & \cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_0}{2}\right)|00\rangle+\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_0}{2}\right)e^{i\theta_2}|11\rangle\\ + & \sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_0}{2}\right)e^{i\theta_3}|10\rangle+\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_0}{2}\right)e^{i\theta_2}e^{i\theta_3}|01\rangle \end{aligned}

Acum aplicăm ultimul set de rotații pe qubit singur și colectăm stările similare pentru a obține:

ψfinal=[cos(θ02)(cos(θ12)cos(θ52)sin(θ12)sin(θ52)eiθ3)cos(θ42)+sin(θ02)(cos(θ12)sin(θ52)sin(θ12)cos(θ52)eiθ3)sin(θ42)eiθ2]00+[cos(θ02)(cos(θ12)cos(θ52)sin(θ12)sin(θ52)eiθ3)sin(θ42)+sin(θ02)(cos(θ12)sin(θ52)+sin(θ12)cos(θ52)eiθ3)cos(θ42)eiθ2]eiθ601+[cos(θ02)(cos(θ12)sin(θ52)+sin(θ12)cos(θ52)eiθ3)cos(θ42)sin(θ02)(cos(θ12)cos(θ52)+sin(θ12)sin(θ52)eiθ3)sin(θ42)eiθ2]eiθ710+[cos(θ02)(cos(θ12)sin(θ52)+sin(θ12)cos(θ52)eiθ3)sin(θ42)+sin(θ02)(cos(θ12)cos(θ52)+sin(θ12)sin(θ52)eiθ3)cos(θ42)eiθ2]eiθ6eiθ711\begin{align*} |\psi\rangle_{\text{final}} = & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)-\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)\right.\\ + & \left.\sin\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)-\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] |00\rangle\\ + & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)-\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)\right.\\ + & \left.\sin\left(\frac{\theta_0}{2}\right)\left(-\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] e^{i\theta_6}|01\rangle\\ + & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)\right.\\ - & \left.\sin\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] e^{i\theta_7}|10\rangle\\ + & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)\right.\\ + & \left.\sin\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] e^{i\theta_6}e^{i\theta_7}|11\rangle \end{align*}

Aceasta este probabil prea complicată pentru a fi analizată. În schimb, fă un pas înapoi și gândește-te câți parametri am încărcat în stare: opt. Dar avem doar patru stări de bază computaționale. La prima vedere, poate părea că am încărcat mai mulți parametri decât ar fi logic, deoarece starea finală poate fi scrisă ca ψfinal=c000+c101+c210+c311\psi_\text{final} = c_0|00\rangle+c_1|01\rangle+c_2|10\rangle+c_3|11\rangle. Rețineți însă că fiecare factor prefix este complex! Scrisă astfel:

ψfinal=(a0+ib0)00+(a1+ib1)01+(a2+ib2)10+(a3+ib3)11\psi_\text{final} = (a_0+ib_0)|00\rangle+(a_1+ib_1)|01\rangle+(a_2+ib_2)|10\rangle+(a_3+ib_3)|11\rangle

Poți vedea că avem, într-adevăr, opt parametri în stare, pe care să codificăm cele opt caracteristici ale noastre.

Prin creșterea numărului de qubiți și a numărului de repetări ale straturilor de entanglare și rotație, se pot codifica mult mai multe date. Scrierea funcțiilor de undă devine rapid intractabilă. Dar putem vedea în continuare codificarea în acțiune. Aici codificăm vectorul de date x\vec{x} cu 12 caracteristici, pe un circuit efficient_su2 de 3 qubiți, folosind fiecare dintre porțile parametrizate pentru a codifica o caracteristică diferită.

x=(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2)\vec{x} = (0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2)

În acest vector de date, caracteristicile sunt prezentate într-o anumită ordine. Izolat, nu contează dacă sunt codificate în această ordine sau în ordine inversă. Ce este important este să ții evidența și să fii consistent. Observă în diagrama circuitului că efficient_su2 presupune o anumită ordine de codificare, umplând în mod specific primul strat de porți parametrizate de la qubit 0 la qubit 2, și apoi trecând la stratul următor. Aceasta nu este nici consistent, nici inconsistent cu notația little-endian, deoarece caracteristicile de date nu pot fi ordonate după qubit a priori, înainte ca un circuit de codificare să fi fost specificat.

x = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2]
circuit = efficient_su2(num_qubits=3, reps=1, insert_barriers=True)
encode = circuit.assign_parameters(x)
encode.decompose().draw(output="mpl")

Output of the previous code cell

În loc să crești numărul de qubiți, ai putea alege să mărești numărul de repetări ale straturilor de entanglare și rotație. Dar există limite ale numărului de repetări utile. Cum s-a menționat anterior, există un compromis: circuitele cu mai mulți qubiți sau mai multe repetări ale straturilor de entanglare și rotație pot stoca mai mulți parametri, dar o fac cu o adâncime mai mare a circuitului. Ne vom întoarce la adâncimile unor hărți de caracteristici încorporate mai jos. Câteva dintre metodele de codificare următoare care sunt încorporate în Qiskit au „hartă de caracteristici" (feature map) ca parte a numelor lor. Să subliniem că codificarea datelor într-un circuit cuantic este o mapare de caracteristici, în sensul că duce datele într-un nou spațiu: spațiul Hilbert al qubiților implicați. Relația dintre dimensionalitatea spațiului original de caracteristici și cea a spațiului Hilbert va depinde de circuitul pe care îl folosești pentru codificare.

Harta de caracteristici ZZ

Harta de caracteristici ZZ (ZFM) poate fi interpretată ca o extensie naturală a codificării de fază. ZFM constă din straturi alternante de porți cu un singur qubit: straturi de porți Hadamard și straturi de porți de fază. Fie vectorul de date x\vec{x} cu NN caracteristici. Circuitul cuantic care realizează maparea de caracteristici este reprezentat ca un operator unitar care acționează asupra stării inițiale:

UZFM(x)0N=ϕ(x)\mathscr{U}_{\text{ZFM}}(\vec{x})|0\rangle^{\otimes N}=|\phi(\vec{x})\rangle

unde 0N|0\rangle^{\otimes N} este starea fundamentală de NN qubiți. Această notație este folosită pentru consistență cu referința [4] Havlicek et al. Caracteristicile de date xix_i sunt mapate unu-la-unu cu qubiții corespunzători. De exemplu, dacă ai 8 caracteristici într-un vector de date, atunci ai folosi 8 qubiți. Circuitul ZFM este compus din rr repetări ale unui subcircuit format din straturi de porți Hadamard și straturi de porți de fază. Un strat Hadamard este alcătuit dintr-o poartă Hadamard care acționează asupra fiecărui qubit dintr-un registru de nn qubiți, HHH=HnH \otimes H \otimes \dots \otimes H = H^{\otimes n}, în aceeași etapă a algoritmului. Această descriere se aplică și unui strat de porți de fază în care qubit-ul ii este acționat de P(xi)P(\vec{x}_i). Fiecare poartă PP are o caracteristică ca argument, dar stratul de porți de fază (P(x1)P(xk)P(xN)P(\vec{x}_1)\otimes\ldots P(\vec{x}_k)\otimes\ldots P(\vec{x}_N)) este o funcție a vectorului de date. Unitarul complet al circuitului ZFM cu o singură repetare este:

UZFM=(P(x1)P(xk)P(xN)HN)=(k=1NP(xk))HN\mathscr{U}_{\text{ZFM}}=\big(P(\vec{x}_1)\otimes\ldots P(\vec{x}_k)\otimes\ldots P(\vec{x}_N)H^{\otimes N}\big)=\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N}

Atunci rr repetări ale acestui unitar ar fi

UZFM(r)(x)=s=1r[(k=1NP(xk))HN]\mathscr{U}^{(r)}_{\text{ZFM}}\left(\vec{x}\right)=\prod_{s=1}^{r}\left[\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N}\right]

Caracteristicile de date, xkx_k, sunt mapate la porțile de fază în același mod în toate cele rr repetări. Starea hărții de caracteristici ZFM este o stare produs și este eficientă pentru simularea clasică[4].

Pentru a începe cu un exemplu mic, un circuit ZFM de doi qubiți este codat folosind Qiskit și desenat pentru a afișa structura simplă a circuitului. În exemplu, o singură repetare, r=1r=1, este implementată cu vectorul de date x=(12π,13π)\vec{x} = \left(\textstyle\frac{1}{2}\pi, \textstyle\frac{1}{3}\pi\right). Rețineți că aceasta este scrisă în ordinea standard a unui vector în Python, adică elementul 00 este 12π.\textstyle\frac{1}{2}\pi. Suntem liberi să codificăm această caracteristică 00 pe qubit-ul nostru 00, sau pe al NN-lea. Din nou, nu poate exista întotdeauna o singură mapare 1:1 de la ordinea caracteristicilor la ordinea qubiților, deoarece diferite hărți de caracteristici codifică numere diferite de caracteristici pe fiecare qubit. Din nou, ceea ce este important este să fim conștienți de locul unde este codificată fiecare caracteristică. Când furnizezi o listă de parametri hărții de caracteristici ZZ, aceasta va codifica caracteristica 0 din listă pe qubit-ul cel mai puțin semnificativ cu o poartă parametrizată, adică qubit 0. Deci vom urma această convenție când facem aceasta manual. Vom codifica 12π\textstyle\frac{1}{2}\pi pe qubit-ul 00 și 13π\textstyle\frac{1}{3}\pi pe qubit-ul 11.

Operatorul unitar al circuitului ZFM acționează asupra stării inițiale în felul următor:

UZFM(xˉ)00=P(xˉ)2H200=(P(13π)H0)(P(12π)H0).\mathscr{U}_{\text{ZFM}}(\bar{x})|00\rangle = P(\bar{x})^{\otimes 2} H^{\otimes 2}|00\rangle = \left( P\left(\textstyle\frac{1}{3}\pi\right)H|0\rangle \right) \otimes \left(P\left(\textstyle\frac{1}{2}\pi\right)H|0\rangle\right).

Formula a fost rearanjată în jurul produsului tensorial pentru a evidenția operațiile pe fiecare qubit. Următorul cod Qiskit folosește porțile Hadamard și de fază în mod explicit pentru a arăta structura ZFM:

qc0 = QuantumCircuit(1)
qc1 = QuantumCircuit(1)

qc0.h(0)
qc0.p(pi / 2, 0)

qc1.h(0)
qc1.p(pi / 3, 0)

# Combine circuits qc0 and qc1 into 1 circuit
qc = QuantumCircuit(2)
qc.compose(qc0, [0], inplace=True)
qc.compose(qc1, [1], inplace=True)

qc.draw("mpl", scale=1)

Output of the previous code cell

Acum codificăm același vector de date x=(12π,13π)\vec{x} = \left(\textstyle\frac{1}{2}\pi, \textstyle\frac{1}{3}\pi\right) pe un circuit ZFM cu trei repetări, r=3r=3, folosind clasa Qiskit z_feature_map, care împreună ne dă harta de caracteristici cuantice UZFM(x)\mathscr{U}_{\text{ZFM}}(\vec{x}). În mod implicit în clasa z_feature_map, parametrii β\beta sunt înmulțiți cu 2 înainte de a fi mapați la poarta de fază βP(θ=2β)\beta \rightarrow P(\theta = 2\beta). Pentru a reproduce aceleași codificări ca mai sus, împărțim la 2.

from qiskit.circuit.library import z_feature_map

zfeature_map = z_feature_map(feature_dimension=2, reps=3)
zfeature_map = zfeature_map.assign_parameters([(1 / 2) * pi / 2, (1 / 2) * pi / 3])
zfeature_map.decompose().draw("mpl")

Output of the previous code cell

Evident, aceasta este o mapare diferită față de cea realizată manual mai sus, dar observați consistența în ordinea parametrilor: 12π\textstyle\frac{1}{2}\pi a fost din nou codificat pe qubit-ul 00.

Poți folosi ZFM prin clasa ZFM a Qiskit; poți folosi, de asemenea, această structură ca sursă de inspirație pentru a-ți construi propria mapare de caracteristici.

Harta de caracteristici ZZZZ

Harta de caracteristici ZZZZ (ZZFM) extinde ZFM cu includerea porților de entanglare cu doi qubiți, în special poarta de rotație ZZZZ, RZZ(θ)R_{ZZ}(\theta). Se presupune că ZZFM este în general costisitoare de calculat pe un calculator clasic, spre deosebire de ZFM.

RZZ(θ)R_{ZZ}(\theta) implementează o interacțiune ZZZZ și este maximal entanglatoare pentru θ=12π\theta = \textstyle{\frac{1}{2}}\pi. RZZ(θ)R_{ZZ}(\theta) poate fi descompusă într-o serie de porți pe doi qubiți, după cum se arată în următorul cod Qiskit folosind poarta RZZ și metoda clasei QuantumCircuit decompose. Codificăm o singură caracteristică a vectorului de date x\vec{x}: xk=π.\vec{x}_k=\pi.

qc = QuantumCircuit(2)
qc.rzz(pi, 0, 1)
qc.draw("mpl", scale=1)

Output of the previous code cell

Cum se întâmplă adesea, o vedem reprezentată ca o unitate asemănătoare unei porți, până când folosim .decompose() pentru a vedea toate porțile componente.

qc.decompose().draw("mpl", scale=1)

Output of the previous code cell

Datele sunt mapate cu o rotație de fază P(θ)=eiθ/2RZ(θ)P(\theta) = e^{i\theta/2}R_Z(\theta) pe al doilea qubit. Poarta RZZ(θ)R_{ZZ}(\theta) entanglează cei doi qubiți pe care operează, cu un grad de entanglare determinat de valoarea caracteristicii codificate.

Circuitul complet ZZFM constă dintr-o poartă Hadamard și o poartă de fază, ca în ZFM, urmată de entanglarea descrisă mai sus. O singură repetare a circuitului ZZFM este:

UZZFM(x)=UZZ(x)(P(x1)P(xk)P(xN)HN)=UZZ(x)(k=1NP(xk))HN,\mathscr{U}_{\text{ZZFM}}(\vec{x}) = U_{ZZ}(\vec{x})\big(P(\vec{x}_1)\otimes\ldots P(\vec{x}_k)\otimes\ldots P(\vec{x}_N)H^{\otimes N}\big)=U_{ZZ}(\vec{x})\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N},

unde UZZ(x)U_{ZZ}(\vec{x}) conține un strat de porți ZZ structurat printr-o schemă de entanglare. Mai jos sunt prezentate mai multe scheme de entanglare în blocuri de cod. Structura lui UZZ(x)U_{ZZ}(\vec{x}) include, de asemenea, o funcție care combină caracteristicile de date din qubiții entanglați în felul următor. Să spunem că poarta RZZR_{ZZ} urmează să fie aplicată qubiților pp și qq. În stratul de fază, acești qubiți au porți de fază care codifică xp\vec{x}_p și xq\vec{x}_q pe ei, respectiv. Argumentul θq,p\theta_{q,p} al lui RZZ,q,p(θq,p)R_{ZZ,q,p}(\theta_{q,p}) nu va fi pur și simplu una dintre aceste caracteristici sau cealaltă, ci o funcție deseori notată prin ϕ\phi (a nu se confunda cu unghiul azimutal):

θq,pϕ(xq,xp)=2(πxq)(πxp).\theta_{q,p} \rightarrow \phi(\vec{x}_q, \vec{x}_p) = 2(\pi-\vec{x}_q)(\pi-\vec{x}_p).

Vom vedea aceasta în mai multe exemple de mai jos. Extensia la repetări multiple este aceeași ca în cazul z_feature_map:

UZZFM(r)(x)=s=1r[UZZ(x)(k=1NP(xk))HN].\mathscr{U}^{(r)}_{\text{ZZFM}}\left(\vec{x}\right)=\prod_{s=1}^{r}\left[U_{ZZ}(\vec{x})\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N}\right].

Pe măsură ce operatorii au crescut în complexitate, să codificăm mai întâi un vector de date x=(x0,x1)\vec{x} = (x_0, x_1) cu un ZZFM de doi qubiți și o repetare folosind următorul cod:

from qiskit.circuit.library import zz_feature_map

feature_dim = 2
zzfeature_map = zz_feature_map(
feature_dimension=feature_dim, entanglement="linear", reps=1
)
zzfeature_map.decompose(reps=1).draw("mpl", scale=1)

Output of the previous code cell

În mod implicit în Qiskit, caracteristicile (x1,x2)(\vec{x}_1, \vec{x}_2) sunt mapate împreună la RZZ(θ)R_{ZZ}(\theta) prin această funcție de mapare θ1,2=ϕ(x1,x2)=2(πx1)(πx2)\theta_{1,2} = \phi(\vec{x}_1, \vec{x}_2) = 2(\pi-\vec{x}_1)(\pi-\vec{x}_2). Qiskit permite utilizatorului să personalizeze funcția ϕ\phi (sau ϕS\phi_S unde SS este mulțimea perechilor de qubiți cuplaţi prin porți RZZR_{ZZ}) ca pas de preprocesare.

Trecând la un vector de date cu patru dimensiuni x=(x1,x2,x3,x4)\vec{x} = (\vec{x}_1, \vec{x}_2, \vec{x}_3, \vec{x}_4) și mapând la un ZZFM cu patru qubiți și o repetare, putem începe să vedem maparea ϕ\phi pentru diverse perechi de qubiți. Putem vedea, de asemenea, semnificația entanglării „liniare":

feature_dim = 4
zzfeature_map = zz_feature_map(
feature_dimension=feature_dim, entanglement="linear", reps=1
)
zzfeature_map.decompose().draw("mpl", scale=1)

Output of the previous code cell

În schema de entanglare liniară, perechile de qubiți vecini (numerotați) din acest circuit sunt entanglați. Există și alte scheme de entanglare încorporate în Qiskit, inclusiv circular și full.

Harta de caracteristici Pauli

Harta de caracteristici Pauli (PFM) este generalizarea ZFM și ZZFM pentru a folosi porți Pauli arbitrare. Harta de caracteristici Pauli are o formă foarte similară cu cele două hărți de caracteristici anterioare. Pentru rr repetări ale codificării celor NN caracteristici ale vectorului x,\vec{x},

UPFM(x)=s=1rU(x)Hn.\mathscr{U}_{\text{PFM}}(\vec{x}) = \prod_{s=1}^{r} U(\vec{x}) H^{\otimes n}.

Pentru PFM, U(x)U(\vec{x}) este generalizat la un operator unitar de expansiune Pauli. Iată o formă mai generalizată a hărților de caracteristici considerate până acum:

U(x)=exp(iSIϕS(x)iSσi),U(\vec{x}) = \exp\left(i \sum_{S \in\mathcal{I}} \phi_S(\vec{x}) \prod_{i \in S} \sigma_i \right),

unde σi\sigma_i este un operator Pauli, σiI,X,Y,Z\sigma_i \in {I,X,Y,Z}. Aici I\mathcal{I} este mulțimea tuturor conectivităților de qubiți, determinată de harta de caracteristici, inclusiv mulțimea de qubiți acționați de porți cu un singur qubit. Adică, pentru o hartă de caracteristici în care qubit-ul 0 a fost acționat de o poartă de fază, iar qubiții 2 și 3 au fost acționați de o poartă RZZR_{ZZ}, mulțimea I\mathcal{I} ar include {{0},{2,3}}\{\{0\},\{2,3\}\}. SS parcurge toate elementele acelei mulțimi. În hărțile de caracteristici anterioare, funcția ϕS(x)\phi_S(\vec{x}) era implicată fie exclusiv cu porți cu un singur qubit, fie exclusiv cu porți cu doi qubiți. Iată cum o definim în general:

ϕS(x)={xiif S={i} (single-qubit)jS(πxj)if S2 (multi-qubit)\phi_S(\vec{x})= \begin{cases} x_i & \text{if } S= \{i\} \text{ (single-qubit)}\\ \prod_{j\in{S}}(\pi-x_j) & \text{if } |S|\ge2 \text{ (multi-qubit)}\\ \end{cases}

Pentru documentație, consultă documentația clasei Qiskit Pauli feature map). În ZZFM, operatorul σi\sigma_i este restricționat la ZiZ_i.

O modalitate de a înțelege unitarul de mai sus este prin analogie cu propagatorul dintr-un sistem fizic. Unitarul de mai sus este un operator de evoluție unitară, exp(itH)\exp(it\mathcal{H}), pentru un Hamiltonian, H\mathcal{H}, similar cu modelul Ising, unde parametrul de timp, tt, este înlocuit cu valorile datelor pentru a conduce evoluția. Expansiunea acestui operator unitar dă circuitul PFM. Conectivitățile de entanglare din SS pot fi interpretate ca cuplaje Ising într-o rețea de spini. Să considerăm un exemplu de operatori Pauli YY și XXXX reprezentând acele interacțiuni de tip Ising. Qiskit oferă o clasă pauli_feature_map pentru instanțierea unui PFM cu o alegere de porți cu un singur qubit și nn qubiți, care în acest exemplu vor fi transmise ca șiruri Pauli 'Y' și 'XX'. De obicei, nn este 1 sau 2 pentru interacțiuni cu un singur qubit și, respectiv, doi qubiți. Schema de entanglare este „liniară", adică doar qubiții vecini din circuitul cuantic sunt cuplați. Rețineți că aceasta nu corespunde qubiților vecini pe calculatorul cuantic însuși, deoarece acest circuit cuantic este un nivel de abstractizare.

from qiskit.circuit.library import pauli_feature_map

feature_dim = 3
pfmap = pauli_feature_map(
feature_dimension=feature_dim, entanglement="linear", reps=1, paulis=["Y", "XX"]
)

pfmap.decompose().draw("mpl", scale=1.5)

Output of the previous code cell

Qiskit oferă un parametru, α\alpha, în hărțile de caracteristici Pauli pentru a controla scalarea rotațiilor Pauli.

U(xˉ)=exp(iαS[n]ϕS(xˉ)iSσi)U(\bar{x}) = \exp\left(i \alpha \sum_{S\subseteq[n]} \phi_S(\bar{x}) \prod_{i \in S} \sigma_i \right)

Valoarea implicită a lui α\alpha este 22. Optimizând valoarea sa în intervalul, de exemplu, [0,4],[0,4], se poate alinia mai bine un nucleu cuantic la date.

Iată că vizualizăm diverse hărți de caracteristici Pauli pentru circuite de doi qubiți, pentru a obține o imagine mai bună a gamei de posibilități.

from qiskit.visualization import circuit_drawer
import matplotlib.pyplot as plt

feature_dim = 2
fig, axs = plt.subplots(9, 2)
i_plot = 0
for paulis in [
["I"],
["X"],
["Y"],
["Z"],
["XX"],
["XY"],
["XZ"],
["YY"],
["YZ"],
["ZZ"],
["X", "ZZ"],
["Y", "ZZ"],
["Z", "ZZ"],
["X", "YZ"],
["Y", "YZ"],
["Z", "YZ"],
["YY", "ZZ"],
["XY", "ZZ"],
]:
pfmap = pauli_feature_map(feature_dimension=feature_dim, paulis=paulis, reps=1)
circuit_drawer(
pfmap.decompose(),
output="mpl",
style={"backgroundcolor": "#EEEEEE"},
ax=axs[int((i_plot - i_plot % 2) / 2), i_plot % 2],
)
axs[int((i_plot - i_plot % 2) / 2), i_plot % 2].title.set_text(paulis)
i_plot += 1

fig.set_figheight(16)
fig.set_figwidth(16)

Output of the previous code cell

Cele de mai sus pot, desigur, fi extinse pentru a include alte permutări și repetări ale matricelor Pauli. Cursanții sunt încurajați să experimenteze cu acele opțiuni.

Recapitularea hărților de caracteristici încorporate

Ai văzut mai multe scheme de codificare a datelor într-un circuit cuantic:

  • Codificare în bază
  • Codificare de amplitudine
  • Codificare unghiulară
  • Codificare de fază
  • Codificare densă

Ai văzut cum să îți construiești propriile hărți de caracteristici folosind aceste scheme de codificare și ai văzut patru hărți de caracteristici încorporate care profită de codificarea unghiulară și de fază:

  • Efficient SU2
  • Harta de caracteristici Z
  • Harta de caracteristici ZZ
  • Harta de caracteristici Pauli

Aceste hărți de caracteristici încorporate s-au deosebit una de alta în mai multe moduri:

  • Adâncimea pentru un număr dat de caracteristici codificate
  • Numărul de qubiți necesar pentru un număr dat de caracteristici
  • Gradul de entanglare (evident legat de celelalte diferențe)

Codul de mai jos aplică aceste patru hărți de caracteristici încorporate la codificarea unui set de caracteristici și trasează adâncimea cu doi qubiți a circuitului rezultat. Deoarece ratele de eroare cu doi qubiți sunt mult mai mari decât ratele de eroare ale porților cu un singur qubit, cineva ar putea fi cel mai interesat în mod rezonabil de adâncimea porților cu doi qubiți. În codul de mai jos, obținem numărătoarea tuturor porților dintr-un circuit prin descompunerea mai întâi a circuitului și apoi folosind count_ops(), după cum se arată mai jos. Iată că porțile cu doi qubiți care ne interesează sunt porțile 'cx':

# Initializing parameters and empty lists for depths
x = [0.1, 0.2]
n_data = []
zz2gates = []
su22gates = []
z2gates = []
p2gates = []

# Generating feature maps
for n in range(3, 10):
x.append(n / 10)
zzcircuit = zz_feature_map(n, reps=1, insert_barriers=True)
zcircuit = z_feature_map(n, reps=1, insert_barriers=True)
su2circuit = efficient_su2(n, reps=1, insert_barriers=True)
pcircuit = pauli_feature_map(n, reps=1, paulis=["XX"], insert_barriers=True)
# Getting the cx depths
zzcx = zzcircuit.decompose().count_ops().get("cx")
zcx = zcircuit.decompose().count_ops().get("cx")
su2cx = su2circuit.decompose().count_ops().get("cx")
pcx = pcircuit.decompose().count_ops().get("cx")

# Appending the cx gate counts to the lists. We shift the zz and pauli data points, because they overlap.
n_data.append(n)
zz2gates.append(zzcx - 0.5)
z2gates.append(0)
su22gates.append(su2cx)
p2gates.append(pcx + 0.5)

# Plot the output
plt.plot(n_data, p2gates, "bo")
plt.plot(n_data, zz2gates, "ro")
plt.plot(n_data, su22gates, "yo")
plt.plot(n_data, z2gates, "go")
plt.ylabel("CX Gates")
plt.xlabel("Data elements")
plt.legend(["Pauli", "ZZ", "SU2", "Z"])
# plt.suptitle('zz_feature_map(n)')
plt.show()

În general, hărțile de caracteristici Pauli și ZZ vor rezulta în adâncimi mai mari ale circuitului și un număr mai mare de porți cu 2 qubiți față de efficient_su2 și hărțile de caracteristici Z.

Deoarece hărțile de caracteristici încorporate în Qiskit sunt aplicabile pe scară largă, de multe ori nu va trebui să ne concepem propriile, mai ales în faza de învățare. Cu toate acestea, experții în învățarea automată cuantică se vor întoarce probabil la subiectul proiectării propriilor mapări de caracteristici, pe măsură ce abordează două provocări complicate:

  1. Hardware modern: prezența zgomotului și suprasarcina mare a codului de corectare a erorilor înseamnă că aplicațiile actuale vor trebui să ia în considerare lucruri precum eficiența hardware și minimizarea adâncimii porților cu doi qubiți.

  2. Mapări care se potrivesc problemei la îndemână: este un lucru să spui că zz_feature_map, de exemplu, este dificil de simulat clasic și, prin urmare, interesant. Este cu totul altceva ca zz_feature_map să fie ideal potrivit sarcinii tale de învățare automată sau setului de date. Performanța diferitelor circuite cuantice parametrizate pe diferite tipuri de date este un domeniu activ de investigare.

Încheiem cu o notă privind eficiența hardware.

Maparea caracteristicilor eficientă hardware

O mapare a caracteristicilor eficientă hardware este una care ține cont de constrângerile calculatoarelor cuantice reale, cu scopul de a reduce zgomotul și erorile din calcul. Atunci când rulezi circuite cuantice pe calculatoare cuantice de generație apropiată, există multe strategii pentru a reduce zgomotul inerent hardware-ului. O strategie principală pentru eficiența hardware este minimizarea adâncimii circuitului cuantic, astfel încât zgomotul și decoerența să aibă mai puțin timp să corupă calculul. Adâncimea unui circuit cuantic reprezintă numărul de pași de porți aliniați temporal necesari pentru a finaliza întregul calcul (după optimizarea circuitului)[5]. Reamintește-ți că adâncimea circuitului abstract, logic poate fi mult mai mică decât adâncimea după ce circuitul este transpilat pentru un calculator cuantic real.

Transpilarea este procesul de conversie a circuitului cuantic dintr-o abstractizare de nivel înalt într-unul pregătit să ruleze pe un calculator cuantic real, ținând cont de constrângerile hardware-ului. Un calculator cuantic are un set nativ de porți cu un qubit și cu doi qubiți. Asta înseamnă că toate porțile din codul Qiskit trebuie transpilate în setul de porți hardware native. De exemplu, în ibm_torino, un QPU dotat cu un procesor Heron r1, finalizat în 2023, porțile native sau de bază sunt \{CZ, ID, RZ, SX, X\}. Acestea sunt poarta cu doi qubiți controlled-Z și porțile cu un qubit numite identitate, rotație-ZZ, rădăcina pătrată a lui NOT și NOT, respectiv, oferind un set universal. Când implementezi porți cu mai mulți qubiți ca un subcircuit echivalent, sunt necesare porți fizice CZCZ cu doi qubiți, împreună cu alte porți cu un qubit disponibile în hardware. În plus, pentru a efectua o poartă cu doi qubiți pe o pereche de qubiți care nu sunt cuplați fizic, se adaugă porți SWAP pentru a muta stările qubiților între qubiți pentru a permite cuplarea, ceea ce duce la o extensie inevitabilă a circuitului. Folosind argumentul optimization care poate fi setat de la 0 până la un nivel maxim de 3. Pentru un control și o personalizare mai mari, pipeline-ul de transpilare poate fi gestionat cu Qiskit Pass Manager. Consultă documentația Qiskit Transpiler pentru mai multe informații despre transpilare.

În Havlicek et al. 2019 [2], unul dintre modurile în care autorii obțin eficiența hardware este prin utilizarea mapei de caracteristici ZZZZ, deoarece aceasta este o expansiune de ordinul doi (vezi secțiunea „Mapa de caracteristici ZZZZ" de mai sus). O expansiune de ordinul NN are porți cu NN qubiți. Calculatoarele cuantice IBM® nu au porți native cu NN qubiți, unde N>2N>2, deci pentru a le implementa ar fi necesară descompunerea în porți CNOT cu doi qubiți disponibile în hardware. O a doua modalitate prin care autorii minimizează adâncimea este alegerea unei topologii de cuplare ZZZZ care se mapează direct la cuplările arhitecturii. O optimizare suplimentară pe care o realizează este țintirea unui subcircuit hardware de înaltă performanță, adecvat conectat. Alte aspecte de luat în considerare sunt minimizarea numărului de repetiții ale mapei de caracteristici și alegerea unei scheme de întrețesere personalizate de adâncime redusă sau „liniare" în locul schemei „complete" care întrețese toți qubiții.

Imagine codificare date

Graficul de mai sus arată o rețea de noduri și muchii care reprezintă qubiți fizici, respectiv cuplări hardware. Harta de cuplare și performanța ibm_torino sunt prezentate cu toate posibilele porți de cuplare CZ cu doi qubiți. Qubiții sunt codificați cromatic pe o scală bazată pe timpul de relaxare T1 în microsecunde (μs), unde timpii T1 mai lungi sunt mai buni și sunt în nuanțe mai deschise. Muchiile de cuplare sunt codificate cromatic în funcție de eroarea CZ, unde nuanțele mai închise sunt mai bune. Informațiile despre specificațiile hardware pot fi accesate în schema de configurare a backend-ului hardware IBMQBackend.configuration().

Referințe

  1. Maria Schuld and Francesco Petruccione, Supervised Learning with Quantum Computers, Springer 2018, doi:10.1007/978-3-319-96424-9.
  2. Vojtech Havlicek et al., "Supervised Learning with Quantum Enhanced Feature Spaces." Nature, vol. 567 (2019): 209–212. https://arxiv.org/abs/1804.11326.
  3. Ryan LaRose and Brian Coyle, "Robust data encodings for quantum classifiers", Physical Review A 102, 032420 (2020), doi:10.1103/PhysRevA.102.032420, arXiv:2003.01695.
  4. Lou Grover and Terry Rudolph. "Creating Superpositions That Correspond to Efficiently Integrable Probability Distributions." arXiv:quant-ph/0208112, August 15, 2002, https://arxiv.org/abs/quant-ph/0208112.
  5. Adrián Pérez-Salinas, Alba Cervera-Lierta, Elies Gil-Fuster, José I. Latorre, "Data re-uploading for a universal quantum classifier", Quantum 4, 226 (2020), ArXiv.org/abs/1907.02085.
  6. Maria Schuld, Ryan Sweke, Johannes Jakob Meyer, "The effect of data encoding on the expressive power of variational quantum machine learning models", Phys. Rev. A 103, 032430 (2021), arxiv.org/abs/2008.08605
import qiskit

qiskit.version.get_version_info()