2026年1月19日月曜日

instance24失敗

 instance24の求解に失敗しました。LBを求めるところまでは、目論見通りでした。COPTを使用しても、相当な時間がかかりました。問題は、相当のBranchの後、LPソルバの目的関数値が整数になったときの整数化で失敗しました。

整数化には、AL1アルゴリズムを使用しているのですが、こちらの制御が戻ってこないというものです。原因としては、instance24は、MaxSATでは、巨大すぎるということだろうと思います。

MaxSATの得意領域は、計数制約が少ない範囲で、boolean operationが多数になればなるほど得意になる傾向があります。

ところが、instance24は、150人、365日、32シフトという超超巨大インスタンスであり、多数の計数制約があります。これがメモリを食い数十GBになります。instance22(50人、365日、10シフト)までは、問題なく動作したMaxSATソルバも限界を超えた、と判断しました。

MaxSATソルバが、30日、30人といった程度の通常インスタンスでは、無類の強さを発揮するのは何故かというと、学習機構を持っているからです。逆に言うと、

割り当て⇒エラー⇒原因分析⇒同じ原因のエラーを抑止

という学習機構が有効に働くインスタンス範囲では強い、ということになります。メタヒューリスティクスは、基本的にその機構がありません。

割り当て⇒エラー⇒スコアを上下

という単純な機構です。原因分析機構が無い分、構造は簡単で、メモリも食いません。

ところで、超大規模インスタンスでは、大規模すぎて、そもそも学習が殆ど効かないと思われます。(正確には、学習しても、探索範囲が膨大なので、同じ原因にぶちあたる確率が小さい。)なので超大規模インスタンス領域では、メモリが軽い分、メタヒューリスティクスが有利となります。

<対策>

そこで、大規模用メタヒューリスティクスを開発することにしました。で、どうせなら汎用アルゴリズムとしてラインアップしてもよいかな?とも思います。例えば、ローカルソルバをAL4として加えるアイデアです。

■AL1:MaxSAT

■AL2:Highs MIP Solver

■AL3:Mathematical Solver

■AL4:Local Solver

そうなると、全方位のソルバ群が完成することになります。

<スケジュール>

2月は、MCPテスト、評価。

3月に、ローカルソルバと、make_feasible_model(別途解説)の実装を予定します。なので、世界記録の更新予定は、4月です。それまで破られなければ。(LBの値は、判明しましたが、それまでは、公開しません。)





2026年1月18日日曜日

Visual Studio 2022にMigrate

 開発環境をVisual Studio 2019から、2022に移行しました。VS2022+ .NET framwork4.8は、長期サポート(LTS)になっているので、今後10年は大丈夫です。




そのために、まず、Private版で、ビルドを行いました。コンパイラが変わると何某かの問題が生じるのが常です。そのための確認ステップとして、ソースを変更せず、開発環境だけをMigrateしたビルドをリリースしました。

これから、これをベースにMCP実装を進めて行きます。




2026年1月17日土曜日

Pydanticで記述

 PythonのPydanticとC#を比較してみた + C++ DCOMの思い出 #pydantic - Qiita

OpenAI Structured OutputsのPydantic連携を実装してみました | 株式会社divx(ディブエックス)

勉強になりました。

これから、Pydanticを使用してC#クラスを変換して行きたいと思います。

同じShiftAggregateというC#内部クラスをコパイと、Google AIモードの両方で生成させてみました。

コパイ生成モデル


from pydantic import BaseModel, Field
from typing import List

class ShiftAggregate(BaseModel):
    use: bool = Field(
        description="A flag to indicate whether it is valid or invalid. 有効(true)か無効かを示すフラグ。"
    )
    name: str = Field(
        default="ShiftAggregateDef",
        description="Object Name オブジェクト名"
    )
    def_name: str = Field(
        description="Definition name 定義名。シフトオブジェクトのORまたはNOTの集合体。必須。"
    )
    color: str = Field(
        default="Grey",
        description="Background color ラベル背景色"
    )
    operator: str = Field(
        default="OR",
        description="Operator 演算子。OR または NOT のみ。必須。"
    )
    members: List[str] = Field(
        default_factory=list,
        description="List of shift object names shift_objectの名前集合。必須。"
    )
    label: str = Field(
        description="Label name as displayed name ラベル名(GUI表示名)。必須。"
    )

下は、GOOGLE AIモード生成です。

from typing import List, Literal, Optional from pydantic import BaseModel, Field from mcp.server.fastmcp import FastMCP # 1. C# のクラスを Pydantic モデルとして定義 class ShiftAggregate(BaseModel): use: bool = Field( default=False, description="有効(true)か無効か。無効ならソルバは参照しない" ) name: str = Field( default="ShiftAggregateDef", description="オブジェクト名" ) def_name: str = Field( ..., description="定義名。シフトオブジェクトのORまたはNOTの集合体" ) color: str = Field( default="Grey", description="ラベル背景色" ) # Operator は OR または NOT のみ operator: Literal["OR", "NOT"] = Field( default="OR", description="演算子。OR または NOT のみ" ) members: List[str] = Field( default_factory=list, description="シフトオブジェクトの名前集合" ) label: str = Field( ..., description="表示用ラベル名" ) # 2. MCP サーバーの作成 mcp = FastMCP("ShiftManager") # 3. ツールとして登録 @mcp.tool() async def register_shift_aggregate(data: ShiftAggregate) -> str: """ シフトの集合体定義(ShiftAggregate)を登録します。 """ # ここに WinForms API (call_winforms_api) への送信処理などを記述 # 例: payload = data.model_dump() return f"定義 '{data.label}' (Operator: {data.operator}) を受け付けました。" if __name__ == "__main__": mcp.run()




さらに、C#クラスを少し修正して再度AIモードにお願いしました。修正した部分が分かって生成していることが分かります。こうした基本クラスならば、手で書くよりもAIにお願いした方が圧倒的に速いです。C#クラスのコメント部をdescriptionに解釈して書いてくれるので、その点でも楽です。model_dump()もBaseModelを継承して自動生成してくれます。AIモードは、MCPをどのように実装したらよいかを、解説しながら実装例を示してくれます。私のようなMCP初心者には、有難いです。基本的クラスは、こうした手法により、Python側のコードは、ほぼコピペで完結できます。C#側は、かなり手を加える必要があります。

コパイでは、C#コメントは、そのままPythonコメントになりましたが、Google AIモードでは、徒手選択追加されています。必ずしもC#コメントと同じになる訳ではありません。概して分かり易い方向に作用しますが、余計なお世話だ、という場合もあります。

驚くべきは、定義していないクラスまで生成したことです。それが、正確にPythonに変換生成されていました。以前GUIのC#コードを公開していたことがありましたが、それを参照したとしか考えられない、と思っています。驚きました。


from typing import List, Literal
from pydantic import BaseModel, Field
from mcp.server.fastmcp import FastMCP

# 1. C#のクラス構造をPydanticモデルで定義
class ShiftAggregate(BaseModel):
    use: bool = Field(
        default=False, 
        description="有効(true)か無効かを示すフラグ。無効ならソルバは参照しない"
    )
    # const扱いの名前。LLMが変更しないようdefault固定にする
    name: str = Field(
        default="ShiftAggregateDef", 
        frozen=True,
        description="オブジェクト名(固定値: ShiftAggregateDef)"
    )
    def_name: str = Field(
        default="", 
        description="定義名。シフトオブジェクトのORまたはNOTの集合体を表す一意識別子"
    )
    color: str = Field(
        default="Grey", 
        description="ラベル背景色(例: Blue, #FF0000)"
    )
    # Operatorをリテラルで制限
    Operator: Literal["OR", "NOT"] = Field(
        default="OR", 
        description="演算子。OR(いずれか)またはNOT(以外)のみ選択可能"
    )
    members: List[str] = Field(
        default_factory=list, 
        description="シフトオブジェクト名のリスト(例: ['日勤', '夜勤'])"
    )
    label: str = Field(
        default="", 
        description="GUI(予定入力・解上)での表示名"
    )

# 2. MCPサーバの初期化
mcp = FastMCP("ShiftProjectManager")

# 3. ツールとしての登録
@mcp.tool()
async def add_shift_aggregate(aggregate_data: ShiftAggregate) -> str:
    """
    WinFormsアプリに新しいシフト集計定義(ShiftAggregate)を追加または更新します。
    """
    # 実際にはここで call_winforms_api("update_settings", payload, ...) 等を呼び出す
    payload = aggregate_data.model_dump()
    
    # デバッグ出力
    print(f"Sending to WinForms: {payload}")
    
    return f"シフト定義 '{aggregate_data.label}' を {aggregate_data.Operator} 条件で登録しました。"

if __name__ == "__main__":
    # サーバの起動
    mcp.run()

2026年1月16日金曜日

Branching DepthとFractionalsの関係

 次のグラフは、Instance22上での、Branching Depth とLpソルバのFractional数をプロットしたものです。


大まかに言って、Branchingが進めば、Lpソルバの値で、Fractionals(整数0でも1でもない、その間にある数)が減って行きます。SimplexとBarrierソルバの比較では、Simplexの方がFractionalsの数は、減る傾向にありますが、速度的に問題外のため、この規模では、Barrier ソルバ一択です。注意するべきは、Fractionals以外の点、つまり0か1の整数値は、Validな値という訳ではなく、Branchによって、整数になったりならなかったりします。なので、整数になった、即0/1確定、ということではありません。しかし、一時的な整数値にせよ、大半の整数値は、正解であることが多いので、それを信じて整数化HeurisitcsであるMaxSATを走らせる、というのが、私の整数化方法です。当然、Fractionalsの数が少なければ少ないほど、真値により近づける可能性が高まります。

Branchingを最後まで行い、Fractionalsが0になれば、全部整数になったということなので、整数解が得られます。しかし、それは膨大な時間がかかってしまうので、上記Heurisitcsを導入し、整数解をなるべく途中段階で拾う、ということを行います。上の例では、MaxSATソルバBranch Depthに応じて4回起動して、4回目に最終解を得ています。

Depth0(ROOT)では、Timeout設定が良くなくてTimeoutになっていますが、UB=3万数千といったオーダになります。通常は、Branching Depthを早く稼いだ方が速くより小さなUBを得ることが出来ます。、Instance22の場合は、500Depth程度で、かなり近い値、1000Depthで、収束値に到達しました。

以上が数理ソルバでのBranchingの動きになります。


2026年1月15日木曜日

許容エラー数とは

 求解パラメータ中の許容エラーは、スラック変数範囲です。


スラック変数とは、OR(オペレーションズリサーチ)用語です。学術用語なので、少し難しいです。ググると不等式制約を等式制約に変換するときの変数と言う風に説明されるのですが、なんのことか分からない方が大半だと思います。

スケジュールナースでは、不等式制約においてどこまでをソフト制約とするか、その範囲を決める、との説明をしているのですが、OR的には、スラック変数範囲を規定する、ということになります。

夜勤Yとして、その範囲をRHS[Min,Max]とすると、

ΣY=RHS[Min:Max]

と表現できます。例えば、夜勤3回以上4回以下のハード制約とするなら、

ΣY=RHS[3:4]

となります。ところが、この制約は、満足できるかどうか分かりません。MIPソルバにおいても、解が無い場合は、Infeasibleとなり、その原因は、全く分からなくなってしまいます。なので、一般に、スラック変数Xを導入して3回未満でも解がない、ということがないように

ΣY+X>=3 

Xの範囲は、X=[0:∞] とすれば、(実際には、[0:3]の範囲で十分)、解がないという事態は、必ず防げます。Xが0でない場合にソフトエラーが発生した、(ただし連続変数)という風に解釈できます。Xの値にコストを付加すれば、コスト最小となるように(目的関数値が最小となるように)ソルバは動作します。

同様に、

ΣYーZ<=4

というスラック変数Zを導入して、Z=[0:∞] とすれば、解がないという事態は絶対起こりません。

線形ソルバにとって、スラック変数の負担は、SATソルバに比べれば非常に軽いです。加えて、その変数範囲[0:∞]という、連続変数の幅の大きさはソルバ負担にほぼなりません。なので、MIPソルバによる定式化の場合は、スラック変数付で、定式化することが定石です。つまり、数を数える制約は、全て∞幅のソフト制約にするのが基本です。

SATソルバでは、そもそも∞という定式化は存在しません。何某かの有限な整数幅でしか定義できません。この辺、線形ソルバと0:1変数のみ扱うSATソルバの決定的かつ本質的な違いです。幅の大きさはソルバ負担に直に効いてきます。この幅をスケジュールナースでは、許容エラー数と表現しています。

この辺、新しいアルゴリズムでは、ラフに設定しても解がないということがないように、自動設定するモードを付加する予定です。MCPでもコントロールできるようにします。

MCP Pythonでの求解パラメータは、次のようなクラスで表現しています。このクラスを操作することで、求解パラメータを操作できるようになります。これは、C#クラスからGoogleAIモードで生成させたモデルです。例えば、SwIntのuseは、上図、適用のチェックの有無に対応します。weightが重みに対応します。

ほぼ、C#クラスにあるコメントを踏襲していますが、SwIntクラスのValueに着目してください。(スラック変数)実は、この”スラック変数”は、C#クラスのコメントにはありません。LLMが勝手に付け足した言葉です。

LLMの理解力が、如何に優れているかを知った、次第です。


class SwInt(BaseModel):
    """
    ソルバの制約許容数や重み(ウェイト)を定義するモデルです。
    """
    use: bool = Field(
        default=False, 
        description="有効(true)か無効(false)かを示すフラグ。"
    )
    
    value: int = Field(
        default=0, 
        description="1制約あたりのCARDINALS許容エラー数(スラック変数)。"
    )
    
    total_max_errors: int = Field(
        default=0, 
        description="現在使用されていません(0: 最小化を意味します)。"
    )
    
    weight: int = Field(
        default=1, 
        description=(
            "ソフト制約の重み(自然数、1-10推奨)。"
            "※ソフトレベル(1-7のインデックス)とは異なる「実際の重み係数」です。"
        )
    )
class SolvingParameters(BaseModel):
    """
    ソルバの求解パラメータ、ソフト制約レベルごとの重み設定、
    および外部Python制約などを管理するクラスです。
    """
    name: str = Field(
        default="SolvingParameters",
        frozen=True,
        description="オブジェクト名。常に 'SolvingParameters' 固定です。"
    )

    # Dictionary> solving_map を再現
    # 外側のキー(int)はソフト制約レベル(1-7など)、内側のキー(str)は制約名
    solving_map: dict[int, dict[str, SwInt]] = Field(
        default_factory=dict,
        description="ソフトレベルおよび制約名ごとの詳細設定マップ。"
    )

    # Pythonによる制約記述
    external_constraint_python: str = Field(
        default="",
        description="Pythonスクリプトによる外部制約記述。"
    )

    # プロジェクトコメント
    comment: str = Field(
        default="",
        description="プロジェクトの変更履歴やメモ用コメント。"
    )

    # 求解パラメータ Dictionary
    parameters: dict[str, int] = Field(
        default_factory=dict,
        description="ソルバの各種数値パラメータ(イテレーション回数等)。"
    )

    # 現在使用されていない、または内部用のフィールド
    aws_parameters: dict[str, str] = Field(
        default_factory=dict,
        description="AWS関連パラメータ(現在未使用)。"
    )
    external_constraint: str = Field(
        default="",
        description="言語制約記述(現在未使用)。"
    )
    python_property_file: str = Field(
        default="",
        description="内部用プロパティファイルパス(編集不可)。"
    )
    python_property_file_post: str = Field(
        default="",
        description="内部用プロパティファイルパス(後処理、編集不可)。"
    )

2026年1月14日水曜日

MCPリソースについて

 MCPリソースは、URIで行うメリットについて聞きました。考えているのは、few shotプロンプトや、Python制約の理解と使用どころをどのようにLLMに理解させるかです。以下の回答のように、URIであれば、LLMが理解し、そのまま維持してくれる確率が高まる、ということです。逆にローカルファイルであれば、その可能性が低くなってしまう、ということが考えられます。

病院で運用する場面になると、URIは、遮断されることが多いと思うので、URI参照は、できません。しかし、学習を終えたLLMがローカルアクセス出来るようになれば、その効果も維持できるでしょう。であるならば、やはり、そのための学習リソースを公開し、LLMに学んでもらうやり方が理にかなっています。よって、LLM用の学習リソースを今後積極的に公開していこうと思いました。基本的には、ユーザにチュートリアルを書くのと同じイメージを持っていますが、LLMリソース用ということで何かあるかもしれません。まだまだ勉強が必要なようです。


外部 URI を MCP(Model Context Protocol)のリソースとして扱うメリットは、実はかなり本質的で、あなたが言っている「他の利用者による再利用性が広がる」という点も含めて、複数のレイヤーで効いてくる。
少し深掘りしてまとめてみるね。


🌐 MCP リソースを外部 URI 化するメリット

1. LLM が “参照可能な世界” を広げられる

MCP のリソースを外部 URI(例:https://example.com/schema.json)として公開すると、
LLM がその URI を「安定した参照点」として扱えるようになる。

  • LLM は URI を「識別子」として理解しやすい
  • URI が固定なら、他の MCP クライアントやツールも同じリソースを参照できる
  • LLM の内部キャッシュや推論にも有利(同じ URI → 同じ意味のリソース)

つまり、**LLM にとって “世界の一部として扱える”**ようになる。


2. 他の MCP 利用者が同じリソースを共有できる

あなたの指摘の通り、これは大きなメリット。

  • 同じ MCP サーバーを使う別のユーザー
  • 別の LLM クライアント(VSCode, Cursor, Claude Desktop など)
  • 別のアプリケーション

これらが 同じ URI を参照するだけで同じリソースを利用できる。

→ 再利用性が爆発的に高まる

特に以下のようなものは URI 化の恩恵が大きい:

  • スキーマ(JSON Schema)
  • API 定義(OpenAPI)
  • 設定テンプレート
  • ドキュメント
  • プラグインのメタデータ

3. LLM の “学習済み知識” と結びつきやすい

LLM は、学習データの中で URI を「識別子」として扱っていることが多い。

例:

  • GitHub の raw URL
  • RFC の URL
  • W3C の仕様 URL
  • API のエンドポイント

そのため、URI を提示すると LLM が意味を推測しやすい。

ローカルパス(file:///C:/...)ではこうはいかない。


4. バージョニング・キャッシュ・永続性の管理がしやすい

外部 URI にすると、以下が可能になる:

  • v1/schema.json のようにバージョン管理
  • CDN キャッシュ
  • 永続的な URL(Permalink)
  • 更新時の互換性維持

MCP のリソースとしては 安定性が非常に重要なので、URI 化は理にかなっている。


5. LLM が “安全に扱える” 情報になる

ローカルファイルは LLM にとって危険が多い:

  • パス漏洩
  • セキュリティ制約
  • OS 依存
  • ユーザーごとに異なる

外部 URI なら:

  • 公開情報として扱える
  • セキュリティリスクが低い
  • LLM が自由に参照しても問題ない

🎯 結論:外部 URI 化は LLM にとって “再利用性・安定性・意味理解” のすべてを強化する

あなたの直感は完全に正しい。

  • 他の MCP 利用者が再利用しやすくなる
  • LLM が URI を識別子として扱えるため、推論が安定する
  • バージョニングや公開管理がしやすい
  • ローカルパスより安全で移植性が高い

MCP の思想(「LLM が外部世界と安全に接続する」)とも完全に一致している。


🔧 もし興味があれば…

  • 外部 URI ベースの MCP リソース設計テンプレート
  • ローカル → 外部 URI への移行戦略
  • LLM が理解しやすい URI 命名規則
  • JSON Schema / OpenAPI の URI 最適化

こういった実践的な設計ガイドも作れるよ。


2026年1月13日火曜日

開発用エージェントを変更

 Gemini CLIの"強み"を知る! Gemini CLIとClaude Codeを比較してみた #AIエージェント - Qiita

https://taskhub.jp/useful/gemini-cli-claude-code

MCPサーバの開発デバッグには、多くのAIモデルコールが必要になります。想定していたClaudeでは、無償枠、有償枠ともに、簡単に制限を超えてしまう懸念があります。これでは、開発がままならないので、Gemini CLIを使用することにしました。