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

Optimization Solver: A Qiskit Function by Q-CTRL Fire Opal

หมายเหตุ

Qiskit Functions เป็นฟีเจอร์ทดลองที่ใช้ได้เฉพาะผู้ใช้ IBM Quantum® Premium Plan, Flex Plan และ On-Prem (ผ่าน IBM Quantum Platform API) Plan เท่านั้น อยู่ในสถานะพรีวิวและอาจมีการเปลี่ยนแปลงได้

ภาพรวม

ด้วย Fire Opal Optimization Solver คุณสามารถแก้ปัญหา optimization ระดับ utility-scale บน quantum hardware ได้โดยไม่ต้องมีความเชี่ยวชาญด้านควอนตัม แค่ป้อนนิยามปัญหาระดับสูงเข้าไป แล้ว Solver จะดูแลส่วนที่เหลือทั้งหมด กระบวนการทำงานทั้งหมดรับรู้ noise และใช้ประโยชน์จาก Fire Opal's Performance Management ภายใต้ฝาครอบ Solver ให้ผลลัพธ์ที่แม่นยำสม่ำเสมอสำหรับปัญหาที่ท้าทายสำหรับคอมพิวเตอร์คลาสสิก แม้กระทั่งในระดับ full-device บน IBM® QPU ที่ใหญ่ที่สุด

Solver มีความยืดหยุ่นและสามารถใช้แก้ปัญหา combinatorial optimization ที่นิยามเป็น objective functions หรือ arbitrary graphs ปัญหาไม่จำเป็นต้องถูก map ลงบน device topology ทั้งปัญหาที่ไม่มีข้อจำกัดและมีข้อจำกัดสามารถแก้ได้ โดยขอให้ข้อจำกัดนั้นสามารถเขียนเป็น penalty terms ได้ ตัวอย่างในคู่มือนี้แสดงวิธีแก้ปัญหา optimization ระดับ utility-scale ทั้งแบบมีและไม่มีข้อจำกัด โดยใช้ input type ของ Solver ต่างกัน ตัวอย่างแรกเป็นปัญหา Max-Cut บน graph แบบ 3-Regular 156 nodes ส่วนตัวอย่างที่สองจัดการกับปัญหา Minimum Vertex Cover ขนาด 50 nodes ที่นิยามด้วย cost function

หากต้องการเข้าถึง Optimization Solver ติดต่อ Q-CTRL

รายละเอียดฟังก์ชัน

Solver ปรับแต่งและทำให้ algorithm ทั้งหมดทำงานอัตโนมัติอย่างสมบูรณ์ ตั้งแต่การกดสัญญาณรบกวนในระดับ hardware ไปจนถึงการ mapping ปัญหาที่มีประสิทธิภาพและ closed-loop classical optimization เบื้องหลัง pipeline ของ Solver ลด error ในทุกขั้นตอน ช่วยให้ performance ที่ดีขึ้นที่จำเป็นต่อการ scale อย่างมีความหมาย กระบวนการทำงานพื้นฐานได้แรงบันดาลใจจาก Quantum Approximate Optimization Algorithm (QAOA) ซึ่งเป็น hybrid quantum-classical algorithm สำหรับสรุปรายละเอียดเต็มของกระบวนการ Optimization Solver ทั้งหมด ดูที่ บทความที่ตีพิมพ์แล้ว

การแสดงภาพ workflow ของ Optimization Solver

ในการแก้ปัญหาทั่วไปด้วย Optimization Solver:

  1. นิยามปัญหาของคุณเป็น objective function, graph, หรือ SparsePauliOp spin chain
  2. เชื่อมต่อกับฟังก์ชันผ่าน Qiskit Functions Catalog
  3. รันปัญหากับ Solver และดึงผลลัพธ์

Inputs และ outputs

Inputs

ชื่อประเภทคำอธิบายจำเป็นค่าเริ่มต้นตัวอย่าง
problemstr หรือ SparsePauliOpรูปแบบใดรูปแบบหนึ่งจากที่ระบุไว้ใน "Accepted problem formats"ใช่N/APoly(2.0*n[0]*n[1] + 2.0*n[0]*n[2] - 3.13520241298341*n[0] + 2.0*n[1]*n[2] - 3.14469748506794*n[1] - 3.18897660121566*n[2] + 6.0, n[0], n[1], n[2], domain='RR')
problem_typestrชื่อของ problem class ใช้เฉพาะกับ graph และ spin chain problem definitions ซึ่งจำกัดอยู่ที่ "maxcut" หรือ "spin_chain" ไม่จำเป็นสำหรับ arbitrary objective function problem definitionsไม่None"maxcut"
backend_namestrชื่อของ Backend ที่ต้องการใช้ไม่Backend ที่ว่างที่สุดที่ instance ของคุณสามารถเข้าถึงได้"ibm_fez"
optionsdictInput options รวมถึง: (ไม่บังคับ) session_id: str พฤติกรรมเริ่มต้นจะสร้าง Session ใหม่ไม่None{"session_id": "cw2q70c79ws0008z6g4g"}

รูปแบบปัญหาที่รองรับ:

  • การแสดงผลด้วย Polynomial expression ของ objective function ควรสร้างใน Python ด้วย SymPy Poly object และ format เป็น string โดยใช้ sympy.srepr
  • การแสดงผลด้วย Graph ของ problem type เฉพาะ ควรสร้าง graph โดยใช้ library networkx ใน Python แล้วแปลงเป็น string ด้วยฟังก์ชัน networkx [nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.)
  • การแสดงผลด้วย Spin chain ของปัญหาเฉพาะ ควรแสดงเป็น SparsePauliOp object ดู เอกสารประกอบ สำหรับรายละเอียดเพิ่มเติม

Backend ที่รองรับ: รันโค้ดต่อไปนี้เพื่อดูรายการ Backend ที่รองรับในปัจจุบัน หาก device ของคุณไม่อยู่ในรายการ ติดต่อ Q-CTRL เพื่อเพิ่มการรองรับ

# Added by doQumentation — required packages for this notebook
!pip install -q networkx numpy qiskit-ibm-catalog qiskit-ibm-runtime sympy
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()

service.backends()
[<IBMBackend('ibm_fez')>,
<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_kingston')>,
<IBMBackend('ibm_torino')>,
<IBMBackend('ibm_marrakesh')>]

Options:

ชื่อประเภทคำอธิบายค่าเริ่มต้น
session_idstrID ของ Qiskit Runtime session ที่มีอยู่แล้ว"cw4r3je6f0t010870y3g"
job_tagslist[str]รายการ job tags[]

Outputs

ชื่อประเภทคำอธิบายตัวอย่าง
resultdict[str, Any]Solution และ metadata ที่ระบุไว้ใน "Result dictionary contents"{'solution_bitstring_cost': 3.0, 'final_bitstring_distribution': {'000001': 100, '000011': 2}, 'iteration_count': 3, 'solution_bitstring': '000001', 'variables_to_bitstring_index_map': {n[1]': 5, 'n[2]': 4, 'n[3]': 3, 'n[4]': 2, 'n[5]': 1}, 'best_parameters': [0.19628831763697097, -1.047052334523102], 'warnings': []}

เนื้อหาของ Result dictionary:

ชื่อประเภทคำอธิบาย
solution_bitstring_costfloatต้นทุนต่ำสุดที่ดีที่สุดในทุก iteration
final_bitstring_distributionCountsDictdictionary จำนวน bitstring ที่เกี่ยวข้องกับต้นทุนต่ำสุด
solution_bitstringstrBitstring ที่มีต้นทุนดีที่สุดใน distribution สุดท้าย
iteration_countintจำนวน QAOA iteration ทั้งหมดที่ Solver ทำ
variables_to_bitstring_index_mapfloatการ mapping จาก variables ไปยัง bit ที่เทียบเท่าใน bitstring
best_parameterslist[float]beta และ gamma parameters ที่ optimize แล้วในทุก iteration
warningslist[str]คำเตือนที่เกิดขึ้นขณะ compile หรือรัน QAOA (ค่าเริ่มต้นคือ None)

Benchmarks

ผลลัพธ์ benchmarking ที่ตีพิมพ์แล้ว แสดงให้เห็นว่า Solver แก้ปัญหาที่มีมากกว่า 120 Qubit ได้สำเร็จ แม้กระทั่งมีประสิทธิภาพเหนือกว่าผลลัพธ์ที่ตีพิมพ์ก่อนหน้าบน quantum annealing และ trapped-ion devices ตัวชี้วัด benchmark ต่อไปนี้ให้ข้อบ่งชี้คร่าวๆ ของความแม่นยำและการ scale ของ problem types จากตัวอย่างบางส่วน ตัวชี้วัดจริงอาจแตกต่างกันตามคุณสมบัติต่างๆ ของปัญหา เช่น จำนวน terms ใน objective function (density) และ locality จำนวน variables และ polynomial order

"Number of qubits" ที่ระบุไม่ใช่ข้อจำกัดแบบ hard limit แต่แสดงถึงค่า threshold คร่าวๆ ที่คุณคาดว่าจะได้ความแม่นยำของ solution ที่สม่ำเสมอมาก ปัญหาขนาดใหญ่กว่านี้ได้รับการแก้สำเร็จมาแล้ว และสนับสนุนให้ทดสอบเกินขอบเขตเหล่านี้

รองรับ qubit connectivity แบบ arbitrary ในทุก problem types

ประเภทปัญหาจำนวน Qubitตัวอย่างความแม่นยำเวลาทั้งหมด (s)การใช้งาน Runtime (s)จำนวน iteration
ปัญหา quadratic แบบ sparsely-connected1563-regular Max-Cut100%176429316
Higher-order binary optimization156Ising spin-glass model100%146127216
ปัญหา quadratic แบบ densely-connected50Fully-connected Max-Cut100%175826812
ปัญหาแบบ constrained ด้วย penalty terms50Weighted Minimum Vertex Cover กับ edge density 8%100%107421510

เริ่มต้นใช้งาน

ก่อนอื่น ยืนยันตัวตนโดยใช้ IBM Quantum API key จากนั้นเลือก Qiskit Function ดังนี้ (snippet นี้สมมติว่าคุณได้ บันทึกบัญชีของคุณ ไว้ใน local environment แล้ว)

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Access Function
solver = catalog.load("q-ctrl/optimization-solver")

ตัวอย่าง: Unconstrained optimization

รันปัญหา maximum cut (Max-Cut) ตัวอย่างต่อไปนี้แสดงความสามารถของ Solver บนปัญหา Max-Cut graph แบบ unweighted 3-regular ขนาด 156 nodes แต่คุณยังสามารถแก้ปัญหา weighted graph ได้เช่นกัน นอกจาก qiskit-ibm-catalog แล้ว คุณยังต้องใช้ packages ต่อไปนี้ในการรันตัวอย่างนี้: networkx และ numpy สามารถติดตั้ง packages เหล่านี้โดย uncomment cell ต่อไปนี้หากคุณรันตัวอย่างนี้ใน notebook ที่ใช้ IPython kernel

# %pip install networkx numpy

1. นิยามปัญหา

คุณสามารถรันปัญหา Max-Cut โดยนิยาม graph problem และระบุ problem_type='maxcut'

import networkx as nx
import numpy as np

# Generate a random graph with 156 nodes
maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)
# Optionally, visualize the graph
nx.draw_networkx(
maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100
)

ผลลัพธ์ของ code cell ก่อนหน้า

Solver รับ string เป็น input นิยามปัญหา

# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)

2. รันปัญหา

เมื่อใช้วิธี input แบบ graph-based ให้ระบุ problem type

# This cell is hidden from users
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend_name = service.least_busy(n_qubits=156).name
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)

ตรวจสอบ สถานะ ของ Qiskit Function workload หรือดึง ผลลัพธ์ ดังนี้:

# Get job status
print(maxcut_job.status())
QUEUED

3. ดึงผลลัพธ์

ดึงค่า optimal cut จาก results dictionary

หมายเหตุ
การ mapping ของ variables ไปยัง bitstring อาจเปลี่ยนแปลงได้ output dictionary ประกอบด้วย sub-dictionary variables_to_bitstring_index_map ที่ช่วยตรวจสอบลำดับ
# Poll for results
maxcut_result = maxcut_job.result()

# Take the absolute value of the solution since the cost function is minimized
qctrl_maxcut = abs(maxcut_result["solution_bitstring_cost"])

# Print the optimal cut value found by the Optimization Solver
print(f"Optimal cut value: {qctrl_maxcut}")
Optimal cut value: 209.0

คุณสามารถตรวจสอบความแม่นยำของผลลัพธ์โดยแก้ปัญหาแบบ classically ด้วย open-source solvers เช่น PuLP หาก graph ไม่ได้ densely connected ปัญหา density สูงอาจต้องใช้ classical solvers ขั้นสูงเพื่อตรวจสอบ solution

ตัวอย่าง: Constrained optimization

ตัวอย่าง Max-Cut ก่อนหน้าเป็นปัญหา quadratic unconstrained binary optimization ทั่วไป Optimization Solver ของ Q-CTRL สามารถใช้กับ problem types ต่างๆ รวมถึง constrained optimization คุณสามารถแก้ arbitrary problem types ได้โดยป้อน problem definition ที่แสดงเป็น polynomial ที่ข้อจำกัดถูก model เป็น penalty terms

ตัวอย่างต่อไปนี้แสดงวิธีสร้าง cost function สำหรับปัญหา constrained optimization, minimum vertex cover (MVC) นอกจาก packages qiskit-ibm-catalog และ qiskit แล้ว คุณยังต้องใช้ packages ต่อไปนี้: numpy, networkx, และ sympy สามารถติดตั้ง packages เหล่านี้โดย uncomment cell ต่อไปนี้หากคุณรันตัวอย่างนี้ใน notebook ที่ใช้ IPython kernel

# %pip install numpy networkx sympy

1. นิยามปัญหา

นิยามปัญหา MVC แบบสุ่มโดยสร้าง graph ที่มี weighted nodes แบบสุ่ม

import networkx as nx
from sympy import symbols, Poly, srepr

# To change the weights, change the seed to any integer.
rng_seed = 18
_rng = np.random.default_rng(rng_seed)
node_count = 50
edge_probability = 0.08
mvc_graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)

# add node weights
for i in mvc_graph.nodes:
mvc_graph.add_node(i, weight=_rng.random())

# Optionally, visualize the graph
nx.draw_networkx(mvc_graph, nx.kamada_kawai_layout(mvc_graph), node_size=200)

ผลลัพธ์ของ code cell ก่อนหน้า

โมเดล optimization มาตรฐานสำหรับ weighted MVC สามารถ formulate ได้ดังนี้ ก่อนอื่น ต้องเพิ่ม penalty สำหรับกรณีใดๆ ที่ edge ไม่ได้เชื่อมต่อกับ vertex ใน subset ดังนั้น ให้ ni=1n_i = 1 หาก vertex ii อยู่ใน cover (คือ อยู่ใน subset) และ ni=0n_i = 0 ถ้าไม่ใช่ ประการที่สอง เป้าหมายคือลดจำนวน vertices ทั้งหมดใน subset ให้น้อยที่สุด ซึ่งแสดงด้วยฟังก์ชันต่อไปนี้:

Minimizey=iVωini\textbf{Minimize}\qquad y = \sum_{i\in V} \omega_i n_i

# Construct the cost function.
variables = symbols([f"n[{i}]" for i in range(node_count)])
cost_function = Poly(0, variables)

for i in mvc_graph.nodes():
weight = mvc_graph.nodes[i].get("weight", 0)
cost_function += variables[i] * weight

ตอนนี้ทุก edge ใน graph ควรมี endpoint อย่างน้อยหนึ่งจุดจาก cover ซึ่งแสดงเป็น inequality:

ni+nj1 for all (i,j)En_i + n_j \ge 1 \texttt{ for all } (i,j)\in E

กรณีใดที่ edge ไม่ได้เชื่อมต่อกับ vertex ของ cover ต้องถูก penalize ซึ่งแสดงใน cost function โดยเพิ่ม penalty ในรูป P(1ninj+ninj)P(1-n_i-n_j+n_i n_j) เมื่อ PP เป็นค่าคงที่ penalty ที่เป็นบวก ดังนั้น unconstrained alternative สำหรับ constrained inequality ของ weighted MVC คือ:

Minimizey=iVωini+P((i,j)E(1ninj+ninj))\textbf{Minimize}\qquad y = \sum_{i\in V}\omega_i n_i + P(\sum_{(i,j)\in E}(1 - n_i - n_j + n_i n_j))

# Add penalty term.
penalty_constant = 2
for i, j in mvc_graph.edges():
cost_function += penalty_constant * (
1 - variables[i] - variables[j] + variables[i] * variables[j]
)

2. รันปัญหา

# Solve the problem
mvc_job = solver.run(
problem=srepr(cost_function),
backend_name=backend_name, # E.g. "ibm_fez"
)

ตรวจสอบ สถานะ ของ Qiskit Function workload หรือดึง ผลลัพธ์ ดังนี้:

print(mvc_job.status())

3. ดูผลลัพธ์

ดึง solution และวิเคราะห์ผลลัพธ์ เนื่องจากปัญหานี้มี weighted nodes solution จึงไม่ใช่แค่จำนวน nodes น้อยที่สุดที่ cover แต่ solution cost แสดงถึงผลรวมของน้ำหนักของ vertices ที่รวมอยู่ใน vertex cover ซึ่งแสดงถึง "ต้นทุน" หรือ "น้ำหนัก" รวมในการ cover ทุก edge ใน graph โดยใช้ vertices ที่เลือก

mvc_result = mvc_job.result()
qctrl_cost = mvc_result["solution_bitstring_cost"]

# Print results
print(f"Solution cost: {qctrl_cost}")
Solution cost: 10.248198273708624

ขอความช่วยเหลือ

สำหรับคำถามหรือปัญหาใดๆ ติดต่อ Q-CTRL

ขั้นตอนถัดไป

Source: IBM Quantum docs — updated 5 พ.ค. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 11 มี.ค. 2569