البتات الكمومية والبوابات والدوائر
Kifumi Numata (19 Apr 2024)
اضغط هنا لتنزيل نسخة PDF من المحاضرة الأصلية. لاحظ أن بعض مقاطع الكود قد تكون قديمة لأنها صور ثابتة.
وقت وحدة المعالجة الكمومية التقريبي لتشغيل هذه التجربة هو 5 ثوانٍ.
1. المقدمة
البتات والبوابات والدوائر هي اللبنات الأساسية للحوسبة الكمومية. ستتعلم الحوسبة الكمومية باستخدام نموذج الدائرة القائم على البتات الكمومية والبوابات، وستراجع أيضاً مفاهيم التراكب والقياس والتشابك.
في هذا الدرس ستتعلم:
- بوابات الكيوبت الواحد
- كرة بلوخ
- التراكب
- القياس
- بوابات الكيوبتين وحالة التشابك
في نهاية هذه المحاضرة، ستتعرف على عمق الدائرة، وهو أمر جوهري للحوسبة الكمومية على نطاق المنفعة.
2. الحوسبة كرسم بياني
عند التعامل مع الكيوبتات أو البتات، نحتاج إلى معالجتها لتحويل المدخلات إلى المخرجات المطلوبة. في أبسط البرامج التي تحتوي على بتات قليلة جداً، يكون من المفيد تمثيل هذه العملية في رسم بياني يُعرف بـ رسم الدائرة.
الشكل في الأسفل على اليسار هو مثال على دائرة كلاسيكية، والشكل في الأسفل على اليمين هو مثال على دائرة كمومية. في كلتا الحالتين، المدخلات على اليسار والمخرجات على اليمين، بينما تُمثَّل العمليات برموز. وتُسمى هذه الرموز "بوابات"، في الغالب لأسباب تاريخية.
3. بوابة الكيوبت الواحد الكمومية
3.1 الحالة الكمومية وكرة بلوخ
تُمثَّل حالة الكيوبت كتراكب لـ و . وتُعبَّر عن حالة كمومية اعتباطية بالصيغة:
حيث و أعداد مركبة تحقق .
و هما متجهان في الفضاء المتجهي المركب ثنائي الأبعاد:
وبناءً على ذلك، تُمثَّل أي حالة كمومية اعتباطية أيضاً بالصيغة:
من هذا يتضح أن حالة البت الكمومي هي متجه وحدوي في فضاء الضرب الداخلي المركب ثنائي الأبعاد بأساس متعامد معياري هو و ، وهو مُعيَّر إلى 1.
|\psi\rangle =\begin\{pmatrix\} \alpha \\ \beta \end\{pmatrix\} يُسمى أيضاً متجه الحالة (statevector).
كما يمكن تمثيل حالة الكيوبت الواحد بالصيغة:
حيث و هما زاوي ا كرة بلوخ الموضحة في الشكل التالي.
في خلايا الكود التالية، سنبني الحسابات الأساسية خطوة بخطوة باستخدام Qiskit. سننشئ دائرة فارغة ثم نضيف العمليات الكمومية، ونناقش البوابات ونتخيل تأثيراتها أثناء التطبيق. يمكنك تشغيل الخلية بالضغط على "Shift" + "Enter". ابدأ باستيراد المكتبات أولاً.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# Import the qiskit library
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
from qiskit_ibm_runtime import Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.visualization import plot_histogram
إعداد الدائرة الكمومية
سننشئ دائرة من كيوبت واحد ونرسمها.
# Create the single-qubit quantum circuit
qc = QuantumCircuit(1)
# Draw the circuit
qc.draw("mpl")
بوابة X
بوابة X هي دوران بمقدار حول محور في كرة بلوخ. تطبيق بوابة X على ينتج ، وتطبيقها على ينتج ، لذا فهي عملية مشابهة لبوابة NOT الكلاسيكية، وتُعرف أيضاً بقلب البت. التمثيل المصفوفي لبوابة X هو:
qc = QuantumCircuit(1) # Prepare the single-qubit quantum circuit
# Apply a X gate to qubit 0
qc.x(0)
# Draw the circuit
qc.draw("mpl")
في IBM Quantum®، الحالة الأولية تُضبط على ، لذا فإن الدائرة الكمومية أعلاه بالتمثيل المصفوفي هي:
لننفّذ الآن هذه الدائرة باستخدام محاكي متجه الحالة.
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))
يُعرض المتجه العمودي كمتجه صفي، مع الأعداد المركبة (الجزء التخيلي يُشار إليه بـ ).
بوابة H
بوابة هادامارد هي دوران بمقدار حول محور يقع في منتصف المسافة بين محوري و على كرة بلوخ. تطبيق بوابة H على ينتج حالة تراكب مثل . التمثيل المصفوفي لبوابة H هو:
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit
# Apply an Hadamard gate to qubit 0
qc.h(0)
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.70710678+0.j, 0.70710678+0.j],
dims=(2,))
هذا هو:
حالة التراكب هذه شائعة ومهمة جداً لدرجة أنه أُعطيت رمزاً خاصاً بها:
بتطبيق البوابة على ، أنشأنا تراكباً بين و بحيث يعطي القياس في الأساس الحسابي (على طول المحور z في صورة كرة بلوخ) كل حالة باحتمالية متساوية.
حالة
ربما خمّنت أن هناك حالة مقابلة :
لإنشاء هذه الحالة، طبّق أولاً بوابة X للحصول على ، ثم طبّق بوابة H.
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit
# Apply a X gate to qubit 0
qc.x(0)
# Apply an Hadamard gate to qubit 0
qc.h(0)
# draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([ 0.70710678+0.j, -0.70710678+0.j],
dims=(2,))
هذا هو:
تطبيق البوابة على ينتج تراكباً متساوياً بين و ، لكن إشارة تكون سالبة.
3.2 الحالة الكمية لـ Qubit الواحد والتطور الوحداني
إجراءات جميع البوابات التي رأيناها حتى الآن كانت وحدانية (unitary)، وهذا يعني أنه يمكن تمثيلها بمؤثر وحداني. بمعنى آخر، يمكن الحصول على حالة الإخراج بتطبيق مصفوفة وحدانية على الحالة الابتدائية:
المصفوفة الوحدانية هي مصفوفة تحقق الشرط
من منظور عمليات الحاسوب الكمي، نقول إن تطبيق بوابة كمية على الـ qubit يُطوّر الحالة الكمية. ومن أبرز بوابات الـ qubit الواحد ما يلي.
بوابات باولي:
حيث يُحسب الضرب الخارجي كما يلي:
بوابات أخرى شائعة للـ qubit الواحد:
شرح مفصّل لمعنى هذه البوابات واستخداماتها متاح في دورة أساسيات المعلومات الكمية.
تمرين 1
استخدم Qiskit لإنشاء دوائر كمية تُعِدّ الحالات الموصوفة أدناه. ثم شغّل كل دائرة باستخدام محاكي متجه الحالة (statevector simulator) واعرض الحالة الناتجة على كرة بلوخ (Bloch sphere). كتحدٍّ إضافي، حاول أن تتوقع مسبقاً ما ستكون عليه الحالة النهائية بناءً على حدسك حول البوابات والدورانات على كرة بلوخ.
(1)
(2)
(3)
تلميح: يمكن استخدام بوابة Z بالطريقة التالية:
qc.z(0)
الحل:
### (1) XX|0> ###
# Create the single-qubit quantum circuit
qc = QuantumCircuit(1) ##your code goes here##
# Add a X gate to qubit 0
qc.x(0) ##your code goes here##
# Add a X gate to qubit 0
qc.x(0) ##your code goes here##
# Draw a circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))
3.3 القياس
القياس من الناحية النظرية موضوع بالغ التعقيد، لكن من الناحية العملية، القياس على المحور (كما تفعل جميع حواسيب IBM® الكمية) يُجبر حالة الـ qubit إما على الانهيار إلى أو إلى ، ونلاحظ النتيجة.
- هي احتمالية الحصول على عند القياس.
- هي احتمالية الحصول على عند القياس.
لذلك، يُسمى كلٌّ من و بسعة الاحتمال (probability amplitude). (راجع "قاعدة بورن")
على سبيل المثال، لديها احتمالية متساوية للانهيار إلى أو عند القياس. أما فلديها احتمالية 75% للانهيار إلى .
محاكي Qiskit Aer
الآن، لنقم بقياس دائرة تُعِدّ التراكب المتساوي الاحتمال المذكور أعلاه. يجب إضافة بوابات القياس، إذ يحاكي Qiskit Aer بشكل افتراضي العتاد الكمي المثالي (بدون ضوضاء). ملاحظة: يمكن لمحاكي Aer أيضاً تطبيق نموذج ضوضاء مبني على حاسوب كمي حقيقي. سنعود إلى نماذج الضوضاء لاحقاً.
# Create a new circuit with one qubits (first argument) and one classical bits (second argument)
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0) # Add the measurement gate
qc.draw(output="mpl")
أصبحنا الآن جاهزين لتشغيل دائرتنا على محاكي Aer. في هذا المثال، سنستخدم القيمة الافتراضية shots=1024، وهذا يعني أننا سنقيس 1024 مرة. ثم سنعرض هذه الأعداد في رسم بياني (histogram).
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'0': 521, '1': 503}
نلاحظ أن القيم 0 و 1 قد قِيست باحتمالية تقترب من 50% لكل منهما. وعلى الرغم من أن الضوضاء لم تُحاكَ هنا، إلا أن الحالات لا تزال احتمالية. لذا، رغم أننا نتوقع توزيعاً متساوياً تقريباً بنسبة 50-50، نادراً ما نحصل على هذه النتيجة بالضبط، تماماً كما أن رمي عملة معدنية 100 مرة نادراً ما يُعطي 50 وجهاً لكل جانب.
4. البوابات الكمومية متعددة الكيوبت والتشابك
4.1 الدائرة الكمومية متعددة الكيوبت
يمكننا إنشاء دائرة كمومية بكيوبتين باستخدام الكود التالي. سنُطبّق بوابة H على كل كيوبت.
# Create the two qubits quantum circuit
qc = QuantumCircuit(2)
# Apply an H gate to qubit 0
qc.h(0)
# Apply an H gate to qubit 1
qc.h(1)
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j],
dims=(2, 2))
ملاحظة: ترتيب البتات في Qiskit
يستخدم Qiskit ترميز Little Endian عند ترتيب الكيوبتات والبتات، ما يعني أن الكيوبت 0 هو البت الموجود في أقصى اليمين في سلاسل البتات. مثال: يعني أن q0 في الحالة وأن q1 في الحالة . انتبه لأن بعض المراجع في الحوسبة الكمومية تستخدم ترميز Big Endian (الكيوبت 0 في أقصى اليسار)، وكذلك كثير من مراجع ميكانيكا الكم.
شيء آخر جدير بالملاحظة هو أنه عند تمثيل الدائرة الكمومية، يُوضع دائمًا في أعلى الدائرة. مع أخذ هذا في الحسبان، يمكن كتابة ال حالة الكمومية للدائرة أعلاه كجداء تنسوري لحالات كيوبت منفردة.
( )
الحالة الابتدائية في Qiskit هي ، فعند تطبيق على كل كيوبت تتحول إلى حالة تراكب متساوٍ.
قاعدة القياس هي نفسها كما في حالة الكيوبت الواحد؛ احتمالية قياس هي .
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

الآن لنقِس هذه الدائرة.
# Create a new circuit with two qubits (first argument) and two classical bits (second argument)
qc = QuantumCircuit(2, 2)
# Apply the gates
qc.h(0)
qc.h(1)
# Add the measurement gates
qc.measure(0, 0) # Measure qubit 0 and save the result in bit 0
qc.measure(1, 1) # Measure qubit 1 and save the result in bit 1
# Draw the circuit
qc.draw(output="mpl")
الآن سنستخدم محاكي Aer مجددًا للتحقق تجريبيًا من أن الاحتماليات النسبية لجميع الحالات الممكنة متساوية تقريبًا.
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'10': 262, '01': 246, '00': 265, '11': 251}
كما هو متوقع، قيست الحالات و و و بنسبة تقارب 25% لكل منها.
4.2 البوابات الكمومية متعددة الكيوبت
بوابة CNOT
بوابة CNOT (أو "NOT المتحكَّم به" أو CX) هي بوابة ثنائية الكيوبت، أي أن عملها يشمل كيوبتين في آنٍ واحد: الكيوبت المتحكِّم والكيوبت الهدف. تقلب بوابة CNOT الكيوبت الهدف فقط عندما يكون الكيوبت المتحكِّم في الحالة .
| المدخل (الهدف، المتحكِّم) | المخرج (الهدف، المتحكِّم) |
|---|---|
| 00 | 00 |
| 01 | 11 |
| 10 | 10 |
| 11 | 01 |
لنحاكِ أولًا تأثير هذه البوابة ثنائية الكيوبت عندما يكون كلٌّ من q0 وq1 في الحالة ، ونستخرج متجه الحالة الناتج. الصيغة المستخدمة في Qiskit هي qc.cx(control qubit, target qubit).
# Create a circuit with two quantum registers and two classical registers
qc = QuantumCircuit(2, 2)
# Apply the CNOT (cx) gate to a |00> state.
qc.cx(0, 1) # Here the control is set to q0 and the target is set to q1.
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
dims=(2, 2))
كما هو متوقع، تطبيق بوابة CNOT على لم يغيّر الحالة، لأن الكيوبت المتحكِّم كان في الحالة . لنعُد إلى عملية CNOT. هذه المرة سنُطبّق بوابة CNOT على ونرى ما سيحدث.
qc = QuantumCircuit(2, 2)
# q0=1, q1=0
qc.x(0) # Apply a X gate to initialize q0 to 1
qc.cx(0, 1) # Set the control bit to q0 and the target bit to q1.
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
dims=(2, 2))
بتطبيق بوابة CNOT، تحوّلت الحالة إلى .
لنتحقق من هذه النتائج بتشغيل الدائرة على محاكٍ.
# Add measurements
qc.measure(0, 0)
qc.measure(1, 1)
# Draw the circuit
qc.draw(output="mpl")
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'11': 1024}
يُظهر الناتج أن قيست باحتمالية 100%.
4.3 التشابك الكمومي والتنفيذ على جهاز كمومي حقيقي
لنبدأ بالتعرف على حالة متشابكة بعينها ذات أهمية خاصة في الحوسبة الكمومية، ثم سنعرّف مصطلح "التشابك":
وتُعرف هذه الحالة بـ حالة Bell.
الحالة المتشابكة هي حالة تتكون من حالتين كموميتين و لا يمكن تمثيلها كجداء تنسو ري للحالات الكمومية الفردية.
إذا كانت أدناه تضم حالتين و؛
فإن الجداء التنسوري لهاتين الحالتين هو