Langkau ke kandungan utama

Algoritma pengoptimuman hampiran kuantum

Anggaran penggunaan: 22 minit pada pemproses Heron r3 (NOTA: Ini hanyalah anggaran. Masa jalan anda mungkin berbeza.)

Hasil pembelajaran

Selepas melengkapkan tutorial ini, anda boleh mengharapkan untuk memahami maklumat berikut:

  • Cara memetakan masalah pengoptimuman kombinatorial klasik (max-cut) kepada Hamiltonian kuantum
  • Cara melaksanakan dan menjalankan Algoritma Pengoptimuman Hampiran Kuantum (QAOA) menggunakan sesi Qiskit Runtime
  • Cara menskalakan aliran kerja QAOA daripada contoh simulator kecil kepada pelaksanaan perkakasan pada skala utiliti

Prasyarat

Adalah disyorkan agar anda membiasakan diri dengan topik-topik ini:

Latar belakang

Algoritma Pengoptimuman Hampiran Kuantum (QAOA) ialah kaedah iteratif hibrid kuantum-klasik untuk menyelesaikan masalah pengoptimuman kombinatorial. Dalam tutorial ini, anda akan menggunakan QAOA untuk menyelesaikan masalah pemotongan maksimum (max-cut) — sebuah masalah pengoptimuman NP-hard dengan aplikasi dalam pengelompokan, sains rangkaian, dan fizik statistik. Diberikan satu graf nod yang disambungkan oleh tepi, matlamatnya ialah membahagikan nod kepada dua set supaya bilangan tepi yang melintasi pembahagian dimaksimumkan.

Ilustrasi masalah max-cut

Daripada pengoptimuman klasik kepada litar kuantum

Max-cut boleh dinyatakan sebagai masalah pengoptimuman binari klasik. Setiap nod diberikan pemboleh ubah binari xi{0,1}x_i \in \{0, 1\} yang menunjukkan set mana ia tergolong. Objektifnya ialah memaksimumkan bilangan tepi yang titik hujungnya berada dalam set yang berbeza:

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 setara dengan masalah Pengoptimuman Binari Tak Berkekangan Kuadratik (QUBO) dalam bentuk minxxTQx\min_x\, x^T Q x. Melalui penggantian pemboleh ubah piawai (xi(1Zi)/2x_i \to (1 - Z_i)/2), QUBO boleh ditulis semula sebagai Hamiltonian kos yang keadaan asasnya mengekodkan penyelesaian optimum. Secara umum, Hamiltonian ini mempunyai kedua-dua sebutan kuadratik dan linear:

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

Untuk masalah max-cut tidak berwajaran yang dipertimbangkan di sini, pekali linear hilang (bi=0b_i = 0) dan Qij=1Q_{ij} = 1 untuk setiap tepi, meninggalkan bentuk yang lebih ringkas HC=(i,j)EZiZjH_C = \sum_{(i,j) \in E} Z_i Z_j yang akan anda bina dalam kod di bawah. Bentuk yang lebih umum di atas ialah apa yang anda perlukan untuk menyesuaikan aliran kerja ini kepada graf berwajaran atau masalah lain yang boleh dinyatakan sebagai QUBO.

Bagaimana QAOA berfungsi

QAOA menyediakan penyelesaian calon dengan menggunakan lapisan bersilih-ganti bagi dua operator kepada keadaan pertindihan awal Hn0H^{\otimes n}|0\rangle: operator kos eiγkHCe^{-i\gamma_k H_C} dan operator pencampur eiβkHme^{-i\beta_k H_m}. Sudut γk\gamma_k dan βk\beta_k dioptimumkan dalam gelung maklum balas klasik; komputer kuantum menilai fungsi kos, dan pengoptimum klasik mengemas kini parameter sehingga penumpuan. Gelung iteratif ini berjalan dalam session Qiskit Runtime, yang mengekalkan peranti kuantum ditempah merentas iterasi untuk latensi yang lebih rendah.

Gambar rajah litar dengan lapisan QAOA

Untuk pengupasan teori QAOA yang lebih mendalam, termasuk terbitan penuh QUBO-kepada-Hamiltonian, lihat modul kursus QAOA.

Dalam tutorial ini anda akan menyelesaikan max-cut pada graf kecil lima nod terlebih dahulu, kemudian menskalakan aliran kerja yang sama kepada masalah skala utiliti 100 nod pada perkakasan sebenar. Nota tentang akses pelan: Tutorial ini menggunakan sesi Qiskit Runtime, yang hanya tersedia pada Pelan Premium. Jika anda berada pada Pelan Terbuka, anda tidak boleh menjalankan tutorial ini seperti yang ditulis; sebaliknya, anda perlu menukar Session kepada mod kerja (iaitu, serahkan setiap iterasi sebagai kerja bebas dan bukannya membalut gelung pengoptimuman dengan with Session(...)). Aliran kerja masih berjalan, tetapi setiap iterasi menanggung latensi baris gilir penuh dan bukannya menggunakan semula peranti yang ditempah. Lihat Gambaran keseluruhan pelan yang tersedia untuk maklumat lanjut.

Keperluan

Sebelum memulakan tutorial ini, pastikan anda telah memasang perkara berikut:

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

Selain itu, anda perlu mengakses instans pada IBM Quantum® Platform.

Persediaan

# 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

Bahagian ini menelusuri setiap langkah aliran kerja QAOA pada instans max-cut lima nod yang kecil. Walaupun berlabel "skala kecil", contoh ini masih berjalan pada perkakasan IBM Quantum sebenar — kod ini memilih Backend dengan 127 atau lebih qubit dan melaksanakan Circuit di sana. Mulakan masalah anda dengan mencipta graf dengan n=5n=5 nod.

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 of the previous code cell

Langkah 1: Petakan input klasik kepada masalah kuantum

Petakan graf klasik kepada Circuit dan operator kuantum. Seperti yang diterangkan dalam Latar belakang, untuk max-cut tidak berwajaran Hamiltonian kos berkurang kepada HC=(i,j)EZiZjH_C = \sum_{(i,j) \in E} Z_i Z_j, dan QAOA menggunakan litar ansatz terparameter untuk menyediakan keadaan asas calon bagi HCH_C.

Bina Hamiltonian kos

Tukarkan tepi graf kepada sebutan Pauli ZiZjZ_iZ_j untuk membina HCH_C (lihat Latar belakang untuk terbitannya).

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

Bina litar ansatz QAOA

Gunakan QAOAAnsatz untuk membina Circuit QAOA terparameter daripada Hamiltonian kos. 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 of the previous code cell

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

Langkah 2: Optimumkan masalah untuk pelaksanaan perkakasan kuantum

Transpilkan Circuit abstrak kepada arahan natif perkakasan. Langkah ini mengendalikan pemetaan qubit, penguraian gate, penghalaan, dan penindasan ralat. Lihat dokumentasi transpilasi untuk maklumat 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 of the previous code cell

Langkah 3: Laksanakan menggunakan primitif Qiskit

Gelung pengoptimuman QAOA berjalan dalam session Qiskit Runtime untuk mengekalkan peranti ditempah merentas iterasi. Sebuah Estimator menilai HC\langle H_C \rangle pada setiap langkah, dan pengoptimum klasik (COBYLA) mengemas kini parameter sehingga penumpuan.

Ilustrasi menunjukkan tingkah laku mod jalan Kerja tunggal, Kelompok, dan Sesi. Takrifkan parameter awal dan jalankan gelung pengoptimuman:

# 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

Pengoptimum berjaya mengurangkan kos dan mencari parameter yang lebih baik untuk Circuit.

Lengkung yang menurun secara lancar dan mendatar ialah tanda penumpuan. Lengkung yang berbunyi dan tidak monoton biasanya menunjukkan sesuatu di hulu memerlukan perhatian; punca lazim ialah terlalu sedikit shot setiap penilaian (varians estimator tinggi), parameter awal yang lemah, atau Circuit yang kedalamannya dikuasai oleh hingar perkakasan. COBYLA ialah kaedah bebas terbitan dan agak teguh terhadap hingar sederhana, tetapi apabila hingar menenggelamkan peningkatan kos sebenar setiap langkah, model hampiran linearnya tidak lagi dapat membezakan penurunan sebenar daripada getaran rawak dan pengoptimum terkial-kial.

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

Output of the previous code cell

Tetapkan parameter yang dioptimumkan dan ambil sampel taburan akhir menggunakan primitif Sampler.

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

Output of the previous code cell

# 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 keputusan dalam format klasik yang dikehendaki

Ekstrak rentetan bit yang paling berkemungkinan daripada taburan yang disampel. Ini mewakili potongan terbaik yang ditemui 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 of the previous code cell

Visualkan potongan terbaik

Daripada rentetan bit optimum, anda kemudian boleh memvisualkan potongan ini pada graf asal.

# 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 of the previous code cell

Sekarang, kira nilai potongan tersebut:

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 sebenar mudah dicari secara kekerasan, jadi anda boleh menyemak semula keputusan dengan membandingkan keputusan QAOA terhadap jawapan tepat.

# 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 perkakasan skala besar

Anda mempunyai akses kepada banyak peranti dengan lebih daripada 100 qubit pada IBM Quantum Platform. Pilih salah satu untuk menyelesaikan max-cut pada graf berwajaran 100 nod. Ini ialah masalah "skala utiliti". Aliran kerja mengikut langkah yang sama seperti di atas, digunakan pada graf yang jauh lebih besar.

Aliran kerja hujung-ke-hujung pada skala utiliti

Keempat-empat langkah ditunjukkan di bawah, digunakan pada graf 100 nod. Strukturnya sama seperti penelusuran skala kecil: petakan, transpilkan, laksanakan, pasca-proses — tetapi dengan masalah yang lebih besar dan dipecahkan kepada empat sel di bawah untuk kejelasan.

# 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: Bina graf, Hamiltonian kos, 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: Transpilkan untuk Backend perkakasan 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 gelung pengoptimuman QAOA dalam sesi, kemudian ambil sampel.

# 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 taburan yang disampel 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

Semak bahawa kos yang diminimumkan dalam gelung pengoptimuman telah menumpu, dan visualkan keputusan.

# 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 of the previous code cell

Output of the previous code cell

Output of the previous code cell

Langkah seterusnya

Jika anda mendapati kerja ini menarik, anda mungkin berminat dengan bahan berikut:

Cadangan