JijModeling 2.10.0 リリースノート#

機能強化#

ランダムインスタンス生成でカテゴリーラベルの要素を直接リストで指定できるように#

これまではランダムインスタンス生成でカテゴリーラベルの値を固定する場合、 {"C": {"keys": ["A", "B", "C"]}} のように keys として指定する必要がありましたが、リストで直接指定できるようになりました。

import jijmodeling as jm

@jm.Problem.define("problem")
def problem(problem: jm.DecoratedProblem):
  C = problem.CategoryLabel()

  a = problem.Float(dict_keys=C)
  x = problem.BinaryVar(dict_keys=C)

  problem += (a * x).sum()
 
problem.generate_random_dataset(
    seed=42,
    options={"C": ["A", "B", "C"]} # {"C": {"keys": [...]}} と同値
)
{'a': {'A': -4.161370289401178,
  'B': -1.210197493373315,
  'C': 1.800434110281392},
 'C': ['A', 'B', 'C']}

リストが利用できるのはカテゴリーラベルのみであり、他のプレースホルダーなどについてはこれまで通りの形式で指定する必要があります。

\(\LaTeX\) 表示のカスタマイズ#

latex パラメータに加えて、 element_latex と subscript_styles という、\(\LaTeX\) の表示をカスタマイズできる引数を Placeholder、DecisionVar、NamedExpr、CategoryLabel のコンストラクタに追加しました。

element_latex は、そのオブジェクトを範囲とする添字に共通の名前を設定します。 自動的に割り当てられる名前(i、j など)のかわりにこの名前が使われます。 なお、lambda 式や for 句でユーザーが書いた名前が、この名前で上書きされることになるのでご注意ください。

problem = jm.Problem("problem")
N = problem.Natural("N", element_latex="n")
c = problem.Float("c", shape=N)
x = problem.BinaryVar("x", shape=N)
problem += jm.sum(c * x)
problem += c.map(lambda v: v * 2).sum()
problem
\[\begin{array}{rl} \text{Problem}\colon &\text{problem}\\\displaystyle \min &\displaystyle \sum _{n=0}^{N-1}{{c}_{n}\cdot {x}_{n}}+\sum _{n=0}^{N-1}{{c}_{n}\cdot 2}\\&\\\text{where}&\\&\text{Decision Variables:}\\&\qquad \begin{alignedat}{2}{x}_{n}&\in \left\{0,1\right\}&\qquad &\text{a }1\text{-dim array of }\text{binary}\text{ decision variables}\\&\forall n\in \left\{0,\ldots ,N-1\right\}&&\\\end{alignedat}\\&\\&\text{Placeholders:}\\&\qquad \begin{alignedat}{2}{c}_{n}&\in \mathbb{R}&\qquad &\text{a }1\text{-dim array of placeholders with elements in }\mathbb{R}\\&\forall n\in \left\{0,\ldots ,N-1\right\}&&\\&&&\\N&\in \mathbb{N}&\qquad &\text{a scalar placeholder in }\mathbb{N}\\\end{alignedat}\end{array} \]

タプル集合などの場合は、element_latexに名前のリストも指定できます。 名前が割り当てられなかった要素については、通常の命名挙動になります。

subscript_styles は、添字の実際の表示方法を変更します。たとえば、上付き添字、括弧、角括弧による表示に切り替えることができます。 多次元配列型は、スタイルを一つだけ指定した場合、全添字に適応されますが、スタイルのリストを渡すことで、添字の位置ごとに異なるスタイルを設定できます。

problem = jm.Problem("problem")
N = problem.Natural("N")
x = problem.BinaryVar("x", shape=(N, N), subscript_styles=jm.SubscriptStyle.BRACKET)
y = problem.BinaryVar(
    "y", shape=(N, N), subscript_styles=[jm.SubscriptStyle.SUP, jm.SubscriptStyle.SUB]
)
problem += x[0, 1] + y[0, 1]
problem
\[\begin{array}{rl} \text{Problem}\colon &\text{problem}\\\displaystyle \min &\displaystyle x\left[0,1\right]+{{y}_{1}}^{0}\\&\\\text{where}&\\&\text{Decision Variables:}\\&\qquad \begin{alignedat}{2}x\left[i,j\right]&\in \left\{0,1\right\}&\qquad &\text{a }2\text{-dim array of }\text{binary}\text{ decision variables}\\&\forall i\in \left\{0,\ldots ,N-1\right\},\;\forall j\in \left\{0,\ldots ,N-1\right\}&&\\&&&\\{{y}_{j}}^{i}&\in \left\{0,1\right\}&\qquad &\text{a }2\text{-dim array of }\text{binary}\text{ decision variables}\\&\forall i\in \left\{0,\ldots ,N-1\right\},\;\forall j\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} \]

バグ修正#

バグ修正 1:ランダムインスタンス生成にカテゴリーラベルの情報が含まれるように#

ランダムインスタンス生成で得られたデータセットにカテゴリーラベルそのものの定義が含まれていなかった問題を修正しました。 これまでは以下の dataset には C が含まれておらず eval に失敗していましたが、本リリースからは問題なく実行されるようになりました。

import jijmodeling as jm

@jm.Problem.define("problem")
def problem(problem: jm.DecoratedProblem):
  C = problem.CategoryLabel()

  a = problem.Float(dict_keys=C)
  x = problem.BinaryVar(dict_keys=C)

  problem += (a * x).sum()
 
dataset = problem.generate_random_dataset(
    seed=42,
    options={"C": {"keys": ["A", "B", "C"]}}
)

dataset
{'C': ['A', 'B', 'C'],
 'a': {'A': -4.161370289401178,
  'B': -1.210197493373315,
  'C': 1.800434110281392}}
problem.eval(dataset)
Instance(raw=<builtins.Instance object at 0x7c6bad0c00>, annotations={})

空リスト・空配列をプレースホルダーとして入力できるように#

これまで、1 次元配列に対して空の配列やリストをインスタンスデータとして入力することはできましたが、2 次元以上の配列に対してはコンパイル時にエラーになっていました。

今回のリリースから、配列のシェイプが空データを許容する場合、いずれかの不定な軸の次元が 0 であるような多次元配列として解釈されるようになりました。

@jm.Problem.define("p")
def problem(problem: jm.DecoratedProblem):
    cuts = problem.Float(ndim=2)

problem.eval({"cuts": []}) # これまではエラーだったが、$0 \times 0$ 配列として解釈される
Instance(raw=<builtins.Instance object at 0x7c6bad2700>, annotations={})

fixed_variables を指定すると制約検出でエラーになる問題の修正#

One-hot 制約や SOS1 制約に含まれる決定変数の値を fixed_variables で固定すると、制約検出が有効な場合に内部エラー E-CE0007 が発生することがあった問題を修正しました。 以下のように、一部の決定変数の値を固定した問題も、制約検出を有効にしたままコンパイルできるようになりました。

import jijmodeling as jm

problem = jm.Problem("one-hot with fixed variables")
x = problem.BinaryVar("x", shape=(3,))
problem += problem.Constraint("onehot", x[0] + x[1] + x[2] == 1)

problem.eval({}, fixed_variables={"x": {0: 0}})
Instance(raw=<builtins.Instance object at 0x7c6bad1b00>, annotations={})

また、One-hot 制約のすべての変数を 0 に固定するなど、固定値によってこれらの制約を満たせなくなる場合には、制約名と理由を示すエラー E-CE0022 が発生するようになりました。