スケジューリング最適化システム OptSeq II Pythonモジュールの使い方 補助資料:OptSeq II によるスケジューリング入門 トライアルバージョン http://www.logopt.com/OptSeq/OptSeq.htm スケジューリングモデルとは • 作業(activity;活動):遂行すべき仕事のこと.別名, オペレーション(operation),ジョブ,仕事(job),タスク (task).作業時間や納期の情報が付加される. • 先行(時間)制約(precedence (time) constraint):ある 作業(ジョブ,活動)がある作業(ジョブ,活動)の前 に処理されなければならないことを表す制約. • 資源(resource):作業を行うときに必要とされるもの で,その量に限りがあるもの.機械(machine)はジョ ブによって占有される資源.その他,人員,材料,お 金なども全部資源. optseq2.pyモジュールとは • スケジューリングソルバーOptSeqを,超高 級言語Pythonから直接呼び出して,求解 するためのモジュール • すべてPythonで書かれたクラスで構成 • ソースコード(すべてPythonで記述)も公開 されているので,ユーザが書き換え可能 Pythonモジュールのクラス • • • • • • 作業クラス ー Activity モードクラス ー Mode モデルクラス ー Model パラメータクラス ー Parameters 資源クラス ー Resource 先行制約クラス ー Temporal 作業クラス ー Activity 作業の定義を行う • activityの引数と引数のデフォルト値 activity. (name=‘’, dudate=‘inf’, weight=1) – name : 作業の名前,文字列で入力 – dudate : 作業の納期,非負の整数または‘inf’で入力 – weight : 作業が納期遅れした時のペナルティ,positive 整数 • activityのaddModesメソッド activity.addModes(modes) – modes: モードクラスで作成 – 作業にモード(一つまたは複数のモード)を追加する時に使用 – 例: activity.addModes(mode1,mode2) モードクラス ー Mode(1) モードの定義を行う • modeの引数と引数のデフォルト値 mode(name=‘ ’, duration=0) – name: モードの名前,文字列で入力 – duration: このモードを選択した場合の作業時間 • modeのメソッド (次のページで詳しく) – addBreak(start=0, finish=0, maxtime=‘inf’) 休憩に関する情報をモードに追加 – addParallel(start=1, finish=1, maxparallel=‘inf’) 並列処理に関する情報をモードに追加 – addResource(resource, requirement={}, rtype=None) 資源に関する情報をモードに追加 モードクラス ー Mode(2) モードメソッド(1) • addBreak メソッド addBreak(start=0, finish=0, maxtime=‘inf’) – start,finish: 休憩開始時間,休憩終了時間 – maxtime: 最大休憩可能時間 • addParallel メソッド addParallel(start=1, finish=1, maxparallel=‘inf’) – start,finish: 並列開始小作業番号,並列終了小作業番号 – maxparallel: 最大並列数 作業の途中中断 • Pythonインターフェイスによる記述 モード名.addBreak(中断開始時刻,中断終了時刻,最大中断時間) 作業Aが,0から3の区間でそれぞれ最大1中断可能: モード名.addBreak(0,3,1) 開始から2時刻で中断した例 作業の並列処理 • Pythonインターフェイスによる記述 モード名.addParallel(開始小作業番号,終了小作業番号, 最大並列数) 作業1(作業時間は3秒)を最大3単位並列可能: モード名.addParallel(1,1,3) 作業1 小作業1,小作業1 並列処理の結果 • 1番目の小作業を3単位並列可能: 1 2 3 モードクラス ー Mode(3) モードメソッド(2) • addResource メソッド addResource(resource, requirement={}, rtype=None) – resource: 資源オブジェクト – requirement: 資源必要量 資源を必要とする開始時刻と終了時刻を辞書で表す 例: mode.addResource(worker,{(0,10):1}) 時刻0から時刻10までworkerという資源を1単位必要 – rtype: リソースのタイプ ----- break と max がある 例: mode.addResource(machine,{(0,"inf"):1},"break") break は休憩中も資源を1単位使用 例: mode.addResource(machine,{(0,"inf"):1},"max") max は並列処理を行う時も資源は1単位使用 モデルクラス ー Model モデルを作成 • モデルクラスのメソッド – addActivity(name=‘’, duedate=‘inf’, weight=1) モデルに作業を追加 (括弧内の引数説明はactivityクラス参照) – addResource(name=‘’, capacity={}, rhs=0, direction=‘<=’) モデルに資源を追加 (括弧内の引数説明はresourceクラス参照) – addTemporal(pred, succ, tempType=‘CS’, delay=0) モデルに先行制約を追加 (括弧内の引数説明はtemporalクラス参照) – optimize() モデルの最適化を行う – write(filename=‘optseq.txt’) テキスト形式のガントチャートを書く パラメータクラス ー Parameters よく使うパラメータ • TimeLimit:求解する時の制限時間;単位は秒;既 定値=600秒 • OutputFlag:モデルを出力する場合True,出力しな い場合False ; 既定値=False • Makespan:最大完了時刻最小化の時はTrue,それ 以外の時はFalse ; 既定値=False • RandomSeed – random seedを設定;整数;既定値=1 資源クラス ー Resource(1) 資源の定義を行う • resourceの引数と引数のデフォルト値 resource. (name=' ', capacity=0, rhs=0, direction='<=') – – – – name : 資源の名前,文字列で入力 capacity : 再生可能資源の最大容量; 区間と容量の辞書で入力 rhs : 再生不能資源の制約 direction : 再生不能資源制約の向き"<=" or ">=" or "=" • resourceのメソッド (次のページで詳しく) – addCapacity(start=0, finish=0, amount=1) 資源に容量を追加 – addTerms(coeffs=[], vars=[], values=[]) 再生不能資源に関する制約の左辺追加 – printConstraint( ) 再生不能資源に関する制約を展開 – setRhs(rhs=0) – 再生不能資源に関する制約の右辺追加 資源クラス ー Resource(2) 資源メソッド resourceのメソッド – addCapacity(start=0, finish=0, amount=1) start, finish : 資源使用開始と終了時刻 amount : 資源の使用量 例: manpower.addCapacity(0,5,2) 時刻0から時刻5の間に資源 (manpower) を2単位使用 – addTerms(coeffs=[], vars=[], values=[]) coeffs : 追加する項; リストで入力可 vars : 作業オブジェクト; リストで入力可 values : モードオブジェクト; リストで入力可 例: budget.addTerms(1,act,express) act と言う作業が express と言うモードで行う時再生不能 資源を1単位追加 時間制約クラス ー Temporal 時間制約の定義を行う • Temporal の引数と引数のデフォルト値 Temporal(pred, succ, tempType, delay) – pred : 先行作業のオブジェクト – succ : 後続作業のオブジェクト – tempType : 時間制約のタイプ; 規定値= "CS" • "CS"=Completion-Start, "SS"=Start-Start, "SC"= Start-Completion, "CC"=Completion-Completion • 先行作業の終了 (開始)時刻+delay <= 後続作業の開始 (終了) 時刻 – delay : 先行作業と後続作業の時間ずれ; 負の数も可 時間制約 時間制約オブジェクトの生成法 Temporal (先行作業 , 後続作業 , “制約タイプ” , 時間ずれ) Completion Start A B 先行作業 後続作業 作業Aの完了後に作業Bの開始 Temporal(pred=A, succ=B, tempType=“CS”, delay=0) 制約タイプ(type) Start Start A B Start type SS Completion A B Completion A type SC Completion B type CC 時間ずれ(delay) Completion A Start B 10 Completion A 作業Bの開始可能時間帯 Start B 作業Bの開始可能時間帯 10 delay 10 delay -10 時間制約の応用 1(同時開始) • Pythonインターフェイスによる記述 モデル名.addTemporal(先行作業,後続作業,“制約タイプ”,時間ずれ) AとBの同時開始 モデル名.addTemporal(A,B,"SS",0) モデル名.addTemporal(B,A,"SS",0) Aの開始+0 ≦ Bの開始 Aの開始=Bの開始 Bの開始+0 ≦ Aの開始 時間制約の応用 2(開始時間固定) Aの開始を50に モデル名.addTemporal(source,A,"SS",50) 固定 モデル名.addTemporal(A, source,"SS",-50) ダミーの始点(source)の開始+50 ≦ Aの開始 Aの開始-50 ≦ダミーの始点(source)の開始 sourceの開始時刻は0 Aの開始=50 時間制約の応用 3 (最大・最小段取り時間) Aの完了後最小 10分最大30分で Bを開始 モデル名.addTemporal(A,B,"CS",10) モデル名.addTemporal(B,A,"SC",-30) Aの終了+10 ≦ Bの開始 Bの開始-30 ≦Aの終了 Bの開始≦ Aの終了+30 Bの開始はAの終了+[10,30] 航空機を早く離陸させよう! • PERT: Program Evaluation and Review Technique,第二次 世界大戦のポラリス潜水艦の建造で利用.(ORの古典) 作業1 作業3 作業4 13分 15分 27分 完了時刻最小化 ダミー作業 先行制約 作業2 25分 作業5 22分 点が作業(活動)のグラフ->点上活動図式 作業1:乗客降ろし(13分) 作業2:荷物降ろし(25分) 作業3:機内清掃(15分) 作業4:乗客搭乗(27分) 作業5:荷物積み込み(22分) 航空機を早く離陸させよう! 作業1 作業3 作業4 完了時刻最小化 開始時刻 15分 13分 27分 終了時刻 先行制約 作業2 ダミー作業 作業5 ダミー作業 25分 作業1:乗客降ろし(13分) 22分 作業2:荷物降ろし(25分) 作業3:機内清掃(15分) 作業4:乗客搭乗(27分) 作業5:荷物積み込み(22分) Pythonインターフェイスによる記述 (1) • OptSeqⅡのモジュールを読み込む from optseq2 import * • モデルオブジェクトm1 を作成 m1=Model() • 作業時間を作業をキー,作業時間を値とする辞 書duration に保管 duration ={1:13, 2:25, 3:15, 4:27, 5:22 } キー 値 Pythonインターフェイスによる記述 (2) • 作業を保管するための空の辞書actを作成 act={} • モードを保管するための空の辞書mode を作成 mode={} Pythonインターフェイスによる記述 (3) • モデルオブジェクトm1 のaddActivity(“作業名”) メソッドを用いてモデルに作業を追加 • モードクラスMode(“モード名”, 作業時間) を用い てモードに必要な作業時間を入力 • addModes メソッドを用いて作業にモードを追加 for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (4) • モデルに addTemporal (先行作業, 後続作業) メソッドを用いて時間制約を追加 m1.addTemporal(act[1],act[3]) m1.addTemporal(act[2],act[4]) m1.addTemporal(act[2],act[5]) m1.addTemporal(act[3],act[4]) Pythonインターフェイスによる記述 (5) • m1.optimize() を入力して求解 m1.Params.TimeLimit=1 求解時の制限時間 m1.Params.OutputFlag=True モデルを出力する場合True m1.Params.Makespan=True m1.optimize() 最大完了時刻最小化の時はTrue Pythonインターフェイスによる記述 (6) • Pythonコードまとめ from optseq2 import * m1.addTemporal(act[1],act[3]) m1=Model() m1.addTemporal(act[2],act[4]) duration ={1:13, 2:25, 3:15, 4:27, 5:22 } m1.addTemporal(act[2],act[5]) act={} m1.addTemporal(act[3],act[4]) mode={} m1.Params.TimeLimit=1 for i in duration: m1.Params.OutputFlag=True act[i]=m1.addActivity("Act[%s]"%i) m1.Params.Makespan=True m1.optimize() mode[i]=Mode("Mode[%s]"%i,duration[i]) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (7) • 実行結果(一部) source ---: 0 0 ダミーの始点の開始時刻が0 sink ---: 55 55 ダミーの終点の開始時刻が55(完了時刻) activity[1] ---: 0 0--13 13 作業1は0に開始されて13に終了 activity[2] ---: 0 0--25 25 作業2は0に開始されて25に終了 activity[3] ---: 13 13--28 28 作業3は13に開始されて28に終了 activity[4] ---: 28 28--55 55 作業4は28に開始されて55に終了 activity[5] ---: 25 25--47 47 作業5は25に開始されて47に終了 objective value = 55 目的関数 cpu time = 0.00/1.00(s) 計算時間 iteration = 1/15962 反復回数 航空機を早く離陸させよう!最適解 作 業 1 作 業 3 13分 15分 作 業 2 25分 0 作 業 4 27分 作 業 5 22分 55 時 間 航空機を早く離陸させよう! 作業1 作業3 作業4 完了時刻最小化 15分 13分 開始時刻 27分 終了時刻 先行制約 作業2 ダミー作業 作業5 ダミー作業 25分 作業員1人 作業1:乗客降ろし(13分) 22分 作業2:荷物降ろし(25分) 作業3:機内清掃(15分) 作業4:乗客搭乗(27分) 作業5:荷物積み込み(22分) Pythonインターフェイスによる記述(1) •資源(この例題の場合は作業員)制約の記述: モデル名.addResource(“資源名”,( 時刻1, 時刻2):供給量) r e s=m1. addResource ( "worker " , (0 , " i n f " ) : 1 ) •モードに資源使用量追加: モード名.addResource (資源,(資源使用可能区間):使用 量) for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (2) • Pythonコードまとめ from optseq2 import * m1.addTemporal(act[1],act[3]) m1=Model() m1.addTemporal(act[2],act[4]) duration ={1:13, 2:25, 3:15, 4:27, 5:22 } m1.addTemporal(act[2],act[5]) res=m1.addResource("worker",capacity={(0,"inf"):1} ) m1.addTemporal(act[3],act[4]) act={} mode={} for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) m1.Params.TimeLimit=1 m1.Params.OutputFlag=True m1.Params.Makespan=True m1.optimize() Pythonインターフェイスによる記述 (3) • 実行結果(一部) source ---: 0 0 sink ---: 102 102 activity[1] ---: 47 47--60 60 activity[2] ---: 0 0--25 25 activity[3] ---: 60 60--75 75 activity[4] ---: 75 75--102 102 activity[5] ---: 25 25--47 47 objective value = 102 cpu time = 0.00/3.00(s) iteration = 0/64983 並列ショップスケジューリング ピットイン時間を短縮せよ! gas 3秒 作業1 作業9 11 秒 3人のピットクルー (資源制約) WA TE R 2 作業2 秒 作業5 2秒 4 作業6 作業3 作業7 作業8 作業4 2 秒 秒 4 秒 4秒 4 秒 2 秒 作業10 資源(機械)制約 があると難しい! Pythonインターフェイスによる記述 (1) 3人の作業員記述:資源制約モデル名.addResource("資源 名",( 時刻1, 時刻2):供給量) の供給量を3 に設定 from optseq2 import * m1=Model() duration ={1:3, 2:2, 3:2, 4:2, 5:4, 6:4, 7:4, 8:4, 9:11, 10:2 } res=m1.addResource("worker",capacity={(0,"inf"):3}) act={} mode={} for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) Pythonインターフェイスによる記述 (2) 時間制約 m1.addTemporal(act[1],act[9]) for i in range(5,9): m1.addTemporal(act[4],act[i]) m1.addTemporal(act[i],act[10]) 求解実行 m1.Params.TimeLimit=1 m1.Params.OutputFlag=True m1.Params.Makespan=True m1.optimize() Pythonインターフェイスによる記述 (3) • Pythonコードまとめ from optseq2 import * m1.addTemporal(act[1],act[9]) m1=Model() for i in range(5,9): duration ={1:3, 2:2, 3:2, 4:2, 5:4, 6:4, 7:4, 8:4, 9:11, 10:2 } res=m1.addResource("worker",capacity={(0,"inf"):3} ) act={} mode={} for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) m1.addTemporal(act[4],act[i]) m1.addTemporal(act[i],act[10]) m1.Params.TimeLimit=1 m1.Params.OutputFlag=True m1.Params.Makespan=True m1.optimize() Pythonインターフェイスによる記述 (4) • 実行結果(一部) source ---: 0 0 sink ---: 14 14 prepare ---: 0 0--3 3 water ---: 0 0--2 2 front ---: 0 0--2 2 jackup ---: 2 2--4 4 tire1 ---: 8 8--12 12 tire2 ---: 4 4--8 8 tire3 ---: 8 8--12 12 tire4 ---: 4 4--8 8 oil ---: 3 3--14 14 jackdown ---: 12 12--14 14 objective value = 14 cpu time = 0.00/3.00(s) iteration = 0/37644 並列ショップスケジューリング - 複数モード gas 1人 2人 3人 3 秒 作業1 作業2 2 秒 3秒 2秒 1秒 3人のピットクルー (資源制約) 作業9 11 秒 作業5 2秒 作業6 作業3 作業7 作業8 2秒 作業4 4秒 4秒 4秒 4 秒 2 秒 作業10 資源(機械)制約 があると難しい! 並列ショップスケジューリング 2 - モードの概念と使用法 モード:作業を行うための処理方法 例: 作業 「ジュースを買う」 コンビニモード: 作業時間 20 分,お金資源 120 円,人資源 1人 自販機モード: 作業時間 5 分,お金資源 120 円,人資源 1人 スーパーモード: 作業時間 40 分,お金資源 100 円,人資源 1人,車資源 1 台 Pythonインターフェイスによる記述 (1) for i in duration: • 多モードの記述 作業1のモード1 act[i]=m1.addActivity("Act[%s]"%i) if i==1: mode[1,1]=Mode("Mode[1_1]",3) mode[1,1].addResource(res,{(0,"inf"):1}) 作業1のモード1に 資源を追加 mode[1,2]=Mode("Mode[1_2]",2) mode[1,2].addResource(res,{(0,"inf"):2}) mode[1,3]=Mode("Mode[1_3]",1) mode[1,3].addResource(res,{(0,"inf"):3}) 作業1にモード1, モード2,モード3 を追加 act[i].addModes(mode[1,1],mode[1,2],mode[1,3]) else: mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (2) • Pythonコードまとめ(1) for i in duration: act[i]=m1.addActivity("Act[%s]"%i) from optseq2 import * m1=Model() duration ={1:3, 2:2, 3:2, 4:2, 5:4, 6:4, 7:4, 8:4, 9:11, 10:2 } res=m1.addResource("worker",capacity={(0, "inf"):3}) act={} mode={} if i==1: mode[1,1]=Mode("Mode[1_1]",3) mode[1,1].addResource(res,{(0,"inf"):1}) mode[1,2]=Mode("Mode[1_2]",2) mode[1,2].addResource(res,{(0,"inf"):2}) mode[1,3]=Mode("Mode[1_3]",1) mode[1,3].addResource(res,{(0,"inf"):3}) act[i].addModes(mode[1,1],mode[1,2],mode[1,3]) else: mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (3) • Pythonコードまとめ(2) m1.addTemporal(act[1],act[9]) for i in range(5,9): m1.addTemporal(act[4],act[i]) m1.addTemporal(act[i],act[10]) m1.Params.TimeLimit=1 m1.Params.OutputFlag=True m1.Params.Makespan=True m1.optimize() Pythonインターフェイスによる記述 (4) objective value = 13 計算結果とガントチャート cpu time = 0.00/1.00(s) iteration = 7/7343 source --- 0 0 sink --- 13 13 Act[1] Mode[1_3] 0 1 Act[2] --- 1 3 Act[3] --- 11 13 Act[4] --- 1 3 Act[5] --- 7 11 Act[6] --- 3 7 Act[7] --- 7 11 Act[8] --- 3 7 Act[9] --- 1 12 Act[10] --- 11 13 planning horizon= 13 資源制約付きスケジュールング お家を早く造ろう! 作業時間=2日 1日目1人,2日目2人の資源 が必要! 1階 屋根 土台 完成! 1人の作業員休暇 資源量(人) 内装 2人 0 3 時間(日) 最適解 資源量(人) 時間(日) 資源制約付きスケジューリングは一般的なモデル (ジョブショップ,並列ショップスケジューリングを特殊形として含む.) Pythonインターフェイスによる記述 (1) • 資源の必要量入力 {(0,1):2 } m1=Model() duration ={1:1,2:3,3:2,4:2} {(開始時刻,終了時刻):資源必要 量} 土台造り( req[1] )は1日目に 資源(人)が2単位必要 • 資源の容量制約入力 req={} req[1]={(0,1):2 } req[2]={(0,1):2 ,(1,3):1} req[3]={(0,2):1 } req[4]={(0,1):1,(1,2):2 } (0,2,2) res=m1.addResource("worker") (開始時刻,終了時刻,資源容量) 最初の2日間は資源(人)が 2単位使用可能 res.addCapacity(0,2,2) res.addCapacity(2,3,1) res.addCapacity(3,"inf",2) Pythonインターフェイスによる記述 (2) • Pythonコードまとめ from optseq2 import * for i in duration: m1=Model() act[i]=m1.addActivity("Act[%s]"%i) duration ={1:1,2:3,3:2,4:2} mode[i]=Mode("Mode[%s]"%i,duration[i]) req={} mode[i].addResource(res,req[i]) req[1]={(0,1):2 } act[i].addModes(mode[i]) req[2]={(0,1):2 ,(1,3):1} m1.addTemporal(act[1],act[2]) req[3]={(0,2):1 } m1.addTemporal(act[1],act[3]) req[4]={(0,1):1,(1,2):2 } m1.addTemporal(act[2],act[4]) res=m1.addResource("worker") res.addCapacity(0,2,2) m1.Params.TimeLimit=1 res.addCapacity(2,3,1) m1.Params.OutputFlag=True res.addCapacity(3,"inf",2) m1.Params.Makespan=True act={} m1.optimize() mode={} Pythonインターフェイスによる記述 (3) • 実行結果(一部) objective value = 6 cpu time = 0.00/1.00(s) iteration = 1/6936 source --- 0 0 sink --- 6 6 Act[1] --- 0 1 Act[2] --- 1 4 Act[3] --- 3 5 Act[4] --- 4 6 planning horizon= 6 1機械スケジュールング 納期遅れを最小にしよう! • 完了時刻最小化(メイクスパン)以外の目的関数の例 – – – – – 納期遅れ最小化 納期ずれ最小化 納期遅れした作業(ジョブ)数最小化 納期遅れの最大値の最小化 上の指標の重み付きの尺度最小化 .... 会社名 A社 B社 C社 D社 作業時間 1日 2日 3日 4日 納期 9日後 6日後 4日後 5日後 Pythonインターフェイスによる記述 (1) • 納期に関する入力 納期を辞書に保管 due={1:5,2:9,3:6,4:4} ....... 納期を作業に追加 for i in req: act[i]=m1.addActivity("Act[%s]"%i,duedate=due[i]) mode[i]=Mode("Mode[%s]"%i,req[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (2) • Pythonコードまとめ from optseq2 import * m1=Model() due={1:5,2:9,3:6,4:4} req={1:1, 2:2, 3:3, 4:4 } for i in req: act[i]=m1.addActivity("Act[%s]"%i,duedate=due[i]) mode[i]=Mode("Mode[%s]"%i,req[i]) mode[i].addResource(res,{(0,"inf"):1}) act[i].addModes(mode[i]) res=m1.addResource("writer") res.addCapacity(0,"inf",1) m1.Params.TimeLimit=1 m1.Params.OutputFlag=True act={} mode={} m1.Params.Makespan=False m1.optimize() Pythonインターフェイスによる記述 (3) --- best solution --source,---, 0 0 sink,---, 10 10 Act[1],---, 4 4--5 5 Act[2],---, 8 8--10 10 Act[3],---, 5 5--8 8 Act[4],---, 0 0--4 4 • 実行結果(一部) 会社名 A社 B社 C社 D社 作業時 1日 間 2日 3日 4日 納期 9日後 5日後 作業4(D社) 6日後 4日後 作業1(A社) --- tardy activity --Act[2]: 1 Act[3]: 2 作業3(C社) 納期遅れ 作業2(B社) 納期遅れ 時間(日) 0 10 A社納期 C社納期 D社納期 B社納期 航空機をもっと早く離陸させよう! クリティカルパス法 (critical path method,略してCPM) 作業時間を費用をかけることによって短縮できる 作業1:乗客降ろし 13分.10分に短縮可能で,1 万円必要. 作業2:荷物降ろし 25分.20 分に短縮可能で,1万円必要. 作業3:機内清掃 15 分.10分に短縮可能で, 1万円必要. 作業4:新しい乗客の搭乗 27 分.25分に短縮可能で,1万円必要. 作業5: 新しい荷物積み込み 22 分.20分に短縮可能で, 1万円必要. 再生不能資源(使用可能なお金)4 万万円以下 再生可能資源と再生不能資源 • 再生可能資源 作業完了後に再び使用可能になる資源 機械や作業員など • 再生不能資源 一度使うとなくなってしまう資源(計画期間 内での合計に制約を設ける) お金や原材料など クリティカルパス法の予算を再生不能資源として扱う! Pythonインターフェイスによる記述 (1) • 再生不能資源に関する入力(1) 通常時の 作業時間 再生不能 資源使用時 の作業時間 m1=Model() durationA = {1:13, 2:25, 3:15, 4:27, 5:22 } durationB = {1:10, 2:20, 3:10, 4:25, 5:20 } act={} mode={} res=m1.addResource("money",rhs=4,direction="<=") 再生不能資源 使用可能量制約 Pythonインターフェイスによる記述 (2) • Pythonコードまとめ from optseq2 import * m1.addTemporal(act[1],act[3]) m1=Model() m1.addTemporal(act[2],act[4]) durationA = {1:13, 2:25, 3:15, 4:27, 5:22 } m1.addTemporal(act[2],act[5]) durationB = {1:10, 2:20, 3:10, 4:25, 5:20 } m1.addTemporal(act[3],act[4]) act={} mode={} print m1 res=m1.addResource("money",rhs=4,direction="<=") for i in durationA: m1.Params.TimeLimit=1 act[i]=m1.addActivity("Act[%s]"%i) m1.Params.OutputFlag=True mode[i,1]=Mode("Mode[%s][1]"%i,durationA[i]) m1.Params.Makespan=True mode[i,2]=Mode("Mode[%s][2]"%i,durationB[i]) m1.optimize() act[i].addModes(mode[i,1],mode[i,2]) res.addTerms(1,act[i],mode[i,2]) 結果 作業1 4万 円 作業3 作業4 10分 10分 25分 作業5 作業2 20分 2 2分 0 45 作業1 1万 円 作業3 作業4 27分 10分 13分 作業5 作業2 25分 2 2分 0 0万 円 時間 52 作業1 作業3 作業4 15分 13分 時間 27分 作業5 作業2 25分 2 2分 0 時間 55 並列ショップスケジューリング 複数モード gas 複数資源による作業の並列処理 1人 2人 3人 3 秒 作業1 作業2 2 秒 3秒 2秒 1秒 3人のピットクルー (資源制約) 作業9 11 秒 作業5 2秒 作業6 作業3 作業7 作業8 2秒 4秒 4秒 4秒 4 秒 2秒 作業10 資源(機械)制約 があると難しい! Pythonインターフェイスによる記述 (1) • 並列処理入力 for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) 1番目の小作業を 3単位並列可能 if i==1: mode[i].addParallel(1,1,3) act[i].addModes(mode[i]) Pythonインターフェイスによる記述 (2) • Pythonコードまとめ from optseq2 import * m1.addTemporal(act[1],act[9]) m1=Model() for i in range(5,9): duration ={1:3, 2:2, 3:2, 4:2, 5:4, 6:4, 7:4, 8:4, 9:11, 10:2 } m1.addTemporal(act[4],act[i]) res=m1.addResource("worker") res.addCapacity(0,"inf",3) m1.addTemporal(act[i],act[10]) act={} mode={} for i in duration: act[i]=m1.addActivity("Act[%s]"%i) mode[i]=Mode("Mode[%s]"%i,duration[i]) mode[i].addResource(res,{(0,"inf"):1}) if i==1: mode[i].addParallel(1,1,3) act[i].addModes(mode[i]) m1.Params.TimeLimit=1 m1.Params.OutputFlag=True m1.Params.Makespan=True m1.optimize() ジョブショップスケジューリンング ジョブ(作業)ごとに機械(工程)順が異なっても良い! 作業 機械 機械2 機械1 7 小作業1 小作業2 機械3 10 機械3 4 小作業3 機械2 機械1 作業2 9 11 5 機械1 3 機械2 6 機械2 機械3 9 機械3 13 作業1 作業3 12 機械1 9 作業4 ジョブショップスケジューリンング • • • • • • • 各作業は,最初の2日間は作業員の作業が必要 各作業は小作業1,2,3の順に処理される 各作業は1日目の作業後休むことが可能 作業員は平日(週5日間)のみ作業可能 作業員は1日最大2人作業可能 機械1の最初の2日間分の作業は並列処理可能 機械2はexpressモードで処理可能;処理時間は4日間に 短縮可能 • expressモードは最大1回しか使用できない • 機械1での作業2は,作業1が完了した直後に行う (他の作業が間に入らない) Pythonインターフェイスによる記述 (1) • 機械に関する制約 machine={} for j in range(1,4): machine[j]=m1.addResource("machine[%s]"%j,capacity={(0,"inf"):1}) # 3種類の 機械;各機械は1つの作業しか処理できない • 作業員に関する制約 manpower=m1.addResource("manpower") for t in range(9): manpower.addCapacity(t*7,t*7+5,2) # 作業員は平日のみ作業可能;平日の作業 可能な作業員上限2人 Pythonインターフェイスによる記述 (2) • 作業と機械 7 小作業1 10 4 小作業3 小作業2 作業1 JobInfo={ (1,1):(1,7), (1,2):(2,10), (1,3):(3,4), (1,1): (1,7) は作業1の子作業1の機 械1での作業時間が7日であること (2,1):(3,9), (2,2):(1,5), (2,3):(2,11), (3,1):(1,3), (3,2):(3,9), (3,3):(2,12), (4,1):(2,6), (4,2):(3,13), (4,3):(1,9) } Pythonインターフェイスによる記述 (3) • 機械2はexpressモードで処理可能 budget=m1.addResource(“budget_constraint”,rhs=1) # expressモードが1回しか使え ないことを資源(budget)の使用可能量(rhs) =1で表現 express=Mode("Express",duration=4) # express の作業時間は4日 express.addResource(machine[2],{(0,"inf"):1},"max") # express は機械2を1単位使用 express.addResource(manpower,{(0,2):1}) express.addBreak(1,1) # 最初の2期の間は作業員が1人必要 # 各作業は1日目作業後休憩可能 express.addResource(machine[2],{(0,"inf"):1},"max") 資源タイプ 資源タイプ“max”:並列作業時も機械は1単位しか使用しない Pythonインターフェイスによる記述 (4) • 作業にモード追加 for (i,j) in JobInfo: act[i,j]=m1.addActivity(“Act[%s][%s]”%(i,j)) # (作業名) mode[i,j]=Mode(“Mode[%s][%s]”%(i,j),duration=JobInfo[i,j][1])#モード名,作業時間追加 mode[i,j].addResource(machine[JobInfo[i,j][0]],{(0,“inf”):1},“max”) # 最初の2期の間は作業員が1人必要 mode[i,j].addResource(manpowe):1}) mode[i,j].addBreak(1,1) # 各作業は1日目作業後休憩可能 if JobInfo[i,j][0]==1: mode[i,j].addParallel(1,1,2) # 機械1での各小作業は並列処理可能 if JobInfo[i,j][0]==2: act[i,j].addModes(mode[i,j],express) budget.addTerms(1,act[i,j],express) else: act[i,j].addModes(mode[i,j]) # 機械2にexpressモードを追加 # expressモードは最大1回しか使用できない Pythonインターフェイスによる記述 (5) • 各作業は子作業1,2,3の作業順で処理される for i in range(1,5): for j in range(1,3): m1.addTemporal(act[i,j],act[i,j+1]) 小作業1 小作業3 小作業2 7 小作業1 10 4 小作業3 小作業2 9 11 5 小作業1 小作業1 9 12 作業3 小作業3 小作業2 6 作業2 小作業3 小作業2 3 作業1 13 9 作業4 Pythonインターフェイスによる記述 (6) 機械1での作業2は,作業1が完了した直後に行う (他の作業が間に入らない) 1. ダミーの作業を生成 d_act=m1.addActivity(“dummy_activity”) # ダミーの作業を追加 d_mode=Mode(“dummy_mode”) d_mode.addBreak(0,0) # ダミーのモードを追加 # 作業開始時刻から無限に休憩可能 d_mode.addResource(machine[1],{(0,0):1},“break”) # 休憩中にも資源を使用 d_act.addModes(d_mode) 資源使用量 # 作業にモードを追加 作業時間 0 のダミーの作業 休憩中にも資源を使用 作業開始時刻から無限に休憩可能 期 Pythonインターフェイスによる記述 (6) 2. 時間制約追加 m1.addTemporal(act[1,1],d_act,tempType=“CS”) #(1) m1.addTemporal(d_act,act[1,1],tempType="SC") #(2) m1.addTemporal(d_act,act[2,2],tempType="CS") #(3) m1.addTemporal(act[2,2],d_act,tempType="SC") #(4) (1) 作業1の小作業1(act[1,1]) が終了しないとダミー作業 (d_act) が開始できない (2) ダミー作業 (d_act) が開始しないと作業1の小作業1 (act[1,1]) が終了できない (3) , (4) も (1) , (2) と同様 結論: 作業1の小作業1 はダミー作業の直後に開始 ダミー作業は作業1の小作業1 の直後に開始 ダミー作業 資源使用量 act[1,1] ダミー作業は休憩中にも資源を使用 act[2,2] 期 Pythonインターフェイスによる記述 (7) • Pythonコードまとめ(1) from optseq2 import * m1=Model() machine={} for j in range(1,4): machine[j]=m1.addResource("machine[%s]"%j,capacity={(0,"inf"):1}) manpower=m1.addResource("manpower") for t in range(9): manpower.addCapacity(t*7,t*7+5,2) budget=m1.addResource("budget_constraint",rhs=1) JobInfo={ (1,1):(1,7), (1,2):(2,10), (1,3):(3,4), (2,1):(3,9), (2,2):(1,5), (2,3):(2,11), (3,1):(1,3), (3,2):(3,9), (3,3):(2,12), (4,1):(2,6), (4,2):(3,13), (4,3):(1,9) act={} } Pythonインターフェイスによる記述 (8) for (i,j) in JobInfo: • Pythonコードまとめ (2) act[i,j]=m1.addActivity("Act[%s][%s]"%(i,j)) mode[i,j]=Mode("Mode[%s][%s]"%(i,j), express=Mode("Express",duration=4) duration=JobInfo[i,j][1]) express.addResource(machine[2],{(0,"i nf"):1},"max") mode[i,j].addResource( express.addResource(manpower,{(0,2): 1}) mode[i,j].addResource(manpower,{(0,2):1}) express.addBreak(1,1) mode[i,j].addBreak(1,1) mode={} if JobInfo[i,j][0]==1: machine[JobInfo[i,j][0]],{(0,"inf"):1},"max") mode[i,j].addParallel(1,1,2) if JobInfo[i,j][0]==2: act[i,j].addModes(mode[i,j],express) budget.addTerms(1,act[i,j],express) else: act[i,j].addModes(mode[i,j]) Pythonインターフェイスによる記述 (9) • Pythonコードまとめ(3) for i in range(1,5): for j in range(1,3): m1.addTemporal(act[i,j],act[i,j+1]) m1.addTemporal(act[1,1],d_act,tempType="CS") m1.addTemporal(d_act,act[1,1],tempType="SC") m1.addTemporal(d_act,act[2,2],tempType="CS") d_act=m1.addActivity("dummy_activity") m1.addTemporal(act[2,2],d_act,tempType="SC") d_mode=Mode("dummy_mode") m1.Params.TimeLimit=1 d_mode.addBreak(0,0) m1.Params.OutputFlag=True d_mode.addResource(machine[1],{(0,0):1},"break" ) m1.Params.Makespan=True d_act.addModes(d_mode) m1.optimize()
© Copyright 2025 ExpyDoc