The ansatz
Obejrzyj, jak Victoria Lipinska wyjaśnia, czym jest ansatz i dlaczego zależy nam na nim w kontekście wariacyjnego rozwiązywacza własnego (VQE).
References
Poniższe artykuły są przywoływane w powyższym filmie.
- The theory of variational hybrid quantum-classical algorithms, McClean, et al.
- Quantum Chemistry in the Age of Quantum Computing, Cao, et al.
- Noisy intermediate-scale quantum (NISQ) algorithms, Bharti, et al.
- The Variational Quantum Eigensolver: A review of methods and best practices, Tilly, et al.
- Hardware-efficient Variational Quantum Eigensolver for Small Molecules and Quantum Magnets, Kandala, et al.
- Quantum computational chemistry, McArdle, et al.
Ansatz code
W poprzedniej lekcji stworzyłeś(-aś) Hamiltonian opisujący energię interesującej cię cząsteczki i odwzorowałeś(-aś) go na format użyteczny dla komputera kwantowego. VQE używa wariacyjnego obwodu do przygotowywania stanów kwantowych. Następnie wykorzystujemy te stany do wyznaczenia wartości oczekiwanej Hamiltonianu (energii). Parametry wariacyjnego obwodu są zmieniane tak długo, aż obliczenie zbiega do minimalnej wartości oczekiwanej. W kontekście chemii kwantowej powinna to być energia stanu podstawowego. Ta lekcja skupia się na wariacyjnym obwodzie, zwanym również ansatzem (słowo niemieckie oznaczające „podejście" lub „metodę"). W tej lekcji dowiesz się:
- jakie gotowe ansaetze są dostępne w bibliotece obwodów
- jak określać lub modyfikować właściwości ansatzu
- jak zbudować własny ansatz
- przykładów dobrych i złych ansaetze
Biblioteka obwodów Qiskit zawiera wiele kategorii obwodów, które mogą służyć jako ansatz. Tutaj ograniczymy nasze rozważania do obwodów dwu-lokalnych (obwodów złożonych z Gate'ów działających jednocześnie na co najwyżej dwóch qubitach). Efficient SU2 to powszechnie stosowany ansatz.
Obwód efficient_su_2 składa się z warstw jednoQubitowych operacji generowanych przez SU(2) (specjalna grupa unitarna stopnia 2, np. Pauli rotation gates) oraz splątań CX. Jest to heurystyczny wzorzec, który może być użyteczny w wariacyjnych algorytmach kwantowych, takich jak VQE, oraz w obwodach klasyfikacyjnych w kwantowym uczeniu maszynowym (QML).
Zaczniemy od czterokubitowego przykładu obwodu efficient_su2 z dwoma rodzajami Gate'ów SU(2), powiedzmy rx i y. Określamy też schemat splątania oraz liczbę powtórzeń. Jeśli użyjesz po prostu .draw() na obwodach, otrzymasz dość abstrakcyjną reprezentację. Bardziej czytelny diagram obwodu uzyskasz, korzystając z .decompose().draw(), i tutaj użyjemy `output = "mpl"'.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit.circuit.library import efficient_su2
SU2_ansatz = efficient_su2(4, su2_gates=["rx", "y"], entanglement="linear", reps=1)
print(SU2_ansatz.draw())
SU2_ansatz.decompose().draw(output="mpl")
┌──────────┐┌───┐ ┌──────────┐ ┌───┐
q_0: ┤ Rx(θ[0]) ├┤ Y ├──■──┤ Rx(θ[4]) ├───┤ Y ├─────────────────────
├──────────┤├───┤┌─┴─┐└──────────┘┌──┴───┴───┐ ┌───┐
q_1: ┤ Rx(θ[1]) ├┤ Y ├┤ X ├─────■──────┤ Rx(θ[5]) ├───┤ Y ├─────────
├──────────┤├───┤└───┘ ┌─┴─┐ └──────────┘┌──┴───┴───┐┌───┐
q_2: ┤ Rx(θ[2]) ├┤ Y ├────────┤ X ├─────────■──────┤ Rx(θ[6]) ├┤ Y ├
├──────────┤├───┤ └───┘ ┌─┴─┐ ├──────────┤├───┤
q_3: ┤ Rx(θ[3]) ├┤ Y ├────────────────────┤ X ├────┤ Rx(θ[7]) ├┤ Y ├
└──────────┘└───┘ └───┘ └──────────┘└───┘
Gate'y SU(2) pojawiają się na początku i na końcu, w kolejności i z elementami określonymi przez su2_gates = [...]. Schemat splątania linear oznacza, że Gate'y CX przemierzają kolejne qubity, splątując 0 i 1, potem 1 i 2, i tak dalej, wzdłuż przekątnej linii w obwodzie. Jak można się spodziewać, ustawienie reps = 2 po prostu dodaje warstwę splątania i kończącą warstwę SU(2). Ustawienie reps = n odpowiada n warstwom splątania, z warstwami SU(2) między nimi oraz na każdym końcu.
SU2_ansatz2 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=2
)
SU2_ansatz2.decompose().draw(output="mpl")
Istnieje kilka innych schematów splątania. Dwa warte odnotowania to circular i full. Splątanie circular jest identyczne ze splątaniem linear, lecz z dodatkowym Gate'em CX splątającym pierwszy i ostatni qubit. Schemat splątania full zawiera Gate CX między każdą parą qubitów. Zauważ, że dla obwodu N-qubitowego daje to Gate'ów , co może być kosztowne obliczeniowo.
SU2_ansatz3 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="circular", reps=1
)
SU2_ansatz3.decompose().draw(output="mpl")
SU2_ansatz4 = efficient_su2(4, su2_gates=["rx", "y", "z"], entanglement="full", reps=1)
SU2_ansatz4.decompose().draw(output="mpl")
Możesz monitorować głębokości swoich obwodów, używając .depth() lub niekiedy .decompose().depth().
print(SU2_ansatz4.decompose().depth())
11
Uogólnieniem efficient_su2 jest obwód dwu-lokalny, który sam w sobie jest szczególnym przypadkiem obwodów n-lokalnych. Obwody dwu-lokalne również zawierają bloki SU(2) (czyli bloki rotacji) oraz bloki splątania. Tutaj możemy swobodnie określić typ Gate'ów splątania, jakich chcemy użyć, na przykład Gate'ów CRX. W tym przykładzie wszystkie Gate'y przyjmują parametr, ale nie musi tak być. Można by użyć Gate'ów rotacji Y i Gate'ów CX do splątania.
from qiskit.circuit.library import n_local
rotation_blocks = ["ry"]
entanglement_blocks = ["crx"]
two_ansatz = n_local(
4, rotation_blocks, entanglement_blocks, "linear", insert_barriers=True, reps=2
)
two_ansatz.decompose().draw(output="mpl")
Ostatnim ansatzem, który omówimy z nazwy, jest Pauli-two-design. Obwód ten zawiera początkową rotację , a warstwy rotacji zawierają jednoQubitowe obroty Pauli, gdzie oś jest wybierana jednostajnie losowo spośród X, Y lub Z. Warstwy splątania złożone są z parowych Gate'ów CZ o łącznej głębokości dwa. Zwróć uwagę na różnicę w głębokości splątania (i całkowitej głębokości obwodu) między tym pauli_two_design a, na przykład, efficient_su2.
from qiskit.circuit.library import pauli_two_design
PtwoD_ansatz = pauli_two_design(5, reps=1, seed=10599, insert_barriers=True)
PtwoD_ansatz.decompose().draw(output="mpl")
Te gotowe obwody wariacyjne są użytecznymi heurystykami — zarówno jeśli chodzi o osiąganie pożądanego poziomu splątania, jak i ograniczanie głębokości obwodu. Nie ma w nich jednak nic magicznego. Możesz swobodnie tworzyć własne obwody wariacyjne. Może to być nawet korzystne w przypadkach, gdy wiesz coś o splątaniu stanu docelowego twojego układu.
Aby zbudować własny ansatz, po prostu konstruujesz obwód kwantowy, w którym pewna część Gate'ów jest funkcją elementów wektora parametrów („theta" w poniższym przykładzie trzekubitowym).
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
n = 3
theta = ParameterVector("θ", length=n)
qc = QuantumCircuit(n)
qc.h(0)
qc.h(2)
for i in range(n - 1):
qc.cx(i, i + 1)
qc.cz(0, n - 1)
qc.barrier()
for i in range(n):
qc.ry(theta[i], i)
qc.barrier()
qc.cz(0, n - 1)
for i in reversed(range(n - 1)):
qc.cx(i, i + 1)
qc.h(0)
qc.h(1)
own_ansatz = qc
print(own_ansatz.depth())
qc.draw("mpl")
9
Generalnie dobór najlepszego ansatzu to sztuka; najlepszy ansatz to taki, który pozwala osiągnąć cel przy jak najmniejszej liczbie kroków optymalizacji. Łatwiej jest wskazać ansaetze, które prawdopodobnie będą złe. Na przykład większa głębokość obwodu zwykle prowadzi do akumulacji błędów. Mitigacja błędów może w tym pomóc, ale dobrą praktyką jest utrzymywanie głębokości obwodu na możliwie niskim poziomie. Nie pomijaj jednak splątania, które jest niezbędne. Możliwe, że twój stan docelowy wymaga schematu pełnego splątania. Poniżej pokazano dwa przykłady, które z dość oczywistych powodów są prawdopodobnie złym wyborem. Dobór odpowiedniego ansatzu zostanie ponownie omówiony w późniejszych sekcjach w kontekście testów zbieżności.
Ten pierwszy obwód jest prawdopodobnie złym wyborem, ponieważ ostatni qubit nie jest w ogóle splątany z pozostałymi. Rzeczywiście, nie ma na nim żadnej obliczeniowo istotnej operacji. Najprawdopodobniej ostatni qubit powinien być albo splątany z pozostałymi, albo usunięty z obliczeń.
n = 4
theta = ParameterVector("θ", length=n)
qc = QuantumCircuit(n)
qc.h(0)
qc.h(2)
for i in range(n - 2):
qc.cx(i, i + 1)
qc.cz(0, n - 2)
qc.barrier()
for i in range(n):
qc.ry(theta[i], i)
qc.barrier()
qc.cz(0, n - 2)
for i in reversed(range(n - 2)):
qc.cx(i, i + 1)
qc.h(0)
qc.h(1)
own_ansatz2 = qc
print(own_ansatz2.depth())
qc.draw("mpl")
9
Ten ostatni obwód jest prawdopodobnie złym wyborem, ponieważ głębokość Gate'ów jest bardzo duża, a czterokrotne powtarzanie warstwy splątania nie przyniesie prawdopodobnie znacznie lepszego dopasowania do stanu docelowego niż dwa lub trzy powtórzenia.
su2_ansatz_long = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=4
)
print(su2_ansatz_long.decompose().depth())
su2_ansatz_long.decompose().draw(output="mpl")
24
Te obwody nie są „kompletne" w tym sensie, że wiele Gate'ów wciąż ma nieznane i zmienne parametry do wstawienia. Parametry te są dobierane poprzez kolejne zgadywanie i aktualizowanie wartości w celu obniżenia wartości oczekiwanej funkcji kosztu (w kontekście chemii, zazwyczaj energii stanu podstawowego). W jednym, czy nawet kilku wymiarach to proste. Jednak powyższy obwód ma 20 parametrów wariacyjnych, co oznacza, że znalezienie stanu docelowego o minimalnej energii wymaga przeszukiwania 20-wymiarowej przestrzeni stanów (to kolejny powód, by nie dodawać zbędnych Gate'ów do obwodu). Tu właśnie do gry wchodzą klasyczne algorytmy optymalizacji — i to jest temat następnej lekcji.