2020年12月29日火曜日

QMC1 Nurse Scheduling Benchmark

 Optimum 解に到達するまで8秒、Optimum証明まで23秒、Cplexは、5秒、Gurobiは、4秒でした。




2020年12月28日月曜日

GPost-B Nurse Scheduling Benchmark

 最適値に到達するまでの時間は、スケジュールナース7.7sec、Cplex 351sec、Gurobi 83secでした。



2020年12月27日日曜日

GPOST Nurse Scheduling Benchmark

 Cplex 206秒、Gurobi 30秒、スケジュールナース7秒でした。この例も、夜勤の4回連続を禁止した結果、2回から3回の連続となっています。欧米の殆どのベンチマークがほぼこのような形態となっています。




2020年12月23日水曜日

Azaiez Nurse Scheduling Benchmark

 SC3で6秒、Cplex9秒、Gurobi3秒でした。

下記の勤務パターンをみてお気づきかと思いますが、欧米の勤務パターンは、日本と全く異なります。基本的に、夜勤は12時間で、それが、2-3日続くパターンが一般的になっています。

クラッシックなベンチマークは、欧米の論文に由来しています。結果、殆どが下記のパターンと似通ったものになります。INRC1のパターンもそのようなパターンとなっています。

ベンチマークとしての問題は、欧米パターンに偏っていることです。もっと難しい問題は、色々ある、という風に力説したいと思います。




2020年12月18日金曜日

LLR Nurse Scheduling Benchmark

 Gurobi /Cplex共5秒程度、ScheduleNurse AL1では、19secでOptimum値(301)に達しますが、厳密解ではありません。AL4では、4secで厳密解となります。ちなみにGoogleORToolsで使われているCBCでは、Optimum値に到達するのが275秒、28000秒かけても、Optimum証明が出来ていません。一般的に、MIPソルバの世界では、商用ソルバとOpensource ソルバでは、相当な性能比となることは、よく知られており、100倍となることは、珍しくないというか普通に起こります。(それ故に商用ソルバの存在価値があるわけです。)



2020年12月14日月曜日

Millar-2Shift-DATA1.1 Nurse Scheduling Benchmark

 

Millar-2Shift-DATA1との違いは、よく分かっていません。どちらにしても1sec以内で解ける簡単なインスタンスです。

2020年12月13日日曜日

Millar-2Shift-DATA1 Nurse Scheduling Benchmark

 
このあたりから少し実用的なモデルになってきます。スケジュールナースでは、0.3秒、CPLEX/Gurobiでも同程度でした。



2020年12月12日土曜日

Musa Nurse Scheduling Benchmark

 これも古いです。シフトが2、2週間で小規模、パターンがほぼありません。ほぼ全体が基数制約です。こういったベンチは、MIPソルバが圧倒します。CPLEXで0.01secでOptimum証明完了です。

スケジュールナースⅢでは、Optimum値が出現するまで10sec,証明完了まで23secかかりました。






2020年12月11日金曜日

Ozkarahan nurse scheduling benchmark

1989年ということですので、最も古いNSP問題ということになりそうです。

現在では、0secで解ける問題です。

ptpmcrender.fcgi (europepmc.org)

驚くべきは、すでにこの時代から、個人毎のスタッフ契約体系になっていることで、土日働けない人の考慮がなされています。




import sc3

for person in [2]:
	clist=[]
	for day in AllDays:
		vlist=[]
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if len(vlist) >= 1:
			clist.append(sc3.Or(vlist))
	sc3.AddSoft(sc3.SeqError(0,0,3, clist),'3_1',1)
for person in [3]:
	clist=[]
	for day in AllDays:
		vlist=[]
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if len(vlist) >= 1:
			clist.append(sc3.Or(vlist))
	sc3.AddSoft(sc3.SeqError(0,0,3, clist),'4_1',1)
for person in [6]:
	clist=[]
	for day in AllDays:
		vlist=[]
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if len(vlist) >= 1:
			clist.append(sc3.Or(vlist))
	sc3.AddSoft(sc3.SeqError(0,0,3, clist),'7_1',1)
for person in [7]:
	clist=[]
	for day in AllDays:
		vlist=[]
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if len(vlist) >= 1:
			clist.append(sc3.Or(vlist))
	sc3.AddSoft(sc3.SeqError(0,0,3, clist),'8_1',1)
for person in [8]:
	clist=[]
	for day in AllDays:
		vlist=[]
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if len(vlist) >= 1:
			clist.append(sc3.Or(vlist))
	sc3.AddSoft(sc3.SeqError(0,0,3, clist),'9_1',1)
for person in [12]:
	clist=[]
	for day in AllDays:
		vlist=[]
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if day in Sat :
			if day +1 in AllDays :
				v=sc3.And(~sc3.GetShiftVar(person ,day+0,'Y'),~sc3.GetShiftVar(person ,day+1,'Y'))
				vlist.append(v)
		if len(vlist) >= 1:
			clist.append(sc3.Or(vlist))
	sc3.AddSoft(sc3.SeqError(0,0,3, clist),'13_1',1)


2020年12月10日木曜日

Real Nurse Scheduling Benchmarksの構想

 ベンチマーキングは、SchedulingBenchmarksとNurseSchedulingCompetitionⅡが現存しており、既に、結果についてはレポート済みです。

Benchmarks|What is Schedule NurseⅢ (nurse-scheduling-software.com)

後は、過去のOR関係の論文で言及があるものを集めたセットのみが残っています。これらをスケジュールナースで解いてみたいと思います。これらのプロジェクトとMPSファイルをGithubに集めてOneStopのベンチマーク集としたいと思います。MPSファイルを添付することで、MIPソルバー同士、あるいは、MIPソルバー対メタヒューリスティクスも論じ易くなるのではないかと思います。

また、日本の実際的なベンチもいくつか提供したいと思います。たとえば、シフト数が25を超えるものや、プリセプタ・プリセプテイ、等、これまでにない、しかし現場での実際的な問題を提供したいと思っています。

これらにより、Realなベンチマーキング集としたいと思います。(Githubなので、どなたでもForkしてPull Request出来ます。)

GitHub - sugawara-system/Schedule_Nurse3_Gallery





2020年12月9日水曜日

.htaccess のリダイレクトを廃止

 挙動不明な動きをするので、廃止しました。ブラウザのキャッシュをクリアすれば、リダイレクトすることはなくなります。

2020年12月8日火曜日

Date Aggregate Rep2/3/4/7/14とは?

 https://nurse-scheduling-software.com/knowledge_base/

に書きました。カレンダを見て頂いた方が早いです。私も使ったことはないのですが、例えば「特定の日基準の隔週」という集合を定義したいときに使うことを設計時意図していたと思います。GUIカレンダとSolver側に認識は、一致しているはずですが、確認したいときは、python で、

import sc3

だけ記述して、言語制約をオンして求めると、


とR2,R3,R4,R7,R14 Day集合を確認できます。Repは、132Dからのサポートになります。


2020年12月6日日曜日

SDK2のAWS ACOUNT ID Checkを廃止

 SDK2(Scheduling Solver Engine on Lambda function in AWS Development Kit 2)では、これまで、お客様固有のアカントしか動作できませんでしたが、この条件を緩和し、どのアカウントでも動作できるようにしました。ただし、期限は変わらず残ります。期限内に量産への移行をできればお願いいたします。

2020年12月5日土曜日

新しいシフトスケジューリング論文

保育士や、パートタイムについて論じています。

http://repository.nihon-u.ac.jp/xmlui/handle/11263/1659

未だあまり読み込んでいないのですが、具体データもあるので、スケジュールナースでモデル化したらどういう感じになるか、興味深いです。



2020年12月4日金曜日

ikegami benchmarkの評価

日本語サイトは、

スケジュールナース ホームページ 

に移動しました。


プロモーション用に最新のデータを採取しました。

Fast|What is Schedule NurseⅢ (nurse-scheduling-software.com)

GurobiとCplexのデータは、Optimumの値証明が完了するまでの時間です。GurobiとCplexは、結構差がついてしまいましたが、Gurobiの進歩が著しいということでしょう。

誤解のないようにお願いしたいのですが、このデータは、これらインスタンスに限ってのお話です。平均的な比較ではありません。

MIPソルバーの性能向上が著しくて、メタヒューリスティクスは、肩身が狭くなりつつはありますが、上記データが示すように全てMIPで済むという話には未だならないと思います。それぞれに得意不得意の領域があり、実務的・典型的ナーススケジューリング問題には、ScheduleNurseⅢ Algorithm1がMIPソルバーより良い結果を示すということであります。

それから、StateOfArtRosteringSolverとは、AutoRosterの4.34でこれは、多分昔とったデータと変わらないと思います。