استكشاف عدم اليقين
في هذه الوحدة ضمن مشروع Qiskit in Classrooms، يجب أن يكون لدى الطلاب بيئة Python تعمل بشكل صحيح مع الحزم التالية مثبّتة:
qiskitv2.1.0 أو أحدثqiskit-ibm-runtimev0.40.1 أو أحدثqiskit-aerv0.17.0 أو أحدثqiskit.visualizationnumpypylatexenc
لإعداد هذه الحزم وتثبيتها، راجع دليل تثبيت Qiskit. لتشغيل المهام على حواسيب كمية حقيقية، يحتاج الطلاب إلى إنشاء حساب على IBM Quantum® باتباع الخطوات في دليل إعداد حساب IBM Cloud الخاص بك.
تم اختبار هذه الوحدة واستخدمت 8 دقائق من وقت QPU. هذا تقدير فقط، وقد يختلف الاستخدام الفعلي. حسابان مكثفان تم الإشارة إليهما في تعليقات العناوين ويمكن إجراؤهما على المحاكيات إذا كان وقت QPU لدى الطلاب محدودًا. بدونهما، تتطلب الوحدة ~30 ثانية فقط من وقت QPU.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'
شاهد شرح الوحدة بواسطة الدكتورة Katie McCormick أدناه، أو اضغط هنا لمشاهدته على YouTube.
مقدمة
على الأرجح سمعت بمبدأ عدم اليقين حتى خارج مقررات الفيزياء. إعادة صياغة شائعة لعدم اليقين هي "بمجرد أن تنظر إلى شيء، تؤثر عليه." وهذا صحيح بالتأكيد. لكن الطريقة الأكثر دقة فيزيائيًا لوصف عدم اليقين هي أن هناك قياسات فيزيائية معينة تتعارض مع بعضها البعض، مما يمنع معرفة كلتيهما في آنٍ واحد بدقة تعسفية. يصادف كثير من الطلاب لأول مرة ثنائية المتغيرات المتعارضة و، وهما الموضع على المحور والزخم الخطي في تلك الاتجاه على التوالي. وللمتغيرين معًا، يُكتب قيد عدم اليقين كالتالي: هنا، تُسمى "عدم اليقين في "، وتعريفها مطابق للانحراف المعياري في الإحصاء، وتُعرَّف بالصيغة: و تُعرَّف بنفس الطريقة. لن نشتق هذه العلاقة هنا؛ سنشير فقط إلى أنها تتسق مع فهمنا للأمواج الكلاسيكية. أي أن موجة لها تردد وطول موجي مثاليان يمتدان إلى ما لا نهاية كجيب تمام مثالي. كميًا، يقابل ذلك معرفة الزخم بدقة تامة وفق فرضية دي برولي: . لكن لكي نعرف يتواجد الجسيم الشبيه بالموجة، يجب أن تصبح الموجة التي تصفه أكثر حدةً في الفضاء، كتوزيع غاوسي ضيق جدًا مثلًا. نعرف أننا نستطيع التعبير عن أي دالة مستمرة، بما فيها دوال الموجة الحادة هذه، كمتسلسلة فورييه من دوال جيبية بأطوال موجية مختلفة. لكن كلما أصبحت دالة الموجة أكثر حدةً (وصار الموضع أكثر معرفةً)، نحتاج إلى مزيد من الحدود في متسلسلة فورييه، أي مزيج من أطوال موجية أكثر (وبالتالي، كميًا، قيم زخم أكثر).
بشكل أبسط: حالة ذات زخم محدد جيدًا (جيب تمام مثالي في الفضاء) لها موضع غير محدد جدًا. وحالة ذات موضع محدد جيدًا (كتوزيع ديراك دلتا) لها زخم غير محدد جدًا.
هناك متغيرات أخرى تُظهر هذا التعارض. على سبيل المث ال، قد يكون لجسيم إسقاط محدد جيدًا على محور واحد، لكننا لا نعرف شيئًا عن إسقاطه على محور عمودي. على سبيل المثال، الحالة (لكيوبت أو جسيم ذو سبين 1/2) لها إسقاط محدد على المحور (يساوي 1 في سياق الكيوبت، و في سياق جسيم السبين 1/2). لكن هذه الحالة يمكن كتابتها كتراكب لحالتين كل منهما لها إسقاط محدد على المحور : أو بصورة مكافئة: لها إسقاط محدد على ، وكذلك . إذا حددنا إسقاط حالة ما على المحور ، فإننا لا نعرف الإسقاط على المحور . وإذا حددنا الإسقاط على المحور ، فلا نعرف الإسقاط على . هناك اختلافات طفيفة عند مناقشة هذا في سياق السبين والكيوبتات. لكن بشكل عام، للحالات الذاتية لمصفوفات باولي علاقة مثيرة للاهتمام يمكننا استكشافها. طوال هذا الدرس، سنتحقق تجريبيًا من حدسنا بشأن عدم اليقين في هذه المتغيرات المتعارضة، ونتحقق من صحة علاقات عدم اليقين على حواسيب IBM® الكمية.
اختبار بسيط للحدس
في هذه التجربة الأولى وطوال الوحدة، سنستخدم إطار العمل الخاص بالحوسبة الكمية المعروف بـ"أنماط Qiskit"، والذي يُقسّم سير العمل إلى الخطوات التالية:
- الخطوة 1: تحويل المدخلات الكلاسيكية إلى مسألة كمية
- الخطوة 2: تحسين المسألة للتنفيذ الكمي
- الخطوة 3: التنفيذ باستخدام Qiskit Runtime Primitives
- الخطوة 4: المعالجة اللاحقة والتحليل الكلاسيكي
سنتبع هذه الخطوات بشكل عام، وإن كنا لن نصنّفها صراحةً في كل مرة.
لنبدأ بتحميل بعض الحزم الضرورية بما فيها Runtime primitives. سنختار أيضًا الحاسوب الكمي الأقل انشغالًا المتاح لنا.
يوجد في الكود أدناه كود لحفظ بيانات الاعتماد عند الاستخدام الأول. تأكد من حذف هذه المعلومات من الدفتر بعد حفظها في بيئتك، حتى لا تُشارَك بيانات اعتمادك بالخطأ عند مشاركة الدفتر. راجع إعداد حساب IBM Cloud الخاص بك وتهيئة الخدمة في بيئة غير موثوقة لمزيد من التوجيهات.
from numpy import pi
# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService
# Syntax for first saving your token. Delete these lines after saving your credentials.
# QiskitRuntimeService.save_account(channel='ibm_quantum_platform', instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR-API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')
# Load saved credentials
service = QiskitRuntimeService()
# Load the Runtime primitive and session
from qiskit_ibm_runtime import (
Batch,
SamplerV2 as Sampler,
EstimatorV2 as Estimator,
)
# Use the least busy backend
backend = service.least_busy(min_num_qubits=127)
print(backend.name)
ibm_sherbrooke
إذا نفد وقت الحوسبة الكمية المتاح للطالب أثناء الدرس، يمكن إلغاء تعليق الأسطر أدناه واستخدامها لإعداد محاكٍ يحاكي جزئيًا سلوك الضوضاء للحاسوب الكمي المختار أعلاه.
# Import an estimator, this time from qiskit (we will import from Runtime for real hardware)
from qiskit_aer.primitives import SamplerV2, EstimatorV2
from qiskit_aer.noise import NoiseModel
# Generate the noise model from the backend properties
noise_model = NoiseModel.from_backend(backend)
noisy_sampler = SamplerV2(options={"backend_options": {"noise_model": noise_model}})
noisy_estimator = EstimatorV2(options={"backend_options": {"noise_model": noise_model}})
ربما تتذكر أن الحالة الذاتية لمؤثر Z ليست حالة ذاتية لمؤثر X. سنلاحظ ذلك الآن تجريبيًا عن طريق إجراء قياسات على المحورين و. للقياس على ، نستخدم ببساطة qc.measure()، لأن الحواسيب الكمية من IBM مُهيأة للقياس على . أما للقياس على ، فنحتاج إلى تدوير النظام لنقل محور إلى اتجاه القياس بفعالية. يتحقق ذلك ببوابة هادامارد. هناك خطوة مماثلة مطلوبة للقياسات على . الخطوات اللازمة مجموعة هنا للرجوع إليها:
- للقياس على :
qc.measure() - للقياس على :
qc.h()ثمqc.measure() - للقياس على :
qc.sdg()،qc.h()،qc.sثمqc.measure()
الخطوة 1: تحويل المدخلات الكلاسيكية إلى مسألة كمية
في هذه الحالة، خطوة التحويل هي ببساطة التعبير عن القياسات والتدويرات الموضحة أعلاه في دائرة كمية:
# Step 1: Map
# Import some general packages
from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(2, "c")
qc = QuantumCircuit(qr, cr)
# Add a first measurement
qc.measure(qr, cr[0])
qc.barrier()
# Change basis so that measurements made on quantum computer which normally tell us about z, now tell us about x.
qc.h(qr)
# Add a second measurement
qc.measure(qr, cr[1])
qc.draw("mpl")
الخطوة 2: تحسين المسألة للتنفيذ الكمي
تأخذ هذه الخطوة العمليات التي نريد إجراءها وتُعبّر عنها بعبارات وظائف حاسوب كمي محدد. كما تُعيّن مسألتنا على تخطيط الحاسوب الكمي.
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
الخطوة 3: التنفيذ باستخدام Qiskit Runtime primitives
يمكننا استخدام Sampler لجمع إحصائيات القياسات. سنُنشئ الـ Sampler primitive للتشغيل على حاسوب كمي حقيقي باستخدام mode = backend. هناك أوضاع أخرى لسير عمل مختلفة، وسنستخدم واحدًا منها أدناه. سيُستخدم Sampler باستدعاء الدالة run() مع قائمة من "pubs" (Primitive Unified Blocs). كل pub يحتوي على ثلاث قيم كحد أقصى تُعرّف معًا وحدة عمل حسابية للـ estimator: الدوائر، والمراقبات، والمعاملات. يمكنك أيضًا تقديم قائمة دوائر وقائمة مراقبات وقائمة معاملات. لمزيد من المعلومات، اقرأ نظرة عامة على PUBs.
نريد التشغيل على حاسوب كمي حقيقي لكي نُجري تجربة فيزياء كمية حقيقية. إذا نفد وقتك المخصص على الحواسيب الكمية الحقيقية، يمكنك التعليق على الكود أدناه الخاص بالحاس وب الكمي، وإلغاء التعليق على الكود الخاص بالتشغيل على محاكٍ.
# Step 3: Run the job on a real quantum computer
sampler = Sampler(mode=backend)
pubs = [qc_isa]
job = sampler.run(pubs)
res = job.result()
counts = res[0].data.c.get_counts()
# Run the job on the Aer simulator with noise model from real backend
# job = noisy_sampler.run([qc_isa])
# res=job.result()
# counts=res[0].data.c.get_counts()
الخطوة 4: المعالجة اللاحقة
هذه حالة بسيطة بشكل خاص من المعالجة اللاحقة، حيث نكتفي بتصوير الأعداد.
لاحظ أن Qiskit يرتّب الكيوبتات والقياسات وغيرها بوضع العنصر ذو الرقم الأصغر في الآخر/على اليمين، وهو اتفاق يُعرف بـ"little-endian". هذا يعني أن العمود أدناه المسمى "10" يشير إلى الأعداد حيث أعطى القياس الأول "0"، وأعطى القياس الثاني "1".
# Step 4: Post-process
from qiskit.visualization import plot_histogram
plot_histogram(counts)
إذا لم يُعجبك هذا الاتفاق، يمكنك استخدام marginal_counts لتصوير نتائج كل قياس على حدة:
from qiskit.result import marginal_counts
plot_histogram(
marginal_counts(counts, indices=[0]), title="Counts after first measurement"
)
plot_histogram(
marginal_counts(counts, indices=[1]), title="Counts after second measurement"
)
بشكل افتراضي، تُهيأ الحالات في Qiskit في حالة . لذا ليس من المفاجئ أن تُسفر معظم القياسات الأولى عن . لاحظ مع ذلك أن هناك انقسامًا شبه متساوٍ تقريبًا في القياس الثاني (الذي يُعطي معلومات عن إسقاطات الحالة على ). يبدو أن هذه الحالة التي تُعطينا نتيجة يمكن التنبؤ بها جدًا للقياسات على تُعطينا نتائج غير قابلة للتنبؤ تمامًا للقياسات على . دعنا نستكشف هذا.
ماذا يحدث لو أجرينا القياسات بالترتيب المعاكس؟ يمكننا البدء باستخدام بوابة هادامارد للحصول على إحصائيات حول احتمال قياس في . ثم للقياس الثاني، سنعود إلى أساس باستخدام بوابة هادامارد ثانية.
# Step 1:
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(2, "c")
qc = QuantumCircuit(qr, cr)
# Change basis to measure along x.
qc.h(qr)
qc.measure(qr, cr[0])
qc.barrier()
# Change our basis back to z and make a second measurement
qc.h(qr)
qc.measure(qr, cr[1])
qc.draw("mpl")
# Step 2: Transpile the circuit for running on a quantum computer
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
# Step 3: Run the job on a real quantum computer
sampler = Sampler(mode=backend)
pubs = [qc_isa]
job = sampler.run(pubs)
res = job.result()
counts = res[0].data.c.get_counts()
# Run the job on the Aer simulator with noise model from real backend
# job = noisy_sampler.run([qc_isa])
# res=job.result()
# counts=res[0].data.c.get_counts()
# Step 4: Post-process
from qiskit.visualization import plot_histogram
plot_histogram(counts)
هنا، يبدو أن لدينا قدرًا أقل من إمكانية التنبؤ! سابقًا، كنا نعرف على الأقل ما ستكون عليه نتيجة القياس الأول، أما الآن فلدينا توزيع متساوٍ تقريبًا على جميع الحالات الممكنة. ليس من الصعب فهم سبب حدوث هذا. بدأنا في ، وهي خليط 50-50 من و، وفقًا لـ: لذا من الواضح أن يكون احتمال الحصول على الحالة + أو - (المُعيَّنة على 0 و1 في الرسم البياني) متساويًا تقريبًا للقياس الأول. يُنهار القياس على الحالة إلى إما الحالة الذاتية أو الحالة الذاتية . كل من هاتين الحالتين هي خليط 50-50 من و، وفقًا لـ: فعندما يكون النظام في حالة ذاتية من ، من الواضح أن القياسات على ستُعطي كلًا من و، وستفعل ذلك باحتمالية متساوية تقريبًا. إذن المثال الأول أظهر لنا أن بعض الحالات ستُعطي نتائج يمكن التنبؤ بها جدًا لبعض القياسات، لكن نتائج غير قابلة للتنبؤ لقياسات أخرى. أما المثال الحالي فيُظهر لنا أننا قد نُحقق نتائج أسوأ من ذلك. هناك حالا ت يمكن أن تُعطينا نتائج غير قابلة للتنبؤ لكلا القياسين، حتى لو اكتفينا بتبديل ترتيب القياسات. دعنا نبحث في مدى اليقين أو عدم اليقين لكمية ما لحالة معينة.
حساب عدم اليقين
يمكننا تكميم هذا باستخدام عدم اليقين، أو التباين. "عدم اليقين" غالبًا يُعرَّف على أنه الجذر التربيعي لـ"تباين" توزيع معيّن. أي أن عدم اليقين لمشاهَد ما يُرمَز له بـ ومعطى بالصيغة
في حالة مصفوفات باولي، التي فيها ، تصبح المعادلة
لنطبّق هذا على مثال ملموس. نبدأ بالحالة ونحدد عدم اليقين للمشاهَد في تلك الحالة.
تحقّق من فهمك
اقرأ السؤال التالي، فكّر في إجابتك، ثم انقر على المثلث لإظهار الحل.
احسب عدم اليقين لـ في الحالة ، يدويًا.
الجواب:
في الحالة المعطاة، ينتج:
يمكننا إنشاء حالة ابتدائية اعتباطية باستخدام qc.initialize(). لاحظ أن صيغة الوحدة التخيلية هنا هي .
# Step 1: Map the problem into a quantum circuit
from qiskit.quantum_info import SparsePauliOp
import numpy as np
obs = SparsePauliOp("X")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Initialize the state
qc.initialize([1, 1j] / np.sqrt(2))
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
estimator = Estimator(mode=backend)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()
# Run the job on the Aer simulator with noise model from real backend
# job = noisy_estimator.run([[qc_isa,obs_isa]])
# res=job.result()
# Step 4: Return the result in classical form, and analyze.
print(res[0].data.evs)
-0.02408454165642664
بناءً على معادلتنا أعلاه، لنبقَ مع نفس الحالة، لكن نوجد قيمة التوقع لـ الآن:
# Step 1: Map the problem into a quantum circuit
obs = SparsePauliOp("Z")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Initialize the state to |+>_y
qc.initialize([1, 1j] / np.sqrt(2))
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
estimator = Estimator(mode=backend)
pubs = [(qc_isa, obs_isa)]
job = estimator.run(pubs)
res = job.result()
# Run the job on the Aer simulator with noise model from real backend
# job = noisy_estimator.run([[qc_isa,obs_isa]])
# res=job.result()
# Step 4: Return the result in classical form, and analyze.
print(res[0].data.evs)
0.04958271968581247
يمكننا إجراء نفس الحسابات السابقة، لكن سنرى أن التباين قريب جدًا من 1.0 مجددًا. يمكننا أن نستنتج أن . وهذا صحيح تقريبًا للحالة التي اخترناها. لكن هل يمكننا تحقيق نتيجة أفضل؟ أو أسوأ؟
تذكّر أن هناك علاقة عدم يقين بين الموضع على اتجاه معيّن، وكمية التحرك على نفس الاتجاه، بالنسبة لتلك المتغيرات، الصيغة الأكثر شهرة على الأرجح هي لو هذا كل ما نتذكره، قد يغرينا التفكير أن و يمكن أيضًا أن يكون لهما حد أدنى أساسي لعدم اليقين. ربما يستحيل أن يبلغ حاصل ضرب الصفر؟ لنجرب حالة أخرى ونرَ إذا كان هذا صحيحًا. هذه المرة، سنستخدم لنرَ ماذا يحدث. لاحظ أنه في الكود أدناه، يستطيع estimator قبول مجموعتين من الدوائر والمشاهَدات في نفس تقديم الوظيفة.
# Step 1: Map the problem into a quantum circuit
obs1 = SparsePauliOp("X")
obs2 = SparsePauliOp("Z")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Initialize the state
qc.initialize([1, 1] / np.sqrt(2))
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs1_isa = obs1.apply_layout(layout=qc_isa.layout)
obs2_isa = obs2.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
pubs = [(qc_isa, obs1_isa), (qc_isa, obs2_isa)]
job = estimator.run(pubs)
res = job.result()
batch.close()
# Run the job on the Aer simulator with noise model from real backend
# job = noisy_estimator.run([[qc,obs1],[qc,obs2]])
# res=job.result()
# Step 4: Return the result in classical form, and analyze.
print("The expectation value of the first observable is: ", res[0].data.evs)
print("The expectation value of the second observable is: ", res[1].data.evs)
The expectation value of the first observable is: 1.0011036174126302
The expectation value of the second observable is: 0.0029429797670141016
قيمة توقع يجب أن تكون قريبة من 1.0، لكن لا تتجاوز 1.0. لا تقلق إذا تجاوزتها بمقدار صغير جدًا. هذا يمكن إرجاعه لعوامل مثل الضوضاء و/أو خطأ القراءة. رغم أن هذا موضوع مهم جدًا، يمكننا تجاهله في الوقت الحالي.
حصلنا على قيمة توقع لـ قريبة جدًا من 1.0 (ما يقابل تباينًا منخفضًا جدًا لـ). هذا يجعل حاصل ضرب التباينين منخفضًا جدًا:
رغم أن هذا ليس صفرًا بالضبط، إلا أن هذه القيمة باتت صغيرة مقارنةً بالقيم الذاتية لمشغّلات باولي (). حسنًا، ربما تتذكر أن علاقة عدم اليقين بين الموضع الخطي وكمية التحرك يمكن كتابتها بشكل مختلف، باستخدام علاقة الإبدال بين المشغّلَين و بشكل صريح:
حيث
هو مبدّل و.
هذه هي الصيغة التي يمكن تعميمها بأسهل طريقة على مشغّلات باولي. بشكل عام، لمشغّلَين و،
وفي حالة مصفوفتَي باولي و، نحتاج لحساب
نبيّن هذا هنا، ونترك حسابات مشابهة للقارئ كتمرين:
هذا جواب مقبول تمامًا، لكن بخطوة إضافية واحدة، نرى
وبذلك تصبح علاقة عدم اليقين لدينا
تحقّق من فهمك
اقرأ ال سؤال التالي، فكّر في إجابتك، ثم انقر على المثلث لإظهار الحل.
احسب و. استخدم هذا لكتابة علاقات عدم اليقين بين و، وبين و.
الجواب:
بالجمع مع علاقة عدم اليقين العامة، نحصل على