2026年10月29日木曜日

ナーススケジューリング問題ソルバ

 2026年になっても、未だ色々な方式が提案されています。

<各ベンチマークについての言及がない>

ASPに基づく適応型優先度付き巨大近傍探索の実装とナーススケジューリング問題への適用
JSME-TJ
ナース・スケジューリングの論文、勤務表づくりに使える?|手組みゼロ



不思議なことに、アカデミックかつクラシカルなベンチマークがいくつもあるのに、それらに対しては、言及がありません。この傾向は、日本に限らず海外文献でも同様です。最近の論文流行は、AIを使った当該病棟でのモデル改善ですが、これは、モデリングの改善であって、ソルバ性能の改善ではありません。ソルバは、MIPソルバ、CP-SAT等、既存汎用ソルバ、であってソルバそのものの改善でないことが多いです。いずれにせよ、共通インスタンスでのベンチマークについて言及がないので、実際のところ、どの程度の有効性があるのか分かりません。アカデミックベンチマークは、問題規模が段階的に揃っているので、問題規模に対する有効性の評価も出来ます。日本とEUでは、そもそも働きかたそのものが違うので比較にならない、と考える方もいるかもしれませんが、私は、組み合わせ最適化能力と問題規模というパラメータのエッセンスは変わらないと考えています。

一部で、ナーススケジューリング問題は、最近のMIPソルバで解けるようになった、との指摘があるのですが、私の認識は、全然そんなことはありません。感覚的には、「MIPソルバでは、ほとんど解けない」、に近いです。(試しにINRC2問題をMIPソルバにやらせてみてください。ほとんど解けません。Scheduling Benchmarksについても解けるのはInstance-8/16/17位までです。) 

ベンチマーク問題と実務インスタンスで、主に違うのは、ペア制約の有無で、これが、インスタンスをより難しくする方向に作用します。また最近では、同数制約という運用で回避可能なはずの制約まで課されるようになってしまっていますが、これもより難しくする方向に作用します。

いずれにしても、骨格的には、アカデミックベンチマークインスタンスでも間違いはなく、実務ではさらに雑多な制約が入ることで、より難しくなる現実があります。


<まず第一に汎用ナーススケジューリング問題ソルバ>

なので、汎用的なナーススケジューリング問題ソルバの開発がまず第一です。そのソルバは、アカデミックベンチマークについてほとんど全ての厳密解を供給できることが必要要件です。これにより、基本的なナーススケジューリング問題についての組み合わせ最適化能力は担保されます。(皆さん、基本的ソルバの能力向上の必要性を無視されていますが、この問題に取り組んで十余年、私はづっとその必要性を切に感じていました。)

さらに、これを市井の様々なナーススケジューリング問題に適用させようということになると、汎用的フレームワークを再設計する必要があり、現在実装中、という訳です。

物理限界に挑むナーススケジューリング問題に対する標準解法

https://schedule-nurse.blogspot.com/2026/05/blog-post_24.html

史上最大規模のナーススケジューリング問題が解けた、と言っても実務で遭遇するプロジェクトの厳密解がただちに求まる、ということではありません。依然として、何割かのプロジェクトの厳密解を求めることは難しいのでは?と感じています。それ位、実務で扱っている現実のナーススケジューリング問題を厳密に解くことは難しいものなのです。

<次にモデリング>

ソルバの改良が終わったのち、次に行うべきは、日本語による指示によるモデリング方法の開発です。昨今、AIが特段に進化したので、それを利用します。問題は、そのモデリングが適切かどうかは、管理者が解を見ないと分からないので、GUIと連携しながら修正を加えていくスタイルが必須と考えています。プロジェクト作成サービスで、私が行っていることの代わりが出来ることが、究極の目標です。

<まとめ>

アカデミックベンチマークで性能を担保し、なおかつ日本語による指示モデリングで市井のナーススケジューリングの現場の諸問題をモデリングできるようにする、2段構えが究極のソルバである、と考えています。



2026年10月28日水曜日

コーディングの未来

 ビル・ゲイツ氏「AIが人間の雇用の大部分を代替…今後20年が最も混乱」(中央日報日本語版)|dメニューニュース(NTTドコモ)

ビル・ゲイツ氏でさせ敵わないと言わせる昨今のAI進化です。

実際、私も活用して本当だと思います。それでも、全体アーキテクチャ設計は、AIに渡してはいません。全体の中の部品設計においては、活用していますが、トータルを俯瞰した設計については、長い経験の方に未だ歩がある、と見ています。

そうした経験なしに、いきなりAIにコーディングさせることが効率的とは思いません。これから、新卒プログラマは、受難の時代を迎えるのではないでしょうか?

優秀なアーキテクトにとってAIは、優秀な部下を持ったことと同じです。機械的な作業は、AIに任せればよいのですから。少数のアーキテクトさえ居れば、巨大なプロジェクトも可能になる、と思います。

自分は、古き良き時代の、最後の世代になるのかもしれません。



2026年10月26日月曜日

unsigned char(8bit)× int16(16bit)のベクトル積

 unsigned charとint16のベクトル積のSSEコード

結論から言うと、unsigned char(8bit)× int16(16bit)のベクトル積を SSE2 で行うには、 「8bit → 16bit へのゼロ拡張」+「16bit × 16bit の乗算」 という 2 ステップ構成になります。

SSE2 には 8bit × 16bit の直接乗算命令は存在しないため、 unpack(ゼロ拡張)→ PMULLW(16bit multiply) が正解です。

最速の SSE2 コードを完成形で提示します。

🧩 unsigned char × int16 の SSE2 ベクトル積の正しい流れ

1. unsigned char(8bit)を 16bit にゼロ拡張

SSE2 の PUNPCKLBW / PUNPCKHBW を使う。

cpp
__m128i bytes = _mm_loadu_si128((__m128i*)uc);   // unsigned char × 16
__m128i zero  = _mm_setzero_si128();

// 下位8バイト → 16bit に拡張
__m128i uc_lo = _mm_unpacklo_epi8(bytes, zero);

// 上位8バイト → 16bit に拡張
__m128i uc_hi = _mm_unpackhi_epi8(bytes, zero);

これで:

  • uc_lo = 16bit × 8

  • uc_hi = 16bit × 8

に変換される。

2. int16 と 16bit × 16bit の乗算(PMULLW)

cpp
__m128i s16_lo = _mm_loadu_si128((__m128i*)i16);      // int16 × 8
__m128i s16_hi = _mm_loadu_si128((__m128i*)(i16 + 8)); // int16 × 8

// 16bit × 16bit の乗算(下位16bit)
__m128i prod_lo = _mm_mullo_epi16(uc_lo, s16_lo);
__m128i prod_hi = _mm_mullo_epi16(uc_hi, s16_hi);

_mm_mullo_epi16(PMULLW)は:

  • signed 16bit × signed 16bit → 下位 16bit を返す

unsigned char はゼロ拡張しているので signed として扱っても問題なし。

✔ 完成形:unsigned char × int16 の SSE2 ベクトル積コード

cpp
void mul_uc_i16_sse(const unsigned char* uc, const int16_t* s16,
                    int16_t* out)
{
    __m128i bytes = _mm_loadu_si128((__m128i*)uc);
    __m128i zero  = _mm_setzero_si128();

    // 8bit → 16bit ゼロ拡張
    __m128i uc_lo = _mm_unpacklo_epi8(bytes, zero);
    __m128i uc_hi = _mm_unpackhi_epi8(bytes, zero);

    // int16 をロード
    __m128i s16_lo = _mm_loadu_si128((__m128i*)s16);
    __m128i s16_hi = _mm_loadu_si128((__m128i*)(s16 + 8));

    // 16bit × 16bit の乗算
    __m128i prod_lo = _mm_mullo_epi16(uc_lo, s16_lo);
    __m128i prod_hi = _mm_mullo_epi16(uc_hi, s16_hi);

    // 結果をストア
    _mm_storeu_si128((__m128i*)out,     prod_lo);
    _mm_storeu_si128((__m128i*)(out+8), prod_hi);
}

2026年10月24日土曜日

飽和演算

unsigned char同士の演算で負の場合0になるSSE命令は?

結論だけ先に言うと、unsigned char(8bit)同士の演算で「負になったら 0 に飽和する」SSE 命令は存在します。 それは SSE2 の saturating subtract for unsigned bytes:

PSUBUSB(Packed SUBtract Unsigned Saturate Bytes) → 8bit の unsigned subtract で、結果が負になる場合 0 に飽和する。

🔍 なぜ PSUBUSB が該当するのか

SSE2 には「飽和演算(saturating arithmetic)」があり、特に unsigned byte に対しては:

  • PSUBUSB:a - b が負なら 0 に clamp

  • PADDUSB:a + b が 255 を超えたら 255 に clamp

あなたの質問は「unsigned char 同士の演算で負の場合 0 になる SSE 命令」なので、 まさに PSUBUSB がそのものです。

🧠 補足:Intel の saturating arithmetic の仕様

検索結果(Arm の saturating arithmetic の一般仕様)では:

unsigned saturating operation では、結果が negative なら 0 になる

と説明されています。 x86 SSE2 でも同じ概念があり、unsigned byte subtract の saturating 版が PSUBUSB です。

📌 使用例(C/C++ intrinsics)

cpp
#include <emmintrin.h>

__m128i a = _mm_set1_epi8(10);
__m128i b = _mm_set1_epi8(20);

// 10 - 20 = -10 → saturate to 0
__m128i r = _mm_subs_epu8(a, b);

_mm_subs_epu8 が PSUBUSB に対応します。

2026年10月20日火曜日

Q.登録マイクロソフトアカウント(買い切り版)の変更

Q.登録アドレス(買い切り版)ですが、県の規制があり、

 xxx@pref.xxxxxxx.lg.jp 

がうまくマイクロソフトアカウントに登録できないことがわかりました。

そこで、代替の

xxx@outlook.jp 

に登録の変更をお願いいたします。

こちらは、すでにマイクロソフトアカウントを取得済のアドレスになります。

Ans.

変更しました。明日には、有効になっている可能性が高いと思います。明日以降、お送りしたインストールマニュアルに従ってトライしてみてください。


2026年10月19日月曜日

上界と最大の違い

 結論だけ先に言うと、「上界(upper bound)」は“解の値の保証”であり、

「最大(max 制約)」は“変数の許容量の上限”であって、まったく別物。

🎯 一言で区別

  • 上界(upper bound): 目的関数の値について「これ以上は小さくできない/大きくできない」という 探索木全体に対する評価値。

  • 最大(max 制約): あるリソースや変数が「これ以上使ってはいけない」という モデル内の制約式。

📌 もう少し厳密に:最適化の文脈での違い

1. 上界(Upper Bound)

最適化(特に Branch-and-Bound / Branch-and-Price)で使う概念。

  • 最小化問題なら 上界 = 現時点での最良実行可能解の値

  • 下界 = 緩和問題の値(LP, Lagrangian, etc.)

探索木で:

  • 下界が上がる → 探索が絞られる

  • 上界が下がる → より良い実行可能解が見つかった

つまり 上界は探索の進捗を示すグローバルな値。

2. 最大(Max 制約)

モデルの中にある通常の制約。

例:

x1+x2+x3≤10

これは「リソース使用量は最大 10」という意味であり、 目的関数の値とは無関係。

Branch-and-Bound の上界とは全く別の概念。

🧩 混同しやすい理由

  • 最小化問題で「上界が上がる」と言うと、 直感的には「制約の上限が上がる」ように聞こえるが、 まったく違う。

  • 上界は 目的関数の値の話

  • 最大は 制約式の話

2026年10月17日土曜日

COPTの評価ライセンスがExpire

 期限が切れたので、当該箇所を#ifdef COPTで切り離しました。

今までの知見を基に、新たな標準解法を構築して行きます。とりあえずは、ひな形を人力で作ることにします。AIを使わないのは、いきなりAIに任せると理解が難しく結局デバグで手間取ることを避けたかったからです。安定したらコードの最適化はAIに任せようと思います。

私自身、新しいソルバがどのような結果になるのか、分かりません。ときどき、今までとの違いを説明して行きたいと思います。