例題研究(1)

以下では、スケジューリングソルバーOptSeqのマニュアルに出てくる例題の分析を行います。

例題(ex1,ex2,ex18,ex8)


●例題ex1:PERT

例題ex1は、作業情報が次表のように与えられるとき、最終作業(出発)の完了時刻(メイクスパン)を求めようとしています。

# 作業名 作業期間(分) 後続作業
1 乗客降し 13 3
2 荷物降し 25 5 4
3 機内清掃  15 4
4 乗客搭乗 27 6
5 荷物積込 22 6
6 出発 1 0

#optseq_ex1.py
from optseq import *
#-----データセット
ex1=Model()
data={\
1:[13,[3]],\
2:[25,[5,4]],\
3:[15,[4]],\
4:[27,[6]],\
5:[22,[6]],\
6:[ 1,[0]],\
}
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex1.addActivity("Act[{0}]".format(i))
  mode[i]=Mode("Mode[{0}]".format(i),data[i][0])
  act[i].addModes(mode[i])
#-----先行制約
for i in data:
  if data[i][1][0]!=0:
    for j in data[i][1]:
      ex1.addTemporal(act[i],act[j])
#-----最適化
ex1.Params.TimeLimit=1
ex1.Params.Makespan=True
#ex1.Params.OutputFlag=True
ex1.optimize()
#ex1.writeExcel("ex1_result.csv")
ex1.write("ex1_result.txt")
#-----ex1_result.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
#----------------------------------------------------------------------------------------------------------------------------------------------
#  Act[1]   Mode[1]      13    ==========================                                                                                      
#  Act[2]   Mode[2]      25    ==================================================                                                              
#  Act[3]   Mode[3]      15                              ==============================                                                        
#  Act[4]   Mode[4]      27                            #                                ======================================================  
#  Act[5]   Mode[5]      22                                                      ============================================                  
#  Act[6]   Mode[6]      1                                                                                                                   ==
#----------------------------------------------------------------------------------------------------------------------------------------------
#   resource usage/capacity     
#----------------------------------------------------------------------------------------------------------------------------------------------


このプログラムを実行して、メイクスパンは56分であることが分かります。ex1_result.csvにおいて、でクリティカルパス(CP)を示すと次のようになりま。


●例題ex2:資源制約付きPERT

例題ex2は例題ex1において、各作業の監督者をアサインする場合を検討しています。

リソース 許容量
1 監督者 1

#optseq_ex2.py
from optseq import *
#-----データセット
ex2=Model()
data={\
1:[13,[3]],\
2:[25,[5,4]],\
3:[15,[4]],\
4:[27,[6]],\
5:[22,[6]],\
6:[ 1,[0]],\
}
res=ex2.addResource("worker",capacity={(0,"inf"):1})
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex2.addActivity("Act[{0}]".format(i))
  mode[i]=Mode("Mode[{0}]".format(i),data[i][0])
  mode[i].addResource(res,requirement=1)
  act[i].addModes(mode[i])
#-----先行制約
for i in data:
  if data[i][1][0]!=0:
    for j in data[i][1]:
      ex2.addTemporal(act[i],act[j])
#-----最適化
ex2.Params.TimeLimit=1
ex2.Params.Makespan=True
#ex2.Params.OutputFlag=True
ex2.optimize()
#ex2.writeExcel("ex2_result.csv")
#ex2.write("ex2_result.txt")
#-----ex2_result.txt
#  activity    mode   duration   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100101102103
#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#  Act[1]   Mode[1]      13    =======================================                                                                                                                                                                                                                                                                              
#  Act[2]   Mode[2]      25                                                                                        ===========================================================================                                                                                                                                                      
#  Act[3]   Mode[3]      15                                           =============================================                                                                                                                                                                                                                                 
#  Act[4]   Mode[4]      27                                                                                                                                                                                                                                     =================================================================================   
#  Act[5]   Mode[5]      22                                                                                                                                                                   ==================================================================                                                                                    
#  Act[6]   Mode[6]      1                                                                                                                                                                                                                                                                                                                       ===
#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#            worker              1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
#                                1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


このプログラムを実行して、監督者が1人しかいないと、メイクスパンは103分にまで伸びることが分かります。

●例題ex18:作業情報の抽出(原題:平準化)

例題ex18は、例題ex2において先行制約を外し、作業員を何人まで増やせば納期52日を満たすかを調べています。

#optseq_ex18.py
from optseq import *
#-----データセット
data={\
  1:[13],\
  2:[25],\
  3:[15],\
  4:[27],\
  5:[22],\
  6:[ 1],\
}
duedate52=52
#=====
for ub in range(1,10):
  ex18=Model()
  res=ex18.addResource("wor##ker",capacity={(0,"inf"):ub})
#-----作業とモード
  act={}
  mode={}
  for i in data:
    act[i]=ex18.addActivity("Act[{0}]".format(i), duedate=duedate52)
    mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
    mode[i].addResource(res,requirement=1)
    act[i].addModes(mode[i])
#-----最適化
  ex18.Params.TimeLimit=1
  ex18.Params.Makespan=True
 #ex18.Params.OutputFlag=True
  ex18.optimize()
  ex18.writeExcel("ex18_result.csv")
  ex18.write("ex18_result.txt")
#-----納期は満たされたか?
  completion=0
  for a in act:
    completion=max(completion, act[a].completion)
  if completion<=duedate52: break
#eof
#-----ex18_result.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
#--------------------------------------------------------------------------------------------------------------------------------------
#  Act[1]   Mode[1]      13                                  ==========================                                                
#  Act[2]   Mode[2]      25                                                          ==================================================
#  Act[3]   Mode[3]      15    ==============================                                                                          
#  Act[4]   Mode[4]      27    ======================================================                                                  
#  Act[5]   Mode[5]      22                                                            ============================================    
#  Act[6]   Mode[6]      1                                                                                                         ==  
#--------------------------------------------------------------------------------------------------------------------------------------
#   resource usage/capacity     
#--------------------------------------------------------------------------------------------------------------------------------------
#            worker             2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1
#                               2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
-----------------------------------------------------------------------------------------

このプログラムを実行して、監督者を1人増やすと、メイクスパンは52分に収まることが分かります。

コマンドdir(作業インスタンス)を用いて、適用可能なメソッドのリストを得るとができます。
 :
‘addModes’,
‘autoselect’,
‘backward’,⇒前詰めか後詰めか
‘completion’,⇒完了時刻
‘duedate’,⇒納期
‘execute’,
‘modes’,
‘name’,⇒作業名
‘selected’,⇒選択されたモード
‘start’,⇒開始時刻
‘weight’


●例題ex8:時間制約

例題ex8は、2つの作業の開始のタイミングを揃えたり、ある作業の開始のタイミングを固定する方法を示しています。

#optseq_ex8.py
from optseq import *
#-----データセット
ex8=Model()
data={\
1:[13,[3],  0],\
2:[25,[5,4],0],\
3:[15,[4],  5],\
4:[27,[6],  0],\
5:[22,[6],  0],\
6:[1, [0],  0],\
}
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex8.addActivity("Act[{0}]".format(i))
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  act[i].addModes(mode[i])
#-----先行制約
for i in data:
  if data[i][1][0]!=0:
    for j in data[i][1]:
      ex8.addTemporal(act[i],act[j])
#-----同時開始
for i in data:
  if data[i][2]>0:
    ex8.addTemporal(act[i],act[data[i][2]],'SS',0)
    ex8.addTemporal(act[data[i][2]],act[i],'SS',0)
#-----最適化
ex8.Params.TimeLimit=1
ex8.Params.Makespan=True
#ex8.Params.OutputFlag=True
ex8.optimize()
#ex8.writeExcel("ex8_result.csv")
ex8.write("ex8_result.txt")
#-----ex8_result.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
#----------------------------------------------------------------------------------------------------------------------------------------------------------------------
#  Act[1]   Mode[1]      13    ==========================                                                                                                              
#  Act[2]   Mode[2]      25    ==================================================                                                                                      
#  Act[3]   Mode[3]      15                                                      ==============================                                                        
#  Act[4]   Mode[4]      27                                                                                    ======================================================  
#  Act[5]   Mode[5]      22                                                      ============================================                                          
#  Act[6]   Mode[6]      1                                                                                                                                           ==
#----------------------------------------------------------------------------------------------------------------------------------------------------------------------
#   resource usage/capacity     
----------------------------------------------------------------------------------------------------------------------------------------------------------------------


このプログラムを実行して、作業3と5の開始のタイミングが揃っていることが分かります。ex8_result.csvでは次のように確認できます。

例題(ex3,ex4,ex10)


●例題ex3:並列ショップスケジューリング

例題ex3は、作業間の先行関係が次表のように与えられるとき、最終作業(出発)の完了時刻(メイクスパン)を求めようとしています。

# 作業名 作業期間 後続作業
1 給油準備 3 9
2 飲料水取替 2 11
3 ガラス拭き 2 11
4 ジャッキアップ 2 5 6 7 8
5 タイヤ交換(前輪左側) 4 10
6 タイヤ交換(前輪右側) 4 10
7 タイヤ交換(後輪左側) 4 10
8 タイヤ交換(後輪右側) 4 10
9 給油 11 11
10 ジャッキダウン 2 11
11 出発 1 0

ただし、配員可能な作業員数は次の通りとします。

リソース 許容量
1 作業員 3

#optseq_ex3.py
from optseq import *	
#-----データセット	
ex3=Model()	
data={\	
1:[3,[9]],\	
2:[2,[11]],\	
3:[2,[11]],\	
4:[2,[5,6,7,8]],\	
5:[4,[10]],\	
6:[4,[10]],\	
7:[4,[10]],\	
8:[4,[10]],\	
9:[11,[11]],\	
10:[2,[11]],\	
11:[1,[0]],\	
}	
res=ex3.addResource("worker",capacity={(0,"inf"):3})	
#-----作業とモード	
act={}	
mode={}	
for i in data:	
  act[i]=ex3.addActivity("Act[{0}]".format(i))	
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])	
  mode[i].addResource(res,requirement=1)
  act[i].addModes(mode[i])	
#-----先行制約	
for i in data:	
  if i<11:	
    for j in data[i][1]:	
      ex3.addTemporal(act[i],act[j])	
#-----最適化	
ex3.Params.TimeLimit=1	
ex3.Params.Makespan=True	
#ex3.Params.OutputFlag=True	
ex3.optimize()	
#ex3.writeExcel("ex3_result.csv")	
ex3.write("ex3_result.txt")		
#-----ex3_result.txt
#   activity    mode   duration  1 2 3 4 5 6 7 8 9101112131415
# -------------------------------------------------------------
#   Act[1]   Mode[1]      3     ======                        
#   Act[2]   Mode[2]      2     ====                          
#   Act[3]   Mode[3]      2     ====                          
#   Act[4]   Mode[4]      2         ====                      
#   Act[5]   Mode[5]      4             ========              
#   Act[6]   Mode[6]      4                     ========      
#   Act[7]   Mode[7]      4             ========              
#   Act[8]   Mode[8]      4                     ========      
#   Act[9]   Mode[9]      11          ======================  
#  Act[10]   Mode[10]     2                             ====  
#  Act[11]   Mode[11]     1                                 ==
# -------------------------------------------------------------
#    resource usage/capacity     
# -------------------------------------------------------------
#             worker             3 3 2 2 3 3 3 3 3 3 3 3 2 2 1
#                                3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
# -------------------------------------------------------------


このプログラムを実行して、最大配員数の範囲内で作業を並列化する(同時進行させる)ことができています。ex3_result.csvでは次のようになります。


●例題ex4:作業の並列実行(モードの利用)

例題ex4は、作業1についての配員数を変えたモードを複数個設定することで、メイクスパンを短縮するモードを自動選択させています。

#optseq_ex4.py
from optseq import *
#-----データセット
ex4=Model()
data={\
1:[3,[9]],\
2:[2,[11]],\
3:[2,[11]],\
4:[2,[5,6,7,8]],\
5:[4,[10]],\
6:[4,[10]],\
7:[4,[10]],\
8:[4,[10]],\
9:[11,[11]],\
10:[2,[11]],\
11:[1,[0]],\
}
res=ex4.addResource("worker",capacity={(0,"inf"):3})
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex4.addActivity("Act[{0}]".format(i))
  if i==1:
    mode[1,1]=Mode("Mode[1_1]",duration=3)
    mode[1,1].addResource(res,requirement=1)
    mode[1,2]=Mode("Mode[1_2]",duration=2)
    mode[1,2].addResource(res,requirement=2)
    mode[1,3]=Mode("Mode[1_3]",duration=1)
    mode[1,3].addResource(res,requirement=3)
    act[i].addModes(mode[1,1],mode[1,2],mode[1,3])
  else:
    mode[i]=Mode("Mode[{0}]".format(i),data[i][0])
    mode[i].addResource(res,requirement=1)
    act[i].addModes(mode[i])
#-----先行制約
for i in data:
  if i<11:
    for j in data[i][1]:
      ex4.addTemporal(act[i],act[j])
#-----最適化
ex4.Params.TimeLimit=1
ex4.Params.Makespan=True
#ex4.Params.OutputFlag=True
ex4.optimize()
#ex4.writeExcel("ex4_result.csv")
ex4.write("ex4.txt")
#-----ex4_result.txt
#   activity    mode   duration  1 2 3 4 5 6 7 8 91011121314
# -----------------------------------------------------------
#   Act[1]  Mode[1_3]     1     ==                          
#   Act[2]   Mode[2]      2       ====                      
#   Act[3]   Mode[3]      2                           ====  
#   Act[4]   Mode[4]      2       ====                      
#   Act[5]   Mode[5]      4           ========              
#   Act[6]   Mode[6]      4                   ========      
#   Act[7]   Mode[7]      4                   ========      
#   Act[8]   Mode[8]      4           ========              
#   Act[9]   Mode[9]      11      ======================   
#  Act[10]   Mode[10]     2                           ====  
#  Act[11]   Mode[11]     1                               ==
# -----------------------------------------------------------
#    resource usage/capacity     
# -----------------------------------------------------------
#             worker             3 3 3 3 3 3 3 3 3 3 3 3 2 1
#                                3 3 3 3 3 3 3 3 3 3 3 3 3 3
# -----------------------------------------------------------


このプログラムを実行して、作業1について3人を配員するモードmode[1,3]が選ばれています。メイクスパンはex3より1日短くなっています。ex4_result.csvでは次のようになります。


●例題ex10:作業の並列実行(小作業の並列化)

例題ex10は、作業1を1人で行うものとして登録し、これを配員可能な複数人で行って、並列化することを狙っています。この手法は小作業並列化手法と呼ばれ、作業期間が自動調整されることから、高度な平準化手法の基礎となる可能性があります(詳しくは改めて説明します)。

#optseq_ex10.py
from optseq import *
#-----データセット
ex10=Model()
data={\
1:[3,[9]],\
2:[2,[11]],\
3:[2,[11]],\
4:[2,[5,6,7,8]],\
5:[4,[10]],\
6:[4,[10]],\
7:[4,[10]],\
8:[4,[10]],\
9:[11,[11]],\
10:[2,[11]],\
11:[1,[0]],\
}
res=ex10.addResource("worker",capacity={(0,"inf"):3})
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex10.addActivity("Act[{0}]".format(i))
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=1)
  if i==1:
    mode[i].addParallel(start=1,finish=1,maxparallel=3)
  act[i].addModes(mode[i])
#-----先行制約
for i in data:
  if i<11:
    for j in data[i][1]:
      ex10.addTemporal(act[i],act[j])
#-----最適化
ex10.Params.TimeLimit=1
ex10.Params.Makespan=True
#ex10.Params.OutputFlag=True
ex10.optimize()
#ex10.writeExcel("ex10_result.csv")
ex10.write("ex10_result.txt")
#-----ex10_result.txt
#   activity    mode   duration  1 2 3 4 5 6 7 8 910111213
# ---------------------------------------------------------
#   Act[1]   Mode[1]      3     *3                        
#   Act[2]   Mode[2]      2                           ====
#   Act[3]   Mode[3]      2                           ====
#   Act[4]   Mode[4]      2       ====                    
#   Act[5]   Mode[5]      4           ========            
#   Act[6]   Mode[6]      4       ========                
#   Act[7]   Mode[7]      4               ========        
#   Act[8]   Mode[8]      4                   ========    
#   Act[9]   Mode[9]      11        ======================
#  Act[10]   Mode[10]     2                       ====    
#  Act[11]   Mode[11]     1       ==                      
# ---------------------------------------------------------
#    resource usage/capacity     
# ---------------------------------------------------------
#             worker             3 3 3 3 3 3 3 3 3 3 3 3 3
#                                3 3 3 3 3 3 3 3 3 3 3 3 3
# ---------------------------------------------------------


このプログラムを実行して、作業1は作業員3人で行ない作業期間は1日となっており(■*3)、ex4と同じメイスパンとなっています。ex10_result.csvでは次のようになります。

例題(ex5,ex6,ex20,ex9)


●例題ex5:一般化資源制約付きスケジューリング

例題ex5は、リソースの許容量を期間ごとに決めています。これは休日をもつカレンダーを導入するために役立ちます。

#optseq_ex5.py
from optseq import *
#-----データセット
ex5=Model()
data={\
1:[1,2,[2,4]],\
2:[1,2,[3]],\
3:[2,1,[5]],\
4:[2,1,[7]],\
5:[1,1,[6]],\
6:[1,2,[7]],\
7:[1,1,[0]],\
}
res=ex5.addResource("worker")
res.addCapacity(start=0,finish=2,amount=2)
res.addCapacity(start=2,finish=3,amount=1)
res.addCapacity(start=3,finish="inf",amount=2)
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex5.addActivity("Act[{0}]".format(i))
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=data[i][1])
  act[i].addModes(mode[i])
#-----先行制約
for i in data:
  if i<7:
    for j in data[i][2]:
      ex5.addTemporal(act[i],act[j])
#-----最適化
ex5.Params.TimeLimit=1
ex5.Params.Makespan=True
#ex5.Params.OutputFlag=True
ex5.optimize()
#ex5.writeExcel("ex5_result.csv")
ex5.write("ex5.txt")
#-----ex5_result.txt
#   activity    mode   duration 1234567
# --------------------------------------
#   Act[1]   Mode[1]      1     =      
#   Act[2]   Mode[2]      1      =     
#   Act[3]   Mode[3]      2       ==   
#   Act[4]   Mode[4]      2        ==  
#   Act[5]   Mode[5]      1         =  
#   Act[6]   Mode[6]      1          = 
#   Act[7]   Mode[7]      1           =
# --------------------------------------
#    resource usage/capacity     
# --------------------------------------
#             worker            2212221
#                               2212222
# --------------------------------------


このプログラムを実行して、3日目の配員が1名に抑えられていることが分かります。


●例題ex6:納期遅れ最小化スケジューリング

例題ex6は、納期のある複数個の作業を、納期遅れ総和を最小にするためには、どの順番で実施すればよいかを検討しています。

#optseq_ex6.py
from optseq import *
#-----データセット
data={\
ex6=Model()
1:[1,5],\
2:[2,9],\
3:[3,6],\
4:[4,4],\
}
res=ex6.addResource("writer")
res.addCapacity(0,"inf",1)
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex6.addActivity("Act[{0}]".format(i),duedate=data[i][1])
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=1)
  act[i].addModes(mode[i])
#-----最適化
ex6.Params.TimeLimit=1
ex6.Params.Makespan=False
#ex6.Params.OutputFlag=True
ex6.optimize()
#ex6.writeExcel("ex6_result.csv")
ex6.write("ex6_result..txt")
#-----ex6_result.txt
#   activity    mode   duration  1 2 3 4 5 6 7 8 910
# ---------------------------------------------------
#   Act[1]   Mode[1]      1             ==          
#   Act[2]   Mode[2]      2                     ====
#   Act[3]   Mode[3]      3               ======    
#   Act[4]   Mode[4]      4     ========            
# ---------------------------------------------------
#    resource usage/capacity     
# ---------------------------------------------------
#             writer             1 1 1 1 1 1 1 1 1 1
#                                1 1 1 1 1 1 1 1 1 1
# ---------------------------------------------------


このプログラムを実行して、作業4,1,3,2の順に行うと納期遅れが最小になることが分かります。それでも、ex6_result.csvにで示すような納期遅れが生じています。


●例題ex20:納期遅れをしない範囲でなるべく多くの仕事をこなす方法

例題ex20では、この目的を達成するために、作業期間0のモードをうまく利用しています。

##optseq_ex20.py
from optseq import *
#-----データセット
ex20= Model()
data={
1:[5,1],\
2:[9,2],\
3:[6,3],\
4:[4,4],\
}
res=ex20.addResource("writer")
res.addCapacity(0,"inf",1)
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex20.addActivity(f"Act[{i}]",duedate=data[i][0],weight=100)
  mode[i]=Mode(f"Mode[{i}]",duration=data[i][1])
  mode[i,0]=Mode(f"Mode[{i}0]",duration=0)
  mode[i].addResource(res,1)
  act[i].addModes(mode[i],mode[i,0])
#-----モード制約
con1=ex20.addResource("constraint_1",rhs=0,direction="<=",weight=1)
con1.addTerms(coeffs=[1,1,1,1], vars=[act[1],act[2],act[3],act[4]],\
              values=[mode[1,0],mode[2,0],mode[3,0],mode[4,0]])
#-----最適化
ex20.Params.TimeLimit=1
ex20.Params.Makespan=False
#ex20.Params.OutputFlag=True
ex20.optimize()
#ex20.writeExcel("ex20_result.csv")
ex20.write("ex20_result.txt")
#-----ex20_result.txt
#   activity    mode   duration 1234567
# --------------------------------------
#   Act[1]   Mode[1]      1         =  
#   Act[2]   Mode[2]      2          ==
#   Act[3]   Mode[30]     0            
#   Act[4]   Mode[4]      4     ====   
# --------------------------------------
#    resource usage/capacity     
# --------------------------------------
#             writer            1111111
#                               1111111
# --------------------------------------


このプログラムを実行して、作業3を行わなければ、他の作業は納期に間に合うことが分かります。

●例題ex9:作業の途中中断 

例題ex9は、例題ex6に休業日を考慮する手法を示しています。

#optseq_ex9.py
from optseq import *
#-----データセット
ex9=Model()
data={\
1:[1,5],\
2:[2,9],\
3:[3,6],\
4:[4,4],\
}
res=ex9.addResource("writer")
res.addCapacity(0,3,1)
res.addCapacity(4,6,1)
res.addCapacity(7,10,1)
res.addCapacity(11,"inf",1)
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex9.addActivity("Act[{0}]".format(i),duedate=data[i][1])
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=1)
  mode[i].addBreak(start=0,finish='inf',maxtime=1)
  mode[i].addResource(res,{(0,"inf"):1},"break")  #break中も資源を使う場合
  act[i].addModes(mode[i])
#-----最適化
ex9.Params.TimeLimit=1
#ex9.Params.OutputFlag=True
ex9.Params.Makespan=False
ex9.optimize()
#ex9.writeExcel("ex9_result.csv")
ex9.write("ex9_result.txt")
#-----ex9_result.txt
#   activity    mode   duration  1 2 3 4 5 6 7 8 9101112131415
# -------------------------------------------------------------
#   Act[1]   Mode[1]      1             ==                    
#   Act[2]   Mode[2]      2                   ====            
#   Act[3]   Mode[3]      3     ======                        
#   Act[4]   Mode[4]      4                           ========
# -------------------------------------------------------------
#    resource usage/capacity     
# -------------------------------------------------------------
#             writer             1 1 1 0 1 0 0 1 1 0 0 1 1 1 1
#                                1 1 1 0 1 1 0 1 1 1 0 1 1 1 1


このプログラムを実行して、次の結果①を得ます。結果②、③,④は中断中のリソースの使用を不可にしたり、中断期間を無制限にしたりして、計画がどのように変わるかを調べています。

例題(ex7,ex19,ex21)


●例題ex7:クリティカルパス法

例題ex7は、例題ex1の作業時間を、次表のように短縮する可能性を探ろうとしています。ただし、短縮する場合は1万円必要とするとします。

# 作業名 作業期間 後続作業 短縮時間
1 乗客降し 13 3 10
2 荷物降し 25 5 4 20
3 機内清掃 15 4 10
4 乗客搭乗 27 6 25
5 荷物積込 22 6 20
6 出発 1 0 1

そのために各作業に通常の作業時間をもつモード1と短縮された作業時間をもつモード2を割り当て、モード2を使う場合は資金rhs万円(再生不能資源)の中から1万円使用するようにします。これは制約式

    \[c_1\times x_{1,2}+c_2\times x_{2,2}+\cdots+c_5\times x_{5,2}\le rhs(man\ yen)\]

ただし

    \[c_1=c_2=c_3=c_4=c_5=1(man\ yen)\]

    \[x_{1,2},\ x_{2,2},\ x_{3,2},\ x_{4,2},\ x_{5,2}=1\ or\ 0\]

を課すように問題定式化が行われます。

#optseq_ex7.py
from optseq import *
#-----データセット
ex7=Model()
data={\
1:[13,[3],  10],\
2:[25,[5,4],20],\
3:[15,[4],  10],\
4:[27,[6],  25],\
5:[22,[6],  30],\
6:[ 1,[0],   1],\
}
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex7.addActivity("Act[{0}]".format(i))
  mode[i,1]=Mode("Mode[{0}][1]".format(i),duration=data[i][0])
  mode[i,2]=Mode("Mode[{0}][2]".format(i),duration=data[i][2])
  act[i].addModes(mode[i,1],mode[i,2])
#-----モード制約
res=ex7.addResource("money",rhs=5,direction="<=")
for i in data:
  if data[i][1][0]!=0:
    res.addTerms(1,act[i],mode[i,2])
#-----先行制約
for i in data:
  if data[i][1][0]!=0:
    for j in data[i][1]:
      ex7.addTemporal(act[i],act[j])
#-----最適化
ex7.Params.TimeLimit=1
ex7.Params.Makespan=True
#ex7.Params.OutputFlag=True
ex7.optimize()
#ex7.writeExcel("ex7_result.csv")
#ex7.write("ex7.txt")


このプログラムを実行して、次の結果を得ます。ただし、資金1,2,3はそれぞれ4万円、1万円、0万円(rhs=4,1,0)に対応しています。


●例題ex19:モード間の関係

#optseq_ex19.py
from optseq import *
#-----データセット
model=Model()
duration ={ 
("A",0) :10,\
("A",1) :13,\
("A",2) :12,\
("B",0) :15,\
("B",1) :11,\
("B",2) :12,\
("B",3) :14,\
}
#-----作業とモード
act={}
mode={}
act["A"] = model.addActivity("Act[A]")
act["B"] = model.addActivity("Act[B]")
for i,j in duration:
  mode[i,j] = Mode(f"Mode[{i},{j}]", duration[i,j])
act["A"].addModes(mode["A",0], mode["A",1], mode["A",2])
act["B"].addModes(mode["B",0], mode["B",1], mode["B",2], mode["B",3] )
#-----モード制約
con1=model.addResource("constraint_1",rhs=0,direction = "=")
con1.addTerms(coeffs=[1,-1], vars= [act["A"], act["B"]],\
 values =[mode["A",0],mode["B",0]])
con2=model.addResource("constraint_2",rhs=1,direction = "<=")
con2.addTerms(coeffs=[1,1], vars= [act["A"], act["B"]],\
 values =[mode["A",1],mode["B",1]])
con3=model.addResource("constraint_3",rhs=0,direction = "<=")
con3.addTerms(coeffs=[1,-1,-1], vars= [act["A"], act["B"], act["B"]],\
 values =[mode["A",2], mode["B",2], mode["B",3]])
#-----最適化
model.Params.TimeLimit=1
model.Params.Makespan=True
model.optimize()
#-----
#eof


任意の作業aがモードmをとるとき値1を、m以外のモードをとるとき値0をとる変数をx_{a,m}とします。このとき
1^\circ 作業Aと作業Bに対して同じモードmを選択させる制約式は

    \[x_{A,m}=x_{B,m}\ \Rightarrow\ x_{A,m}+(-1)x_{B,m}=0\]

2^\circ 作業Aと作業Bのどちらかにモードmを選択させる制約式は

    \[x_{A,m}+x_{B,m}\le 1\]

3^\circ 作業Aがモードmを選択するとき、作業Bにはモードmまたはモードm'を選択させる制約式は

    \[x_{A,m}\le x_{B,m}+x_{B,m'}\ \Rightarrow\ x_{A,m}+(-1)x_{B,m}+(-1)x_{B,m'}\le0\]


●例題ex21:資源の優先利用の設定法

#optseq_ex21.py
from optseq import *
#-----データセット
ex21=Model()
data={
1:[3,[]],\
2:[2,[]],\
3:[2,[]],\
4:[2,[]],\
5:[4,[]],\
6:[4,[]],\
7:[4,[]],\
8:[4,[]],\
9:[11,[]],\
10:[2,[]],\
}
res={}
for r in range(1,4):
  res[r]=ex21.addResource("worker"+str(r), capacity=1)
#-----作業とモード
act={}
mode={}
for i in data:
  act[i]=ex21.addActivity("Act[{0}]".format(i))
  for r in range(1,4):
    mode[i,r]=Mode("Mode[{0}{1}]".format(i,r),duration=data[i][0])
    mode[i,r].addResource(res[r],1)
    act[i].addModes(mode[i,r])
#-----モード制約  
con={}
weight={1:1, 2:2, 3:3}
for r in range(1,4):
  con[r]=ex21.addResource(name="Constraint{0}".format(r),\
                          rhs=0,direction="<=",weight=weight[r])
  for i in data:
    con[r].addTerms(1,act[i],mode[i,r])
#-----先行制約
ex21.addTemporal(act[1],act[9])
for i in range(5,9):
  ex21.addTemporal(act[4],act[i])
  ex21.addTemporal(act[i],act[10])
#-----最適化
ex21.Params.TimeLimit=1
ex21.Params.Makespan=True
#ex21.Params.OutputFlag=True
ex21.optimize()
ex21.writeExcel("ex21_result.csv")
#ex21.write("ex21.txt")
#-----
#eof

工程計画ベンチマーク問題

●一般の工程計画問題は、日程計画・配員計画・定盤計画・運搬計画がお互いに連動すると考えられます。ここで、定盤計画とは作業場所の割付を意味しています。これを解くために参考になると思われるベンチマーク問題を作成してみました。各計画において考慮すべきファクターとして、次のものがあります。

1^\circ 日程計画: 作業間先行制約
2^\circ 配員計画: 作業員(平準化)、工場カレンダー
3^\circ 定盤計画: 作業場所、待機場所
4^\circ 運搬計画: 作業機械(台車、クレーン)

これらの計画を行うためには、まず各作業の総時数と各作業員数を考慮して、各作業期間を決定し、納期までの先行関係を明らかにし、配員計画と日程計画を同時に立てます。次に日程計画に沿って、作業場所の確保(定盤計画)を行います。場所が空いていなければ、待機が必要となりますが、その期間を計画前に予め把握することはできないことに注意します。また、各運搬日には運搬機械を使う時間帯を決める運搬計画を立てます。さらに、悪天候や機械の故障、作業員の欠勤などによる工程中断後、工程の再計画(リスケジューリング)を行う必要があります。

●次の初期計画を考えます。

これから作業と先行制約に関して次表を得ます。

また、リソースについては次表のようにまとめられます。

(1) 作業場所・待機場所

(2) 作業機械

(3) 作業員

●次のプログラムを考えます、

#prob31.py
from optseq import *
import math
#=====リソース
bp=Model() 
resJ={}
for j in range(0,4):
  for k in range(0,5):
    resJ[j,k] =bp.addResource("J{0}{1}".format(j,k), capacity={(0,"inf"):1})
resH={}
for k in range(0,5):
  resH[k] =bp.addResource("H{0}".format(k), capacity={(0,"inf"):3})
resP=bp.addResource("P", capacity={(0,"inf"):1}) 
#resD=bp.addResource("D", capacity={(0,"inf"):3})
resT=bp.addResource("T", capacity={(0,"inf"):20})
resC={}
for i in range(0,2):
  for j in range(0,3):
    resC[i,j] =bp.addResource("C{0}{1}".format(i,j), capacity={(0,"inf"):1})
resW1=bp.addResource("W1", capacity={(0,"inf"):1}) 
resW2=bp.addResource("W2", capacity={(0,"inf"):4})
#=====データセット
#0:"アクティビティ", #1:[作業ID],
#2:[作業日数], #3:"納期(順時間)", 
#4:["後続アクティビティNo(順時間)"], #5:["CS時差(順時間)"], 
#6:"作業場所選択肢", #7:["作業場所使用量"], 
#8:"待機場所選択肢", #9:["待機場所使用量"], 
#10:"作業設備選択肢", #11:{"作業設備使用量"},
#12:["職種選択肢"], #13:["作業員使用期間"],#14:["作業員使用量"]  
J=1; P=2; D=3; T=1; H=1; C=2; W1=1; W2=2;
data={\
1:["100_B1_取付",[1],[2], 13, [2],[0], J, [3, 2], 0, [0], H, {1:[3]},[W1],[2],[1]],\
2:["100_B1_溶接",[2],[4], 18, [-3,9],[0,0], J, [3, 2], 0, [0], H, {1:[2]},[W2],[4],[2]],\
3:["100_B1_塗装",[3],[1], 21, [-4],[0], P, [1], T, [3, 2], 0, {0},[0],[0],[0]],\
4:["100_B1_搭載",[4],[1], 25, [0],[0], D, [0], T, [3, 2], C, {1:[1, 2]},[0],[0],[0]],\
5:["100_B2_取付",[1],[3], 14, [6],[-1], J, [2, 3], 0, [0], H, {1:[2]},[W1],[3],[1]],\
6:["100_B2_溶接",[2],[5], 18, [-7,9],[0,0], J, [2, 3], 0, [0], H, {1:[2]},[W2],[5],[2]],\
7:["100_B2_塗装",[3],[1], 21, [-8],[0], P, [1], T, [2, 3], 0, {0},[0],[0],[0]],\
8:["100_B2_搭載",[4],[1], 25, [0],[0], D, [0], T, [2, 3], C, {1:[1, 2]},[0],[0],[0]],\
9:["100_B3_取付",[1],[1], 20, [10],[0], J, [3, 3], 0, [0], H, {1:[1]},[W1],[1],[1]],\
10:["100_B3_溶接",[2],[2], 22, [-11],[0], J, [3, 3], 0, [0], H, {1:[1]},[W2],[2],[2]],\
11:["100_B3_搭載",[4],[1], 26, [0],[0], D, [0], T, [3, 3], C, {1:[1, 2]},[0],[0],[0]],\
}  
#=====アクティビティ
act={}
for i in data:                                  #納期設定
  act[i]=bp.addActivity(data[i][0],duedate=data[i][3])
#=====先行制約
for i in data:
  if not data[i][4]==[0]:                       #次作業が搭載(終端)でなければ先行関係を定義
    for j in range(0,len(data[i][4])):
      bp.addTemporal(act[i],act[abs(data[i][4][j])],tempType="CS",delay=0)# data[i][5][j])
      if data[i][1]==[1]:        
        bp.addTemporal(act[abs(data[i][4][j])],act[i],tempType="SC",delay=0) #-data[i][5][j])
#-----納期日
for i in data:
  if data[i][1]==[4]:                           #現作業が搭載ならば納期を実日程に固定
    bp.addTemporal("source",act[i],"SS",delay= data[i][3])
    bp.addTemporal(act[i],"source","SS",delay=-data[i][3])   
bp.addTemporal(act[2],act[6],"CC",delay=0)
bp.addTemporal(act[6],act[2],"CC",delay=0)
#-----開始日
start=12
bp.addTemporal("source",act[1],"SS",delay= start)
bp.addTemporal(act[1],"source","SS",delay=-start)
bp.addTemporal("source",act[5],"SS",delay= start)
bp.addTemporal(act[5],"source","SS",delay=-start)
#=====資源制約
mode={}
for i in data:
#-----作業場所(組立定盤) resJ
  if data[i][6]==J:
    l=data[i][7][0]
    b=data[i][7][1]
    for j in range(0,4-b+1):          #幅方向の定盤選択
      for k in range(0,5-l+1):        #長さ方向の定盤選択
        mode[i,j,k]=Mode("mode[{0:03d}][{1}_{2}][{3}_{4}]".format(i,j,k,b,l),duration=sum(data[i][2]))
        mode[i,j,k].addBreak(0,'inf')
        for s in range(0,b):  #ブロック幅の定盤確保
          for t in range(0,l):#ブロック長の定盤確保
            mode[i,j,k].addResource(resJ[j+s,k+t],1)
            mode[i,j,k].addResource(resJ[j+s,k+t],1,"break")
            mode[i,j,k].addResource(resH[k+t],data[i][11][1][0])
            mode[i,j,k].addResource(resH[k+t],data[i][11][1][0],"break")        
#-----高さ制約
        if i==2 or i==6 or i==10:
          for q in range(k+l,5):  
            mode[i,j,k].addResource(resH[q],data[i][11][1][0],"break")     
#-----員数制約
        if data[i][12][0]==W1:
          mode[i,j,k].addResource(resW1,data[i][14][0])
        if data[i][12][0]==W2:
          mode[i,j,k].addResource(resW2,data[i][14][0])
        act[i].addModes(mode[i,j,k])
#-----作業場所(塗装工場) resP
  if data[i][6]==P:
    mode[i]=Mode("mode[{0:03d}1]".format(i),duration=data[i][2][0])
    mode[i].addResource(resP,1)
    act[i].addModes(mode[i])
#-----作業場所(ドック) resD, resC
  if data[i][6]==D:
    for time in data[i][11]:
      for j in range(0,3-time):               #開始時間
        mode[i,j]=Mode("mode[{0:03d}1][{1}]".format(i,j),duration=1)
       #mode[i,j].addResource(resD,{(0,1):1})
        for k in data[i][11][time]:
          for s in range(0,time):             #稼働時間
            mode[i,j].addResource(resC[k-1,j+s],1)
        act[i].addModes(mode[i,j])
#=====待機場所(塗装前、搭載前) resT
d_act={}
for i in data:
  if data[i][1]==[3] or data[i][1]==[4]:
    d_act[i]=bp.addActivity("act[{0:03d}]待機".format(i))
    bp.addTemporal(act[i-1],d_act[i],tempType="CS")
    bp.addTemporal(d_act[i],act[i-1],tempType="SC")
    bp.addTemporal(d_act[i],act[i],tempType="CS")
    bp.addTemporal(act[i],d_act[i],tempType="SC")
d_mode={}
for i in data:
  if data[i][1]==[3] or data[i][1]==[4]:
    d_mode[i]=Mode("mode[{0:03d}]".format(i))
    d_mode[i].addBreak(0,0)
    d_mode[i].addResource(resT,{(0,"inf"):data[i][9][0]*data[i][9][1]},"break")
    d_act[i].addModes(d_mode[i])
#=====モード制約
n_res={}
for i in data:
  if data[i][6]==J  and data[i+1][6]==J:
    l=data[i][7][0]
    b=data[i][7][1]
    for j in range(0,4-b+1):
      for k in range(0,5-l+1):      
        n_res[i,j,k]=bp.addResource("constraint[{0}_{1}_{2}]".format(i,j,k),rhs=0,direction="=")
        n_res[i,j,k].addTerms(1,act[i],mode[i,j,k])
        n_res[i,j,k].addTerms(-1,act[i+1],mode[i+1,j,k])
#=====最適化
bp.Params.Makespan=False
bp.Params.Initial=False
bp.Params.TimeLimit=1
bp.Params.Neighborhood=20
bp.optimize()
bp.writeExcel("bp_result.csv")
bp.write("bp_result.txt")
#-----
#eof

●日程計画

●定盤計画

●その他

場所の割付

ここでは、作業場所の割付、配置計画について検討します。

上図のように、作業場所はサイズ5Lx5Bの定盤で、ここにサイズ2Lx2Bの製作物を3日間だけ据置きたいとします。ただし黒色のメッシュは柱があって使えないとします。このとき割付可能な候補は12通りあり、このうちのどれか1つを選びたいとします。

#prob21.py
from optseq import *
#=====リソース
prob21=Model()
res={}
for i in range(0,5):
  for j in range(0,5):  
    if i==1 and j==3 :
      res[i,j] =prob21.addResource("place[{0:02d}_{1:02d}]".format(i,j),capacity={(0,"inf"):0})            
    else:     
      res[i,j] =prob21.addResource("place[{0:02d}_{1:02d}]".format(i,j),capacity={(0,"inf"):1})           
#====データセット
#i:[期間,納期,[l,b]]
data={1:[3,5,[2,2]]}    
#=====アクティビティ
act={}
for i in data.keys():
  act[i]=prob21.addActivity("act[{0}]".format(i))
#====資源制約  
L=5; B=5; L1=0; L2=4; B1=0; B2=4; skipL=1; skipB=1; 
mode={}  
for i in data.keys():
  #mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  l=data[i][2][0]; b=data[i][2][1];
  for j in range(L1,L2-l+1,skipL):
    for k in range(B1,B2-b+1,skipB):         
      mode[i,j,k]=Mode("mode[{0:02d}]_[{1:02d}_{2:02d}][{3:02d}_{4:02d}]".\
                  format(i,j,k,l,b),duration=data[i][0])
      for s in range(0,l):
        for t in range(0,b):
          mode[i,j,k].addResource(res[j+s,k+t],1)         
      act[i].addModes(mode[i,j,k])       
#=====最適化
prob21.Params.Makespan=True
prob21.Params.TimeLimit=1
#prob3.Params.OutputFlag=True
prob21.optimize()
prob21.write("prob21.txt")
prob21.writeExcel("prob21.csv")
#=====prob21.txt
#  activity    mode   duration 123
#----------------------------------
#  act[1]  mode[01]_[01_00][02_02]    3     ===
#----------------------------------
#   resource usage/capacity     
#----------------------------------
#         place[00_00]         000      #         place[03_00]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[00_01]         000      #         place[03_01]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[00_02]         000      #         place[03_02]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[00_03]         000      #         place[03_03]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[00_04]         000      #         place[03_04]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[01_00]         111      #         place[04_00]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[01_01]         111      #         place[04_01]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[01_02]         000      #         place[04_02]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[01_03]         000      #         place[04_03]         000
#                              000      #                              111
#----------------------------------     #----------------------------------
#         place[01_04]         000      #         place[04_04]         000
#                              111      #                              111
#----------------------------------     #----------------------------------
#         place[02_00]         111
#                              111
#----------------------------------
#         place[02_01]         111
#                              111
#----------------------------------
#         place[02_02]         000
#                              111
#----------------------------------
#         place[02_03]         000
#                              111
#----------------------------------
#         place[02_04]         000
#                              111
#----------------------------------  

このプログラムを実行して、次の結果を得ます。

OptSeqによる平準化

OptSeqによる平準化

以下では、「工程’s」による平準化で扱った同じ問題を考えます。

●まず山積みを行うプログラムは次のようになります。 

#prob11.py
from optseq import *
#=====リソース
prob11=Model()
res1=ex.addResource("Resource",capacity={(0,"inf"):20}) #10
#-----データセット
#i:[期間、後続、納期]
data={\
1:[3,[4,5],3],\
2:[4,[6,7],1],\
3:[6,[9]  ,1],\
4:[4,[6,7],4],\
5:[5,[8]  ,4],\
6:[4,[10] ,5],\
7:[5,[9],  4],\
8:[2,[10] ,5],\
9:[2,[10] ,2],\
10:[5,[0] ,3],\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob11.addActivity("Act[{0}]".format(i))
#=====先行制約
for i in data:
  for j in data[i][1]:
    if j>0: prob11.addTemporal(act[i],act[j])  
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0:02d}]".format(i),data[i][0])
  mode[i].addResource(res1,requirement=data[i][2])
  act[i].addModes(mode[i])
#=====最適化
prob11.Params.Makespan=True
prob11.Params.TimeLimit=1
#prob11.Params.OutputFlag=True
prob11.optimize()
prob11.write("prob11.txt")
prob11.writeExcel("prob11.csv")
#=====prob11.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 910111213141516171819
#---------------------------------------------------------------------
#  Act[1]   Mode[01]     3     ======                                
#  Act[2]   Mode[02]     4     ========                              
#  Act[3]   Mode[03]     6     ============                          
#  Act[4]   Mode[04]     4           ========                        
#  Act[5]   Mode[05]     5           ==========                      
#  Act[6]   Mode[06]     4                   ========                
#  Act[7]   Mode[07]     5                   ==========              
#  Act[8]   Mode[08]     2                     ====                  
#  Act[9]   Mode[09]     2                             ====          
# Act[10]   Mode[10]     5                                 ==========
#---------------------------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------------------------
#           Resource            5 5 510 9 9 8131414 9 4 2 2 3 3 3 3 3
#                              20202020202020202020202020202020202020
#--------------------------------------------------------------------- 

このプログラムを実行して、次の結果を得ます。

この結果は「工程’s」では次のように得られていました。


●これに対してOptSeqでは、次のような平準化が可能です。

これを行うプログラムを次に示します。データセットでは、各作業を1人で行う場合の作業期間が登録されています。これを小作業の並列化という手法を用いて平準化しています。

#prob12.py
from optseq import *
#=====リソース
prob12=Model()
res1=ex.addResource("Resource",capacity={(0,"inf"):10})
#-----データセット
#i:[期間、後続、納期]
data={\
1:[9,[4,5] ,1],\
2:[4,[6,7] ,1],\
3:[6,[9]   ,1],\
4:[16,[6,7],1],\
5:[20,[8]  ,1],\
6:[20,[10] ,1],\
7:[20,[9]  ,1],\
8:[10,[10] ,1],\
9:[4,[10]  ,1],\
10:[15,[0] ,1],\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob12.addActivity("Act[{0}]".format(i))
#=====先行制約
for i in data:
  for j in data[i][1]:
    if j>0: prob12.addTemporal(act[i],act[j])  
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0:02d}]".format(i),data[i][0])
  mode[i].addResource(res1,requirement=data[i][2])
  mode[i].addParallel(1,data[i][0],10)     
  act[i].addModes(mode[i])
#=====最適化
prob12.Params.Makespan=True
prob12.Params.TimeLimit=1
#prob11.Params.OutputFlag=True
prob12.optimize()
prob12.write("prob12.txt")
prob12.writeExcel("prob12.csv")
#=====prob12.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 910111213
#---------------------------------------------------------
#  Act[1]   Mode[01]     9     *6*3                      
#  Act[2]   Mode[02]     4     *4                        
#  Act[3]   Mode[03]     6       *6                      
#  Act[4]   Mode[04]     16        *10*6                  
#  Act[5]   Mode[05]     20          *4*10*6              
#  Act[6]   Mode[06]     20                  *4*10*6      
#  Act[7]   Mode[07]     20              *4*10*6          
#  Act[8]   Mode[08]     10                      *4*6    
#  Act[9]   Mode[09]     4                         *4    
#  Act[10]   Mode[10]     15                          *10*5 
#---------------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------------
#           Resource           10 910101010101010101010 5
#                              10101010101010101010101010
#---------------------------------------------------------

このプログラムを実行して、次の結果を得ます。

●上の例は配員計画を基に作業期間を定めており、日程計画と配員計画の同時計画を達成しています。次の例は、作業期間が未知の仮想作業を定義し、作業設備の確保も併せて達成しようとしています。

##prob13.py
from optseq import *
#=====リソース
prob13=Model()
res1=prob13.addResource("Resource1",capacity={(0,"inf"):10}) #10
res2=prob13.addResource("Resource2",capacity={(0,"inf"):10})  #10
#-----データセット
#i:[期間、後続、納期]
data={\
1:[9,[4,5] ,1],\
2:[4,[6,7] ,1],\
3:[6,[9]   ,1],\
4:[16,[6,7],1],\
5:[20,[8]  ,1],\
6:[20,[10] ,1],\
7:[20,[9]  ,1],\
8:[10,[10] ,1],\
9:[4,[10]  ,1],\
10:[15,[0] ,1],\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob13.addActivity("Act[{0}]".format(i))
#=====先行制約
for i in data:
  for j in data[i][1]:
    if j>0: prob13.addTemporal(act[i],act[j])  
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0:02d}]".format(i),data[i][0])
  mode[i].addResource(res1,requirement=data[i][2])
  mode[i].addParallel(1,data[i][0],10)     
  act[i].addModes(mode[i])
#====仮想作業    
dact={}
for i in data:
  dact[i]=prob13.addActivity("DAct[{0:02d}]".format(i))
  prob13.addTemporal(act[i],dact[i],tempType="SS") 
  prob13.addTemporal(act[i],dact[i],tempType="CC")
dmode={}
for i in data:
  dmode[i]=Mode("DMode[{0:02d}]".format(i))
  dmode[i].addBreak(0,0)
  dmode[i].addResource(res2,{(0,"inf"):1},"break")
  dact[i].addModes(dmode[i])
#=====最適化
prob13.Params.Makespan=True
prob13.Params.TimeLimit=1
#prob13.Params.OutputFlag=True
prob13.optimize()
prob13.write("prob13.txt")
prob13.writeExcel("prob13.csv")
#=====prob13.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 910111213
#---------------------------------------------------------
#  Act[1]   Mode[01]     9     *4*5                      
#  Act[2]   Mode[02]     4       *4                      
#  Act[3]   Mode[03]     6     *6                        
#  Act[4]   Mode[04]     16            *10*6              
#  Act[5]   Mode[05]     20        *10*10                  
#  Act[6]   Mode[06]     20                    *4*10*6    
#  Act[7]   Mode[07]     20                *4*10*6        
#  Act[8]   Mode[08]     10              *4*6            
#  Act[9]   Mode[09]     4                         *4    
# Act[10]   Mode[10]     15                          *10*5
# DAct[01] DMode[01]     0     ....                      
# DAct[02] DMode[02]     0       ..                      
# DAct[03] DMode[03]     0     ..                        
# DAct[04] DMode[04]     0             ....              
# DAct[05] DMode[05]     0         ....                  
# DAct[06] DMode[06]     0                     ......    
# DAct[07] DMode[07]     0                 ......        
# DAct[08] DMode[08]     0               ....            
# DAct[09] DMode[09]     0                         ..    
# DAct[10] DMode[10]     0                           ....
#---------------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------------
#          Resource1           10 910101010101010101010 5
#                              10101010101010101010101010
#---------------------------------------------------------
#          Resource2            2 2 1 1 1 2 2 1 2 1 2 1 1
#                               2 2 2 2 2 2 2 2 2 2 2 2 2

このプログラムを実行して、次の結果を得ます。

RCPSP

RCPSP

RCPSP (Resource Constraint Project Scheduling Problem, 資源制約プロジェクトスケジューリング問題)とは、先行制約(Precedence Constraints)と資源制約(Resource Constraints)のもとで、決定変数を各作業の開始日とし、目的関数を最小化する組合せ最適化問題で、次のように定式化(またはモデル化)表されます。

    \[\displaystyle{\boxed{\begin{array}{cl} {\bf min}&\ Objective\ Function\\ {start\ times}&\\ {\bf subject\ to}&1^\circ\ Precedence\ Constraints\\ &2^\circ\ Resource\ Constraints \end{array}}}\]

これを拡張した問題に対する商用ソルバーとして、OptSeqがあります。そこで使用可能なクラス、インスタンス、メソッドを以下に示します。

●クラス「モデル」
model=Model()

●クラス「作業」
act=model.addActivity(name=””,duedate=”inf”,backward=False,weight=1,autoselect=False,quadratic=False)
act.addModes(mode1,mode2,…)

●クラス「モード」
mode=Mode(name,duration=0)
mode.addResource(resource,requirement={(start,finish):req},rtype=None)
mode.addBreak(start=0,finish=0,maxtime=’inf’)
mode.addBreak(start=0,{(start,finish):req},’break’)
mode.addParallel(start=1,finish=1,maxparallel=’inf’)
mode.addState(state,fromValue=0,toValue=0)

●クラス「資源」
res=model.addResource(name,capacity,rhs=0,direction=’<=',weight='inf')
res.addCapacity(start,finish,amount)
res.addTerms(coeffs,vars,values)

●クラス「時間制約」
model.addTemporal(pred,succ,tempType=’CS’,delay=0,pred_mode=None,succ_mode=None)

●クラス「状態」
model.addState(name)

基本的例題

例1(プッシュ型とプル型) ある作業の納期と作業期間が与えられるとき、納期までどれくらい余裕があるのか(prob1a)、または納期にジャストインするためにはいつから作業に取りかかればよいか(prob1b)という問題を考えます。そのために作業をどう前詰めするか、納期に後詰めするかが問われ、それぞれプッシュ型とプル型に対応します。

#prob1a.py
from optseq import *
#=====リソース
prob1=Model()
res=prob1.addResource("worker",1)
#=====データセット
#i:[期間、後続、納期]
data={
1:[5,2,'inf'],\
2:[1,0,10]\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob1.addActivity("Act[{0}]".format(i))
#=====先行制約
for i in data:
  if data[i][1]!=0:
    prob1.addTemporal(act[i],act[data[i][1]])    
#-----納期日
for i in data:
  if data[i][1]==0:
    prob1.addTemporal("source",act[i],"SS",delay= data[i][2]-1)
    prob1.addTemporal(act[i],"source","SS",delay=-data[i][2]+1) 
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=1)
  act[i].addModes(mode[i])
#=====最適化
prob1.Params.Makespan=True
prob1.Params.TimeLimit=1
#prob1.Params.OutputFlag=True
prob1.optimize()
prob1.write("prob1a.txt")
prob1.writeExcel("prob1a.csv")
#=====prob1a.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 910
#---------------------------------------------------
#  Act[1]   Mode[1]      5     ==========          
#  Act[2]   Mode[2]      1                       ==
#---------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------
#            worker             1 1 1 1 1 0 0 0 0 1
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------

OptSeqは指定がなければ前詰めが実行されます。納入作業は10日目に行うと考えて、開始のタイミングを9日目終了に合わせています。

#prob1b.py
from optseq import *
#=====リソース
prob1=Model()
res=prob1.addResource("worker",1)
#=====データセット
#i:[期間、後続、納期]
data={
1:[5,2,10],\
2:[1,0,10]\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob1.addActivity("Act[{0}]".format(i),duedate=data[i][2],backward=True)  
#=====先行制約
for i in data:
  if data[i][1]!=0:
    prob1.addTemporal(act[i],act[data[i][1]])    
#-----納期日
for i in data:
  if data[i][1]==0:
    prob1.addTemporal("source",act[i],"SS",delay= data[i][2]-1)
    prob1.addTemporal(act[i],"source","SS",delay=-data[i][2]+1)    
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=1)
  act[i].addModes(mode[i])
#=====最適化
prob1.Params.Makespan=True
prob1.Params.TimeLimit=1
#prob1.Params.OutputFlag=True
prob1.optimize()
prob1.write("prob1b.txt")
prob1.writeExcel("prob1b.csv")
#=====prob1a.txt
#  activity    mode   duration  1 2 3 4 5 6 7 8 910
#---------------------------------------------------
#  Act[1]   Mode[1]      5             ==========  
#  Act[2]   Mode[2]      1                       ==
#---------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------
#            worker             0 0 0 0 1 1 1 1 1 1
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------

作業の定義において、納期の指定(duedate)と後詰めの指定(backward=True)を行っています。そのためにデータセットで主作業(Act[1])にも納期を指定する必要があることに注意してください(後続作業として納品作業があるので本来は不要であるように思われますが)。

例2(その場待機と置き場待機) 種々の制約があると待機期間が生じます。リソース(作業員、作業機械、作業場所)によっては待機中も確保が必要になります。たとえば、リソースが作業場所の場合、その作業場所で待機をするその場待機と、どこか待機場所を確保する置き場待機が考えられます。

#prob2a.py
from optseq import *
#=====リソース
prob2=Model()
res=prob2.addResource("place",1)
#=====データセット
#i:[期間、後続、納期]
data={
1:[5,2,'inf'],\
2:[1,0,10]\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob2.addActivity("Act[{0}]".format(i))
#=====先行制約
for i in data:
  if data[i][1]!=0:
    prob2.addTemporal(act[i],act[data[i][1]],tempType="CS",delay=0)
    prob2.addTemporal(act[data[i][1]],act[i],tempType="SC",delay=0)  
#-----納期日
for i in data:
  if data[i][1]==0:
    prob2.addTemporal("source",act[i],"SS",delay= data[i][2]-1)
    prob2.addTemporal(act[i],"source","SS",delay=-data[i][2]+1)  
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res,requirement=1)
  mode[i].addBreak(0,'inf') 
 #mode[i].addBreak(0,'inf',maxtime=2)
 #mode[i].addBreak(0,0)  
  mode[i].addResource(res,{(0,'inf'):1},'break')
  act[i].addModes(mode[i])
#=====最適化  
prob2.Params.Makespan=True
prob2.Params.TimeLimit=1
#prob2.Params.OutputFlag=True
prob2.optimize()
prob2.write("prob2a.txt")
prob2.writeExcel("prob2a.csv")
#=====prob2a.txt: mode[i].addBreak(0,'inf') 
#  activity    mode   duration  1 2 3 4 5 6 7 8 910
#---------------------------------------------------
#  Act[1]   Mode[1]      5     ==========........  
#  Act[2]   Mode[2]      1                       ==
#---------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------
#            place              1 1 1 1 1 1 1 1 1 1
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------
#=====prob2a.txt: mode[i].addBreak(0,'inf',maxtime=2) 
#  activity    mode   duration  1 2 3 4 5 6 7 8 910
#---------------------------------------------------
#  Act[1]   Mode[1]      5     ========....==....  
#  Act[2]   Mode[2]      1                       ==
#---------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------
#            place              1 1 1 1 1 1 1 1 1 1
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------
#=====prob2a.txt: mode[i].addBreak(0,0) 
#  activity    mode   duration  1 2 3 4 5 6 7 8 910
#---------------------------------------------------
#  Act[1]   Mode[1]      5     ........==========  
#  Act[2]   Mode[2]      1                       ==
#---------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------
#            place              1 1 1 1 1 1 1 1 1 1
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------

ここでは、待機期間の取り方を3種類考えて、その場待機を実現しています。

#prob2b.py
from optseq import *
#=====リソース
prob2=Model()
res0=prob2.addResource("place",1)
res1=prob2.addResource("stock",1)
#=====データセット
#i:[期間、後続、納期]
data={
1:[5,-2,"inf"],\
2:[1,0,10]\
}
#=====アクティビティ
act={}
for i in data:
  act[i]=prob2.addActivity("Act[{0}]".format(i))
#=====先行制約
for i in data:
  if data[i][1]!=0:
    prob2.addTemporal(act[i],act[data[i][1]],tempType="CS",delay=0)  
#-----納期日
for i in data:
  if data[i][1]==0:
    prob2.addTemporal("source",act[i],"SS",delay= data[i][2]-1)
    prob2.addTemporal(act[i],"source","SS",delay=-data[i][2]+1)  
#====資源制約  
mode={}  
for i in data:
  mode[i]=Mode("Mode[{0}]".format(i),duration=data[i][0])
  mode[i].addResource(res0,1)
  act[i].addModes(mode[i])
#=====仮想作業
d_act={}
for i in data:
  if data[i][1]<0:
    d_act[i]=prob2.addActivity("Wait[{0}]".format(i))
    prob2.addTemporal(act[i],d_act[i],tempType="CS")
    prob2.addTemporal(d_act[i],act[i],tempType="SC")
    prob2.addTemporal(d_act[i],act[abs(data[i][1])],tempType="CS")
    prob2.addTemporal(act[abs(data[i][1])],d_act[i],tempType="SC")
d_mode={}
for i in data:
  if data[i][1]<0:
    d_mode[i]=Mode("d_Mode[{0}]".format(i))
    d_mode[i].addBreak(0,0)
    d_mode[i].addResource(res1,{(0,"inf"):1},"break")
    d_act[i].addModes(d_mode[i])
#=====最適化      
prob2.Params.Makespan=True
prob2.Params.TimeLimit=1
#prob2.Params.OutputFlag=True
prob2.optimize()
prob2.write("prob2b.txt")
prob2.writeExcel("prob2b.csv")
#=====prob2b.txt 
#  activity    mode   duration  1 2 3 4 5 6 7 8 910
#---------------------------------------------------
#  Act[1]   Mode[1]      5     ==========          
#  Act[2]   Mode[2]      1                       ==
# Wait[1]  d_Mode[1]     0               ........  
#---------------------------------------------------
#   resource usage/capacity     
#---------------------------------------------------
#            place              1 1 1 1 1 0 0 0 0 1
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------
#            stock              0 0 0 0 0 1 1 1 1 0
#                               1 1 1 1 1 1 1 1 1 1
#---------------------------------------------------

ここでは、待機場所を別に確保して、待機中はそこを使うようにしています。この置き場待機を実施するかどうかは、データセットにおける後続作業の番号が負であるかどうかで判断しています。

エクスポート

エクスポート

●「工程’s」のファイルにエクスポートを行うと、次のような13種類のTSVファイルが得られます。

ここで、次の3つのファイルに注目します。
・koutei.tsv ⇒作業の定義
・requirement.tsv ⇒要求リソース
・constraint.tsv ⇒先行制約
これらから、データセットを作成するのに必要な項目(以下の赤太字)を抽出します。

koutei.tsvのレコードを構成するフィールド(129個)

第1階層グループID 第1階層グループ名称 第1階層グループ備考1 第1階層グループ備考2 第1階層グループ備考3 第1階層グループ備考4 第1階層グループ備考5 第1階層グループ備考6 第1階層グループ備考7 第1階層グループ備考8 第1階層グループ備考9 第1階層グループ備考10 第2階層グループID 第2階層グループ名称 第3階層グループID 第3階層グループ名称 第4階層グループID 第4階層グループ名称 第5階層グループID 第5階層グループ名称 第6階層グループID 第6階層グループ名称 第7階層グループID 第7階層グループ名称 第8階層グループID 第8階層グループ名称 第9階層グループID 第9階層グループ名称 第10階層グループID 第10階層グループ名称 行番号 工程ID 工程名称 初期工程開始日 初期工程終了日 工程開始日 工程終了日 工程期間 カレンダID 進捗度 数量 備考1 備考2 備考3 備考4 備考5 備考6 備考7 備考8 備考9 備考10 バーを移動しない 後続に影響しない範囲で遅く開始する 編集不可 左シンボル形状 中シンボル形状 右シンボル形状 左シンボル色 中シンボル色 右シンボル色 テキストの表示位置 URL表示名1 URL1 URL表示名2 URL2 URL表示名3 URL3 URL表示名4 URL4 URL表示名5 URL5 第1階層グループ非表示 第1階層グループバー非表示 第1階層グループ折りたたみ(バーチャート) 第1階層グループ折りたたみ(ツリービュー) 第1階層グループ名称の書き方 第2階層グループ非表示 第2階層グループバー非表示 第2階層グループ折りたたみ(バーチャート) 第2階層グループ折りたたみ(ツリービュー) 第2階層グループ名称の書き方 第3階層グループ非表示 第3階層グループバー非表示 第3階層グループ折りたたみ(バーチャート) 第3階層グループ折りたたみ(ツリービュー) 第3階層グループ名称の書き方 第4階層グループ非表示 第4階層グループバー非表示 第4階層グループ折りたたみ(バーチャート) 第4階層グループ折りたたみ(ツリービュー) 第4階層グループ名称の書き方 第5階層グループ非表示 第5階層グループバー非表示 第5階層グループ折りたたみ(バーチャート) 第5階層グループ折りたたみ(ツリービュー) 第5階層グループ名称の書き方 第6階層グループ非表示 第6階層グループバー非表示 第6階層グループ折りたたみ(バーチャート) 第6階層グループ折りたたみ(ツリービュー) 第6階層グループ名称の書き方 第7階層グループ非表示 第7階層グループバー非表示 第7階層グループ折りたたみ(バーチャート) 第7階層グループ折りたたみ(ツリービュー) 第7階層グループ名称の書き方 第8階層グループ非表示 第8階層グループバー非表示 第8階層グループ折りたたみ(バーチャート) 第8階層グループ折りたたみ(ツリービュー) 第8階層グループ名称の書き方 第9階層グループ非表示 第9階層グループバー非表示 第9階層グループ折りたたみ(バーチャート) 第9階層グループ折りたたみ(ツリービュー) 第9階層グループ名称の書き方 第10階層グループ非表示 第10階層グループバー非表示 第10階層グループ折りたたみ(バーチャート) 第10階層グループ折りたたみ(ツリービュー) 第10階層グループ名称の書き方 初期計画左シンボル形状 初期計画中シンボル形状 初期計画右シンボル形状 初期計画左シンボル色 初期計画中シンボル色 初期計画右シンボル色 拡張設定1 EOR

PERTの例題について赤太字の項目を抜き出すと次のようになります。

requirement.tsvのレコードを構成するフィールド(5個)

工程ID 資源ID 必要量 タイプ EOR

PERTの例題について赤太字の項目を抜き出すと次のようになります。

constraint.tsvのレコードを構成するフィールド(26個)

先行工程ID 後続工程ID タイプ 作業間隔 カレンダID 備考 非表示 描画順序1 描画オフセット右1 描画オフセット下1 描画順序2 描画オフセット右2 描画オフセット下2 描画順序3 描画オフセット右3 描画オフセット下3 描画順序4 描画オフセット右4 描画オフセット下4 描画順序5 描画オフセット右5 描画オフセット下5 描画順序6 描画オフセット右6 描画オフセット下6 EOR

PERTの例題について赤太字の項目を抜き出すと次のようになります。

データセットの作成

「工程’s」による平準化

「工程’s」による平準化…Homework

●作業情報が次表のように与えられる例を考えます。

# 作業名 作業時間 先行関係 作業員
1 A1 3 4 5 3
2 A2 4 6 7 1
3 A3 6 8 1
4 A4 4 6 7 4
5 A5 5 9 4
6 A6 4 10 5
7 A7 5 8 4
8 A8 2 10 5
9 A9 2 10 2
10 A10 5 0 3

山積みを行った結果を次に示します。毎日10名まで配員可能とすると、これをオーバーしていることが分かります。

ここで、赤い作業の列がCPを表しています。通常は、CP以外の作業を前後させて平準化(山均し)を行います。

●「工程’s」の「オシカ」を用いて平準化を行った結果を次に示します。

●以上は、各作業を作業期間中は毎日一定の配員で行う場合です。しかし日ごとに配員数を変えてよい場合を検討するために、次の山積みを行います。

●これを平準化して、次の結果を得ます。

本来は、作業81が始まるまでに作業A74を終わらせたいところですが、「工程’s」でそれが可能かどうかは現在検討中です。

●参考までに、別の方法で日ごとの配員数を変えながら(したがって作業期間を変えながら)、平準化を行った結果を次に示します。

ここで、10名の作業員はどの作業も担当できることを仮定しています。

基本操作

アクティティビティの定義と先行関係の付与…Homework

●操作1:編集期間の設定

●操作2:通し日の設定

●操作3:グループ名の定義(行の追加、行番号の定義)

●操作4:アクティビティの定義(名称の設定)

●操作5:シンボルの変更

●操作5:後続作業の定義、先行関係の付与

先行関係には、CS関係(完了⇒開始)、SC関係(開始⇒完了)、SS関係(開始⇒開始)、CC関係(完了⇒完了)の4種類がある。

●操作6:グループバーを非表示
●操作7:操作の取り消し

リソースの付与…Homework

●操作8:資源マスターの定義

●操作9:必要資源の付与

●操作10:バーレイアウトの変更

●操作11:キャパ(提供可能資源量)の付与

補遺:出力FB型追従SMI制御


補遺:出力FB型追従SMI制御…Homework


[1] 定値外乱を受ける制御対象

\displaystyle{(1)\quad \begin{array}{l} \dot{x}(t)=Ax(t)+Bu(t)+w\\ y(t)=Cx(t)\\ (x(t)\in{\rm\bf R}^n, u(t)\in{\rm\bf R}^m, y(t)\in{\rm\bf R}^p, w\in{\rm\bf R}^n, m=p) \end{array} }

の出力を、次のコマンド(定値目標)

\displaystyle{(2)\quad r\in{\rm\bf R}^m %\begin{array}{l} %\dot{r}(t)=\Gamma(r(t)-r_c)\\ %(r(t),r_c\in{\rm\bf R}^m) %\end{array} %\Gammaは安定行列 }

に追従させることを考えます。そのために、積分動作

\displaystyle{(3)\quad \begin{array}{l} \dot{x}_I(t)=y(t)-r\\ %\dot{x}_I(t)=r(t)-y(t)\\ (x_I(t)\in{\rm\bf R}^m) \end{array} }

を考え、次の拡大系を構成します。

\displaystyle{(4)\quad %\underbrace{ \left[\begin{array}{c} \dot{x}(t) \\ \dot{x}_I(t) \end{array}\right] %}_{\dot{x}_E(t)} = %\underbrace{ \left[\begin{array}{cc} A & 0 \\ C & 0 \end{array}\right] %}_{A_E} %\underbrace{ \left[\begin{array}{c} x(t) \\ x_I(t) \end{array}\right] %}_{x_E(t)} + %\underbrace{ \left[\begin{array}{c} B \\ 0 \end{array}\right] %}_{B_E} u(t) + %\underbrace{ \left[\begin{array}{c} w \\ -r \end{array}\right] %}_{w_E} }

定常状態では

\displaystyle{(5)\quad \left[\begin{array}{c} 0 \\ 0 \end{array}\right] = \left[\begin{array}{cc} A & 0 \\ C & 0 \end{array}\right] \left[\begin{array}{c} x_\infty \\ x_{I\infty} \end{array}\right] + \left[\begin{array}{c} B \\ 0 \end{array}\right] u_\infty + \left[\begin{array}{c} w \\ -r \end{array}\right] }

を得ます(x_{\infty},x_{I\infty},u_{\infty}は定数ベクトル)。

●さて、関係式

\displaystyle{(10)\quad \underbrace{ \left[\begin{array}{c} {\dot x}(t) \\ y(t)-r \end{array}\right] }_{x_{E2}(t)} = \underbrace{ \left[\begin{array}{cc} A & B \\ C & 0 \end{array}\right] }_{S_E} \underbrace{ \left[\begin{array}{c} x(t)-x_\infty \\ u(t)-u_\infty \end{array}\right] }_{x_{E3}(t)} }

を用いて、偏差系

\displaystyle{(15)\quad \begin{array}{l} \boxed{ \underbrace{ \left[\begin{array}{c} \dot x_1(t)\\ \dot x_2(t) \end{array}\right] }_{\dot{x}_{E3}(t)} = \underbrace{ \left[\begin{array}{cc} A & B \\ 0 & 0 \end{array}\right] }_{A_{E3}} \underbrace{ \left[\begin{array}{c} x_1(t)\\ x_2(t) \end{array}\right] }_{x_{E3}(t)} + \underbrace{ \left[\begin{array}{c} 0\\ I_m \end{array}\right] }_{B_{E3}} {\dot u}(t)}\\ (x_1(t)=x(t)-x_\infty, x_2(t)=u(t)-u_\infty) \end{array} }

を得ます。これはSM標準形となっていることに注意して、SM制御系を設計します。

●スイッチング関数として、次式を考えます。

\displaystyle{(16)\quad s(t)= \underbrace{ \left[\begin{array}{cc} S_1C & S_2 \\ \end{array}\right] }_{S} \underbrace{ \left[\begin{array}{c} x_1(t)\\ x_2(t) \end{array}\right] }_{x_{E3}(t)} = \underbrace{S_2 \left[\begin{array}{cc} M & I \\ \end{array}\right] }_{S} \underbrace{ \left[\begin{array}{c} x_1(t)\\ x_2(t) \end{array}\right] }_{x_{E3}(t)} \ (M=S_2^{-1}S_1C) }

(15)に対して、座標変換

\displaystyle{(17)\quad \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right] }_{x'_{E3}(t)} = \underbrace{ \left[\begin{array}{cc} I & 0 \\ S_1C & S_2 \\ \end{array}\right] }_{T_s} \underbrace{ \left[\begin{array}{c} x_1(t)\\ x_2(t) \end{array}\right] }_{x_{E3}(t)}\\ \Leftrightarrow \underbrace{ \left[\begin{array}{c} x_1(t)\\ x_2(t) \end{array}\right] }_{x_{E3}(t)} = \underbrace{ \left[\begin{array}{cc} I & 0 \\ -S_2^{-1}S_1C & S_2^{-1} \\ \end{array}\right] }_{T_s^{-1}} \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right] }_{x'_{E3}(t)} }

を行って、次式を得ます。

\displaystyle{(18)\quad \underbrace{ \left[\begin{array}{c} \dot x_1(t)\\ \dot s(t) \end{array}\right] }_{\dot{x}'_{E3}(t)} = \underbrace{ \left[\begin{array}{cc} \bar{A}_{11} & \bar{A}_{12} \\ \bar{A}_{21} & \bar{A}_{22} \\ \end{array}\right] }_{T_sA_{E3}T_s^{-1}} \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right] }_{x'_{E3}(t)} + \underbrace{ \left[\begin{array}{cc} 0\\ S_2 \end{array}\right] }_{T_sB_{E3}} {\dot u}(t) }

\displaystyle{(19)\quad \begin{array}{l} \bar{A}_{11}=A-BM\quad(M=S_2^{-1}S_1C)\\ \bar{A}_{12}=BS_2^{-1}\\ \bar{A}_{21}=S_1(A-BM)\\ \bar{A}_{22}=S_1BS_2^{-1} \end{array} }

以下では、\bar{A}_{11}が安定行列となるようにスイッチング関数が選ばれていると仮定します。

●このとき、スライディングモード制御則(SM制御則、SMC則)

\displaystyle{(20)\quad  {\dot u}(t)=\underbrace{{\dot u}_\ell(t)}_{linear\ control}+\underbrace{{\dot u}_n(t)}_{switching\ component}} }

を、2次安定性

\displaystyle{(21)\quad  \begin{array}{lll} V(\bar{x})= \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right]^T }_{\bar{x}^T(t)} \underbrace{ \left[\begin{array}{cc} P_1 & 0\\ 0 & P_2 \end{array}\right] }_{P} \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right] }_{\bar{x}(t)}\\ \Rightarrow \dot{V}(\bar{x})\le - \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right]^T }_{\bar{x}^T(t)} \underbrace{ \left[\begin{array}{cc} Q_1 & 0\\ 0 & I \end{array}\right] }_{Q} \underbrace{ \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right] }_{\bar{x}(t)} \end{array}} }

すなわち

\displaystyle{(21')\quad  \begin{array}{lll} V(\bar{x})= \underbrace{x_1^T(t)P_1x_1(t)}_{V(x_1)}+\underbrace{s^T(t)P_2s(t)}_{V(s)}\\ \Rightarrow \dot{V}(\bar{x})\le  -x_1^T(t)Q_1x_1(t)-s^T(t)s(t) \end{array}} }

が成り立つように決定します(P_1>0, P_2>0, Q_1>0)。

可到達性の検討

等価制御は

\displaystyle{(22)\quad \begin{array}{l} s(t)=0\Rightarrow\dot{s}(t)=0 \Rightarrow 0=\bar{A}_{21}x_1(t)+\bar{A}_{22}s(t)+S_2{\dot u}(t)\\ \Rightarrow {\dot u}_{eq}(t)=-\underbrace{S_2^{-1}}_{(SB_{E3})^{-1}} \underbrace{\left[\begin{array}{cc} \bar{A}_{21} & \bar{A}_{22} \\ \end{array}\right]x'_{E3}(t)}_{SA_{E3}x_{E3}(t)}} \end{array} }

のように得られます。(20)の第1項{\dot u}_\ellは、この等価制御をベースして

\displaystyle{(23)\quad  \begin{array}{l} {\dot u}_\ell(t)=-\underbrace{S_2^{-1}}_{(SB_{E3})^{-1}} \underbrace{(\left[\begin{array}{cc} \bar{A}_{21} & \bar{A}_{22} \\ \end{array}\right]x'_{E3}(t)-\Phi s(t))}_{(SA_{E3}-\Phi S)x_{E3}(t)}} \end{array}} }

のように構成します(\Phiは安定行列)。このとき閉ループ系は次式で与えられます。

\displaystyle{(24)\quad \begin{array}{l} \left[\begin{array}{c} \dot{x}_1(t)\\ \dot{s}(t) \end{array}\right] = \left[\begin{array}{cc} \bar{A}_{11} & \bar{A}_{12} \\ 0 & \Phi \\ \end{array}\right] \left[\begin{array}{c} x_1(t)\\ s(t) \end{array}\right] + \left[\begin{array}{c} 0\\ S_2{\dot u}_n(t) \end{array}\right] \end{array }

すなわち

\displaystyle{(25)\quad \begin{array}{l} \dot{x}_1(t)=\bar{A}_{11}x_1(t)+\bar{A}_{12}s(t)\\ \dot{s}(t)=\Phi s(t)+S_2{\dot u}_n(t) \end{array} }

ここで、\Phiは安定行列なので

\displaystyle{(26)\quad \begin{array}{l} P_2\Phi+\Phi^TP_2=-I \end{array} }

を満たすP_2>0を選ぶことができます。これを用いて

\displaystyle{(27)\quad  {\dot u}_n(t)=-\underbrace{S_2^{-1}}_{(SB_{E3})^{-1}}\rho\frac{P_2s(t)}{||P_2s(t)||}} }

と選びます(\rho>0は定数)。このとき次式が成り立ちます。

\displaystyle{(28)\quad \begin{array}{l} \dot{V}(s)=2s^T(t)P_2\dot{s}(t)\\ =2s^T(t)P_2(\Phi s(t)-\rho\frac{P_2s(t)}{||P_2s(t)||})\\ =s^T(t)(P_2\Phi+\Phi^TP_2)s(t)+2s^T(t)P_2(-\rho\frac{P_2s(t)}{||P_2s(t)||})\\ = -||s(t)||^2-2\rho||P_2s(t)||\\ \le -||s(t)||^2 \end{array} }

スライディングモードの検討

\bar{A}_{11}は安定行列なので

\displaystyle{(29)\quad \begin{array}{l} P_1\bar{A}_{11}+\bar{A}_{11}^TP_1=-Q_1<0 \end{array} }

を満たすP_1>0を選ぶことができます。

\displaystyle{(30)\quad \begin{array}{l} \dot{V}(x_1)=2x_1^T(t)P_1\dot{x}_1(t)\\ =2x_1^T(t)P_1( \bar{A}_{11}x_1(t)+\bar{A}_{12}\underbrace{s(t)}_{0})\\ =x_1^T(t)(P_1\bar{A}_{11}+\bar{A}_{11}^TP_1)x_1(t)\\ =-x_1^T(t)Q_1x_1(t) \end{array} }

積分動作をもつSMC

上で求めた偏差系E3に対するSMCは次式で与えられました。

\displaystyle{(31)\quad  {\dot u}(t)={\dot u}_\ell(t)+{\dot u}_n(t) }

\displaystyle{(32)\quad  {\dot u}_\ell(t)=-(SB_{E3})^{-1}(SA_{E3}-\Phi S)x_{E3}(t) }

\displaystyle{(33)\quad  {\dot u}_n(t)=-(SB_{E3})^{-1}\rho\frac{P_2s(t)}{||P_2s(t)||}}=-(SB_{E3})^{-1}\rho\,{\rm sgn}(P_2Sx_{E3}(t)) }

これらを積分して、制御対象(1)に対する積分動作をもつSMCを導出します。

\displaystyle{(34)\quad  {u}(t)={u}_\ell(t)+{u}_n(t) }

まず(32)は(10)を用いて次式のように書けます。

\displaystyle{(35)\quad  {\dot u}_\ell(t)=- \underbrace{ (SB_{E3})^{-1}(SA_{E3}-\Phi S)S_E^{-1} }_{\left[\begin{array}{cc} F & F_I \end{array}\right]} \underbrace{ \left[\begin{array}{c} {\dot x}(t) \\ y(t)-r \end{array}\right] }_{x_{E2}(t)} }

これを積分して

\displaystyle{(36)\quad  u_\ell(t)=-Fx(t)+F_I\int_0^t(r-y(\tau))d\tau }

次に(33)は(10)を用いて次式のように書けます。

\displaystyle{(37)\quad  {\dot u}_n(t) =-S_2^{-1}\rho\, {\rm sgn}( \underbrace{ P_2SS_E^{-1} }_{\left[\begin{array}{cc} G & G_I \end{array}\right]} \underbrace{ \left[\begin{array}{c} {\dot x}(t) \\ y(t)-r \end{array}\right] }_{x_{E2}(t)}) }

これを積分すれば

\displaystyle{(38)\quad  u_n(t)=S_2^{-1}\rho\,\int_0^t\underbrace{{\rm sgn}(-G{\dot x}(\tau)+G_I(r-y(\tau)))}_{0,\pm 1}d\tau }