تطبيق Qiskit
في الدرس السابق، ألقينا نظرة أولى على صنفَي Statevector وOperator في Qiskit، واستخدمناهما لمحاكاة العمليات والقياسات على كيوبت واحد.
في هذا القسم، سنستخدم هذين الصنفين لاستكشاف سلوك كيوبتات متعددة.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import __version__
print(__version__)
2.1.1
سنبدأ باستيراد صنفَي Statevector وOperator، إضافةً إلى دالة الجذر التربيعي من NumPy.
من الآن فصاعداً، سنحرص بشكل عام على تنفيذ جميع الاستيرادات المطلوبة في بداية كل درس.
from qiskit.quantum_info import Statevector, Operator
from numpy import sqrt
حاصل الضرب التنسوري
يمتلك الصنف Statevector دالةً تُسمى tensor، تُعيد حاصل الضرب التنسوري لهذا المتجه مع متجه آخر يُمرَّر كمعامل.
يُفسَّر المعامل على أنه العامل التنسوري على اليمين.
على سبيل المثال، ننشئ في الكود أدناه متجهَي حالة يمثلان و، ونستخدم دالة tensor لإنشاء متجه جديد .
لاحظ أننا نستخدم دالة from_label لتعريف الحالتين و، بدلاً من تعريفهما يدوياً.
zero = Statevector.from_label("0")
one = Statevector.from_label("1")
psi = zero.tensor(one)
display(psi.draw("latex"))
تشمل التسميات المسموح بها أيضاً "+" و"-" للحالتين الموجبة والسالبة، وكذلك "r" و"l" (اختصاراً لـ"right" و"left") للحالتين
هنا، تأتي كلمتا "+" و"-" أو "right" و"left" من سياق الدوران الكمي (spin) في الفيزياء الكمية، حيث قد يشير مركّب الدوران إلى اليسار أو اليمين في التجربة؛ ولا يقصد بها الكيوبت الأيمن أو الأيسر في أنظمة الكيوبتات المتعددة. إليك مثالاً على حاصل الضرب التنسوري لـ و:
plus = Statevector.from_label("+")
minus_i = Statevector.from_label("l")
phi = plus.tensor(minus_i)
display(phi.draw("latex"))
بديل آخر هو استخدام عملية ^ للضرب التنسوري، والتي تعطي بطبيعة الحال نفس النتائج.
display((plus ^ minus_i).draw("latex"))
يمتلك الصنف Operator أيضاً دالة tensor (إضافةً إلى دالة from_label)، كما نرى في الأمثلة التالية.
H = Operator.from_label("H")
Id = Operator.from_label("I")
X = Operator.from_label("X")
display(H.tensor(Id).draw("latex"))
display(H.tensor(Id).tensor(X).draw("latex"))
وكذلك، كما في حالة المتجهات، تُكافئ عملية ^ تماماً.
display((H ^ Id ^ X).draw("latex"))
يمكن تطوير الحالات المركّبة باستخدام عمليات مركّبة كما هو متوقع — تماماً كما رأينا في الأنظمة المفردة في الدرس السابق. على سبيل المثال، يحسب الكود التالي الحالة حيث (والذي تم تعريفه أعلاه بالفعل).
display(phi.evolve(H ^ Id).draw("latex"))
إليك كوداً يعرّف عملية ويحسب للحالة . للتوضيح، هذه عملية تكون فيها الكيوبت اليسرى هي كيوبت التحكم والكيوبت اليمنى هي كيوبت الهدف. والنتيجة هي حالة بل .
CX = Operator([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
psi = plus.tensor(zero)
display(psi.evolve(CX).draw("latex"))
القياسات الجزئية
في الدرس السابق، استخدمنا دالة measure لمحاكاة قياس متجه الحالة الكمية.
تُعيد هذه الدالة عنصرَين: نتيجة القياس المحاكاة، والـStatevector الجديدة بعد هذا القياس.
افتراضياً، تقيس measure جميع الكيوبتات في متجه الحالة.
يمكننا بدلاً من ذلك تمرير قائمة من الأعداد الصحيحة كمعامل، مما يجعل القياس يشمل تلك الفهارس فقط.
لتوضيح ذلك، ينشئ الكود أدناه الحالة
ويقيس الكيوبت رقم 0، وهو الكيوبت الأقصى يميناً. (يُرقّم Qiskit الكيوبتات بدءاً من 0، من اليمين إلى اليسار. سنعود إلى هذه الاتفاقية في الترقيم في الدرس القادم.)
w = Statevector([0, 1, 1, 0, 1, 0, 0, 0] / sqrt(3))
display(w.draw("latex"))
result, state = w.measure([0])
print(f"Measured: {result}\nState after measurement:")
display(state.draw("latex"))
result, state = w.measure([0, 1])
print(f"Measured: {result}\nState after measurement:")
display(state.draw("latex"))
Measured: 0
State after measurement:
Measured: 00
State after measurement: