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は、動きませんでした。