تطبيق Qiskit
في هذا الدرس، نُطبّق بعض الأفكار الواردة في درس التشابك في العمل باستخدام Qiskit.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"qiskit": "qiskit", "qiskit_aer": "qiskit-aer"}
_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
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, array_to_latex
from qiskit.result import marginal_distribution
from qiskit.circuit.library import UGate
from math import pi
import random
هذا تطبيق بدائرة كمية لبروتوكول النقل الكمي.
qubit = QuantumRegister(1, "Q")
ebit0 = QuantumRegister(1, "A")
ebit1 = QuantumRegister(1, "B")
a = ClassicalRegister(1, "a")
b = ClassicalRegister(1, "b")
protocol = QuantumCircuit(qubit, ebit0, ebit1, a, b)
# Prepare ebit used for teleportation
protocol.h(ebit0)
protocol.cx(ebit0, ebit1)
protocol.barrier()
# Alice's operations
protocol.cx(qubit, ebit0)
protocol.h(qubit)
protocol.barrier()
# Alice measures and sends classical bits to Bob
protocol.measure(ebit0, a)
protocol.measure(qubit, b)
protocol.barrier()
# Bob uses the classical bits to conditionally apply gates
with protocol.if_test((a, 1)):
protocol.x(ebit1)
with protocol.if_test((b, 1)):
protocol.z(ebit1)
display(protocol.draw(output="mpl"))
تستخدم هذه الدائرة بعض ميزات Qiskit التي لم نرها في الدروس السابقة، من بينها الدالتان barrier وif_test.
تُنشئ barrier فاصلًا بصريًا يجعل مخطط الدائرة أكثر وضوحًا، كما تمنع Qiskit من إجراء تبسيطات وتحسينات مختلفة عبر الحاجز أثناء التصريف عند تشغيل الدوائر على أجهزة حقيقية.
أما if_test فتُطبّق عملية بشكل مشروط بحسب حالة بت كلاسيكي أو سجل كلاسيكي.
تبدأ الدائرة بتهيئة لتكون في الحالة (وهذا ليس جزءًا من البروتوكول في حد ذاته)، ثم تأتي عمليات أليس، ثم قياساتها، وأخيرًا عمليات بوب. للتحقق من أن البروتوكول يعمل بشكل صحيح، سنُطبّق بوابة كيوبت واحدة مُولَّدة عشوائيًا على الحالة المُهيَّأة للكيوبت للحصول على متجه حالة كمية عشوائية يُراد نقلها. وبتطبيق معكوس تلك البوابة (أي المرافقة الهيرميتية) على بعد تشغيل البروتوكول، نستطيع التحقق من نجاح النقل بقياس الكيوبت والتأكد من عودته إلى الحالة .
أولًا، سنختار بوابة كيوبت أحادية (unitary) بشكل عشوائي.
random_gate = UGate(
theta=random.random() * 2 * pi,
phi=random.random() * 2 * pi,
lam=random.random() * 2 * pi,
)
display(array_to_latex(random_gate.to_matrix()))
سننشئ الآن دائرة اختبار جديدة تُطبّق أولًا بوابتنا العشوائية على , ثم تُشغّل دائرة النقل الكمي، وأخيرًا تُطبّق معكوس بوابتنا العشوائية على الكيوبت وتقيسه. يجب أن تكون النتيجة بشكل مؤكد.
# Create a new circuit including the same bits and qubits used in the
# teleportation protocol.
test = QuantumCircuit(qubit, ebit0, ebit1, a, b)
# Start with the randomly selected gate on Q
test.append(random_gate, qubit)
test.barrier()
# Append the entire teleportation protocol from above.
test = test.compose(protocol)
test.barrier()
# Finally, apply the inverse of the random unitary to B and measure.
test.append(random_gate.inverse(), ebit1)
result = ClassicalRegister(1, "Result")
test.add_register(result)
test.measure(ebit1, result)
display(test.draw(output="mpl"))
أخيرًا، لنُشغّل محاكي Aer على هذه الدائرة ونرسم مخطط التوزيع للمخرجات. سنرى الإحصاءات للبتات الكلاسيكية الثلاثة جميعها: يجب أن يكون البت الأدنى/الأيسر دائمًا , مما يدل على أن الكيوبت قد نُقل بنجاح إلى , بينما يكون البتان الآخران موزّعَين بالتساوي تقريبًا.
result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))
يمكننا أيضًا تصفية الإحصاءات للتركيز فقط على كيوبت نتيجة الاختبار إذا أردنا، كما يلي:
filtered_statistics = marginal_distribution(statistics, [2])
display(plot_histogram(filtered_statistics))
الترميز فائق الكثافة
الترميز فائق الكثافة هو بروتوكول يحقق، بمعنى ما، هدفًا مكملًا للنقل الكمي. فبدلًا من إتاحة نقل كيوبت واحد باستخدام بتين كلاسيكيين من الاتصال (على حساب e-bit واحد من التشابك)، يتيح نقل بتين كلاسيكيين باستخدام كيوبت واحد من الاتصال الكمي (أيضًا على حساب e-bit واحد من التشابك).
بمزيد من التفصيل، لدينا مُرسِل (أليس) ومُستقبِل (بوب) يتشاركان e-bit واحدًا من التشابك. وفق الاصطلاحات المعتمدة في الدرس، هذا يعني أن أليس تمتلك الكيوبت , وبوب يمتلك الكيوبت , والزوج معًا في الحالة . تريد أليس إرسال بتين كلاسيكيين إلى بوب، نرمز لهما بـ و, وستُحقق ذلك بإرسال كيوبت واحد.
من المنطقي اعتبار هذا الإنجاز أقل إثارة مما يحققه النقل الكمي. فإرسال الكيوبتات سيكون على الأرجح أصعب بكثير من إرسال البتات الكلاسيكية في المستقبل المنظور، لدرجة أن مبادلة كيوبت واحد من الاتصال الكمي ببتين من الاتصال الكلاسيكي، وعلى حساب e-bit فوق ذلك، يبدو غير مجدٍ. غير أن هذا لا يعني أن الترميز فائق الكثافة غير مثير للاهتمام، فهو مثير للاهتمام بلا شك.
انسجامًا مع موضوع الدرس، أحد أسباب إثارة الترميز فائق الكثافة للاهتمام هو أنه يُظهر استخدامًا ملموسًا — ولافتًا في سياق نظرية المعلومات — للتشابك. نظرية مشهورة في نظرية المعلومات الكمية، تُعرف بـ نظرية هولِيفو، تعني أنه دون استخدام حالة متشابكة مشتركة، يستحيل توصيل أكثر من بت كلاسيكي واحد بإرسال كيوبت واحد. (نظرية هولِيفو أعم من ذلك. صياغتها الدقيقة تقنية وتستلزم شرحًا، لكن هذه إحدى نتائجها.) إذن، عبر الترميز فائق الكثافة، يُتيح التشابك المشترك مضاعفة الطاقة الاستيعابية للمعلومات الكلاسيكية التي يمكن نقلها بإرسال الكيوبتات.
البروتوكول
يصف مخطط الدائرة الكمية التالي بروتوكول الترميز فائق الكثافة:

وصفًا بالكلمات، إليك ما تفعله أليس:
-
إذا كان , تُطبّق أليس بوابة على كيوبتها (وإذا كان فلا تفعل شيئًا).
-
إذا كان , تُطبّق أليس بوابة على كيوبتها (وإذا كان فلا تفعل شيئًا).
ثم تُرسل أليس كيوبتها إلى بوب.
ما يفعله بوب عند استلامه الكيوبت هو أن يُطبّق أولًا بوابة NOT المتحكَّم بها، مع كمتحكِّم و كهدف، ثم يُطبّق بوابة هادامارد على . بعدها يقيس للحصول على ويقيس للحصول على , وذلك بقياسات الأساس القياسي في كلتا الحالتين.
التحليل
الفكرة وراء هذا البروتوكول بسيطة: تختار أليس فعليًا حالة بِل التي تريد مشاركتها مع بوب، ترسل له كيوبتها، ويقيس بوب ليحدد أي حالة بِل اختارت أليس.
أي أنهما يتشاركان في البداية وبحسب البتين و إما أن تتركها أليس كما هي أو تحوّلها إلى إحدى حالات بِل الأخرى بتطبيق أو أو أو على كيوبتها
تُحدث عمليات بوب التأثيرات التالية على حالات بِل الأربع:
يمكن التحقق من هذا مباشرةً بحساب نتائج عمليات بوب على هذه الحالات واحدة تلو الأخرى.
إذن، حين يُجري بوب قياساته، يستطيع تحديد أي حالة بِل اختارت أليس. والتحقق من صحة البروتوكول يتم بفحص كل حالة على حدة:
-
إذا كان , فحالة عندما يستلم بوب هي . يحوّل هذه الحالة إلى ويحصل على .
-
إذا كان , فحالة عندما يستلم بوب هي . يحوّل هذه الحالة إلى ويحصل على .
-
إذا كان , فحالة عندما يستلم بوب هي . يحوّل هذه الحالة إلى ويحصل على .
-
إذا كان , فحالة عندما يستلم بوب هي . يحوّل هذه الحالة إلى ويحصل على . (عامل الطور السالب لا تأثير له هنا.)
تطبيق الترميز فائق الكثافة
هذا تطبيق بسيط للترميز فائق الكثافة حيث نُحدد الدائرة نفسها بحسب البتات المراد إرسالها. أولًا سنختار بتين للإرسال. (لاحقًا سنختارهما عشوائيًا، لكن الآن سنتخذ اختيارًا اعتباطيًا.)
c = "1"
d = "0"
الآن سنبني الدائرة وفق ذلك. هنا سنسمح لـ Qiskit باستخدام الأسماء الافتراضية للكيوبتات: للكيوبت العلوي و للسفلي.
protocol = QuantumCircuit(2)
# Prepare ebit used for superdense coding
protocol.h(0)
protocol.cx(0, 1)
protocol.barrier()
# Alice's operations
if d == "1":
protocol.z(0)
if c == "1":
protocol.x(0)
protocol.barrier()
# Bob's actions
protocol.cx(0, 1)
protocol.h(0)
protocol.measure_all()
display(protocol.draw(output="mpl"))
لا جديد كثير هنا سوى الدالة measure_all، التي تقيس جميع الكيوبتات وتضع النتائج في سجل كلاسيكي واحد (ومن ثَمّ تحتوي على بتين في هذه الحالة).
تشغيل محاكي Aer يُنتج المخرج المتوقع.
result = AerSimulator().run(protocol).result()
statistics = result.get_counts()
for outcome, frequency in statistics.items():
print(f"Measured {outcome} with frequency {frequency}")
display(plot_histogram(statistics))
Measured 10 with frequency 1024
لنستخدم الآن كيوبتًا إضافيًا كمولّد للبتات العشوائية — بشكل أساسي لرمي عملات معدنية عادلة. سنستخدمه لاختيار و عشوائيًا، ثم نُشغّل بروتوكول الترميز فائق الكثافة.
rbg = QuantumRegister(1, "coin")
ebit0 = QuantumRegister(1, "A")
ebit1 = QuantumRegister(1, "B")
Alice_c = ClassicalRegister(1, "Alice c")
Alice_d = ClassicalRegister(1, "Alice d")
test = QuantumCircuit(rbg, ebit0, ebit1, Alice_d, Alice_c)
# Initialize the ebit
test.h(ebit0)
test.cx(ebit0, ebit1)
test.barrier()
# Use the 'coin' qubit twice to generate Alice's bits c and d.
test.h(rbg)
test.measure(rbg, Alice_c)
test.h(rbg)
test.measure(rbg, Alice_d)
test.barrier()
# Now the protocol runs, starting with Alice's actions, which depend
# on her bits.
with test.if_test((Alice_d, 1), label="Z"):
test.z(ebit0)
with test.if_test((Alice_c, 1), label="X"):
test.x(ebit0)
test.barrier()
# Bob's actions
test.cx(ebit0, ebit1)
test.h(ebit0)
test.barrier()
Bob_c = ClassicalRegister(1, "Bob c")
Bob_d = ClassicalRegister(1, "Bob d")
test.add_register(Bob_d)
test.add_register(Bob_c)
test.measure(ebit0, Bob_d)
test.measure(ebit1, Bob_c)
display(test.draw(output="mpl"))
تشغيل محاكي Aer يُظهر النتائج: تتطابق البتات الكلاسيكية لأليس وبوب دائمًا.
result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))
تطبيق Qiskit
يمكننا تطبيق لعبة CHSH، إلى جانب الاستراتيجية الكمية المُعرَّفة أعلاه، في Qiskit على النحو التالي.
أولاً، إليك تعريف اللعبة نفسها، الذي يُتيح إضافة أي استراتيجية كمعامل.
def chsh_game(strategy):
# This function runs the CHSH game, using the strategy (a function
# from two bits to two bits), returning 1 for a win and 0 for a loss.
# Choose x and y randomly
x, y = random.randint(0, 1), random.randint(0, 1)
# Use the strategy to determine a and b
a, b = strategy(x, y)
# Decide if the strategy wins or loses
if (a != b) == (x & y):
return 1 # Win
return 0 # Lose
الآن سننشئ دالة تُخرج دائرة بحسب الأسئلة الموجَّهة لأليس وبوب. سنترك الكيوبتات بأسمائها الافتراضية للبساطة، وسنستخدم بوّابة المدمجة لإجراءات أليس وبوب.
def chsh_circuit(x, y):
# This function creates a `QuantumCircuit` implementing the quantum
# strategy described above (including the e-bit preparation).
qc = QuantumCircuit(2, 2)
# Prepare an e-bit
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# Alice's actions
if x == 0:
qc.ry(0, 0)
else:
qc.ry(-pi / 2, 0)
qc.measure(0, 0)
# Bob's actions
if y == 0:
qc.ry(-pi / 4, 1)
else:
qc.ry(pi / 4, 1)
qc.measure(1, 1)
return qc
إليك الدوائر الأربع المحتملة، بحسب الأسئلة المطروحة.
# Draw the four possible circuits
print("(x,y) = (0,0)")
display(chsh_circuit(0, 0).draw(output="mpl"))
print("(x,y) = (0,1)")
display(chsh_circuit(0, 1).draw(output="mpl"))
print("(x,y) = (1,0)")
display(chsh_circuit(1, 0).draw(output="mpl"))
print("(x,y) = (1,1)")
display(chsh_circuit(1, 1).draw(output="mpl"))
(x,y) = (0,0)
(x,y) = (0,1)
(x,y) = (1,0)
(x,y) = (1,1)
الآن سننشئ مهمة باستخدام محاكي Aer تُشغّل الدائرة مرة واحدة لزوج الإدخال المحدد .
def quantum_strategy(x, y):
# This function runs the appropriate quantum circuit defined above
# one time and returns the measurement results
# Setting `shots=1` to run the circuit once
result = AerSimulator().run(chsh_circuit(x, y), shots=1).result()
statistics = result.get_counts()
# Determine the output bits and return them
bits = list(statistics.keys())[0]
a, b = bits[0], bits[1]
return a, b
أخيراً، سنلعب اللعبة 1000 مرة ونحسب نسبة الفوز.
NUM_GAMES = 1000
TOTAL_SCORE = 0
for _ in range(NUM_GAMES):
TOTAL_SCORE += chsh_game(quantum_strategy)
print("Fraction of games won:", TOTAL_SCORE / NUM_GAMES)
Fraction of games won: 0.867
يمكننا أيضاً تعريف استراتيجية كلاسيكية ومعرفة مدى فاعليتها. هذه مجرد استراتيجية واحدة — يمكن اختبار استراتيجيات أخرى بتغيير الكود — لكنها من بين الاستراتيجيات الكلاسيكية المثلى.
def classical_strategy(x, y):
# This function implements just one example of an optimal classical
# strategy for the CHSH game. Other classical strategies can be
# implemented by changing the bit values assigned to a and b.
# Alice's answer
if x == 0:
a = 0
elif x == 1:
a = 1
# Bob's answer
if y == 0:
b = 1
elif y == 1:
b = 0
return a, b
لنلعب اللعبة مجدداً 1000 مرة لنرى مدى فاعليتها.
NUM_GAMES = 1000
TOTAL_SCORE = 0
for _ in range(NUM_GAMES):
TOTAL_SCORE += chsh_game(classical_strategy)
print("Fraction of games won:", TOTAL_SCORE / NUM_GAMES)
Fraction of games won: 0.747
رغم وجود العشوائية، من المستبعد جدًا أن تنحرف الإحصاءات كثيرًا بعد 1000 تشغيل. تفوز الاستراتيجية الكمية نحو 85% من المرات، بينما لا تستطيع أي استراتيجية كلاسيكية الفوز بأكثر من نحو 75% من المرات.