Перейти до основного вмісту

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

примітка

Qiskit Functions — це експериментальна функція, доступна лише користувачам IBM Quantum® Premium Plan, Flex Plan та On-Prem (через IBM Quantum Platform API) Plan. Вона перебуває у статусі попереднього релізу та може змінюватись.

Огляд

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

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

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

Опис функції

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

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

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

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

Входи та виходи

Входи

НазваТипОписОбов'язковийЗа замовчуваннямПриклад
problemstr або SparsePauliOpОдне з представлень, зазначених у розділі «Підтримувані формати задач»ТакНемаєPoly(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Назва класу задачі; використовується лише для визначень задач на графі та спіновому ланцюжку, які обмежені значеннями "maxcut" або "spin_chain"; не потрібний для довільних цільових функційНіNone"maxcut"
backend_namestrНазва бекенду для використанняНіНайменш завантажений бекенд, доступний твоєму інстансу"ibm_fez"
optionsdictВхідні параметри, зокрема: (необов'язковий) session_id: str; за замовчуванням створюється нова сесіяНіNone{"session_id": "cw2q70c79ws0008z6g4g"}

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

  • Представлення цільової функції у вигляді поліноміального виразу. В ідеалі — створений у Python на основі існуючого об'єкта SymPy Poly та відформатований у рядок за допомогою sympy.srepr.
  • Представлення задачі у вигляді графа для конкретного типу задачі. Граф слід створювати за допомогою бібліотеки networkx у Python, а потім перетворювати на рядок функцією networkx [nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.).
  • Представлення задачі у вигляді спінового ланцюжка. Спіновий ланцюжок має бути представлений як об'єкт SparsePauliOp; детальніше — у документації.

Підтримувані бекенди: Виконай наступний код, щоб переглянути список бекендів, які підтримуються на даний момент. Якщо твій пристрій не вказано у списку, звернись до 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')>]

Параметри:

НазваТипОписЗа замовчуванням
session_idstrІдентифікатор існуючої сесії Qiskit Runtime"cw4r3je6f0t010870y3g"
job_tagslist[str]Список тегів завдань[]

Виходи

НазваТипОписПриклад
resultdict[str, Any]Рішення та метадані, перелічені у розділі «Вміст словника результатів»{'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': []}

Вміст словника результатів:

НазваТипОпис
solution_bitstring_costfloatНайкраща мінімальна вартість по всіх ітераціях
final_bitstring_distributionCountsDictСловник лічильників бітових рядків, що відповідає мінімальній вартості
solution_bitstringstrБітовий рядок із найкращою вартістю в фінальному розподілі
iteration_countintЗагальна кількість ітерацій QAOA, виконаних Solver
variables_to_bitstring_index_mapfloatВідображення змінних на відповідні біти у бітовому рядку
best_parameterslist[float]Оптимізовані параметри beta та gamma по всіх ітераціях
warningslist[str]Попередження, що виникли під час компіляції або виконання QAOA (за замовчуванням None)

Бенчмарки

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

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

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

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

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

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

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# 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
)

Вихід попередньої комірки коду

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

# Get job status
print(maxcut_job.status())
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: 209.0

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

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

Попередній приклад Max-Cut є класичною квадратичною необмеженою задачею бінарної оптимізації. Optimization Solver від Q-CTRL може використовуватись для різних типів задач, зокрема обмеженої оптимізації. Ти можеш розв'язувати довільні типи задач, подаючи визначення задачі у вигляді полінома, де обмеження моделюються як штрафні доданки.

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

# %pip install numpy networkx sympy

1. Визначте задачу

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

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)

Вихід попередньої комірки коду

Стандартна модель оптимізації для зваженого MVC може бути сформульована наступним чином. Спочатку необхідно додати штраф для будь-якого випадку, коли ребро не з'єднане з вершиною з підмножини. Тому нехай ni=1n_i = 1, якщо вершина ii входить до покриття (тобто знаходиться у підмножині), і ni=0n_i = 0 в іншому випадку. По-друге, мета полягає у мінімізації загальної кількості вершин у підмножині, що може бути виражено такою функцією:

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

Тепер кожне ребро в графі повинне мати хоча б одну кінцеву точку з покриття, що можна виразити як нерівність:

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

Будь-який випадок, коли ребро не з'єднане з вершиною покриття, має бути оштрафований. Це можна представити у цільовій функції, додавши штрафний доданок виду P(1ninj+ninj)P(1-n_i-n_j+n_i n_j), де PP — це позитивна штрафна константа. Таким чином, необмежена альтернатива обмеженій нерівності для зваженого 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 або отримай результати наступним чином:

print(mvc_job.status())

3. Отримайте результат

Отримай рішення та проаналізуй результати. Оскільки ця задача має зважені вузли, рішення — це не просто мінімальна кількість покритих вузлів. Натомість вартість рішення представляє суму ваг вершин, що включені до покриття вершин. Вона відображає загальну «вартість» або «вагу» покриття всіх ребер у графі за допомогою вибраних вершин.

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.

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