Algoritmo di ottimizzazione approssimata quantistica
Stima di utilizzo: 22 minuti su un processore Heron r3 (NOTA: Questa è solo una stima. Il tuo tempo di esecuzione potrebbe variare.)
Risultati di apprendimento
Dopo aver completato questo tutorial, puoi aspettarti di comprendere le seguenti informazioni:
- Come mappare un problema classico di ottimizzazione combinatoria (max-cut) a un Hamiltoniano quantistico
- Come implementare ed eseguire il Quantum Approximate Optimization Algorithm (QAOA) usando le sessioni di Qiskit Runtime
- Come scalare un flusso di lavoro QAOA da un piccolo esempio su simulatore all'esecuzione su hardware a scala utility
Prerequisiti
Si consiglia di familiarizzare con questi argomenti:
- Basi dei circuiti quantistici
- Algoritmi variazionali
- QAOA in profondità — per una trattazione completa dell'algoritmo QAOA e della sua applicazione a scala utility
Contesto
Il Quantum Approximate Optimization Algorithm (QAOA) è un metodo iterativo ibrido quanto-classico per risolvere problemi di ottimizzazione combinatoria. In questo tutorial userai QAOA per risolvere il problema del taglio massimo (max-cut) — un problema di ottimizzazione NP-hard con applicazioni nel clustering, nelle scienze delle reti e nella fisica statistica. Dato un grafo di nodi collegati da archi, l'obiettivo è partizionare i nodi in due insiemi in modo tale che il numero di archi che attraversano la partizione sia massimizzato.

Dall'ottimizzazione classica ai circuiti quantistici
Il max-cut può essere espresso come un problema classico di ottimizzazione binaria. A ogni nodo è assegnata una variabile binaria che indica a quale insieme appartiene. L'obiettivo è massimizzare il numero di archi i cui estremi si trovano in insiemi diversi:
Questo è equivalentemente un problema di ottimizzazione binaria quadratica non vincolata (QUBO) della forma . Tramite una sostituzione di variabili standard (), il QUBO può essere riscritto come un Hamiltoniano di costo il cui stato fondamentale codifica la soluzione ottimale. In generale, questo Hamiltoniano ha sia termini quadratici che lineari:
Per il problema max-cut non pesato considerato qui, i coefficienti lineari si annullano () e per ogni arco, lasciando la forma più semplice che costruirai nel codice qui sotto. La forma più generale sopra è ciò di cui avresti bisogno per adattare questo flusso di lavoro a grafi pesati o ad altri problemi esprimibili come QUBO.
Come funziona QAOA
QAOA prepara soluzioni candidate applicando strati alternati di due operatori a uno stato di sovrapposizione iniziale : l'operatore di costo e un operatore di miscelazione (mixer) . Gli angoli e sono ottimizzati in un ciclo di retroazione classico; il computer quantistico valuta la funzione di costo e un ottimizzatore classico aggiorna i parametri fino alla convergenza. Questo ciclo iterativo viene eseguito all'interno di una session di Qiskit Runtime, che mantiene il dispositivo quantistico riservato attraverso le iterazioni per una latenza inferiore.
Per una trattazione più approfondita della teoria QAOA, inclusa la derivazione completa da QUBO a Hamiltoniano, consulta il modulo del corso QAOA.
In questo tutorial risolverai prima il max-cut su un piccolo grafo a cinque nodi, poi scalerai lo stesso flusso di lavoro a un problema a scala utility di 100 nodi su hardware reale. Nota sull'accesso al piano: Questo tutorial usa le sessioni di Qiskit Runtime, che sono disponibili solo nel piano Premium. Se sei nel piano Open, non puoi eseguire questo tutorial così com'è scritto; invece, dovrai sostituire Session con la modalità job (ovvero, inviare ogni iterazione come job indipendente anziché racchiudere il ciclo di ottimizzazione in with Session(...)). Il flusso di lavoro funziona comunque, ma ogni iterazione incorre nell'intera latenza di coda anziché riutilizzare un dispositivo riservato. Consulta la Panoramica dei piani disponibili per maggiori informazioni.
Requisiti
Prima di iniziare questo tutorial, assicurati di avere installato quanto segue:
- Qiskit SDK v2.0 o successivo, con supporto per la visualizzazione
- Qiskit Runtime v0.22 o successivo (
pip install qiskit-ibm-runtime)
Inoltre, avrai bisogno di accesso a un'istanza su IBM Quantum® Platform.
Configurazione
# 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
Esempio su piccola scala
Questa sezione percorre ogni passo del flusso di lavoro QAOA su una piccola istanza di max-cut a cinque nodi. Nonostante l'etichetta "piccola scala", questo esempio viene comunque eseguito su hardware reale IBM Quantum — il codice seleziona un backend con 127 o più qubit ed esegue il circuito lì. Inizializza il tuo problema creando un grafo con nodi.
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)
Passo 1: Mappare gli input classici a un problema quantistico
Mappa il grafo classico in circuiti e operatori quantistici. Come descritto nel Contesto, per il max-cut non pesato l'Hamiltoniano di costo si riduce a , e QAOA usa un circuito ansatz parametrizzato per preparare stati fondamentali candidati di .
Costruire l'Hamiltoniano di costo
Converti gli archi del grafo in termini di Pauli per costruire (consulta il Contesto per la derivazione).
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])
Costruire il circuito ansatz QAOA
Usa QAOAAnsatz per costruire il circuito QAOA parametrizzato a partire dall'Hamiltoniano di costo. Qui usiamo reps=2 (due strati QAOA, quattro parametri: ).
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])])
Passo 2: Ottimizzare il problema per l'esecuzione su hardware quantistico
Transpila il circuito astratto in istruzioni native dell'hardware. Questo passo gestisce la mappatura dei qubit, la decomposizione dei gate, il routing e la soppressione degli errori. Consulta la documentazione sulla transpilazione per maggiori informazioni.
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')>

Passo 3: Eseguire utilizzando le primitive Qiskit
Il ciclo di ottimizzazione QAOA viene eseguito all'interno di una session di Qiskit Runtime per mantenere il dispositivo riservato attraverso le iterazioni. Un Estimator valuta ad ogni passo, e un ottimizzatore classico (COBYLA) aggiorna i parametri fino alla convergenza.
Definisci i parametri iniziali ed esegui il ciclo di ottimizzazione:
# 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
L'ottimizzatore è stato in grado di ridurre il costo e trovare parametri migliori per il circuito.
Una curva che decresce dolcemente e si appiattisce è la firma della convergenza. Una curva rumorosa e non monotona di solito indica che qualcosa a monte richiede attenzione; le cause comuni sono troppo pochi shot per valutazione (alta varianza dell'estimator), parametri iniziali scadenti, o un circuito la cui profondità è dominata dal rumore hardware. COBYLA è privo di derivate e abbastanza robusto a un rumore moderato, ma quando il rumore sommerge i miglioramenti effettivi del costo per passo, il suo modello di approssimazione lineare non riesce più a distinguere la discesa reale dal jitter casuale e l'ottimizzatore vaga.
plt.figure(figsize=(12, 6))
plt.plot(objective_func_vals)
plt.xlabel("Iteration")
plt.ylabel("Cost")
plt.show()
Assegna i parametri ottimizzati e campiona la distribuzione finale usando la primitiva Sampler.
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}
Passo 4: Post-elaborare e restituire il risultato nel formato classico desiderato
Estrai il bitstring più probabile dalla distribuzione campionata. Questo rappresenta il miglior taglio trovato da QAOA.
# 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()
Visualizzare il taglio migliore
Dal bitstring ottimale, puoi quindi visualizzare questo taglio sul grafo originale.
# 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)
Ora, calcola il valore del taglio:
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
Per un grafo così piccolo, l'ottimo reale è facile da trovare con la forza bruta, quindi puoi ricontrollare i risultati confrontando il risultato di QAOA con la risposta esatta.
# 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
Esempio su hardware a grande scala
Hai accesso a molti dispositivi con oltre 100 qubit su IBM Quantum Platform. Selezionane uno su cui risolvere il max-cut su un grafo pesato di 100 nodi. Questo è un problema a "scala utility". Il flusso di lavoro segue gli stessi passi descritti sopra, applicati a un grafo molto più grande.
Flusso di lavoro end-to-end a scala utility
Tutti e quattro i passi sono mostrati qui sotto, applicati al grafo di 100 nodi. La struttura è la stessa della guida su piccola scala: mappare, transpilare, eseguire, post-elaborare — ma con un problema più grande e suddivisa nelle quattro celle seguenti per chiarezza.
# 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
Passo 1: Costruisci il grafo, l'Hamiltoniano di costo e l'ansatz.
# 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()
Passo 2: Transpila per il backend hardware selezionato.
# Step 2: transpile for hardware.
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
candidate_circuit_100 = pm.run(circuit_100)
Passo 3: Esegui il ciclo di ottimizzazione QAOA all'interno di una session, poi campiona.
# 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
Passo 4: Post-elabora la distribuzione campionata per estrarre il miglior taglio.
# 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
Verifica che il costo minimizzato nel ciclo di ottimizzazione sia convergente, e visualizza i risultati.
# 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)

Prossimi passi
Se hai trovato interessante questo lavoro, potresti essere interessato al seguente materiale:
- Tecniche avanzate per QAOA — esplora strategie avanzate per migliorare le prestazioni di QAOA
- Sfida di ottimizzazione multi-obiettivo — metti alla prova le tue competenze con questa sfida della community sull'ottimizzazione quantistica multi-obiettivo
- Documentazione sulla transpilazione per la regolazione fine dell'ottimizzazione dei circuiti
- Soppressione e mitigazione degli errori per migliorare i risultati su hardware