تحسين الدارة الكمومية
Toshinari Itoko (21 يونيو 2024)
تحميل ملف PDF للمحاضرة الأصلية. لاحظ أن بعض مقتطفات الكود قد تصبح متقادمة لأنها صور ثابتة.
الوقت التقريبي لتشغيل هذه التجربة على وحدة المعالجة الكمومية (QPU) هو 15 ثانية.
(ملاحظة: بعض خلايا الجزء الثاني مأخوذة من دفتر الملاحظات "Qiskit Deep dive" الذي كتبه Matthew Treinish، أحد مشرفي Qiskit)
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_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")
# !pip install 'qiskit[visualization]'
# !pip install qiskit_ibm_runtime qiskit_aer
# !pip install jupyter
# !pip install matplotlib pylatexenc pydot pillow
import qiskit
qiskit.__version__
'2.0.2'
import qiskit_ibm_runtime
qiskit_ibm_runtime.__version__
'0.40.1'
import qiskit_aer
qiskit_aer.__version__
'0.17.1'
1. مقدمة
يتناول هذا الدرس عدة جوانب من تحسين الدوائر في الحوسبة الكمومية. تحديدًا، سنرى قيمة تحسين الدوائر باستخدام إعدادات التحسين المدمجة في Qiskit. ثم سنتعمق قليلًا ونرى ما يمكنك فعله بوصفك خبيرًا في مجال تطبيقك لبناء دوائر بطريقة ذكية. وأخيرًا، سنلقي نظرة فاحصة على ما يجري أثناء عملية الترجمة (transpilation) التي تساعدنا على تحسين دوائرنا.
2. أهمية تحسين الدوائر
نبدأ بمقارنة نتائج تشغيل دوائر تحضير حالة GHZ ذات 5 كيوبتات () مع التحسين وبدونه.
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.primitives import BackendSamplerV2 as Sampler
from qiskit_ibm_runtime.fake_provider import FakeBrisbane
backend = FakeBrisbane()
نبدأ باستخدام دائرة GHZ المركّبة بشكل ساذج كما يلي.
num_qubits = 5
ghz_circ = QuantumCircuit(num_qubits)
ghz_circ.h(0)
[ghz_circ.cx(0, i) for i in range(1, num_qubits)]
ghz_circ.measure_all()
ghz_circ.draw("mpl")
2.1 مستوى التحسين
يحتوي optimization_level على أربعة خيارات، من 0 إلى 3. كلما زاد مستوى التحسين، زاد الجهد الحسابي المبذول لتحسين الدارة. المستوى 0 لا يجري أي تحسين ويقوم فقط بالحد الأدنى من العمل لجعل الدارة قابلة للتشغيل على backend المحدد. المستوى 3 يبذل أكبر قدر من الجهد (وعادةً وقت التشغيل) لمحاولة تحسين الدارة. المستوى 1 هو مستوى التحسين الافتراضي.
نُترجم الدارة بدون تحسين (optimization_level=0) ومع تحسين (optimization_level=2).
نرى فرقًا كبيرًا في طول الدارات المُترجَمة.
pm0 = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=777
)
pm2 = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=777
)
circ0 = pm0.run(ghz_circ)
circ2 = pm2.run(ghz_circ)
print("optimization_level=0:")
display(circ0.draw("mpl", idle_wires=False, fold=-1))
print("optimization_level=2:")
display(circ2.draw("mpl", idle_wires=False, fold=-1))
optimization_level=0:
optimization_level=2:
2.2 تمرين
جرّب optimization_level=1 أيضًا وقارن الدائرة الناتجة بالاثنتين أعلاه. جرّب ذلك بتعديل الكود أعلاه.
الحل:
pm1 = generate_preset_pass_manager(
optimization_level=1, backend=backend, seed_transpiler=777
)
circ1 = pm1.run(ghz_circ)
print("optimization_level=1:")
display(circ1.draw("mpl", idle_wires=False, fold=-1))
optimization_level=1:
تشغيل على خلفية وهمية (محاكاة ضوضائية). راجع الملحق 1 لمعرفة كيفية التشغيل على خلفية حقيقية.
# run the circuits on the fake backend (noisy simulator)
sampler = Sampler(backend=backend)
job = sampler.run([circ0, circ2], shots=10000)
print(f"Job ID: {job.job_id()}")
Job ID: 93a4ac70-e3ea-44ad-aea9-5045840c9076
# get results
result = job.result()
unoptimized_result = result[0].data.meas.get_counts()
optimized_result = result[1].data.meas.get_counts()
from qiskit.visualization import plot_histogram
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[result for result in [sim_result, unoptimized_result, optimized_result]],
bar_labels=False,
legend=[
"ideal",
"no optimization",
"with optimization",
],
)
3. أهمية تركيب الدوائر
بعد ذلك نقارن نتائج تشغيل دارتين لتحضير حالة GHZ لخمسة كيوبتات () مُصنَّعتين بطريقتين مختلفتين.
# Original GHZ circuit (naive synthesis)
ghz_circ.draw("mpl")
# A cleverly-synthesized GHZ circuit
ghz_circ2 = QuantumCircuit(5)
ghz_circ2.h(2)
ghz_circ2.cx(2, 1)
ghz_circ2.cx(2, 3)
ghz_circ2.cx(1, 0)
ghz_circ2.cx(3, 4)
ghz_circ2.measure_all()
ghz_circ2.draw("mpl")
# transpile both with the same optimization level 2
circ_org = pm2.run(ghz_circ)
circ_new = pm2.run(ghz_circ2)
print("original synthesis:")
display(circ_org.draw("mpl", idle_wires=False, fold=-1))
print("new synthesis:")
display(circ_new.draw("mpl", idle_wires=False, fold=-1))
original synthesis:
new synthesis:
تنتج عملية التركيب الجديدة دائرة أقل عمقًا. لماذا؟
وذلك لأن الدائرة الجديدة يمكن وضعها على كيوبتات متصلة خطيًا، ومن ثم على الرسم البياني للاقتران السداسي الثقيل الخاص بـ IBM® Brisbane أيضًا، في حين تتطلب الدائرة الأصلية اتصالًا نجميًا (عقدة بدرجة 4) وبالتالي لا يمكن وضعها على الرسم البياني للاقتران السداسي الثقيل، الذي تكون درجة عقده 3 كحد أقصى. ونتيجة لذلك، تتطلب الدائرة الأصلية توجيه الكيوبتات مما يضيف بوابات SWAP، ما يزيد عدد البوابات.
ما فعلناه في الدارة الجديدة يمكن اعتباره تصنيعًا يدويًا للدارة "واعيًا بقيود الاقتران". بعبارة أخرى: حل تصنيع الدارة وتخطيط الدارة يدويًا في الوقت نفسه.
# run the circuits
sampler = Sampler(backend=backend)
job = sampler.run([circ_org, circ_new], shots=10000)
print(f"Job ID: {job.job_id()}")
Job ID: 19d635b0-4d8b-44c2-a76e-49e4b9078b1b
# get results
result = job.result()
synthesis_org_result = result[0].data.meas.get_counts()
synthesis_new_result = result[1].data.meas.get_counts()
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[
result
for result in [
sim_result,
unoptimized_result,
synthesis_org_result,
synthesis_new_result,
]
],
bar_labels=False,
legend=[
"ideal",
"no optimization",
"synthesis_org",
"synthesis_new",
],
)
بشكل عام، يعتمد تصنيع الدارة على التطبيق ومن الصعب جدًا على برنامج أن يغطي جميع التطبيقات الممكنة. يصادف أن مترجم Qiskit ليس لديه وظائف لتصنيع دارة تحضير حالة GHZ. في مثل هذه الحالة، يستحق التصنيع اليدوي للدارة كما هو موضح أعلاه أن يُنظر فيه. في هذا القسم، سننظر بالتفصيل في كيفية عمل مترجم Qiskit باستخدام دارة المثال التوضيحي التالية.
# Build a toy example circuit
from math import pi
import itertools
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import excitation_preserving
circuit = QuantumCircuit(4, name="Example circuit")
circuit.append(excitation_preserving(4, reps=1, flatten=True), range(4))
circuit.measure_all()
value_cycle = itertools.cycle([0, pi / 4, pi / 2, 3 * pi / 4, pi, 2 * pi])
circuit.assign_parameters(
[x[1] for x in zip(range(len(circuit.parameters)), value_cycle)], inplace=True
)
circuit.draw("mpl")
3.1 رسم تدفق الترجمة الكامل في Qiskit
نستعرض مراحل المترجم (المهام) لـ optimization_level=1.
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
# There is no need to read this entire image, but this outputs all the steps in the transpile() call
# for optimization level 1
pm = generate_preset_pass_manager(1, backend, seed_transpiler=42)
pm.draw()

يتكون التدفق من ست مراحل:
print(pm.stages)
('init', 'layout', 'routing', 'translation', 'optimization', 'scheduling')
3.2 رسم مرحلة فردية
أولًا، لنرسم جميع المهام (مراحل المترجم) المنجزة في مرحلة init.
pm.init.draw()
يمكننا تشغيل كل مرحلة بشكل منفصل. لنشغّل مرحلة init لدائرتنا. بتفعيل المسجّل، يمكننا رؤية تفاصيل التشغيل.
import logging
logger = logging.getLogger()
logger.setLevel("INFO")
init_out = pm.init.run(circuit)
init_out.draw("mpl", fold=-1)
INFO:qiskit.passmanager.base_tasks:Pass: UnitarySynthesis - 0.03576 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: HighLevelSynthesis - 0.16618 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BasisTranslator - 0.07176 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: InverseCancellation - 0.27299 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: ContractIdleWiresInControlFlow - 0.00811 (ms)
3.3 تمرين
ارسم مراحل layout وشغّل المرحلة على الدائرة الناتجة من مرحلة init (init_out)، بتعديل الخلايا المستخدمة أعلاه.
الحل:
display(pm.layout.draw())
layout_out = pm.layout.run(init_out)
layout_out.draw("mpl", idle_wires=False, fold=-1)

INFO:qiskit.passmanager.base_tasks:Pass: SetLayout - 0.01001 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: TrivialLayout - 0.07129 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: CheckMap - 0.08917 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: VF2Layout - 1.24431 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BarrierBeforeFinalMeasurements - 0.02599 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: SabreLayout - 5.11169 (ms)
افعل الشيء نفسه لمرحلة translation.
الحل:
display(pm.translation.draw())
basis_out = pm.translation.run(layout_out)
basis_out.draw("mpl", idle_wires=False, fold=-1)
INFO:qiskit.passmanager.base_tasks:Pass: UnitarySynthesis - 0.03386 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: HighLevelSynthesis - 0.02718 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BasisTranslator - 2.64192 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: CheckGateDirection - 0.02217 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: GateDirection - 0.36502 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: BasisTranslator - 0.64778 (ms)
ملاحظة: لا يمكن دائمًا تشغيل كل مرحلة بشكل مستقل (إذ تحتاج بعضها إلى نقل معلومات من المرحلة السابقة).
3.4 مرحلة التحسين
المرحلة الافتراضية الأخيرة في خط الأنابيب هي التحسين. بعد أن أدرجنا الدائرة للهدف، توسعت الدائرة كثيرًا. معظم هذا التوسع يعود إلى عدم الكفاءة في علاقات التكافؤ من ترجمة الأساس وإدراج SWAP. تُستخدم مرحلة التحسين لمحاولة تقليل حجم الدائرة وعمقها. تُشغّل سلسلة من المراحل في حلقة do while حتى تصل إلى ناتج ثابت.
# pm.pre_optimization.draw()
pm.optimization.draw()

logger = logging.getLogger()
logger.setLevel("INFO")
opt_out = pm.optimization.run(basis_out)
INFO:qiskit.passmanager.base_tasks:Pass: Depth - 0.30112 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.03195 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Size - 0.01216 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.01001 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Optimize1qGatesDecomposition - 0.63729 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: InverseCancellation - 0.41723 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: ContractIdleWiresInControlFlow - 0.01192 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: GatesInBasis - 0.05484 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Depth - 0.08583 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.20599 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Size - 0.00787 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.00715 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Optimize1qGatesDecomposition - 0.16809 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: InverseCancellation - 0.17190 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: ContractIdleWiresInControlFlow - 0.00691 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: GatesInBasis - 0.02408 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Depth - 0.04935 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.00525 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: Size - 0.00620 (ms)
INFO:qiskit.passmanager.base_tasks:Pass: FixedPoint - 0.00286 (ms)
opt_out.draw("mpl", idle_wires=False, fold=-1)
4. أمثلة متعمقة
4.1 تحسين كتل ثنائية الكيوبت باستخدام تركيب المؤثرات الأحادية (الوحدوية) ثنائية الكيوبت
للمستويين 2 و3، لدينا مراحل إضافية (Collect2qBlocks وConsolidateBlocks وUnitarySynthesis) لمزيد من التحسين، وهو تحسين كتل ثنائية الكيوبت. (قارن تدفق مرحلة التحسين للمستوى 2 بذلك أعلاه للمستوى 1)
يتكون تحسين الكتل ثنائية الكيوبت من خطوتين: جمع وتوحيد كتل الكيوبتين وتركيب المصفوفات الموحّدة ثنائية الكيوبت.
pm2 = generate_preset_pass_manager(2, backend, seed_transpiler=42)
pm2.optimization.draw()

from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import (
Collect2qBlocks,
ConsolidateBlocks,
UnitarySynthesis,
)
# Collect 2q blocks and consolidate to unitary when we expect that we can reduce the 2q gate count
# for that unitary
consolidate_pm = PassManager(
[
Collect2qBlocks(),
ConsolidateBlocks(target=backend.target),
]
)
display(basis_out.draw("mpl", idle_wires=False, fold=-1))
consolidated = consolidate_pm.run(basis_out)
consolidated.draw("mpl", idle_wires=False, fold=-1)
# Synthesize unitaries
UnitarySynthesis(target=backend.target)(consolidated).draw(
"mpl", idle_wires=False, fold=-1
)
logger.setLevel("WARNING")
رأينا في الجزء الثاني أن تدفق المترجم الكمومي الحقيقي ليس بسيطًا وهو مؤلف من مراحل كثيرة (مهام). يعود ذلك بشكل رئيسي إلى متطلبات هندسة البرمجيات لضمان الأداء لمجموعة واسعة من دوائر التطبيقات وقابلية صيانة البرنامج. يعمل مترجم Qiskit بشكل جيد في معظم الحالات، ولكن إذا لاحظت أن دائرتك لم تُحسَّن جيدًا بواسطة مترجم Qiskit، فسيكون ذلك فرصة جيدة للبحث في تحسين الدوائر الخاصة بتطبيقك كما هو موضح في الجزء الأول. تقنية المترجم تتطور، ومساهمتك في البحث والتطوير مرحّب بها.
from qiskit.circuit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
backend = service.backend("ibm_brisbane")
sampler = Sampler(backend)
circ = QuantumCircuit(3)
circ.ccx(0, 1, 2)
circ.measure_all()
circ.draw("mpl")
sampler.run([circ]) # IBMInputValueError will be raised
4.2 أهمية تحسين الدوائر
نبدأ بمقارنة نتائج تشغيل دوائر تحضير حالة GHZ ذات 5 كيوبتات () مع التحسين وبدونه.
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
# backend = service.backend('ibm_brisbane')
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
) # Eagle
backend
نبدأ باستخدام دائرة GHZ المركّبة بشكل ساذج كما يلي.
num_qubits = 5
ghz_circ = QuantumCircuit(num_qubits)
ghz_circ.h(0)
[ghz_circ.cx(0, i) for i in range(1, num_qubits)]
ghz_circ.measure_all()
ghz_circ.draw("mpl")
نُترجم الدائرة بدون تحسين (optimization_level=0) وبتحسين (optimization_level=2).
كما ترى، هناك فرق كبير في طول الدوائر المُترجمة.
pm0 = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=777
)
pm2 = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=777
)
circ0 = pm0.run(ghz_circ)
circ2 = pm2.run(ghz_circ)
print("optimization_level=0:")
display(circ0.draw("mpl", idle_wires=False, fold=-1))
print("optimization_level=2:")
display(circ2.draw("mpl", idle_wires=False, fold=-1))
optimization_level=0:
optimization_level=2:
# run the circuits
sampler = Sampler(backend)
job = sampler.run([circ0, circ2], shots=10000)
job_id = job.job_id()
print(f"Job ID: {job_id}")
Job ID: d13rnnemya70008ek1zg
# REPLACE WITH YOUR OWN JOB IDS
job = service.job(job_id)
# get results
result = job.result()
unoptimized_result = result[0].data.meas.get_counts()
optimized_result = result[1].data.meas.get_counts()
from qiskit.visualization import plot_histogram
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[result for result in [sim_result, unoptimized_result, optimized_result]],
bar_labels=False,
legend=[
"ideal",
"no optimization",
"with optimization",
],
)
4.3 أهمية تركيب الدوائر
بعد ذلك نقارن نتائج تشغيل دارتين لتحضير حالة GHZ لخمسة كيوبتات () مُصنَّعتين بطريقتين مختلفتين.
# Original GHZ circuit (naive synthesis)
ghz_circ.draw("mpl")
# A better GHZ circuit (smarter synthesis), you learned in a previous lecture
ghz_circ2 = QuantumCircuit(5)
ghz_circ2.h(2)
ghz_circ2.cx(2, 1)
ghz_circ2.cx(2, 3)
ghz_circ2.cx(1, 0)
ghz_circ2.cx(3, 4)
ghz_circ2.measure_all()
ghz_circ2.draw("mpl")
circ_org = pm2.run(ghz_circ)
circ_new = pm2.run(ghz_circ2)
print("original synthesis:")
display(circ_org.draw("mpl", idle_wires=False, fold=-1))
print("new synthesis:")
display(circ_new.draw("mpl", idle_wires=False, fold=-1))
original synthesis:
new synthesis:
# run the circuits
sampler = Sampler(backend)
job = sampler.run([circ_org, circ_new], shots=10000)
job_id = job.job_id()
print(f"Job ID: {job_id}")
Job ID: d13rp283grvg008j12fg
# REPLACE WITH YOUR OWN JOB IDS
job = service.job(job_id)
# get results
result = job.result()
synthesis_org_result = result[0].data.meas.get_counts()
synthesis_new_result = result[1].data.meas.get_counts()
# plot
sim_result = {"0" * 5: 0.5, "1" * 5: 0.5}
plot_histogram(
[result for result in [sim_result, synthesis_org_result, synthesis_new_result]],
bar_labels=False,
legend=[
"ideal",
"synthesis_org",
"synthesis_new",
],
)
4.4 تحليل بوابات الكيوبت الواحد العامة
from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit.library.standard_gates import UGate
phi, theta, lam = Parameter("φ"), Parameter("θ"), Parameter("λ")
qc = QuantumCircuit(1)
qc.append(UGate(theta, phi, lam), [0])
qc.draw(output="mpl")
transpile(qc, basis_gates=["rz", "sx"]).draw(output="mpl")
4.5 تحسين كتل الكيوبت الواحد
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.x(0)
qc.y(0)
qc.z(0)
qc.rx(1.23, 0)
qc.ry(1.23, 0)
qc.rz(1.23, 0)
qc.h(0)
qc.s(0)
qc.t(0)
qc.sx(0)
qc.sdg(0)
qc.tdg(0)
qc.draw(output="mpl")
from qiskit.quantum_info import Operator
Operator(qc)
Operator([[ 0.45292511-0.57266982j, -0.66852684-0.14135058j],
[ 0.14135058+0.66852684j, -0.57266982+0.45292511j]],
input_dims=(2,), output_dims=(2,))
from qiskit import transpile
qc_opt = transpile(qc, basis_gates=["rz", "sx"])
qc_opt.draw(output="mpl")
Operator(qc_opt)
Operator([[ 0.45292511-0.57266982j, -0.66852684-0.14135058j],
[ 0.14135058+0.66852684j, -0.57266982+0.45292511j]],
input_dims=(2,), output_dims=(2,))
Operator(qc).equiv(Operator(qc_opt))
True
4.6 تحليل بوابة Toffoli
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.draw(output="mpl")
from qiskit import QuantumCircuit, transpile
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc = transpile(qc, basis_gates=["rz", "sx", "cx"])
qc.draw(output="mpl")
4.7 تحليل بوابة CU
from qiskit.circuit.library.standard_gates import CUGate
phi, theta, lam, gamma = Parameter("φ"), Parameter("θ"), Parameter("λ"), Parameter("γ")
qc = QuantumCircuit(2)
# qc.cu(theta, phi, lam, gamma, 0, 1)
qc.append(CUGate(theta, phi, lam, gamma), [0, 1])
qc.draw(output="mpl")
from qiskit.circuit.library.standard_gates import CUGate
phi, theta, lam, gamma = Parameter("φ"), Parameter("θ"), Parameter("λ"), Parameter("γ")
qc = QuantumCircuit(2)
qc.append(CUGate(theta, phi, lam, gamma), [0, 1])
qc = transpile(qc, basis_gates=["rz", "sx", "cx"])
qc.draw(output="mpl")
4.8 تكافؤ CX و ECR و CZ بوابات كليفورد محلية
لاحظ أن (هادامارد)، و (دوران Z بمقدار )، و (دوران Z بمقدار )، و (باولي X) كلها بوابات كليفورد.
qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.draw(output="mpl", style="bw")
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["x", "s", "h", "sdg", "ecr"]).draw(output="mpl", style="bw")
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["h", "cz"]).draw(output="mpl", style="bw")
استخدام بوابات الأساس لكيوبت واحد من خلفية IBM: "rz" و"sx" و"x".
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["rz", "sx", "x", "ecr"]).draw(output="mpl", style="bw")
qc = QuantumCircuit(2)
qc.cx(0, 1)
transpile(qc, basis_gates=["rz", "sx", "x", "cz"]).draw(output="mpl", style="bw")
# Check Qiskit version
import qiskit
qiskit.__version__
'2.0.2'







