ハード制約の記述  
   -外部制約をスマートに記述する

前章で、デバッグの仕方とPythonによる検証について述べました。ここでは、それらを総合して、例で見ていきます。

1. GUIでの記述を外部制約に置き換えてみる


GUIで記述可能なものは、全て外部制約に置き換えることが出来ます。GUIを介さずに直接にソルバを呼ぶことも可能ですが、ここでは、基本の枠組みだけを残して、あるプロジェクトのGUI制約を、外部制約に置き換えることを行います。記述の参考にしてください。
 
最初の制約は、下の公休数です。最大10、最小10、制約の開始日がスケジュール開始日になっています
ソフト制約レベルが1になっていますが、レベル1は、求解ページでチェックしていないのでGUI上では、制約していないことになります。 



これを等価値な外部制約で記述すると以下のようになります。

制約ステートメントの前に、適当な名前 ここでは、”ハード制約”を追加します。

for (person in 全スタッフ){
	vector V;
	for (day in 全日){
		if (day >=スケジュール開始日){
			V.push_back(X[person][day][Y]);
		}
	}
	Hard:$SeqLE(10,10,V);
}


確認として、外部制約を使わない設定で求解すると、次のように公休数が出鱈目に生成されます。

次に、外部制約を使って求解すると、つぎのように公休数が全て10に揃います。外部制約が効いている、ということです。
なの、GUIで公休数の項目が出ているのは、GUIでの行制約として記述されているためです。(外部制約とは無関係です。今回、同じ定義であるため流用可能です。)


また、ソルバが生成したファイル交代3X3.pyをcygwin64上で走らせてみると、以下のようになり、当然エラーは出てきません。

$ python3 交代3X3.py
Checking Hard
Pass.
Pass.
Pass.
Pass.
Pass.
Pass.
...

以下、同様にして、このページの制約をすべて外部制約に置き換えてみると次のような記述になります。

for (person in 全スタッフ){
	vector VY,VS,VJ,Va;
	for (day in 全日){
		if (day >=スケジュール開始日){
			VY.push_back(X[person][day][Y]);//全員適用
		}
	}
	HardY:$SeqLE(10,10,VY);//Min:10,Max:10

}
for (スキル in スキルclass){//職能毎に
	for (person in スキル){//そのスキルに属する人について
		vector VS,VJ,Va;
		for (day in 全日){
			if (day >=スケジュール開始日){
				VS.push_back(X[person][day][S]);//深夜
				VJ.push_back(X[person][day][J]);//準夜
				//a=深夜または準夜	
				vector Vs;
				for (shift in a){
					Vs.push_back(X[person][day][shift]);
				}
				Va.push_back($Or(Vs));
			}
		}
		HardS:$SeqLE(3,5,VS);
		HardJ:$SeqLE(3,5,VJ);//Min:3, Max:5
		Harda:$SeqLE(0,9,Va);//Max9
	}
}


これを走らせて見ると次のようにGUIで定義した制約と同じ結果になりました。


勿論、下のScriptもOKでした。

$ python3 交代3X4.py

次に下の制約を置き換えました

for (スキル in スキルclass){//職能毎に
	for (person in スキル){//そのスキルに属する人について
		vector 土日フリー休日;
		for (day in 全日){
			if (day >=スケジュール開始日){
				
				JS禁止:$Inv($And(X[person][day-1][J],X[person][day][S]));
				a_a禁止:$Inv($And($Or(X[person][day-2][J],X[person][day-2][S]),$Or(X[person][day][J],X[person][day][S])));
				vector V;			
				for (i=-1 to 0){
					V.push_back(X[person][day+i][S]);
				}
				SS禁止:$Inv($And(V));

				V.clear();
				for (i=-1 to 0){
					V.push_back(X[person][day+i][J]);
				}
				JJ禁止:$Inv($And(V));

				V.clear();				
				V.push_back(X[person][day-1][S]);
				V.push_back(X[person][day  ][N]);
				Sm禁止:$Inv($And(V));

				V.clear();				
				V.push_back($Or(X[person][day-1][Y],X[person][day-1][H]));
				V.push_back(X[person][day  ][S]);
				xS禁止:$Inv($And(V));



				V.clear();				
				V.push_back(X[person][day-1][J]);
				V.push_back(X[person][day  ][N]);
				Jm禁止:$Inv($And(V));

				vector Z1;
				for (i=-5 to 0){
					Z1.push_back($Inv($Or(X[person][day+i][Y],X[person][day+i][H])));
				}
				6日連続公休なし禁止:$Inv($And(Z1));

				V.clear();
				if (day-3 in 金){
					V.push_back(    !X[person][day-3][J]);
					V.push_back($Or( X[person][day-2][Y],X[person][day-2][H]));
					V.push_back($Or( X[person][day-1][Y],X[person][day-1][H]));
					V.push_back(    !X[person][day  ][S]);
					土日フリー休日.push_back($And(V));
			
				}
			}
		}
		$SeqLE(1,2,土日フリー休日);
	}
}

その結果、次のように意図通りに動作しました。


最後に列に関する制約を置き換えました。

//列制約
for (day in 全日){
	if (day >=スケジュール開始日){
		vector VJ,VS,vj,vs,VN, V中べ;
		for (person in 全スタッフ){
			if (person in 若手){
				VJ.push_back(X[person][day  ][J]);
				VS.push_back(X[person][day  ][S]);
				
			}
			if (person in 中ベ){
				V中べ.push_back(X[person][day  ][N]);		
			}
			vj.push_back(X[person][day  ][J]);
			vs.push_back(X[person][day  ][S]);
			VN.push_back(X[person][day  ][N]);

		}
		準夜若手勤務者数:   $SeqLE(0,1,VJ);
		深夜夜若手勤務者数: $SeqLE(0,1,VS);
		準夜勤務者数:		$SeqLE(3,3,vj);
		深夜勤務者数:		$SeqLE(3,3,vs);
	
		if (day in 祝日でない土曜日){
			土曜日日勤者数:   $SeqLE(7,7,VN);
			土曜日日勤中べ:	  $SeqLE(1,0,V中べ);
		}
		if (day in それ以外の休診日){
			日曜日日勤者数:   $SeqLE(6,6,VN);
			日曜日日勤中べ:	  $SeqLE(1,0,V中べ);
		}
		if (day in 診療日){
			平日日勤者数:   $SeqLE(7,0,VN);
		}
	}
}


これも下のように意図通りに動作しました。


最後に、全体のエラーがないことを次のコマンドで確認しました。

$ python3 交代3X6.py

[Prev] [Next] [Index] [Home]