JijModelingの使い方
JijModelingとは?
Section titled “JijModelingとは?”量子アニーリングの実用化が進む中で、定式化したモデルをソルバー(量子アニーリングマシンやシミュレーター)が理解できる言葉に翻訳するのは非常に骨の折れる作業でした。
その課題を解決するために「あたかも数式を書くようにプログラミングをする」というのを実現したのがJij社が開発するオープンソースライブラリJijModelingです。
JijModelingのできること
Section titled “JijModelingのできること”JijModelingの最大の強みは、定式化の数式の記述とソルバーの分離です。
- 直感的な記述:自分でコーディングした定式化をシグマ記号や添字など数学記号を可視化することができ、望んだ記述ができているかの確認ができます。
- ハードウェアとの相互関係:一度定式化をプログラムで書けば、コードを書き換えることなく、ソルバーに実行させることが可能です。
- 自動変換: 複雑な制約条件を、イジングマシンが解けるQUBO形式へ自動的に変換(トランスパイル)してくれます。
対応ソルバー
Section titled “対応ソルバー”JijModelingで記述された定式化は以下のソルバーで活用できます。
ここで、シミュレーテッドアニーリングをSA、シミュレーテッド・量子アニーリングをSQAと記述します。
| ソルバー名 | 特徴 | タイプ |
|---|---|---|
| D-Wave | D-Wave Systems社の量子アニーリングマシン | 量子アニーリング |
| Openjij | Jij社が提供するソルバー。SA(SASampler), SQA(SQASampler) | 古典的シミュレーション |
| Toshiba SQBM+ | 東芝の開発したシミュレーテッド分岐アルゴリズム搭載マシン | イジングマシン |
| Fixstars Amplify | GPU/CPUを活用した高速アニーリングクラウド。 | イジングマシン |
コード例:二次ナップサック問題
Section titled “コード例:二次ナップサック問題”ここでは、通常のナップサック問題(重さ制限内で価値を最大化)に、「特定のアイテム同士を一緒に選ぶとボーナス価値が生まれる」という相乗効果を加えた二次ナップサック問題を解いてみます。
- 目的関数: アイテムの価値の合計+相乗効果ボーナスの最大化
- 制約: 重さの合計 容量
以下は、Jupyter Notebook(.ipynb形式)で実行することを想定したコードです。
※ JijModelingを使う場合、途中経過を見ることでコードが正しい記述ができているかを確かめられる利点があるからです。
Step1: ライブラリのインストールとインポート
Section titled “Step1: ライブラリのインストールとインポート”# インストール!pip install jijmodeling jijmodeling-transpiler openjij今回はソルバーとしてOpenjijを利用してみます。
# インポートimport numpy as npimport openjij as ojimport jijmodeling as jmimport jijmodeling_transpiler as jmtStep2: 定式化のコード化
Section titled “Step2: 定式化のコード化”# --- 1. 定数の定義(プレースホルダー) ---# アイテム数 N, 容量 CN = jm.Placeholder('N')C = jm.Placeholder('C')# アイテム単体の価値 v, 重さ wv = jm.Placeholder('v', ndim=1)w = jm.Placeholder('w', ndim=1)# 相乗効果(二次)の価値 q (N x N行列)q = jm.Placeholder('q', ndim=2)
# 決定変数 x (i番目のアイテムを選ぶなら1, 選ばないなら0)x = jm.BinaryVar('x', shape=(N,))i = jm.Element('i', belong_to=(0, N))j = jm.Element('j', belong_to=(0, N))
# --- 2. 問題の構築 ---problem = jm.Problem('Quadratic_Knapsack')
# 目的関数: 単体価値の合計 + 相乗効果の合計 を最大化# (Maximize sum(v_i * x_i) + sum(q_ij * x_i * x_j))problem += jm.sum(i, v[i] * x[i]) + jm.sum([i, j], q[i, j] * x[i] * x[j])
# 制約条件: 重さの合計 <= 容量 C# (Subject to sum(w_i * x_i) <= C)problem += jm.Constraint('capacity', jm.sum(i, w[i] * x[i]) <= C)
# 作成したproblemを見てみましょう。problem
解説
- まず、定数に関しては、jm.Placeholder(‘N’)のようにプレースホルダーというのを用いて、NやCという定式化で自分たちが定めた文字をここに記載します。(日本語はNG)
- 次にndim はベクトルや行列のサイズを表すものでndim=1ならベクトル、ndim=2なら行列というようになります。
- 変数は、jm.BinaryVar(‘x’, shape=(N,))のように、BinaryVar(二値の変数)と定義して、shape(次数)を決めてあげます。今回はNが一つなので1次になっています。
- また変数の添字は、i = jm.Element(‘i’, belong_to=(0, N))のように、belong_to=(0, N)の範囲でiという添字を使いますという意思表示です。
- あとは、それをproblem内で定式化した通りに記述することでproblemを作成できます。
- 最後にprint(problem)とすると以下のように数式が表示されます。
定式化したproblemに具体的な数値(インスタンス)を流し込み、計算します。
# --- データの準備 ---num_items = 5capacity_val = 10
instance_data = { 'N': num_items, 'C': capacity_val, 'v': [2, 4, 3, 5, 2], # 単体の価値 'w': [3, 4, 2, 5, 1], # 重さ 'q': [ # 相乗効果 (対角成分0) [0, 1, 0, 0, 0], [0, 0, 2, 0, 0], # アイテム1と2で+2 [0, 0, 0, 1, 0], [0, 0, 0, 0, 2], [0, 0, 0, 0, 0] ]}
# --- コンパイルとQUBO変換 ---# 1. モデルとデータを結合compiled_model = jmt.compile_model(problem, instance_data)
# 2. PUBO (Polynomial Unconstrained Binary Optimization) ビルダーを作成pubo_builder = jmt.pubo.transpile_to_pubo(compiled_model)
# 3. QUBO辞書を取得# multipliers: 制約 'capacity' を破ったときのペナルティの重み# この値が小さすぎると制約違反が起き、大きすぎると最適解が見つかりにくくなりますqubo, offset = pubo_builder.get_qubo_dict( multipliers={"capacity": 5.0})
print("\n--- QUBO変換完了 ---")print(qubo)ソルバーでの実行
Section titled “ソルバーでの実行”# サンプラーの準備sampler = oj.SASampler()
# 実行 (100回サンプリング)result = sampler.sample_qubo(qubo, num_reads=100)
# 最も良い解(サンプル)を取得first_sampleset = result.first.samplefirst_energy = result.first.energy
print("--- 最適解の情報 ---")print(first_sampleset)print(first_energy)実行結果
--- 最適解の情報 ---{0: 1, 1: 1, 2: 0, 3: 0, 4: 0}-9.799999999999997JijModelingを使うことで、複雑なイジングモデルへの変換式を意識することなく、「解きたい問題の構造」に集中してプログラミングができます。
今回はOpenJij(シミュレータ)で動かしましたが、sampler の部分を DWaveSampler などに差し替えるだけで、同じコードでD-Wave上でも動作します。これがJijModelingの最大の魅力になっています。
[1] : JijModeling, https://jij-inc.github.io/JijModeling-Tutorials/en/introduction.html, 閲覧日 2025/12/03