Quanten-Näherungsoptimierungsalgorithmus
Geschätzte Nutzungsdauer: 22 Minuten auf einem Heron-r3-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine Laufzeit kann abweichen.)
Lernziele
Nach Abschluss dieses Tutorials wirst du folgende Inhalte verstehen:
- Wie man ein klassisches kombinatorisches Optimierungsproblem (Max-Cut) auf einen Quanten-Hamiltonoperator abbildet
- Wie man den Quantum Approximate Optimization Algorithm (QAOA) mit Qiskit Runtime Sessions implementiert und ausführt
- Wie man einen QAOA-Workflow von einem kleinen Simulator-Beispiel auf Utility-Scale-Hardware skaliert
Voraussetzungen
Es wird empfohlen, dich mit folgenden Themen vertraut zu machen:
- Grundlagen von Quantum Circuits
- Variationsalgorithmen
- QAOA im Detail — für eine umfassende Behandlung des QAOA-Algorithmus und seiner Anwendung im Utility-Scale
Hintergrund
Der Quantum Approximate Optimization Algorithm (QAOA) ist eine hybride quantenklassische Iterationsmethode zur Lösung kombinatorischer Optimierungsprobleme. In diesem Tutorial verwendest du QAOA, um das Maximum-Cut-Problem (Max-Cut) zu lösen — ein NP-hartes Optimierungsproblem mit Anwendungen in der Clusteranalyse, Netzwerkwissenschaft und statistischen Physik. Gegeben ist ein Graph aus Knoten, die durch Kanten verbunden sind; das Ziel ist es, die Knoten in zwei Mengen aufzuteilen, sodass die Anzahl der Kanten, die die Partition überqueren, maximiert wird.

Von klassischer Optimierung zu Quantum Circuits
Max-Cut lässt sich als klassisches binäres Optimierungsproblem formulieren. Jedem Knoten wird eine binäre Variable zugewiesen, die angibt, zu welcher Menge er gehört. Das Ziel ist es, die Anzahl der Kanten zu maximieren, bei denen die Endpunkte in verschiedenen Mengen liegen:
Dies ist äquivalent zu einem Quadratic Unconstrained Binary Optimization (QUBO)-Problem der Form . Durch eine Standardvariablensubstitution () kann das QUBO als Kosten-Hamiltonoperator umgeschrieben werden, dessen Grundzustand die optimale Lösung kodiert. Im Allgemeinen enthält dieser Hamiltonoperator sowohl quadratische als auch lineare Terme:
Für das ungewichtete Max-Cut-Problem, das hier betrachtet wird, verschwinden die linearen Koeffizienten () und für jede Kante, was die einfachere Form ergibt, die du im Code weiter unten aufbauen wirst. Die allgemeinere Form oben ist das, was du benötigen würdest, um diesen Workflow auf gewichtete Graphen oder andere QUBO-ausdrückbare Probleme anzupassen.
Wie QAOA funktioniert
QAOA bereitet Kandidatenlösungen vor, indem es abwechselnde Schichten zweier Operatoren auf einen anfänglichen Superpositionszustand anwendet: den Kostenoperator und einen Mixer-Operator . Die Winkel und werden in einer klassischen Rückkopplungsschleife optimiert; der Quantencomputer wertet die Kostenfunktion aus, und ein klassischer Optimierer aktualisiert die Parameter bis zur Konvergenz. Diese iterative Schleife läuft innerhalb einer Qiskit Runtime Session, die das Quantengerät über mehrere Iterationen hinweg reserviert hält, um geringere Latenz zu erzielen.
Für eine tiefere Behandlung der QAOA-Theorie, einschließlich der vollständigen QUBO-zu-Hamiltonoperator-Ableitung, siehe das QAOA-Kursmodul.
In diesem Tutorial löst du zunächst Max-Cut auf einem kleinen Fünf-Knoten-Graphen und skalierst dann denselben Workflow auf ein 100-Knoten-Utility-Scale-Problem auf echter Hardware. Hinweis zum Planzugang: Dieses Tutorial verwendet Qiskit Runtime Sessions, die nur im Premium-Plan verfügbar sind. Wenn du den Open-Plan nutzt, kannst du dieses Tutorial nicht wie beschrieben ausführen; stattdessen musst du Session gegen den Job-Modus tauschen (d. h. jede Iteration als unabhängigen Job einreichen, anstatt die Optimierungsschleife in Session(...) zu verpacken). Der Workflow funktioniert dennoch, aber bei jeder Iteration entsteht die volle Warteschlangenlatenz anstatt ein reserviertes Gerät wiederzuverwenden. Weitere Informationen findest du unter Übersicht der verfügbaren Pläne.
Anforderungen
Stelle vor dem Start dieses Tutorials sicher, dass du Folgendes installiert hast:
- Qiskit SDK v2.0 oder höher, mit Visualisierungs-Unterstützung
- Qiskit Runtime v0.22 oder höher (
pip install qiskit-ibm-runtime)
Außerdem benötigst du Zugang zu einer Instanz auf IBM Quantum® Platform.
Setup
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime rustworkx scipy
import matplotlib.pyplot as plt
import rustworkx as rx
from rustworkx.visualization import mpl_draw as draw_graph
import numpy as np
from scipy.optimize import minimize
from collections import defaultdict
from typing import Sequence
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import QAOAAnsatz
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session, EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler
Kleinskaliges Beispiel
Dieser Abschnitt führt durch jeden Schritt des QAOA-Workflows an einem kleinen Max-Cut-Problem mit fünf Knoten. Trotz der Bezeichnung „kleinskalig" läuft dieses Beispiel dennoch auf echter IBM-Quantum-Hardware — der Code wählt einen Backend mit 127 oder mehr Qubits aus und führt den Circuit dort aus. Initialisiere dein Problem, indem du einen Graphen mit Knoten erstellst.
n_small = 5
graph = rx.PyGraph()
graph.add_nodes_from(np.arange(0, n_small, 1))
edge_list = [
(0, 1, 1.0),
(0, 2, 1.0),
(0, 4, 1.0),
(1, 2, 1.0),
(2, 3, 1.0),
(3, 4, 1.0),
]
graph.add_edges_from(edge_list)
draw_graph(graph, node_size=600, with_labels=True)
Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
Bilde den klassischen Graphen auf Quanten-Circuits und Operatoren ab. Wie im Hintergrund beschrieben, reduziert sich der Kosten-Hamiltonoperator für ungewichtetes Max-Cut auf , und QAOA verwendet einen parametrisierten Ansatz-Circuit, um Kandidaten-Grundzustände von vorzubereiten.
Kosten-Hamiltonoperator aufbauen
Konvertiere die Graphkanten in Pauli--Terme, um zu konstruieren (siehe Hintergrund für die Herleitung).
def build_max_cut_paulis(
graph: rx.PyGraph,
) -> list[tuple[str, list[int], float]]:
"""Convert graph edges to a list of ZZ Pauli terms.
The returned list is in the sparse format expected by
``SparsePauliOp.from_sparse_list``: each element is
``(pauli_string, qubit_indices, coefficient)``.
"""
pauli_list = []
for edge in list(graph.edge_list()):
weight = graph.get_edge_data(edge[0], edge[1])
pauli_list.append(("ZZ", [edge[0], edge[1]], weight))
return pauli_list
max_cut_paulis = build_max_cut_paulis(graph)
cost_hamiltonian = SparsePauliOp.from_sparse_list(max_cut_paulis, n_small)
print("Cost Function Hamiltonian:", cost_hamiltonian)
Cost Function Hamiltonian: SparsePauliOp(['IIIZZ', 'IIZIZ', 'ZIIIZ', 'IIZZI', 'IZZII', 'ZZIII'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])
QAOA-Ansatz-Circuit aufbauen
Verwende QAOAAnsatz, um den parametrisierten QAOA-Circuit aus dem Kosten-Hamiltonoperator zu konstruieren. Hier verwenden wir reps=2 (zwei QAOA-Schichten, vier Parameter: ).
circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2)
circuit.measure_all()
circuit.draw("mpl")
circuit.parameters
ParameterView([ParameterVectorElement(β[0]), ParameterVectorElement(β[1]), ParameterVectorElement(γ[0]), ParameterVectorElement(γ[1])])
Schritt 2: Problem für die Ausführung auf Quantenhardware optimieren
Transpiliere den abstrakten Circuit in hardware-native Anweisungen. Dieser Schritt übernimmt Qubit-Mapping, Gate-Zerlegung, Routing und Fehlerunterdrückung. Weitere Informationen findest du in der Transpilierungs-Dokumentation.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
print(backend)
# Create pass manager for transpilation. Level 3 is the most aggressive
# preset: slower to transpile, but produces shorter circuits that are
# more robust to hardware noise.
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
candidate_circuit = pm.run(circuit)
candidate_circuit.draw("mpl", fold=False, idle_wires=False)
<IBMBackend('ibm_pittsburgh')>

Schritt 3: Mit Qiskit-Primitiven ausführen
Die QAOA-Optimierungsschleife läuft innerhalb einer Qiskit Runtime Session, um das Gerät über mehrere Iterationen hinweg reserviert zu halten. Ein Estimator bewertet bei jedem Schritt, und ein klassischer Optimierer (COBYLA) aktualisiert die Parameter bis zur Konvergenz.
Definiere die anfänglichen Parameter und führe die Optimierungsschleife aus:
# QAOA doesn't prescribe principled default angles — any bounded choice
# works as a warm start for problems this small. beta and gamma are
# periodic (beta in [0, pi] and gamma in [0, 2*pi] modulo the underlying
# Pauli-rotation periods), and pi/2 and pi are just midpoints of those
# ranges. For harder problems you would typically warm start from known
# good angles or transfer parameters from smaller instances.
initial_gamma = np.pi
initial_beta = np.pi / 2
init_params = [initial_beta, initial_beta, initial_gamma, initial_gamma]
def cost_func_estimator(params, ansatz, hamiltonian, estimator):
# transform the observable defined on virtual qubits to
# an observable defined on all physical qubits
isa_hamiltonian = hamiltonian.apply_layout(ansatz.layout)
pub = (ansatz, isa_hamiltonian, params)
job = estimator.run([pub])
results = job.result()[0]
cost = results.data.evs
objective_func_vals.append(cost)
return cost
objective_func_vals = [] # Global variable
with Session(backend=backend) as session:
# If using qiskit-ibm-runtime<0.24.0, change `mode=` to `session=`
estimator = Estimator(mode=session)
estimator.options.default_shots = 1000
# Set simple error suppression/mitigation options
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XY4"
estimator.options.twirling.enable_gates = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.environment.job_tags = ["TUT_QAOA"]
result = minimize(
cost_func_estimator,
init_params,
args=(candidate_circuit, cost_hamiltonian, estimator),
method="COBYLA",
tol=1e-2,
)
print(result)
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -2.0402211719947774
x: [ 3.041e+00 1.212e+00 2.081e+00 4.471e+00]
nfev: 36
maxcv: 0.0
Der Optimierer konnte die Kosten reduzieren und bessere Parameter für den Circuit finden.
Eine gleichmäßig abfallende Kurve, die ein Plateau erreicht, ist das Zeichen für Konvergenz. Eine verrauschte, nicht-monotone Kurve weist in der Regel darauf hin, dass vorgelagerte Schritte Aufmerksamkeit erfordern; häufige Ursachen sind zu wenige Aufnahmen pro Auswertung (hohe Estimator-Varianz), schlechte Anfangsparameter oder ein Circuit, dessen Tiefe von Hardware-Rauschen dominiert wird. COBYLA ist ableitungsfrei und gegenüber moderatem Rauschen ziemlich robust, aber wenn das Rauschen die eigentlichen Kostenverbesserungen pro Schritt übertönt, kann sein lineares Näherungsmodell keinen echten Abstieg mehr von zufälligem Rauschen unterscheiden, und der Optimierer irrt umher.
plt.figure(figsize=(12, 6))
plt.plot(objective_func_vals)
plt.xlabel("Iteration")
plt.ylabel("Cost")
plt.show()
Weise die optimierten Parameter zu und sample die endgültige Verteilung mit dem Sampler-Primitiv.
optimized_circuit = candidate_circuit.assign_parameters(result.x)
optimized_circuit.draw("mpl", fold=False, idle_wires=False)

# If using qiskit-ibm-runtime<0.24.0, change `mode=` to `backend=`
sampler = Sampler(mode=backend)
sampler.options.default_shots = 10000
# Set simple error suppression/mitigation options
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
sampler.options.twirling.enable_gates = True
sampler.options.twirling.num_randomizations = "auto"
sampler.options.environment.job_tags = ["TUT_QAOA"]
pub = (optimized_circuit,)
job = sampler.run([pub], shots=int(1e4))
counts_int = job.result()[0].data.meas.get_int_counts()
counts_bin = job.result()[0].data.meas.get_counts()
shots = sum(counts_int.values())
final_distribution_int = {key: val / shots for key, val in counts_int.items()}
final_distribution_bin = {key: val / shots for key, val in counts_bin.items()}
print(final_distribution_int)
{18: 0.039, 5: 0.0665, 20: 0.0973, 29: 0.0063, 9: 0.0899, 13: 0.0379, 2: 0.0047, 1: 0.0153, 11: 0.0932, 14: 0.0327, 12: 0.0314, 25: 0.0193, 21: 0.0398, 6: 0.0224, 4: 0.0197, 10: 0.0387, 3: 0.0181, 26: 0.07, 17: 0.0327, 19: 0.0332, 22: 0.0914, 24: 0.007, 0: 0.0033, 8: 0.0066, 30: 0.0158, 28: 0.0169, 27: 0.0222, 16: 0.0073, 7: 0.0057, 23: 0.0062, 15: 0.0054, 31: 0.0041}
Schritt 4: Nachverarbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
Extrahiere den wahrscheinlichsten Bitstring aus der gesampleten Verteilung. Dieser repräsentiert den besten von QAOA gefundenen Schnitt.
# auxiliary functions to sample most likely bitstring
def to_bitstring(integer, num_bits):
result = np.binary_repr(integer, width=num_bits)
return [int(digit) for digit in result]
keys = list(final_distribution_int.keys())
values = list(final_distribution_int.values())
most_likely = keys[np.argmax(np.abs(values))]
most_likely_bitstring = to_bitstring(most_likely, len(graph))
most_likely_bitstring.reverse()
print("Result bitstring:", most_likely_bitstring)
Result bitstring: [0, 0, 1, 0, 1]
plt.rcParams.update({"font.size": 10})
final_bits = final_distribution_bin
values = np.abs(list(final_bits.values()))
top_4_values = sorted(values, reverse=True)[:4]
positions = []
for value in top_4_values:
positions.append(np.where(values == value)[0])
fig = plt.figure(figsize=(11, 6))
ax = fig.add_subplot(1, 1, 1)
plt.xticks(rotation=45)
plt.title("Result Distribution")
plt.xlabel("Bitstrings (reversed)")
plt.ylabel("Probability")
ax.bar(list(final_bits.keys()), list(final_bits.values()), color="tab:grey")
for p in positions:
ax.get_children()[int(p[0])].set_color("tab:purple")
plt.show()
Besten Schnitt visualisieren
Anhand des optimalen Bitstrings kannst du diesen Schnitt auf dem ursprünglichen Graphen visualisieren.
# auxiliary function to plot graphs
def plot_result(G, x):
colors = ["tab:grey" if i == 0 else "tab:purple" for i in x]
pos, _default_axes = rx.spring_layout(G), plt.axes(frameon=True)
rx.visualization.mpl_draw(
G, node_color=colors, node_size=100, alpha=0.8, pos=pos
)
plot_result(graph, most_likely_bitstring)
Berechne nun den Wert des Schnitts:
def evaluate_sample(x: Sequence[int], graph: rx.PyGraph) -> float:
assert len(x) == len(
list(graph.nodes())
), "The length of x must coincide with the number of nodes in the graph."
return sum(
x[u] * (1 - x[v]) + x[v] * (1 - x[u])
for u, v in list(graph.edge_list())
)
cut_value = evaluate_sample(most_likely_bitstring, graph)
print("The value of the cut is:", cut_value)
The value of the cut is: 5
Für einen so kleinen Graphen ist das wahre Optimum leicht durch Brute-Force zu bestimmen, sodass du die Ergebnisse überprüfen kannst, indem du das QAOA-Ergebnis mit der exakten Antwort vergleichst.
# Classical baseline: enumerate all 2**n_small bitstrings and take the best cut.
def brute_force_max_cut(graph: rx.PyGraph) -> tuple[int, list[int]]:
n = len(list(graph.nodes()))
best_cut = -1
best_x: list[int] = []
for i in range(2**n):
x = [(i >> k) & 1 for k in range(n)]
cut = evaluate_sample(x, graph)
if cut > best_cut:
best_cut = int(cut)
best_x = x
return best_cut, best_x
classical_best, classical_x = brute_force_max_cut(graph)
print(f"Classical optimum (brute force): {classical_best}")
print(f"QAOA cut value: {cut_value}")
Classical optimum (brute force): 5
QAOA cut value: 5
Großskaliges Hardware-Beispiel
Du hast Zugang zu vielen Geräten mit über 100 Qubits auf der IBM Quantum Platform. Wähle eines aus, um Max-Cut auf einem gewichteten Graphen mit 100 Knoten zu lösen. Dies ist ein Problem im „Utility-Scale". Der Workflow folgt denselben Schritten wie oben, angewendet auf einen viel größeren Graphen.
End-to-End-Workflow im Utility-Scale
Alle vier Schritte sind unten dargestellt, angewendet auf den 100-Knoten-Graphen. Die Struktur ist dieselbe wie bei der kleinskaligen Anleitung: abbilden, transpilieren, ausführen, nachverarbeiten — aber mit einem größeren Problem und der Übersichtlichkeit halber auf die vier Zellen unten aufgeteilt.
# Precomputed parity lookup table: _PARITY[b] = +1 if popcount(b) is even, else -1.
# We use this to vectorize expectation-value evaluation across all Pauli terms.
_PARITY = np.array(
[-1 if bin(i).count("1") % 2 else 1 for i in range(256)],
dtype=np.complex128,
)
def evaluate_sparse_pauli(state: int, observable: SparsePauliOp) -> complex:
"""Expectation value of a SparsePauliOp on a single computational-basis state.
For a Z-only observable (which QAOA cost Hamiltonians are, after the
QUBO-to-Hamiltonian mapping), the eigenvalue of each Pauli term on a
computational-basis state is simply (-1)**popcount(z_mask AND state),
i.e., the parity of the bitwise-AND of the term's Z-support and the
measured bitstring.
This routine packs the Z-support of every Pauli term into bytes, ANDs
them against the measured state in a single vectorized op, and looks up
the parity in _PARITY. For a 100-qubit / ~hundreds-of-terms Hamiltonian
over 10_000 samples, this is dramatically faster than calling
SparsePauliOp.expectation_value per sample.
"""
packed_uint8 = np.packbits(observable.paulis.z, axis=1, bitorder="little")
state_bytes = np.frombuffer(
state.to_bytes(packed_uint8.shape[1], "little"), dtype=np.uint8
)
reduced = np.bitwise_xor.reduce(packed_uint8 & state_bytes, axis=1)
return np.sum(observable.coeffs * _PARITY[reduced])
def best_solution(samples, hamiltonian):
"""Return the sampled bitstring (as int) with the lowest Hamiltonian cost."""
min_cost = float("inf")
min_sol = None
for bit_str in samples.keys():
candidate_sol = int(bit_str)
fval = evaluate_sparse_pauli(candidate_sol, hamiltonian).real
if fval <= min_cost:
min_cost = fval
min_sol = candidate_sol
return min_sol
def _plot_cdf(objective_values: dict, ax, color):
x_vals = sorted(objective_values.keys(), reverse=True)
y_vals = np.cumsum([objective_values[x] for x in x_vals])
ax.plot(x_vals, y_vals, color=color)
def plot_cdf(dist, ax, title):
_plot_cdf(dist, ax, "C1")
ax.vlines(min(list(dist.keys())), 0, 1, "C1", linestyle="--")
ax.set_title(title)
ax.set_xlabel("Objective function value")
ax.set_ylabel("Cumulative distribution function")
ax.grid(alpha=0.3)
def samples_to_objective_values(samples, hamiltonian):
"""Convert the samples to values of the objective function."""
objective_values = defaultdict(float)
for bit_str, prob in samples.items():
candidate_sol = int(bit_str)
fval = evaluate_sparse_pauli(candidate_sol, hamiltonian).real
objective_values[fval] += prob
return objective_values
Schritt 1: Graphen, Kosten-Hamiltonoperator und Ansatz aufbauen.
# Step 1: build the 100-node graph, cost Hamiltonian, and QAOA ansatz.
n_large = 100
graph_100 = rx.PyGraph()
graph_100.add_nodes_from(np.arange(0, n_large, 1))
elist = []
for edge in backend.coupling_map:
if edge[0] < n_large and edge[1] < n_large:
elist.append((edge[0], edge[1], 1.0))
graph_100.add_edges_from(elist)
max_cut_paulis_100 = build_max_cut_paulis(graph_100)
cost_hamiltonian_100 = SparsePauliOp.from_sparse_list(
max_cut_paulis_100, n_large
)
circuit_100 = QAOAAnsatz(cost_operator=cost_hamiltonian_100, reps=1)
circuit_100.measure_all()
Schritt 2: Für den ausgewählten Hardware-Backend transpilieren.
# Step 2: transpile for hardware.
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
candidate_circuit_100 = pm.run(circuit_100)
Schritt 3: Die QAOA-Optimierungsschleife innerhalb einer Session ausführen, dann sampeln.
# Step 3: run the QAOA optimization loop on the device, then sample the
# final distribution with the optimized parameters.
initial_gamma = np.pi
initial_beta = np.pi / 2
init_params = [initial_beta, initial_gamma]
objective_func_vals = [] # Global variable
with Session(backend=backend) as session:
estimator = Estimator(mode=session)
estimator.options.default_shots = 1000
# Set simple error suppression/mitigation options
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XY4"
estimator.options.twirling.enable_gates = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.environment.job_tags = ["TUT_QAOA"]
result = minimize(
cost_func_estimator,
init_params,
args=(candidate_circuit_100, cost_hamiltonian_100, estimator),
method="COBYLA",
)
print(result)
# Assign optimal parameters and sample the final distribution.
optimized_circuit_100 = candidate_circuit_100.assign_parameters(result.x)
sampler = Sampler(mode=backend)
sampler.options.default_shots = 10000
# Set simple error suppression/mitigation options
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
sampler.options.twirling.enable_gates = True
sampler.options.twirling.num_randomizations = "auto"
# Add a unique tag to the job execution
sampler.options.environment.job_tags = ["TUT_QAOA"]
pub = (optimized_circuit_100,)
job = sampler.run([pub], shots=int(1e4))
counts_int = job.result()[0].data.meas.get_int_counts()
shots = sum(counts_int.values())
final_distribution_100_int = {
key: val / shots for key, val in counts_int.items()
}
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -17.172689238986344
x: [ 2.574e+00 4.166e+00]
nfev: 28
maxcv: 0.0
Schritt 4: Die gesampelte Verteilung nachverarbeiten, um den besten Schnitt zu extrahieren.
# Step 4: find the best-cost sample and evaluate its cut value.
best_sol_100 = best_solution(final_distribution_100_int, cost_hamiltonian_100)
best_sol_bitstring_100 = to_bitstring(int(best_sol_100), len(graph_100))
best_sol_bitstring_100.reverse()
print("Result bitstring:", best_sol_bitstring_100)
cut_value_100 = evaluate_sample(best_sol_bitstring_100, graph_100)
print("The value of the cut is:", cut_value_100)
Result bitstring: [1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0]
The value of the cut is: 156
Überprüfe, ob die in der Optimierungsschleife minimierten Kosten konvergiert sind, und visualisiere die Ergebnisse.
# Plot convergence
plt.figure(figsize=(12, 6))
plt.plot(objective_func_vals)
plt.xlabel("Iteration")
plt.ylabel("Cost")
plt.show()
# Visualize the cut
plot_result(graph_100, best_sol_bitstring_100)
# Plot cumulative distribution function
result_dist = samples_to_objective_values(
final_distribution_100_int, cost_hamiltonian_100
)
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
plot_cdf(result_dist, ax, backend.name)

Nächste Schritte
Wenn du diese Arbeit interessant findest, könnten dich folgende Materialien interessieren:
- Fortgeschrittene Techniken für QAOA — erkundet fortgeschrittene Strategien zur Verbesserung der QAOA-Leistung
- Multi-Ziel-Optimierungsaufgabe — teste deine Fähigkeiten mit dieser Community-Aufgabe zur multi-objektiven Quantenoptimierung
- Transpilierungsdokumentation zur Feinabstimmung der Circuit-Optimierung
- Fehlerunterdrückung und -minderung zur Verbesserung der Hardware-Ergebnisse