الخوارزميات الكمية: بحث غروفر وتطبيقاته
Atsushi Matsuo (10 مايو 2024)
تحميل ملف PDF للمحاضرة الأصلية. لاحظ أن بعض مقاطع الكود قد تكون قديمة لأنها صور ثابتة.
الوقت التقريبي للتشغيل على وحدة المعالجة الكمية (QPU) هو ثانيتان.
1. مقدمة إلى خوارزمية غروفر
هذا الدفتر هو الرابع في سلسلة محاضرات حول مسار الاستخدام الفعلي للحوسبة الكمية. سنتعلم في هذا الدفتر عن خوارزمية غروفر.
تُعدّ خوارزمية غروفر من أشهر الخوارزميات الكمية بسبب تسريعها التربيعي مقارنةً بأساليب البحث الكلاسيكية. في الحوسبة الكلاسيكية، يتطلب البحث في قاعدة بيانات غير مرتبة تحتوي على عنصر تعقيداً زمنياً من الرتبة ، أي أنه في أسوأ الحالات قد يتعين فحص كل عنصر على حدة. غير أن خوارزمية غروفر تتيح تحقيق هذا البحث في زمن ، مستعينةً بمبادئ ميكانيكا الكم لتحديد العنصر المستهدف بكفاءة أعلى.
تستخدم الخوارزمية تضخيم السعة (Amplitude Amplification)، وهي عملية ترفع احتمالية سعة الحالة الصحيحة في التراكب الكمي، مما يجعلها أكثر احتمالاً عند القياس. هذا التسريع يجعل خوارزمية غروفر ذات قيمة في تطبيقات عديدة تتخطى مجرد البحث في قواعد البيانات، لا سيما حين يكون حجم البيانات كبيراً. يمكن الاطلاع على شرح مفصّل للخوارزمية في دفتر خوارزمية غروفر.
البنية الأساسية لخوارزمية غروفر
تتكوّن خوارزمية غروفر من أربعة مكوّنات رئيسية:
- التهيئة: إعداد التراكب الكمي على جميع الحالات الممكنة.
- الأوراكل: تطبيق دالة أوراكل تُعلّم الحالة المستهدفة بقلب طورها.
- عامل الانتشار: تطبيق سلسلة من العمليات لتضخيم احتمالية الحالة المُعلَّمة.
كل خطوة من هذه الخطوات تؤدي دوراً محورياً في جعل الخوارزمية تعمل بكفاءة. الشرح التفصيلي لكل خطوة يأتي لاحقاً.
2. تطبيق خوارزمية غروفر
2.1 الإعداد
استيراد المكتبات اللازمة وإعداد البيئة لتشغيل الدائرة الكمية.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
%config InlineBackend.figure_format = 'svg' # Makes the images look nice
# importing Qiskit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
# import basic plot tools
from qiskit.visualization import plot_histogram
الخطوة 1: تعيين المسألة إلى دوائر وعوامل كمية
تأمّل قائمةً من 4 عناصر، وهدفنا إيجاد فهرس العنصر الذي يستوفي شرطاً محدداً. على سبيل المثال، نريد إيجاد فهرس العنصر المساوي للعدد 2. في هذا المثال، تمثّل الحالة الكمية فهرس العنصر الذي يحقق هذا الشرط، إذ تشير إلى الموضع الذي توجد فيه القيمة 2.
الخطوة 2: التحسين لأجهزة العتاد المستهدفة
1: التهيئة
في خطوة التهيئة، نُنشئ تراكباً كمياً لجميع الحالات الممكنة. يتحقق ذلك بتطبيق بوابة هادامار على كل كيوبت في سجل من كيوبت، مما ينتج تراكباً متساوياً لـ حالة. رياضياً، يمكن التعبير عن ذلك كما يلي:
حيث هو العدد الإجمالي للحالات الممكنة. كذلك نغيّر حالة بت المساعدة (ancilla) إلى .
def initialization(circuit):
# Initialization
n = circuit.num_qubits
# For input qubits
for qubit in range(n - 1):
circuit.h(qubit)
# For the ancilla bit
circuit.x(n - 1)
circuit.h(n - 1)
circuit.barrier()
return circuit
n = 2
qr = QuantumRegister(n, "q")
anc = QuantumRegister(1, "ancillary")
initialization_circuit = QuantumCircuit(qr, anc)
initialization(initialization_circuit)
initialization_circuit.draw(output="mpl", idle_wires=False)
2: الأوراكل
الأوراكل هو الجزء الأساسي في خوارزمية غروفر. يُعلّم الحالة المستهدفة بتطبيق إزاحة طور، عادةً بقلب إشارة السعة المرتبطة بتلك الحالة. يكون الأوراكل في الغالب خاصاً بالمسألة المطروحة ويُبنى وفق معايير تحديد الحالة المستهدفة. رياضياً، يطبّق الأوراكل التحويل التالي:
f(x) = \begin\{cases\} 1, & \text\{if \} x = x_\{\text\{target}\} \\ 0, & \text\{otherwise\} \end\{cases\}
يتحقق قلب الطور هذا بتطبيق إشارة سالبة على سعة الحالة المستهدفة عبر آلية رفس الطور (Phase Kickback).
def oracle(circuit):
circuit.x(1)
circuit.ccx(0, 1, 2)
circuit.x(1)
circuit.barrier()
n = 2
qr = QuantumRegister(n, "q")
anc = QuantumRegister(1, "ancillary")
oracle_circuit = QuantumCircuit(qr, anc)
oracle(oracle_circuit)
oracle_circuit.draw(output="mpl", idle_wires=False)
3: عامل الانتشار
تضخيم السعة هو ما يُميّز خوارزمية غروفر عن البحث الكلاسيكي. بعد أن يُعلّم الأوراكل الحالة المستهدفة، نطبّق سلسلة من العمليات التي ترفع سعة هذه الحالة المُعلَّمة، مما يجعل احتمال قياسها أعلى. يتحقق ذلك عبر عامل الانتشار، الذي يُنفّذ فعلياً انعكاساً حول متوسط السعة. العملية الرياضية هي:
حيث هو عامل الانتشار، و هي مصفوفة الوحدة، و هي حالة التراكب المتساوي. يُطبَّق تركيب الأوراكل وعامل الانتشار تقريباً مرة لتحقيق أقصى احتمال لقياس الحالة المُعلَّمة.
def diffusion(circuit):
input_qubits = circuit.num_qubits - 1
circuit.h(range(0, input_qubits))
circuit.x(range(0, input_qubits))
circuit.h(input_qubits - 1)
circuit.mcx([i for i in range(0, input_qubits - 1)], input_qubits - 1)
circuit.h(input_qubits - 1)
circuit.x(range(0, input_qubits))
circuit.h(range(0, input_qubits))
circuit.barrier()
n = 2
qr = QuantumRegister(n, "q")
anc = QuantumRegister(1, "ancillary")
diffusion_circuit = QuantumCircuit(qr, anc)
diffusion(diffusion_circuit)
diffusion_circuit.draw(output="mpl", idle_wires=False)
2.2 مثال على بحث غروفر بكيوبتين
n = 2
qr = QuantumRegister(n, "q")
anc = QuantumRegister(1, "ancillary")
meas = ClassicalRegister(3, "meas")
grover_circuit = QuantumCircuit(qr, anc, meas)
# the number of iterations
num_iterations = 1
# Let's do Grover search
initialization(grover_circuit)
for i in range(0, num_iterations):
oracle(grover_circuit)
diffusion(grover_circuit)
# Clear the ancilla bit
grover_circuit.h(n)
grover_circuit.x(n)
grover_circuit.measure_all(add_bits=False)
grover_circuit.draw(output="mpl", idle_wires=False)