Algorytm Deutscha-Jozsy
Algorytm Deutscha przewyższa wszystkie klasyczne algorytmy w pewnym problemie zapytaniowym, ale przewaga ta jest dość skromna: jedno zapytanie zamiast dwóch. Algorytm Deutsch-Jozsa rozszerza tę przewagę — a w rzeczywistości może być używany do rozwiązywania kilku różnych problemów zapytaniowych.
Oto opis algorytmu Deutsch-Jozsa w postaci obwodu kwantowego. W zależności od konkretnego rozwiązywanego problemu może być również wymagany dodatkowy klasyczny krok przetwarzania końcowego, którego nie przedstawiono na rysunku.
Oczywiście, nie omówiliśmy jeszcze, jakie problemy rozwiązuje ten algorytm; zostanie to zrobione w dwóch kolejnych sekcjach.
Problem Deutsch-Jozsa
Zaczniemy od problemu zapytaniowego, który algorytm Deutsch-Jozsa miał pierwotnie rozwiązywać, znanego jako problem Deutsch-Jozsa.
Funkcja wejściowa dla tego problemu ma postać dla dowolnej dodatniej liczby całkowitej Podobnie jak w problemie Deutscha, zadaniem jest zwrócenie wartości , jeśli jest stała, oraz , jeśli jest zrównoważona, co ponownie oznacza, że liczba ciągów wejściowych, dla których funkcja przyjmuje wartość , jest równa liczbie ciągów wejściowych, dla których funkcja przyjmuje wartość .
Zauważ, że gdy jest większe niż istnieją funkcje postaci , które nie są ani stałe, ani zrównoważone. Na przykład funkcja zdefiniowana jako
nie należy do żadnej z tych dwóch kategorii. W problemie Deutsch-Jozsa po prostu nie przejmujemy się takimi funkcjami — są one traktowane jako wejścia „nieistotne”. Oznacza to, że dla tego problemu mamy obietnicę, że jest albo stała, albo zrównoważona.
Algorytm Deutsch-Jozsa, za pomocą pojedynczego zapytania, rozwiązuje ten problem w następującym sensie: jeśli każdy z wyników pomiaru wynosi to funkcja jest stała; w przeciwnym razie, jeśli co najmniej jeden z wyników pomiaru wynosi to funkcja jest zrównoważona. Inaczej mówiąc, po opisanym powyżej obwodzie następuje klasyczny krok przetwarzania końcowego, w którym obliczana jest operacja OR wyników pomiarów w celu uzyskania wyjścia.
Analiza algorytmu
Aby przeanalizować wydajność algorytmu Deutsch-Jozsa w problemie Deutsch-Jozsa, pomocne jest zacząć od rozważenia działania pojedynczej warstwy bramek Hadamarda. Operację Hadamarda można wyrazić jako macierz w zwykły sposób,
ale możemy również wyrazić tę operację poprzez jej działanie na stanach bazy standardowej:
Te dwa równania można połączyć w jeden wzór,
który jest prawdziwy dla obu wyborów
Załóżmy teraz, że zamiast pojedynczego kubitu mamy kubitów, i na każdym z nich wykonywana jest operacja Hadamarda. Połączoną operację na kubitach opisuje iloczyn tensorowy ( razy), który dla zwięzłości i przejrzystości zapisujemy jako . Korzystając z powyższego wzoru, a następnie rozwijając i upraszczając, możemy wyrazić działanie tej połączonej operacji na stanach bazy standardowej kubitów w następujący sposób:
Przy okazji, zapisujemy tutaj ciągi binarne o długości jako oraz zgodnie z konwencją indeksowania Qiskit.
Ten wzór dostarcza nam użytecznego narzędzia do analizy powyższego obwodu kwantowego. Po wykonaniu pierwszej warstwy bramek Hadamarda stan kubitów (w tym skrajnie lewego/dolnego kubitu, który jest traktowany oddzielnie od pozostałych) wynosi
Gdy wykonywana jest operacja , stan ten zostaje przekształcony w
dokładnie poprzez to samo zjawisko odrzutu fazy (phase kick-back), które widzieliśmy w analizie algorytmu Deutscha.
Następnie wykonywana jest druga warstwa bramek Hadamarda, która (zgodnie z powyższym wzorem) przekształca ten stan w
To wyrażenie wygląda nieco skomplikowanie i nie można wiele wywnioskować na temat prawdopodobieństw uzyskania różnych wyników pomiarów bez posiadania większej wiedzy o funkcji
Na szczęście wszystko, co musimy wiedzieć, to prawdopodobieństwo, że każdy z wyników pomiarów wynosi — ponieważ jest to prawdopodobieństwo, że algorytm stwierdzi, iż jest stała. To prawdopodobieństwo ma prosty wzór.
Bardziej szczegółowo, jeśli jest stała, to albo dla każdego ciągu w którym to przypadku wartość sumy wynosi albo dla każdego ciągu w którym to przypadku wartość sumy wynosi Dzielenie przez i podniesienie wartości bezwzględnej do kwadratu daje
Jeśli natomiast jest zrównoważona, to przyjmuje wartość na połowie ciągów i wartość na drugiej połowie, więc wyrazy i w sumie się znoszą i pozostaje nam wartość
Wnioskujemy, że algorytm działa poprawnie, pod warunkiem że obietnica jest spełniona.
Trudność klasyczna
Algorytm Deutscha-Jozsy działa za każdym razem, zawsze dając nam poprawną odpowiedź, gdy obietnica jest spełniona, i wymaga jednego zapytania. Jak to wypada w porównaniu z klasycznymi algorytmami zapytań dla problemu Deutscha-Jozsy?
Po pierwsze, każdy deterministyczny klasyczny algorytm, który poprawnie rozwiązuje problem Deutscha-Jozsy, musi wykonać wykładniczo wiele zapytań: w najgorszym przypadku wymagane jest zapytań. Rozumowanie jest takie, że jeśli algorytm deterministyczny zapyta o lub mniej różnych ciągów i za każdym razem otrzyma tę samą wartość funkcji, to obie odpowiedzi są nadal możliwe. Funkcja może być stała lub może być zrównoważona, ale przez pech wszystkie zapytania zwróciły tę samą wartość funkcji.
Druga możliwość może wydawać się mało prawdopodobna — ale dla algorytmów deterministycznych nie ma losowości ani niepewności, więc będą one systematycznie zawodzić na pewnych funkcjach. Mamy zatem pod tym względem znaczącą przewagę algorytmów kwantowych nad klasycznymi.
Jest jednak pewien haczyk, a mianowicie probabilistyczne klasyczne algorytmy mogą rozwiązać problem Deutscha-Jozsy z bardzo wysokim prawdopodobieństwem, używając zaledwie kilku zapytań. W szczególności, jeśli po prostu wybierzemy kilka różnych losowych ciągów długości i zapytamy o te ciągi, jest mało prawdopodobne, że otrzymamy tę samą wartość funkcji dla wszystkich z nich, gdy jest zrównoważona.
Mówiąc konkretnie, jeśli wybierzemy ciągów wejściowych jednostajnie losowo, obliczymy i odpowiemy , jeśli wszystkie wartości funkcji są takie same, oraz w przeciwnym razie, to zawsze będziemy mieli rację, gdy jest stała, a pomylimy się w przypadku, gdy jest zrównoważona, z prawdopodobieństwem zaledwie Na przykład, jeśli weźmiemy ten algorytm odpowie poprawnie z prawdopodobieństwem większym niż %.
Z tego powodu wciąż mamy raczej skromną przewagę algorytmów kwantowych nad klasycznymi — niemniej jednak jest to wymierna przewaga stanowiąca poprawę względem algorytmu Deutscha.
Deutsch-Jozsa w Qiskit
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as np
Aby zaimplementować algorytm Deutscha-Jozsy w Qiskit, zaczniemy od zdefiniowania funkcji dj_query, która generuje obwód kwantowy realizujący bramkę zapytania dla losowo wybranej funkcji spełniającej obietnicę problemu Deutscha-Jozsy.
Z prawdopodobieństwem 50% funkcja jest stała, a z prawdopodobieństwem 50% funkcja jest zrównoważona.
Dla każdej z tych dwóch możliwości funkcja jest wybierana jednostajnie spośród funkcji danego typu.
Argumentem jest liczba bitów wejściowych funkcji.
def dj_query(num_qubits):
# Create a circuit implementing for a query gate for a random function
# satisfying the promise for the Deutsch-Jozsa problem.
qc = QuantumCircuit(num_qubits + 1)
if np.random.randint(0, 2):
# Flip output qubit with 50% chance
qc.x(num_qubits)
if np.random.randint(0, 2):
# return constant circuit with 50% chance
return qc
# Choose half the possible input strings
on_states = np.random.choice(
range(2**num_qubits), # numbers to sample from
2**num_qubits // 2, # number of samples
replace=False, # makes sure states are only sampled once
)
def add_cx(qc, bit_string):
for qubit, bit in enumerate(reversed(bit_string)):
if bit == "1":
qc.x(qubit)
return qc
for state in on_states:
qc.barrier() # Barriers are added to help visualize how the functions are created.
qc = add_cx(qc, f"{state:0b}")
qc.mcx(list(range(num_qubits)), num_qubits)
qc = add_cx(qc, f"{state:0b}")
qc.barrier()
return qc
Implementację bramki zapytania w postaci obwodu kwantowego możemy pokazać za pomocą metody draw jak zwykle.
display(dj_query(3).draw(output="mpl"))
Następnie definiujemy funkcję, która tworzy obwód Deutscha-Jozsy, przyjmując jako argument implementację bramki zapytania w postaci obwodu kwantowego.
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in the Deutsch-Jozsa algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.compose(function, inplace=True)
qc.h(range(n))
qc.measure(range(n), range(n))
return qc
Na koniec definiujemy funkcję, która uruchamia obwód Deutscha-Jozsy jeden raz.
def dj_algorithm(function: QuantumCircuit):
# Determine if a function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if "1" in measurements[0]:
return "balanced"
return "constant"
Możemy przetestować naszą implementację, wybierając losowo funkcję, wyświetlając implementację bramki zapytania dla tej funkcji w postaci obwodu kwantowego, a następnie uruchamiając algorytm Deutscha-Jozsy na tej funkcji.
f = dj_query(3)
display(f.draw("mpl"))
display(dj_algorithm(f))

'balanced'
Problem Bernsteina-Vaziraniego
Następnie omówimy problem znany jako problem Bernsteina-Vaziraniego. Nazywany jest również problemem próbkowania Fouriera, choć istnieją bardziej ogólne sformułowania tego problemu, które również noszą tę nazwę.
Najpierw wprowadźmy pewną notację. Dla dowolnych dwóch łańcuchów binarnych i o długości definiujemy
Będziemy nazywać tę operację binarnym iloczynem skalarnym. Alternatywny sposób jej zdefiniowania jest następujący.
Zauważmy, że jest to operacja symetryczna, co oznacza, że wynik nie zmienia się, jeśli zamienimy i więc możemy to robić kiedy jest to wygodne. Czasem przydatne jest myślenie o binarnym iloczynie skalarnym jako o parzystości bitów na pozycjach, na których łańcuch ma lub równoważnie, parzystości bitów na pozycjach, na których łańcuch ma
Mając tę notację, możemy teraz zdefiniować problem Bernsteina-Vaziraniego.
Tak naprawdę nie potrzebujemy nowego algorytmu kwantowego do tego problemu; rozwiązuje go algorytm Deutscha-Jozsy. Dla jasności nazwijmy obwód kwantowy z góry, który nie obejmuje klasycznego etapu przetwarzania końcowego polegającego na obliczaniu OR, obwodem Deutscha-Jozsy.
Analiza algorytmu
Aby przeanalizować, jak działa obwód Deutscha-Jozsy dla funkcji spełniającej obietnicę problemu Bernsteina-Vaziraniego, zaczniemy od krótkiej obserwacji. Używając binarnego iloczynu skalarnego, możemy alternatywnie opisać działanie bramek Hadamarda na stanach bazy standardowej kubitów w następujący sposób.
Podobnie jak widzieliśmy przy analizie algorytmu Deutscha, wynika to z faktu, że wartość dla dowolnej liczby całkowitej zależy tylko od tego, czy jest parzysta czy nieparzysta.
Wracając do obwodu Deutscha-Jozsy, po wykonaniu pierwszej warstwy bramek Hadamarda stan kubitów wynosi
Następnie wykonywana jest bramka zapytania, która (poprzez zjawisko phase kickback) przekształca stan w
Korzystając z naszego wzoru na działanie warstwy bramek Hadamarda, widzimy, że druga warstwa bramek Hadamarda przekształca następnie ten stan w
Teraz możemy dokonać pewnych uproszczeń w wykładniku wewnątrz sumy. Mamy obietnicę, że dla pewnego łańcucha więc możemy wyrazić stan jako
Ponieważ i są wartościami binarnymi, możemy zastąpić dodawanie operacją exclusive-OR — znowu dlatego, że jedyne, co ma znaczenie dla liczby całkowitej w wykładniku to czy jest ona parzysta czy nieparzysta. Wykorzystując symetrię binarnego iloczynu skalarnego, otrzymujemy następujące wyrażenie dla stanu:
(Nawiasy zostały dodane dla jasności, chociaż nie są one naprawdę konieczne, ponieważ zwyczajowo traktuje się binarny iloczyn skalarny jako mający wyższy priorytet niż exclusive-OR.)
W tym momencie skorzystamy z następującego wzoru.
Możemy uzyskać ten wzór dzięki podobnemu wzorowi dla bitów,
wraz z rozwinięciem binarnego iloczynu skalarnego i bitowego exclusive-OR:
Pozwala to wyrazić stan obwodu bezpośrednio przed pomiarami w następujący sposób:
Ostatnim krokiem jest wykorzystanie jeszcze jednego wzoru, który działa dla każdego łańcucha binarnego
Używamy tu prostej notacji dla łańcuchów, której użyjemy jeszcze kilka razy w tej lekcji: to łańcuch samych zer o długości
Prostym sposobem uzasadnienia tego wzoru jest rozważenie dwóch przypadków osobno. Jeśli to dla każdego łańcucha więc wartość każdego składnika sumy wynosi i otrzymujemy poprzez zsumowanie i podzielenie przez Z drugiej strony, jeśli którykolwiek z bitów jest równy to binarny iloczyn skalarny jest równy dla dokładnie połowy możliwych wyborów oraz dla drugiej połowy — ponieważ wartość binarnego iloczynu skalarnego zmienia się (z na lub z na ), gdy zmienimy którykolwiek bit na pozycji, na której ma
Jeśli teraz zastosujemy ten wzór do uproszczenia stanu obwodu przed pomiarami, otrzymamy
dzięki temu, że wtedy i tylko wtedy, gdy Zatem pomiary ujawniają dokładnie ten łańcuch którego szukamy.
Trudność klasyczna
Podczas gdy obwód Deutscha-Jozsy rozwiązuje problem Bernsteina-Vaziraniego za pomocą jednego zapytania, każdy klasyczny algorytm zapytaniowy musi wykonać co najmniej zapytań, aby rozwiązać ten problem.
Można to uzasadnić za pomocą tzw. argumentu teorio-informacyjnego, który w tym przypadku jest bardzo prosty. Każde klasyczne zapytanie ujawnia pojedynczy bit informacji o rozwiązaniu, a mamy bitów informacji, które należy odkryć — więc potrzeba co najmniej zapytań.
W istocie możliwe jest klasyczne rozwiązanie problemu Bernsteina-Vaziraniego poprzez odpytanie funkcji na każdym z łańcuchów mających pojedynczą na każdej możliwej pozycji, oraz dla wszystkich pozostałych bitów, co ujawnia bity jeden po drugim. Dlatego przewaga algorytmów kwantowych nad klasycznymi dla tego problemu wynosi zapytanie względem zapytań.
Bernstein-Vazirani z Qiskit
Zaimplementowaliśmy już obwód Deutscha-Jozsy powyżej i tutaj wykorzystamy go do rozwiązania problemu Bernsteina-Vaziraniego. Najpierw zdefiniujemy funkcję, która implementuje bramkę zapytania dla problemu Bernsteina-Vaziraniego dla dowolnego łańcucha binarnego
def bv_query(s):
# Create a quantum circuit implementing a query gate for the
# Bernstein-Vazirani problem.
qc = QuantumCircuit(len(s) + 1)
for index, bit in enumerate(reversed(s)):
if bit == "1":
qc.cx(index, len(s))
return qc
display(bv_query("1011").draw(output="mpl"))
Teraz możemy utworzyć funkcję, która uruchamia obwód Deutscha-Jozsy na funkcji, używając funkcji compile_circuit zdefiniowanej wcześniej.
def bv_algorithm(function: QuantumCircuit):
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
return result.get_memory()[0]
display(bv_algorithm(bv_query("1011")))
'1011'
Uwaga dotycząca nazewnictwa
W kontekście problemu Bernsteina-Vaziraniego często zdarza się, że algorytm Deutscha-Jozsy jest nazywany "algorytmem Bernsteina-Vaziraniego". Jest to nieco mylące, ponieważ ten algorytm jest algorytmem Deutscha-Jozsy, o czym Bernstein i Vazirani jasno pisali w swojej pracy.
To, co Bernstein i Vazirani zrobili po pokazaniu, że algorytm Deutscha-Jozsy rozwiązuje problem Bernsteina-Vaziraniego (tak jak został on sformułowany powyżej), to zdefiniowanie znacznie bardziej skomplikowanego problemu, znanego jako problem rekurencyjnego próbkowania Fouriera. Jest to wysoce sztuczny problem, w którym rozwiązania różnych instancji problemu faktycznie odblokowują nowe poziomy problemu ułożone w strukturę drzewiastą. Problem Bernsteina-Vaziraniego jest zasadniczo tylko przypadkiem bazowym tego bardziej skomplikowanego problemu.
Problem rekurencyjnego próbkowania Fouriera był pierwszym znanym przykładem problemu zapytaniowego, w którym algorytmy kwantowe mają tak zwaną superwielomianową przewagę nad algorytmami probabilistycznymi, tym samym przewyższając przewagę kwantowych nad klasycznymi oferowaną przez algorytm Deutscha-Jozsy. Intuicyjnie mówiąc, rekurencyjna wersja problemu wzmacnia przewagę kontra algorytmów kwantowych do czegoś znacznie większego.
Najbardziej wymagającym aspektem analizy matematycznej ustanawiającej tę przewagę jest wykazanie, że klasyczne algorytmy zapytaniowe nie mogą rozwiązać problemu bez wykonania wielu zapytań. Jest to dość typowe; dla wielu problemów wykluczenie kreatywnych podejść klasycznych, które rozwiązują je efektywnie, może być bardzo trudne.
Problem Simona oraz algorytm do jego rozwiązania opisany w następnej sekcji stanowi znacznie prostszy przykład superwielomianowej (a w zasadzie wykładniczej) przewagi algorytmów kwantowych nad klasycznymi i z tego powodu problem rekurencyjnego próbkowania Fouriera jest rzadziej omawiany. Jest on jednak sam w sobie interesującym problemem obliczeniowym.