このセクションでは、ジョブショップスケジューリング問題を定式化し、Pythonを用いてモデルを実装する方法を学びます。
まずはJijZept SDKを利用するための推奨Python環境セットアップ手順を以下に示します。
仮想環境を使うことで、依存関係の衝突を防ぎ、クリーンな開発環境を維持できます。
JijZept IDE環境ではPythonやパッケージのセットアップは不要です。すぐにチュートリアルを始められます。
詳細はJijZept IDEドキュメントをご覧ください。
python -m venv .venv
source .venv/bin/activate
pip install "jijzept_sdk[all]"
このコマンドでJijModeling、OMMX、MINTOなど最適化チュートリアルに必要な全てのパッケージがまとめてインストールされます。
pip install pandas matplotlib
JijModelingは、数理最適化問題をPythonを用いて直感的かつ宣言的に記述するためのライブラリです。数理最適化の専門家だけでなく、ソフトウェアエンジニアやデータサイエンティストにとっても、数学的な定式化を自然なPythonコードとして表現できるように設計されています。
JijModelingの主な役割は、人間が理解しやすい形で記述された最適化モデル(決定変数、目的関数、制約条件)を、コンピュータ(最適化ソルバー)が解釈できる形式に変換することです。これにより、複雑な数式を手計算で扱う手間を省き、モデルの構築、検証、再利用を容易にします。
このセクションでは、JijModelingの基本的な使い方を学び、実際に最適化問題をコードで表現する方法を習得します。既存のJijModelingチュートリアル (https://jij-inc.github.io/JijModeling-Tutorials/ja/introduction.html) も詳細なリファレンスとして役立ちますが、このチュートリアルでは、学習パスに沿った段階的な解説と例を提供します。
JijModelingを使って最適化モデルを構築する基本的な手順を見ていきましょう。以下のように、パラメータ、決定変数、目的関数、制約条件を定義する方法を説明します。 詳細な使い方は、JijModelingチュートリアルを参照してください。
import jijmodeling as jm
# --- パラメータの定義(例: 重みベクトル) ---
w = jm.Placeholder("w", ndim=1) # 1次元の変数
v = jm.Placeholder("w", ndim=1) # 1次元の変数
N = w.len_at(0, latex="N") # wの長さ (len(w[0]))をNと表示
# --- 決定変数の定義(1次元バイナリ, 整数、連続変数) ---
x = jm.BinaryVar("x", shape=(N,)) # バイナリ変数 (shape=(N,))
y = jm.IntegerVar("y", shape=(N,), lower_bound=0, upper_bound=1000) # 整数変数 (0から1000まで) (shape=(N,))
z = jm.ContinuousVar("z", shape=(N,2), lower_bound=0, upper_bound=10) # 連続変数 (0から10まで) (shape=(N,2))
# --- インデックスの定義 ---
i = jm.Element("i", belong_to=(0, N)) # 0 <= i < N までの範囲をとるインデックス
# --- 問題の定義(最大化) ---
problem = jm.Problem("sample_problem", sense=jm.ProblemSense.MAXIMIZE)
# --- 目的関数の定義 ---
problem += jm.sum(i, v[i] * x[i])
# --- 制約条件の定義 ---
# 例1: Σ_i w[i] * x[i] <= 5
problem += jm.Constraint("select_limit", jm.sum(i, w[i] * x[i]) <= 5)
# 例2: すべてのiについて、x[i] <= 2
problem += jm.Constraint("sum_constraint", x[i] <= 2, forall=[i])
まずは、並列ジョブショップスケジューリング問題を定式化してみましょう。
以下が対応する目的関数、制約条件、決定変数です。
目的関数 | 制約条件 | 決定変数 |
---|---|---|
メイクスパン(全てのジョブが完了するまでの時間)の最小化 |
|
|
この表と合致するように定式化すると、以下のようになります。
上記の数理モデルをJijModelingを用いてPythonで実装してみましょう。以下がそのコード例です。
import jijmodeling as jm
# --- 1. プレースホルダー(入力データ) ---
# JT[i]: ジョブiの処理時間
JT = jm.Placeholder("JT", ndim=1, description="ジョブiの処理時間")
# N: ジョブ数(JTの長さから自動取得)
N = JT.len_at(0, latex="N", description="ジョブ数")
# M: 機械数
M = jm.Placeholder("M", description="機械数")
# --- 2. インデックス ---
i = jm.Element("i", belong_to=(0, N), description="ジョブのインデックス")
m = jm.Element("m", belong_to=(0, M), description="機械のインデックス")
# --- 3. 決定変数 ---
# x[i, m]: ジョブiを機械mに割り当てるか(バイナリ)
x = jm.BinaryVar(
"x",
shape=(N, M),
description="x[i, m]: ジョブiが機械mに割り当てられると1"
)
# makespan: 全機械の完了時間の最大値
makespan = jm.ContinuousVar(
"makespan",
lower_bound=0,
upper_bound=jm.sum(i, JT[i]),
description="メイクスパン(全体の完了時間)"
)
# --- 4. 問題定義 ---
problem = jm.Problem("Parallel Machine Scheduling")
# --- 5. 制約条件 ---
# 各ジョブは必ず1台の機械に割り当てる
problem += jm.Constraint(
"Job_Assignment",
jm.sum(m, x[i, m]) == 1,
forall=i,
)
# 各機械の合計処理時間はmakespan以下
problem += jm.Constraint(
"Max_Time_per_Machine",
jm.sum(i, JT[i] * x[i, m]) <= makespan,
forall=m,
)
# --- 6. 目的関数 ---
# メイクスパンを最小化
problem += makespan
print(problem)
このように、数式と似た感覚でJijModelingを用いて数理モデルを定式化することができます。
もしNotebook、あるいはJijZept IDEを使用している場合は、変数名を表示させると数式をレンダリングすることが可能です。