ข้ามไปยังเนื้อหาหลัก

การทดลองควอนตัมแรกของคุณ

บทนำ

ในวิดีโอต่อไปนี้ Olivia Lanes จะพาคุณผ่านเนื้อหาในบทเรียนนี้ หรือจะเปิด วิดีโอ YouTube ของบทเรียนนี้ในหน้าต่างแยกต่างหากก็ได้

ตอนนี้คุณได้รัน Circuit ควอนตัมแรกแล้ว และเรียนรู้พื้นฐานของการคำนวณควอนตัม: วิธีแสดงแทนสถานะควอนตัม วิธีที่ Gate ทำงานกับสถานะเหล่านั้น และวิธีที่คุณสมบัติควอนตัมอย่าง superposition และ entanglement มีส่วนเกี่ยวข้อง ถึงเวลาแล้วที่จะนำทุกอย่างมาใช้งานจริงและแก้ปัญหาแรกบนคอมพิวเตอร์ควอนตัม

เราจะสำรวจภูมิทัศน์กว้างๆ ของปัญหาที่เหมาะกับควอนตัมในบทเรียนต่อๆ ไป ตอนนี้เราจะมุ่งเน้นไปที่ปัญหาในโดเมนการจำลองธรรมชาติ: การใช้คอมพิวเตอร์ควอนตัมเป็นตัวแทนที่สะอาดกว่าและควบคุมได้ดีกว่าสำหรับระบบควอนตัมในธรรมชาติ ในความเป็นจริง นี่คือการประยุกต์ใช้แรกที่ Richard Feynman นึกถึงสำหรับคอมพิวเตอร์ควอนตัมในทศวรรษ 1980 ดังที่เขากล่าวไว้อย่างโด่งดังว่า: "ธรรมชาติไม่ใช่คลาสสิก ไอ้หน้าหนา และถ้าคุณต้องการทำการจำลองธรรมชาติ คุณก็ต้องทำให้มันเป็นกลศาสตร์ควอนตัม..."

ในบทเรียนนี้ เราจะทำตามหลักการนั้นเพื่อจำลองปฏิกิริยาระหว่างสปินสองตัว ซึ่งคุณสามารถคิดว่าเป็นแม่เหล็กขนาดเล็ก ขึ้นอยู่กับเครื่องหมายของปฏิกิริยา พวกมันอาจชอบที่จะจัดแนวและชี้ไปในทิศทางเดียวกัน หรือต้านแนวและชี้ไปในทิศทางตรงข้าม เราจะมุ่งเน้นกรณีหลังเพราะมักนำไปสู่พฤติกรรมที่น่าสนใจ — และท้าทายกว่า เมื่อเข้าใจระบบ Qubit สองตัวเล็กๆ นี้แล้ว เราจะแสดงให้เห็นว่าแนวคิดเดียวกันสามารถขยายขนาดได้ ทำให้คอมพิวเตอร์ควอนตัมสามารถใช้ประโยชน์จาก exponential scaling เมื่อจำลองระบบสปินขนาดใหญ่

แม่เหล็กสองตัวที่มีปฏิกิริยากัน

สำหรับปัญหานี้ เราจะใช้ Qubit สองตัว ตัวละหนึ่งสำหรับแต่ละสปินในโมเดลของเรา แต่ละสปินสามารถชี้ขึ้น (สถานะ Qubit 0|0\rangle) ชี้ลง (สถานะ Qubit 1|1\rangle) หรืออยู่ใน superposition ของทั้งสองสถานะ

ถ้าสปินมีปฏิกิริยา antiferromagnetic หมายความว่าพวกมันต้องการต้านแนว ดังนั้นเมื่อตัวหนึ่งชี้ขึ้น อีกตัวก็ต้องการชี้ลง และในทางกลับกัน

สมมติว่ามีสนามแม่เหล็กชี้จากซ้ายไปขวาในระบบของเรา เพราะสนามนี้ชี้ขวางทิศทางขึ้น-ลงปกติของสปิน จึงเรียกว่าสนามตามขวาง สนามนี้สามารถพลิกสปินได้ ซึ่งทำให้การกำหนดค่าพลังงานต่ำสุดเป็น superposition เฉพาะของการจัดเรียงสปินแบบขึ้น-ลง แทนที่จะเป็นรูปแบบสปินที่แน่นอนเพียงแบบเดียว

เราสามารถอธิบายผลกระทบทั้งหมดเหล่านี้โดยใช้วัตถุทางคณิตศาสตร์ที่เรียกว่า Hamiltonian Hamiltonian บอกเราถึงพลังงานของระบบสำหรับการจัดเรียงสปินที่กำหนด:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

โดยที่ JJ คือสัมประสิทธิ์ควบคุมความแข็งแกร่งของปฏิกิริยาระหว่างสปิน และ hxh_x คือสัมประสิทธิ์สำหรับความแข็งแกร่งของสนามแม่เหล็กภายนอก Z1Z0Z_1 Z_0 ให้รางวัลหรือลงโทษสปินขึ้นอยู่กับว่ามันจัดแนวหรือต้านแนวกัน และ X0X_0 และ X1X_1 แสดงถึงผลการพลิกสปินของสนามแม่เหล็ก

ในฟิสิกส์ ระบบมักตกลงสู่สถานะที่มีพลังงานต่ำที่สุด เรียกว่าสถานะพื้น การหาสถานะพลังงานต่ำสุดนี้เป็นปัญหาทั่วไป แต่ต้องใช้เทคนิคการหาค่าที่เหมาะสมที่สุดซึ่งเกินขอบเขตของบทเรียนนี้

แทนที่จะทำอย่างนั้น เราจะถามคำถามที่ง่ายกว่า: ถ้าเราเตรียมสปินให้อยู่ในสถานะหนึ่งๆ พลังงานของสถานะนั้นคือเท่าไหร่?

เพื่อตอบคำถามนี้ เราจะ:

  1. เตรียมสปินให้อยู่ในสถานะที่เราเลือก
  2. วัดพลังงานของสถานะนั้นโดยใช้ Hamiltonian ด้านบน

นี่คือประเภทของการคำนวณที่ปรากฏใน algorithm ควอนตัมขนาดใหญ่ เช่น variational algorithms ซึ่งคุณสามารถสำรวจในคอร์สต่อๆ ไปได้

การติดตั้งใช้งาน Qiskit

ก่อนจะเริ่มเขียนโค้ด เราต้องปูพื้นบริบทก่อนสักเล็กน้อย เมื่อรัน Circuit ควอนตัม เราจะสิ้นสุดด้วยการวัด Qubit เสมอ แต่มีคำถามสองประเภทที่แตกต่างกันที่เราอาจต้องการถามเกี่ยวกับผลของการวัดนั้น: บางครั้งเราแค่ต้องการรู้ว่าสถานะของ Qubit เป็นอะไร บางครั้งเราต้องการรู้ว่า สำหรับสถานะควอนตัมนั้น ค่าของปริมาณทางกายภาพ เช่น พลังงาน คือเท่าไหร่?

ใน Qiskit คำถามสองประเภทนี้ถูกจัดการด้วยเครื่องมือสองอย่างที่แตกต่างกัน เรียกว่า primitives

Sampler ตอบคำถามประเภทแรก มันรัน Circuit หลายครั้งและบอกเราว่าเราวัดผลลัพธ์แต่ละแบบบ่อยแค่ไหน เช่น 00, 01, 10 หรือ 11 ผลลัพธ์คือ histogram ที่แสดงความน่าจะเป็นของผลลัพธ์การวัดแต่ละแบบ

Estimator ตอบคำถามประเภทที่สอง แทนที่จะให้ histogram มันรวมการวัดหลายครั้งเบื้องหลังเพื่อคำนวณตัวเลขเดียว เช่น พลังงานของสถานะตาม Hamiltonian ที่เราให้

เพื่อให้เข้าใจว่าเมื่อไหร่และทำไมเราจึงใช้เครื่องมือแต่ละอย่าง เราจะดำเนิน workflow ที่สมบูรณ์สองแบบ (เรียกว่า "Qiskit patterns") กับระบบ Qubit สองตัวเดียวกัน

Workflow ของ Qiskit patterns

Workflow ของ Qiskit patterns คือกรอบการทำงานทั่วไปที่เราใช้แก้ปัญหาควอนตัมด้วย Qiskit มันแบ่งงานการคำนวณควอนตัมออกเป็นสี่ขั้นตอน:

  1. Map ปัญหาไปสู่โมเดลที่สามารถแสดงแทนด้วย Circuit ควอนตัมได้
  2. Optimize Circuit ให้รันบน Backend เฉพาะได้
  3. Execute Circuit ที่ optimize แล้วบน Backend ที่เลือก
  4. Post-process ข้อมูลการวัดดิบ

การทดลองที่ 1: ใช้ Sampler วัดสถานะ

Map

โดยทั่วไป ขั้นตอน mapping คือที่ที่เราคิดหาวิธีแสดงปัญหาในโลกจริงในรูปของ Qubit, operator และการวัด ในหลายแอปพลิเคชัน นี่เป็นส่วนที่ยุ่งยากและซับซ้อนที่สุดของ workflow — แม้แต่คำถามง่ายๆ เช่น "แต่ละ Qubit แทนอะไร?" ก็ไม่เสมอไปที่จะมีคำตอบที่ตรงไปตรงมา

อย่างไรก็ตาม ในการทดลองนี้ mapping ถูกทำให้เรียบง่ายโดยตั้งใจ แต่ละ degree of freedom ทางกายภาพ map โดยตรงไปยัง Qubit เดียว เพราะการสอดคล้องแบบหนึ่งต่อหนึ่งนี้ ขั้นตอน mapping จึงลดลงเหลือแค่การเลือกสถานะควอนตัมที่ต้องการเตรียมและเขียน Circuit ที่เตรียมและวัดสถานะนั้น

ที่นี่ เราจะเตรียม Bell state แบบ entangled คล้ายกับที่ทำในบทเรียนแรกของคอร์สนี้:

Ψ=12(1001)\vert\Psi\rangle = \frac{1}{\sqrt{2}}(\vert 10\rangle - \vert 01\rangle)
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Import Qiskit primitives
from qiskit import QuantumCircuit

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)

# Measure state
qc.measure_all()

# Draw circuit
qc.draw("mpl")

Output of the previous code cell

Optimize

ก่อนที่เราจะรัน Circuit บนคอมพิวเตอร์ควอนตัม (หรือ Simulator ถ้าใช้เวลาฟรีบนคอมพิวเตอร์ควอนตัมจริงสำหรับเดือนนั้นหมดแล้ว) เราต้องเตรียมมันให้พร้อมสำหรับการ execute ขั้นตอนนี้เรียกว่า optimization (หมายเหตุ: การใช้คำว่า "optimization" นี้อาจทำให้สับสน ในการคำนวณควอนตัม optimization problems หมายถึงคลาสของปัญหาเฉพาะ ที่นี่เราใช้ optimization เพื่ออธิบายขั้นตอนการเตรียมที่จำเป็นที่ทุก Circuit ควอนตัมต้องผ่านก่อนที่จะรันบน Hardware ได้อย่างมีประสิทธิภาพ)

ระหว่าง optimization:

  1. เราเลือก Backend — ไม่ว่าจะเป็นคอมพิวเตอร์ควอนตัมจริงหรือ Simulator
  2. เรากำหนด Qubit ของ Circuit ให้กับ Qubit ทางกายภาพบน Device
  3. เราเขียน Circuit ใหม่โดยใช้เฉพาะ Gate ที่คอมพิวเตอร์ควอนตัมสามารถ perform ได้จริง
  4. เพิ่มเทคนิค error mitigation และ suppression แบบ optional เพื่อลดผลของ Noise

ใน Qiskit สิ่งนี้ถูกจัดการโดยอัตโนมัติโดย Transpiler เมื่อคุณเลือก Backend Transpiler จะทำงานทั้งหมดเพื่อให้ Circuit ของคุณพร้อมสำหรับการ execute โดยที่คุณไม่ต้องปรับ Gate หรือการกำหนด Qubit ด้วยตัวเอง Transpiler ยังมี optimization level ต่างๆ ซึ่งสามารถช่วยลด error ได้ถ้าจำเป็น การ optimization ทำในหลายขั้นตอนที่เรียกว่า 'passes' ดังนั้น optimization นี้จะถูกจัดการโดย pass_manager ในโค้ดด้านล่าง หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ error และ error mitigation ดูคอร์ส Quantum Computing in Practice ของ Olivia Lane

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

## Load the Qiskit Runtime service
# QiskitRuntimeService.save_account(
# channel="ibm_quantum_platform",
# token="YOUR_TOKEN_HERE",
# overwrite=True,
# set_as_default=True,
# )
# service = QiskitRuntimeService(channel="ibm_quantum_platform")

# Or load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)
ibm_fez
# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Output of the previous code cell

Execute

ตอนนี้เราพร้อม execute แล้ว! เราจะโหลด Sampler แล้วส่ง job ไปยัง Backend

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

หรือถ้าใช้ Simulator สามารถ uncomment และรัน Cell นี้แทนได้:

## Load the backend sampler
# from qiskit.primitives import BackendSamplerV2

## Load the Aer simulator and generate a noise model based on the currently-selected backend.
# from qiskit_aer import AerSimulator
# from qiskit_aer.noise import NoiseModel

# noise_model = NoiseModel.from_backend(backend)

## Define a simulator using Aer, and use it in Sampler.
# backend_sim = AerSimulator(noise_model=noise_model)
# sampler_sim = BackendSamplerV2(backend=backend_sim)

## Alternatively, load a fake backend with generic properties and define a simulator.
## backend_gen = GenericBackendV2(num_qubits=18)
## sampler_gen = BackendSamplerV2(backend=backend_gen)
job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

Post-process

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)
counts =  {'10': 49, '01': 50, '11': 1}

Output of the previous code cell

เราเห็นว่า count ส่วนใหญ่อยู่ใน 01 หรือ 10 ซึ่งหมายความว่าเมื่อ Qubit หนึ่งถูกวัดว่าเป็น 0 อีกตัวเป็น 1 และในทางกลับกัน สิ่งนี้สอดคล้องกับ Bell state Ψ\vert \Psi^- \rangle ที่เราเตรียมไว้

การทดลองที่ 2: ใช้ Estimator วัดพลังงาน

ตอนนี้ที่เราได้เห็นวิธี sample สถานะควอนตัมแล้ว มาใช้ Estimator คำนวณพลังงานของ Bell state Ψ=12(0110)\vert \Psi^- \rangle = \frac{1}{\sqrt{2}}(\vert 01 \rangle - \vert 10 \rangle) กัน

Map

เป็นการเตือนความจำ พลังงานของระบบถูกกำหนดโดยปฏิกิริยาระหว่างสปิน (JJ) และสนามแม่เหล็กภายนอก (hxh_x) ตามที่ Hamiltonian กำหนดไว้:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

แต่ละพจน์ใน Hamiltonian บอกเราว่าการรวมกันของสปินหนึ่งๆ มีส่วนร่วมต่อพลังงานอย่างไร ใน Qiskit เราสามารถแสดงพจน์เหล่านี้เป็น Pauli operator ซึ่งเป็นเพียงป้ายกำกับสำหรับการกระทำง่ายๆ บน Qubit:

  • Z1Z0Z_1 Z_0 ทำงานด้วย ZZ บน Qubit ทั้งสอง
  • X0X_0 ทำงานด้วย XX บน Qubit 0
  • X1X_1 ทำงานด้วย XX บน Qubit 1

SparsePauliOp ใน Qiskit คือวิธีเก็บรายการของ Pauli operator เหล่านี้พร้อมกับสัมประสิทธิ์ตัวเลข Pauli operator เหล่านี้คือ observable ที่เราต้องการให้คอมพิวเตอร์ควอนตัมวัด — ปริมาณที่บอกเราเกี่ยวกับระบบ เมื่อใช้ Estimator เราสามารถคำนวณค่าเฉลี่ยของแต่ละ observable ในสถานะของเรา และรวมมันตามสัมประสิทธิ์ใน Hamiltonian เพื่อให้ได้พลังงานรวม

# Import Qiskit primitives
from qiskit.quantum_info import SparsePauliOp

# Parameters
J = 1.0 # antiferromagnetic coupling (J<0)
hx = -0.5 # transverse field strength

# 1. Define the Hamiltonian H = J Z1 Z2 + hx (X1 + X2)
obs = SparsePauliOp.from_list([("ZZ", J), ("XI", hx), ("IX", hx)])

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)
<qiskit.circuit.instructionset.InstructionSet at 0x1387ed630>

สังเกตว่าเราละบรรทัด qc.measure_all() ออกจากโค้ด นี่เพราะกับ Estimator เราไม่จำเป็นต้องระบุว่าจะวัดที่ใดใน Circuit เราจะบอกมันว่าต้องการ estimate observable อะไร และ Qiskit จะดูแลการวัดเบื้องหลัง

Optimize

ขั้นตอน optimization ดำเนินไปเช่นเดิม โดยเพิ่มการทำให้แน่ใจว่า observable ของเราก็ถูกเขียนในแบบที่คอมพิวเตอร์ควอนตัมสามารถเข้าใจได้ด้วย

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)

qc_isa.draw("mpl")

Output of the previous code cell

Execute

ในขั้นตอน Execute เราจะโหลด Estimator แล้วส่ง Circuit พร้อมรายการ observable ที่ต้องการ estimate ไปยังคอมพิวเตอร์ควอนตัม

# Load the Runtime primitive and session
from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
# Load the backend sampler

# noise_model = NoiseModel.from_backend(backend)

# Use Aer simulator in Estimator
# estimator_sim = BackendEstimatorV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# estimator_gen = BackendEstimatorV2(backend=backend_gen)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()

# Uncomment lines below to run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()

Post-process

สุดท้าย ในขั้นตอน post-processing เราแค่ print พลังงานที่คำนวณเบื้องหลังโดย Estimator ออกมา

print(res[0].data.evs)
-0.9934112021453058

นี่คือพลังงานของสถานะเรา!

บทสรุป

ในบทเรียนนี้ เราได้เรียนรู้วิธีเตรียมสถานะควอนตัม Qubit สองตัวง่ายๆ ที่แสดงถึงสปินสองตัวที่มีปฏิกิริยากัน เราได้เห็นวิธีใช้ Sampler เพื่อสังเกตการกระจายของผลการวัด และวิธีใช้ Estimator เพื่อคำนวณพลังงานของสถานะตาม Hamiltonian ระหว่างทาง เราได้เห็นว่า Hamiltonian เข้ารหัสปฏิกิริยาระหว่างสปินและผลของสนามภายนอกอย่างไร และสถานะต่างๆ สามารถมีพลังงานต่างกันได้

การขยายไปสู่สปินหลายตัว

จนถึงตอนนี้ เราดูแค่สปินสองตัวเท่านั้น ซึ่งง่ายพอที่จะวิเคราะห์ด้วยมือ ในระบบทางกายภาพจริง เช่น ในแม่เหล็กหรือวัสดุเชิงซ้อนอื่นๆ มักมีสปินที่มีปฏิกิริยากันจำนวนมาก เมื่อจำนวนสปินเพิ่มขึ้น Hamiltonian จะซับซ้อนขึ้นและการหาสถานะพลังงานต่ำสุดก็ยากขึ้นมาก นี่คือจุดที่คอมพิวเตอร์ควอนตัมสามารถช่วยได้: โดยการเตรียมสถานะต่างๆ และ estimate พลังงานของมัน เราสามารถสำรวจการกำหนดค่าพลังงานต่ำได้อย่างมีประสิทธิภาพมากกว่าคอมพิวเตอร์คลาสสิกสำหรับระบบขนาดใหญ่

การขยายตามธรรมชาติของการทดลองนี้คือการเพิ่มจำนวน Qubit เพื่อแสดงสปินมากขึ้น และปรับวิธีที่สปินถูกเตรียมเพื่อพยายามหาสถานะพลังงานต่ำสุด แนวทางนี้คือแก่นของ variational methods ซึ่งคุณสามารถเรียนรู้ได้ในคอร์ส Variational Quantum Algorithms

นอกจากนี้ยังมีแนวทางควอนตัมอื่นๆ สำหรับศึกษาพลังงานสถานะพื้นที่ไปไกลกว่า variational techniques แต่เมธอดเหล่านี้ไม่ได้ครอบคลุมที่นี่ แต่ถูกแนะนำในคอร์ส Quantum Diagonalization Algorithms ถ้าอยากเรียนรู้เพิ่มเติม

วัตถุประสงค์การเรียนรู้

กลับไปที่จุดเริ่มต้นของการทดลองที่ 2 และลองใหม่ด้วยสถานะ superposition ที่แตกต่างกัน คุณสามารถหาสถานะที่มีพลังงานต่ำกว่าที่เราใช้ได้ไหม?

This translation based on the English version of 7 พ.ค. 2569