อัลกอริทึมของ Deutsch
อัลกอริทึมของ Deutsch แก้ปัญหา parity สำหรับกรณีพิเศษที่ ในบริบทของการคำนวณเชิงควอนตัม ปัญหานี้บางครั้งเรียกว่า ปัญหาของ Deutsch และเราจะใช้ชื่อนี้ตลอดบทเรียน
พูดอย่างเจาะจง อินพุตแทนด้วยฟังก์ชัน จากหนึ่งบิตไปยังหนึ่งบิต มีฟังก์ชันในรูปแบบนี้อยู่สี่ตัว:
ฟังก์ชันตัวแรกและตัวสุดท้ายเป็น ค่าคงที่ (constant) ส่วนสองตัวตรงกลางเป็น สมดุล (balanced) หมายความว่าค่าเอาต์พุตทั้งสองที่เป็นไปได้ปรากฏในจำนวนครั้งเท่ากันเมื่อวนผ่านอินพุตทั้งหมด ปัญหาของ Deutsch คือการตัดสินว่าฟังก์ชันอินพุตอยู่ในหมวดใด: constant หรือ balanced
ถ้ามองฟังก์ชันอินพุต ในปัญหาของ Deutsch ว่าเป็นตัวแทนของการเข้าถึงสตริงแบบสุ่ม เราจะนึกถึงสตริงสองบิต:
เมื่อมองในมุมนี้ ปัญหาของ Deutsch คือการคำนวณ parity (หรือเทียบเท่าคือ exclusive-OR) ของสองบิต
อัลกอริทึมคลาสสิกทุกตัวที่แก้ปัญหานี้ได้อย่างถูกต้องต้องสอบถามทั้งสองบิต: และ ตัวอย่างเช่น ถ้าเรารู้ว่า คำตอบยังอาจเป็น หรือ ก็ได้ ขึ้นอยู่กับว่า หรือ ตามลำดับ ทุกกรณีอื่นก็คล้ายกัน การรู้เพียงหนึ่งในสองบิตไม่ให้ข้อมูลใด ๆ เกี่ยวกับ parity ของบิตทั้งสองเลย ดังนั้น วงจร Boolean ที่อธิบายในส่วนก่อนหน้าจึงเป็นสิ่งที่ดีที่สุดในแง่ของจำนวน query ที่ต้องใช้เพื่อแก้ปัญหานี้
การอธิบายด้วยวงจรควอนตัม
อัลกอริทึมของ Deutsch แก้ปัญหาของ Deutsch โดยใช้ query เดียว ซึ่งแสดงให้เห็นถึงข้อได้เปรียบเชิงปริมาณของควอนตัมเหนือคลาสสิก ข้อได้เปรียบนี้อาจดูไม่มากนัก — หนึ่ง query แทนที่จะสอง — แต่เราต้องเริ่มต้นที่ไหนสักที่ ความก้าวหน้าทางวิทยาศาสตร์บางครั้งมีจุดเริ่มต้นที่ดูธรรมดา
นี่คือวงจรควอนตัมที่อธิบายอัลกอริทึมของ Deutsch:
การวิเคราะห์
เพื่อวิเคราะห์อัลกอริทึมของ Deutsch เราจะติดตามการกระทำของวงจรข้างต้นและระบุสถานะของ Qubit ตามเวลาที่ภาพนี้แสดง:
สถานะเริ่มต้นคือ และการดำเนินการ Hadamard สองครั้งทางด้านซ้ายของวงจรแปลงสถานะนี้เป็น
(เช่นเคย เราปฏิบัติตามข้อตกลงการจัดลำดับ Qubit ของ Qiskit ที่วาง Qubit บนไว้ทางขวาและ Qubit ล่างไว้ทางซ้าย) การเขียนสถานะผลคูณแบบกระจายบางส่วน (เหลือสถานะของ Qubit 1 แยกไว้) อาจดูไม่เป็นธรรมชาติ แต่จะทำให้นิพจน์ต่อ ๆ ไปกระชับมากขึ้น
ต่อมา Gate ถูกดำเนินการ ตามนิยามของ Gate ค่าของฟังก์ชัน สำหรับสถานะคลาสสิกของ Qubit บน/ขวาสุดจะถูก XOR เข้ากับ Qubit ล่าง/ซ้ายสุด ซึ่งแปลง เป็นสถานะ
เราสามารถลดรูปนิพจน์นี้ได้โดยสังเกตว่าสูตร
ใช้ได้กับทั้งสองค่าที่เป็นไปได้ อธิบายอย่างละเอียด ทั้งสองกรณีมีดังนี้
ดังนั้น เราสามารถแสดง ในอีกรูปแบบหนึ่งได้:
มีบางอย่างน่าสนใจเกิดขึ้น! แม้ว่าการกระทำของ Gate บนสถานะฐานมาตรฐานจะปล่อย Qubit บน/ขวาสุดไว้ตามเดิมและ XOR ค่าฟังก์ชันเข้ากับ Qubit ล่าง/ซ้ายสุด แต่ที่นี่เราเห็นว่าสถานะของ Qubit บน/ขวาสุดเปลี่ยนแปลง (โดยทั่วไป) ในขณะที่สถานะของ Qubit ล่าง/ซ้ายสุดยังคงเดิม — โดยอยู่ในสถานะ ทั้งก่อนและหลังการดำเนินการ Gate ปรากฏการณ์นี้เรียกว่า phase kickback และเราจะพูดถึงมันอีกในเร็ว ๆ นี้
ด้วยการลดรูปขั้นสุดท้าย ซึ่งคือการดึงตัวประกอบ ออกนอกผลรวม เราได้นิพจน์สถานะ ดังนี้:
สังเกตว่าในนิพจน์นี้ เรามี ในเลขชี้กำลังของ แทนที่จะเป็น ซึ่งเป็นสิ่งที่เราอาจคาดจากมุมมองพีชคณิตล้วน ๆ แต่เราได้ผลลัพธ์เดียวกันทั้งสองทาง เพราะค่า สำหรับจำนวนเต็ม ใด ๆ ขึ้นอยู่กับว่า เป็นคู่หรือคี่เท่านั้น
การใช้ Hadamard Gate สุดท้ายกับ Qubit บนทำให้เราได้สถานะ
ซึ่งนำไปสู่ผลลัพธ์ที่ถูกต้องด้วยความน่าจะเป็น เมื่อวัด Qubit ขวา/บนสุด
หมายเหตุเพิ่มเติมเกี่ยวกับ phase kickback
ก่อนที่จะดำเนินต่อ มาดูการวิเคราะห์ข้างต้นจากมุมมองที่ต่างออกไปเล็กน้อย ซึ่งอาจช่วยให้เข้าใจปรากฏการณ์ phase kickback ได้ดีขึ้น
อันดับแรก สังเกตว่าสูตรต่อไปนี้ใช้ได้สำหรับทุกค่าของบิต
สูตรนี้สามารถตรวจสอบได้โดยแทนค่าสองค่าที่เป็นไปได้ และ :
โดยใช้สูตรนี้ เราจะเห็นว่า
สำหรับทุกค่าของบิต เนื่องจากสูตรนี้เป็นจริงสำหรับ และ เราจึงเห็นโดยความเป็นเชิงเส้นว่า
สำหรับเวกเตอร์สถานะ Qubit ทั้งหมด และดังนั้น
กุญแจที่ทำให้สิ่งนี้ได้ผลคือ ในทางคณิตศาสตร์ เวกเตอร์ คือ eigenvector ของเมทริกซ์ ที่มี eigenvalue เป็น
เราจะพูดถึง eigenvector และ eigenvalue อย่างละเอียดในบทเรียนถัดไปเรื่อง การประมาณ phase และการแยกตัวประกอบ ซึ่ง phase kickback จะถูกนำไปใช้กับการดำเนินการ unitary อื่น ๆ
โดยระลึกว่าสเกลาร์ลอยผ่าน tensor product ได้อิสระ เราพบวิธีอื่นในการให้เหตุผลว่าการดำเนินการ แปลง เป็น ในการวิเคราะห์ข้างต้นอย่างไร:
การนำไปใช้งานใน Qiskit
ตอนนี้มาดูกันว่าเราสามารถนำอัลกอริทึมของ Deutsch ไปใช้งานใน Qiskit ได้อย่างไร เราจะเริ่มด้วยการตรวจสอบเวอร์ชันแล้วทำการ import ที่จำเป็นสำหรับการนำไปใช้งานนี้โดยเฉพาะ สำหรับการนำไปใช้งานของอัลกอริทึมอื่น ๆ ที่ตามมา เราจะ import ที่จำเป็นแยกกันเพื่อความเป็นโมดูลาร์มากขึ้น
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer
from qiskit import __version__
print(__version__)
2.1.1
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
ก่อนอื่นเราจะนิยามวงจรควอนตัมที่นำ query gate สำหรับฟังก์ชันใดฟังก์ชันหนึ่งในสี่ตัว หรือ จากหนึ่งบิตไปยังหนึ่งบิตที่อธิบายไว้ก่อนหน้า ดังที่กล่าวถึงแล้ว การนำ query gate ไปใช้งานไม่ใช่ส่วนหนึ่งของอัลกอริทึมของ Deutsch จริง ๆ ที่นี่เราแค่แสดงวิธีหนึ่งในการเตรียมอินพุต ในรูปแบบการนำ query gate ไปใช้งานด้วยวงจร
def deutsch_function(case: int):
# This function generates a quantum circuit for one of the 4 functions
# from one bit to one bit
if case not in [1, 2, 3, 4]:
raise ValueError("`case` must be 1, 2, 3, or 4.")
f = QuantumCircuit(2)
if case in [2, 3]:
f.cx(0, 1)
if case in [3, 4]:
f.x(1)
return f
เราสามารถดูว่าแต่ละวงจรหน้าตาเป็นอย่างไรโดยใช้เมธอด draw นี่คือวงจรสำหรับฟังก์ชัน
display(deutsch_function(3).draw(output="mpl"))
ต่อมาเราจะสร้างวงจรควอนตัมจริงสำหรับอัลกอริทึมของ Deutsch โดยแทนที่ query gate ด้วยการนำ Circuit ไปใช้งานที่รับเป็นอาร์กิวเมนต์ ในไม่ช้าเราจะเสียบหนึ่งในสี่วงจรที่นิยามโดยฟังก์ชัน deutsch_function ที่เราสร้างก่อนหน้า
มีการเพิ่ม Barrier เพื่อแสดงการแบ่งแยกทางภาพระหว่างการนำ query gate ไปใช้งานและส่วนที่เหลือของวงจร
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in Deutsch's algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.barrier()
qc.compose(function, inplace=True)
qc.barrier()
qc.h(range(n))
qc.measure(range(n), range(n))
return qc
เราสามารถดูว่าวงจรหน้าตาเป็นอย่างไรอีกครั้งโดยใช้เมธอด draw
display(compile_circuit(deutsch_function(3)).draw(output="mpl"))
สุดท้าย เราจะสร้างฟังก์ชันที่รันวงจรที่นิยามไว้ก่อนหน้าหนึ่งครั้งและแสดงผลลัพธ์ที่เหมาะสม: "constant" หรือ "balanced"
def deutsch_algorithm(function: QuantumCircuit):
# Determine if a one-bit function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if measurements[0] == "0":
return "constant"
return "balanced"
ตอนนี้เราสามารถรันอัลกอริทึมของ Deutsch บนฟังก์ชันใดหนึ่งในสี่ตัวที่นิยามไว้ข้างต้นได้แล้ว
f = deutsch_function(3)
display(deutsch_algorithm(f))
'balanced'