انتقل إلى المحتوى الرئيسي
هذا المشروع في مرحلة تجريبية. وجدت خطأ؟ افتح مشكلة على GitHub. لديك سؤال أو فكرة؟ اسأل في Discussions — نحب سماع ملاحظاتك!
كيف جودة الترجمة في هذه الصفحة؟

محلّل التحسين: دالة Qiskit من Q-CTRL Fire Opal

راجع مرجع API

ملاحظة

Qiskit Functions ميزة تجريبية متاحة فقط لمستخدمي IBM Quantum® Premium Plan وFlex Plan وOn-Prem (عبر IBM Quantum Platform API) Plan. هي في مرحلة إصدار معاينة وقابلة للتغيير.

إصدارات الحزم

تم تطوير الكود في هذه الصفحة باستخدام المتطلبات التالية. نوصي باستخدام هذه الإصدارات أو أحدث.

qiskit-ibm-runtime~=0.47.0
sympy~=1.14.0

نظرة عامة​

مع محلّل التحسين Fire Opal، يمكنك حلّ مسائل التحسين على النطاق الاستخدامي على الأجهزة الكمومية دون الحاجة إلى خبرة كمومية. ما عليك سوى إدخال تعريف المسألة رفيع المستوى، ويتولى المحلّل الباقي. سير العمل بالكامل يدرك الضجيج ويستفيد من إدارة أداء Fire Opal في الخلفية. يقدّم المحلّل باستمرار حلولاً دقيقة للمسائل الصعبة كلاسيكيًا، حتى على نطاق الجهاز الكامل لأكبر أجهزة IBM® QPU.

المحلّل مرن ويمكن استخدامه لحلّ مسائل التحسين التوافقية المعرَّفة كدوال هدف أو رسوم بيانية اعتباطية. لا يجب ربط المسائل بطبولوجيا الجهاز. المسائل غير المقيدة والمقيدة قابلة للحلّ، مع فرض القيود كقيود Hamming-weight-1 صعبة بدلًا من حدود العقوبة. الأمثلة المضمّنة في هذا الدليل توضّح كيفية حلّ مسألة تحسين غير مقيدة ومسألة مقيدة على النطاق الاستخدامي باستخدام أنواع مدخلات مختلفة للمحلّل. المثال الأول يتضمن مسألة max-cut معرَّفة على رسم بياني منتظم ثلاثي 156 عقدة، بينما يعالج المثال الثاني مسألة graph partitioning من 50 عقدة معرَّفة بدالة تكلفة.

للحصول على إمكانية الوصول إلى محلّل التحسين، تواصل مع Q-CTRL.

وصف الدالة​

يحسّن المحلّل الخوارزمية بالكامل ويؤتمتها، من قمع الأخطاء على مستوى الأجهزة إلى تعيين المسائل بكفاءة والتحسين الكلاسيكي في حلقة مغلقة. خلف الكواليس، تقلّل خطوط أنابيب المحلّل الأخطاء في كل مرحلة، مما يُمكّن الأداء المحسّن المطلوب للتوسع الفعلي. سير العمل الأساسي مستوحى من خوارزمية التحسين الكمومي التقريبي (QAOA)، وهي خوارزمية هجينة كمومية-كلاسيكية. للاطلاع على ملخص تفصيلي لسير عمل محلّل التحسين الكامل، راجع المخطوطة المنشورة.

تصوّر لسير عمل محلّل التحسين

لحلّ مسألة عامة مع محلّل التحسين:

  1. عرِّف مسألتك كدالة هدف، أو رسم بياني، أو سلسلة دوران SparsePauliOp.
  2. اتصل بالدالة عبر كتالوج Qiskit Functions.
  3. شغّل المسألة مع المحلّل واسترجع النتائج.

صيغ المسائل المقبولة​

  • تمثيل التعبير متعدد الحدود لدالة هدف. يُنصح بإنشائه في Python بكائن SymPy Poly موجود وتنسيقه كسلسلة نصية باستخدام sympy.srepr.

  • تمثيل رسم بياني لنوع مسألة محدد. يجب إنشاء الرسم البياني باستخدام مكتبة networkx في Python. ثم يجب تحويله إلى سلسلة نصية باستخدام دالة networkx nx.readwrite.json_graph.adjacency_data.

  • تمثيل سلسلة الدوران لمسألة محددة. يجب تمثيل سلسلة الدوران ككائن SparsePauliOp؛ راجع التوثيق لمزيد من التفاصيل.

هل تدعم هذه الدالة جميع خلفيات IBM؟

إذا أردت استخدام خلفية لا تدعمها هذه الدالة حاليًا، تواصل مع Q-CTRL لإضافة الدعم.

المعايير المرجعية​

إخلاء مسؤولية

قد يعتمد الأداء على كلٍّ من مثيل المسألة المحدد وخطوات المعالجة اللاحقة. في بعض الحالات، قد تحقق العينات الكلاسيكية والعينات المولَّدة كموميًا جودة حل نهائية متشابهة بعد معالجة لاحقة مكافئة. لذلك ينبغي أن يراعي التقييم سير عمل التحسين الكامل.

تُظهر نتائج المعايير المرجعية المنشورة أن المحلّل ينجح في حلّ مسائل تتجاوز 120 كيوبت، متفوقًا حتى على النتائج المنشورة سابقًا على أجهزة التليين الكمومي والأيونات المحاصرة. مقاييس المعايير المرجعية التالية تعطي مؤشرًا تقريبيًا على دقة أنواع المسائل وقابليتها للتوسع بناءً على بعض الأمثلة. قد تختلف المقاييس الفعلية بناءً على خصائص المسألة المختلفة، مثل عدد الحدود في دالة الهدف (الكثافة) وموقعها، وعدد المتغيرات، والرتبة متعددة الحدود.

"عدد الكيوبتات" المشار إليه ليس حدًا صارمًا بل يمثّل عتبات تقريبية حيث يمكن توقع دقة حلول عالية الاتساق. مسائل أكبر حجمًا تمّ حلّها بنجاح، ويُشجّع الاختبار خارج هذه الحدود.

الاتصالية الاعتباطية بين الكيوبتات مدعومة عبر جميع أنواع المسائل.

نوع المسألةعدد الكيوبتاتمثالالدقةالوقت الكلي (ث)استخدام وقت التشغيل (ث)عدد التكرارات
مسائل تربيعية متفرقة الاتصال156max-cut ثلاثي منتظم100%176429316
تحسين ثنائي ذو رتبة عليا156نموذج Ising spin-glass100%146127216
مسائل تربيعية كثيفة الاتصال50max-cut متكامل الاتصال100%175826812
مسألة مقيدة بقيود صعبة50graph partitioning موزون بكثافة حواف 8%100%107421510

البدء​

أولًا، سجّل الدخول باستخدام مفتاح IBM Quantum API. ثم اختر دالة Qiskit كما يلي. (هذا المقتطف يفترض أنك حفظت حسابك في بيئتك المحلية مسبقًا.)

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"networkx": "networkx", "numpy": "numpy", "qiskit_ibm_catalog": "qiskit-ibm-catalog", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "sympy": "sympy"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Verify that you have access to the function
catalog.list()
[QiskitFunction(qunova/hivqe-chemistry),
QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
QiskitFunction(algorithmiq/tem),
QiskitFunction(qedma/qesem),
QiskitFunction(multiverse/singularity),
QiskitFunction(ibm/circuit-function),
QiskitFunction(q-ctrl/optimization-solver),
QiskitFunction(colibritd/quick-pde),
QiskitFunction(q-ctrl/performance-management),
QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# Access Function
solver = catalog.load("q-ctrl/optimization-solver")

مثال: تحسين غير مقيد​

شغّل مسألة القطع الأقصى (Max-Cut). يوضّح المثال التالي قدرات المحلّل على مسألة Max-Cut لرسم بياني غير موزون منتظم ثلاثي من 156 عقدة، لكن يمكنك أيضًا حلّ مسائل الرسم البياني الموزون. بالإضافة إلى qiskit-ibm-catalog، ستحتاج أيضًا إلى الحزم التالية لتشغيل هذا المثال: networkx وnumpy. يمكنك تثبيت هذه الحزم بإلغاء تعليق الخلية التالية إذا كنت تشغّل هذا المثال في notebook باستخدام نواة IPython.

# %pip install networkx numpy

1. تعريف المسألة​

يمكنك تشغيل مسألة Max-Cut بتعريف مسألة رسم بياني وتحديد problem_type='maxcut'.

import networkx as nx
import numpy as np

# Generate a random graph with 156 nodes
maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)
# Optionally, visualize the graph
nx.draw_networkx(
maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100
)

Output of the previous code cell

يقبل المحلّل سلسلة نصية كمدخل لتعريف المسألة.

# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)

2. تشغيل المسألة​

عند استخدام طريقة الإدخال القائمة على الرسم البياني، حدّد نوع المسألة.

# This cell is hidden from users
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend_name = service.least_busy(n_qubits=156).name
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)

تحقق من حالة حمل عمل دالة Qiskit أو استرجع النتائج كما يلي:

# Print the ID so you can use it later, if necessary
print(maxcut_job.job_id)

# Get job status
print(maxcut_job.status())
34b53970-d95a-4e24-8763-fc6f3d112843
QUEUED

3. استرجاع النتيجة​

استرجع قيمة القطع المثلى من قاموس النتائج.

ملاحظة

ربما تغيّر تعيين المتغيرات إلى السلسلة الثنائية. يحتوي قاموس الإخراج على قاموس فرعي variables_to_bitstring_index_map يساعد في التحقق من الترتيب.

# Poll for results
maxcut_result = maxcut_job.result()

# Take the absolute value of the solution since the cost function is minimized
qctrl_maxcut = abs(maxcut_result["solution_bitstring_cost"])

# Print the optimal cut value found by the Optimization Solver
print(f"Optimal cut value: {qctrl_maxcut}")
Optimal cut value: 210.0

يمكنك التحقق من دقة النتيجة بحلّ المسألة كلاسيكيًا باستخدام محلّلات مفتوحة المصدر مثل PuLP إذا لم يكن الرسم البياني كثيف الاتصال. قد تتطلب مسائل الكثافة العالية محلّلات كلاسيكية متقدمة للتحقق من صحة الحلّ.

مثال: تحسين مقيد​

المثال السابق للقطع الأقصى (max-cut) هو مسألة شائعة من مسائل التحسين الثنائي غير المقيد التربيعي (quadratic unconstrained binary optimization). يمكن لحلّال التحسين الخاص بـ Q-CTRL أيضًا حل مسائل التحسين المقيدة بتمرير قيود صارمة (hard constraints) مباشرة إلى الحلّال (Solver) من خلال مُدخل constraint، بدلاً من ترميزها كحدود عقوبة (penalty terms) في دالة الهدف. يدعم الحلّال حاليًا قيود وزن هامينغ-1 (Hamming-weight-1): يحدد كل قيد مجموعة من المتغيرات حيث يجب أن يساوي متغير واحد بالضبط 1 والبقية يجب أن تساوي 0.

يوضّح المثال التالي كيفية بناء دالة تكلفة ومجموعة من القيود الصارمة لمسألة تحسين مقيدة، تقسيم الرسم البياني (graph partitioning)، بتعيين كل عقدة في رسم بياني إلى واحدة بالضبط من عدة مجموعات مع تقليل الوزن الإجمالي للحواف التي تقع نقاط نهايتها في نفس المجموعة. بالإضافة إلى حزمتي qiskit-ibm-catalog وqiskit، ستحتاج أيضًا إلى الحزم التالية لتشغيل هذا المثال: numpy وnetworkx وsympy. يمكنك تثبيت هذه الحزم بإلغاء تعليق الخلية التالية إذا كنت تشغّل هذا المثال في notebook باستخدام نواة IPython.

# %pip install numpy networkx sympy

1. تعريف المسألة​

عرِّف مسألة تقسيم رسم بياني عشوائية بتوليد رسم بياني بعقد موزونة عشوائيًا.

import networkx as nx
from sympy import Symbol, Poly, srepr

# To change the weights, change the seed to any integer.
rng_seed = 18
_rng = np.random.default_rng(rng_seed)
node_count = 50
edge_probability = 0.08
graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)

# add node weights
min_weight = -1.0
max_weight = 1.0
for i in graph.nodes:
weight = (max_weight - min_weight) * _rng.random() + min_weight
graph.add_node(i, weight=weight)

# Optionally, visualize the graph
nx.draw_networkx(graph, nx.kamada_kawai_layout(graph), node_size=200)

Output of the previous code cell

يمكن صياغة نموذج تحسين قياسي لتقسيم رسم بياني موزون كما يلي. قسّم عقد الرسم البياني إلى ثلاث مجموعات g∈{0,1,2}g \in \{0, 1, 2\}، وليكن ni,g=1n_{i,g} = 1 إذا كانت العقدة ii مُعيَّنة إلى المجموعة gg، وni,g=0n_{i,g} = 0 في الحالات الأخرى. الهدف هو تقليل الوزن الإجمالي للحواف التي تُعيَّن نقاط نهايتها إلى نفس المجموعة، حيث يكون وزن الحافة (i,j)(i,j) هو الوزن المُجمَّع لنقطتي نهايتها، ωi,j=ωi+ωj\omega_{i,j} = \omega_i + \omega_j:

Minimizey=∑(i,j)∈Eωi,j∑gni,g nj,g\textbf{Minimize}\qquad y = \sum_{(i,j)\in E} \omega_{i,j} \sum_{g} n_{i,g}\, n_{j,g}

# Construct the cost function.
group_count = 3
variables = [
Symbol(f"n[{i},{g}]")
for i in range(node_count)
for g in range(group_count)
]
node_group_var = {
(i, g): variables[i * group_count + g]
for i in range(node_count)
for g in range(group_count)
}
cost_function = Poly(0, *variables)

for i, j in graph.edges():
edge_weight = graph.nodes[i]["weight"] + graph.nodes[j]["weight"]
for g in range(group_count):
cost_function += (
edge_weight * node_group_var[(i, g)] * node_group_var[(j, g)]
)

يجب تعيين كل عقدة إلى واحدة بالضبط من المجموعات الثلاث. هذا قيد وزن هامينغ-1: لكل عقدة ii، يجب أن يساوي واحد بالضبط من ni,0,ni,1,ni,2n_{i,0}, n_{i,1}, n_{i,2} القيمة 1، والبقية يجب أن تساوي 0:

ni,0+ni,1+ni,2=1 for all i∈Vn_{i,0} + n_{i,1} + n_{i,2} = 1 \texttt{ for all } i \in V

بدلاً من ترميز هذا الشرط كحد عقوبة في دالة التكلفة، مرّره مباشرة إلى الحلّال (Solver) كقيد صارم باستخدام مُدخل constraint.

# Build the hard constraint: exactly one group per node.
constraint_dict = {
str(tuple(f"n[{i},{g}]" for g in range(group_count))): 1
for i in range(node_count)
}
print(f"Problem constraints: {constraint_dict}")
Problem constraints: {"('n[0,0]', 'n[0,1]', 'n[0,2]')": 1, "('n[1,0]', 'n[1,1]', 'n[1,2]')": 1, "('n[2,0]', 'n[2,1]', 'n[2,2]')": 1, "('n[3,0]', 'n[3,1]', 'n[3,2]')": 1, "('n[4,0]', 'n[4,1]', 'n[4,2]')": 1, "('n[5,0]', 'n[5,1]', 'n[5,2]')": 1, "('n[6,0]', 'n[6,1]', 'n[6,2]')": 1, "('n[7,0]', 'n[7,1]', 'n[7,2]')": 1, "('n[8,0]', 'n[8,1]', 'n[8,2]')": 1, "('n[9,0]', 'n[9,1]', 'n[9,2]')": 1, "('n[10,0]', 'n[10,1]', 'n[10,2]')": 1, "('n[11,0]', 'n[11,1]', 'n[11,2]')": 1, "('n[12,0]', 'n[12,1]', 'n[12,2]')": 1, "('n[13,0]', 'n[13,1]', 'n[13,2]')": 1, "('n[14,0]', 'n[14,1]', 'n[14,2]')": 1, "('n[15,0]', 'n[15,1]', 'n[15,2]')": 1, "('n[16,0]', 'n[16,1]', 'n[16,2]')": 1, "('n[17,0]', 'n[17,1]', 'n[17,2]')": 1, "('n[18,0]', 'n[18,1]', 'n[18,2]')": 1, "('n[19,0]', 'n[19,1]', 'n[19,2]')": 1, "('n[20,0]', 'n[20,1]', 'n[20,2]')": 1, "('n[21,0]', 'n[21,1]', 'n[21,2]')": 1, "('n[22,0]', 'n[22,1]', 'n[22,2]')": 1, "('n[23,0]', 'n[23,1]', 'n[23,2]')": 1, "('n[24,0]', 'n[24,1]', 'n[24,2]')": 1, "('n[25,0]', 'n[25,1]', 'n[25,2]')": 1, "('n[26,0]', 'n[26,1]', 'n[26,2]')": 1, "('n[27,0]', 'n[27,1]', 'n[27,2]')": 1, "('n[28,0]', 'n[28,1]', 'n[28,2]')": 1, "('n[29,0]', 'n[29,1]', 'n[29,2]')": 1, "('n[30,0]', 'n[30,1]', 'n[30,2]')": 1, "('n[31,0]', 'n[31,1]', 'n[31,2]')": 1, "('n[32,0]', 'n[32,1]', 'n[32,2]')": 1, "('n[33,0]', 'n[33,1]', 'n[33,2]')": 1, "('n[34,0]', 'n[34,1]', 'n[34,2]')": 1, "('n[35,0]', 'n[35,1]', 'n[35,2]')": 1, "('n[36,0]', 'n[36,1]', 'n[36,2]')": 1, "('n[37,0]', 'n[37,1]', 'n[37,2]')": 1, "('n[38,0]', 'n[38,1]', 'n[38,2]')": 1, "('n[39,0]', 'n[39,1]', 'n[39,2]')": 1, "('n[40,0]', 'n[40,1]', 'n[40,2]')": 1, "('n[41,0]', 'n[41,1]', 'n[41,2]')": 1, "('n[42,0]', 'n[42,1]', 'n[42,2]')": 1, "('n[43,0]', 'n[43,1]', 'n[43,2]')": 1, "('n[44,0]', 'n[44,1]', 'n[44,2]')": 1, "('n[45,0]', 'n[45,1]', 'n[45,2]')": 1, "('n[46,0]', 'n[46,1]', 'n[46,2]')": 1, "('n[47,0]', 'n[47,1]', 'n[47,2]')": 1, "('n[48,0]', 'n[48,1]', 'n[48,2]')": 1, "('n[49,0]', 'n[49,1]', 'n[49,2]')": 1}
مسائل مقيدة جزئيًا

لست بحاجة لإضافة كل متغير إلى constraint. أي متغير يُترك خارج القاموس (dictionary) يبقى غير مقيد، لذا يمكنك مزج مجموعات متغيرات ذات قيود صارمة مع متغيرات حرة في نفس المسألة.

2. تشغيل المسألة​

# Solve the problem
partition_job = solver.run(
problem=srepr(cost_function),
constraint=constraint_dict,
backend_name="ibm_marrakesh", # E.g. "ibm_marrakesh"
)

تحقق من حالة حمل عمل دالة Qiskit أو استرجع النتائج كما يلي:

# Print the ID so you can use it later, if necessary
print(partition_job.job_id)

# Get job status
print(partition_job.status())
b8085944-f313-444e-be39-ea61b1b47ebd
QUEUED

3. الحصول على النتيجة​

استرجع الحل وحلّل النتائج. تمثل تكلفة الحل الوزن الإجمالي للحواف التي انتهت نقاط نهايتها في نفس المجموعة، لذا تشير التكلفة الأقل إلى تقسيم أفضل للرسم البياني.

partition_result = partition_job.result()
qctrl_cost = partition_result["solution_bitstring_cost"]
solution_bitstring = partition_result["solution_bitstring"]

# Print results
print(f"Total weight of same-group edges: {qctrl_cost}")
print(f"Solution bitstring: {solution_bitstring}")
Total weight of same-group edges: -36.5539
Solution bitstring: 100100100100100001100100100100100100100100100100100001010100010100100100100010001001100100100001100001100001010001001010100100100100100010100100100100

الدعم​

لأي أسئلة أو مشكلات، تواصل مع Q-CTRL.

سجل التغييرات​

  • 2026-08-10: إضافة دعم للقيود الصارمة (وزن هامينغ 1) عبر مُدخل constraint، وتحديث مثال التحسين المقيد لاستخدامها.

  • 2026-02-11: أصبح لدينا الآن دعم لـibm_miami

الخطوات التالية​