Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Optimization Solver: функція Qiskit від Q-CTRL Fire Opal

Дивись довідник API

примітка

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 ти можеш розв'язувати задачі оптимізації утилітарного масштабу на квантовому залізі без потреби в квантовій експертизі. Просто введи визначення задачі на високому рівні — і Solver зробить усе інше. Весь робочий процес є шумостійким і використовує Fire Opal Performance Management «під капотом». Solver стабільно надає точні рішення для класично складних задач навіть у повному масштабі на найбільших QPU від IBM®.

Solver є гнучким і може розв'язувати комбінаторні задачі оптимізації, визначені як цільові функції або довільні графи. Задачі не потрібно відображати на топологію пристрою. Розв'язуються як необмежені, так і обмежені задачі, з обмеженнями, що застосовуються як жорсткі обмеження Хеммінгової ваги 1, а не як штрафні терміни. Приклади, наведені в цьому посібнику, демонструють, як розв'язати необмежену та обмежену задачу оптимізації утилітарного масштабу, використовуючи різні типи вхідних даних для Solver. Перший приклад охоплює задачу max-cut на 156-вузловому 3-регулярному графі, тоді як другий приклад розв'язує задачу розбиття графа на 50 вузлах, визначену через цільову функцію.

Щоб отримати доступ до Optimization Solver, звернись до Q-CTRL.

Опис функції​

Solver повністю оптимізує та автоматизує весь алгоритм — від придушення помилок на рівні заліза до ефективного відображення задачі та замкненої класичної оптимізації. «За лаштунками» конвеєр Solver зменшує помилки на кожному етапі, забезпечуючи підвищену продуктивність, необхідну для реального масштабування. Базовий робочий процес натхненний алгоритмом квантової наближеної оптимізації (QAOA) — гібридним квантово-класичним алгоритмом. Детальний опис повного робочого процесу Optimization Solver наведено в опублікованій статті.

Візуалізація робочого процесу Optimization Solver

Щоб розв'язати загальну задачу за допомогою Optimization Solver:

  1. Визнач свою задачу як цільову функцію, граф або SparsePauliOp спінового ланцюжка.
  2. Підключись до функції через Qiskit Functions Catalog.
  3. Запусти задачу на Solver і отримай результати.

Підтримувані формати задач​

  • Представлення цільової функції у вигляді поліноміального виразу. В ідеалі — створений у Python на основі існуючого об'єкта SymPy Poly та відформатований у рядок за допомогою sympy.srepr.

  • Графове представлення конкретного типу задачі. Граф слід створювати за допомогою бібліотеки networkx у Python. Потім його слід перетворити на рядок за допомогою функції networkx nx.readwrite.json_graph.adjacency_data.

  • Представлення задачі у вигляді спінового ланцюжка. Спіновий ланцюжок має бути представлений як об'єкт SparsePauliOp; детальніше — у документації.

Чи підтримує ця функція всі бекенди IBM?

Якщо ти хочеш використовувати бекенд, який ця функція наразі не підтримує, звернись до Q-CTRL, щоб додати підтримку.

Бенчмарки​

Застереження

Продуктивність може залежати як від конкретного екземпляра задачі, так і від подальших етапів обробки. У деяких випадках класичні та квантово згенеровані вибірки можуть досягати подібної кінцевої якості розв'язку після еквівалентної постобробки. Тому оцінювання має враховувати весь робочий процес оптимізації.

Опубліковані результати бенчмаркінгу показують, що Solver успішно розв'язує задачі на понад 120 кубітах, навіть перевершуючи раніше опубліковані результати на пристроях квантового відпалу та пастки іонів. Наступні метрики бенчмарку дають приблизне уявлення про точність і масштабування типів задач на основі кількох прикладів. Фактичні метрики можуть відрізнятись залежно від різних характеристик задачі, таких як кількість доданків у цільовій функції (щільність) та їхня локальність, кількість змінних і порядок полінома.

«Кількість кубітів», що вказана, не є жорстким обмеженням, а лише приблизними порогами, при яких можна очікувати надзвичайно стабільної точності рішення. Задачі більшого розміру вже були успішно розв'язані, і тестування за межами цих порогів заохочується.

Довільне з'єднання кубітів підтримується для всіх типів задач.

Тип задачіКількість кубітівПрикладТочністьЗагальний час (с)Використання Runtime (с)Кількість ітерацій
Розріджено-зв'язані квадратичні задачі1563-регулярний max-cut100%176429316
Бінарна оптимізація вищого порядку156Ізингова модель спінового скла100%146127216
Щільно-зв'язані квадратичні задачі50Повністю-зв'язаний max-cut100%175826812
Обмежена задача з жорсткими обмеженнями50Розбиття графа із щільністю ребер 8%100%107421510

Початок роботи​

Спочатку пройди автентифікацію за допомогою свого API-ключа IBM Quantum. Потім вибери функцію Qiskit наступним чином. (Цей фрагмент передбачає, що ти вже зберіг свій обліковий запис у локальному середовищі.)

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

_needed = {"networkx": "networkx", "numpy": "numpy", "qiskit_ibm_catalog": "qiskit-ibm-catalog", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "sympy": "sympy"}
_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_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")

Приклад: необмежена оптимізація​

Запусти задачу максимального розрізу (Max-Cut). Наступний приклад демонструє можливості Solver на задачі Max-Cut на 156-вузловому, 3-регулярному незваженому графі, але ти також можеш розв'язувати задачі на зважених графах. Окрім qiskit-ibm-catalog, для виконання цього прикладу тобі знадобляться такі пакети: networkx і numpy. Ти можеш встановити їх, розкоментувавши наступну комірку, якщо запускаєш цей приклад у ноутбуці з ядром IPython.

# %pip install networkx numpy

1. Визнач задачу​

Ти можеш запустити задачу Max-Cut, визначивши задачу у вигляді графа та вказавши 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 приймає рядок як вхідне визначення задачі.

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

2. Запусти задачу​

При використанні графового методу вводу вкажи тип задачі.

# 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 або отримай результати наступним чином:

# 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. Отримай результат​

Отримай оптимальне значення розрізу зі словника результатів.

примітка
Відображення змінних на бітовий рядок може змінитись. Вихідний словник містить підсловник 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

Ти можеш перевірити точність результату, розв'язавши задачу класично за допомогою відкритих солверів, таких як PuLP, якщо граф не є щільно зв'язаним. Для задач із високою щільністю може знадобитись використання просунутих класичних солверів для підтвердження рішення.

Приклад: обмежена оптимізація​

Попередній приклад max-cut — це поширена задача квадратичної безумовної бінарної оптимізації. Optimization Solver від Q-CTRL також може розв'язувати задачі умовної оптимізації, передаючи жорсткі обмеження безпосередньо до Solver через вхід constraint, замість того щоб кодувати їх як штрафні члени в цільовій функції. Наразі Solver підтримує обмеження ваги Гемінга 1: кожне обмеження визначає групу змінних, у якій рівно одна змінна має дорівнювати 1, а решта — 0.

Наступний приклад демонструє, як побудувати цільову функцію та набір жорстких обмежень для задачі умовної оптимізації — розбиття графа, призначаючи кожен вузол графа рівно одній із кількох груп, мінімізуючи при цьому загальну вагу ребер, чиї кінцеві точки опиняються в одній групі. Окрім пакетів qiskit-ibm-catalog і qiskit, для виконання цього прикладу тобі також знадобляться такі пакети: numpy, networkx і sympy. Ти можеш встановити їх, розкоментувавши наступну комірку, якщо запускаєш цей приклад у ноутбуці з ядром IPython.

# %pip install numpy networkx sympy

1. Визнач задачу​

Визнач випадкову задачу розбиття графа, згенерувавши граф із вузлами, що мають випадкові ваги.

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

Стандартну модель оптимізації для зваженого розбиття графа можна сформулювати так. Розділи вузли графа на три групи g∈{0,1,2}g \in \{0, 1, 2\}, і нехай ni,g=1n_{i,g} = 1, якщо вузол ii призначено групі gg, і ni,g=0n_{i,g} = 0 в іншому випадку. Мета — мінімізувати загальну вагу ребер, чиї кінцеві точки призначені одній групі, де вага ребра (i,j)(i,j) — це сумарна вага його двох кінцевих точок, ωi,j=ωi+ωj\omega_{i,j} = \omega_i + \omega_j:

Minimizey=∑(i,j)∈Eωi,j∑gni,g nj,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)]
)

Кожен вузол має бути призначений рівно одній із трьох груп. Це обмеження ваги Гемінга 1: для кожного вузла 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 i∈Vn_{i,0} + n_{i,1} + n_{i,2} = 1 \texttt{ for all } i \in V

Замість того щоб кодувати цю вимогу як штрафний член у цільовій функції, передай її безпосередньо до Solver як жорстке обмеження за допомогою входу 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}
Частково обмежені задачі

Тобі не потрібно додавати кожну змінну до constraint. Будь-яка змінна, не включена до словника, залишається необмеженою, тож ти можеш поєднувати жорстко обмежені групи змінних із вільними змінними в одній задачі.

2. Запусти задачу​

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

Перевір статус робочого навантаження своєї функції Qiskit або отримай результати наступним чином:

# 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. Отримай результат​

Отримай розв'язок і проаналізуй результати. Вартість розв'язку відображає загальну вагу ребер, чиї кінцеві точки опинилися в одній групі, тож нижча вартість означає краще розбиття графа.

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.

Журнал змін​

  • 2026-08-10: Додано підтримку жорстких обмежень (ваги Гемінга 1) через вхід constraint, і оновлено приклад умовної оптимізації, щоб використовувати їх.

  • 2026-02-11: Тепер підтримується ibm_miami

Наступні кроки​