تطبيق Qiskit
في الدرس السابق، ألقينا نظرة أولى على صنفَي Statevector وOperator في Qiskit، واستخدمناهما لمحاكاة العمليات والقياسات على كيوبت واحد.
في هذا القسم، سنستخدم هذين الصنفين لاستكشاف سلوك كيوبتات متعددة.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit"}
_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")
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، التي تُعيد حاصل الضرب التنسوري لذلك Statevector مع آخر، يُعطى كوسيط.
يُفسَّر الوسيط على أنه عامل الضرب التنسوري على اليمين.
على سبيل المثال، ننشئ في الكود أدناه متجهَي حالة يمثلان و، ونستخدم دالة 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: