2023年12月29日金曜日

ペア制約再考

 ずっとこの問題を考えてきて、一つの方策がリニア化だったのですが、十分でないどころか、殆ど寄与できないことが明らかになり、どうしても再考せざるを得ません。(リニアペア制約が失敗という訳ではなく、前回の会議の日は日勤増みたい制約で威力を発揮しています。)
幸いにしてこの時期は、サポート関係の仕事が入ってこないので、深く思索する時間が持てます。加えて、単にペア制約というだけでなく、ペア制約の一般化を考える必要があるということがあります。ライフワークとして取り組んでいるナーススケジューリング問題ですが、実際的によく起きる問題でありながら、誰もこの問題について指摘・改善に取り組んでいません。孤峰に独り挑む気分です。

一般化ペア制約の定義

スタッフ間でリニア制約以外の関係があるときの制約とします。リニア制約とは、

ΣCoffA[i]*A[i]=[max,min]

という制約で表現できるときは、 リニア制約です。そのスタッフ内でクローズした制約、例えば、公休回数や、夜勤回数は、そのスタッフ内でクローズしているので、ペア制約にはなりません。スタッフ間で、上記制約で表現できない関係があるときに一般化ペア制約と定義します。

これによれば、ペア禁止は、一般化ペア制約ではありません。ペア禁止は、リニア制約で表現できるので、一般化ペア制約ではありません。また列制約の全ては、上記リニア制約で表現出来ます。

プリセプタプリセプティの関係は、リニアです。会議の制約で、シフトを専用に割り振れば、リニアになります。CCと新人の関係もそうです。

専用シフトを設けない会議、CCと新人、助産師と看護師がいて看護師のみペアの夜勤連続不可、といった制約も、一般化ペア制約の範疇ということになります。これらは、AL3/4では、苦手もしくは扱いきれない問題、ということが出来ます。

逆に言えば、専用シフト・タスクを設ければ、大半の問題をリニアに出来るということではないかと思います。





2023年12月28日木曜日

会議の日は、日勤者数を増やす ダイナミック版

というご要望があったので、とりあえずダイナミック版を作成してみました。

ポイントは二つあります。

会議は、ダイナミックにアサインしますが、その際に、日勤者を増員するというものです。

(1)インタラクティブに増員数を決める

増員する度合いが分からないので、そこは、インタラクティブに聞くダイアログを作成しました。pywin32でそのものズバリのダイアログがあったのでそれを使います。列制約で、日勤者数という制約があり、最小2以上を制約しています。それをソース全体から拾ってきて、増員数をインタラクティブに決めています。



(2)看護師長が会議ならば、日勤者数を増やす

これは、SoftLinerCompで実装できます。看護師長が会議という状態をブール変数vとして

Σv*CoffA <= ΣListB[i]*CoffB

とすれば、自由に増員できます。ここで、CoffAは、会議の日における増員後の日勤者数です。

看護師長に会議がアサインされないときは、0<=Σ

となり常に成立しますから、悪さはしません。

実装してみたら、そもそも会議で、日勤者が増えているので、制約することの程でもないような...というオチでした。



import sc3
import pywin.dialogs.list




def 会議の日は日勤者数を増やす(会議シフト名):
    min_day_workers=日勤数[8]#min
    print(min_day_workers)
    title=会議シフト名+'日は日勤者を何人にしますか?'
    s0='通常日勤者数'+str(min_day_workers)+'人に同じ'
    s1='増員1人 '+str(min_day_workers+1)+'人以上'
    s2='増員2人 '+str(min_day_workers+2)+'人以上'
    s3='増員3人 '+str(min_day_workers+3)+'人以上'
    s4='増員4人 '+str(min_day_workers+4)+'人以上'
    s5='増員5人 '+str(min_day_workers+5)+'人以上'
    s6='増員6人 '+str(min_day_workers+6)+'人以上'
    result=pywin.dialogs.list.SelectFromList(title, [s0, s1,s2,s3,s4,s5,s6])
    print(result)
    if result==None or result==0:#同じなら制約しなくてよい
        return
 
    for day in 今月:
        v=sc3.GetShiftVar(0,day,会議シフト名)#看護師長
        listA=[]
        listA.append(v)
        s=会議シフト名+'SLC '+daydef[day]
        mode=-1#mode=1 >=  mode=0 ==  mode=-1 <=
        CoffA=result+min_day_workers
        CoffB=1
        offset=0
        allowable_errors=3
        listB=[]
        for person in 日勤要員:
            v=sc3.GetShiftVar(person,day,'日勤')
            listB.append(v)
        print(daydef[day],CoffA,len(日勤要員))
        sc3.AddSoft(sc3.SoftLinearComp(mode,CoffA,CoffB,offset,allowable_errors,listA,listB),s,5)#level 5
    

会議の日は日勤者数を増やす('リーダ会議')
会議の日は日勤者数を増やす('副看護師長会議')

    

2023年12月27日水曜日

手動Updateの仕方

本ブログ等で、リリースしたとの情報があるにも拘わらず、アップデートされないときは、

以下の手順で更新します。 この手順がもっとも安定しているようです。

(1)ストアにマイクロソフトアカウントを開く







 (2) ストアにサインインする

(3)更新をクリックする


注意:

ストアをサインアウトすると使えません。使用時は、購入時のマイクロソフトアカウントでサインインしてください。

2023年12月25日月曜日

DEC252023BUILD内容

 買い切り版(DEC202023BUILD)内容に同じです。

現在審査中なのでリリースは年末だと思います。

   1)StaffPropertySheet frozen

スケジュールナースのブログ: 細かな改善 (schedule-nurse.blogspot.com)

2)add_fm_solution実装

スケジュールナースのブログ: 選択部を予定に送る (schedule-nurse.blogspot.com)

スケジュールナースのブログ: 「予定に送る」と「選択部を予定に送る」の違い (schedule-nurse.blogspot.com)

3)Python レベル7エラーの色が黄色ー>赤に修正

4)Excelからのインポートでロックが残ってしまう問題の修正

5)入力へ送るで、ロックが解除されるように修正

6)フィルタフォント修正

スケジュールナースのブログ: 細かな改善 (schedule-nurse.blogspot.com)

     7)予定シフト・タスクのみExcelに出力

スケジュールナースのブログ: 予定シフト・タスクのみをExcelに出力 (schedule-nurse.blogspot.com)

2023年12月24日日曜日

スタッフに忖度した勤務表作成の仕方

手動だと、どうしても、こういう作り方になると思います。

人生初の勤務表作成 勤務表作成に、これもありか、という考え方 (nurse-scheduling-software.com)

自動にしてから、それは無くなったとおっしゃっていましたので、自動の常識とあてはまらない世界にいたのだな、と思います。公平・公正・公明が当たり前で、そうあるべきと思っていたのですが、現場の管理者の実際は、もっとドロドロとした世界なのかな?と思います。少し反省し寄り添ってみました。


大学病院での講義ノートとセットでご覧ください

 スケジュールナース講義ノート

2023年12月23日土曜日

細かな改善

1)フィルタの文字が小さすぎる⇒小さくならないようにしました


2)コメント列までFrozen
コメント行が常に見えるようにしました

 いずれも年末のリリースから適用です

2023年12月22日金曜日

「予定に送る」と「選択部を予定に送る」の違い

 予定に送る時は、ロックを全て外します(DEC2023以降のビルド)

これは、次月への移行時のみ使う機能です。予定にロックが残っていると、スタッフ毎の予定でConlfictした予定をSoft化して回避している機構が働かないため、そのように変更しました。

一方、「選択部を予定に送る」は、未だ予定のEdit中を想定しています。そのためUndo/Redoスタックも動作しますし、ロックされたところには書き込めないのは、通常のコピペ動作と同じです。



2023年12月21日木曜日

5連勤の後2連休に制約追加

 制約終了日付近では、5連勤務になり易いです。結果、来月の最初、2連休の人が続出することがあります。そこで、制約終了日では、5連勤務を禁止を追加しました。





2023年12月20日水曜日

予定シフト・タスクのみをExcelに出力

予定関係だけをExcelにExportする機能を実装しました。
 Excelには、予定シフト・予定タスク・基数制約のみがExportされます。

ソフト制約も色でExportされるので、コピペで入力すれば、ソフト制約で入力することも出来ます。
スタッフの希望をExcelで取りまとめる部署もあると思うので、転記の手間が省けると思います。
これも12月Endでのリリース機能になります。

2023年12月19日火曜日

選択部を予定に送る

解のViewで挙動に違いがあります。 

タスク解:

選択されたタスク解が予定タスクに送られます。対応するシフト日のシフト解が予定シフトに送られます。

シフト解:

選択されたシフト外が予定シフトに送られます。対応する全てのタスク解が予定タスクにも送られます。

Hybrid解:

選択されたタスク解と選択されたシフト解のみが、シフト予定とタスク予定に送られます。

送られた予定Viewでは、取り消し・やり直し が出来ます。



適用は、12月Endです。


2023年12月18日月曜日

キャッチを変更

 スケジュールナース (nurse-scheduling-software.com)

を変更しました。人力からの移行は、勤務表作成サービスをご利用頂ければ、難しくはありません。しかし、一気に移行するには、不安もあるかもしれないので、人力でのやり方に近い勤務表作成の仕方を紹介したいと思います。

そのためのデモプロジェクトを用意しました。このデモから、「選択部を予定に送る」を新たに実装しました。




2023年12月17日日曜日

ソフトの手動更新

 自動更新されるので、通常必要ありません。ただし、アップデート時のサーバ間の配信の差によって、最新のプロジェクトファイルが読み取れないことも可能性としてはあります。


その場合は、ストアにサインインすると、更新が表示されますので、更新をクリックすれば最新となります。





スケジュールナースは、サブスクリプション、買い切り版、旧版からの更新のお客さま用

の3種類があります。

買い切り版は、マイクロソフトの審査が、自動で行われるようです。これは、買い切り版をストア上では、価格0にしている為のようです。(買い切り版はストアでは公開していません。)
こちらでリリース後、数時間で上記状態となります。その他の版は、土日祝(米国)休みの人力で審査が行われるようです。そのため、リリースそのものが、買い切り版より遅れるのが常です。当然、複数の審査者がいるので、ほぼ毎回、担当者が変わるためそのときの判断が毎回異なるという弊害があります。旧版からの更新のお客さま用は、リリース後、2週間程、英語でやり取りしても審査が通らなかったこともあります。毎回、一から議論を始めなくてはならず、とても疲れました。そのため、出来れば、旧版からの更新のお客さまは、買い切り版に移行したいと思っております。移行してもよい、と思われる方は、ご連絡頂ければ、ライセンスを発行しますので、ご検討ください。

2023年12月16日土曜日

シフト集合の改善

 次のパターンでは解がありません。

入集は、入りまたは単発入りですが、単発入りは、自動シフトでないために無視されます。

入りは、その前に長日をパターン上要求するために解なしとなります。

そこで、演算子を増やしました。または(非自動シフトを含)を追加しています。

ソフト制約でも同様に動作します。


リリースは、今週末位かと思います。




2023年12月15日金曜日

ユーザ論文

整理していたら、昔のユーザさまの論文が出てきました。

病院学会での発表内容らしいです。スケジュールナースのユーザさまは、病院の事務方も沢山、おられるので、こうしたテーマで発表していただけると、昨今の働き方改革に寄与すると思います。(他のソフトと比較検証していただくと、スケジュールナースの素晴らしさがより広まると思います。)

病棟勤務シフト表の作成自動化における効果と課題

ちなみに欧米では、看護師長が作るというよりは、事務方が機械で作るのが一般的だと思います。予定も、日本のように2週間~1週間前に決まると言う感じではなく、かなり前から決まっているのが普通だと思います。機械(ソフト)がつくるので、ある意味日本のように融通が効かない、ということはあります。そうしたシステムが成り立つのは、大病院化と派遣システムが充実しているからだと思います。



2023年12月13日水曜日

ストアにログイン

 ストアにログインできないと、サブスクも買い切り版も使えません。従い、マイクロソフトアカウントの確認として、ストアにログインできることを確認してください。



2023年12月10日日曜日

公休4連休を抑制したい

 Q.希望以外の連休が4連休、5連休2回ついています。ここはのちに手作業で日勤にすると他を休みに調整できるでしょうか?

Ans.

公休4回連続は禁止制約を追加しました。希望4連休が2回あるのは、ハード入力のため、そのままですが、それ以外の公休4連続以上は、禁止となります。
解の手直しが必要なときは、解を直接に直すのではなく、予定で制約、再求解するのが基本となります。
人手で行うと、どうしても制約無視が出てくる恐れがあるためです。 希望予定をロックし、さらに上記のような手入力をロックで確定していくことをお勧めしています。


2023年12月9日土曜日

主任の夜勤は出来る限り分散したい

 Q.     6日、20日の2回、主任さんが長日勤・夜勤に2名重なっています。苦肉の策でそうする場合はありますが、年に1回程度です(昨年度はゼロ、今年度は1回、人力解では。)。減らすことはできるでしょうか?


解説:主任というのは、副看護師長のことです。この病棟の場合3人いらっしゃいます。看護師長から見ると、「副看護師長は、あらゆる時間帯にいて欲しい。」ということらしいです。全体を見て廻してくれる、貴重な存在であるので、そのリソースは、できるかぎり広く使いたい、ということが背景としてあります。あるレベルのスタッフが何人以上 という制約は既に記述済みですが、それプラスの制約が必要ということです。

実装:列制約を追加しました。





2023年12月8日金曜日

Python制約プロセスとポストプロセスとの情報交換

 Pythonでの制約プロセスと、ポスト処理は、別プロセスです。制約プロセスで、A=1と書き込んでも、ポスト処理では、それを見事に忘れてしまいます。なので、情報交換する手段がありません。

これを回避する手段としては、制約プロセスとポスト処理プロセスで、同じ設定ルーチンを呼べば、同じ値で動作させることができます。

しかし、制約プロセスで、ユーザにインタラクティブに挙動を決定させた場合に、その結果をポスト処理で知ることは出来ません。そうした場合の処理方法についてです。

制約プロセスで取得した情報は、CSVで書きこみ、ポスト処理ではそのCSVを読む方法と取っています。



def set_advisor_method():
    global advisor_count_method
    advisor_count_method=1 #0→0 1→1 2→1 3→2 4→2
    #advisor_count_method=1 #0→0 1→0 2→1 3→1 4→2
    
    MessageBox = ctypes.windll.user32.MessageBoxW
    res=MessageBox(HWND, 'アドバイザカウント方法 3人(1人)で2人(1人)でカウント(推奨)する', 'アドバイザカウント方法', 3)
    if res==6:
        advisor_count_method=0
    write_csv('csv.csv')

def write_csv(file_name1):
    file_name =project_file_path+'/'+file_name1
    with open(file_name, 'w') as f:
        writer = csv.writer(f)
        list=[]
        list.append(advisor_count_method)
        writer.writerow(list)

def read_csv(file_name1):
    file_name =project_file_path+'/'+file_name1
    global advisor_count_method
    with open(file_name,'r') as f:
        reader = csv.reader(f)
        cnt=0
        for row in reader:
            print(row)
            if cnt==0:
                advisor_count_method=row[0]
            cnt +=1
        print('advisor_cnt_method',advisor_count_method)

def post_main():
    global advisor_count_method
    read_csv('csv.csv')
    check_comittee()



allowable_errors=3
global advisor_count_method
set_advisor_method()

2023年12月5日火曜日

師長不在の平日は、副師長1名は日勤にする

 看護師長または副看護師長が少なくとも1名とすればよいです。


AならばB
----------------------
A B   C
0 0 1 
0 1 1
1   0   0
1   1   1

題意からは、AならばBです。これは、Implication ~A|B となります。 A=看護師長不在なので、 a |B (a=~A ) aは在籍、となります。
「または」は、OR オペレータですが、数理的には、「少なくとも1個」 に等しいです。よって少なくとも1人を実装すればよい、ということになります。

aまたはB
----------------------
看護師長 副看護師長
x     1
1      x


2023年12月3日日曜日

組織の要件を満足しない要因

プロジェクトファイルを設計していると、組織の要件を満足しないケースが出てきます。伺っていた仕様に問題ガある場合もあるし休み希望が多すぎる問題もあります。経験した事項をまとめてみました。

 

1)物理的限界を超えている

入学式や、年末年始等、大量に発生する休み希望が、物理限界を超えると配置できません。これは、普段の日曜日でもあり得る話です。で、物理限界がどこにあるかは、ある程度計算で求めることはできます。具体的には、予定書き込み痔に、何人以上休みは入れられない、超えたらじゃんけん、というのが最も効率的かつ公平です。この辺の割り切りは、管理者として必要だと思います。


人生初の勤務表作成 算数ができない?師長 (nurse-scheduling-software.com)

変則2交代の場合

長日 10

入り 10

明け 10

明け休み 10

明けの後2連休 (10)

必要日勤数  10

日勤要員数   68

明けの後の2連休まで考えると、純粋な休みとして取得可能なのは、68-60=8人、

明けの後の2連休を考えないと18人 となります。明け休みが休みと被る場合も想定されますが、それは、マージンと考えればよく、目安になると思います。


2)若手の割合

「若手何人以上を同時に配置しない」 という制約は、よくある制約ですが、問題は若手の割合です。例えば、夜勤3人のうち、若手は、1人までという制約があるとすると、若手割合は、1/3以下である必要があります。夜勤要員24人として、若手の定義が8人を超えていたとすると、若手以外のベテランの夜勤回数が必然的に多くなります。こうした状況は、求解時間の増加を招くし、何よりも組織の要件を満足できなくなる可能性が高くなるので好ましくありません。配置制限割合以下に若手の定義を見直す必要があります。以上の議論は、夜勤6人、10人の職場でも同様です。

3)不規則シーケンス

変則2交代は、長入明が一定のパターンとなりますが、不規則シーケンスがあると決してフラットに出来ません。

長遅入明パターンが入ると人数をフラットに出来ない

一般化した変則2交代 (nurse-scheduling-software.com)

https://schedule-nurse.blogspot.com/2023/10/2.html

2)3)は制約設計時の問題で、制約が適切であれば、問題は生じません。1)は、毎月起こりえる問題なので、アラートシステムを備えておくとよいでしょう。次は、限界近くまで休み希望があった場合に警告を発します。


def 休み数警告(threshold):
    for day in 今月:
        off_cnt=0
        for person in 全スタッフ:
            if person in 看護師長:
                continue
            v=shift_schedules[person][day][0]
            if is_off(v):
                off_cnt +=1
        if off_cnt>=threshold:
            print('警告: ',daydef[day],'休み数が',off_cnt,'になっています')

4)過渡なスタッフ間の公平化(フラット化)

スタッフ間の公平、例えば夜勤数は、最大・最小に少し余裕がないと、求解に時間がかかったり、最悪、組織の要件を満足できなかったりします。

5)遊びがあること
組織要件に、遊びがあることが必要です。一般に、休日日勤者の数は、固定でよいのですが、日勤者の数は、最小のみ、最大があったとしても、余裕があるようにします。

2023年12月2日土曜日

翌月の予定を取り込んだ制約

Q. 現在、添付の様なデータになっているのですが、

作成する際に翌月数日間の予定入力を考慮して作成することは可能でしょうか?

但し、勤務日数は今月内だけとして数えます。


例えば、作成したデータでは今月末で4連勤となっており、

5連勤は禁止しているが、翌月の1日は他の方の希望休が多いことが分かっており、

どうしても5連勤になってしまうような場合、初めから月末を4連勤にならないようにしたい。


また、夜間の拘束が連続しないようにしたいが、

翌月1日はAさん以外の3名が拘束できないことが分かっている場合は、

当月末日の拘束はAさんにつかないようにしたい場合などです。


何か解決する方法はありますでしょうか?

Ans.

確かに、月末は、連続勤務になりやすいと思うので、

添付のように、月末4連禁不可を定常的に組み込むのもアリだと思います。

>作成する際に翌月数日間の予定入力を考慮して作成することは可能でしょうか?
難しいです。理屈的には、翌月数日を含んだプロジェクトを作成するこも可能ですが、今度は、またその範囲を超えたところの対応が必要になるので、変更する労力に見合った効果は得にくいと思います。

現実的な対応としては、予め月末予定に制限を加える、もしくは、翌月の予定書き込み時に物理制限を設け、じゃんけんで決める、等になろうかと思います。(物理制約を越えて配置することは所詮無理なので。)

2023年12月1日金曜日

Python Soft LinearComp

 Python SoftLinearComp (nurse-scheduling-software.com)

SoftLinearCompは、GUIペア制約のリニアペア制約の内部実装関数です。比較関数は他にもありますが、これ一つで全てを包含します。