انتقل إلى المحتوى الرئيسي

تجربة على نطاق الفائدة I

ملاحظة

تاميا أونودِيرا (5 يوليو 2024)

تحميل ملف PDF للمحاضرة الأصلية. لاحظ أن بعض مقاطع الكود قد تكون قديمة لأنها صور ثابتة.

الوقت التقريبي للمعالج الكمومي لتشغيل هذه التجربة هو 45 ثانية.

1. مقدمة إلى ورقة الفائدة

في هذا الدرس، نُشغِّل دائرة كمومية على نطاق الفائدة تظهر في ما نسميه بشكل غير رسمي "ورقة الفائدة" المنشورة في Nature المجلد 618، 15 يونيو 2023. تتناول الورقة تطور نموذج إيزنغ ثنائي الأبعاد للمجال المستعرض بالزمن. بالتحديد، يدرسون ديناميكيات الزمن لهاميلتونيان:

H=HZZ+HX=J(i,j)ZiZj+hiXiH = H_{ZZ} + H_X = - J \sum_{(i,j)} Z_i Z_j + h \sum_{i} X_i

حيث J>0J > 0 هو اقتران الدورانات المتجاورة مع i<ji < j وhh هو المجال المستعرض الكلي. يُحاكون ديناميكيات الدوران من حالة ابتدائية باستخدام تحليل تروتر من الرتبة الأولى لمؤثر التطور الزمني:

exp(iHZZδt)=(i,j)exp(iJδtZiZj)=(i,j)RZiZj(2Jδt)exp(iHXδt)=iexp(ihδtXi)=iRXi(2hδt)\begin{aligned} \exp(-i H_{ZZ} \delta t) &= \prod_{(i,j)} \exp (i J \delta t Z_i Z_j) = \prod_{(i,j)} \mathrm{R}_{Z_i Z_j} ( - 2 J \delta t) \\ \exp(-i H_X \delta t) &= \prod_{i} \exp (-i h \delta t X_i ) = \prod_{i} \mathrm{R}_{X_i} ( 2 h \delta t) \end{aligned}

حيث يُجزَّأ زمن التطور TT إلى T/δtT / \delta t خطوة تروتر، وRZiZj(θJ)\mathrm{R}_{Z_i Z_j}(\theta_J) وRXi(θh)\mathrm{R}_{X_i}(\theta_h) هما بوابتا الدوران ZZZZ وXX على التوالي.

أجروا التجارب على معالج IBM Quantum® Eagle، وهو جهاز مؤلف من 127 كيوبت بتوصيل سداسي ثقيل، طبّقوا فيه تفاعلات XX على جميع الكيوبتات وتفاعلات ZZZZ على جميع أضلاع خريطة الاقتران. لاحظ أنه لا يمكن تطبيق جميع تفاعلات ZZZZ في آنٍ واحد بسبب "الاعتماد على البيانات". لذا يُلوِّنون خريطة الاقتران لتجميعها في طبقات. تُعطى كل طبقة اللون نفسه، ويمكن تطبيقها بالتوازي.

بالإضافة إلى ذلك، ولبساطة التجربة، ركّزوا على الحالة θJ=π/2\theta_J=-\pi /2.

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

منذ ذلك الحين، أصبحنا نسمي مثل هذه التجارب والدوائر "ذات نطاق الفائدة".

1.1 هدفك

هدفك في هذا الدرس هو بناء دائرة كمومية على نطاق الفائدة وتشغيلها على معالج Eagle. يخرج عن نطاق هذا الكتاب استخلاص نتائج موثوقة، جزئياً لأن PEA ميزة تجريبية في Qiskit حتى وقت كتابة هذا النص، وجزئياً لأن تطبيق ZNE مع PEA سيستغرق وقتاً طويلاً.

بالتحديد، أنت مطلوب منك بناء وتشغيل الدائرة المقابلة للشكل 4b في الورقة، ورسم نقاط "غير مُخففة" خاصة بك. كما ترى، هي دائرة بـ127 كيوبت × 60 طبقة (20 خطوة تروتر) مع Z62\langle Z_{62} \rangle كمراقَب. image.png يبدو الأمر ضخماً؟   لا تقلق. الدروس الثلاثة الأخيرة من هذه الدورة تُمهِّد الطريق. للبداية، سنوضح تجربة أصغر حجماً تتمثل في بناء وتشغيل على جهاز افتراضي دائرة بـ27 كيوبت × 6 طبقات (2 خطوة تروتر) مع Z13\langle Z_{13} \rangle كمراقَب.

هذا كل ما في المقدمة. لننطلق في مغامرة نطاق الفائدة!

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime rustworkx
import qiskit

qiskit.__version__
'2.0.2'
#!pip install qiskit_ibm_runtime
#!pip install qiskit_aer
import matplotlib.pyplot as plt
import numpy as np
import rustworkx as rx

from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit.library import YGate
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import (
QiskitRuntimeService,
fake_provider,
EstimatorV2 as Estimator,
)
from qiskit_aer import AerSimulator
service = QiskitRuntimeService()

2. التحضير

2.1 بناء RZZ(-π\pi / 2)

أولاً، لاحظ أن بوابة RZZ بشكل عام تتطلب بوابتَي CXCX.

from qiskit.circuit.library import RZZGate

θ_h = Parameter("$\\theta_h$")
qc1 = QuantumCircuit(2)
qc1.append(RZZGate(θ_h), [0, 1])
qc1.decompose(reps=1).draw("mpl")

Output of the previous code cell

كما ذُكر أعلاه، نُركِّز على بوابة RZZ بزاوية محددة وهي -π\pi / 2 لهذه التجربة. كما هو موضح في الورقة، يمكن تحقيقها ببوابة CXCX واحدة فقط.

qc2 = QuantumCircuit(2)

qc2.sdg([0, 1])
qc2.append(YGate().power(1 / 2), [1])
qc2.cx(0, 1)
qc2.append(YGate().power(1 / 2).adjoint(), [1])

qc2.draw("mpl")

Output of the previous code cell

نُعرِّف بوابة انطلاقاً من هذه الدائرة للرجوع إليها لاحقاً.

rzz = qc2.to_gate(label="RZZ")

لنجرب استخداماً عشوائياً للبوابة rzz التي عرّفناها للتو.

qc3 = QuantumCircuit(3)
qc3.append(rzz, [0, 1])
qc3.append(rzz, [0, 2])
display(qc3.draw("mpl"))
# display(qc.decompose(reps=1).draw("mpl"))

Output of the previous code cell

قبل المضي قُدُماً، لنتحقق من التكافؤ المنطقي بين qc1 (بوابة RZZ) للزاوية -pi/2 وبوابة rzz أو qc2 التي عرّفناها:

from qiskit.quantum_info import Operator

op1 = Operator(qc1.assign_parameters([-np.pi / 2]))
op2 = Operator(qc2)

op1.equiv(op2)
True

2.2 تلوين خريطة الاقتران

لندرس كيف نُلوِّن خريطة اقتران خلفية. هذا ضروري لتجميع تفاعلات ZZZZ في طبقات.

للبداية، لنعرض خريطة اقتران خلفية. لاحظ أن خرائط الاقتران هي سداسية ثقيلة لجميع أجهزة IBM Quantum الحالية.

backend = service.least_busy(operational=True, simulator=False)

backend.coupling_map.draw()

Output of the previous code cell

لتلوين خريطة الاقتران، نستخدم rustworkx، وهي حزمة Python للتعامل مع الرسوم البيانية والشبكات المعقدة. تُوفر خوارزميات تلوين متعددة، وهي جميعها إرشادية وبالتالي لا تضمن إيجاد تلوين أدنى.

مع ذلك، بما أن الرسوم البيانية السداسية الثقيلة ثنائية التقسيم، نختار graph_bipartite_edge_color، الذي ينبغي أن يجد تلويناً أدنى لهذه الرسوم.

def color_coupling_map(backend):
graph = backend.coupling_map.graph
undirected_graph = graph.to_undirected(multigraph=False)
edge_color_map = rx.graph_bipartite_edge_color(undirected_graph)
if edge_color_map is None:
edge_color_map = rx.graph_greedy_edge_color(undirected_graph)
# build a map from color to a list of edges
edge_index_map = undirected_graph.edge_index_map()
color_edges_map = {color: [] for color in edge_color_map.values()}
for edge_index, color in edge_color_map.items():
color_edges_map[color].append(
(edge_index_map[edge_index][0], edge_index_map[edge_index][1])
)
return edge_color_map, color_edges_map

ينبغي رسم الرسوم البيانية السداسية الثقيلة بثلاثة ألوان. لنتحقق من ذلك لخريطة الاقتران أعلاه.

edge_color_map, color_edges_map = color_coupling_map(backend)
print(
f"{backend.name}, {backend.num_qubits}-qubit device, {len(color_edges_map.keys())} colors assigned."
)
ibm_strasbourg, 127-qubit device, 3 colors assigned.

صحيح!

للمتعة، لنرسم خريطة الاقتران بالألوان التي حصلنا عليها، مستخدمين ميزة تصوير rustworkx.

color_str_map = {0: "green", 1: "red", 2: "blue"}

undirected_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
for i in undirected_graph.edge_indices():
undirected_graph.get_edge_data_by_index(i)["color"] = color_str_map[
edge_color_map[i]
]

rx.visualization.graphviz_draw(
undirected_graph, method="neato", edge_attr_fn=lambda edge: {"color": edge["color"]}
)

Output of the previous code cell

3. حل التطور الزمني بتروتر لنموذج إيزنغ ثنائي الأبعاد

لنُعرِّف روتيناً لبناء دائرة ورقة الفائدة لتطور نموذج إيزنغ ثنائي الأبعاد بالزمن. يأخذ الروتين ثلاثة معاملات: خلفية، وعدداً صحيحاً يشير إلى عدد خطوات تروتر، ومتغير منطقي يتحكم في إدراج الحواجز.

def get_utility_circuit(backend, num_steps: int, barrier: bool = False):
num_qubits = backend.num_qubits
_, color_edges_map = color_coupling_map(backend)
θ_h = Parameter("$\\theta_h$")
qc = QuantumCircuit(num_qubits)

for i in range(num_steps):
qc.rx(θ_h, range(num_qubits))

for _, edge_list in color_edges_map.items():
for edge in edge_list:
qc.append(rzz, edge)

if barrier:
qc.barrier()
return qc

لاحظ أننا أجرينا بالفعل يدوياً رسم الكيوبتات وتوجيهها للدائرة المبنية. لذا، عند نقل الدائرة لاحقاً، لا ينبغي (ولا يجب) أن نطلب من المُحوِّل القيام برسم الكيوبتات وتوجيهها. كما ستلاحظ قريباً، نستدعيه بمستوى تحسين 1 وطريقة تخطيط "trivial".

بعد ذلك نُعرِّف روتيناً بسيطاً للحصول على معلومات عن الدائرة المبنية للفحص السريع.

def get_circuit_info(qc: QuantumCircuit, reps: int = 0):
qc0 = qc.decompose(reps=reps)
return (
f"{qc0.num_qubits} qubits × {qc0.depth(lambda x: x.operation.num_qubits == 2)} layers ({qc0.depth()}-depth)"
+ ", "
+ f"""Gate breakdown: {", ".join([f"{k.upper()} {v}" for k, v in qc0.count_ops().items()])}"""
)

لنجرب هذه الروتينات. يجب أن ترى دائرة بـ27 كيوبت × 15 طبقة (5 خطوات تروتر). بما أن الجهاز الافتراضي لديه 28 ضلعاً، يجب أن يكون هناك 28*5 بوابات تشابك.

backend = fake_provider.FakeTorontoV2()
num_steps = 5
qc = get_utility_circuit(backend, num_steps, True)

display(qc.draw(output="mpl", fold=-1))
print(get_circuit_info(qc, reps=0))
print(get_circuit_info(qc, reps=1))

Output of the previous code cell

27 qubits × 15 layers (20-depth),  Gate breakdown: CIRCUIT-165 140, RX 135, BARRIER 5
27 qubits × 15 layers (60-depth), Gate breakdown: SDG 280, UNITARY 280, CX 140, R 135, BARRIER 5

4. حل نسخة 27 كيوبت من المسألة

نوضح الآن نسخة أصغر حجماً من تجربة الفائدة. سنبني دائرة بـ27 كيوبت × 6 طبقات (2 خطوة تروتر) مع Z13\langle Z_{13} \rangle كمراقَب، ونشغّلها على AerSimulator وجهاز افتراضي.

بالطبع، نتبع سير عملنا الرباعي "نمط Qiskit"، الذي يتكون من: الربط، والتحسين، والتنفيذ، والمعالجة اللاحقة. بالتحديد:

  • ربط المدخلات الكلاسيكية بحوسبة كمومية.
  • تحسين الدوائر للحوسبة الكمومية.
  • تنفيذ الدوائر باستخدام البدائيات.
  • المعالجة اللاحقة وإعادة النتائج بتنسيق كلاسيكي.

في ما يلي، لدينا خطوة الربط لإنشاء دائرة لتجربة أصغر حجماً. ثم لدينا مجموعة واحدة من التحسين والتنفيذ لـAerSimulator وأخرى لجهاز افتراضي. أخيراً، لدينا خطوة المعالجة اللاحقة لرسم النتائج.

4.1 الخطوة 1: الربط

backend = fake_provider.FakeTorontoV2()  # a 27 qubit fake device.
num_steps = 2
qc = get_utility_circuit(backend, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [13], 1)], num_qubits=backend.num_qubits
) # Falcon
angles = [
0,
0.1,
0.2,
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
1.0,
np.pi / 2,
] # We try 11 angles for theta_h.

4.2 الخطوتان 2 و3: التحسين والتنفيذ (المحاكي)

backend_sim = AerSimulator()
transpiled_qc_sim = transpile(
qc, backend_sim, optimization_level=1, layout_method="trivial"
)
transpiled_obs_sim = obs.apply_layout(layout=transpiled_qc_sim.layout)

print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc_sim, reps=1))
27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54
27 qubits × 6 layers (16-depth), Gate breakdown: U3 80, CX 56, R 54, U1 32, U 28

شغّل أحد المستخدمين الخلية التالية على MacBook Pro بمعالج Intel Core i7 رباعي النواة بتردد 2.3 غيغاهرتز مزوّد بـ32 غيغابايت من ذاكرة RAM من نوع 3LPDDR4X، يعمل بنظام macOS 14.5. استغرق 161 مللي ثانية في الوقت الفعلي. قد يختلف الأمر قليلاً من جهاز لآخر.

%%time
params = [[p] for p in angles]
estimator = Estimator(mode=backend_sim)
pub = (transpiled_qc_sim, transpiled_obs_sim, params)
result_sim = estimator.run([pub]).result()
CPU times: user 231 ms, sys: 186 ms, total: 417 ms
Wall time: 111 ms

4.3 الخطوتان 2 و3: التحسين والتنفيذ (الجهاز الافتراضي)

backend_fake = fake_provider.FakeTorontoV2()
transpiled_qc_fake = transpile(
qc, backend_fake, optimization_level=1, layout_method="trivial"
)
transpiled_obs_fake = obs.apply_layout(layout=transpiled_qc_fake.layout)

print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc_fake, reps=1))
27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54
27 qubits × 6 layers (49-depth), Gate breakdown: SDG 324, U1 274, H 162, CX 56, U3 14

عندما شغّل المستخدم ذاته الخلية التالية بنفس البيئة، استغرق الأمر دقيقتين و19 ثانية في الوقت الفعلي. تنفيذ دائرة على جهاز افتراضي يستدعي محاكاة مضوضأة تستغرق وقتاً أطول بكثير من المحاكاة الدقيقة. ننصحك بعدم تشغيل دائرة أكبر (مثل 27 كيوبت × 9 طبقات بـ3 خطوات تروتر) على جهاز افتراضي.

%%time
params = [[p] for p in angles]
estimator = Estimator(mode=backend_fake)
pub = (transpiled_qc_fake, transpiled_obs_fake, params)
result_fake = estimator.run([pub]).result()
CPU times: user 4min 42s, sys: 9.35 s, total: 4min 51s
Wall time: 38.3 s

4.4 الخطوة 4: المعالجة اللاحقة

نرسم النتائج من المحاكاتين الدقيقة والمضوضأة. ستلاحظ التأثيرات الشديدة للضوضاء على FakeToronto.

plt.plot(angles, result_fake[0].data.evs, "o", label="Fake Device")
plt.plot(angles, result_sim[0].data.evs, "o", label="AerSimulator")
plt.xlabel("$\\mathrm{R_x}$ angle $\\theta_h$")
plt.title("$\\langle Z_{13} \\rangle$")
plt.legend()
plt.show()

Output of the previous code cell

5. حل نسخة 127 كيوبت من المسألة

هدفك هو تشغيل تجربة نطاق الفائدة كما ذُكر في البداية. ستُنشئ وتُشغِّل دائرة بـ127 كيوبت و60 طبقة (20 خطوة تروتر) مع Z62\langle Z_{62} \rangle كمراقَب. ننصحك بمحاولة القيام بذلك بنفسك، مستعيناً بكود نسخة 27 كيوبت حيثما يناسب. لكن الحل مُقدَّم هنا.

الحل:

5.1 الخطوة 1: الربط

# backend_map = service.backend("ibm_brisbane")
backend_map = service.least_busy(operational=True, simulator=False)

num_steps = 20
qc = get_utility_circuit(backend_map, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [62], 1)], num_qubits=backend_map.num_qubits
) # Eagle
angles = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, np.pi / 2]

5.2 الخطوتان 2 و3: التحسين والتنفيذ

نلاحظ أن خريطة اقتران معالج Eagle تحتوي على 144 ضلعاً.

# backend = service.backend("ibm_brisbane")
backend = backend_map

transpiled_qc = transpile(qc, backend, optimization_level=1, layout_method="trivial")
transpiled_obs = obs.apply_layout(layout=transpiled_qc.layout)

print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc))
156 qubits × 60 layers (221-depth),  Gate breakdown: SDG 7040, UNITARY 7040, CX 3520, R 3120
156 qubits × 60 layers (201-depth), Gate breakdown: RZ 11933, SX 6240, CZ 3520
params = [[p] for p in angles]
estimator = Estimator(mode=backend)
pub = (transpiled_qc, transpiled_obs, params)
job = estimator.run([pub])

job_id = job.job_id()
print(f"job id={job_id}")
job id=d1479n6qf56g0081sxa0

5.3 المعالجة اللاحقة

نُوفِّر القيم لنقاط "المُخففة" في الشكل 4b من ورقة الفائدة. ارسمها جنباً إلى جنب مع نتائجك.

result_paper = [
1.0171,
1.0044,
0.9563,
0.9602,
0.8394,
0.8120,
0.5466,
0.4556,
0.1953,
0.0141,
0.0117,
]

# REPLACE WITH YOUR OWN JOB ID
job = service.job(job_id)

plt.plot(angles, job.result()[0].data.evs, "o", label=f"{job.backend().name}")
plt.plot(angles, result_paper, "o", label="Utility Paper")
plt.xlabel("$\\mathrm{R_x}$ angle $\\theta_h$")
plt.title("$\\langle Z_{62} \\rangle$")
plt.legend()
plt.show()

Output of the previous code cell

هل نتائجك مشابهة للنتائج "غير المُخففة" في الشكل 4b؟   قد تكون مختلفة جداً، تبعاً للجهاز وحالته وقت التجربة. لا تقلق بشأن النتائج نفسها. ما سنتحقق منه هو ما إذا كنت قد كتبت الكود بصورة صحيحة. إذا فعلت ذلك، فتهانينا، لقد وصلت إلى نقطة الانطلاق في حقبة الفائدة.

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

المرجع