はじめに
前回のブログでは、電力平準化問題が数理最適化のスケジューリング問題として扱えること、SASのCLP Procedureで簡単にモデリング可能であることを紹介しました。今回は具体的にサンプル問題でCLP Procedureの使い方を見ていきます(ブログ末尾にサンプルコード記載)。
CLP Procedureの使い方
ここではプロシジャの使い方を分かりやすく示すため、図1のような3タスク、2設備の簡略化された問題のスケジューリングを例として説明します。タスクの詳細な設定は表1の通りとします。
中身の詳しい説明に入る前に、一つだけ最適化の処理で理解しておくべき概念として、Resourceというものを紹介します。Resourceとはタスクを実行するために必要な何かしらの資源で、CLP Procedureによるスケジューリングでは「ある時間帯の使用電力がXである=電力という資源をX占有する」のように読み替えることがポイントです。図1を例にとると、全体のResourceとしては電力設備M1とM2が1つずつ、電力は設定した電力上限分が用意されており、タスクA実行中は電力設備M1というResourceを1つ、電力というResourceを電力波形で表される分だけ各時間で占有する、と見なすことができます。最終的に実行可能なスケジュール(同じ時間帯に複数のタスクが同じ設備で実行されない、電力上限を超過しない)を作成することは、各タスクが上限の範囲内で上手くResourceを分け合うような割り当てを作成することに置き換えられます。
では、実際にSASコードでCLP Procedureを呼び出してスケジューリングを行う部分ですが、必要なのは以下のコードだけです。
proc clp actdata=act resdata=res schedtime=outtime schedres=outres; run; |
“schedtime”と”schedres”は出力先データセットの指定なので、ユーザーが用意する必要があるのは入力データの”actdata”と”resdata”のみです。では、それぞれのデータセットの内容を確認してみましょう。
“resdata”ではResourceの設定を行います。上での説明の通り、電力設備M1とM2が1つずつで、使用可能な電力総量の上限が“12”であることを表現しており非常に簡潔な設定です。
“actdata”はスケジューリングの対象となる最小要素であるActivityの設定を行います。今回の例では、タスクを図1のように分割したフェーズそれぞれを一つのActivityと考えます。こちらのデータセットは様々な要素が一行に並んでいるのでやや複雑ですが、設定項目は大まかに5種類の系統に分かれます。
まず_ACTIVITY_列では、各Activityにそれぞれ固有のIDを設定します。タスクAを例にとると、各フェーズの電力使用に当たるA01~A05と、タスク全体として設備M1を使用することを表すA00を設定しています。今回の問題では「設備M1を使用している間電力を使用する」という前提なので、A00の_DURATION_とA01~A05の合計が一致していることが確認できます。問題によっては「待ち時間などで使用電力0だが設備は占有したまま」のような状況で、両者の_DURATION_が異なる設定もあり得ます。
_SUCCESSOR_は前後関係を設定する対象のActivityを示しており、前後関係の種類と程度は_LAG_と_LAGDUR_で指定します。例えばFSは、「当該ActivityのFinishと_SUCCESSOR_で指定するActivityのStartの差が_LAGDUR_で指定する値以上」を示しています。今回の例では一つのタスクを分割したフェーズに相当するActivityは常に間を空けることなく実行されるため、_LAG_=FSE(FinishとStartが_LAGDUR_にEqual)、_LAGDUR_=0と設定しています。6行目のように、「タスクAの後3時間単位空けてタスクBを開始する」という業務的な意味での前後関係を表すのにも使用できます。また1行目のA00では、_LAG_=SSE(StartとStartが_LAGDUR_にEqual)でA01と開始が一致するように設定されていることにも注意が必要です。設定の詳細はマニュアルをご覧ください。
_ALIGNDATE_と_ALIGNTYPE_の組み合わせは_SUCCESSOR_と_LAG_の関係に似ていて、当該Activityと_ALIGNDATE_で指定した期日の間に_ALIGNTYPE_で指定する前後関係を設定します。例えば作業に着手可能になる時期や納期を表すのに使用でき、12行目では_ALIGNTYPE_=FEQ(Finishが_ALIGNDATE_=24にEqual)で「タスクB(のフェーズ5)が時刻24丁度に終了すること」を指定しています。
この設定でCLP Procedureを実行すると以下のような結果が得られ、時間に関する結果の”schedtime”とResourceに関する結果の”schedres”が出力されます。”schedtime”は各Activityをどの時間枠に割り当てるかという関心のある結果そのもの、”schedres”には例えば(今回の問題とは異なり)タスクを実行する設備にも選択肢がある場合のResourceの割り当てが出力されています。
最後に少しデータの加工が必要ですが、以下のようにガントチャートや電力波形に可視化して、諸々の条件が満たされたスケジュールになっていることが確認できます。図2 では"resdata"(表2)で指定した、「使用可能な電力総量の上限が“12”」を下回っていることが確認できます。
なお前回も記載しましたが、「ピーク値をどこまで下げることができるか?」を知りたい場合は、「使用可能な電力総量の上限」の指定値を下げて計算し、その条件を満たすスケジュールが作成可能かを調べることで下限を得ることができます。
まとめ
本ブログでは電力平準化問題を例として、CLP Procedureの使い方を見てきました。手順の中心となる入力データ準備の部分は、他のプログラミング言語で用意したものを読み込ませても良いですし、規模が小さければExcel上で作成してしまうことも可能です。SAS言語の知識も最適化の知識も最小限で、便利にスケジューリング機能が使えると感じていただけたなら幸いです。(反響があれば、設備の割り当ても考える場合など、より高度なモデリング方法についても取り上げます)
サンプルコード
入力データは全てdataステップで作成しています。ganttプロシジャはclpプロシジャの結果である"schedtime"をほとんどそのまま使用できます(今回はグループ化の処理だけ追加しています)。使用電力の棒グラフについては、今回説明していないデータ加工が多少必要になるため、ソースコードからは割愛しています。
data res; input _RESOURCE_ $ _CAPACITY_; datalines; M1 1 M2 1 El 12 ; data act; input _ACTIVITY_ $ _DURATION_ _SUCCESSOR_ $ _LAG_ $ _LAGDUR_ _ALIGNDATE_ _ALIGNTYPE_ $ _RESOURCE_ $ _QTY_; datalines; A00 10 A01 SSE 0 . . M1 1 A01 1 A02 FSE 0 . . El 4 A02 3 A03 FSE 0 . . El 10 A03 2 A04 FSE 0 . . El 7 A04 1 A05 FSE 0 . . El 8 A05 3 B01 FS 3 . . El 3 B00 10 B01 SSE 0 . . M1 1 B01 1 B02 FSE 0 . . El 4 B02 3 B03 FSE 0 . . El 10 B03 2 B04 FSE 0 . . El 7 B04 1 B05 FSE 0 . . El 8 B05 3 . . . 24 FEQ El 3 C00 10 C01 SSE 0 . . M2 1 C01 1 C02 FSE 0 . . El 5 C02 3 C03 FSE 0 . . El 8 C03 2 C04 FSE 0 . . El 7 C04 2 C05 FSE 0 . . El 6 C05 2 . . . . . El 1 ; proc clp actdata=act resdata=res schedtime=outtime schedres=outres; run; data outtime2; set outtime; task=substr(activity,1,1); num=input(substr(activity,2,2),8.); run; data outtime2; set outtime2; where num>0; run; proc gantt data=outtime2; chart / act=activity ss=start sf=finish zone=task onezoneval nojobnum nolegend compress hpages=1 vpages=1; run; |