メインコンテンツへスキップ

Optimization Solver: Q-CTRL Fire OpalによるQiskit Function

備考

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は、最大規模のIBM® QPU上でもフルデバイススケールで、古典的に困難な問題に対して一貫して正確な解を提供します。

Solverは柔軟性があり、目的関数または任意のグラフとして定義された組み合わせ最適化問題を解くために使用できます。問題をデバイストポロジーにマッピングする必要はありません。制約をペナルティ項として定式化できる場合、制約なしの問題と制約付きの問題の両方を解くことができます。このガイドに含まれる例では、異なるSolver入力タイプを使用して、制約なしおよび制約付きのユーティリティスケール最適化問題を解く方法を示しています。最初の例は156ノード、3-Regularグラフで定義されたMax-Cut問題に関するものであり、2番目の例はコスト関数で定義された50ノードのMinimum Vertex Cover問題に取り組んでいます。

Optimization Solverへのアクセスを取得するには、Q-CTRLにお問い合わせください

機能の説明

Solverは、ハードウェアレベルでのエラー抑制から効率的な問題マッピングおよびクローズドループの古典的最適化まで、アルゴリズム全体を完全に最適化および自動化します。Solverのパイプラインは裏側で各ステージのエラーを削減し、意味のあるスケールアップに必要な強化されたパフォーマンスを実現しています。基礎となるワークフローはQuantum Approximate Optimization Algorithm(QAOA)から着想を得ており、ハイブリッド量子古典アルゴリズムです。Optimization Solverワークフロー全体の詳細な概要については、公開されている論文を参照してください。

Optimization Solverワークフローの視覚化

Optimization Solverで一般的な問題を解くには:

  1. 問題を目的関数、グラフ、またはSparsePauliOpスピンチェーンとして定義します。
  2. Qiskit Functions Catalogを通じて機能に接続します。
  3. Solverで問題を実行し、結果を取得します。

入力と出力

入力

名前説明必須デフォルト
problemstr または SparsePauliOp「受け付ける問題形式」に記載されている表現のいずれかはい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問題クラスの名前。グラフおよびスピンチェーンの問題定義にのみ使用され、「maxcut」または「spin_chain」に限定されます。任意の目的関数の問題定義には不要です。いいえNone"maxcut"
backend_namestr使用するバックエンドの名前いいえインスタンスがアクセスできる最も空いているバックエンド"ibm_fez"
optionsdict以下を含む入力オプション:(任意)session_idstr;デフォルトの動作では新しいSessionが作成されますいいえNone{"session_id": "cw2q70c79ws0008z6g4g"}

受け付ける問題形式:

  • 目的関数の多項式表現。理想的にはPythonで既存のSymPy Polyオブジェクトを使用して作成し、sympy.sreprを使用して文字列にフォーマットします。
  • 特定の問題タイプのグラフ表現。グラフはPythonのnetworkxライブラリを使用して作成する必要があります。その後、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セッションID"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_countintSolverが実行したQAOAイテレーションの総数
variables_to_bitstring_index_mapfloat変数からビット文字列の対応するビットへのマッピング
best_parameterslist[float]全イテレーションにわたって最適化されたベータおよびガンマパラメータ
warningslist[str]QAOAのコンパイルまたは実行中に生成された警告(デフォルトはNone)

ベンチマーク

公開されているベンチマーク結果は、Solverが120量子ビットを超える問題を正常に解き、量子アニーリングやトラップイオンデバイスで以前に公開された結果をも上回ることを示しています。以下のベンチマーク指標は、いくつかの例に基づいた問題タイプの精度とスケーリングの大まかな目安を提供します。実際の指標は、目的関数の項数(密度)とその局所性、変数の数、多項式の次数などのさまざまな問題の特性によって異なる場合があります。

「量子ビット数」は厳密な制限ではなく、非常に一貫した解の精度が期待できる大まかな閾値を表しています。これらの制限を超えた大きな問題サイズも正常に解かれており、これらの制限を超えてテストすることをお勧めします。

任意の量子ビット接続性はすべての問題タイプでサポートされています。

問題タイプ量子ビット数精度合計時間 (s)Runtime使用量 (s)イテレーション数
疎に連結された二次問題1563-regular Max-Cut100%176429316
高次バイナリ最適化156Ising spin-glass model100%146127216
密に連結された二次問題50完全連結Max-Cut100%175826812
ペナルティ項を持つ制約付き問題50エッジ密度8%の重み付きMinimum Vertex Cover100%107421510

はじめに

まず、IBM Quantum APIキーを使用して認証します。次に、以下のようにQiskit Functionを選択します。(このスニペットは、すでにアカウントをローカル環境に保存していることを前提としています。)

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

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

例:制約なし最適化

最大カット(Max-Cut)問題を実行します。以下の例は156ノード、3-regularの非重み付きグラフMax-Cut問題におけるSolverの能力を示していますが、重み付きグラフ問題も解くことができます。 qiskit-ibm-catalogに加え、この例を実行するにはnetworkxnumpyパッケージも使用します。IPythonカーネルを使用してノートブックでこの例を実行する場合は、次のセルのコメントを解除してこれらのパッケージをインストールできます。

# %pip install networkx numpy

1. 問題を定義する

グラフ問題を定義し、problem_type='maxcut'を指定することでMax-Cut問題を実行できます。

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 Functionワークロードのステータスを確認するか、以下のように結果を返します:

# 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例は、一般的な二次制約なしバイナリ最適化問題です。Q-CTRLのOptimization Solverは、制約付き最適化を含むさまざまな問題タイプに使用できます。制約をペナルティ項としてモデル化した多項式として表された問題定義を入力することで、任意の問題タイプを解くことができます。

以下の例では、制約付き最適化問題であるminimum vertex cover(MVC)のコスト関数を構築する方法を示します。 qiskit-ibm-catalogおよびqiskitパッケージに加えて、この例を実行するにはnumpynetworkx、および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の標準的な最適化モデルは以下のように定式化できます。まず、エッジがサブセット内の頂点に接続されていない場合、ペナルティを追加する必要があります。したがって、頂点iiがカバー(つまりサブセット)に含まれる場合はni=1n_i = 1、そうでない場合は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

グラフ内のすべてのエッジは、カバーから少なくとも1つのエンドポイントを含む必要があり、以下の不等式として表すことができます:

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

エッジがカバーの頂点に接続されていないすべての場合は、ペナルティを科す必要があります。これは、PPが正のペナルティ定数であるP(1ninj+ninj)P(1-n_i-n_j+n_i n_j)の形式のペナルティを追加することでコスト関数に表すことができます。したがって、重み付き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ワークロードのステータスを確認するか、以下のように結果を返します:

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にお問い合わせください

次のステップ

おすすめ