Sari la conținutul principal

Algoritmul lui Deutsch

Algoritmul lui Deutsch rezolvă problema parității pentru cazul special în care n=1.n = 1. În contextul calculului cuantic, această problemă este uneori denumită problema lui Deutsch, iar în această lecție vom folosi această nomenclatură.

Mai precis, intrarea este reprezentată printr-o funcție f:ΣΣf:\Sigma \rightarrow \Sigma de la un bit la un bit. Există patru astfel de funcții:

af1(a)0010af2(a)0011af3(a)0110af4(a)0111\rule[-10mm]{0mm}{10mm} \begin{array}{c|c} a & f_1(a)\\ \hline 0 & 0\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_2(a)\\ \hline 0 & 0\\ 1 & 1 \end{array} \qquad \begin{array}{c|c} a & f_3(a)\\ \hline 0 & 1\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_4(a)\\ \hline 0 & 1\\ 1 & 1 \end{array}

Prima și ultima dintre aceste funcții sunt constante, iar celelalte două din mijloc sunt echilibrate, adică cele două valori posibile de ieșire ale funcției apar de același număr de ori pe măsură ce parcurgem toate intrările. Problema lui Deutsch constă în a determina căreia dintre aceste două categorii îi aparține funcția de intrare: constantă sau echilibrată.

Problema lui Deutsch

Intrare: o funcție f:{0,1}{0,1}f:\{0,1\}\rightarrow\{0,1\}
Ieșire: 00 dacă ff este constantă, 11 dacă ff este echilibrată

Dacă privim funcția de intrare ff din problema lui Deutsch ca pe un acces aleator la un șir de caractere, ne gândim la un șir de doi biți: f(0)f(1).f(0)f(1).

functionstringf100f201f310f411\begin{array}{cc} \mathsf{function} & \mathsf{string}\\ \hline f_1 & 00 \\ f_2 & 01 \\ f_3 & 10 \\ f_4 & 11 \end{array}

Privind astfel, problema lui Deutsch constă în calcularea parității (sau, echivalent, a SAU-EXCLUSIV) celor doi biți.

Orice algoritm clasic de interogare care rezolvă corect această problemă trebuie să interogheze ambii biți: f(0)f(0) și f(1).f(1). Dacă aflăm, de exemplu, că f(1)=1,f(1) = 1, răspunsul poate fi în continuare 00 sau 1,1, în funcție de dacă f(0)=1f(0) = 1 sau f(0)=0,f(0) = 0, respectiv. Fiecare alt caz este similar; cunoașterea a doar unui singur bit din doi nu oferă nicio informație despre paritatea lor. Prin urmare, circuitul boolean descris în secțiunea anterioară este cel mai bun rezultat posibil din punctul de vedere al numărului de interogări necesare pentru a rezolva această problemă.

Descrierea circuitului cuantic

Algoritmul lui Deutsch rezolvă problema lui Deutsch folosind o singură interogare, oferind astfel un avantaj cuantificabil al calculului cuantic față de cel clasic. Acesta poate părea un avantaj modest — o interogare față de două — dar trebuie să începem de undeva. Progresele științifice au uneori origini aparent modeste.

Iată un circuit cuantic care descrie algoritmul lui Deutsch:

Algoritmul lui Deutsch

Analiză

Pentru a analiza algoritmul lui Deutsch, vom parcurge acțiunea circuitului de mai sus și vom identifica stările qubiților la momentele sugerate de această figură:

Stările din timpul algoritmului lui Deutsch

Starea inițială este 10,\vert 1\rangle \vert 0 \rangle, iar cele două operații Hadamard din partea stângă a circuitului transformă această stare în

π1=+=12(01)0+12(01)1.\vert \pi_1 \rangle = \vert - \rangle \vert + \rangle = \frac{1}{2} \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \vert 0\rangle + \frac{1}{2} \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \vert 1\rangle.

(Ca întotdeauna, urmăm convenția de ordonare a qubiților din Qiskit, care plasează qubit-ul de sus la dreapta și qubit-ul de jos la stânga.) Poate părea neintuitivă scrierea acestei stări produs parțial distribuită (lăsând stările qubit-ului 1 factorizate), dar acest lucru va face expresiile ulterioare mai compacte.

În continuare, se aplică poarta UfU_f. Conform definiției porții UfU_f, valoarea funcției ff pentru starea clasică a qubit-ului din dreapta/sus este aplicată prin XOR pe qubit-ul din stânga/jos, transformând π1\vert \pi_1\rangle în starea

π2=12(0f(0)1f(0))0+12(0f(1)1f(1))1.\vert \pi_2 \rangle = \frac{1}{2} \bigl( \vert 0 \oplus f(0) \rangle - \vert 1 \oplus f(0) \rangle \bigr) \vert 0 \rangle + \frac{1}{2} \bigl( \vert 0 \oplus f(1) \rangle - \vert 1 \oplus f(1) \rangle \bigr) \vert 1 \rangle.

Putem simplifica această expresie observând că formula

0a1a=(1)a(01)\vert 0 \oplus a\rangle - \vert 1 \oplus a\rangle = (-1)^a \bigl( \vert 0\rangle - \vert 1\rangle \bigr)

este valabilă pentru ambele valori posibile aΣ.a\in\Sigma. Mai explicit, cele două cazuri sunt următoarele.

0010=01=(1)0(01)0111=10=(1)1(01)\begin{aligned} \vert 0 \oplus 0\rangle - \vert 1 \oplus 0\rangle & = \vert 0 \rangle - \vert 1 \rangle = (-1)^0 \bigl( \vert 0\rangle - \vert 1\rangle \bigr)\\ \vert 0 \oplus 1\rangle - \vert 1 \oplus 1\rangle & = \vert 1 \rangle - \vert 0\rangle = (-1)^1 \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \end{aligned}

Astfel, putem exprima alternativ π2\vert\pi_2\rangle în felul următor:

π2=12(1)f(0)(01)0+12(1)f(1)(01)1=((1)f(0)0+(1)f(1)12).\begin{aligned} \vert\pi_2\rangle & = \frac{1}{2} (-1)^{f(0)} \bigl( \vert 0 \rangle - \vert 1 \rangle \bigr) \vert 0 \rangle + \frac{1}{2} (-1)^{f(1)} \bigl( \vert 0 \rangle - \vert 1 \rangle \bigr) \vert 1 \rangle \\ & = \vert - \rangle \biggl( \frac{(-1)^{f(0)} \vert 0\rangle + (-1)^{f(1)} \vert 1\rangle}{\sqrt{2}}\biggr). \end{aligned}

Tocmai s-a întâmplat ceva interesant! Deși acțiunea porții UfU_f asupra stărilor din baza standard lasă qubit-ul din dreapta/sus neschimbat și aplică XOR cu valoarea funcției pe qubit-ul din stânga/jos, vedem că starea qubit-ului din dreapta/sus s-a schimbat (în general), în timp ce starea qubit-ului din stânga/jos rămâne aceeași — aflându-se în starea \vert - \rangle atât înainte, cât și după aplicarea porții UfU_f. Acest fenomen este cunoscut sub numele de phase kickback și vom vorbi mai mult despre el în curând.

Cu o ultimă simplificare, și anume extragerea factorului (1)f(0)(-1)^{f(0)} în afara sumei, obținem această expresie a stării π2\vert\pi_2\rangle:

π2=(1)f(0)(0+(1)f(0)f(1)12)={(1)f(0)+if f(0)f(1)=0(1)f(0)if f(0)f(1)=1.\begin{aligned} \vert\pi_2\rangle & = (-1)^{f(0)} \vert - \rangle \biggl( \frac{\vert 0\rangle + (-1)^{f(0) \oplus f(1)} \vert 1\rangle}{\sqrt{2}}\biggr) \\ & = \begin{cases} (-1)^{f(0)} \vert - \rangle \vert + \rangle & \text{if $f(0) \oplus f(1) = 0$}\\[1mm] (-1)^{f(0)} \vert - \rangle \vert - \rangle & \text{if $f(0) \oplus f(1) = 1$}. \end{cases} \end{aligned}

Observă că în această expresie avem f(0)f(1)f(0) \oplus f(1) în exponentul lui 1-1 în loc de f(1)f(0),f(1) - f(0), ceea ce ne-am putea aștepta dintr-un punct de vedere pur algebric, dar obținem același rezultat în ambele cazuri. Aceasta deoarece valoarea (1)k(-1)^k pentru orice număr întreg kk depinde doar de dacă kk este par sau impar.

Aplicând ultima poartă Hadamard qubit-ului de sus, obținem starea

π3={(1)f(0)0if f(0)f(1)=0(1)f(0)1if f(0)f(1)=1,\vert \pi_3 \rangle = \begin{cases} (-1)^{f(0)} \vert - \rangle \vert 0 \rangle & \text{if $f(0) \oplus f(1) = 0$}\\[1mm] (-1)^{f(0)} \vert - \rangle \vert 1 \rangle & \text{if $f(0) \oplus f(1) = 1$}, \end{cases}

care conduce la rezultatul corect cu probabilitatea 11 atunci când qubit-ul din dreapta/sus este măsurat.

Observații suplimentare despre phase kickback

Înainte de a continua, să privim analiza de mai sus dintr-un unghi ușor diferit, care poate clarifica fenomenul de phase kickback.

În primul rând, observă că formula de mai jos este valabilă pentru orice alegere de biți b,cΣ.b,c\in\Sigma.

bc=Xcb\vert b \oplus c\rangle = X^c \vert b \rangle

Aceasta poate fi verificată pentru cele două valori posibile c=0c = 0 și c=1c = 1:

b0=b=Ib=X0bb1=¬b=Xb=X1b.\begin{aligned} \vert b \oplus 0 \rangle & = \vert b\rangle = \mathbb{I} \vert b \rangle = X^0 \vert b \rangle\\ \vert b \oplus 1 \rangle & = \vert \neg b\rangle = X \vert b \rangle = X^1 \vert b \rangle. \end{aligned}

Folosind această formulă, vedem că

Uf(ba)=bf(a)a=(Xf(a)b)aU_f \bigl(\vert b\rangle \vert a \rangle\bigr) = \vert b \oplus f(a) \rangle \vert a \rangle = \bigl(X^{f(a)}\vert b \rangle\bigr) \vert a \rangle

pentru orice alegere de biți a,bΣ.a,b\in\Sigma. Deoarece această formulă este adevărată pentru b=0b=0 și b=1,b=1, vedem prin liniaritate că

Uf(ψa)=(Xf(a)ψ)aU_f \bigl( \vert \psi \rangle \vert a \rangle \bigr) = \bigl(X^{f(a)}\vert \psi \rangle\bigr) \vert a \rangle

pentru orice vector de stare qubit ψ,\vert \psi\rangle, și prin urmare

Uf(a)=(Xf(a))a=(1)f(a)a.U_f \bigl( \vert - \rangle \vert a \rangle \bigr) = \bigl(X^{f(a)} \vert - \rangle \bigr) \vert a \rangle = (-1)^{f(a)} \vert - \rangle \vert a \rangle.

Cheia care face ca asta să funcționeze este că X=.X\vert - \rangle = - \vert - \rangle. În termeni matematici, vectorul \vert - \rangle este un vector propriu al matricei XX cu valoarea proprie 1.-1.

Vom discuta vectorii proprii și valorile proprii în mai mare detaliu în lecția viitoare despre estimarea fazei și factorizare, unde fenomenul de phase kickback este generalizat la alte operații unitare.

Ținând cont că scalarele se propagă liber prin produse tensoriale, găsim o modalitate alternativă de a explica cum operația UfU_f transformă π1\vert \pi_1\rangle în π2\vert \pi_2\rangle în analiza de mai sus:

π2=Uf(+)=12Uf(0)+12Uf(1)=((1)f(0)0+(1)f(1)12).\begin{aligned} \vert \pi_2 \rangle & = U_f \bigl( \vert - \rangle \vert + \rangle \bigr)\\ & = \frac{1}{\sqrt{2}} U_f \bigl(\vert - \rangle \vert 0\rangle \bigr) + \frac{1}{\sqrt{2}} U_f \bigl(\vert - \rangle \vert 1\rangle \bigr)\\ & = \vert - \rangle \biggl( \frac{(-1)^{f(0)} \vert 0\rangle + (-1)^{f(1)} \vert 1\rangle}{\sqrt{2}}\biggr). \end{aligned}

Implementare în Qiskit

Acum să vedem cum putem implementa algoritmul lui Deutsch în Qiskit. Vom începe cu o verificare a versiunii și apoi vom efectua importurile necesare exclusiv pentru această implementare. Pentru implementările altor algoritmi care urmează, vom efectua importurile necesare separat, pentru o modularitate mai mare.

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

print(__version__)
2.1.1
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

Mai întâi vom defini un Circuit cuantic care implementează un query gate pentru una dintre cele patru funcții f1,f_1, f2,f_2, f3,f_3, sau f4f_4 de la un bit la un bit, descrise anterior. Cum am menționat deja, implementarea query gate-urilor nu face cu adevărat parte din algoritmul lui Deutsch în sine; aici arătăm practic o modalitate de a pregăti intrarea, sub forma unei implementări cu circuit a unui query gate.

def deutsch_function(case: int):
# This function generates a quantum circuit for one of the 4 functions
# from one bit to one bit

if case not in [1, 2, 3, 4]:
raise ValueError("`case` must be 1, 2, 3, or 4.")

f = QuantumCircuit(2)
if case in [2, 3]:
f.cx(0, 1)
if case in [3, 4]:
f.x(1)
return f

Putem vedea cum arată fiecare circuit folosind metoda draw. Iată circuitul pentru funcția f3.f_3.

display(deutsch_function(3).draw(output="mpl"))

Output of the previous code cell

În continuare vom crea circuitul cuantic propriu-zis pentru algoritmul lui Deutsch, înlocuind query gate-ul cu o implementare de circuit cuantic furnizată ca argument. În scurt timp vom introduce unul dintre cele patru circuite definite de funcția deutsch_function pe care am definit-o anterior. Barierele sunt incluse pentru a arăta separarea vizuală dintre implementarea query gate-ului și restul circuitului.

def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in Deutsch's algorithm.

n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)

qc.x(n)
qc.h(range(n + 1))

qc.barrier()
qc.compose(function, inplace=True)
qc.barrier()

qc.h(range(n))
qc.measure(range(n), range(n))

return qc

Din nou, putem vedea cum arată circuitul folosind metoda draw.

display(compile_circuit(deutsch_function(3)).draw(output="mpl"))

Output of the previous code cell

În final, vom crea o funcție care rulează circuitul definit anterior o singură dată și afișează rezultatul corespunzător: „constant" sau „balanced."

def deutsch_algorithm(function: QuantumCircuit):
# Determine if a one-bit function is constant or balanced.

qc = compile_circuit(function)

result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if measurements[0] == "0":
return "constant"
return "balanced"

Putem rula acum algoritmul lui Deutsch pe oricare dintre cele patru funcții definite mai sus.

f = deutsch_function(3)
display(deutsch_algorithm(f))
'balanced'