3.8.8. 境界条件やフロー保存則の記述テクニック

PySIMPLE で扱う添字付きオブジェクトは範囲外の添字にアクセスすると通常 KeyError 例外を投げます.:

>>> i = Element(value=[1, 2, 3], name='i')
>>> z = BinaryVariable(index=i, name='z')
>>> z[i+1]
KeyError: 'z[(i+1)[i]][4]'

上記の場合,i=3 のときに z[4] にアクセスしているため,KeyError となります. V26 で新たに導入された get メソッドでは Python の辞書における get メソッドのように範囲外にアクセスしても 例外とならず,PySIMPLE においては,0 となります.

モデルを記述する上で頻出である境界値で分岐する制約式を get メソッドなし・ありで記述してみましょう. get メソッドを用いることで制約式を二つに分割することなく,一括して記述することができます.:

>>> # without get
>>> i1 = i!=1  # i1 in (2, 3)
>>> z[1] >= 1
(z[1]>=1):
z[1]>=1
>>> z[i1] - z[i1-1] >= 1
((z[(i!=1)]-z[((i!=1)-1)[(i!=1)]][(i!=1)])[(i!=1)]>=1):
-z[1]+z[2]>=1
-z[2]+z[3]>=1
>>> # with get
>>> z[i] - z.get(i-1) >= 1
((z[i]-z.get((i-1)[i])[i])[i]>=1):
z[1]>=1
-z[1]+z[2]>=1
-z[2]+z[3]>=1

今度はフローネットワークの保存則を get メソッドなし・ありで記述してみましょう. 入力のみのノード 1,出力のみのノード 6 の扱いに注意すると,get メソッドなしでは制約式を三つに分割する 必要があるのに対し,get メソッドありでは一括して記述することができます.:

>>> IJ = Set(value=[(1,2),(1,4),(2,3),(3,6),(4,5),(5,2),(5,6)], name='IJ')
>>> ij = Element(set=IJ, name='ij')
>>> x = Variable(index=ij, name='x')
>>> # without get
>>> m = Element(set=IJ(0)&IJ(1), name='m')  # m in (2, 3, 4, 5)
>>> Sum(x[ij], ij(0))[m] == Sum(x[ij], ij(1))[m]  # 入出力あり
(Sum(x[ij], ij(0))[m]==Sum(x[ij], ij(1))[m]):
x[1,2]-x[2,3]+x[5,2]==0
x[1,4]-x[4,5]==0
x[2,3]-x[3,6]==0
x[4,5]-x[5,2]-x[5,6]==0
>>> Sum(x[ij], ij(1))[ij(0)>IJ(1)] == 0  # ij(0)>IJ(1) in (1,)
(Sum(x[ij], ij(1))[(ij(0)>IJ(1))]==0):
x[1,2]+x[1,4]==0
>>> Sum(x[ij], ij(0))[ij(1)>IJ(0)] == 0  # ij(1)>IJ(0) in (6,)
(Sum(x[ij], ij(0))[(ij(1)>IJ(0))]==0):
x[3,6]+x[5,6]==0
>>> # with get
>>> k = Element(set=IJ(0)|IJ(1), name='k')  # k in (1, ..., 6)
>>> Sum(x[ij], ij(0)).get(k) == Sum(x[ij], ij(1)).get(k)
(Sum(x[ij], ij(0)).get(k)[k]==Sum(x[ij], ij(1)).get(k)[k]):
-x[1,2]-x[1,4]==0
x[1,2]-x[2,3]+x[5,2]==0
x[2,3]-x[3,6]==0
x[1,4]-x[4,5]==0
x[4,5]-x[5,2]-x[5,6]==0
x[3,6]+x[5,6]==0

get メソッドを用いて一括して記述することで,見た目が簡潔になるだけでなく,その後に式の値を取得したり 制約式を削除したりするなど,制御も簡単になります. get メソッドは C++SIMPLE には存在しない PySIMPLE オリジナルの文法です. 非常に強力な記述力を持ちますが,一方で全く異なる式を一括して記述することには向いていません.