2019年12月26日木曜日

よくある誤解

https://www.qmedia.jp/misunderstanding-of-qc/

とてもよくまとまっていると思います。
https://www.aist.go.jp/aist_j/press_release/pr2018/pr20181009/pr20181009.html
こちらについても懐疑的な印象を持っています。

二つの課題があると思います。
1)厳密解を出せない
SimulatedAnnealingに限らず、MetaHeuristicsによる解法は、基本的に厳密解を出せません。私の知る限り、厳密解を出せるのは、数理的解法か、SAT解法かのどちらかしかありません。(
その両方を備えたソルバがSC3です。)
2)解がない原因を示せない
制約モデルの設計・開発において、ハード制約とソフト制約を分けて設計を行うのは、人間が行う上では、必須であると思います。ハード制約の解がない原因を知る機構が設計においては、必須になります。(これも私の知る限りSC2/SC3のみの機能です。)

そもそも、通常のNSP問題においても、組み合わせ解空間が宇宙の素粒子数を超えることはよくあります。それでいて厳密解が求まる場合もあります。また、巡回セールスマンの世界記録も数理解法によるものです。同じベンチマークで論じるべきだと思います。

今年は、曲がりなりにもSC3をリリース出来、長年実現できなかったNSP記述の一般化形態をリリース出来ました。お正月にかけて、良い結果を出せるようにソルバAl4の改善を行います。

本年度は、業務終了です。ありがとうございました。

2019年12月25日水曜日

Simulated Annealing 実装の参考

こちらが、参考になります。
https://jetbead.hatenablog.com/entry/20180120/1516387673

いずれにしても問題は、NSPで近傍をどのように表現するかではないかと思います。
こちらも先人の知恵を拝借したいと思います。

2019年12月24日火曜日

D-wavesを用いたNSP

論文が公開されています。残念ながら、既存の方法との比較がないので、評価のしようがありません。

一方
https://www.itmedia.co.jp/news/articles/1911/20/news149.html
https://www.itmedia.co.jp/news/articles/1906/03/news033_3.html

のように否定的な記事もあります。

2019年12月23日月曜日

SchedulingBenchmarkサイトが更新

NurseSchduling問題の他に、Retail問題が追加されています。これが線形モデリングならば、フェーズタスク形式に記述することでSC3でも解けるはずです。しかし、残念ながら、線形モデリングではなく、2次形式によるモデリングなので、線形ソルバでは解けません。

NurseScehduling問題の方は、Instance20の最新BestValueが記録更新されています。しかし、私のソルバは、LB・UB既に確定させており、真の厳密解は、4769です。報告すれば、認められるでしょうが、英語サイトが準備出来ていないこともあり未だ報告していません。SC3による日本独自のベンチマークと、SchedulingBenchmarks及びNRC2のベンチマーク結果が整備出来た後の公開になります。その意味で、Algorithm4の改善は必須です。 鋭意改善中です。

2019年12月22日日曜日

シミュレーテッドアニーリングサーヴェイ

とりあえず勉強中です。

http://www.orsj.or.jp/~archive/pdf/bul/Vol.40_05_268.pdf

メタヒューリスティクスは、これまでのところ実装する必要を感じていません。コンペティションでの結果を見ても、超大規模インスタンスを除き、メタヒューリィスティクス系ソルバは、IntegerProgramming CPLEX/Gurobiには適わないという結果になっています。
しかし、問題によっては、より良い結果を生む可能性を否定はできません。これを実装するとSAT系、数理系にプラスすることになり、ソルバーとしての価値が高まるかもしれません。少し研究してみようと思います。


2019年12月21日土曜日

ランダム予定生成機能

127Aから上記をサポートしています。例えば、正循環と深準型で、どちらがロバストな制約パターンは、予定負荷をかけて評価するしかありません。実休みパターンを数種類用意すれば、良いのですが、それも面倒なときは、ランダムに予定生成する機能を使います。ただしランダムとは言っても、休みとそうでないときでは、一定の法則性があると思うので、それぞれの曜日用に生成するようにしています。(単純なランダムでは、全く意味がありません。例えば、5日希望まで許されている職場で、平日休日問わずにランダム生成すると、意外なほど耐性がなく実態と乖離が生じます。
5日希望がまがりなりにも成立しているのは、休日には、休み希望が集中するという法則性に助けられている側面があります。)
曜日集合の上から4番目までを選択することができます。制度設計用にご活用ください。

2019年12月20日金曜日

127Aをリリースしました

マニュアルも更新しました。
Python制約プログラミングマニュアルも更新しました。

127Aでは、GUIの制約コントロールをPythonで行っています。
今後は、Algorithm4の改善を行う予定です。
来年は、AWS検討を行う予定です。

2019年12月19日木曜日

Python ctypesでメッセージボックスを表示

任意の制約をEnable/Disable出来るようになったので、デモを作りました。
127Aから、32bit/64bit共に、ctypes を添付したので、Win32のAPIを呼ぶことができます。
python ctypesについては、こちらが詳しいかと

デモは、正循環パターンと深準夜パターンをGUIで切り替えます。ソースは以下です。
import sc3
import ctypes
MessageBox = ctypes.windll.user32.MessageBoxW
res=MessageBox(None, '正循環パターンにしますか?', 'パターン選択', 3)

#sc3.print(str(res))
if res==6:
    sc3.ConstraintEnable('正循環')
    sc3.ConstraintEnable('深準パターン',False)
    sc3.print('行制約正循環をEnableしました。')
else:
    sc3.ConstraintEnable('正循環',False)
    sc3.ConstraintEnable('深準パターン',True)
    sc3.print('深準パターンをEnableしました。')

制約自体は、正循環も深準パターンもGUIで記述しています。制約グループの切り替えをPythonで行っている、ということです。 下がログになりますが、Propertyファイルの生成した後、Python記述が評価されGUIダイアログが表示されます。ユーザがボタンを押すとYES/NOが戻ってきて、後は、制約のオンオフを行っているだけす。
コンパイルの準備中ソルバを呼び出し中です。
 python propertyファイル生成を開始します。
 python propertyファイル生成が終了しました。
行制約正循環をEnableしました。
 Algorithm 1 Solving Process Started.
   o  1429055   2.02(sec) 
c    Bloop 1
   o  1409152   2.036(sec) 
   o  1399055   2.056(sec) 
   o  1319057   2.069(sec) 
   o  1298952   2.082(sec) 
   o  1288855   2.094(sec) 
   o  1278750   2.105(sec) 
   o  1238553   2.116(sec) 
   o  1168560   2.128(sec) 
   o  1158463   2.14(sec) 
   o  1138366   2.152(sec) 
   o  1128263   2.164(sec) 
   o  1088066   2.175(sec) 
   o  1067966   2.187(sec) 
   o  1057969   2.199(sec) 
   o  1047862   2.215(sec) 
   o  1027765   2.226(sec) 
   o  1017668   2.238(sec) 
   o  1007569   2.25(sec) 
   o   997474   2.261(sec) 
   o   987471   2.272(sec) 
   o   967368   2.283(sec) 
   o   957365   2.296(sec) 
   o   937268   2.309(sec) 
   o   897171   2.321(sec) 
   o   877074   2.333(sec) 
   o   857071   2.345(sec) 
   o   846974   2.356(sec) 
   o   836877   2.368(sec) 
   o   826883   2.379(sec) 
   o   806877   2.39(sec) 
   o   786778   2.401(sec) 
   o   766778   2.413(sec) 
   o   736784   2.424(sec) 
   o   726687   2.437(sec) 
   o   716590   2.449(sec) 
   o   706395   2.462(sec) 
   o   696298   2.474(sec) 
   o   676199   2.486(sec) 
   o   656199   2.499(sec) 
   o   646102   2.51(sec) 
   o   636005   2.523(sec) 
   o   616005   2.533(sec) 
   o   595908   2.544(sec) 
   o   585796   2.557(sec) 
   o   565696   2.569(sec) 
   o   545598   2.581(sec) 
   o   535598   2.593(sec) 
   o   525598   2.608(sec) 
   o   505498   2.619(sec) 
   o   485398   2.631(sec) 
   o   475298   2.643(sec) 
   o   465298   2.656(sec) 
   o   455298   2.668(sec) 
   o   445284   2.682(sec) 
   o   435284   2.695(sec) 
   o   425284   2.706(sec) 
   o   415386   2.719(sec) 
   o   395285   2.731(sec) 
   o   385188   2.743(sec) 
   o   365182   2.754(sec) 
   o   355182   2.766(sec) 
   o   345191   2.78(sec) 
   o   335191   2.791(sec) 
   o   325191   2.802(sec) 
   o   314988   2.816(sec) 
   o   304791   2.828(sec) 
   o   294794   2.84(sec) 
   o   284591   2.852(sec) 
   o   274591   2.864(sec) 
   o   264488   2.875(sec) 
   o   254485   2.887(sec) 
   o   244388   2.899(sec) 
   o   234276   2.928(sec) 
   o   224176   2.939(sec) 
   o   214076   2.951(sec) 
   o   204082   2.963(sec) 
   o   193982   2.975(sec) 
   o   183882   2.987(sec) 
   o   173782   2.999(sec) 
   o   163680   3.011(sec) 
   o   153580   3.023(sec) 
   o   143580   3.034(sec) 
   o   133480   3.045(sec) 
   o   123472   3.059(sec) 
   o   113372   3.073(sec) 
   o   103272   3.085(sec) 
   o    93172   3.097(sec) 
   o    83078   3.109(sec) 
   o    72978   3.121(sec) 
   o    62878   3.132(sec) 
   o    52778   3.143(sec) 
   o    42672   3.155(sec) 
   o    32574   3.166(sec) 
   o    22568   3.179(sec) 
   o    12366   3.192(sec) 
  c output terms=0 weight=10000
   o    12366   5.124(sec) 
c    Bloop 1
   o    12272   5.139(sec) 
   o    11766   5.197(sec) 
   o    11666   5.207(sec) 
   o    11566   5.218(sec) 
   o    11466   5.228(sec) 
   o    11254   5.239(sec) 
   o    11154   5.252(sec) 
   o    11054   5.263(sec) 
   o    10954   5.274(sec) 
   o    10854   5.285(sec) 
   o    10436   5.307(sec) 
   o    10230   5.318(sec) 
   o    10130   5.329(sec) 
   o    10030   5.339(sec) 
   o     9840   5.352(sec) 
   o     9740   5.363(sec) 
   o     9640   5.373(sec) 
   o     9540   5.385(sec) 
   o     9242   5.404(sec) 
   o     9142   5.414(sec) 
   o     8942   5.425(sec) 
   o     8736   5.436(sec) 
   o     8636   5.447(sec) 
   o     8536   5.466(sec) 
   o     8436   5.478(sec) 
   o     8236   5.49(sec) 
   o     8136   5.501(sec) 
   o     8036   5.521(sec) 
   o     7850   5.531(sec) 
   o     7750   5.543(sec) 
   o     7648   5.556(sec) 
   o     7548   5.566(sec) 
   o     7450   5.577(sec) 
   o     7350   5.588(sec) 
   o     7252   5.602(sec) 
   o     7146   5.618(sec) 
   o     7032   5.648(sec) 
   o     6918   5.661(sec) 
   o     6818   5.672(sec) 
   o     6712   5.684(sec) 
   o     6512   5.7(sec) 
   o     6412   5.717(sec) 
   o     6312   5.729(sec) 
   o     6212   5.745(sec) 
   o     6112   5.771(sec) 
   o     6012   5.782(sec) 
   o     5912   5.793(sec) 
   o     5806   5.804(sec) 
   o     5700   5.815(sec) 
   o     5600   5.826(sec) 
   o     5502   5.837(sec) 
   o     4998   5.852(sec) 
   o     4892   5.866(sec) 
   o     4792   5.878(sec) 
   o     4686   5.891(sec) 
   o     4586   5.902(sec) 
   o     4374   5.914(sec) 
   o     4168   5.926(sec) 
   o     4058   5.937(sec) 
   o     3952   5.952(sec) 
   o     3852   5.963(sec) 
   o     3752   5.975(sec) 
   o     3652   5.986(sec) 
   o     3552   5.998(sec) 
   o     3452   6.009(sec) 
   o     3346   6.02(sec) 
   o     3246   6.032(sec) 
   o     3146   6.042(sec) 
   o     3046   6.053(sec) 
   o     2948   6.07(sec) 
   o     2848   6.081(sec) 
   o     2636   6.094(sec) 
   o     2538   6.106(sec) 
   o     2444   6.119(sec) 
   o     2344   6.13(sec) 
   o     2244   6.141(sec) 
   o     2144   6.151(sec) 
   o     1944   6.179(sec) 
   o     1844   6.19(sec) 
   o     1746   6.205(sec) 
   o     1646   6.217(sec) 
   o     1546   6.228(sec) 
   o     1440   6.239(sec) 
   o     1340   6.251(sec) 
   o     1240   6.262(sec) 
   o     1140   6.273(sec) 
   o     1036   6.285(sec) 
   o      930   6.296(sec) 
   o      830   6.307(sec) 
   o      638   6.318(sec) 
   o      538   6.329(sec) 
   o      432   6.34(sec) 
   o      332   6.351(sec) 
   o      222   6.363(sec) 
  c output terms=0 weight=100
   o      222   8.247(sec) 
c    Bloop 1
   o      216   8.293(sec) 
   o      204   8.311(sec) 
   o      188   8.325(sec) 
   o      182   8.337(sec) 
   o      164   8.371(sec) 
   o      158   8.381(sec) 
   o      152   8.393(sec) 
   o      136   8.403(sec) 
   o      130   8.414(sec) 
   o      122   8.425(sec) 
   o      114   8.437(sec) 
   o      108   8.447(sec) 
   o      102   8.458(sec) 
   o       99   8.468(sec) 
   o       93   8.478(sec) 
   o       74   8.491(sec) 
   o       66   8.507(sec) 
   o       60   8.521(sec) 
   o       57   8.532(sec) 
   o       53   8.544(sec) 
   o       47   8.555(sec) 
   o       44   8.582(sec) 
   o       36   8.594(sec) 
   o       33   8.604(sec) 
   o       30   8.614(sec) 
  c output terms=0 weight=3
   o       30   10.512(sec) 
c    Bloop 1
   o       28   10.534(sec) 
   o       26   10.548(sec) 
   o       24   10.576(sec) 
   o       20   10.615(sec) 
   o       18   10.626(sec) 
   o       16   10.64(sec) 
   o       14   10.653(sec) 
   o       12   10.664(sec) 
   o       10   10.683(sec) 
   o        6   10.71(sec) 
   o        4   10.722(sec) 
   o        2   10.734(sec) 
   o        0   10.753(sec) 
  c output terms=0 weight=2
 Status  Optimum.
 _____________________________________
|           |           |             |
|   Weight  |   Errors  |    Cost     |
|___________|___________|_____________|
|           |           |             |
|     10000 |      0    |        0    |
|       100 |      0    |        0    |
|         3 |      0    |        0    |
|         2 |      0    |        0    |
|___________|___________|_____________|
|                       |             |
|         Total         |        0    |
|_______________________|_____________|
o 0(0)
 充足解を書き込みました。C:\Users\Public\test\sc3\sim_engine32\solution1.txt 12.607(CPU秒)
92376 [KB] used.
解探索が終了しました。 12 (秒)
解が得られました。
ところで、重み10000は、オプションハード列基数制約のソフト化で生成した自動重みです。これをDisableすることにしましょう。 オプションを設定するには、SetSolveingParameterを使います。problem.jsonを参照すれば、オプションコマンド名が分かります。
import sc3
import ctypes
MessageBox = ctypes.windll.user32.MessageBoxW
res=MessageBox(None, '正循環パターンにしますか?', 'パターン選択', 3)

#sc3.print(str(res))
if res==6:
    sc3.ConstraintEnable('正循環')
    sc3.ConstraintEnable('深準パターン',False)
    sc3.print('行制約正循環をEnableしました。\n')
else:
    sc3.ConstraintEnable('正循環',False)
    sc3.ConstraintEnable('深準パターン',True)
    sc3.print('深準パターンをEnableしました。\n')

res=MessageBox(None, 'ハード列基数制約のソフト化を行いますか?', 'ソフト化選択', 3)
if res==6:
    sc3.SetSolvingParameter('implict_hard_to_soft_enable',1)
    sc3.print('ハード列基数制約のソフト化を実行します。')
else:
    sc3.SetSolvingParameter('implict_hard_to_soft_enable',0)
    sc3.print('ハード列基数制約のソフト化を実行しません。')
ところで、個別の制約をオンオフするには、次のようにドットで記述します。 ただし、上位のグループ制約をオフにすると効きませんのでご注意ください。
if res2==6:
    if res==6:
        sc3.ConstraintEnable('正循環.3連続夜勤禁止',True)
    else:
        sc3.ConstraintEnable('正循環.3連続夜勤禁止',False)
else:
    if res==6:
        sc3.ConstraintEnable('深準パターン.夜勤3連不可',True)
    else:
        sc3.ConstraintEnable('深準パターン.夜勤3連不可',False)

2019年12月18日水曜日

深準型で、公休深夜パターンを一回以上

という方式検討依頼がありました。

スタッフ26名
A・Bチーム
夜勤3人
平日日勤12人以上
休日日勤6名(A/Bチーム各3名)
6日連続出勤禁止
公休8回

もはや、かなり少なくなったクラッシックパターンですが、公休深夜パターンを1回以上、というのが入るものかどうか、という依頼です。
早速コーディングすること30分、求解40秒で次の解となりました。(他に、前後に夜勤のない土日休みが1回以上という看護協会仕様の制約も入れています。) 26人というのが、夜勤回数全員を8回以下を満たすための味噌なんですね。

Pureな深夜準夜パターンしかない解を初めてみました。面白いので、次のリリース時にプロジェクトサンプルに入れておきます。

2019年12月17日火曜日

pythonの変数を日本語化の実装その2


実装後による結果です。126Aでは、次のようにプロパティファイルを生成しました。
#daydef
制約開始日=5
制約終了日=35
表示開始日=0
daydef=['2016-06-26','2016-06-27','2016-06-28','2016-06-29','2016-06-30','2016-07-01','2016-07-02','2016-07-03','2016-07-04','2016-07-05','2016-07-06','2016-07-07','2016-07-08','2016-07-09','2016-07-10','2016-07-11','2016-07-12','2016-07-13','2016-07-14','2016-07-15','2016-07-16','2016-07-17','2016-07-18','2016-07-19','2016-07-20','2016-07-21','2016-07-22','2016-07-23','2016-07-24','2016-07-25','2016-07-26','2016-07-27','2016-07-28','2016-07-29','2016-07-30','2016-07-31']
#staffcollection
全スタッフ=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
「@地上波+BS」=[0,4,10,11]
「@地上波のみ」=[1,2,6,7,8,19,20]
「@地上波+NBD」=[3,5]
「@BS+NBD」=[9,17]
「@BSのみ」=[12,14,15,16,18,21,22]
「@BSのみ女性」=[13]
「@BS+NBD」=[9,17]
「@アンクル」=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
「@データトラフィック」=[19,20,21,22]
「@NS3連続禁止」=[0,1,2,3,4,5,6,7,8,9,10,13,14,15,17,18,19,20,21,22]
「CとEのバランス気にしない」=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
「明けの後勤務最大1日」=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
「単勤最大日数1日」=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
「単休最大日数1日」=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
「6日連続勤務0日」=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
「4連休OK」=[0,9,11,12,13,14,15,16,17,18]
「@NS単発0回」=[11,12,16]
「@シフト希望4~5日」=[0,2,6,12,13,16,17,22]
「@シフト希望6日以上」=[9,19]
「@BS+勤務禁止」=[18]
「3連休なくても良い」=[]
「2単勤最大日数1日」=[]
「2単勤最大日数2日」=[]
パターン制約適用しない=[23]
シフト希望4~5日でない=[1,3,4,5,7,8,9,10,11,14,15,18,19,20,21]
シフト希望6日以上でない=[0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,20,21,22]
4連休OKでない=[1,2,3,4,5,6,7,8,10,19,20,21,22]
3連休なくても良くない=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
CとEのバランス気にしなくない=[1,2,3,5,6,7,8,19,20]
@4連休NG=[1,3,4,5,7,8,10,20,21]
@3連休1つ以上=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@CとEのバランス気にする=[1,3,5,7,8,20]
@単勤最大日数1日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@単勤最大日数2日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@単休最大日数1日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@単休最大日数2日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@2単勤最大日数1日=[]
@2単勤最大日数2日=[]
@明けの後勤務最大1日=[4,10,11,14,15,18,21]
@明けの後勤務最大2日=[4,10,11,14,15,18,21]
@6日連続勤務禁止=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@希望3日以下=[1,3,4,5,7,8,10,11,14,15,18,20,21]
@NS3連続OK=[11,12,16]
パターン制約適用=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
J=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
X=[3,9,17]
Z=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
t=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
単宿=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
明勤=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
朝番=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
遅番=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
A=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
B=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
C=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
D=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
E=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
F=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
G=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
H=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
a=[0,4,9,10,11,12,13,21,22]
b=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
c=[0,4,9,10,11,12,13,21,22]
d=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
e=[0,4,9,10,11,12,13,21,22]
f=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
V=[3,5,9,17]
W=[3,5,9,17]
X=[3,9,17]
Y=[3,5,17]
g=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
h=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
i=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
t=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]

#daycollection
今月=[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
日=[0,7,14,21,28,35]
月=[1,8,15,22,29]
火=[2,9,16,23,30]
水=[3,10,17,24,31]
木=[4,11,18,25,32]
金=[5,12,19,26,33]
土=[6,13,20,27,34]
全日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
祝=[22]
NBD1人=[16,18,32,33]
NB夜1人=[10,11,13,17]
NB夜2人=[12]
NB夜3人=[]
NB遅1人=[5,6,9,10,15,22,25,26,27,29,34,35]
NB遅2人=[23,24]
NB遅3人=[11]
NB朝1人=[8,9,10,17,18,19,20,31,32]
NB朝2人=[14,15,16]
NB朝3人=[]
MS+の日=[]
DS+の日=[26,27,28]
NS+の日=[]
平日=[1,2,3,4,5,8,9,10,11,12,15,16,17,18,19,22,23,24,25,26,29,30,31,32,33]
週末=[0,7,14,21,28,35]
休日=[0,7,14,21,28,35]
稼働日=[1,2,3,4,5,6,8,9,10,11,12,13,15,16,17,18,19,20,22,23,24,25,26,27,29,30,31,32,33,34]
制約開始日一日前=[4]
制約開始日二日前=[3]
制約開始日三日前=[2]
制約開始日四日前=[1]
制約開始日五日前=[0]
制約開始日六日前=[]
制約開始日七日前=[]
制約開始日P1=[6]
制約開始日P2=[7]
制約開始日P3=[8]
制約開始日P4=[9]
制約開始日P5=[10]
制約開始日P6=[11]
第一週=[5,6,7,8,9,10,11]
第二週=[12,13,14,15,16,17,18]
第三週=[19,20,21,22,23,24,25]
第四週=[26,27,28,29,30,31,32]
第五週=[33,34,35]
第六週=[]
四週間=[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]
制約開始日1日前から=[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日2日前から=[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日3日前から=[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日4日前から=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日5日前から=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日6日前から=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約終了日六日前=[29]
制約終了日五日前=[30]
制約終了日四日前=[31]
制約終了日三日前=[32]
制約終了日二日前=[33]
制約終了日一日前=[34]
金土日=[0,5,7,12,14,19,21,26,28,33,35]
金土日月=[0,1,5,7,8,12,14,15,19,21,22,26,28,29,33,35]
NBD0人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,19,20,21,22,23,24,25,26,27,28,29,30,31,34,35]
notNB夜1人=[0,1,2,3,4,5,6,7,8,9,12,14,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB夜2人=[0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB夜3人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
NB夜0人=[5,6,7,8,9,14,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
MS+がない日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
DS+がない日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,29,30,31,32,33,34,35]
NS+がない日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB朝1人=[0,1,2,3,4,5,6,7,11,12,13,14,15,16,21,22,23,24,25,26,27,28,29,30,33,34,35]
notNB朝2人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB朝3人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
NB朝0人=[]
notNB遅1人=[0,1,2,3,4,7,8,11,12,13,14,16,17,18,19,20,21,23,24,28,30,31,32,33]
notNB遅2人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,25,26,27,28,29,30,31,32,33,34,35]
notNB遅3人=[0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
NB遅0人=[]
第一週P3=[8,9,10,11,12,13,14]
F10日=[5,6,7,8,9,10,11,12,13,14]
F20日=[15,16,17,18,19,20,21,22,23,24]
F30日=[25,26,27,28,29,30,31,32,33,34]
F31日=[25,26,27,28,29,30,31,32,33,34,35]

#shiftcollection
泊=[0,4]
S=[0,1,3,4,5,6,7]

#classcollection
全スタッフ属性=[全スタッフ]
地上波・BS・NBD=[「@地上波+BS」,「@地上波のみ」,「@地上波+NBD」,「@BS+NBD」,「@BSのみ」,「@BSのみ女性」,「@BS+NBD」]
アンクルデータトラフィック=[「@アンクル」,「@データトラフィック」]
「@NS3連続禁止」属性=[「@NS3連続禁止」]
「CとEのバランス気にしない」属性=[「CとEのバランス気にしない」]
「明けの後勤務最大1日」属性=[「明けの後勤務最大1日」]
「単勤最大日数1日」属性=[「単勤最大日数1日」]
「単休最大日数1日」属性=[「単休最大日数1日」]
「6日連続勤務0日」属性=[「6日連続勤務0日」]
「4連休OK」属性=[「4連休OK」]
「@NS単発0回」属性=[「@NS単発0回」]
シフト希望属性=[「@シフト希望4~5日」,「@シフト希望6日以上」]
「@BS+勤務禁止」属性=[「@BS+勤務禁止」]
「3連休なくても良い」属性=[「3連休なくても良い」]
「2単勤最大日数1日」属性=[「2単勤最大日数1日」]
「2単勤最大日数2日」属性=[「2単勤最大日数2日」]
パターン制約=[パターン制約適用しない]

#shiftdef
shiftdef={'J':J,'X':X,'Z':Z,'t':t,'単宿':単宿,'明勤':明勤,'朝番':朝番,'遅番':遅番}
#taskdef
taskdef={'A':A,'B':B,'C':C,'D':D,'E':E,'F':F,'G':G,'H':H,'a':a,'b':b,'c':c,'d':d,'e':e,'f':f,'V':V,'W':W,'X':X,'Y':Y,'g':g,'h':h,'i':i,'t':t}
#non_auto_tasks
non_auto_tasks=['t']
#phase_list
PH0=0

#task collections
R={'A':A,'C':C}
U={'g':g,'h':h,'i':i}
#digited group
#column_constraints
A=(True,('今月',今月),'',PH0,('A',A),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
B=(True,('今月',今月),'',PH0,('B',B),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
C=(True,('今月',今月),'',PH0,('C',C),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
D=(True,('今月',今月),'',PH0,('D',D),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
E=(True,('今月',今月),'',PH0,('E',E),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
F=(True,('今月',今月),'',PH0,('F',F),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
G=(True,('今月',今月),'',PH0,('G',G),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
H=(True,('今月',今月),'',PH0,('H',H),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
a=(True,('今月',今月),'',PH0,('a',a),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
b=(True,('今月',今月),'',PH0,('b',b),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
c=(True,('今月',今月),'',PH0,('c',c),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
d=(True,('今月',今月),'',PH0,('d',d),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
e=(True,('今月',今月),'',PH0,('e',e),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
f=(True,('今月',今月),'',PH0,('f',f),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NBデスク1人=(True,('NBD1人',NBD1人),'',PH0,('X',X),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NBデスク0人=(True,('NBD0人',NBD0人),'',PH0,('X',X),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB夜0人=(True,('NB夜0人',NB夜0人),'',PH0,('Y',Y),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB夜1人=(True,('NB夜1人',NB夜1人),'',PH0,('Y',Y),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NB夜2人=(True,('NB夜2人',NB夜2人),'',PH0,('Y',Y),全スタッフ,1,2,2,-1.000000,-1.000000,0,100)
NB夜3人=(True,('NB夜3人',NB夜3人),'',PH0,('Y',Y),全スタッフ,1,3,3,-1.000000,-1.000000,0,100)
MS+あり=(True,('MS+の日',MS+の日),'',PH0,('g',g),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
MS+なし=(True,('MS+がない日',MS+がない日),'',PH0,('g',g),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
DS+あり=(True,('DS+の日',DS+の日),'',PH0,('h',h),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
DS+なし=(True,('DS+がない日',DS+がない日),'',PH0,('h',h),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NS+あり=(True,('NS+の日',NS+の日),'',PH0,('i',i),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NS+なし=(True,('NS+がない日',NS+がない日),'',PH0,('i',i),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB朝0人=(True,('NB朝0人',NB朝0人),'',PH0,('W',W),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB朝1人=(True,('NB朝1人',NB朝1人),'',PH0,('W',W),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NB朝2人=(True,('NB朝2人',NB朝2人),'',PH0,('W',W),全スタッフ,1,2,2,-1.000000,-1.000000,0,100)
NB朝3人=(True,('NB朝3人',NB朝3人),'',PH0,('W',W),全スタッフ,1,3,3,-1.000000,-1.000000,0,100)
NB遅0人=(True,('NB遅0人',NB遅0人),'',PH0,('V',V),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB遅1人=(True,('NB遅1人',NB遅1人),'',PH0,('V',V),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NB遅2人=(True,('NB遅2人',NB遅2人),'',PH0,('V',V),全スタッフ,1,2,2,-1.000000,-1.000000,0,100)
NB遅3人=(True,('NB遅3人',NB遅3人),'',PH0,('V',V),全スタッフ,1,3,3,-1.000000,-1.000000,0,100)
列制約グループ1フェーズ={'A':A,'B':B,'C':C,'D':D,'E':E,'F':F,'G':G,'H':H,'a':a,'b':b,'c':c,'d':d,'e':e,'f':f,'NBデスク1人':NBデスク1人,'NBデスク0人':NBデスク0人,'NB夜0人':NB夜0人,'NB夜1人':NB夜1人,'NB夜2人':NB夜2人,'NB夜3人':NB夜3人,'MS+あり':MS+あり,'MS+なし':MS+なし,'DS+あり':DS+あり,'DS+なし':DS+なし,'NS+あり':NS+あり,'NS+なし':NS+なし,'NB朝0人':NB朝0人,'NB朝1人':NB朝1人,'NB朝2人':NB朝2人,'NB朝3人':NB朝3人,'NB遅0人':NB遅0人,'NB遅1人':NB遅1人,'NB遅2人':NB遅2人,'NB遅3人':NB遅3人}
column_constraints={'列制約グループ1フェーズ':列制約グループ1フェーズ}

当然ながら、この状態では、次のようにコンパイルエラーとなります。
コンパイルの準備中ソルバを呼び出し中です。
 python propertyファイル生成を開始します。
 python propertyファイル生成が終了しました。
SyntaxError: ('invalid character in identifier', ('', 11, 5, '「@地上波+BS」=[0,4,10,11]\n'))解探索が終了しました。 0 (秒)
解を見つけることが出来ませんでした。


同じプロジェクトですが、127Aでは、次のように生成されエラーとはなりません。
#daydef
制約開始日=5
制約終了日=35
表示開始日=0
daydef=['2016-06-26','2016-06-27','2016-06-28','2016-06-29','2016-06-30','2016-07-01','2016-07-02','2016-07-03','2016-07-04','2016-07-05','2016-07-06','2016-07-07','2016-07-08','2016-07-09','2016-07-10','2016-07-11','2016-07-12','2016-07-13','2016-07-14','2016-07-15','2016-07-16','2016-07-17','2016-07-18','2016-07-19','2016-07-20','2016-07-21','2016-07-22','2016-07-23','2016-07-24','2016-07-25','2016-07-26','2016-07-27','2016-07-28','2016-07-29','2016-07-30','2016-07-31']
#staffcollection
全スタッフ=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
a_地上波BS=[0,4,10,11]
b_地上波のみ=[1,2,6,7,8,19,20]
c_地上波NBD=[3,5]
d_BSNBD=[9,17]
e_BSのみ=[12,14,15,16,18,21,22]
f_BSのみ女性=[13]
d_BSNBD=[9,17]
g_アンクル=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
h_データトラフィック=[19,20,21,22]
i_NS3連続禁止=[0,1,2,3,4,5,6,7,8,9,10,13,14,15,17,18,19,20,21,22]
j_CとEのバランス気にしない=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
k_明けの後勤務最大1日=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
l_単勤最大日数1日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
m_単休最大日数1日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
n_6日連続勤務0日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
o_4連休OK=[0,9,11,12,13,14,15,16,17,18]
p_NS単発0回=[11,12,16]
q_シフト希望45日=[0,2,6,12,13,16,17,22]
r_シフト希望6日以上=[9,19]
s_BS勤務禁止=[18]
t_3連休なくても良い=[]
u_2単勤最大日数1日=[]
v_2単勤最大日数2日=[]
パターン制約適用しない=[23]
w_シフト希望45日でない=[1,3,4,5,7,8,9,10,11,14,15,18,19,20,21]
シフト希望6日以上でない=[0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,20,21,22]
x_4連休OKでない=[1,2,3,4,5,6,7,8,10,19,20,21,22]
y_3連休なくても良くない=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
CとEのバランス気にしなくない=[1,2,3,5,6,7,8,19,20]
z_4連休NG=[1,3,4,5,7,8,10,20,21]
ba_3連休1つ以上=[1,3,4,5,7,8,10,11,14,15,18,20,21]
bb_CとEのバランス気にする=[1,3,5,7,8,20]
bc_単勤最大日数1日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
bd_単勤最大日数2日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
be_単休最大日数1日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
bf_単休最大日数2日=[1,3,4,5,7,8,10,11,14,15,18,20,21]
bg_2単勤最大日数1日=[]
bh_2単勤最大日数2日=[]
bi_明けの後勤務最大1日=[4,10,11,14,15,18,21]
bj_明けの後勤務最大2日=[4,10,11,14,15,18,21]
bk_6日連続勤務禁止=[1,3,4,5,7,8,10,11,14,15,18,20,21]
bl_希望3日以下=[1,3,4,5,7,8,10,11,14,15,18,20,21]
bm_NS3連続OK=[11,12,16]
パターン制約適用=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
J=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
X=[3,9,17]
Z=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
t=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
単宿=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
明勤=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
朝番=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
遅番=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
A=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
B=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
C=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
D=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
E=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
F=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
G=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
H=[0,1,2,3,4,5,6,7,8,10,11,19,20,23]
a=[0,4,9,10,11,12,13,21,22]
b=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
c=[0,4,9,10,11,12,13,21,22]
d=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
e=[0,4,9,10,11,12,13,21,22]
f=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
V=[3,5,9,17]
W=[3,5,9,17]
X=[3,9,17]
Y=[3,5,17]
g=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
h=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
i=[0,4,9,10,11,12,13,14,15,16,17,18,21,22]
t=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]

#daycollection
今月=[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
日=[0,7,14,21,28,35]
月=[1,8,15,22,29]
火=[2,9,16,23,30]
水=[3,10,17,24,31]
木=[4,11,18,25,32]
金=[5,12,19,26,33]
土=[6,13,20,27,34]
全日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
祝=[22]
NBD1人=[16,18,32,33]
NB夜1人=[10,11,13,17]
NB夜2人=[12]
NB夜3人=[]
NB遅1人=[5,6,9,10,15,22,25,26,27,29,34,35]
NB遅2人=[23,24]
NB遅3人=[11]
NB朝1人=[8,9,10,17,18,19,20,31,32]
NB朝2人=[14,15,16]
NB朝3人=[]
bn_MSの日=[]
bo_DSの日=[26,27,28]
bp_NSの日=[]
平日=[1,2,3,4,5,8,9,10,11,12,15,16,17,18,19,22,23,24,25,26,29,30,31,32,33]
週末=[0,7,14,21,28,35]
休日=[0,7,14,21,28,35]
稼働日=[1,2,3,4,5,6,8,9,10,11,12,13,15,16,17,18,19,20,22,23,24,25,26,27,29,30,31,32,33,34]
制約開始日一日前=[4]
制約開始日二日前=[3]
制約開始日三日前=[2]
制約開始日四日前=[1]
制約開始日五日前=[0]
制約開始日六日前=[]
制約開始日七日前=[]
制約開始日P1=[6]
制約開始日P2=[7]
制約開始日P3=[8]
制約開始日P4=[9]
制約開始日P5=[10]
制約開始日P6=[11]
第一週=[5,6,7,8,9,10,11]
第二週=[12,13,14,15,16,17,18]
第三週=[19,20,21,22,23,24,25]
第四週=[26,27,28,29,30,31,32]
第五週=[33,34,35]
第六週=[]
四週間=[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]
制約開始日1日前から=[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日2日前から=[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日3日前から=[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日4日前から=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日5日前から=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約開始日6日前から=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
制約終了日六日前=[29]
制約終了日五日前=[30]
制約終了日四日前=[31]
制約終了日三日前=[32]
制約終了日二日前=[33]
制約終了日一日前=[34]
金土日=[0,5,7,12,14,19,21,26,28,33,35]
金土日月=[0,1,5,7,8,12,14,15,19,21,22,26,28,29,33,35]
NBD0人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,19,20,21,22,23,24,25,26,27,28,29,30,31,34,35]
notNB夜1人=[0,1,2,3,4,5,6,7,8,9,12,14,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB夜2人=[0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB夜3人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
NB夜0人=[5,6,7,8,9,14,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
bq_MSがない日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
br_DSがない日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,29,30,31,32,33,34,35]
bs_NSがない日=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB朝1人=[0,1,2,3,4,5,6,7,11,12,13,14,15,16,21,22,23,24,25,26,27,28,29,30,33,34,35]
notNB朝2人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
notNB朝3人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
NB朝0人=[]
notNB遅1人=[0,1,2,3,4,7,8,11,12,13,14,16,17,18,19,20,21,23,24,28,30,31,32,33]
notNB遅2人=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,25,26,27,28,29,30,31,32,33,34,35]
notNB遅3人=[0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35]
NB遅0人=[]
第一週P3=[8,9,10,11,12,13,14]
F10日=[5,6,7,8,9,10,11,12,13,14]
F20日=[15,16,17,18,19,20,21,22,23,24]
F30日=[25,26,27,28,29,30,31,32,33,34]
F31日=[25,26,27,28,29,30,31,32,33,34,35]

#shiftcollection
泊=[0,4]
S=[0,1,3,4,5,6,7]

#classcollection
全スタッフ属性=[全スタッフ]
bt_地上波BSNBD=[a_地上波BS,b_地上波のみ,c_地上波NBD,d_BSNBD,e_BSのみ,f_BSのみ女性,d_BSNBD]
アンクルデータトラフィック=[g_アンクル,h_データトラフィック]
bu_NS3連続禁止属性=[i_NS3連続禁止]
bv_CとEのバランス気にしない属性=[j_CとEのバランス気にしない]
bw_明けの後勤務最大1日属性=[k_明けの後勤務最大1日]
bx_単勤最大日数1日属性=[l_単勤最大日数1日]
by_単休最大日数1日属性=[m_単休最大日数1日]
bz_6日連続勤務0日属性=[n_6日連続勤務0日]
ca_4連休OK属性=[o_4連休OK]
cb_NS単発0回属性=[p_NS単発0回]
シフト希望属性=[q_シフト希望45日,r_シフト希望6日以上]
cc_BS勤務禁止属性=[s_BS勤務禁止]
cd_3連休なくても良い属性=[t_3連休なくても良い]
ce_2単勤最大日数1日属性=[u_2単勤最大日数1日]
cf_2単勤最大日数2日属性=[v_2単勤最大日数2日]
パターン制約=[パターン制約適用しない]

#shiftdef
shiftdef={'J':J,'X':X,'Z':Z,'t':t,'単宿':単宿,'明勤':明勤,'朝番':朝番,'遅番':遅番}
#taskdef
taskdef={'A':A,'B':B,'C':C,'D':D,'E':E,'F':F,'G':G,'H':H,'a':a,'b':b,'c':c,'d':d,'e':e,'f':f,'V':V,'W':W,'X':X,'Y':Y,'g':g,'h':h,'i':i,'t':t}
#non_auto_tasks
non_auto_tasks=['t']
#phase_list
PH0=0

#task collections
R={'A':A,'C':C}
U={'g':g,'h':h,'i':i}
#digited group
#column_constraints
A=(True,('今月',今月),'',PH0,('A',A),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
B=(True,('今月',今月),'',PH0,('B',B),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
C=(True,('今月',今月),'',PH0,('C',C),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
D=(True,('今月',今月),'',PH0,('D',D),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
E=(True,('今月',今月),'',PH0,('E',E),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
F=(True,('今月',今月),'',PH0,('F',F),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
G=(True,('今月',今月),'',PH0,('G',G),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
H=(True,('今月',今月),'',PH0,('H',H),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
a=(True,('今月',今月),'',PH0,('a',a),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
b=(True,('今月',今月),'',PH0,('b',b),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
c=(True,('今月',今月),'',PH0,('c',c),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
d=(True,('今月',今月),'',PH0,('d',d),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
e=(True,('今月',今月),'',PH0,('e',e),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
f=(True,('今月',今月),'',PH0,('f',f),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NBデスク1人=(True,('NBD1人',NBD1人),'',PH0,('X',X),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NBデスク0人=(True,('NBD0人',NBD0人),'',PH0,('X',X),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB夜0人=(True,('NB夜0人',NB夜0人),'',PH0,('Y',Y),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB夜1人=(True,('NB夜1人',NB夜1人),'',PH0,('Y',Y),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NB夜2人=(True,('NB夜2人',NB夜2人),'',PH0,('Y',Y),全スタッフ,1,2,2,-1.000000,-1.000000,0,100)
NB夜3人=(True,('NB夜3人',NB夜3人),'',PH0,('Y',Y),全スタッフ,1,3,3,-1.000000,-1.000000,0,100)
AA_MSあり=(True,('MS+の日',bn_MSの日),'',PH0,('g',g),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
AB_MSなし=(True,('MS+がない日',bq_MSがない日),'',PH0,('g',g),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
AC_DSあり=(True,('DS+の日',bo_DSの日),'',PH0,('h',h),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
AD_DSなし=(True,('DS+がない日',br_DSがない日),'',PH0,('h',h),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
AE_NSあり=(True,('NS+の日',bp_NSの日),'',PH0,('i',i),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
AF_NSなし=(True,('NS+がない日',bs_NSがない日),'',PH0,('i',i),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB朝0人=(True,('NB朝0人',NB朝0人),'',PH0,('W',W),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB朝1人=(True,('NB朝1人',NB朝1人),'',PH0,('W',W),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NB朝2人=(True,('NB朝2人',NB朝2人),'',PH0,('W',W),全スタッフ,1,2,2,-1.000000,-1.000000,0,100)
NB朝3人=(True,('NB朝3人',NB朝3人),'',PH0,('W',W),全スタッフ,1,3,3,-1.000000,-1.000000,0,100)
NB遅0人=(True,('NB遅0人',NB遅0人),'',PH0,('V',V),全スタッフ,1,0,-1,-1.000000,-1.000000,0,100)
NB遅1人=(True,('NB遅1人',NB遅1人),'',PH0,('V',V),全スタッフ,1,1,1,-1.000000,-1.000000,0,100)
NB遅2人=(True,('NB遅2人',NB遅2人),'',PH0,('V',V),全スタッフ,1,2,2,-1.000000,-1.000000,0,100)
NB遅3人=(True,('NB遅3人',NB遅3人),'',PH0,('V',V),全スタッフ,1,3,3,-1.000000,-1.000000,0,100)
列制約グループ1フェーズ={'A':A,'B':B,'C':C,'D':D,'E':E,'F':F,'G':G,'H':H,'a':a,'b':b,'c':c,'d':d,'e':e,'f':f,'NBデスク1人':NBデスク1人,'NBデスク0人':NBデスク0人,'NB夜0人':NB夜0人,'NB夜1人':NB夜1人,'NB夜2人':NB夜2人,'NB夜3人':NB夜3人,'MS+あり':AA_MSあり,'MS+なし':AB_MSなし,'DS+あり':AC_DSあり,'DS+なし':AD_DSなし,'NS+あり':AE_NSあり,'NS+なし':AF_NSなし,'NB朝0人':NB朝0人,'NB朝1人':NB朝1人,'NB朝2人':NB朝2人,'NB朝3人':NB朝3人,'NB遅0人':NB遅0人,'NB遅1人':NB遅1人,'NB遅2人':NB遅2人,'NB遅3人':NB遅3人}
column_constraints={'列制約グループ1フェーズ':列制約グループ1フェーズ}

ご覧のように、INVALID CHARは、スキップされます。そうすると同じ名前になってしまうものが出てしまうので、Prefixで別な名前にしています。 (元の名前が同じならば、同じ名前に変更されます。) ただし、Day集合名とColumn制約名が同じ場合があったので、制約名のマッピングは別にして実装しています。これは、よくありそうな事象であり、自然にその対策になります。いずれにしても、元の制約名は推定可能なので、このファイルを参照すれば、Pythonで書くのに障害にはなりません。 また、Column制約については、 チェックを外してもプロパティファイルを生成しています。(これは、少し複雑な事情によります。)GUIでは、チェックを外せば、Day集合記述がなくてもエラーとはなりませんが、Propertyファイル生成時は、エラーとなります。グループの適用を外せば、Propertyファイルは生成されません。 この一点だけ、ご注意頂ければ、任意のGUIをPythonプロパティ形式に落とせます。

これによりすぐにPython記述を始めることができます。Python記述が可能になれば、後述のように、任意制約のEnable/Disable、任意SolvingPrameterの変更、任意ソフトレベルのソフト制約適用・不適用、重み変更、許容エラー数変更が可能になります。なので、Pythonで制約を記述しなくても
プロジェクト全体に渡る制約のコントロールが可能となります。

以上は、全ロケールで適用となるので、プロパティファイルについては、(日本語のみならず)国際化完了です。 次回は、pythonでの制約コントロール方法について述べます。

2019年12月16日月曜日

pythonの変数を日本語化の実装その1


前回構想に基づいた実装です。変数の頭とそれ以外で、禁止文字が異なるので、tableは、2回に分けて生成します。連続charは、一まとめにしたC++2次元テーブルを生成するようにしています。
以下は、頭文字禁止の場合のtable生成コードです。
python3 first.py> test.txt で生成されるtableをそのままC++ソースに貼り付けています。
頭禁止で、6万、頭以外で10万程度しかありませんでした。ユニコード全体でも高々100万程度のオーダです。意外に多くはないです。

後は、unf8をutf32に変換し、table参照してinvalid charだったらスキップして、prefixを付加します。

from collections import defaultdict
import unicodedata



vlist=[]
b=0
f=0
print ('int first_inhibit_chars[][2]={')

for i in range(0x10Ffff):
 

    try:

        #s='AAA'
        #s+=chr(i)+'BBB' +'=1'
        s=chr(i)+'AAA=1'
        exec(s)#

        vlist.append(i)
        
    except:
 
        continue


#print(len(vlist))        
b=0
f=0
for i in vlist:
    if i==35:
        continue

    if b==0:
        f=i
        print ('{',f)
        
    else:
        if i !=f+b:
            f=i
            print (',',b,'}')
            print (',{',i,end='')
            b=0         
    b+=1                   
print(',',b,'}')
print('};')        

2019年12月15日日曜日

ユーザエクスペリアンス

最も難しいのは、ソルバーではなく、制約プログラムですらありません。

ユーザの意図を組んで制約を作り上げていく作業です。

タイプは、二つに別れ全くサポートの不要な方とそうでない方の2極ですが、後者の場合、3ヶ月以下で物になった経験はありません。中には、半年経っても未だ、稼動すら行き着いていないプロジェクトもあります。ですので、作業時間だけで取ったとしても、10万円では殆どの場合ペイしません。(反対に、前者の方には申し訳ないので、サポートとソフト費用は別にするストアアプリ化を行います。) 後者の方にどのようなアプローチがよいのか、悩みつつも、良い開発スタイルであると感じるのは

1)ユーザ実勤務表ベースでのExcelによるやり取り(ユーザ実勤務表に制約達成度を数値化・可視化していく)
2)ユーザ希望仕様をそのまま実装しない。(ユーザ経験に基づかない新規制約は、結局実現不能が多い。思いつき制約には付き合わない。背後にある本当の制約を推し量ることが重要)
3)実勤務表作成者に評価・FBをして頂く。(上位管理者ではNG)

です。特に、思いつき制約には要注意です。「どうせ機械に生成させるのだから」という発想で、やられると、苦労して実装したとしても、他の制約とのPriorityとの兼ね合いで殆ど有効に動作しない場合が多く、ない袖は振れない・絵に描いた餅を体感するばかりか、何ヶ月経っても収束しない事態になってしまいます。実績のあるルールにこだわる理由がここにあります。

いずれにしても、ダイバーシティとか人口知能とか、そういった技術範疇ではなくて、実勤務表作成者の頭の中をいかにして整理してあげるか、これに尽きます。実担当者は、本当に色々な事に思いをはせ、気を配りながら作っています。しかし、それが系統的に整理されてはいません。その時々の思いつきに左右されることも多いです。それを一つ一つ拾い上げ、本当に必要なルールだけを制約化していくことは、骨が折れる作業です。業界の常套宣伝文句の「簡単」は、簡単ではないのです。

2019年12月14日土曜日

Pythonの日本語で使えない変数を使えるようにする構想

こちらで、確認プログラムを見ることが出来ます。これを実行し、結果を予め辞書として持っていて、除去、適当な接頭辞をつければ、とりあえず雰囲気をある程度残したまま使えるようになると思います。(こちらは、後で述べるロケール問題(国際化)でも必要となる作業です。)
どの道、変数名は、生成されるプロパティファイルを参照することになるので、多分これでも支障がないでしょう。

さらに、年末年始など、特定のグループ・制約をPythonで、Enable・Disable出来ると、全てをPythonで書き換えなくても既存のGUI記述を生かすことが出来ます。

そうすると、GUI上では、オフだったものが実際にはオンになるものが出てきてしまうものが出てきます。逆もまたしかりです。この辺を対処するために、各行列制約のレベルや重みも変える機能も必要になります。最終的にはPythonで設定した状態でSolveされます。

以上の対応が出来ると、「月」から自立的にPythonで必要な制約のEnable・Disableが出来、究極のメンテナンスフリーが実現できることになります。長年願って実現出来なかった機能ですが、やっと形が見えてきました。

狙っている形態は、SEがメンテナンスフリーの制約プログラムを書きます。

ユーザは、Excel毎月、で必要な稼働日集合、タスク集合、スタッフプロパティ、予定シフト集合を設定してインポートするだけで、特にオンオフ・調整箇所はないことです。これは、設計と使い方の分離を意味し分業体制が可能となる素地となります。

以上は、構想です。近いうちに実現したいと思います。




日曜出勤したらその週のどこかで休む

というご要求を受けたのですが、以下のように実装し、ご要求そのままではありません。
理由は、
1)端数処理が面倒
2)解空間をなるべく狭めたくはない

その週となると、月はじめ、月終わりの処理が面倒になります。休みというシフトも区別する必要があり、例えば代休というシフトを新設する必要があります。そして、今月以外に見る必要も生じます。

第2の問題は、その週に限定すると、解空間が狭まります。

そこで、要求のエッセンスを満たす方式として、日曜出勤した数だけ代休するという処理にしました。以下がコードです。こうすれば、その週に限定することはないので、解空間の減少を極小にすることができ、なおかつ月内処理に限定できるので、実装が簡単になります。



def 日曜日出勤なら代休():
    for person in 全スタッフ:
        if person not in 正社員:
            for day in 全日:
                if day in 今月:
                    s='正社員でなければ全日代休禁止'+daydef[day]
                    v=sc3.GetShiftVar(person,day,'代休')
                    sc3.AddHard(~v,s)
        else:#正社員
            sc3.print('正社員'+staffdef[person]+'の代休処理を行います\n')
            日_list=[]
            代_list=[]
            s='日曜働いた数に代休カウントは等しい '+staffdef[person]#DEC132019
            for day in 全日:
                if day in 今月:
                    if day in 日:
                        s='日は代休禁止'
                        v=sc3.GetShiftVar(person,day,'代休')#日は代休禁止
                        sc3.AddHard(~v,s)
                        v=sc3.GetShiftVar(person,day,'休日')
                        日_list.append(~v)#日曜働いたなら日_listに追加
                    if day in 今月休診日:
                        v=sc3.GetShiftVar(person,day,'代休')#休診日は代休禁止
                        sc3.AddHard(~v,s)
                    else:
                        v=sc3.GetShiftVar(person,day,'代休')
                        代_list.append(v)#稼働日の代休をカウント
            #s='日曜働いた数に代休カウントは等しい '+staffdef[person]
            sc3.AddHard(sc3.SeqComp(日_list,代_list),s)

日曜出勤した分だけ、代休(青色)しているのが分かると思います。

ところで、SeqCompが意外に使い勝手があり、あることを思いつきました。次回これを拡張することを考えます。

2019年12月12日木曜日

外来看護師が1人休みのときに病棟看護師は外来勤務

という制約の実装をどうするかという問題です。

外来看護師というグループがあり外来専門で外来を行っています。このグループ内で休みがあったとき、休み人数分病棟看護師から持ってくるという制約になります。

つまり外来看護師数がNのとき、
 Σ日勤@外来看護師+Σ外来@外来担当の病棟看護師==N
という制約にすればよいことになります。
外来看護師数が増減してもPythonで書けば、動的に処理が可能でメンテナンスフリー
に出来ます。

def 外来支援Day(day):#外来看護師が1人休みのときに病棟から1人外来として支援する
    
    nnurses_out=len(外来看護師)
    vlist=[]
    for person in 外来看護師:
        v=sc3.GetShiftVar(person,day,'日勤')
        vlist.append(v)
    for person in 外来担当:
        v=sc3.GetShiftVar(person,day,'外来')
        vlist.append(v)
    s='外来看護師が1人休みのときに病棟から1人外来として支援する '+daydef[day]
    sc3.AddSoft(sc3.SeqError(nnurses_out,nnurses_out,3,vlist),s,5)
    
    for person in 全スタッフ:#外来担当外の外来禁止
        if person not in 外来担当:
            s='外来禁止'+staffdef[person]+' '+daydef[day]
            v=sc3.GetShiftVar(person,day,'外来')
            sc3.AddHard(~v,s)#
    
def 外来支援():
    for day in 全日:
        if day in 祝ではない月火水金今月:
            外来支援Day(day)
        else:
            for person in 全スタッフ:
                v=sc3.GetShiftVar(person,day,'外来')
                s='外来禁止'+staffdef[person]+' '+daydef[day]
                sc3.AddHard(~v,s)#外来担当外
 

2019年12月10日火曜日

エラー解析は難しい

125Aで明らかなハードエラーの解析ルーチンを改善したつもりだったのですが、時間がかかり過ぎるインスタンスがあることが判明しました。こちらは、鋭意改善の思索中です。

ソルバーという意味では、MIPソルバーも含めて、数々のソルバーがこの世には存在します。しかし、原因解析と真面目に向き合っているのは、SC2/SC3のみだと思います。特に、専門家が使うのではない、NSPのモデルを開発・改変・変更する過程において、必須となる機能です。改めて宣伝しますが、こちらの基本特許は、未だ使用権は私のみです。基本的な考えは、この特許に基づいていますが、いまだに実装上では、色々な事を考えないといけなくて経験とノウハウを要する奥の深いところです。

2019年12月9日月曜日

126Aをリリースしました

マニュアルも更新しました。
Python制約プログラミングマニュアルも更新しました。

126Aでは、
■Excel Importの速度改善を行いました。
■フェーズタスクモードのサンプルを追加しました。

2019年12月7日土曜日

125Aをリリースしました

125Aよりフェーズ・タスクをサポートします。これによりNSP(Nurse Scheduling Problem)のみならず、NRP(Nurse Rostering Problem 私の造語?)を記述できるようになります。Tutorialの整備はこれからです。

2019年12月6日金曜日

PythonでCSVを読む

import sc3
import sys
import os
import csv

os.chdir(r'C:\Users\sugaw\Documents\FA\sc3') #current dirに移動 r'がないとSyntaxError: ("(unicode error) 'unicodeescape' codec can't decode bytes
with open('タスクインポートサンプルV5.2.5.csv',encoding="utf-8") as f:#encodeがないとUnicodeDecodeError: 'cp932' codec can't decode
    reader = csv.reader(f)
    for row in reader:
        for s in row:
            sc3.print(s+' ')
        sc3.print('\n')
解説です。SC3のPythonインタープリタは、InstallFolderのsim_engineにあります。ですので、ExcelのCSV出力(utf8を指定)のフォルダに移動します。このとき、逆スラッシュがExcapeされないようにr’をつけておきます。encodeの指定をutf-8にして読むことが出来ました。
これで、Excel直ではないにしろExcelで管理するデータを読むことができるので、よりPythonでダイナミックに制約し易くなるのではないかと思います。

2019年12月4日水曜日

数独 Python Task版の実装

現在、チュートリアルで、タスク記述例の作成を行っています。
この数独では、シフトは1個、日勤のみ使っています。Dayを3つのフェーズに分けて、各フェーズには、9個のタスクのうち1個だけが入ります。Pythonでの記述は以下の通りです。

import sc3
for day in 全日: #列制約
    for ph in range(3):
        for タスク in 全タスク:
            s=daydef[day]+' '+タスク
            V=[]
            for 人 in 全スタッフ:
                V.append(sc3.GetTaskVar(人,day,ph,タスク))
            sc3.AddHard(sc3.SeqLE(1,1,V),s)

for 人 in 全スタッフ:#行制約
    for タスク in 全タスク:
        V=[]
        s=staffdef[人]+' '+タスク
        for day in 全日: 
            for ph in range(3):
                V.append(sc3.GetTaskVar(人,day,ph,タスク))
        sc3.AddHard(sc3.SeqLE(1,1,V),s) 
      
for 人 in スタッフブロックトップ: #ブロック制約
    for day in 全日:
        for タスク in 全タスク:
            V=[]
            s=staffdef[人]+daydef[day]+' '+タスク
            for i in range(3):
                for j in range(3):
                    V.append(sc3.GetTaskVar(人+i,day,j,タスク))
            sc3.AddHard(sc3.SeqLE(1,1,V),s)

2019年12月3日火曜日

予定入力の再掲を禁止の実装

125A Algorithm1で実装しました。この機能は、スタッフの予定を変更せざるを得ないときの別解を提供します。
どうしても看護品質・ケア品質は落としたくない場合は、スタッフの予定を変更せざるえません。そんなとき、スタッフと調整するときに別解を持っておけば、調整し易いでしょう。
上の例では、4つの予定変更が余儀なくされていて、その別解を求めています。
赤枠部がそれになりますが、微妙に、しかし確実に異なる組み合わせの解のみが掲示されています。この例では、設定を100解にして、62解まで求まりました。実用的には、GUIの速度的に、数解まででしょう。



同じ、パラメータで、Algorithm0(旧SC2)でやってみると、21個のエラーに対する別解を求めていました。
同じ、パラメータで、Algorihtm4でやってみると、厳密解が3であることが5秒足らずで判明しました。


コンパイルの準備中ソルバを呼び出し中です。
 Algorithm 4 Solving Process Started.
 Compling nurse 1
 Combination Space on Nurse 1 =5.32257e+11
 Compling nurse 2
 Combination Space on Nurse 2 =4.19788e+11
 Compling nurse 3
 Combination Space on Nurse 3 =4.7214e+11
 Compling nurse 4
 Combination Space on Nurse 4 =1.26248e+11
 Compling nurse 5
 Combination Space on Nurse 5 =6.41217e+11
 Compling nurse 7
 Combination Space on Nurse 7 =1.20307e+11
 Compling nurse 9
 Combination Space on Nurse 9 =5.13837e+11
 Compling nurse 11
 Combination Space on Nurse 11 =4.30146e+11
 Compling nurse 14
 Combination Space on Nurse 14 =4.6134e+11
 Compling nurse 15
 Combination Space on Nurse 15 =5.11604e+11
 Compling nurse 16
 Combination Space on Nurse 16 =5.12742e+11
 Compling nurse 17
 Combination Space on Nurse 17 =6.49804e+11
 Compling nurse 0
 Combination Space on Nurse 0 =3.39036e+11
 Compling nurse 6
 Combination Space on Nurse 6 =2.57e+11
 Compling nurse 8
 Combination Space on Nurse 8 =4.14028e+11
 Compling nurse 10
 Combination Space on Nurse 10 =3.80578e+11
 Compling nurse 12
 Combination Space on Nurse 12 =4.14248e+11
 Compling nurse 13
 Combination Space on Nurse 13 =7.09886e+11
 充足解を書き込みました。C:\Users\sugaw\Documents\Visual Studio 2017\Projects\schedule_nurse_ver3\WindowsFormsApplication1\test\sim_engine32\solution1.txt 3.697(CPU秒)
 *********UB=249000(0)  3.697(cpu秒)
 充足解を書き込みました。C:\Users\sugaw\Documents\Visual Studio 2017\Projects\schedule_nurse_ver3\WindowsFormsApplication1\test\sim_engine32\solution1.txt 3.785(CPU秒)
 *********UB=130138(0)  3.785(cpu秒)
  ov=130138 ext=18 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=63943.3 ext=36 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=39247 ext=54 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=23382.6 ext=72 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=11637 ext=90 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=7072.13 ext=108 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=5912.01 ext=126 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=2403.56 ext=144 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=306.022 ext=162 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=281.892 ext=180 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=270.299 ext=198 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=235.73 ext=216 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=200.077 ext=234 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=166.915 ext=252 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=145.496 ext=270 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=116.713 ext=288 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=97.3505 ext=306 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=84.9907 ext=324 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=75.5003 ext=342 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=67.7986 ext=360 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=58.5907 ext=378 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=53.0225 ext=396 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=48.538 ext=414 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=37.0741 ext=432 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=28.4404 ext=450 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=20.165 ext=468 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=16.3962 ext=486 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=13.6106 ext=504 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=10.8996 ext=522 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=9.56909 ext=540 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=7.82315 ext=558 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=6.40981 ext=576 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=5.66453 ext=594 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=5.08227 ext=612 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=4.2991 ext=630 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=3.78261 ext=648 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=3.49199 ext=666 del=0 depth=0 LB=1.79769e+308 UB=130138
  ov=3.3186 ext=684 del=0 depth=0 LB=1.79769e+308 UB=130138
 充足解を書き込みました。C:\Users\sugaw\Documents\Visual Studio 2017\Projects\schedule_nurse_ver3\WindowsFormsApplication1\test\sim_engine32\solution1.txt 5.358(CPU秒)
 *********UB=3(0)  5.358(cpu秒)
  ov=3 ext=702 del=0 depth=0 LB=1.79769e+308 UB=3
 *********LB=3(0)  5.381(cpu秒)
 Algorithm 1 Solving Process Started.
  c output terms=0 weight=1000
  c output terms=0 weight=10
   o        3   0.104(sec) 
c    Bloop 1
  c output terms=3 weight=1
 Status  Optimum.
 充足解を書き込みました。C:\Users\sugaw\Documents\Visual Studio 2017\Projects\schedule_nurse_ver3\WindowsFormsApplication1\test\sim_engine32\solution1.txt 5.586(CPU秒)
 branch_node_map.size()=0
 Algorithm 1 Solving Process Started.
  c output terms=0 weight=1000
  c output terms=0 weight=10
   o        3   0.112(sec) 
c    Bloop 1
  c output terms=3 weight=1
 Status  Optimum.
 充足解を書き込みました。C:\Users\sugaw\Documents\Visual Studio 2017\Projects\schedule_nurse_ver3\WindowsFormsApplication1\test\sim_engine32\solution1.txt 5.727(CPU秒)
 _____________________________________
|           |           |             |
|   Weight  |   Errors  |    Cost     |
|___________|___________|_____________|
|           |           |             |
|      1000 |      0    |        0    |
|        10 |      0    |        0    |
|         1 |      3    |        3    |
|___________|___________|_____________|
|                       |             |
|         Total         |        3    |
|_______________________|_____________|
 *********Final UB=3
 充足解を書き込みました。C:\Users\sugaw\Documents\Visual Studio 2017\Projects\schedule_nurse_ver3\WindowsFormsApplication1\test\sim_engine32\solution1.txt 5.730(CPU秒)
解探索が終了しました。 5 (秒)
解が得られました。

(これより、Algorithm1の解は近似解であることが分かります。)
歩みは遅いですが、確実に性能向上しています。

2019年12月1日日曜日

Pythonでその月だけ制約

import sc3
import re

def get処理月():#処理月を文字列で返す
    s=daydef[0] #daydef=['2019-12-01','2019-12-02'...
    m=re.search(r'-\d+-', s)#-dd- Regular Pattern search
    s=m.group().replace('-','')#dd delete -
    #sc3.print(s)
    return s

def 支援禁止(person,day,phase):#日勤なら余りを強制する
    sam='日勤ならAM余りのみ'+staffdef[person]+' '+daydef[day]
    S=sc3.GetShiftVar(person,day,'日勤')
    vam=sc3.GetTaskVar(person,day,AM,'半日')#余り
    #A->B =!A|B
    sc3.AddHard(~S|vam,sam)
    spm='日勤ならPM余りのみ'+staffdef[person]+' '+daydef[day]
 
    vpm=sc3.GetTaskVar(person,day,PM,'半日')#余り
    #A->B =!A|B
    sc3.AddHard(~S|vpm,spm)

def 今月支援禁止(person):
    s=staffdef[person]+'を今月支援禁止にします。\n'
    sc3.print(s)
    for day in 全日:
        支援禁止(person,day,AM);
        支援禁止(person,day,PM);

def Month支援禁止制約():
    for person in 出勤1月属性.keys():
        if get処理月()=='1':
            今月支援禁止(person)
    for person in 出勤2月属性.keys():
        if get処理月()=='2':
            今月支援禁止(person)
    for person in 出勤3月属性.keys():
        if get処理月()=='3':
            今月支援禁止(person)
    for person in 出勤4月属性.keys():
        if get処理月()=='4':
            今月支援禁止(person)
    for person in 出勤5月属性.keys():
        if get処理月()=='5':
            今月支援禁止(person)
    for person in 出勤6月属性.keys():
        if get処理月()=='6':
            今月支援禁止(person)
    for person in 出勤7月属性.keys():
        if get処理月()=='7':
            今月支援禁止(person)
    for person in 出勤8月属性.keys():
        if get処理月()=='8':
            今月支援禁止(person)
    for person in 出勤9月属性.keys():
        if get処理月()=='9':
            今月支援禁止(person)
    for person in 出勤10月属性.keys():
        if get処理月()=='10':
            今月支援禁止(person)
    for person in 出勤12月属性.keys():
        if get処理月()=='12':
            今月支援禁止(person)
 

スタッフプロパティ、各月で、支援業務遂行不可能なスタッフに0がついています。
Pythonで、それを拾ってきて、今月がその月になったときだけ制約するという究極のメンテナンスフリーを狙った記述になります。(この部分だけ取れば、Excelを毎月読ませることも不要)
Python記述で必要になるのは、今月が該当月に相当するかどうかの判定部です。それには、今月のmonthを取得するルーチンが必要となり、それが、get処理月()になります。正規表現ライブラリを
importして、正規表現で処理しています。正規表現で、シンプルに文字列処理できます。
pythonインタプリタがどこまで頑張れるかよく分かっていないのですが、標準ライブラリについては大丈夫なようです。巨大なライブラリ、例えばopenpyxlは、動きませんでした。

2019年11月29日金曜日

年末年始対応その2

年末年始だけの記述は、下Fig.です。
Day集合年末年始は、Excel稼働日から得ていますが、年末年始でないとき、つまり空集合のときは、ソフトエラーがカウントされてしまいます。 ソフトエラーならまだしも、これがハード制約だったとすると解なしになってしまうので、年末年始外はオフにする必要があります。つまりこの制約はメンテナンスフリーではありません。そこで、同じことをPythonで記述します。
空集合でないときだけ制約をEnableするので、年末年始でないときは、制約されません。
Pythonを用いると、このようにダイナミックに判断することができるので、メンテナンス上好ましい形にすることが出来ます。

別な応用としては、稼働日Day集合名及び、集合要素は、仮想的なものでも構わない筈です。
つまり、Day集合中DayIndexに対応する●の有無をその月のPythonに対するコマンドとしても使えます。
 

2019年11月28日木曜日

導入手順書プロジェクトファイル編

書きました。実は、このマニュアルは、ある特養向けのマニュアルです。予定入力についても相談があったので、自分の考えも書き添えました。

2019年11月27日水曜日

組み合わせ最適化の最前線

梅谷先生の論文は、貴重な示唆に富んでいてとても参考になります。

キーワードは、「最適化のための機械学習」です。恐らく、商用ソルバは、既にその仕組みを組み込んでいるのではないかと思います。(NEOS等で自然にインスタンスデータは集まってきます。数年前解けなかった問題が解けるようになっていたりします。)
SATでもそうですが、特徴のない世界、つまりランダム化したインスタンスは無力で僅か数百変数でも難儀することがあります。しかし、人間界のインダストリアルインスタンスは、人間である以上、なにがしかの偏り、特徴があることは必然であり、 そこに解く鍵がある、ということだと思います。BIGデータが勝者と敗者を分ける、そんな波が組み合わせ最適化の世界に来ているということではないでしょうか?

https://speakerdeck.com/umepon/a-practical-approach-for-hard-combinatorial-optimization-problems-in-real-applications

http://www.orsj.or.jp/archive2/or59-01/or59_1_20.pdf

2019年11月26日火曜日

ShiftとTask

Taskは、未だTutorialに書いていません。
ナーススケジューリングの場合、Taskが必要になることはあまりないのですが、確かに必要となる場合があります。例えば、
NRC2(Nurse Scheduling CompetitionⅡ)では、次のような解になります。

いつものSC3と様子が違うのは、1日が4つに分割されていることです。この場合、4Shift1Phase の例になります。一つのシフトに複数の業務の一つを割り振る場合に必要となるのがTaskです。上の例の場合でも、一つのシフトに複数のTaskが割り振られているのが分かります。

あるいは、シフトをさらに2つに分割して、
3Shift2Phaseの例(1日に6個の独立したTaskが記述)も可能です。やはり、午前と午後で別なTaskを割り振るのに加え、早番遅残業があるという工場でのシフト問題で、超難問です。
 
簡単な例では、1Shift2Phaseというのもあります。日勤外来専用のナース配置問題で、
午前と午後で行き先が違う(Taskが異なる)問題です。
 
Shiftは、1日に一つです。Shiftは、時間帯概念です。基本的に、1Shift内では、一つの仕事となります。
その概念を拡張するのがTaskになります。Taskを用いると複数の仕事の中から一つの仕事を割り振ることが出来ます。さらに、シフトを分割するのがPhaseという概念になります。1シフト内に複数のシーケンス(Phase)を持ち、Phase毎に複数の仕事の中から一つの仕事を割り振りたい場合に使います。
 
 
ポイントは、複数の仕事の中から一つの仕事を割り振る必要があるかということです。Taskを用いると次元が増えるためにGUIでは書けなくなる傾向にあります。(従いPythonで書く必要が出てきます。) ですから、なるべくTaskは、使わなくて済むならば使いたくはないです。
一見Taskが必要に見えても、実はShiftだけで非常に細かく書ける例も存在します。そのような例を次回紹介します。
 
 
 
 
 

2019年11月14日木曜日

Incident Report Continued...

Hi Takayuki,
ライブラリバグではなくて、私のライブラリ使用の仕方に問題があったようです。
Syncfusion社さま、ごめんなさい。

123Aで修正しました。

Thanks for your patience.
We maintain the maximum limit of interior functions as 100 by default. The reported issue occurs when the limit exceeds the default value. This can be avoided by assigning some high value for [a:https://help.syncfusion.com/cr/cref_files/windowsforms/Syncfusion.Calculate.Base~Syncfusion.Calculate.CalcEngine~MaximumRecursiveCalls.html]MaximumRecursiveCalls property of CalcEngine. Please find the code snippet below.
Code snippet:

//To increase the maximum limit for interior functions
worksheet.CalcEngine.MaximumRecursiveCalls = 1000;
Please look into the following link to know more about limitations in CalcEngine.
Help Link: https://help.syncfusion.com/windowsforms/calculate/performance

P.S.
私事ですが、身内に不幸がありしばらくお休みします。

2019年11月13日水曜日

年末年始対応

盆と正月は、大抵のプロジェクトで特別な対応が必要になります。

下は、クライアントプロジェクトの「今月」になります。通常月の今月は、(1日から月末まで)になりますが、「今月」は、(12月1日から1月4日まで)になります。

クライアントの祝定義は、下の通りです。(病院で異なります。妻のところは、9連休らしいです。)
来月の定義も必要になります。
そうすると、公休数や、夜勤数は、「今月」ではなく12月に限定する必要があります。
それが「来月ではない今月」になります。
あとは、計数部分に対してこの定義を適用すればよいです。
この職場の場合、(前月19日から今月18日まで)期間も同じようような制約がかかっています。
年末年始は、
・年末勤務した場合 年始優先
・年始勤務した場合 年末を優先
というご要望を頂いていたのですが、解を出してみると出勤回数は、2回程度でしたので、単純に年末年始内の勤務数を制約することにしました。(意図は、平準化であると考えました。)
このプロジェクトは、メンテナンスフリーではありません。
「来月でない今月」は、年末年始が定義されていない(空集合)状態でも問題にはなりません。
しかし、年末年始内制約は、定義されていない場合エラーとなる可能性があります。
つまり、年末年始でないときは、制約を適用しない操作(適用を外す)が必要となります。

GUIでは、この辺が限界です。次回、同じ処理をPythonで書いてメンテナンスフリー化を試みることにします。

2019年11月12日火曜日

Incident Report Continued..

数ヶ月は、覚悟していたのですが、意外に早いかもしれません。

Hi Takayuki,
Greeting from Syncfusion.
The Excel documents you have shared us (correct.xlsx and wrong.xlsx) have same formula =IF(D5<>"",IF($D$5+COLUMN()-COLUMN($D$5)<=$C$2,$D$5+COLUMN()-COLUMN($D$5),""),"") . However we have modified the formula in wrong.xlsx document as =IF(D5<>"",IF(D5+1<=$C$2,D5+1,""),"") and tried to reproduce the issue.
The CalculatedValue is returned improperly from AK5 cell instead of AM5 cell up to BP5 cell. We have forwarded this issue to our development for further validation. We will update further details on 13th November 2019.

2019年11月11日月曜日

チュートリアル動画作成構想

ナレーションの問題は、AIトークが素晴らしいのですが、いかんせん価格で手がでません。
しかし、最近合成技術が進歩しており、Google、Amazon,MicrosoftでText To Speechを従量課金で、手に入れることができます。WEBのデモが殆どないので、全部を試せてないのですが、そこそこといったレベルでしょうか。未だ違和感があります。
参考にしたのは、

https://blog.systemjp.net/entry/2019/03/26/125644
https://azure.microsoft.com/ja-jp/services/cognitive-services/text-to-speech/
https://qiita.com/yukari120/items/70cdea43743f4a35d85c

https://ttsmp3.com/
最後は、探していたWEBサービスですが、これを使って操作動画を作ってみようかな、と思います。

2019年11月10日日曜日

スケジュール(予定入力)のExcelエクスポート

121Aから実装しています。

Excelから予定入力をImportするのと反対です。この意味は、二つあって、予定入力の段階で、Excelに落としたい場合と、解出力→予定入力へ送る でExcel入力フォームに解を送る
という意味があります。

方法は、Import設定をするのと同じです。(Import設定済みならOKです。)
後は、予定入力から右クリックでExcel出力をクリックしてください。

Excelの予定入力シートをコピーしておきます。(勤務表SC3シート)
書き込むシートにチェックします。

解を求めるときの予定入力状態です。

解を求め予定入力に送ります。

Excelへ送ります。
Excelを開いていると次のようなエラーが出るので、閉じてください。

しっかり解が書き込めました。

私的には、勤務表の設定開発時に、人力解とソフト解の比較に使っています。Excelでスタッフ属性を読むシートは、共通で、全入力(人力解)シート とソフト解(予定だけを入れた)シートだけを分けて、読み込むと他のパラメータを共通にして、解の品質評価が出来ます。つまり一つのプロジェクト
で、人力解とソフト解の品質比較が出来るようになります。プロジェクトが分かれているとあれやこれで、どうしてもパラメータが異なってしまいがちですが、これだと、きっちり入力だけを交換して解の比較ができます。入力を全て埋めた人力解にすれば、人力解とソフト解の比較が出来ます。

ユーザさんのプロジェクトは、仕様というより願望を書いてこられる方が多いです。
そのままSC3解を出しても、満足していない状況には変わりないので、あまり説得力がありません。ところが人力解と比較すると、圧倒的に高品質なので、ぐっと印象が良くなるという仕掛けです。
(目的関数値が実力テストの比較に使えます。0点が一番良いのですが。)

現状認識をまずきちっとやってもらって、「そこからこれだけ良くなる」 というアプローチが重要です。 

2019年11月9日土曜日

Incident Report

使用している商用Excelライブラリでバグがあり、ベンダに対して改善を要求しました。


An  attached excel behavior shows expected behavior. 
Wrong formula calculation occurs in wrong.xlsx
No wrong formula calculations have been seen in correct.xlsx.

The difference between two excel files are as follows
wrong  E5 cell:=IF(D5<>"",IF(D5+1<=$C$2,D5+1,""),"") 
correct E5 cell:=IF(D5<>"",IF($D$5+COLUMN()-COLUMN($D$5)<=$C$2,$D$5+COLUMN()-COLUMN($D$5),""),"")


Apparent wrong calculation starts from AM5 cell in wrong.xlsx
Please note it should result identical results between two excels though they have different formula.
It seems incremental rather deep formula reference causes the wrong calculation.
 

2019年10月31日木曜日

時間制約の改善

を119Aで行いました。118A 以前では、プロジェクトサンプル特養で、Python記述と時間制約では、4-5倍の差がありましたが、ほぼ同じ時間となりました。これにより、Pythonで記述しなくてもよくなりました。

 118A119A
Python制約あり19sec19sec
Python制約なし92sec24sec

ところで、時間制約のほかに、夜勤回数、日勤回数、半日回数を下記のように
制約しています。
 
夜勤回数は、夜勤回数を6回にしないために必要な記述です。その他は、158.5h-160.25hを保証する意味では、必要がない記述です。
そこで、もし、時間制約以外の制約がないとどうなるかやってみました。
案の定、158.5-160.25hを満たす、夜勤回数、日勤回数、半日回数は、上記に限定されず多くの組み合わせが存在することが分かります。
このとき、時間は、34secかかりました。解空間は広がりますが、探索空間も制約が付加されないので広がることに注意します。Algorithm1の場合、かかる時間は、比=解空間/探索空間 に
反比例する方向ですので、時間がよりかかったと推測されます。ともあれ、Pythonで気を配る必要がなくなったのは良い方向だと思います。 
 schduling benchmarksからの結果です。目的関数値は、Instance13を除いて、さして改善していません。悪化している場合もあります。しかし、解が出るまでの時間は、例外なく短くなっています。
Note:1)Algorithm1の比較であることに注意してください。Algrorithm4では、時間をかければ、KnownUB値を得ることは可能です。
2)119Aでバグがあり120Aで修正しました。
 

 Algorithm1
 118A 120A Known UB
Instance9441141(sec)539113(sec)439
Instance104834213(sec)4842170(sec)4631
Instance1339442109(sec)3285(1042sec)1348
Instance141522235(sec)1421209(sec)1278
Instance155249412(sec)5135331(sec)3225
Instance194825616(sec)5035483(sec3149