Kipu QuantumのIskay量子オプティマイザーでMarket Split問題を解く
Qiskit Functionsは、IBM Quantum® Premium Plan、Flex Plan、およびOn-Prem(IBM Quantum Platform API経由)Planのユーザーのみが利用できる実験的な機能です。プレビューリリースの状態であり、変更される可能性があります。
使用量の目安: Heron r2プロセッサで20秒。(注意: これはあくまで目安です。実際の実行時間は異なる場合があります。)
背景
このチュートリアルでは、Kipu QuantumのIskay量子オプティマイザー [1]を使用してMarket Split問題を解く方法を説明します。Market Split問題は、正確な需要目標を満たすために市場をバランスの取れた販売地域に分割する、実世界のリソース割り当て課題を表しています。
Market Splitの課題
Market Split問題は、リソース割り当てにおける一見単純でありながら計算的に非常に困難な課題です。種類の製品をの異なる市場で販売する企業を考えてみましょう。各市場は特定の製品の組み合わせを購入します(行列の列で表されます)。ビジネス上の目的は、これらの市場を2つのバランスの取れた販売地域に分割し、各地域がすべての製品について総需要のちょうど半分を受け取るようにすることです。
数学的定式化:
二値の割り当てベクトルを求めます。ここで:
- は市場を地域Aに割り当てることを意味します
- は市場を地域Bに割り当てることを意味します
- 制約が満たされなければなりません。ここでは目標売上(通常、製品ごとの総需要の半分)を表します
コスト関数:
この問題を解くために、制約違反の二乗和を最小化します:
ここで:
- は市場における製品の売上を表します
- は市場の二値割り当てです
- は各地域における製品の目標売上です
- コストはすべての制約が満たされたときに正確にゼロになります
和の各項は、特定の製品の目標売上からの二乗偏差を表しています。このコスト関数を展開すると、次のようになります:
は定数であるため、の最小化は二次関数の最小化と等価であり、これはまさにQUBO(二次制約なし二値最適化)問題です。
計算量の複雑さ:
ビジネス上の解釈は分かりやすいにもかかわらず、この問題は顕著な計算困難性を示します:
- 小規模での失敗: 従来の混合整数計画法ソルバーは、1時間のタイムアウト下でわずか7製品のインスタンスでも失敗します [4]
- 指数的な増大: 解空間は指数的に増大し(通りの割り当て)、総当たり探索は実行不可能です
この深刻な計算上の障壁と、テリトリー計画やリソース割り当てにおける実用的な関連性が相まって、Market Split問題は量子最適化アルゴリズムの理想的なベンチマークとなっています [4]。
Iskayのアプローチが独自である理由
Iskayオプティマイザーは、**bf-DCQO(バイアス場デジタル化反断熱量子最適化)**アルゴリズム [1]を使用しており、これは量子最適化における重要な進歩を表しています:
回路の効率性: bf-DCQOアルゴリズムは驚くべきゲート削減を達成します [1]:
- デジタル量子アニーリング(DQA)と比較して最大10倍少ないエンタングリングゲート
- 大幅に浅い回路により以下が可 能になります:
- 量子実行中のエラー蓄積の低減
- 現在の量子ハードウェアでより大きな問題に取り組む能力
- エラー軽減技術が不要
非変分設計: 約100回の反復を必要とする変分アルゴリズムとは異なり、bf-DCQOは通常約10回の反復のみで済みます [1]。これは以下によって達成されます:
- 測定された状態分布からのインテリジェントなバイアス場計算
- 前回の解に近いエネルギー状態からの各反復の開始
- 局所探索を統合した古典的な後処理
反断熱プロトコル: このアルゴリズムは、短い発展時間中の不要な量子励起を抑制する反断熱項を組み込んでおり、急速な遷移でもシステムが基底状態の近くに留まることを可能にします [1]。
前提条件
このチュートリアルを始める前に、以下がインストールされていることを確認してください:
- Qiskit IBM Runtime (
pip install qiskit-ibm-runtime) - Qiskit Functions (
pip install qiskit-ibm-catalog) - NumPy (
pip install numpy) - Requests (
pip install requests) - Opt Mapper Qiskitアドオン (
pip install qiskit-addon-opt-mapper)
また、Qiskit FunctionsカタログからIskay量子オプティマイザー関数へのアクセスを取得する必要があります。
セットアップ
まず、このチュートリアルに必要なすべてのパッケージをインポートします。
# Added by doQumentation — installs packages not in the Binder environment
%pip install -q qiskit-addon-opt-mapper
import os
import tempfile
import time
from typing import Tuple, Optional
import numpy as np
import requests
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_addon_opt_mapper import OptimizationProblem
from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo
print("All required libraries imported successfully")
IBM Quantum資格情報の設定
IBM Quantum® Platformの資格情報を定義します。以下が必要です:
- APIトークン: IBM Quantum Platformの44文字のAPIキー
- インスタンスCRN: IBM Cloud®のインスタンス識別子
token = "<YOUR_API_KEY>"
instance = "<YOUR_INSTANCE_CRN>"
ステップ1: 古典的な入力を量子問題にマッピングする
古典的な問題を量子互換の表現にマッピングすることから始めます。このステップでは以下を行います:
- Iskay量子オプティマイザーへの接続
- Market Split問題の読み込みと定式化
- 問題を解くbf-DCQOアルゴリズムの理解
Iskay量子オプティマイザーへの接続
まず、Qiskit Functionsカタログへの接続を確立し、Iskay量子オプティマイザーを読み込みます。IskayオプティマイザーはKipu Quantumが提供する量子関数であり、量子ハードウェア上で最適化問題を解くためのbf-DCQOアルゴリズムを実装しています。
catalog = QiskitFunctionsCatalog(token=token, instance=instance)
iskay_solver = catalog.load("kipu-quantum/iskay-quantum-optimizer")
print("Iskay optimizer loaded successfully")
print("Ready to solve optimization problems using bf-DCQO algorithm")
問題の読み込みと定式化
問題データ形式の理解
QOBLIB(量子最適化ベンチマーキングライブラリ)[2]の問題インスタンスは、シンプルなテキスト形式で保存されています。対象インスタンスms_03_200_177.datの実際の内容を確認しましょう:
3 20
60 92 161 53 97 2 75 81 6 139 132 45 108 112 181 93 152 200 164 51 1002
176 196 41 143 2 88 0 79 10 71 75 148 82 135 34 187 33 155 58 46 879
68 68 179 173 127 163 48 49 99 78 44 52 173 131 73 198 84 109 180 95 1040
形式の構造:
-
1行目:
3 203= 製品数(制約数/行列の行数)20= 市場数(変数数/行列の列数)
-
次の3行: 係数行列と目標ベクトル
- 各行には21個の数値があります:最初の20個は行の係数、最後の1つが目標値です
- 2行目:
60 92 161 ... 51 | 1002- 最初の20個の数値: 20の市場それぞれにおける製品1の販売量
- 最後の数値(1002): 一方の地域にお ける製品1の目標売上
- 3行目:
176 196 41 ... 46 | 879- 各市場における製品2の販売量と目標値(879)
- 4行目:
68 68 179 ... 95 | 1040- 各市場における製品3の販売量と目標値(1040)
ビジネス上の解釈:
- 市場0の販売量: 製品1が60単位、製品2が176単位、製品3が68単位
- 市場1の販売量: 製品1が92単位、製品2が196単位、製品3が68単位
- 20の市場すべてについて同様に続きます...
- 目標: これら20の市場を2つの地域に分割し、各地域が製品1をちょうど1002単位、製品2を879単位、製品3を1040単位受け取るようにします
QUBOへの変換
制約からQUBOへ:数学的変換
量子最適化の力は、制約付き問題を制約なしの二次形式に変換することにあります [4]。Market Split問題では、等式制約
(ここで)を、制約違反にペナルティを課すことでQUBOに変換します。
ペナルティ法: を正確に成り立たせる必要があるため、二乗違反を最小化します:
これはすべての制約が満たされたときに正確にゼロになります。代数的に展開すると:
QUBO目的関数: は定数であるため、最適化は以下のようになります:
重要な知見: この変換は近似ではなく厳密です。等式制約は補助変数やペナルティパラメータを必要とせずに自然に二次形式に二乗されます。これにより、量子ソルバーにとって数学的にエレガントかつ計算効率の高い定式化が得られます [4]。制約付き問題を定義するためにOptimizationProblemクラスを使用し、次にqiskit_addon_opt_mapperパッケージのOptimizationProblemToQuboを使用してQUBO形式に変換します。これによりペナルティベースの変換が自動的に処理されます。
データ読み込みとQUBO変換関数の実装
ここで3つのユーティリティ関数を定義します:
parse_marketsplit_dat()-.datファイル形式を解析し、行列とを抽出しますfetch_marketsplit_data()- QOBLIBリポジトリから問題インスタンスを直接ダウンロードします
def parse_marketsplit_dat(filename: str) -> Tuple[np.ndarray, np.ndarray]:
"""
Parse a market split problem from a .dat file format.
Parameters
----------
filename : str
Path to the .dat file containing the market split problem data.
Returns
-------
A : np.ndarray
Coefficient matrix of shape (m, n) where m is the number of products
and n is the number of markets.
b : np.ndarray
Target vector of shape (m,) containing the target sales per product.
"""
with open(filename, "r", encoding="utf-8") as f:
lines = [
line.strip()
for line in f
if line.strip() and not line.startswith("#")
]
if not lines:
raise ValueError("Empty or invalid .dat file")
# First line: m n (number of products and markets)
m, n = map(int, lines[0].split())
# Next m lines: each row of A followed by corresponding element of b
A, b = [], []
for i in range(1, m + 1):
values = list(map(int, lines[i].split()))
A.append(values[:-1]) # First n values: product sales per market
b.append(values[-1]) # Last value: target sales for this product
return np.array(A, dtype=np.int32), np.array(b, dtype=np.int32)
def fetch_marketsplit_data(
instance_name: str = "ms_03_200_177.dat",
) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]:
"""
Fetch market split data directly from the QOBLIB repository.
Parameters
----------
instance_name : str
Name of the .dat file to fetch (default: "ms_03_200_177.dat").
Returns
-------
A : np.ndarray or None
Coefficient matrix if successful, None if failed.
b : np.ndarray or None
Target vector if successful, None if failed.
"""
url = f"https://git.zib.de/qopt/qoblib-quantum-optimization-benchmarking-library/-/raw/main/01-marketsplit/instances/{instance_name}"
try:
response = requests.get(url, timeout=30)
response.raise_for_status()
with tempfile.NamedTemporaryFile(
mode="w", suffix=".dat", delete=False, encoding="utf-8"
) as f:
f.write(response.text)
temp_path = f.name
try:
return parse_marketsplit_dat(temp_path)
finally:
os.unlink(temp_path)
except Exception as e:
print(f"Error: {e}")
return None, None
問題インスタンスの読み込み
ここでQOBLIB [2]から特定の問題インスタンスms_03_200_177.datを読み込みます。このインスタンスは以下の特徴を持ちます:
- 3種類の製品(制約)
- 20の市場(二値決定変数)
- 100万通り以上の市場割り当てを探索()
# Load the problem instance
instance_name = "ms_03_200_177.dat"
A, b = fetch_marketsplit_data(instance_name=instance_name)
if A is not None:
print("Successfully loaded problem instance from QOBLIB")
print("\nProblem Instance Analysis:")
print("=" * 50)
print(f"Coefficient Matrix A: {A.shape[0]} × {A.shape[1]}")
print(f" → {A.shape[0]} products (constraints)")
print(f" → {A.shape[1]} markets (decision variables)")
print(f"Target Vector b: {b}")
print(" → Target sales per product for each region")
print(
f"Solution Space: 2^{A.shape[1]} = {2**A.shape[1]:,} possible assignments"
)
QUBO形式への変換
ここで制約付き最適化問題をQUBO形式に変換します:
# Create optimization problem
ms = OptimizationProblem(instance_name.replace(".dat", ""))
# Add binary variables (one for each market)
ms.binary_var_list(A.shape[1])
# Add equality constraints (one for each product)
for idx, rhs in enumerate(b):
ms.linear_constraint(A[idx, :], sense="==", rhs=rhs)
# Convert to QUBO with penalty parameter
qubo = OptimizationProblemToQubo(penalty=1).convert(ms)
print("QUBO Conversion Complete:")
print("=" * 50)
print(f"Number of variables: {qubo.get_num_vars()}")
print(f"Constant term: {qubo.objective.constant}")
print(f"Linear terms: {len(qubo.objective.linear.to_dict())}")
print(f"Quadratic terms: {len(qubo.objective.quadratic.to_dict())}")