Der Ansatz
Schau zu, wie Victoria Lipinska erklärt, was ein Ansatz ist und warum er im Kontext eines variationellen Quanten-Eigenwertsolvers (VQE) wichtig ist.
Referenzen
Die folgenden Artikel werden im obigen Video referenziert.
- 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
In der vorherigen Lektion hast du einen Hamiltonian erstellt, der die Energie deines interessierenden Moleküls beschreibt, und ihn in ein für einen Quantencomputer geeignetes Format überführt. VQE verwendet einen variationellen Circuit, um Quantenzustände vorzubereiten. Anschließend nutzen wir diese Zustände, um den Erwartungswert des Hamiltonians (die Energie) zu bestimmen. Die Parameter im variationellen Circuit werden so lange variiert, bis die Berechnung auf ein Minimum des Erwartungswerts konvergiert. Im Kontext der Quantenchemie sollte dies die Grundzustandsenergie sein. Diese Lektion konzentriert sich auf den variationellen Circuit, auch Ansatz genannt (ein deutsches Wort für „Herangehensweise" oder „Methode"). In dieser Lektion lernst du:
- die Menge der vorgefertigten Ansätze in der Circuit-Bibliothek
- wie man die Eigenschaften eines Ansatzes festlegt oder verändert
- wie man seinen eigenen Ansatz erstellt
- Beispiele für gute und schlechte Ansätze
Die Qiskit Circuit-Bibliothek enthält viele Kategorien von Circuits, die als Ansatz verwendet werden können. Hier beschränken wir unsere Diskussion auf Zwei-lokale Circuits (Circuits, die aus Gates bestehen, die auf höchstens zwei Qubits gleichzeitig wirken). Efficient SU2 ist ein häufig verwendeter Ansatz.
Ein efficient_su_2-Circuit besteht aus Schichten einzelner Qubit-Operationen, die von SU(2) (spezielle unitäre Gruppe vom Grad 2, wie Pauli-Rotations-Gates) und CX-Verschränkungen aufgespannt werden. Dies ist ein heuristisches Muster, das in variationellen Quantenalgorithmen wie VQE und Klassifizierungscircuits im Quantenmaschinellen Lernen (QML) nützlich sein kann.
Wir beginnen mit einem vier-Qubit-Beispiel-efficient_su2-Circuit mit zwei Typen von SU(2)-Gates, nämlich rx und y. Außerdem legen wir ein Verschränkungsschema und die Anzahl der Wiederholungen fest. Wenn du die Circuits einfach mit .draw() zeichnest, erhältst du eine recht abstrakte Darstellung. Eine verständlichere Circuit-Darstellung erhältst du mit .decompose().draw(), hier verwenden wir 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 ├
└──────────┘└───┘ └───┘ └──────────┘└───┘
Die SU(2)-Gates erscheinen am Anfang und Ende in der Reihenfolge und mit den Elementen, wie in su2_gates = [...] angegeben. Das Verschränkungsschema linear bedeutet, dass die CX-Gates der Reihe nach durch die nummerierten Qubits schreiten und 0 & 1, dann 1 & 2, und so weiter, diagonal durch den Circuit verschränken. Wie zu erwarten, fügt reps = 2 einfach eine weitere Verschränkungsschicht und eine abschließende SU(2)-Schicht hinzu. Die Einstellung reps = n entspricht n Verschränkungsschichten, mit SU(2)-Schichten dazwischen und an jedem Ende.
SU2_ansatz2 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=2
)
SU2_ansatz2.decompose().draw(output="mpl")
Es gibt noch weitere Verschränkungsschemata. Zwei davon sind erwähnenswert: circular und full. Die zirkuläre Verschränkung ist identisch mit der linearen Verschränkung, fügt aber ein zusätzliches CX-Gate hinzu, das das erste und das letzte Qubit verschränkt. Das vollständige Verschränkungsschema enthält ein CX-Gate zwischen jedem Qubit-Paar. Beachte: Bei einem N-Qubit-Circuit sind das -Gates, was rechenintensiv werden kann.
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")
Die Tiefe deines Circuits kannst du mit .depth() oder manchmal .decompose().depth() überwachen.
print(SU2_ansatz4.decompose().depth())
11
Eine Verallgemeinerung des efficient_su2 ist der Zwei-lokale Circuit, der selbst ein Sonderfall der n-lokalen Circuits ist. Zwei-lokale Circuits enthalten ebenfalls SU(2)-Blöcke (oder Rotationsblöcke) und Verschränkungsblöcke. Hier können wir frei festlegen, welche Art von Verschränkungsgattern wir verwenden möchten, z. B. CRX-Gates. In diesem Beispiel akzeptieren alle Gates einen Parameter, was aber nicht zwingend der Fall sein muss. Man könnte beispielsweise Y-Rotations-Gates und CX-Verschränkungsgatter verwenden.
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")
Der letzte Ansatz, den wir namentlich besprechen, ist das Pauli-Zwei-Design. Dieser Circuit enthält eine initiale Rotation von , und die Rotationsschichten bestehen aus einzelnen Qubit-Pauli-Rotationen, bei denen die Achse gleichmäßig zufällig als X, Y oder Z gewählt wird. Die Verschränkungsschichten bestehen aus paarweisen CZ-Gates mit einer Gesamttiefe von zwei. Beachte den Unterschied in der Verschränkungs- (und Gesamt-Circuit-)Tiefe zwischen diesem pauli_two_design und z. B. dem 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")
Diese vorgefertigten variationellen Circuits sind nützliche Heuristiken, sowohl um ein gewünschtes Maß an Verschränkung zu erreichen als auch um die Circuit-Tiefe zu begrenzen. Aber es gibt nichts Magisches an ihnen. Du kannst deinen eigenen variationellen Circuit erstellen. Das kann sogar vorteilhaft sein, wenn du etwas über die Verschränkung des Zielzustands deines Systems weißt.
Um deinen eigenen Ansatz zu erstellen, baust du einfach einen Quantencircuit auf, bei dem ein Teil der Gates Funktionen der Elemente eines Parametervektors sind („theta" im Drei-Qubit-Beispiel unten).
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
Im Allgemeinen ist die Auswahl des besten Ansatzes eine Kunst; der beste Ansatz ist derjenige, der dir hilft, dein Ziel in den wenigsten Optimierungsschritten zu erreichen. Es ist einfacher, Ansätze zu erkennen, die wahrscheinlich schlecht sind. Eine größere Circuit-Tiefe führt beispielsweise tendenziell zur Ansammlung von Fehlern. Fehlerminderung kann dabei helfen, aber es ist gute Praxis, die Circuit-Tiefe so gering wie vernünftig zu halten. Überspringe aber keine Verschränkung, die notwendig ist. Dein Zielzustand könnte ein vollständiges Verschränkungsschema erfordern. Zwei Beispiele für wahrscheinlich schlechte Entscheidungen sind unten gezeigt. Die Auswahl eines guten Ansatzes wird in späteren Abschnitten im Kontext von Konvergenztests erneut aufgegriffen.
Dieser erste Circuit ist wahrscheinlich eine schlechte Wahl, weil das letzte Qubit überhaupt nicht mit den anderen verschränkt ist. Tatsächlich gibt es keine rechnerisch sinnvolle Aktion auf dem letzten Qubit. Höchstwahrscheinlich sollte das letzte Qubit entweder mit den anderen verschränkt oder aus der Berechnung entfernt werden.
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
Dieser letzte Circuit ist wahrscheinlich eine schlechte Wahl, weil die Gate-Tiefe sehr groß ist und eine viermalige Wiederholung der Verschränkungsschicht gegenüber zwei oder drei Wiederholungen voraussichtlich keine wesentlich bessere Annäherung an den Zielzustand ergibt.
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
Diese Circuits sind nicht „vollständig" in dem Sinne, dass noch unbekannte und variable Parameter in viele der Gates eingesetzt werden müssen. Diese Parameter werden durch aufeinanderfolgende Schätzungen gewählt und aktualisiert, um den Erwartungswert der Kostenfunktion (im Chemiekontext typischerweise die Grundzustandsenergie) zu senken. In einer oder sogar wenigen Dimensionen ist das trivial. Der obige Circuit hat jedoch 20 variationelle Parameter, was bedeutet, dass die Suche nach dem Zielzustand mit der minimalen Energie die Durchsuchung eines 20-dimensionalen Raums erfordert (ein weiterer Grund, unnötige Circuit-Gates zu vermeiden). Hier kommen klassische Optimierungsalgorithmen ins Spiel – das ist das Thema der nächsten Lektion.