ข้ามไปยังเนื้อหาหลัก
โครงการนี้อยู่ในช่วงเบต้า พบข้อผิดพลาด? สร้าง GitHub Issue มีคำถามหรือไอเดีย? ถามใน Discussions — ยินดีรับฟังความคิดเห็น!
คุณภาพการแปลในหน้านี้เป็นอย่างไร?

ลูปการปรับแต่ง

ในบทเรียนนี้ เราจะเรียนรู้วิธีใช้ optimizer เพื่อสำรวจสถานะควอนตัมแบบมีพารามิเตอร์ของ ansatz ซ้ำ ๆ:

  • เริ่มต้นลูปการปรับแต่ง

  • เข้าใจข้อดีข้อเสีย (trade-off) ของการใช้ optimizer แบบ local และ global

  • สำรวจ barren plateaus และวิธีหลีกเลี่ยง

ในระดับสูง optimizer มีบทบาทสำคัญในการสำรวจพื้นที่การค้นหาของเรา optimizer ใช้การประเมินฟังก์ชันต้นทุนเพื่อเลือกชุดพารามิเตอร์ถัดไปในลูป variational และทำซ้ำจนกว่าจะถึงสภาวะเสถียร ณ จุดนั้น ชุดค่าพารามิเตอร์ที่เหมาะสมที่สุด θ⃗∗\vec\theta^* จะถูกส่งคืน

A diagram of some important factors in optimization, including barren plateaus, gradient vs gradient-free optimizers, and bootstrapping.

Local และ Global Optimizers​

เราจะตั้งค่าปัญหาก่อนที่จะสำรวจคลาสของ optimizer แต่ละคลาส เราจะเริ่มด้วย Circuit ที่มีพารามิเตอร์ variational 8 ตัว:

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "scipy": "scipy"}
_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 QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import TwoLocal
import numpy as np

theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()
observable = SparsePauliOp.from_list([("XX", 1), ("YY", -3)])

reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)

variational_form = TwoLocal(
2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)
ansatz = reference_circuit.compose(variational_form)

ansatz.decompose().draw("mpl")

Output of the previous code cell

def cost_func_vqe(params, ansatz, hamiltonian, estimator):
"""Return estimate of energy from estimator

Parameters:
params (ndarray): Array of ansatz parameters
ansatz (QuantumCircuit): Parameterized ansatz circuit
hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
estimator (Estimator): Estimator primitive instance

Returns:
float: Energy estimate
"""
pub = (ansatz, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
return cost
from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

ตัวหาค่าเหมาะสมที่สุดเฉพาะที่ (Local Optimizers)​

Local optimizer ค้นหาจุดที่ลดค่าฟังก์ชันต้นทุนให้น้อยที่สุด โดยเริ่มจากจุดเริ่มต้น C(θ0⃗)C(\vec{\theta_0}) และเคลื่อนไปยังจุดต่าง ๆ ตามสิ่งที่สังเกตได้ในบริเวณที่กำลังประเมินในการวนซ้ำแต่ละครั้ง ซึ่งหมายความว่าอัลกอริทึมเหล่านี้มักจะ converge เร็ว แต่ผลลัพธ์ขึ้นอยู่กับจุดเริ่มต้นเป็นอย่างมาก Local optimizer ไม่สามารถมองเห็นเกินบริเวณที่กำลังประเมินและอาจติดอยู่ใน local minima ได้ง่าย โดยรายงานว่า converge แล้วเมื่อพบ local minima และละเลยสถานะอื่น ๆ ที่มีการประเมินที่ดีกว่า

# SciPy minimizer routine
from scipy.optimize import minimize

x0 = np.ones(8)

result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="SLSQP"
)

result
message: Optimization terminated successfully
success: True
status: 0
fun: -3.9999999964520634
x: [ 1.000e+00 1.000e+00 -1.571e+00 -4.556e-05 -1.207e+00
-1.935e+00 4.079e-01 -4.079e-01]
nit: 12
jac: [ 0.000e+00 0.000e+00 -7.957e-04 2.543e-04 1.381e-03
1.381e-03 5.430e-04 5.431e-04]
nfev: 112
njev: 12

ตัวหาค่าเหมาะสมที่สุดแบบทั่วไป (Global optimizers)​

Global optimizer ค้นหาจุดที่ลดค่าฟังก์ชันต้นทุนให้น้อยที่สุดในหลายบริเวณของโดเมน (นั่นคือ แบบ non-local) โดยประเมินซ้ำ (นั่นคือ ที่การวนซ้ำ ii) บน vector พารามิเตอร์ชุดหนึ่ง Θi:=θ⃗i,j∣j∈Jopti\Theta_i := \\{ {\vec\theta_{i,j} | j \in \mathcal{J}_\text{opt}^i} \\} ที่กำหนดโดย optimizer ทำให้มีความเสี่ยงต่อ local minima น้อยกว่าและค่อนข้างเป็นอิสระจากการกำหนดค่าเริ่มต้น แต่ก็ converge ไปสู่วิธีแก้ปัญหาช้ากว่าอย่างมีนัยสำคัญ

การปรับให้เหมาะสมแบบ Bootstrapping​

Bootstrapping หรือการกำหนดค่าเริ่มต้นสำหรับพารามิเตอร์ θ⃗\vec\theta จากการปรับแต่งก่อนหน้า ช่วยให้ optimizer ของเรา converge ไปสู่วิธีแก้ปัญหาได้เร็วขึ้น เราเรียกสิ่งนี้ว่าจุดเริ่มต้น θ⃗0\vec\theta_0 และ ∣ψ(θ⃗0)⟩=UV(θ⃗0)∣ρ⟩|\psi(\vec\theta_0)\rangle = U_V(\vec\theta_0)|\rho\rangle ว่าสถานะเริ่มต้น สถานะเริ่มต้นนี้แตกต่างจาก reference state ∣ρ⟩|\rho\rangle ของเรา เนื่องจากสถานะแรกมุ่งเน้นที่พารามิเตอร์เริ่มต้นที่กำหนดในลูปการปรับแต่งของเรา ในขณะที่สถานะหลังมุ่งเน้นที่การใช้วิธีแก้ปัญหา "อ้างอิง" ที่รู้จัก ทั้งสองอาจตรงกันหาก UV(θ⃗0)≡IU_V(\vec\theta_0) \equiv I (นั่นคือ identity operation)

เมื่อ local optimizer converge ไปสู่ local minima ที่ไม่เหมาะสมที่สุด เราสามารถลอง bootstrap การปรับแต่งแบบ global และปรับ convergence แบบ local ให้ละเอียดขึ้น แม้ว่าจะต้องตั้งค่า variational workload สองชุด แต่ช่วยให้ optimizer ค้นหาวิธีแก้ปัญหาที่เหมาะสมกว่า local optimizer เพียงอย่างเดียว

Gradient-Based และ Gradient-Free Optimizers​

แบบใช้เกรเดียนต์ (Gradient-Based)​

สำหรับฟังก์ชันต้นทุน C(θ⃗)C(\vec\theta) ของเรา หากเราเข้าถึง gradient ของฟังก์ชัน ∇⃗C(θ⃗)\vec{\nabla} C(\vec\theta) จากจุดเริ่มต้น วิธีง่ายที่สุดในการลดค่าฟังก์ชันคือการอัปเดตพารามิเตอร์ตามทิศทางที่ลาดชันลงมากที่สุดของฟังก์ชัน นั่นคือ เราอัปเดตพารามิเตอร์เป็น θ⃗n+1=θ⃗n−η∇⃗C(θ⃗)\vec\theta_{n+1} = \vec\theta_n - \eta \vec{\nabla} C(\vec\theta) โดยที่ η\eta คืออัตราการเรียนรู้ ซึ่งเป็น hyperparameter ขนาดเล็กบวกที่ควบคุมขนาดของการอัปเดต เราทำสิ่งนี้ต่อไปจนกว่าจะ converge ไปสู่ local minimum ของฟังก์ชันต้นทุน C(θ⃗∗)C({\vec\theta^*}) เราสามารถใช้ฟังก์ชันต้นทุนนี้และ optimizer เพื่อคำนวณพารามิเตอร์ที่เหมาะสมที่สุด

# SciPy minimizer routine
from scipy.optimize import minimize

x0 = np.ones(8)

result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="BFGS"
)

result
message: Optimization terminated successfully.
success: True
status: 0
fun: -3.9999999999997025
x: [ 1.000e+00 1.000e+00 1.571e+00 3.220e-07 2.009e-01
-2.009e-01 6.342e-01 -6.342e-01]
nit: 14
jac: [-1.192e-07 -2.980e-08 8.345e-07 1.103e-06 5.960e-08
0.000e+00 -5.960e-08 2.980e-08]
hess_inv: [[ 1.000e+00 1.872e-10 ... 5.077e-05 3.847e-05]
[ 1.872e-10 1.000e+00 ... -5.208e-05 -4.060e-05]
...
[ 5.077e-05 -5.208e-05 ... 7.243e-01 -2.604e-01]
[ 3.847e-05 -4.060e-05 ... -2.604e-01 8.179e-01]]
nfev: 144
njev: 16

ข้อเสียหลักของการปรับแต่งประเภทนี้คือความเร็วในการ converge ซึ่งอาจช้ามาก และไม่มีการรับประกันว่าจะบรรลุวิธีแก้ปัญหาที่เหมาะสมที่สุด

graph of f(theta) against theta, multiple dots show different states of a gradient descent algorithm finding the minimum of a curve.

แบบไม่ใช้เกรเดียนต์ (Gradient-Free)​

อัลกอริทึมการปรับแต่งแบบ gradient-free ไม่ต้องการข้อมูล gradient และมีประโยชน์ในสถานการณ์ที่การคำนวณ gradient ทำได้ยาก มีค่าใช้จ่ายสูง หรือมีสัญญาณรบกวนมากเกินไป อัลกอริทึมเหล่านี้มักมีความทนทานมากกว่าในการค้นหา global optima ในขณะที่วิธีการที่ใช้ gradient มักจะ converge ไปสู่ local optima เราจะสำรวจตัวอย่างบางส่วนที่ gradient-free optimizer ช่วยหลีกเลี่ยง barren plateaus อย่างไรก็ตาม วิธี gradient-free ต้องการทรัพยากรการคำนวณสูงกว่า โดยเฉพาะสำหรับปัญหาที่มีพื้นที่การค้นหามิติสูง

นี่คือตัวอย่างที่ใช้ COBYLA optimizer แทน:

# SciPy minimizer routine
from scipy.optimize import minimize

x0 = np.ones(8)

result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="COBYLA"
)

result
message: Optimization terminated successfully.
success: True
status: 1
fun: -3.999999973369678
x: [ 1.631e+00 1.492e+00 1.571e+00 3.142e+00 1.375e+00
-1.767e+00 1.484e+00 1.658e+00]
nfev: 137
maxcv: 0.0

Barren Plateaus (ที่ราบที่เกรเดียนต์แทบหายไป)​

ในความเป็นจริง ภูมิทัศน์ต้นทุนอาจซับซ้อนมาก ดังเห็นได้จากเนินและหุบเขาในตัวอย่างด้านล่าง วิธีการปรับแต่งจะพาเราเคลื่อนไปบนภูมิทัศน์ต้นทุนเพื่อค้นหาค่าต่ำสุด (จุดและเส้นสีดำ) เราจะเห็นว่าการค้นหาสองในสามครั้งไปติดอยู่ที่ local minimum ของภูมิทัศน์ แทนที่จะไปถึง global minimum

A complicated curved manifold with many peaks and troughs.

ไม่ว่าจะใช้วิธีการปรับแต่งประเภทใด หากภูมิทัศน์ต้นทุนค่อนข้างแบนราบ วิธีการดังกล่าวอาจเป็นเรื่องยากที่จะกำหนดทิศทางการค้นหาที่เหมาะสม สถานการณ์นี้เรียกว่า barren plateau, ซึ่งภูมิทัศน์ต้นทุนยิ่งแบนราบมากขึ้นเรื่อย ๆ (และยิ่งยากต่อการกำหนดทิศทางไปยังค่าต่ำสุด) สำหรับ Circuit ควอนตัมแบบมีพารามิเตอร์ที่หลากหลาย ความน่าจะเป็นที่ gradient ตามทิศทางที่สมเหตุสมผลใด ๆ จะไม่เป็นศูนย์ที่ความแม่นยำคงที่บางค่าลดลงแบบเอกซ์โพเนนเชียลตามจำนวน Qubit ที่เพิ่มขึ้น

A diagram of a geographic plateau compared to a slope of a mountain, to explain why a gradient helps us find a minimum and a plateau hinders our efforts.

แม้ว่าพื้นที่นี้ยังอยู่ในการวิจัยที่ยังดำเนินอยู่ แต่เรามีคำแนะนำสองสามข้อเพื่อปรับปรุงประสิทธิภาพการปรับแต่ง:

  • Bootstrapping ช่วยให้ลูปการปรับแต่งหลีกเลี่ยงการติดอยู่ในพื้นที่พารามิเตอร์ที่ gradient มีขนาดเล็ก

  • การทดลองกับ hardware-efficient ansatz: เนื่องจากเราใช้ระบบควอนตัมที่มีสัญญาณรบกวนเป็น oracle แบบ black-box คุณภาพของการประเมินเหล่านั้นอาจส่งผลต่อประสิทธิภาพของ optimizer การใช้ hardware-efficient ansatz เช่น EfficientSU2 อาจหลีกเลี่ยงการสร้าง gradient ที่เล็กแบบเอกซ์โพเนนเชียล

  • การทดลองกับ error suppression และ error mitigation: IBM Quantum primitives มี interface ง่าย ๆ สำหรับทดลองกับค่าต่าง ๆ ของ optimization_level และ resilience_setting ตามลำดับ ซึ่งช่วยลดผลกระทบของสัญญาณรบกวนและทำให้กระบวนการปรับแต่งมีประสิทธิภาพมากขึ้น

  • การทดลองกับ gradient-free optimizers: ต่างจากอัลกอริทึมการปรับแต่งที่ใช้ gradient, optimizer เช่น COBYLA ไม่พึ่งพาข้อมูล gradient ในการปรับพารามิเตอร์ จึงมีโอกาสน้อยกว่าที่จะได้รับผลกระทบจาก barren plateau

สรุป​

ในบทเรียนนี้ คุณได้เรียนรู้วิธีกำหนดลูปการปรับแต่งของคุณ:

  • เริ่มต้นลูปการปรับแต่ง

  • เข้าใจข้อดีข้อเสีย (trade-off) ของการใช้ optimizer แบบ local และ global

  • สำรวจ barren plateaus และวิธีหลีกเลี่ยง

variational workload ระดับสูงของเราสมบูรณ์แล้ว:

A quantum circuit now with both a unitary to prepare the reference state, and a second unitary to vary the state using variational parameters.

ต่อไป เราจะสำรวจอัลกอริทึม variational เฉพาะโดยคำนึงถึงกรอบงานนี้