مقارنة إعدادات المُحوِّل
Package versions
The code on this page was developed using the following requirements. We recommend using these versions or newer.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
توفر إعدادات المُحوِّل المختلفة أنواعًا مختلفة من التحسين للدائرة، وغالبًا على حساب وقت معالجة كلاسيكي أطول. يستعرض هذا الدليل العملية الكاملة لإنشاء الدوائر وتحويلها وإرسالها لتوضيح كيفية اختبار أداء الإعدادات المتنوعة.
لاحظ أن الإعداد نفسه قد يُحسِّن نتائج دائرة معينة بينما يُعيق دائرة أخرى. تأكد من فحص الدوائر المُحوَّلة الناتجة قبل التشغيل على العتاد الفعلي.
الإعداد وإنشاء دائرة نموذجية
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import grover_operator, DiagonalGate
# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
from qiskit.transpiler import PassManager
from qiskit.circuit.library import XGate
from qiskit.quantum_info import hellinger_fidelity
أنشئ دائرة صغيرة ليجرّب عليها المُحوِّل التحسين. يُنشئ هذا المثال دائرة تنفّذ خوارزمية Grover مع أوراكل يُعلِّم الحالة 111. بعد ذلك، قم بمحاكاة التوزيع المثالي (ما تتوقع قياسه لو شغّلت هذا على حاسوب كمومي مثالي عددًا لانهائيًا من المرات) للمقارنة لاحقًا.
oracle = DiagonalGate([1] * 7 + [-1])
qc = QuantumCircuit(3)
qc.h([0, 1, 2])
qc = qc.compose(grover_operator(oracle))
qc.draw(output="mpl", style="iqp")
ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()
plot_histogram(ideal_distribution)
تحويل الدوائر
بعد ذلك، حوِّل الدوائر لوحدة المعالجة الكمومية. ستقارن أداء المُحوِّل مع optimization_level مضبوطًا على 0 (الأدنى) مقابل 3 (الأعلى). يقوم مستوى التحسين الأدنى بالحد الأدنى اللازم لتشغيل الدائرة على الجهاز؛ إذ يُعيِّن Qubits الدائرة إلى Qubits الجهاز ويضيف بوابات التبديل للسماح بجميع عمليات الـ Qubitين. أما مستوى التحسين الأعلى فهو أكثر ذكاءً بكثير ويستخدم الكثير من الحيل لتقليل عدد البوابات الإجمالي. بما أن بوابات متعددة الـ Qubit لها معدلات خطأ مرتفعة وتتفكك Qubits بمرور الوقت، فإن الدوائر الأقصر ينبغي أن تعطي نتائج أفضل.
يستخدم هذا المثال عتاد IBM Quantum®، لكن يمكنك تجربته على أي وحدة معالجة كمومية متوافقة مع Qiskit. ق د تختلف نتائجك.
تقوم الخلية التالية بتحويل qc لكلتا قيمتَي optimization_level، وتطبع عدد بوابات الـ Qubitين، وتضيف الدوائر المُحوَّلة إلى قائمة. بعض خوارزميات المُحوِّل عشوائية، لذا يُحدَّد seed لضمان إمكانية التكرار.
# Use Qiskit Runtime to run jobs on hardware
from qiskit_ibm_runtime import (
QiskitRuntimeService,
SamplerV2 as Sampler,
)
# Select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
backend.name
'ibm_marrakesh'
# Need to add measurements to the circuit
qc.measure_all()
# Find the correct two-qubit gate
twoQ_gates = set(["ecr", "cz", "cx"])
for gate in backend.basis_gates:
if gate in twoQ_gates:
twoQ_gate = gate
circuits = []
for optimization_level in [0, 3]:
pm = generate_preset_pass_manager(
optimization_level, backend=backend, seed_transpiler=0
)
t_qc = pm.run(qc)
print(
f"Two-qubit gates (optimization_level={optimization_level}): ",
t_qc.count_ops()[twoQ_gate],
)
circuits.append(t_qc)
Two-qubit gates (optimization_level=0): 21
Two-qubit gates (optimization_level=3): 12
بما أن بوابات CNOT عادةً ما تمتلك معدل خطأ مرتفعًا، فإن الدائرة المُحوَّلة بـ optimization_level=3 ينبغي أن تُحقق أداءً أفضل بكثير.
هناك طريقة أخرى يمكنك من خلالها تحسين الأداء وهي الفصل الديناميكي، من خلال تطبيق سلسلة من البوابات على Qubits الخاملة. يؤدي ذلك إلى إلغاء بعض التفاعلات غير المرغوب فيها مع البيئة. تضيف الخلية التالية الفصل الديناميكي إلى الدائرة المُحوَّلة بـ optimization_level=3 وتضيفها إلى القائمة.
from qiskit_ibm_runtime.transpiler.passes.scheduling import (
ASAPScheduleAnalysis,
PadDynamicalDecoupling,
)
# Get gate durations so the transpiler knows how long each operation takes
durations = backend.target.durations()
# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]
# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager(
[
ASAPScheduleAnalysis(durations),
PadDynamicalDecoupling(durations, dd_sequence),
]
)
circ_dd = pm.run(circuits[1])
# Add this new circuit to our list
circuits.append(circ_dd)
circ_dd.draw(output="mpl", style="iqp", idle_wires=False)
تنفيذ الدائرة
في هذه المرحلة، لديك قائمة من الدوائر المُحوَّلة بإعدادات مختلفة. بعد ذلك، شغِّل هذه الدوائر باستخدام البدائي Sampler وخزِّن النتائج في result.
sampler = Sampler(backend)
job = sampler.run(
[(circuit) for circuit in circuits], # sample all three circuits
shots=8000,
)
result = job.result()
عرض النتائج
أخيرًا، ارسم النتائج من عمليات تشغيل الجهاز مقابل التوزيع المثالي. يمكنك أن ترى أن نتائج optimization_level=3 أقرب إلى التوزيع المثالي بسبب عدد البوابات الأقل، وأن optimization_level=3 + dd أقرب حتى من ذلك بفضل الفصل الديناميكي.
binary_prob = [
{
k: v / res.data.meas.num_shots
for k, v in res.data.meas.get_counts().items()
}
for res in result
]
plot_histogram(
binary_prob + [ideal_distribution],
bar_labels=False,
legend=[
"optimization_level=0",
"optimization_level=3",
"optimization_level=3 + dd",
"ideal distribution",
],
)
يمكنك التحقق من ذلك بحساب دقة Hellinger بين كل مجموعة نتائج والتوزيع المثالي (القيمة الأعلى أفضل، والقيمة 1 تعني دقة مثالية).
for prob in binary_prob:
print(f"{hellinger_fidelity(prob, ideal_distribution):.3f}")
0.985
0.989
0.988
الخطوات التالية
-
استكشف بعض موارد التحويل المتقدمة، مثل:
-
تصفّح البرامج التعليمية المتاحة.