Lewati ke konten utama

Algoritma optimasi perkiraan kuantum

Perkiraan penggunaan: 22 menit pada prosesor Heron r3 (CATATAN: Ini hanya perkiraan. Waktu aktual kamu bisa berbeda.)

Hasil pembelajaran

Setelah menyelesaikan tutorial ini, kamu diharapkan memahami informasi berikut:

  • Cara memetakan masalah optimasi kombinatorial klasik (max-cut) ke sebuah Hamiltonian kuantum
  • Cara mengimplementasikan dan menjalankan Quantum Approximate Optimization Algorithm (QAOA) menggunakan session Qiskit Runtime
  • Cara menskalakan alur kerja QAOA dari contoh simulator kecil ke eksekusi perangkat keras skala utilitas

Prasyarat

Disarankan kamu membiasakan diri dengan topik-topik ini:

Latar belakang

Quantum Approximate Optimization Algorithm (QAOA) adalah metode iteratif hibrida kuantum-klasik untuk memecahkan masalah optimasi kombinatorial. Dalam tutorial ini, kamu akan menggunakan QAOA untuk memecahkan masalah maximum-cut (max-cut) — sebuah masalah optimasi NP-hard dengan aplikasi dalam klasterisasi, ilmu jaringan, dan fisika statistik. Diberikan sebuah graf berisi simpul-simpul yang terhubung oleh sisi, tujuannya adalah membagi simpul-simpul ke dalam dua himpunan sedemikian rupa sehingga jumlah sisi yang melintasi partisi dimaksimalkan.

Ilustrasi masalah max-cut

Dari optimasi klasik ke circuit kuantum

Max-cut dapat dinyatakan sebagai masalah optimasi biner klasik. Setiap simpul diberi sebuah variabel biner xi{0,1}x_i \in \{0, 1\} yang menunjukkan himpunan mana ia berada. Tujuannya adalah memaksimalkan jumlah sisi di mana titik-titik ujungnya berada dalam himpunan yang berbeda:

maxx{0,1}n(i,j)xi+xj2xixj.\max_{x \in \{0,1\}^n} \sum_{(i,j)} x_i + x_j - 2x_ix_j.

Ini secara ekuivalen merupakan masalah Quadratic Unconstrained Binary Optimization (QUBO) berbentuk minxxTQx\min_x\, x^T Q x. Melalui substitusi variabel standar (xi(1Zi)/2x_i \to (1 - Z_i)/2), QUBO dapat ditulis ulang sebagai sebuah Hamiltonian biaya yang keadaan dasarnya mengkodekan solusi optimal. Secara umum, Hamiltonian ini memiliki suku kuadratik maupun linier:

HC=ijQijZiZj+ibiZi.H_C = \sum_{ij} Q_{ij} \, Z_i Z_j + \sum_i b_i \, Z_i.

Untuk masalah max-cut tak berbobot yang dipertimbangkan di sini, koefisien linier menghilang (bi=0b_i = 0) dan Qij=1Q_{ij} = 1 untuk setiap sisi, sehingga tersisa bentuk yang lebih sederhana HC=(i,j)EZiZjH_C = \sum_{(i,j) \in E} Z_i Z_j yang akan kamu bangun dalam kode di bawah. Bentuk yang lebih umum di atas adalah apa yang kamu butuhkan untuk mengadaptasi alur kerja ini ke graf berbobot atau masalah lain yang dapat dinyatakan sebagai QUBO.

Cara kerja QAOA

QAOA menyiapkan solusi kandidat dengan menerapkan lapisan-lapisan berselang-seling dari dua operator pada sebuah keadaan superposisi awal Hn0H^{\otimes n}|0\rangle: operator biaya eiγkHCe^{-i\gamma_k H_C} dan sebuah operator mixer eiβkHme^{-i\beta_k H_m}. Sudut-sudut γk\gamma_k dan βk\beta_k dioptimalkan dalam loop umpan balik klasik; komputer kuantum mengevaluasi fungsi biaya, dan sebuah pengoptimal klasik memperbarui parameter sampai konvergen. Loop iteratif ini berjalan di dalam sebuah session Qiskit Runtime, yang menjaga perangkat kuantum tetap dipesan sepanjang iterasi untuk latensi yang lebih rendah.

Diagram circuit dengan lapisan-lapisan QAOA

Untuk pembahasan teori QAOA yang lebih mendalam, termasuk penurunan QUBO-ke-Hamiltonian lengkap, lihat modul kursus QAOA.

Dalam tutorial ini kamu pertama-tama akan memecahkan max-cut pada sebuah graf kecil lima-simpul, lalu menskalakan alur kerja yang sama ke masalah skala utilitas 100-simpul pada perangkat keras nyata. Catatan tentang akses paket: Tutorial ini menggunakan session Qiskit Runtime, yang hanya tersedia pada Premium Plan. Jika kamu menggunakan Open Plan, kamu tidak dapat menjalankan tutorial ini sebagaimana ditulis; sebagai gantinya, kamu perlu menukar Session dengan job mode (yaitu, kirim setiap iterasi sebagai job independen alih-alih membungkus loop optimasi dengan Session(...)). Alur kerja tetap berjalan, tetapi setiap iterasi menanggung latensi antrean penuh alih-alih menggunakan kembali perangkat yang dipesan. Lihat Gambaran umum paket yang tersedia untuk informasi lebih lanjut.

Persyaratan

Sebelum memulai tutorial ini, pastikan kamu telah menginstal hal-hal berikut:

  • Qiskit SDK v2.0 atau lebih baru, dengan dukungan visualisasi
  • Qiskit Runtime v0.22 atau lebih baru (pip install qiskit-ibm-runtime)

Selain itu, kamu memerlukan akses ke sebuah instance di 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

Contoh skala kecil

Bagian ini memandu kamu melalui setiap langkah alur kerja QAOA pada sebuah instance max-cut kecil lima-simpul. Meskipun berlabel "skala kecil", contoh ini tetap berjalan pada perangkat keras IBM Quantum nyata — kode memilih sebuah backend dengan 127 qubit atau lebih dan mengeksekusi circuit di sana. Inisialisasi masalahmu dengan membuat sebuah graf berisi n=5n=5 simpul.

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)

Output dari sel kode sebelumnya

Langkah 1: Petakan input klasik ke masalah kuantum

Petakan graf klasik ke dalam circuit dan operator kuantum. Seperti dijelaskan dalam Latar belakang, untuk max-cut tak berbobot Hamiltonian biaya tereduksi menjadi HC=(i,j)EZiZjH_C = \sum_{(i,j) \in E} Z_i Z_j, dan QAOA menggunakan sebuah circuit ansatz terparametrasi untuk menyiapkan keadaan dasar kandidat dari HCH_C.

Bangun Hamiltonian biaya

Konversikan sisi-sisi graf menjadi suku Pauli ZiZjZ_iZ_j untuk membangun HCH_C (lihat Latar belakang untuk penurunannya).

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])

Bangun circuit ansatz QAOA

Gunakan QAOAAnsatz untuk membangun circuit QAOA terparametrasi dari Hamiltonian biaya. Di sini kita menggunakan reps=2 (dua lapisan QAOA, empat parameter: β0,β1,γ0,γ1\beta_0, \beta_1, \gamma_0, \gamma_1).

circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2)
circuit.measure_all()

circuit.draw("mpl")

Output dari sel kode sebelumnya

circuit.parameters
ParameterView([ParameterVectorElement(β[0]), ParameterVectorElement(β[1]), ParameterVectorElement(γ[0]), ParameterVectorElement(γ[1])])

Langkah 2: Optimalkan masalah untuk eksekusi perangkat keras kuantum

Transpilasikan circuit abstrak menjadi instruksi native perangkat keras. Langkah ini menangani pemetaan qubit, dekomposisi gate, perutean, dan penekanan kesalahan. Lihat dokumentasi transpilasi untuk informasi lebih lanjut.

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')>

Output dari sel kode sebelumnya

Langkah 3: Eksekusi menggunakan primitif Qiskit

Loop optimasi QAOA berjalan di dalam sebuah session Qiskit Runtime untuk menjaga perangkat tetap dipesan sepanjang iterasi. Sebuah Estimator mengevaluasi HC\langle H_C \rangle pada setiap langkah, dan sebuah pengoptimal klasik (COBYLA) memperbarui parameter sampai konvergen.

Ilustrasi yang menunjukkan perilaku mode runtime Single job, Batch, dan Session. Tentukan parameter awal dan jalankan loop optimasi:

# 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

Pengoptimal berhasil mengurangi biaya dan menemukan parameter yang lebih baik untuk circuit.

Kurva yang menurun secara mulus dan kemudian mendatar adalah ciri khas konvergensi. Kurva yang berisik dan tidak monoton biasanya menunjukkan bahwa ada sesuatu di hulu yang perlu diperhatikan; penyebab umumnya adalah terlalu sedikit shot per evaluasi (varians estimator tinggi), parameter awal yang buruk, atau circuit yang kedalamannya didominasi oleh noise perangkat keras. COBYLA bebas turunan dan cukup tahan terhadap noise sedang, tetapi ketika noise menenggelamkan peningkatan biaya yang sebenarnya per langkah, model aproksimasi liniernya tidak lagi dapat membedakan penurunan nyata dari jitter acak dan pengoptimal pun mengembara.

plt.figure(figsize=(12, 6))
plt.plot(objective_func_vals)
plt.xlabel("Iteration")
plt.ylabel("Cost")
plt.show()

Output dari sel kode sebelumnya

Tetapkan parameter yang telah dioptimalkan dan ambil sampel distribusi akhir menggunakan primitif Sampler.

optimized_circuit = candidate_circuit.assign_parameters(result.x)
optimized_circuit.draw("mpl", fold=False, idle_wires=False)

Output dari sel kode sebelumnya

# 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}

Langkah 4: Pasca-proses dan kembalikan hasil dalam format klasik yang diinginkan

Ekstrak bitstring yang paling mungkin dari distribusi yang telah diambil sampelnya. Ini merepresentasikan potongan terbaik yang ditemukan oleh 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()

Output dari sel kode sebelumnya

Visualisasikan potongan terbaik

Dari bitstring optimal, kamu kemudian dapat memvisualisasikan potongan ini pada graf asli.

# 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)

Output dari sel kode sebelumnya

Sekarang, hitung nilai potongannya:

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

Untuk graf sekecil ini, optimum sebenarnya mudah dicari dengan brute-force, sehingga kamu dapat memeriksa ulang hasilnya dengan membandingkan hasil QAOA terhadap jawaban eksak.

# 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

Contoh perangkat keras skala besar

Kamu memiliki akses ke banyak perangkat dengan lebih dari 100 qubit di IBM Quantum Platform. Pilih salah satu untuk memecahkan max-cut pada sebuah graf berbobot 100-simpul. Ini adalah masalah "skala utilitas". Alur kerjanya mengikuti langkah-langkah yang sama seperti di atas, diterapkan pada graf yang jauh lebih besar.

Alur kerja menyeluruh pada skala utilitas

Keempat langkah ditunjukkan di bawah, diterapkan pada graf 100-simpul. Strukturnya sama dengan panduan skala kecil: petakan, transpilasi, eksekusi, pasca-proses — tetapi dengan masalah yang lebih besar dan dibagi ke dalam empat sel di bawah agar lebih jelas.

# 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

Langkah 1: Bangun graf, Hamiltonian biaya, dan 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()

Langkah 2: Transpilasikan untuk backend perangkat keras yang dipilih.

# Step 2: transpile for hardware.
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
candidate_circuit_100 = pm.run(circuit_100)

Langkah 3: Jalankan loop optimasi QAOA di dalam sebuah session, lalu ambil sampelnya.

# 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

Langkah 4: Pasca-proses distribusi yang diambil sampelnya untuk mengekstrak potongan terbaik.

# 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

Periksa bahwa biaya yang diminimalkan dalam loop optimasi telah konvergen, dan visualisasikan hasilnya.

# 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)

Output dari sel kode sebelumnya

Output dari sel kode sebelumnya

Output dari sel kode sebelumnya

Langkah selanjutnya

Jika kamu merasa karya ini menarik, kamu mungkin tertarik dengan materi berikut:

Rekomendasi