خوارزمية التحسين التقريبي الكمومي
تقدير الاستخدام: 22 دقيقة على معالج Heron r3 (ملاحظة: هذا تقدير فحسب. قد يختلف وقت التشغيل الفعلي.)
نواتج التعلم
بعد إكمال هذا البرنامج التعليمي، يمكنك أن تتوقع فهم المعلومات التالية:
- كيفية تحويل مسألة تحسين تركيبي كلاسيكية (القطع الأقصى) إلى هاميلتوني كمومي
- كيفية تنفيذ وتشغيل خوارزمية التحسين التقريبي الكمومي (QAOA) باستخدام جلسات Qiskit Runtime
- كيفية توسيع سير عمل QAOA من مثال صغير على المحاكي إلى تنفيذ على نطاق الاستخدام العملي على الأجهزة
المتطلبات المسبقة
يُنصح بأن تطّلع على هذه الموضوعات:
- أساسيات الدوائر الكمومية
- الخوارزميات التغايرية
- QAOA بعمق — لمعالجة شاملة لخوارزمية QAOA وتطبيقها على نطاق الاستخدام العملي
الخلفية النظرية
خوارزمية التحسين التقريبي الكمومي (QAOA) هي طريقة تكرارية هجينة (كمومية-كلاسيكية) لحل مسائل التحسين التركيبي. في هذا البرنامج التعليمي، ستستخدم QAOA لحل مسألة القطع الأقصى (max-cut) — وهي مسألة تحسين من نوع NP-hard لها تطبيقات في التجميع وعلم الشبكات والفيزياء الإحصائية. بإعطاء رسم بياني من عقد مرتبطة بحواف، الهدف هو تقسيم العقد إلى مجموعتين بحيث يكون عدد الحواف التي يقطعها التقسيم أقصى ما يمكن.

من التحسين الكلاسيكي إلى الدوائر الكمومية
يمكن التعبير عن القطع الأقصى كمسألة تحسين ثنائي كلاسيكية. تُخصَّص لكل عقدة متغيرة ثنائية تشير إلى المجموعة التي تنتمي إليها. الهدف هو تعظيم عدد الحواف التي تكون نقاطها الطرفية في مجموعتين مختلفتين:
وهذا يكافئ مسألة التحسين الثنائي التربيعي غير المقيّد (QUBO) من الشكل . عبر استبدال متغيرات قياسي ()، يمكن إعادة كتابة QUBO على شكل هاميلتوني التكلفة الذي تشفّر حالته الأساسية الحل الأمثل. بصفة عامة، يحتوي هذا الهاميلتوني على حدود تربيعية وخطية معاً:
بالنسبة لمسألة القطع الأقصى غير الموزونة المطروحة هنا، تتلاشى المعاملات الخطية () ويكون لكل حافة، مما يترك الشكل الأبسط الذي ستبنيه في الكود أدناه. أما الشكل الأعم أعلاه فهو ما ستحتاج إليه لتكييف سير العمل هذا مع الرسوم البيانية الموزونة أو غيرها من المسائل القابلة للتعبير عنها بصيغة QUBO.
كيف تعمل QAOA
تُعدّ QAOA حلولاً مرشحة بتطبيق طبقات متناوبة من عاملين على حالة تراكب أولية : عامل التكلفة وعامل الخلط . تُحسَّن الزوايا و في حلقة تغذية راجعة كلاسيكية؛ يقيّم الحاسوب الكمومي دالة التكلفة، ويحدّث محسّن كلاسيكي المعاملات حتى التقارب. تُشغَّل هذه الحلقة التكرارية ضمن جلسة (Session) Qiskit Runtime، التي تُبقي الجهاز الكمومي محجوزاً عبر التكرارات لتقليل زمن الاستجابة.
لمعالجة أعمق لنظرية QAOA، بما في ذلك الاشتقاق الكامل من QUBO إلى الهاميلتوني، راجع وحدة دورة QAOA.
في هذا البرنامج التعليمي ستحل أولاً مسألة القطع الأقصى لرسم بياني صغير من خمس عقد، ثم ستوسّع سير العمل ذاته إلى مسألة على نطاق الاستخدام العملي بـ 100 عقدة على أجهزة حقيقية. ملاحظة حول صلاحية الوصول للخطة: يستخدم هذا البرنامج التعليمي جلسات Qiskit Runtime، المتاحة فقط ضمن الخطة المميزة. إذا كنت على الخطة المفتوحة، فلا يمكنك تشغيل هذا البرنامج التعليمي كما هو مكتوب؛ بدلاً من ذلك، ستحتاج إلى استبدال Session بـ وضع المهام (أي تقديم كل تكرار كمهمة مستقلة بدلاً من تغليف حلقة التحسين بـ Session(...)). يظل سير العمل قابلاً للتشغيل، لكن كل تكرار يتكبّد زمن انتظار الطابور كاملاً بدلاً من إعادة استخدام جهاز محجوز. راجع نظرة عامة على الخطط المتاحة لمزيد من المعلومات.
المتطلبات
قبل البدء في هذا البرنامج التعليمي، تأكد من تثبيت ما يلي:
- Qiskit SDK الإصدار v2.0 أو أحدث، مع دعم التصور المرئي
- Qiskit Runtime الإصدار v0.22 أو أحدث (
pip install qiskit-ibm-runtime)
بالإضافة إلى ذلك، ستحتاج إلى صلاحية الوصول إلى نسخة على IBM Quantum® Platform.
الإعداد
# 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
مثال على نطاق صغير
يستعرض هذا القسم كل خطوة من سير عمل QAOA على نسخة صغيرة من مسألة القطع الأقصى بخمس عقد. ورغم وصفه بأنه "صغير النطاق"، فإن هذا المثال يظل يُنفَّذ على أجهزة IBM Quantum الحقيقية — إذ يختار الكود معالجاً خلفياً Backend بـ 127 كيوبت أو أكثر وينفّذ الدائرة عليه. هيّئ مسألتك بإنشاء رسم بياني بـ عقد.
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)
الخطوة 1: تحويل المدخلات الكلاسيكية إلى مسألة كمومية
حوّل الرسم البياني الكلاسيكي إلى دوائر (Circuits) وعوامل كمومية. كما هو موضح في الخلفية النظرية، بالنسبة للقطع الأقصى غير الموزون يُختزَل هاميلتوني التكلفة إلى ، وتستخدم QAOA دائرة Ansatz ذات معاملات لتهيئة الحالات الأساسية المرشحة لـ .
بناء هاميلتوني التكلفة
حوّل حواف الرسم البياني إلى حدود Pauli من النوع لإنشاء (راجع الخلفية النظرية للاطلاع على الاشتقاق).
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
استخدم QAOAAnsatz لإنشاء دائرة QAOA ذات المعاملات من هاميلتوني التكلفة. هنا نستخدم reps=2 (طبقتا QAOA، أربعة معاملات: ).
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])])
الخطوة 2: تحسين المسألة لتنفيذها على الأجهزة الكمومية
حوّل برمجياً الدائرة المجردة إلى تعليمات أصيلة للأجهزة. تتولى هذه الخطوة تعيين الكيوبتات، وفك تسلسل البوابات، والتوجيه، وقمع الأخطاء. راجع توثيق التحويل البرمجي لمزيد من المعلومات.
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')>

الخطوة 3: التنفيذ باستخدام عوامل Qiskit الأولية
تُشغَّل حلقة تحسين QAOA داخل جلسة (Session) Qiskit Runtime لإبقاء الجهاز محجوزاً عبر التكرارات. يقيّم Estimator القيمة في كل خطوة، ويحدّث محسّن كلاسيكي (COBYLA) المعاملات حتى التقارب.
حدّد المعاملات الأولية وشغّل حلقة التحسين:
# 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
تمكّن المحسّن من تقليل التكلفة والعثور على معاملات أفضل للدائرة.
المنحنى المتناقص بسلاسة الذي يستقر عند مستوى ثابت هو علامة التقارب. أما المنحنى المتشوش غير الرتيب فيشير عادةً إلى وجود مشكلة في إحدى الخطوات السابقة تستدعي الانتباه؛ ومن الأسباب الشائعة: قلة عدد اللقطات (shots) لكل تقييم (تباين عالٍ في Estimator)، أو معاملات أولية رديئة، أو دائرة يهيمن على عمقها ضوضاء الأجهزة. خوارزمية COBYLA لا تعتمد على المشتقات وهي متينة إلى حدٍّ ما تجاه الضوضاء المعتدلة، لكن حين تطغى الضوضاء على التحسينات الفعلية في التكلفة بكل خطوة، يعجز نموذجها التقريبي الخطي عن تمييز الانحدار الحقيقي عن التذبذب العشوائي فيبدأ المحسّن في التشتّت.
plt.figure(figsize=(12, 6))
plt.plot(objective_func_vals)
plt.xlabel("Iteration")
plt.ylabel("Cost")
plt.show()
عيّن المعاملات المُحسَّنة وخذ عينات من التوزيع النهائي باستخدام عامل 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}
الخطوة 4: ما بعد المعالجة وإعادة النتيجة بالصيغة الكلاسيكية المطلوبة
استخرج السلسلة البتية الأكثر احتمالاً من التوزيع المأخوذ عينته. وهي تمثل أفضل قطع وجدته 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()
تصوّر أفضل قطع
انطلاقاً من السلسلة البتية المثلى، يمكنك بعد ذلك تصوّر هذا القطع على الرسم البياني الأصلي.
# 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)
الآن، احسب قيمة القطع:
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
بالنسبة لرسم بياني بهذا الصغر، يسهل إيجاد الأمثل الحقيقي بالقوة الغاشمة، فيمكنك التحقق المزدوج من النتائج بمقارنة نتيجة QAOA مع الإجابة الدقيقة.
# 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
مثال على نطاق كبير على الأجهزة
لديك صلاحية الوصول إلى العديد من الأجهزة التي تتجاوز 100 كيوبت على IBM Quantum Platform. اختر أحدها لحل القطع الأقصى لرسم بياني موزون بـ 100 عقدة. هذه مسألة على "نطاق الاستخدام العملي". يتبع سير العمل الخطوات ذاتها أعلاه، مطبَّقة على رسم بياني أكبر بكثير.
سير عمل شامل على نطاق الاستخدام العملي
تُعرَض جميع الخطوات الأربع أدناه، مطبَّقة على الرسم البياني بـ 100 عقدة. البنية هي ذاتها التي في الاستعراض الصغير النطاق: التحويل، التحويل البرمجي، التنفيذ، ما بعد المعالجة — لكن مع مسألة أكبر ومقسَّمة على الخلايا الأربع أدناه للوضوح.
# 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
الخطوة 1: ابنِ الرسم البياني وهاميلتوني التكلفة ودائرة 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()
الخطوة 2: حوّل برمجياً للمعالج الخلفي Backend المختار.
# Step 2: transpile for hardware.
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
candidate_circuit_100 = pm.run(circuit_100)
الخطوة 3: شغّل حلقة تحسين QAOA داخل جلسة، ثم خذ العينات.
# 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
الخطوة 4: عالِج التوزيع المأخوذ عينته لاستخراج أفضل قطع.
# 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
تحقق من أن التكلفة المُقلَّلة في حلقة التحسين قد تقاربت، وصوّر النتائج.
# 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)

الخطوات التالية
إذا وجدت هذا العمل مثيراً للاهتمام، فقد تهتم بالمواد التالية:
- تقنيات متقدمة لـ QAOA — يستكشف استراتيجيات متقدمة لتحسين أداء QAOA
- تحدي التحسين متعدد الأهداف — اختبر مهاراتك مع هذا التحدي المجتمعي حول التحسين الكمومي متعدد الأهداف
- توثيق التحويل البرمجي لضبط تحسين الدائرة بدقة
- قمع الأخطاء والتخفيف منها لتحسين نتائج الأجهزة