2020年2月15日土曜日

夜勤新人月が終わったら

夜勤新人月が終わった後、また来年も使うかもしれませんので、メンテナンスしておきましょう。

実は、訓練期間が、下のようにブランクだと、右ペインに示されるようにエラーになってしまいます。
原因は、行制約で訓練期間では、2回夜勤を行うという制約をハード制約として記述したからです。
単純にソフト制約とすれば回避できますが、ソフトエラーとしてカウントされるのが面白くない、という方もおられでしょう。それが今日のテーマです。


訓練期間が定義されていないときは、制約そのものをDisableしてしまえばよい、という発想が浮かびます。下の最後の方、 夜勤新人Disable()が制約をDisableする記述です。僅か3行で済みます。

スタッフプロパティで夜勤新人を設定、上で期間設定を行う、ということをマニュアルに書いておけば、来年もまた使えます。

import sc3

def 夜勤回数設定sub(person):
    st=staffdef[person]+'夜勤回数を制約'
    sc3.print(st+'します。\n')
    max=-1
    min=0
    if person in 夜勤回数最大属性:
        max=夜勤回数最大属性[person]
    if person in 夜勤回数最小属性:
        min=夜勤回数最小属性[person]

    sc3.print(str(max)+str(min)+'\n')
    vlist=[]
    for day in 今月:
        v=sc3.GetShiftVar(person,day,'入り')
        vlist.append(v)
    sc3.AddSoft(sc3.SeqError(min,max,3,vlist),st,5)

def 夜勤回数設定():
    for person in 夜勤あり:
        夜勤回数設定sub(person)
   


def 公休数設定():
    for person in 全スタッフ:
        max=-1
        min=0
        if person not in 公休数属性最大:
            continue
        st=staffdef[person]+'公休数を制約'
        sc3.print(st+'します。\n')
        if person in 公休数属性最大:
                max=公休数属性最大[person]#float
        if person in 公休数属性最小:
                min=公休数属性最小[person]
        max *=2
        min *=2
        if isinstance(max, float) and not max.is_integer():
            sc3.print('公休数設定で、maxが整数型ではありません。')
            continue
        if isinstance(min, float) and not min.is_integer():
            sc3.print('公休数設定で、minが整数型ではありません。')
            continue
        max =int(max) #float to int
        min =int(min) #float to int
        sc3.print(str(max)+str(min)+'\n')
        vlist=[]
        for day in 今月:
            v=sc3.GetShiftVar(person,day,'公休')#公休は2回分カウント
            vlist.append(v)
            vlist.append(v)
            v=sc3.GetShiftVar(person,day,'早半休')#早半休は1回
            vlist.append(v)
            v=sc3.GetShiftVar(person,day,'半休')#半休は1回
            vlist.append(v)
        sc3.AddSoft(sc3.SeqError(min,max,4,vlist),st,6)

def 土曜以外の半休抑制():
    for person in 全スタッフ:
        for day in 土曜でない今月:
            ts=shift_schedules[person][day][0]
            if ts !='半休':
                v=sc3.GetShiftVar(person,day,'半休')
                st=staffdef[person]+'土曜以外の半休抑制'
                sc3.AddSoft(~v,st,1)

def 夜勤新人Disable():
    if len(夜勤新人訓練期間)==0:
        sc3.ConstraintEnable('夜勤新人.夜勤新人は、夜勤訓練期間に2回訓練を行う',False)


夜勤回数設定()
公休数設定()
土曜以外の半休抑制()
夜勤新人Disable()
 
夜勤新人訓練期間がなくても意図通り動作しました。
 
期間を日月火水木にした例です。
 

2020年2月14日金曜日

夜勤新人の最初の2回は、プリセプター付

4月5月は、看護師の移動が多い月です。看護師としてはベテランでも、
その病棟にとっては夜勤新人という方が多いかもしれません。そのような状況を想定し、頑張って制約化してみましょう。 (勿論、制約化しないで予定として入れてしまうのもアリです。)

仕様は、最初の2回は、先輩看護師付で、以降は、通常ルーチンとします。最初の2回の期間を考えて、夜勤新人訓練期間を定義します。月の前半を訓練期間としましょう。


月前半 訓練期間
 夜勤新人が夜勤する場合は、2+1人勤務
月後半 通常勤務
夜勤新人が入っても2人勤務

今月を全体集合として、上の補集合を求めましょう。
スタッフプロパティで夜勤新人を定義します
夜勤新人でないグループを定義します。
夜勤新人は、訓練期間に2回訓練を行う制約を追加します。
列制約です。
解です。2人の夜勤新人が訓練期間中に各々2回夜勤を行っています。
この期間は、プリセプティ分、夜勤者が増えて3人となります。月後半は、通常ルーチンに戻り、
夜勤新人に関わらず2人夜勤となります。かなり複雑な仕様ですが、GUIのみで記述できました。
 
 


2020年2月13日木曜日

男同士の夜勤をさせない

ペア設定で書きたくなりますが、A/B とも同じ集合(男)だと、動作しないと思います。
これは、ペア設定ではなく、

Σ男 <=1

と制約すれば、2名以上の男性が勤務することはありません。よって、スタッフプロパティで、性別を指定し、次のように記述します。


2020年2月12日水曜日

2020年2月10日月曜日

xbyakによる実装

Xbyakは、Intelのアセンブリ言語命令を実行時に生成するC++のライブラリです。
今の時代、LLVMがあるので、C++コードのJITコンパイルも可能になっており、その必要性は狭まってはいますが、Xbyakで記述した方が手軽な場合もあります。

例えば、IntelのMKL-DLLという深層学習用の高速化ライブラリがありますが、そのエンジンには、Xbyakが使われているそうです。

https://blog.cybozu.io/entry/2019/04/15/170000

またスパコンCPU aarch64(ARMの64ビットアークテクチャ)にも移植されているようです。

Xbyakの概要はこちら

今回の使用目的は、機械・深層学習用ではなくて、AL4の高速化です。AL4は、数理的ソルバーですが、部分問題を解く必要があり、そのための新しいアイデアに対する実装に使用します。
コンパイル時定数であれば、C++テンプレートでC++コンパイラが勝手に最適化してくれますが、RUNTIME時定数では、テンプレートは何の役にも立ちません。RUNTIME時定数というメリットを最大限使って高速化を図ります。Xbyakの使用は、初めてで、SSE2も初めてなので、初めてのXbyakによるSSE2ということで、実装中のコードを使用して、この先書きたいと思います。

2020年2月9日日曜日

リーダ1日以上4日以下

下記は、スニペットです。GUIだけで記述できました。







平準化のため、各スタッフ6日以下としていますが、Pythonを使えば、この部分もダイナミック化
が可能です。ダイナミック化とは、動的に例えば6という数字を決定することです。元々、6というのは、

 リーダ回数=月の日数/リーダ可能者人数+1

で割り出したはずです。ところが、月の日数は変わるし、リーダ可能者も、スタッフプロパティに現れなくて出来ない人(実習等で予定書き込み)もいるかもしれません。その場合でも、予定を集約すれば、より精度の高い平準化が可能になります。同時に、将来のリーダ可能者の増減にも自動で対応可能となります。ロバストな勤務表づくりの要点は、そういった将来の人数の増減まで考えることです。

2020年2月7日金曜日

予定の入っていないところだけ制約する

前回から続きプロジェクト実装です。
確かに、公休数11.5を実現するのに、全休10回、半休3回(長坂さん)でよい雰囲気です。

しかし、例えば横山さんの公休数11を実現するために、全休8日、半日が6日もアサインがされてしまっていて問題です。半日には、何ら制約が入っていないために、こういう解が出てしまいます。

そこで、土曜日以外は、なるべく半日をアサインしないように制約したくなるのですが、元々予定が入っているところは、意味がないので外すことにします。(ソフト制約は、ソルバーにとって重いのでなるべく少なくしたいという意図もあります。)
土曜日だけ半日→土曜以外は半日禁止→今月土曜以外は、半日禁止 というロジックにより

def 土曜以外の半休抑制():
    for person in 全スタッフ:
        for day in 土曜でない今月:
            ts=shift_schedules[person][day][0]
            if ts =='':
                v=sc3.GetShiftVar(person,day,'半休')
                st=staffdef[person]+'土曜以外の半休抑制'
                sc3.AddSoft(~v,st,1)
shift_schedules[person[day][0] に予定が入っていればシフトが入ってきます。
空白のときだけ、半休がアサインされ、レベル1のペナルティを与えるコードになっています。
このコードをEnableすると、土曜日だけ半休がアサインされて下の結果となります。

 
GUIでは、静的制約しかできませんが、Pythonを使えば、上のようにダイナミックに制約することが出来ます。

2020年2月6日木曜日

公休数10.5日をどう記述する?

時間制約でも記述できます。この場合、スタッフプロパティで 8.5/9/9.5/10/10.5/11...に対応するプロパティを用意して、それぞれ時間制約で制約すればよいです。

Pythonで記述すること考えたのですが、プロパティファイル生成時、浮動小数に対応していないという問題が発生しました。そこで、以下のように正規表現を使って浮動小数に対応しました。(ソルバ C++)を変更しました。(対応Versionは、127C)



bool check_int(std::string str)
{
#ifdef FEB052020
 regex re("[0-9]+(\.[0-9]*)?|[0-9]+");
 if (regex_match(str, re)) {
  return true;
 }else return false;
#endif
 
127C以降では、次のようにPythonで記述できます。
 
 半休は、1回、公休(1日休み)は、2回カウントしてやると、0.5が整数になります。なので、min/maxを2倍にしてやります。これで時間制約の記述なしにpythonで、浮動小数の公休数に対応しました。
なお、float型かint型かの判定をしたあと、float型で,かつまだ浮動小数があるならメッセージを出しています。
 
def 公休数設定():
    for person in 全スタッフ:
        max=-1
        min=0
        if person not in 公休数属性:
            continue
        st=staffdef[person]+'公休数を制約'
        sc3.print(st+'します。\n')
        if person in 公休数属性:
                max=公休数属性[person]#float
                min=公休数属性[person]
        max *=2
        min *=2
        if isinstance(max, float) and not max.is_integer():
            sc3.print('公休数設定で、maxが整数型ではありません。')
            continue

        max =int(max) #float to int
        min =int(min) #float to int
        sc3.print(str(max)+str(min)+'\n')
        vlist=[]
        for day in 今月:
            v=sc3.GetShiftVar(person,day,'公休')#公休は2回分カウント
            vlist.append(v)
            vlist.append(v)
            v=sc3.GetShiftVar(person,day,'早半休')#早半休は1回
            vlist.append(v)
            v=sc3.GetShiftVar(person,day,'半休')#半休は1回
            vlist.append(v)
        sc3.AddSoft(sc3.SeqError(min,max,4,vlist),st,6)

2020年2月3日月曜日

2020年2月2日日曜日

ORのレクチャー

目的関数値とか、スケジュールナースのマニュアルには、時々OR用語が出てきますが、
良い、解説がありました。

http://www.robot.t.u-tokyo.ac.jp/dcm/lec_opt/lec01.pdf

http://www.robot.t.u-tokyo.ac.jp/dcm/lec_opt/lec02.pdf

http://www.dais.is.tohoku.ac.jp/~shioura/teaching/mp11/mp11-06.pdf




見て分かるように、ナップザック問題や最短経路問題は、ナーススケジューリングにおいても重要な部分問題であり、この性能の良し悪しが全体のボトルネックになります。数理最適化において最も重要な解法は、分岐限定法です。分岐限定法は、ポイントが二つあり、ノードの選択とと切除平面です。前者については、Reserchgateの回答がエッセンスです。
https://www.researchgate.net/post/Branch_and_Bound_Algorithm_How_to_choose_variables_in_case_of_more_than_one_fractional_values

切除平面については、色々な方法がありますが、最も基本的なものは、小数カットと呼ばれる方法です。

http://www.orsj.or.jp/~archive/pdf/bul/Vol.48_12_936.pdf
http://infoshako.sk.tsukuba.ac.jp/~maiko/mathpro/n_note2-2.pdf
http://dopal.cs.uec.ac.jp/okamotoy/lect/2013/opt/handout06.pdf
http://www.msi.co.jp/nuopt/glossary/term_167e2968d79e57b147ffb376f70159d277feacb7.html

2020年1月26日日曜日

勤務表づくりは簡単 ではない

>4月から看護師長になります。勤務表を作ることになりました。作成の労力をなくしたく..

というメールを頂きました。それに対する回答を以下に書きます。

本ソフトは、高額ですので個人でお求めになられる方は、殆どおられません。
10年分割(1万1千円消費税込み/年)でも構いませんが、それ以前に、いくつか注意点がございます。

1)Windows PCで動作することをご確認お願いします。
https://nurse-scheduling-software.com/publications/introduction.pdf

2)職場のルールを記述して頂く
https://nurse-scheduling-software.com/publications/SC3_introduction_procedure2.pdf
です。

勤務表づくりは、どのようなソフトを使ったとしても簡単ではありません。簡単ではない、というのは、ソフトの問題ではなく、勤務表作成そのものが、多くの事項について考慮を要する知的作業である、ということです。その知的作業自体は、ソフトを使う・使わないに関係しません。
まずは、ソフトを使わずに、何ヶ月か作成してみることをお勧めします。その上で、ソフトを試用してみたいということでしたら、
1)2)を行って仕様を作成ください。仕様を頂いてこちらでプロジェクトファイルを作成します。
3ヶ月間程度、やり取りしながらプロジェクトファイルをブラシュアップしていくと、ほほ意のままに使えるようになります。

以上、ご検討の程よろしくお願い申し上げます。

続き:
>職場のルールをメールで送信でよろしいでしょうか?

はい、そうです。出来ましたら、勤務表実績をExcelで頂けましたらBetterです。

下記17ページ付近をご覧ください。現実実績(人力解)が、お送り頂いたルールにどのようにマッチしているか(していないか)が共通認識の出発点になります。多くの方に見られる間違いが、実績ではなく願望を書いてしまうことです。
プロジェクトファイルが固まった後は、自由にトライされてよいのですが、最初からそれをやってしまうとお客さま中で、混乱してしまうことが多いです。
まずは、現実実績をきちっと見据えることを推奨しています。(そうした作業の中で、本当にあるべきルールが見えてきます。)
https://nurse-scheduling-software.com/publications/SC3_introduction_procedure2.pdf

2020年1月25日土曜日

ナーススケジューリング

という池上先生の著作を読み返しています。この本は、研究エッセイに見えて、それでいて学術書としての面をしっかり抑えていて、私のような工学的にナーススケジューリングソルバを開発している者にとっては座右の書です。ネットワークで、解空間を表現しようというのは、他の論文では見ない先進性をもっていると思いますし、人間を中心においた真のモデリングの難しさに触れています。また、実践的な課題について語っていて共感するところ多々であります。

2020年1月22日水曜日

準夜とロング日勤の数を同じくしたい

職員毎に入院基本料の時間を揃える関係で、「準夜」と「ロング日勤」は数を揃えるようにシフト作成しているのだそうです。

そういう場合のスニペットです。

import sc3
for person in 全スタッフ:
    夜勤list=[]
    ロング日勤list=[]
    for day in 今月:
        夜勤list.append(sc3.GetShiftVar(person,day,'夜勤'))
        ロング日勤list.append(sc3.GetShiftVar(person,day,'ロング日勤'))
    sc3.AddSoft(sc3.SeqComp(夜勤list,ロング日勤list),'夜勤とロング日勤は等しい',5)
 
夜勤○ とロング日勤ロ の数が等しくなっています。

2020年1月19日日曜日

ラグランジェ緩和再び

新たな発想で再度トライしています。ヒントになったのは、

http://www.bunkyo.ac.jp/~nemoto/lecture/opt-model/2008/duality1-2007.pdf

https://www.jstage.jst.go.jp/article/bjsiam/23/3/23_KJ00008829092/_pdf/-char/ja

https://slidesplayer.net/slide/11198759/

https://www.amazon.co.jp/dp/4320016475/ref=rdr_ext_tmb

です。特に、久保先生の本は、具体的な例題の積み重ねから一般論を導く方式で高度な概念を説明されていて本当に役に立ちます。最適化の本は10冊以上ありますが、この本が一番良いです。

ところで、問題は、規模が大きくなったときにメモリが大きくなりすぎることですが、大体目処がつきました。長い思索が必要でした。

一つの方式を採ったときに、性能はともかくとして、必要なのは汎用性です。「この問題は解けるけども、別な問題では、解が出ない、」では困ります。そこを何とかする必要がありました。どんな問題でも一応は解けるという、汎用性が商用ソルバでは重要となります。

実装は未だですが何とかなるでしょう。(で、やってみると性能が出ないというのが常ですが。)

それが、解決したとして、実務問題への展開には、最後の障害があります。一言でいうとペア制約に関する問題です。よく経験するのは、ペア制約でも、AさんとBさんが一緒に勤務しない、というのは、殆ど影響がなく容易に解が見つかりますが、Aさんが勤務したらBさん(A→B)、あるいは、その逆(B→A)、あるいはその両方(A⇔B)となると、途端に解が難しくなります。この辺、解空間で考えて本当に空間が狭まっているのか、理論的に解析された論文はありません。試しに、ペア制約を外してみてください。簡単に解を見つかるケースが多いことでしょう。理論的にも興味深い問題です



2020年1月16日木曜日

ドメイン移転

長年に渡って使っていたサーバ管理業者が全く連絡が取れなくなってしまいました。毎年契約更新の連絡が来ていたのですが、同時期になっても連絡が来ず、不安になってネット情報を見ると不安が的中してしまいました。最後まで連絡が取れない場合、メールサーバが使えなくなる可能性が生じてしまいました。NurseScehdulingのウェブは、既にxxxxxに移したので問題ないのですが、sugawara-systems.com のドメインが現在、身動きが取れません。最悪メールアドレスが変更になってしまう可能性があります。その際は、WEBも含めて告知します。ドメインの方は、来年まで有効であり、多分来年までは大丈夫だと思います。(ただ、管理業者が上の状態なのでいつメールサーバがダウンするとも知れません。)

続き

WEBに記載されている電話番号にTELしたら、現在使われておりません、でした。
消費者センターに相談したら、東北総合通信局を紹介されたので、そちらに相談してみます。

続き
ドメイン管理者は、総務省の管轄ではない、との回答でした。まあ、役人の仕事はそんなレベルでしょう。

続き
電気通信事業者ということで総務省の管轄にはなっているが、総務省では何も出来ない、とのことでした。

残念ながら、打つ手がなくなりましたので、新しいドメインを採ることにします。メールアドレスが変わることになります。

2020年1月11日土曜日

127Bをリリースしました

ペア設定をオフにしているのにオンと認識してしまうバグを修正しました。(127Aで入り込んでしまったようです。)

年末年始AL4の改善に取り組んだのですが、上手くいきませんでした。もう2年近くこの問題に取り組んでいるのですが未だ解決できていません。ですが、これを解決することなしには、世界に出て行けません。もう少し頑張ってみます。