JijModeling 2.7.0 リリースノート#

機能強化#

コンパイラの実行速度とメモリ効率を改善#

コンパイラの大規模な最適化により、実行速度とメモリ効率が大幅に向上しました 🎉

ベンチマークでは、JijModeling 2.6.0 に対して最大 8 倍、1.14.2 に対して最大 5 倍の高速化が観測されました。以下は代表的な処理の実行時間を、2.7.0 を 1.0 として比較した結果です。値が大きいほど、今回のリリース版よりも時間がかかったことを示します。

JijModeling 1.14.2、2.6.0、2.7.0 の相対実行時間を、ナップサック、supportcase18、FMA の代表的な処理で比較した縦棒グラフ

図 7 代表的なベンチマークでコンパイルにかかった相対実行時間。棒上の数値は 2.7.0 に対する比率(1.0 以上=2.7.0 の方が高速か同等)#

1 回のコンパイルあたりのメモリ確保量についても、大幅な削減が見られています。 具体的には、1 回のコンパイルあたりの累積確保量は 2.6.0 より 76–97%、1.14.2 より 51–94% 削減されました。

JijModeling 1.14.2、2.6.0、2.7.0 の 1 回のコンパイルあたりの累積メモリ確保量を、時間ベンチマークと同じ順序のナップサック、supportcase18、FMA で比較した縦棒グラフ

図 8 代表的なベンチマークにおける 1 回のコンパイルあたりの累積メモリ確保量#

いずれも計測には Google Cloud の n2-standard-8(8 vCPU、32 GB、Ubuntu 26.04 LTS、x86_64)を使用しました。

特にコンパイル時間が課題となるモデルで大きな改善が得られていますので、これを機に JijModeling 2 への移行をぜひ検討してください。

Decorator API の update 関数で引数の自動取得が可能に#

@Problem.update が追加引数で定義済の決定変数やプレスホルダーを取れるようになりました。 旧来は他のブロックで定義されたパラメーターは problem.decision_vars などで手動アクセスする必要がありましたが、今回のリリースから対象のパラメーターを直接関数の引数として自動的に取得できるようになりました。

import jijmodeling as jm


@jm.Problem.define("MyProblem")
def problem(problem):
    w = problem.Float(ndim=1, description="Weights of the items")
    N = w.len_at(0)
    W = problem.Float(description="Total weight")
    x = problem.BinaryVar(shape=(N,), description="Selected items")


@problem.update
def _myupdate(
    problem: jm.DecoratedProblem,
    w: jm.Placeholder,
    W: jm.Placeholder,
    x: jm.DecisionVar,
):
    problem += problem.Constraint("weight", jm.sum(w * x) <= W)

    # いつも通り、新しい決定変数やプレスホルダーの定義も可
    v = problem.Float(ndim=1, description="Values of the items")
    problem += jm.sum(v * x)


problem
\[\begin{array}{rl} \text{Problem}\colon &\text{MyProblem}\\\displaystyle \min &\displaystyle \sum _{\vec{\imath }}{{{\left(v\cdot x\right)}}_{\vec{\imath }}}\\&\\\text{s.t.}&\\&\begin{aligned} \text{weight}&\quad \displaystyle \sum _{\vec{\imath }}{{{\left(w\cdot x\right)}}_{\vec{\imath }}}\leq W\end{aligned} \\&\\\text{where}&\\&\text{Decision Variables:}\\&\qquad \begin{alignedat}{2}{x}_{i}&\in \left\{0,1\right\}&\qquad &\text{a }1\text{-dim array of }\text{binary}\text{ decision variables}\\&\forall i\in \left\{0,\ldots ,\mathop{\mathtt{len\_{}at}}\left(w,0\right)-1\right\}&&\text{Selected items}\\\end{alignedat}\\&\\&\text{Placeholders:}\\&\qquad \begin{alignedat}{2}{v}_{i}&\in \mathbb{R}&\qquad &\text{a }1\text{-dim array of placeholders with elements in }\mathbb{R}\\&\forall i\in \left\{0,\ldots ,\mathop{\mathtt{len\_{}at}}\left(v,0\right)-1\right\}&&\text{Values of the items}\\&&&\\W&\in \mathbb{R}&\qquad &\text{a scalar placeholder in }\mathbb{R}\\&&&\text{Total weight}\\&&&\\{w}_{i}&\in \mathbb{R}&\qquad &\text{a }1\text{-dim array of placeholders with elements in }\mathbb{R}\\&\forall i\in \left\{0,\ldots ,\mathop{\mathtt{len\_{}at}}\left(w,0\right)-1\right\}&&\text{Weights of the items}\\\end{alignedat}\end{array} \]

Type Mismatch エラーの改善#

Type Mismatch エラーが必要に応じて実際に型が合わなかった項を含むようになりました。

import jijmodeling as jm


try:

    @jm.Problem.define("MyProblem")
    def problem(problem: jm.DecoratedProblem):
        N = problem.Length()
        W = problem.Float()
        x = problem.BinaryVar(shape=N)

        problem += x[W]  # Error!
except Exception as e:
    print(e)
Traceback (most recent last):
    while checking if expression `x[W]` has type `float!`,
        defined at File "/var/folders/mg/mg6st30d18s7pxjjrk6pkxym0000gn/T/ipykernel_47502/2016213372.py", line 12, col 20-24
    while inferring the type of expression `x[W]`,
        defined at File "/var/folders/mg/mg6st30d18s7pxjjrk6pkxym0000gn/T/ipykernel_47502/2016213372.py", line 12, col 20-24
    while inferring the type of expression `x[W]`,
        defined at File "/var/folders/mg/mg6st30d18s7pxjjrk6pkxym0000gn/T/ipykernel_47502/2016213372.py", line 12, col 20-24
    while checking if type `Array[N; binary!]` can be subscripted with (W): (float),
        defined at File "/var/folders/mg/mg6st30d18s7pxjjrk6pkxym0000gn/T/ipykernel_47502/2016213372.py", line 12, col 20-24
    while checking if expression `W` has type `natural`,
        defined at File "/var/folders/mg/mg6st30d18s7pxjjrk6pkxym0000gn/T/ipykernel_47502/2016213372.py", line 12, col 22-23

File "/var/folders/mg/mg6st30d18s7pxjjrk6pkxym0000gn/T/ipykernel_47502/2016213372.py", line 12, col 22-23:

    12  |          problem += x[W]  # Error!
                                ^

error[E-TE0004] Could not match actual type `float` with expected `natural` on an expression `W`

Hint: You can read the description and possible fix at https://jij-inc-jijmodeling.readthedocs-hosted.com/en/stable/error_codes/error/E-TE0004.html

合わせて、エラーコードインデックスの当該エラーの内容がより詳細になりました。

添え字つき変数の数式出力の改善#

添え字つきの変数の表示がより読みやすくなりました。

import jijmodeling as jm


@jm.Problem.define("Vars Beautiful")
def problem(problem: jm.DecoratedProblem):
    C = problem.CategoryLabel()
    N = problem.Natural()
    M = problem.Natural()
    w = problem.Float(shape=(N, M))

    x = problem.ContinuousVar(
        shape=(N, M),
        lower_bound=w,
        upper_bound=2,
        description="添え字がわかりやすくなった",
    )
    z = problem.IntegerVar(
        dict_keys=(C, N),
        lower_bound=lambda c, i: i,
        upper_bound=42,
    )
    u = problem.BinaryVar()


problem
\[\begin{array}{rl} \text{Problem}\colon &\text{Vars Beautiful}\\\displaystyle \min &\displaystyle 0\\&\\\text{where}&\\&\text{Decision Variables:}\\&\qquad \begin{alignedat}{2}u&\in \left\{0,1\right\}&\qquad &\text{a binary decision variable}\\&&&\\{x}_{i,j}&\in \mathbb{R}\;\left({w}_{i,j}\leq {x}_{i,j}\leq 2\right)&\qquad &\text{a }2\text{-dim array of }\text{continuous}\text{ decision variables}\\&\forall i\in \left\{0,\ldots ,N-1\right\},\;\forall j\in \left\{0,\ldots ,M-1\right\}&&\text{添え字がわかりやすくなった}\\&&&\\{z}_{c,i}&\in \mathbb{Z}\;\left(i\leq {z}_{c,i}\leq 42\right)&\qquad &\text{a dictionary of }\text{integer}\text{ decision variables}\\&\forall c\in \mathrm{C},\;\forall i\in \left\{0,\ldots ,N-1\right\}&&\\\end{alignedat}\\&\\&\text{Placeholders:}\\&\qquad \begin{alignedat}{2}M&\in \mathbb{N}&\qquad &\text{a scalar placeholder in }\mathbb{N}\\&&&\\N&\in \mathbb{N}&\qquad &\text{a scalar placeholder in }\mathbb{N}\\&&&\\{w}_{i,j}&\in \mathbb{R}&\qquad &\text{a }2\text{-dim array of placeholders with elements in }\mathbb{R}\\&\forall i\in \left\{0,\ldots ,N-1\right\},\;\forall j\in \left\{0,\ldots ,M-1\right\}&&\\\end{alignedat}\\&\\&\text{Category Labels:}\\&\qquad \begin{array}{rl} C&\text{Category Label}\end{array} \end{array} \]

バグ修正#

引数に式を含む jm.range が内部エラーになる問題を修正#

これまで、jm.range の引数に N - 1 のような計算式を渡すと、モデルの評価時に内部エラー(E-CE0007)が発生し、JijModeling 側のバグとして報告されていました。この問題は制約の domain= に限らず、総和の添字集合など jm.range を評価する全ての箇所で発生していました(リテラルや単独のプレースホルダーを引数とする jm.range(N) などは影響を受けません)。

今回の修正により、引数に式を含む range も正しく評価されるようになりました。

import jijmodeling as jm


@jm.Problem.define("RangeWithComputedBounds")
def problem(problem: jm.DecoratedProblem):
    N = problem.Natural()
    x = problem.BinaryVar(shape=(N,))
    problem += jm.sum(x[i] for i in jm.range(N - 1))
    problem += problem.Constraint("fix", lambda i: x[i] == 0, domain=jm.range(N - 1))


display(problem)

problem.eval({"N": 4})
\[\begin{array}{rl} \text{Problem}\colon &\text{RangeWithComputedBounds}\\\displaystyle \min &\displaystyle \sum _{i\in \mathop{\mathtt{range}}\left(N-1\right)}{{x}_{i}}\\&\\\text{s.t.}&\\&\begin{aligned} \text{fix}&\quad \displaystyle {x}_{i}=0\quad \forall i\;\text{s.t.}\;i\in \mathop{\mathtt{range}}\left(N-1\right)\end{aligned} \\&\\\text{where}&\\&\text{Decision Variables:}\\&\qquad \begin{alignedat}{2}{x}_{i}&\in \left\{0,1\right\}&\qquad &\text{a }1\text{-dim array of }\text{binary}\text{ decision variables}\\&\forall i\in \left\{0,\ldots ,N-1\right\}&&\\\end{alignedat}\\&\\&\text{Placeholders:}\\&\qquad \begin{alignedat}{2}N&\in \mathbb{N}&\qquad &\text{a scalar placeholder in }\mathbb{N}\\\end{alignedat}\end{array} \]
Instance(raw=<builtins.Instance object at 0xc878f8000>, annotations={})