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

Optimization Solver: Qiskit Function โดย Q-CTRL Fire Opal

ดู API reference

หมายเหตุ

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

Package versions

โค้ดในหน้านี้พัฒนาขึ้นโดยใช้ข้อกำหนดต่อไปนี้ เราแนะนำให้ใช้เวอร์ชันเหล่านี้หรือใหม่กว่า

qiskit-ibm-runtime~=0.47.0
sympy~=1.14.0

ภาพรวม

ด้วย 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 ทั้งปัญหาที่ไม่มีข้อจำกัดและมีข้อจำกัดสามารถแก้ได้ โดยใช้ hard Hamming-weight-1 constraints แทน penalty terms ตัวอย่างในคู่มือนี้แสดงวิธีแก้ปัญหา optimization ระดับ utility-scale ทั้งแบบมีและไม่มีข้อจำกัด โดยใช้ input types ของ Solver ต่างกัน ตัวอย่างแรกเป็นปัญหา max-cut บน graph แบบ 3-Regular 156 nodes ส่วนตัวอย่างที่สองจัดการกับปัญหา 50-node graph partitioning ที่นิยามด้วย cost function

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

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

Solver ปรับแต่งและทำให้ algorithm ทั้งหมดทำงานอัตโนมัติอย่างสมบูรณ์ ตั้งแต่การกด error ในระดับ 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 และดึงผลลัพธ์

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

  • การแทน objective function ด้วย Polynomial expression ควรสร้างใน Python ด้วย SymPy Poly object และ format เป็น string โดยใช้ sympy.srepr

  • การแทนปัญหาเฉพาะประเภทด้วย Graph ควรสร้าง graph โดยใช้ library networkx ใน Python แล้วแปลงเป็น string ด้วยฟังก์ชัน networkx nx.readwrite.json_graph.adjacency_data

  • การแทนปัญหาเฉพาะด้วย Spin chain ควรแสดงเป็น SparsePauliOp object ดู เอกสารประกอบ สำหรับรายละเอียดเพิ่มเติม

Does this function support all IBM backends?

หากต้องการใช้ Backend ที่ฟังก์ชันนี้ยังไม่รองรับในปัจจุบัน ติดต่อ Q-CTRL เพื่อเพิ่มการรองรับ

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)จำนวน iterations
ปัญหา quadratic แบบ sparsely-connected1563-regular max-cut100%176429316
Higher-order binary optimization156Ising spin-glass model100%146127216
ปัญหา quadratic แบบ densely-connected50Fully-connected max-cut100%175826812
ปัญหาแบบ constrained ด้วย hard constraints50Weighted graph partitioning ที่มี 8% edge density100%107421510

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

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

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

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Verify that you have access to the function
catalog.list()
[QiskitFunction(qunova/hivqe-chemistry),
QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
QiskitFunction(algorithmiq/tem),
QiskitFunction(qedma/qesem),
QiskitFunction(multiverse/singularity),
QiskitFunction(ibm/circuit-function),
QiskitFunction(q-ctrl/optimization-solver),
QiskitFunction(colibritd/quick-pde),
QiskitFunction(q-ctrl/performance-management),
QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# 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
)

Output of the previous 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 หรือดึง ผลลัพธ์ ดังนี้:

# Print the ID so you can use it later, if necessary
print(maxcut_job.job_id)

# Get job status
print(maxcut_job.status())
34b53970-d95a-4e24-8763-fc6f3d112843
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: 210.0

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

ตัวอย่าง: Constrained optimization

ตัวอย่าง max-cut ก่อนหน้านี้เป็นปัญหา quadratic unconstrained binary optimization ทั่วไป Q-CTRL's Optimization Solver ยังสามารถแก้ปัญหา constrained optimization ได้ด้วยการส่ง hard constraints โดยตรงไปยัง Solver ผ่าน input constraint แทนที่จะเข้ารหัสเป็น penalty terms ใน objective function ปัจจุบัน Solver รองรับ Hamming-weight-1 constraints: แต่ละ constraint กำหนดกลุ่มของตัวแปรที่ต้องมีตัวแปรเดียวเท่านั้นที่มีค่าเท่ากับ 1 และที่เหลือต้องมีค่าเท่ากับ 0

ตัวอย่างต่อไปนี้แสดงวิธีสร้าง cost function และชุด hard constraints สำหรับปัญหา constrained optimization, graph partitioning โดยการกำหนด node ทุกตัวใน graph ให้เป็นหนึ่งในกลุ่มต่างๆ พร้อมกับลดน้ำหนักรวมของ edges ที่ปลายทั้งสองข้างอยู่ในกลุ่มเดียวกันให้น้อยที่สุด นอกจาก packages qiskit-ibm-catalog และ qiskit แล้ว คุณยังต้องใช้ packages ต่อไปนี้: numpy, networkx, และ sympy สามารถติดตั้ง packages เหล่านี้โดย uncomment cell ต่อไปนี้หากคุณรันตัวอย่างนี้ใน notebook ที่ใช้ IPython kernel

# %pip install numpy networkx sympy

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

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

import networkx as nx
from sympy import Symbol, 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
graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)

# add node weights
min_weight = -1.0
max_weight = 1.0
for i in graph.nodes:
weight = (max_weight - min_weight) * _rng.random() + min_weight
graph.add_node(i, weight=weight)

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

Output of the previous code cell

โมเดล optimization มาตรฐานสำหรับ weighted graph partitioning สามารถกำหนดได้ดังนี้ แบ่ง node ของ graph ออกเป็นสามกลุ่ม g{0,1,2}g \in \{0, 1, 2\} และให้ ni,g=1n_{i,g} = 1 ถ้า node ii ถูกกำหนดให้อยู่ในกลุ่ม gg และ ni,g=0n_{i,g} = 0 ในกรณีอื่น เป้าหมายคือการลดน้ำหนักรวมของ edges ที่ปลายทั้งสองข้างถูกกำหนดให้อยู่ในกลุ่มเดียวกันให้น้อยที่สุด โดยที่น้ำหนักของ edge (i,j)(i,j) คือน้ำหนักรวมของปลายทั้งสองข้าง, ωi,j=ωi+ωj\omega_{i,j} = \omega_i + \omega_j:

Minimizey=(i,j)Eωi,jgni,gnj,g\textbf{Minimize}\qquad y = \sum_{(i,j)\in E} \omega_{i,j} \sum_{g} n_{i,g}\, n_{j,g}

# Construct the cost function.
group_count = 3
variables = [
Symbol(f"n[{i},{g}]")
for i in range(node_count)
for g in range(group_count)
]
node_group_var = {
(i, g): variables[i * group_count + g]
for i in range(node_count)
for g in range(group_count)
}
cost_function = Poly(0, *variables)

for i, j in graph.edges():
edge_weight = graph.nodes[i]["weight"] + graph.nodes[j]["weight"]
for g in range(group_count):
cost_function += (
edge_weight * node_group_var[(i, g)] * node_group_var[(j, g)]
)

node ทุกตัวต้องถูกกำหนดให้อยู่ในหนึ่งในสามกลุ่มเท่านั้น นี่คือ Hamming-weight-1 constraint: สำหรับ node ii ทุกตัว จะต้องมีเพียงหนึ่งใน ni,0,ni,1,ni,2n_{i,0}, n_{i,1}, n_{i,2} เท่านั้นที่มีค่าเท่ากับ 1 และที่เหลือต้องมีค่าเท่ากับ 0:

ni,0+ni,1+ni,2=1 for all iVn_{i,0} + n_{i,1} + n_{i,2} = 1 \texttt{ for all } i \in V

แทนที่จะเข้ารหัสข้อกำหนดนี้เป็น penalty term ใน cost function ให้ส่งมันโดยตรงไปยัง Solver เป็น hard constraint โดยใช้ input constraint

# Build the hard constraint: exactly one group per node.
constraint_dict = {
str(tuple(f"n[{i},{g}]" for g in range(group_count))): 1
for i in range(node_count)
}
print(f"Problem constraints: {constraint_dict}")
Problem constraints: {"('n[0,0]', 'n[0,1]', 'n[0,2]')": 1, "('n[1,0]', 'n[1,1]', 'n[1,2]')": 1, "('n[2,0]', 'n[2,1]', 'n[2,2]')": 1, "('n[3,0]', 'n[3,1]', 'n[3,2]')": 1, "('n[4,0]', 'n[4,1]', 'n[4,2]')": 1, "('n[5,0]', 'n[5,1]', 'n[5,2]')": 1, "('n[6,0]', 'n[6,1]', 'n[6,2]')": 1, "('n[7,0]', 'n[7,1]', 'n[7,2]')": 1, "('n[8,0]', 'n[8,1]', 'n[8,2]')": 1, "('n[9,0]', 'n[9,1]', 'n[9,2]')": 1, "('n[10,0]', 'n[10,1]', 'n[10,2]')": 1, "('n[11,0]', 'n[11,1]', 'n[11,2]')": 1, "('n[12,0]', 'n[12,1]', 'n[12,2]')": 1, "('n[13,0]', 'n[13,1]', 'n[13,2]')": 1, "('n[14,0]', 'n[14,1]', 'n[14,2]')": 1, "('n[15,0]', 'n[15,1]', 'n[15,2]')": 1, "('n[16,0]', 'n[16,1]', 'n[16,2]')": 1, "('n[17,0]', 'n[17,1]', 'n[17,2]')": 1, "('n[18,0]', 'n[18,1]', 'n[18,2]')": 1, "('n[19,0]', 'n[19,1]', 'n[19,2]')": 1, "('n[20,0]', 'n[20,1]', 'n[20,2]')": 1, "('n[21,0]', 'n[21,1]', 'n[21,2]')": 1, "('n[22,0]', 'n[22,1]', 'n[22,2]')": 1, "('n[23,0]', 'n[23,1]', 'n[23,2]')": 1, "('n[24,0]', 'n[24,1]', 'n[24,2]')": 1, "('n[25,0]', 'n[25,1]', 'n[25,2]')": 1, "('n[26,0]', 'n[26,1]', 'n[26,2]')": 1, "('n[27,0]', 'n[27,1]', 'n[27,2]')": 1, "('n[28,0]', 'n[28,1]', 'n[28,2]')": 1, "('n[29,0]', 'n[29,1]', 'n[29,2]')": 1, "('n[30,0]', 'n[30,1]', 'n[30,2]')": 1, "('n[31,0]', 'n[31,1]', 'n[31,2]')": 1, "('n[32,0]', 'n[32,1]', 'n[32,2]')": 1, "('n[33,0]', 'n[33,1]', 'n[33,2]')": 1, "('n[34,0]', 'n[34,1]', 'n[34,2]')": 1, "('n[35,0]', 'n[35,1]', 'n[35,2]')": 1, "('n[36,0]', 'n[36,1]', 'n[36,2]')": 1, "('n[37,0]', 'n[37,1]', 'n[37,2]')": 1, "('n[38,0]', 'n[38,1]', 'n[38,2]')": 1, "('n[39,0]', 'n[39,1]', 'n[39,2]')": 1, "('n[40,0]', 'n[40,1]', 'n[40,2]')": 1, "('n[41,0]', 'n[41,1]', 'n[41,2]')": 1, "('n[42,0]', 'n[42,1]', 'n[42,2]')": 1, "('n[43,0]', 'n[43,1]', 'n[43,2]')": 1, "('n[44,0]', 'n[44,1]', 'n[44,2]')": 1, "('n[45,0]', 'n[45,1]', 'n[45,2]')": 1, "('n[46,0]', 'n[46,1]', 'n[46,2]')": 1, "('n[47,0]', 'n[47,1]', 'n[47,2]')": 1, "('n[48,0]', 'n[48,1]', 'n[48,2]')": 1, "('n[49,0]', 'n[49,1]', 'n[49,2]')": 1}
Partially constrained problems

คุณไม่จำเป็นต้องเพิ่มตัวแปรทุกตัวลงใน constraint ตัวแปรใดๆ ที่ไม่ได้อยู่ใน dictionary จะยังคงไม่ถูก constrain ดังนั้นคุณสามารถผสมกลุ่มของตัวแปรที่ถูก hard-constrained กับตัวแปรอิสระในปัญหาเดียวกันได้

2. รันปัญหา

# Solve the problem
partition_job = solver.run(
problem=srepr(cost_function),
constraint=constraint_dict,
backend_name="ibm_marrakesh", # E.g. "ibm_marrakesh"
)

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

# Print the ID so you can use it later, if necessary
print(partition_job.job_id)

# Get job status
print(partition_job.status())
b8085944-f313-444e-be39-ea61b1b47ebd
QUEUED

3. ดูผลลัพธ์

ดึงคำตอบและวิเคราะห์ผลลัพธ์ solution cost แสดงถึงน้ำหนักรวมของ edges ที่ปลายทั้งสองข้างลงเอยในกลุ่มเดียวกัน ดังนั้น cost ที่ต่ำกว่าจึงบ่งชี้ถึงการแบ่ง graph ที่ดีกว่า

partition_result = partition_job.result()
qctrl_cost = partition_result["solution_bitstring_cost"]
solution_bitstring = partition_result["solution_bitstring"]

# Print results
print(f"Total weight of same-group edges: {qctrl_cost}")
print(f"Solution bitstring: {solution_bitstring}")
Total weight of same-group edges: -36.5539
Solution bitstring: 100100100100100001100100100100100100100100100100100001010100010100100100100010001001100100100001100001100001010001001010100100100100100010100100100100

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

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

Changelog

  • 2026-08-10: เพิ่มการรองรับ hard (Hamming weight 1) constraints ผ่าน input constraint และอัปเดตตัวอย่าง constrained optimization ให้ใช้งานสิ่งเหล่านี้

  • 2026-02-11: รองรับ ibm_miami แล้ว

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