SQOA Solver - JIJ 社による Qiskit Function
API リファレンスをご覧ください。
Qiskit Functions は実験的な機能であり、IBM Quantum® Premium Plan、Flex Plan、On-Prem (IBM Quantum Platform API 経由) Plan のユーザーのみが利用できます。プレビューリリースの状態であり、変更される場合があります。
概要
JIJ 社の SQOA Solver を使うと、IBM® の量子コンピューターで大規模な組合せ最適化問題を解くことができます。このソルバーは、Sampling-based Quantum Optimization Algorithm with Quantum Relaxation (SQOA-QR) [1] に基づいています。古典的な決定変数と量子ビットを一対一で対応させる量子アルゴリズムとは異なり、SQOA Solver は、現在の量子デバイスで利用可能な量子ビット数よりも多くの変数を持つ最適化問題を扱えます。また、サンプリングに基づく設計のため、現在の量子ハードウェアの物理ノイズの影響を受けにくくなっています。これらの特長により、SQOA Solver は、量子コンピューティングの現段階における最適化問題に適しています。
このソルバーは、古典的な数式として与えられた二次制約なし二値最適化 (QUBO) 問題を扱い、古典的な定式化を量子の設定に自動的に変換します。ペナルティ法を使えば、制約付きの問題を制約なしの問題に変換することもできます。
以降のセクションでは、ソルバーのワークフロー、ベンチマーク結果、そして qiskit-addon-opt-mapper と、必要に応じて Open Mathematical prograMming eXchange (OMMX) で問題をモデル化するエンドツーエンドの例を説明します。OMMX は、ソフトウェアツール間での最適化問題のやり取りを簡単にする、オープンなデータ形式および SDK です。
説明
SQOA Solver は Sampling-based Quantum Optimization Algorithm with Quantum Relaxation (SQOA-QR) を実装し、その前後に古典的な前処理と後処理を組み合わせています。このセクションでは、SQOA フレームワークと、それが Quantum Approximate Optimization Algorithm (QAOA) とどう違うのかを説明します。
次の図は、SQOA Solver のワークフローの概略を示しています。
SQOA フレームワーク
SQOA は、組合せ最適化のためのサンプリングベースのフレームワークです [1]。これは 2 つの技術を組み合わせています。1 つは、問題を量子緩和として符号化する Quantum Random Access Optimization (QRAO) [2] です。もう 1 つは、sample-based quantum diagonalization (SQD) [3] で、quantum-selected configuration interaction (QSCI) [4] としても知られ、量子コンピューターをビット列のサンプリングにのみ使い、低エネルギー状態を古典コンピューターで求めます。SQOA は、この組合せを固定しつつ、変数を量子ビットに割り当てる方法やサンプルを生成する回路など、詳細は各実装に委ねるという意味でのフレームワークです。
-
QRAO による量子緩和。 QAOA などの Ising ベースの手法では、各二値変数をその量子ビットの 演算子で表現します。QRAO では 演算子と 演算子も使います。(3,1) 量子ランダムアクセスコード (QRAC) では、最大 3 つの二値変数が 1 つの量子ビットを共有し、各変数は異なるパウリ演算子で表現されます。
これにより QUBO は、Ising ハミルトニアンよりも少ない量子ビットで済む緩和ハミルトニアンになります。QUBO で相互作用する変数は、異なる量子ビットに配置しなければなりません。同じ量子ビットを共有すると、それらのパウリ演算子の積がその量子ビット上の単一の演算子に簡約され (たとえば )、緩和ハミルトニアンがそれらの相互作用を表現できなくなってしまいます。
したがって、変数の量子ビットへの割り当てが、量子ビット数と緩和の質の両方を決めるため、SQOA Solver は前処理でこの割り当てを設計します。
-
Sample-based quantum diagonalization (SQD)。 量子コンピューターはまず、緩和ハミルトニアンの低エネルギー状態と重なりを持つ量子状態を準備し、そこからビット列をサンプリングします。古典コンピューターは、緩和ハミルトニアンをサンプリングされたビット列が張る部分空間に射影し、対角化して基底状態を近似します。
緩和ハミルトニアンは非対角であるため、対角化ではサンプリングされたビット列が重ね合わせに組み合わされます。対角な Ising ハミルトニアンの場合、すべてのビット列がすでに固有状態であり、対角化は最良のサンプルを選ぶだけになってしまいます。同じアプローチは、量子化学における大規模な問題にも適用されています。
SQD で得られる近似基底状態は、元の問題の古典的な解に直接対応しないため、そこから古典的な解を復号する必要があります。この状態は古典コンピューターで計算されるため、丸め (rounding) と呼ばれる復号は古典的な後処理です。SQOA Solver は、ヒューリスティックにより良い古典解を得るよう設計された復号技術を使用します。
次の表は、SQOA を、同じクラスの問題に対する一般的な変分アプローチである QAOA と比較したものです。
| QAOA | SQOA | |
|---|---|---|
| 個の二値変数に必要な量子ビット数 | 1 つの量子ビットに最大 3 つの変数を保持します。正確な数は問題の構造によって決まります | |
| QPU の使い方 | 古典的な最適化ループの中で、パラメーター化された回路のエネルギーを繰り返し推定します | ビット列のサンプリングのみを行います。QPU 上で回路パラメーターの学習は行いません |
| サンプルから解へ | 測定されたビット列のうち最良のものを選びます | サンプリングされた部分空間で緩和ハミルトニアンを古典コンピューター上で対角化し、得られた状態を丸めます |
SQOA-QR の前後で、SQOA Solver は古典的な処理を追加します。前処理では、最適値をあらかじめ決定できる変数を固定し、変数の量子ビットへの割り当てを設計します。後処理では、丸められた解を局所探索で改良します。これは postprocessing_level=0 でオフにできます。
ベンチマーク
性能は、問題インスタンスと後続の処理ステップの両方に左右されることがあります。場合によっては、同等の後処理を行った後で、古典サンプルと量子生成サンプルが同程度の最終的な解の品質に達することもあります。したがって、評価では最適化ワークフロー全体を考慮する必要があります。
次のベンチマーク指標は、選択した問題サイズと構造における解の品質、量子ビットの圧縮率、実行時間の一般的な目安を示します。表には、Quantum Optimization Benchmarking Library (QOBLIB) の最大独立集合インスタンスの一部と、2 つの 3-正則 MaxCut インスタンスを掲載しています。ommx-quantum-benchmarks パッケージを使うと、QOBLIB インスタンスを最適解とともに ommx.v1.Instance オブジェクトとして読み込めます。
各インスタンスは、IBM Heron r2 プロセッサー上で 10 回実行しました。Number of qubits は、前処理と QRAC 符号化の後にハードウェア上で実際に使われる量子ビット数です。圧縮率はインスタンスによって異なります。Approximation ratio (AR) は、10 回の実行で見つかった最良解の品質を示します。
ここで は見つかった最良の目的関数値、 は既知の最適目的関数値です。すべてのインスタンスは最適値が正の最大化問題なので、AR は最大でも 100% であり、Approximation ratio 列の 100% は、ソルバーが最適解を見つけたことを示します。Total time は、1 回の実行あたりの QPU 時間と CPU 時間を合わせた平均です。QPU time は、1 回の実行あたりの IBM Quantum Compute 使用量の平均です。
| インスタンス | 変数の数 | 量子ビット数 | 近似比 | 合計時間 (秒) | QPU 時間 (秒) |
|---|---|---|---|---|---|
| maxcut_384_nodes | 384 | 129 | 98.5% | 62.1 | 12.0 |
| gen200_p0-9_44 | 200 | 67 | 100% | 38.0 | 12.0 |
| es60fst02 | 186 | 42 | 100% | 24.7 | 12.0 |
| es60fst04 | 162 | 29 | 100% | 17.9 | 12.0 |
| maxcut_150_nodes | 150 | 51 | 100% | 23.1 | 12.0 |
| sloane_1dc_128 | 128 | 37 | 93.8% | 19.1 | 12.0 |
| sloane_2dc_128 | 128 | 8 | 100% | 12.3 | 11.5 |
| C125-9 | 125 | 43 | 100% | 22.3 | 12.0 |
| es60fst01 | 123 | 12 | 100% | 13.6 | 12.0 |
| es60fst03 | 113 | 26 | 100% | 14.9 | 12.0 |
| sloane_1dc_64 | 64 | 16 | 100% | 13.4 | 11.9 |
| chesapeake | 39 | 13 | 100% | 13.1 | 12.0 |
| football | 35 | 6 | 100% | 12.5 | 12.0 |
| karate | 34 | 2 | 100% | 6.2 | 6.0 |
| ibm32 | 32 | 11 | 100% | 12.9 | 12.0 |
| farm | 17 | 4 | 100% | 8.5 | 8.0 |
はじめに
この Qiskit Function へのアクセス情報は、Qiskit Functions Catalog で確認できます。
このセクションでは、認証と関数の読み込み方法、そして qiskit-addon-opt-mapper の OptimizationProblem を使って、SQOA Solver が受け付ける辞書形式で問題を準備する方法を示します。続く例では、関数を実行して結果を読み取ります。
関数を認証して読み込む
まず、IBM Quantum API キーで認証します。(次のコードは、すでにローカル環境にアカウントを保存していることを前提としています。) 次に、次のようにカタログクライアントを作成します。
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"jijmodeling": "jijmodeling", "networkx": "networkx", "ommx": "ommx", "qiskit_addon_opt_mapper": "qiskit-addon-opt-mapper", "qiskit_ibm_catalog": "qiskit-ibm-catalog"}
_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(jij/sqoa-solver)]
solver = catalog.load("jij/sqoa-solver")
qiskit-addon-opt-mapper で入力を準備する
このガイドでは、qiskit-addon-opt-mapper の OptimizationProblem クラスで問題をモデル化します。SQOA Solver は、IBM の最適化関数で使われるタプル文字列形式の係数辞書として QUBO 問題を受け付けるため、このガイドのすべての例は、同じクライアント側の準備パターンに従います。
-
モデルを記述する。
OptimizationProblemを作成し、二値決定変数を追加して、目的関数と必要な制約を設定します。 -
QUBO に変換する。
OptimizationProblemToQuboで問題を変換します。これは、制約をペナルティ項として目的関数に加え、等価な制約なしの最小化問題を返します。 -
辞書に変換する。 QUBO を、次のセクションで定義する
to_problem_dict()ヘルパー関数に渡し、その結果をvariable_type="binary"とともにproblemとしてsolver.run()に渡します。
SQOA Solver は辞書形式の問題を常に最小化します。これは OptimizationProblemToQubo が返す最小化問題と一致します。そのため、最大化問題では、返される objective_value は元の目的関数の符号を反転した値になります。元の問題の観点で結果を読み取るには、例で示すように、返された割り当てを OptimizationProblem で評価します。辞書形式の詳細は、API リファレンスの対応している問題形式を参照してください。
# %pip install qiskit-addon-opt-mapper
次のヘルパー関数を一度定義し、すべての例で再利用します。これは、OptimizationProblemToQubo が返す QUBO を辞書形式に変換します。キーには QUBO の変数インデックスが使われるため、結果の解のキー x_0、x_1 などは、そのインデックスに対応します。返される解がすべての変数をカバーするよう、係数がゼロであっても、すべての変数に線形項が設定されます。
from qiskit_addon_opt_mapper import OptimizationProblem
from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo
def to_problem_dict(qubo: OptimizationProblem) -> dict[str, float]:
"""Convert a QUBO from OptimizationProblemToQubo to the SQOA Solver dictionary format."""
objective = qubo.objective
payload = {"()": objective.constant}
# Keep every variable in the problem, even with a zero linear coefficient.
payload.update({f"({i},)": 0.0 for i in range(qubo.get_num_vars())})
for i, coefficient in objective.linear.to_dict().items():
payload[f"({i},)"] += coefficient
for (i, j), coefficient in objective.quadratic.to_dict().items():
# Fold a binary diagonal term x_i * x_i = x_i into the linear term.
key = f"({i},)" if i == j else f"({i}, {j})"
payload[key] = payload.get(key, 0.0) + coefficient
return {key: float(value) for key, value in payload.items()}
例 1: 3-正則グラフ上の MaxCut
MaxCut のような多くのグラフ問題は NP 困難であり、量子最適化の自然な候補です。この例を実行するには、追加の依存関係として networkx をインストールします。
# %pip install networkx
1. モデルを記述する
グラフ の各頂点に二値変数 を割り当て、その頂点がカットのどちら側にあるかを示します。辺は、両端点が異なる側にあるときにカットされるため、カットサイズは次のとおりです。
この目的関数は、制約のないそのままの QUBO なので、OptimizationProblemToQubo は最大化を等価な最小化に変えるだけで、変数はそのままです。qiskit_addon_opt_mapper.applications の Maxcut アプリケーションクラスを使うと、Maxcut(G).to_optimization_problem() で同じモデルを構築できます。
import networkx as nx
G = nx.random_regular_graph(3, 40, seed=42)
linear = {v: 0 for v in G.nodes}
quadratic = {}
for u, v in G.edges:
linear[u] += 1
linear[v] += 1
quadratic[(u, v)] = -2
maxcut = OptimizationProblem("MaxCut")
maxcut.binary_var_list(G.number_of_nodes(), name="x")
maxcut.maximize(linear=linear, quadratic=quadratic)
2. ソルバーを実行する
モデルを QUBO、さらに辞書に変換して送信します。problem と variable_type は必須です。backend_name は任意です。省略した場合や None の場合、関数はインスタンスで利用可能な、最も待ちの少ない稼働中の QPU を選択します。
options 辞書は、shots、seed、session、postprocessing_level、job_tags を受け付けます。説明とデフォルト値は、API リファレンスを参照してください。
qubo = OptimizationProblemToQubo().convert(maxcut)
job = solver.run(
problem=to_problem_dict(qubo),
variable_type="binary",
backend_name="ibm_fez",
options={
"shots": 4096,
"seed": 42,
},
)
3. 結果を取得する
job.result() は、4 つのキーを持つ辞書を返します。
-
solutionは完全な変数の割り当てで、x_iをキーとします。iは変数のインデックスです (たとえばx_3)。 -
objective_valueとfeasibleは、送信した辞書で評価した、その割り当ての目的関数値と実行可能性です。 -
metadataはsolver_infoとresource_usageを保持します。
QUBO は MaxCut の最小化形式であるため、objective_value はカットサイズの符号を反転した値です。カットサイズを直接読み取るには、元の OptimizationProblem で割り当てを評価します。
result = job.result()
x = [result["solution"][f"x_{i}"] for i in range(maxcut.get_num_vars())]
print(f"Cut value: {maxcut.objective.evaluate(x)}")
print(f"Nodes on side 1: {[v for v, value in enumerate(x) if value > 0.5]}")
Cut value: 55.0
Nodes on side 1: [5, 7, 9, 11, 14, 16, 17, 19, 21, 23, 24, 26, 30, 31, 33, 34, 36, 37, 38]
4. リソース使用量を確認する
result["metadata"]["resource_usage"] は、各実行フェーズで費やされた時間を報告します。RUNNING: EXECUTING_QPU 以外のフェーズは、利用可能な場合、そのフェーズの実時間 (秒) である CPU_TIME を報告します。
RUNNING: EXECUTING_QPU フェーズは、すべてのジョブで最終的な使用量が利用可能な場合、その実行におけるすべての Quantum Compute ジョブの QPU 使用量である QPU_TIME を報告します。詳細は、API リファレンスを参照してください。
for phase, metrics in result["metadata"]["resource_usage"].items():
line = f"{phase:36s}"
if "CPU_TIME" in metrics:
line += f" CPU {metrics['CPU_TIME']:8.1f} s"
if "QPU_TIME" in metrics:
line += f" QPU {metrics['QPU_TIME']:8.1f} s"
print(line)
RUNNING: MAPPING CPU 3.2 s
RUNNING: OPTIMIZING_FOR_HARDWARE CPU 5.0 s
RUNNING: WAITING_FOR_QPU CPU 1.1 s
RUNNING: EXECUTING_QPU QPU 3.0 s
RUNNING: POST_PROCESSING CPU 0.9 s
例 2: 制約付き問題を QUBO に変換する
SQOA Solver は制約なしの問題を受け付けます。この例では、OptimizationProblemToQubo で制約付き問題を QUBO に変換し、結果を元の問題の変数に対応付け直す方法を示します。
selection = OptimizationProblem("ConstrainedExample")
selection.binary_var_list(2, name="x")
selection.maximize(linear={"x0": 1, "x1": 1})
selection.linear_constraint({"x0": 1, "x1": 1}, "<=", 1, name="at_most_one")
print(selection.prettyprint())
Problem name: ConstrainedExample
Maximize
x0 + x1
Subject to
Linear constraints (1)
x0 + x1 <= 1 'at_most_one'
Binary variables (2)
x0 x1
OptimizationProblemToQubo は、制約をペナルティ項として目的関数に加え、問題を最小化に変換します。適切なペナルティの重みは問題によって異なります。
実行後、interpret() は QUBO の割り当てを元の問題の変数に対応付け直します。
元の問題で目的関数値と実行可能性を評価します。辞書形式の問題では、辞書に制約がないため、結果の feasible フィールドは変数の定義域のみを指します。
converter = OptimizationProblemToQubo(penalty=4.0)
qubo = converter.convert(selection)
job = solver.run(
problem=to_problem_dict(qubo),
variable_type="binary",
backend_name="ibm_fez",
)
result = job.result()
x_qubo = [result["solution"][f"x_{i}"] for i in range(qubo.get_num_vars())]
x = converter.interpret(x_qubo)
print("Objective:", selection.objective.evaluate(x))
print("Feasible:", selection.is_feasible(x))
print("x values:", x)
Objective: 1.0
Feasible: True
x values: [1. 0.]
例 3: OMMX インスタンスを送信する
SQOA Solver は、OMMX SDK の ommx.v1.Instance も受け付けます。これは、変数名、添字、変換メタデータを問題とともに保持します。辞書と比べて、OMMX 入力には次の利点があります。
-
結果には、
solver_info["ommx_solution"]に正規のommx.v1.Solutionも含まれます。 -
objective_valueはインスタンスの意味で評価されるため、最大化問題の目的関数は符号反転されません。 -
Instance.to_qubo()でその場で変換したインスタンスでは、feasibleは元の制約に対して評価されます。
この例では、モデルを OMMX インスタンスにコンパイルする数理最適化モデラーである JijModeling を使って、例 1 の MaxCut モデルを構築します。JijModeling をインストールします。これにより ommx パッケージもインストールされます。
# %pip install "jijmodeling~=2.8.0"
SQOA Solver は、OMMX データを標準のパディング付き Base64 文字列としてやり取りします。この変換のために、次のヘルパー関数を定義します。
-
to_problem_payload()は、ommx.v1.Instanceを、problemとして渡す文字列にシリアライズします。 -
to_ommx_solution()は、結果から正規のommx.v1.Solutionをデコードします。
import base64
from ommx.v1 import Instance, Solution
def to_problem_payload(instance: Instance) -> str:
"""Serialize an OMMX instance into the `problem` string for SQOA Solver."""
return base64.b64encode(instance.to_bytes()).decode()
def to_ommx_solution(result: dict) -> Solution:
"""Decode the canonical OMMX solution from a SQOA Solver result."""
payload = result["metadata"]["solver_info"]["ommx_solution"]
return Solution.from_bytes(base64.b64decode(payload, validate=True))
1. JijModeling でモデルを記述する
グラフ 上の MaxCut の一般的なスピン定式化では、各頂点に を割り当てます。辺は のときにカットされるため、カットサイズは次のとおりです。
JijModeling にはネイティブなスピン決定変数がなく、SQOA Solver は二値の QUBO 変数を受け付けます。そのため、 を宣言し、モデルの中でスピン値の式 を定義します。この式により、目的関数は標準的なスピン形式のままとなり、 コンパイルされたインスタンスは二値の QUBO のままになります。
import jijmodeling as jm
@jm.Problem.define("MaxCut", sense=jm.ProblemSense.MAXIMIZE)
def maxcut_model(problem: jm.DecoratedProblem):
V = problem.Natural() # number of nodes
E = problem.Graph(dtype=V) # edge list
b = problem.BinaryVar(shape=(V,))
s = 1 - 2 * b # spin-valued expression
problem += jm.sum((1 - s[u] * s[v]) / 2 for u, v in E)
ノートブックで maxcut_model を表示すると、次の定式化が表示されます。
2. インスタンスをコンパイルしてソルバーを実行する
例 1 のグラフをバインドして ommx.v1.Instance をコンパイルし、variable_type="binary" を指定して送信します。OMMX インスタンスは常に二値問題として送信されます。
instance = maxcut_model.eval(
{"V": G.number_of_nodes(), "E": [list(edge) for edge in G.edges]}
)
job = solver.run(
problem=to_problem_payload(instance),
variable_type="binary",
backend_name="ibm_fez",
)
3. 結果を取得する
OMMX 入力では、objective_value はカットサイズであり、to_ommx_solution() は正規の解をデコードするため、変数名で割り当てを読み取れます。
result = job.result()
print(f"Cut value: {result['objective_value']}")
solution = to_ommx_solution(result)
b_values = solution.extract_decision_variables("b")
print(
f"Nodes on side 1: {sorted(v for (v,), value in b_values.items() if value > 0.5)}"
)
Cut value: 55.0
Nodes on side 1: [5, 7, 9, 11, 14, 16, 17, 19, 21, 23, 24, 26, 30, 31, 33, 34, 36, 37, 38]
サポート
サポートが必要な場合は、info@j-ij.com にメールをお送りください。
次のステップ
- このフォームに記入して、SQOA Solver の無料トライアルを申し込みます。
- この Qiskit Function の API リファレンスをご覧ください。
- 最大独立集合問題のチュートリアルで、エンドツーエンドの例を試してみましょう。
- qiskit-addon-opt-mapper で最適化問題をモデル化する方法を学びます。
- JijModeling と OMMX で最適化問題をモデル化し、やり取りする方法を学びます。
- SQOA フレームワークについて詳しく知るには、SQOA-QR の論文 [1] をお読みください。
参考文献
[1] Matsuyama, H., & Yamashiro, Y. (2025). Sampling-based quantum optimization algorithm with quantum relaxation. 2025 IEEE International Conference on Quantum Computing and Engineering (QCE), 1, 2323–2333. DOI, arXiv:2504.12629
[2] Fuller, B., Hadfield, C., Glick, J. R., Imamichi, T., Itoko, T., Thompson, R. J., Jiao, Y., Kagele, M. M., Blom-Schieber, A. W., Raymond, R., et al. (2024). Approximate solutions of combinatorial problems via quantum relaxations. IEEE Transactions on Quantum Engineering, 5, 1–15. DOI, arXiv:2111.03167
[3] Robledo-Moreno, J., Motta, M., Haas, H., Javadi-Abhari, A., Jurcevic, P., Kirby, W., et al. (2025). Chemistry beyond the scale of exact diagonalization on a quantum-centric supercomputer. Science Advances, 11(25), eadu9991. DOI, arXiv:2405.05068
[4] Kanno, K., Kohda, M., Imai, R., Koh, S., Mitarai, K., Mizukami, W., & Nakagawa, Y. O. (2026). Quantum-selected configuration interaction: Classical diagonalization of Hamiltonians in subspaces selected by quantum computers. Physical Review Research, 8(2), 023268. DOI, arXiv:2302.11320