Ansätze und Variationsformen
Im Kern aller variationalen Algorithmen liegt die zentrale Idee, Unterschiede zwischen Zuständen zu analysieren, die durch eine wohlverhaltene Abbildung (z.B. stetig, differenzierbar) von einem Satz von Parametern oder Variablen verbunden sind – daher der Name.
Zunächst werden wir erkunden, wie man parametrisierte Circuits von Hand konstruiert. Wir verwenden diese Circuits, um eine Variationsform zu definieren, die eine Sammlung parametrisierter Zustände für unseren variationalen Algorithmus zum Erkunden darstellt. Dann konstruieren wir unseren Ansatz, indem wir diese Variationsform auf unseren Referenzzustand anwenden.
Wir werden auch erkunden, wie man Geschwindigkeit und Genauigkeit bei der Erkundung dieses Suchraums abwägt.
Parametrisierte Quantencircuits
Variationale Algorithmen funktionieren, indem sie eine Reihe von Quantenzuständen erkunden und vergleichen, die von einer endlichen Menge von Parametern abhängen. Diese Zustände können mithilfe eines parametrisierten Quantencircuits vorbereitet werden, bei dem Gates mit abstimmbaren Parametern definiert sind. Es ist möglich, diesen parametrisierten Circuit zu erstellen, ohne spezifische Winkel zu binden:
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit rustworkx
from qiskit.circuit import QuantumCircuit, Parameter
theta = Parameter("θ")
qc = QuantumCircuit(3)
qc.rx(theta, 0)
qc.cx(0, 1)
qc.x(2)
qc.draw("mpl")
from math import pi
angle_list = [pi / 3, pi / 2]
circuits = [qc.assign_parameters({theta: angle}) for angle in angle_list]
for circuit in circuits:
display(circuit.draw("mpl"))
Variationsform und Ansatz
Um iterativ von einem Referenzzustand zu einem Zielzustand zu optimieren, müssen wir eine Variationsform definieren, die eine Sammlung parametrisierter Zustände für unseren variationalen Algorithmus zum Erkunden darstellt:
Beachte, dass der parametrisierte Zustand sowohl vom Referenzzustand , der von keinen Parametern abhängt, als auch von der Variationsform , die immer von Parametern abhängt, abhängt. Wir bezeichnen die Kombination dieser beiden Hälften als Ansatz: .
Während wir unseren Ansatz konstruieren, um eine Sammlung parametrisierter Zustände für unseren variationalen Algorithmus zum Erkunden darzustellen, stoßen wir auf ein wichtiges Problem: die Dimensionalität. Ein -Qubit-System (d.h. Hilbert-Raum) hat eine enorme Anzahl von verschiedenen Quantenzuständen im Konfigurationsraum. Wir würden eine unhandhabbare Anzahl von Parametern benötigen, um ihn vollständig zu erkunden. Quantitativ beträgt seine Dimensionalität . Erschwerend kommt hinzu, dass die Laufzeitkomplexität von Suchalgorithmen und ähnlichen exponentiell mit dieser Dimensionalität wächst – ein Phänomen, das in der Literatur oft als Fluch der Dimensionalität bezeichnet wird.
Um diesem Problem entgegenzuwirken, ist es gängige Praxis, der Variationsform sinnvolle Einschränkungen aufzuerlegen, sodass nur die relevantesten Zustände erkundet werden. Das Finden eines effizienten, verkürzten Ansatzes ist ein aktives Forschungsgebiet, aber wir werden zwei gängige Designs vorstellen.
Heuristische Ansätze und Kompromisse
Wenn du keine Informationen über dein spezifisches Problem hast, die dabei helfen könnten, die Dimensionalität einzuschränken, kannst du eine beliebige Familie parametrisierter Circuits mit weniger als Parametern ausprobieren. Es gibt jedoch einige Kompromisse zu bedenken:
- Geschwindigkeit: Durch die Verkleinerung des Suchraums kann der Algorithmus schneller laufen.
- Genauigkeit: Die Verkleinerung des Raums birgt das Risiko, die tatsächliche Lösung des Problems auszuschließen, was zu suboptimalen Lösungen führt.
- Rauschen: Tiefere Circuits sind stärker von Rauschen betroffen, daher müssen wir mit der Konnektivität, den Gates und der Gate-Treue unseres Ansatzes experimentieren.
Es gibt einen grundlegenden Kompromiss zwischen Qualität (oder sogar Lösbarkeit) und Geschwindigkeit: Je mehr Parameter, desto wahrscheinlicher ist es, ein genaues Ergebnis zu finden, aber desto länger dauert die Ausführung des Algorithmus.
N-lokale Circuits
Eines der am häufigsten verwendeten Beispiele heuristischer Ansätze sind die N-lokalen Circuits, aus mehreren Gründen:
- Effiziente Implementierung: Der N-lokale Ansatz besteht typischerweise aus einfachen, lokalen Gates, die effizient auf einem Quantencomputer implementiert werden können, unter Verwendung einer kleinen Anzahl physikalischer Qubits. Dies erleichtert die Konstruktion und Optimierung von Quantencircuits.
- Erfasst wichtige Korrelationen: Der N-lokale Ansatz kann wichtige Korrelationen zwischen den Qubits in einem Quantensystem erfassen, auch mit einer kleinen Anzahl von Gates. Dies liegt daran, dass die lokalen Gates auf benachbarte Qubits wirken und Verschränkungen zwischen ihnen erzeugen können, was für die Simulation komplexer Quantensysteme wichtig sein kann.
Diese Circuits bestehen aus Rotations- und Verschränkungsschichten, die abwechselnd ein oder mehrere Male wie folgt wiederholt werden:
- Jede Schicht wird aus Gates der Größe höchstens gebildet, wobei kleiner als die Anzahl der Qubits sein muss.
- Bei einer Rotationsschicht werden die Gates übereinander gestapelt. Wir können Standard-Rotationsoperationen verwenden, wie
RXoderCRZ. - Bei einer Verschränkungsschicht können wir Gates wie
Toffoli-Gates oderCXmit einer Verschränkungsstrategie verwenden. - Beide Schichttypen können parametrisiert sein oder nicht, aber mindestens eine von ihnen muss Parameter enthalten. Andernfalls gäbe es ohne mindestens einen Parameter keine Variationen!
- Optional wird am Ende des Circuits eine zusätzliche Rotationsschicht hinzugefügt.
Erstellen wir zum Beispiel einen fünf-Qubit-NLocal-Circuit mit Rotationsblöcken aus RX- und CRZ-Gates, Verschränkungsblöcken aus Toffoli-Gates, die auf Qubits , , und wirken, und Wiederholungen jeder Schicht.
from qiskit.circuit.library import NLocal, CCXGate, CRZGate, RXGate
from qiskit.circuit import Parameter
theta = Parameter("θ")
ansatz = NLocal(
num_qubits=5,
rotation_blocks=[RXGate(theta), CRZGate(theta)],
entanglement_blocks=CCXGate(),
entanglement=[[0, 1, 2], [0, 2, 3], [4, 2, 1], [3, 1, 0]],
reps=2,
insert_barriers=True,
)
ansatz.decompose().draw("mpl")
Im obigen Beispiel ist das größte Gate das Toffoli-Gate, das auf drei Qubits wirkt, was den Circuit -lokal macht. Der am häufigsten verwendete Typ von -lokalen Circuits sind -lokale Circuits mit Einzel-Qubit-Rotationsgates und -Qubit-Verschränkungsgates.
Erstellen wir einen -lokalen Circuit mit Qiskits TwoLocal-Klasse. Die Syntax ist dieselbe wie bei NLocal, aber es gibt einige Unterschiede. Zum Beispiel können die meisten Gates, wie RX, RZ und CNOT, als Strings übergeben werden, ohne die Gates zu importieren oder eine Parameter-Instanz zu erstellen.
from qiskit.circuit.library import TwoLocal
ansatz = TwoLocal(
num_qubits=5,
rotation_blocks=["rx", "rz"],
entanglement_blocks="cx",
entanglement="linear",
reps=2,
insert_barriers=True,
)
ansatz.decompose().draw("mpl")
In diesem Fall haben wir die lineare Verschränkungsverteilung verwendet, bei der jedes Qubit mit dem nächsten verschränkt ist. Informationen zu anderen Strategien findest du in der TwoLocal-Dokumentation.
Efficient SU2
efficient_su2 ist ein hardware-effizienter Circuit, der aus Schichten von Einzel-Qubit-Operationen über SU(2) und CX-Verschränkungen besteht. Dies ist ein heuristisches Muster, das verwendet werden kann, um Test-Wellenfunktionen für variationale Quantenalgorithmen vorzubereiten oder als Klassifikations-Circuit für maschinelles Lernen.
from qiskit.circuit.library import efficient_su2
ansatz = efficient_su2(4, su2_gates=["rx", "y"], entanglement="linear", reps=1)
ansatz.decompose().draw("mpl")
Problemspezifische Ansätze
Während heuristische und hardware-effiziente Ansätze uns helfen, ein Problem auf naive Weise zu lösen, können wir problemspezifisches Wissen nutzen, um unseren Circuit-Suchraum auf einen bestimmten Typ einzuschränken. Dies hilft uns, Geschwindigkeit zu gewinnen, ohne bei unserem Suchprozess an Genauigkeit einzubüßen.
Optimierung
Bei einem Max-Cut-Problem möchten wir die Knoten eines Graphen so partitionieren, dass die Anzahl der Kanten zwischen Knoten in verschiedenen Gruppen maximiert wird. Die gewünschte Max-Cut-Partition für den folgenden Graphen ist klar: der 0.-Knoten auf der linken Seite sollte durch einen Schnitt von den übrigen Knoten auf der rechten Seite getrennt werden.
import rustworkx as rx
from rustworkx.visualization import mpl_draw
n = 4
G = rx.PyGraph()
G.add_nodes_from(range(n))
# The edge syntax is (start, end, weight)
edges = [(0, 1, 1.0), (0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0), (2, 3, 1.0)]
G.add_edges_from(edges)
mpl_draw(
G, pos=rx.shell_layout(G), with_labels=True, edge_labels=str, node_color="#1192E8"
)
Um den QAOA-Algorithmus für ein Max-Cut-Problem zu nutzen, benötigen wir einen Pauli-Hamilton-Operator, der die Kosten so kodiert, dass der minimale Erwartungswert des Operators der maximalen Anzahl von Kanten zwischen den Knoten in zwei verschiedenen Gruppen entspricht.
Für dieses einfache Beispiel ist der Operator eine Linearkombination von Termen mit Z-Operatoren an Knoten, die durch eine Kante verbunden sind (beachte, dass das 0. Qubit ganz rechts ist): . Sobald der Operator konstruiert ist, kann der Ansatz für den QAOA-Algorithmus leicht mithilfe des QAOAAnsatz-Circuits aus der Qiskit Circuit-Bibliothek aufgebaut werden.
# Pre-defined ansatz circuit, operator class and visualization tools
from qiskit.circuit.library import QAOAAnsatz
from qiskit.quantum_info import SparsePauliOp
# Problem to Hamiltonian operator
hamiltonian = SparsePauliOp.from_list(
[("ZZII", 1), ("IZZI", 1), ("ZIIZ", 1), ("IZIZ", 1), ("IIZZ", 1)]
)
# QAOA ansatz circuit
ansatz = QAOAAnsatz(hamiltonian, reps=2)
# Draw
ansatz.decompose(reps=3).draw("mpl")
Das vorherige Bild zeigt den Ansatz in grundlegenden Gates zur Verdeutlichung. Er kann jedoch in mehreren Zerlegungsebenen dargestellt werden, indem das reps-Argument geändert oder der Circuit ohne die Decompose-Methode gezeichnet wird. Die folgende Darstellung zeigt zum Beispiel direkt die QAOA-Struktur mit dem Standard-reps-Wert, d.h. reps=1.
ansatz.decompose(reps=2).draw("mpl")
Quantenmaschinelles Lernen
Im maschinellen Lernen ist eine häufige Anwendung die Klassifikation von Daten in zwei oder mehr Kategorien. Dabei wird ein Datenpunkt in eine Feature Map kodiert, die klassische Feature-Vektoren in den Quanten-Hilbert-Raum abbildet. Die Konstruktion quantenmechanischer Feature Maps basierend auf parametrisierten Quantencircuits, die klassisch schwer zu simulieren sind, ist ein wichtiger Schritt auf dem Weg zu einem potenziellen Vorteil gegenüber klassischen maschinellen Lernansätzen und ist ein aktives Forschungsgebiet.
Die zz_feature_map kann verwendet werden, um einen parametrisierten Circuit zu erstellen. Wir können unsere Datenpunkte an die Feature Map () und eine separate Variationsform übergeben, um Gewichte als Parameter () einzuspeisen.
from qiskit.circuit.library import zz_feature_map, TwoLocal
data = [0.1, 0.2]
zz_feature_map_reference = zz_feature_map(feature_dimension=2, reps=2)
zz_feature_map_reference = zz_feature_map_reference.assign_parameters(data)
variation_form = TwoLocal(2, ["ry", "rz"], "cz", reps=2)
vqc_ansatz = zz_feature_map_reference.compose(variation_form)
vqc_ansatz.decompose().draw("mpl")
Zusammenfassung
In dieser Lektion hast du gelernt, wie du deinen Suchraum mit einer Variationsform definierst:
- Zustände mit einem parametrisierten Quantencircuit vorbereiten, bei dem Gates mit abstimmbaren Parametern definiert sind
- Wie man Ansätze konstruiert, die Geschwindigkeit und Genauigkeit abwägen
- Heuristische Ansätze
- Problemspezifische Ansätze
Unser variationaler Arbeitsablauf auf hoher Ebene sieht wie folgt aus:
Für jeden variationalen Parameter wird ein anderer Quantenzustand erzeugt. Um die optimalen Parameter zu finden, müssen wir eine problemspezifische Kostenfunktion definieren, um die Parameter unseres Ansatzes iterativ zu aktualisieren.