@ohtaman/washi 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.ja.md +149 -121
  2. package/README.md +149 -121
  3. package/README.zh-CN.md +150 -122
  4. package/THIRD_PARTY_LICENSES.md +3 -4
  5. package/dist/browser/_washi-node-only-BvRk9kiK.js +0 -0
  6. package/dist/browser/browser-runtime-TLCY0hkc.js +1793 -0
  7. package/dist/browser/highs-worker.js +21 -3
  8. package/dist/browser/highs.wasm +0 -0
  9. package/dist/browser/highs.wasm.LICENSE +1 -2
  10. package/dist/browser/loader-DewlSho8.js +643 -0
  11. package/dist/browser/solvers/glpk.js +433 -286
  12. package/dist/browser/solvers/glpk.min.js +1 -1
  13. package/dist/browser/solvers/z3.js +1008 -981
  14. package/dist/browser/solvers/z3.min.js +2 -2
  15. package/dist/browser/washi.js +2778 -673
  16. package/dist/browser/washi.min.js +3 -4
  17. package/dist/lib/_washi-node-only-BvRk9kiK.js +0 -0
  18. package/dist/lib/highs-browser-runtime.js +2120 -4124
  19. package/dist/lib/highs-node-runtime.js +24 -1
  20. package/dist/lib/highs-runtime.js +2 -0
  21. package/dist/lib/highs-worker.js +21 -3
  22. package/dist/lib/highs.wasm +0 -0
  23. package/dist/lib/highs.wasm.LICENSE +1 -2
  24. package/dist/lib/loader-BZUQNvh4.js +649 -0
  25. package/dist/lib/registry-C20sxTwm.js +964 -0
  26. package/dist/lib/solvers/glpk.js +201 -90
  27. package/dist/lib/solvers/z3.js +399 -259
  28. package/dist/lib/washi.js +2363 -1056
  29. package/dist/types/decomposition/benders.d.ts +32 -0
  30. package/dist/types/decomposition/benders.d.ts.map +1 -0
  31. package/dist/types/decomposition/column-generation.d.ts +34 -0
  32. package/dist/types/decomposition/column-generation.d.ts.map +1 -0
  33. package/dist/types/decomposition/index.d.ts +4 -0
  34. package/dist/types/decomposition/index.d.ts.map +1 -0
  35. package/dist/types/decomposition/types.d.ts +230 -0
  36. package/dist/types/decomposition/types.d.ts.map +1 -0
  37. package/dist/types/index.d.ts +5 -4
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/modeler/big-m.d.ts +75 -0
  40. package/dist/types/modeler/big-m.d.ts.map +1 -0
  41. package/dist/types/modeler/boolean-expr.d.ts +103 -14
  42. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  43. package/dist/types/modeler/constraint.d.ts +47 -9
  44. package/dist/types/modeler/constraint.d.ts.map +1 -1
  45. package/dist/types/modeler/expression.d.ts +123 -28
  46. package/dist/types/modeler/expression.d.ts.map +1 -1
  47. package/dist/types/modeler/index.d.ts +11 -6
  48. package/dist/types/modeler/index.d.ts.map +1 -1
  49. package/dist/types/modeler/linearizer.d.ts +15 -0
  50. package/dist/types/modeler/linearizer.d.ts.map +1 -0
  51. package/dist/types/modeler/lp-writer.d.ts +30 -3
  52. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  53. package/dist/types/modeler/model.d.ts +623 -131
  54. package/dist/types/modeler/model.d.ts.map +1 -1
  55. package/dist/types/modeler/objective.d.ts +59 -0
  56. package/dist/types/modeler/objective.d.ts.map +1 -0
  57. package/dist/types/modeler/parser.d.ts +34 -10
  58. package/dist/types/modeler/parser.d.ts.map +1 -1
  59. package/dist/types/modeler/quad-expr.d.ts +26 -7
  60. package/dist/types/modeler/quad-expr.d.ts.map +1 -1
  61. package/dist/types/modeler/solution-validator.d.ts +93 -0
  62. package/dist/types/modeler/solution-validator.d.ts.map +1 -0
  63. package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
  64. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  65. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -1
  66. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -1
  67. package/dist/types/modeler/solvers/highs/driver.d.ts +232 -0
  68. package/dist/types/modeler/solvers/highs/driver.d.ts.map +1 -0
  69. package/dist/types/modeler/solvers/highs/index.d.ts +1 -0
  70. package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -1
  71. package/dist/types/modeler/solvers/highs/loader.d.ts +38 -13
  72. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  73. package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -1
  74. package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -1
  75. package/dist/types/modeler/solvers/highs/solver.d.ts +12 -5
  76. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  77. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  78. package/dist/types/modeler/solvers/mock.d.ts +14 -5
  79. package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
  80. package/dist/types/modeler/solvers/types.d.ts +73 -11
  81. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  82. package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
  83. package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
  84. package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
  85. package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
  86. package/dist/types/modeler/types.d.ts +86 -1
  87. package/dist/types/modeler/types.d.ts.map +1 -1
  88. package/dist/types/modeler/validation.d.ts +56 -0
  89. package/dist/types/modeler/validation.d.ts.map +1 -0
  90. package/dist/types/modeler/variable.d.ts +48 -14
  91. package/dist/types/modeler/variable.d.ts.map +1 -1
  92. package/docs/examples/diet-sensitivity.md +90 -0
  93. package/docs/examples/ecommerce-coupons.md +106 -0
  94. package/docs/examples/index.md +57 -0
  95. package/docs/examples/knapsack.md +60 -0
  96. package/docs/examples/portfolio-qp.md +83 -0
  97. package/docs/examples/production.md +51 -0
  98. package/docs/examples/shift-scheduling.md +175 -0
  99. package/docs/examples/smt-security.md +74 -0
  100. package/docs/examples/transportation.md +90 -0
  101. package/docs/examples/traveling-salesperson.md +173 -0
  102. package/docs/examples/unsat-core.md +64 -0
  103. package/docs/examples/vehicle-routing.md +206 -0
  104. package/docs/getting-started/ai-agents.md +271 -0
  105. package/docs/getting-started/cross-origin.md +87 -0
  106. package/docs/getting-started/index.md +116 -0
  107. package/docs/getting-started/runtimes.md +227 -0
  108. package/docs/guide/constraints.md +177 -0
  109. package/docs/guide/decomposition.md +399 -0
  110. package/docs/guide/expressions.md +204 -0
  111. package/docs/guide/objectives.md +170 -0
  112. package/docs/guide/unsat-core.md +154 -0
  113. package/docs/guide/variables.md +188 -0
  114. package/docs/ja/getting-started/ai-agents.md +270 -0
  115. package/docs/solvers/custom-solver.md +525 -0
  116. package/docs/solvers/glpk.md +66 -0
  117. package/docs/solvers/highs.md +115 -0
  118. package/docs/solvers/index.md +42 -0
  119. package/docs/solvers/z3.md +78 -0
  120. package/docs/zh/getting-started/ai-agents.md +270 -0
  121. package/llms-full.txt +4065 -0
  122. package/llms.txt +201 -0
  123. package/package.json +32 -16
  124. package/skills/washi/SKILL.md +197 -0
  125. package/dist/browser/browser-runtime-D13gHOT5.js +0 -4189
  126. package/dist/browser/loader-CVt4CloI.js +0 -58
  127. package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
  128. package/dist/lib/constraint-BRuB3ooC.js +0 -90
  129. package/dist/lib/loader-Dti0v9yC.js +0 -50
  130. package/dist/lib/registry-DksqCSjI.js +0 -32
package/README.ja.md CHANGED
@@ -2,9 +2,9 @@
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="./assets/washi-header-dark.png">
4
4
  <source media="(prefers-color-scheme: light)" srcset="./assets/washi-header-light.png">
5
- <img alt="WaSHI - TypeScript / JavaScript のための数理最適化・代数モデラー" src="./assets/washi-header-light.png" width="420" />
5
+ <img alt="WaSHI - 数理最適化と制約プログラミングのための代数モデラー (TypeScript / JavaScript)" src="./assets/washi-header-light.png" width="420" />
6
6
  </picture>
7
- <p><strong>TypeScript / JavaScript のための代数モデラー</strong></p>
7
+ <p><strong>数理最適化と制約プログラミングのための代数モデラー</strong></p>
8
8
  <p>
9
9
  <em>線形計画 (LP) • 混合整数計画 (MIP) • 凸二次計画 (QP) • SMT論理制約 &amp; 矛盾診断</em>
10
10
  </p>
@@ -16,7 +16,7 @@
16
16
  <p>
17
17
  <a href="https://www.npmjs.com/package/@ohtaman/washi"><img src="https://img.shields.io/npm/v/@ohtaman/washi?color=crimson&label=npm" alt="npm version" /></a>
18
18
  <a href="https://github.com/ohtaman/washi/actions/workflows/distribution.yml"><img src="https://github.com/ohtaman/washi/actions/workflows/distribution.yml/badge.svg" alt="CI Status" /></a>
19
- <a href="https://ohtaman.github.io/washi/"><img src="https://img.shields.io/badge/docs-online-blue" alt="docs" /></a>
19
+ <a href="https://ohtaman.github.io/washi/ja/"><img src="https://img.shields.io/badge/docs-online-blue" alt="docs" /></a>
20
20
  <a href="https://ohtaman.github.io/washi/demo/"><img src="https://img.shields.io/badge/demo-playground-brightgreen" alt="live demo" /></a>
21
21
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license" /></a>
22
22
  </p>
@@ -24,24 +24,24 @@
24
24
 
25
25
  ---
26
26
 
27
- **WaSHI**(WebAssembly-backed, Solver-independent, High-level modeling Interface)は、Python の **PuLP** や Julia の **JuMP** に着想を得た、TypeScript / JavaScript 向けの代数モデラーです。アプリケーションのコードで変数・目的関数・数値制約や論理制約を記述できます。WaSHI はモデルの構築、ソルバー向けの変換、変数への結果の反映を担当し、最適化や充足可能性の判定は **HiGHS**・**Z3**・**GLPK** が行います。
27
+ **WaSHI**(WebAssembly-backed, Solver-independent, High-level modeling Interface)は、Python の **PuLP** や Julia の **JuMP** に着想を得た、数理最適化と制約プログラミングのための TypeScript / JavaScript 向け代数モデラーです。アプリケーションのコードで変数・目的関数・数値制約や論理制約を記述できます。WaSHI はモデルの構築、ソルバー向けの変換、変数への結果の反映を担当し、最適化や充足可能性の判定は **HiGHS**・**Z3**・**GLPK** が行います。
28
28
 
29
29
  HiGHS が標準ソルバーで、Z3 と GLPK は任意で追加するプラグインです。Node.js 用とブラウザ用の配布物に WebAssembly アセットを含みます。配信に必要な設定は[ブラウザでの利用方法](#-ブラウザ--web-worker-での利用方法)を参照してください。
30
30
 
31
- [ブラウザデモを試す](https://ohtaman.github.io/washi/demo/) · [サンプルのソースと実行方法](https://github.com/ohtaman/washi/blob/main/examples/README.md) · [ESM / script / CodePen のサンプル](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)
31
+ [ドキュメント](https://ohtaman.github.io/washi/ja/) · [ブラウザデモを試す](https://ohtaman.github.io/washi/demo/) · [AIエージェント向けクイックリファレンス](docs/ja/getting-started/ai-agents.md) · [Agent Skill (`SKILL.md`)](skills/washi/SKILL.md) · [サンプルのソースと実行方法](https://github.com/ohtaman/washi/blob/main/examples/README.md) · [ESM / script / CodePen のサンプル](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)
32
32
 
33
33
  ```ts
34
- import { Model, expr } from "@ohtaman/washi";
34
+ import { Model, expr, maximize } from "@ohtaman/washi";
35
35
 
36
36
  const m = new Model("木工製品の利益最大化");
37
37
  const x = m.integer("机", { lb: 0 });
38
38
  const y = m.integer("椅子", { lb: 0 });
39
39
 
40
- m.maximize(expr`30 * ${x} + 50 * ${y}`);
41
- m.constrain(expr`${x} + 2 * ${y} <= 40`, "木材上限");
42
- m.constrain(expr`2 * ${x} + ${y} <= 50`, "労働力上限");
40
+ m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
41
+ m.constraint(expr`${x} + 2 * ${y} <= 40`, "木材上限");
42
+ m.constraint(expr`2 * ${x} + ${y} <= 50`, "労働力上限");
43
43
 
44
- await m.optimize();
44
+ await m.solve();
45
45
  console.log(`生産量: x=${x.value}, y=${y.value} (最大利益: $${m.objectiveValue})`);
46
46
  ```
47
47
 
@@ -49,10 +49,11 @@ console.log(`生産量: x=${x.value}, y=${y.value} (最大利益: $${m.objective
49
49
 
50
50
  ## 🎯 WaSHI が提供するもの
51
51
 
52
- 1. **代数的なモデル記述**: 直感的なドメイン変数メソッド(`m.binary`, `m.integer`, `m.continuous`)、タグ付きテンプレート(`expr`)、ベクトル集計関数(`dot`, `sum`)、そして第1推奨の `m.constrain()` により、数学的定義に忠実なモデルを簡潔に記述できます。
53
- 2. **ソルバーアダプター**: 共通のモデリング API から HiGHS・Z3・GLPK を利用でき、[独自アダプターも登録できます](https://github.com/ohtaman/washi/blob/main/docs/custom-solver-guide.md)。モデルの問題クラスを扱えるソルバーを選ぶ必要があり、対応機能はソルバーごとに異なります。
54
- 3. **ローカル実行と WebAssembly の配布**: ローカルソルバーのアダプターでは、別途 Python サービスや C/C++ のネイティブビルドを用意せず、アプリケーション内で求解できます。ブラウザへの配布には生成された JS・Worker・WASM が必要です。オフライン利用には、これらをローカルに配置するかキャッシュしておく必要があります。Z3 には追加のブラウザ隔離設定があります。
55
- 4. **名前付き制約と SMT の結果**: Z3 を明示的に使って充足可能性を確認し、矛盾する制約の調査に Unsat Core を利用できます。返されるのは矛盾する制約の部分集合であり、最小の集合とは限りません。
52
+ 1. **代数的なモデル記述**: ドメイン変数メソッド(`m.binary`, `m.integer`, `m.continuous`)、タグ付きテンプレート(`expr`)、ベクトル集計関数(`dot`, `sum`)、およびファーストクラスの制約(`m.constraint`)によりモデルを記述します。JavaScript には演算子オーバーロードがないため、`` expr`...` `` は SQL のパラメータバインディングと同様に、中置記法の演算子(`+`, `-`, `*`, `^2`, `<=`, `>=`, `==`)を構文解析しつつ `${...}` に渡された値を型付きの `Var` や式のオブジェクト参照として直接バインドします。これにより、TypeScript の型検査、IDE のシンボルリネーム、多次元変数の添字参照(`${flow[i][j]}`)がそのまま機能します。
53
+ 2. **ソルバーアダプター**: 共通のモデリング API から HiGHS・Z3・GLPK を利用でき、[独自アダプターも登録できます](https://github.com/ohtaman/washi/blob/main/docs/ja/solvers/custom-solver.md)。モデルの問題クラスを扱えるソルバーを選ぶ必要があり、対応機能はソルバーごとに異なります。
54
+ 3. **ローカル実行と WebAssembly の配布**: ローカルソルバーのアダプターでは、別途 Python サービスや C/C++ のネイティブビルドを用意せずアプリケーション内で求解できるため、プロトタイプから Web ツールを公開する際のバックエンド構築のボトルネックを解消でき、ブラウザ内 LLM(WebGPU / WebLLM 等)とのクライアントサイド連携も容易です。ブラウザへの配布には生成された JS・Worker・WASM が必要です。オフライン利用には、これらをローカルに配置するかキャッシュしておく必要があります。Z3 には追加のブラウザ隔離設定があります。
55
+ 4. **名前付き制約と矛盾原因の特定**: 充足可能性チェック(`m.check()`)により、矛盾した制約(`conflicts` / `unsatCore`)を調査して実行不能の原因を特定できます。組み込み HiGHS では**エラスティック修復集合**(実行可能性の回復のために緩和が必要な制約集合 `conflicts`)を検出し、Z3 では **SMT 充足不能コア**(相互に矛盾する制約部分集合 `unsatCore` / `conflicts`、最小性は保証されません)を抽出します。
56
+ 5. **高度な分解法オーケストレーション**: 素朴な一体型 MIP では探索木が爆発するボトルネック構造(カッティングストック、配送計画/VRP、巡回セールスマン/TSP、施設配置計画など)を、ファーストクラスの分解法(`master.solveColumnGeneration`, `master.solveBenders`)で克服します。価格付け(Pricing)や連結制約(`sub.link(...)`)を活用したクリーンなコールバック設計により、双対価格付け・カット生成・整数解の復元をきれいに調整します。
56
57
 
57
58
  モデルの構築と変換は WaSHI が担当します。扱える問題クラス、数値計算の挙動、求解時間は、選択したソルバー・設定・問題に依存します。モデル構築のオーバーヘッドとソルバーの実行時間は分けて計測する必要があり、特定のモデルでの結果は一般的な性能保証ではありません。
58
59
 
@@ -62,9 +63,9 @@ console.log(`生産量: x=${x.value}, y=${y.value} (最大利益: $${m.objective
62
63
 
63
64
  | アダプター | エンジンのライセンス | 対応する問題クラス | エンジンの役割 | 提供形態 |
64
65
  | :--- | :--- | :--- | :--- | :--- |
65
- | **HiGHS** | **MIT** | **LP, MIP, 凸QP** | 数値最適化 | 標準ソルバー |
66
- | **Z3** | **MIT** | **SMT, SAT, 命題論理, MIP** | 充足可能性の判定、Unsat Core、最適化 | オプションプラグイン (`@ohtaman/washi/solvers/z3`) |
67
- | **GLPK** | **GPL-3.0** | **LP, MIP** | 線形・混合整数最適化 | オプションプラグイン (`@ohtaman/washi/solvers/glpk`) |
66
+ | **[HiGHS](https://github.com/ERGO-Code/HiGHS)** | **MIT** | **LP, MIP, 凸QP** | 数値最適化 | 標準ソルバー |
67
+ | **[Z3](https://github.com/Z3Prover/z3)** (via [`z3-solver`](https://www.npmjs.com/package/z3-solver)) | **MIT** | **SMT, SAT, 命題論理, MIP** | 充足可能性の判定、Unsat Core、最適化 | オプションプラグイン (`@ohtaman/washi/solvers/z3`) |
68
+ | **[GLPK](https://www.gnu.org/software/glpk/)** (via [`glpk.js`](https://www.npmjs.com/package/glpk.js)) | **GPL-3.0** | **LP, MIP** | 線形・混合整数最適化 | オプションプラグイン (`@ohtaman/washi/solvers/glpk`) |
68
69
  | **Mock** | **MIT** | — | 統合テスト用の模擬応答 | 標準搭載。モデルの求解は行わない |
69
70
 
70
71
  ---
@@ -92,61 +93,58 @@ npm install z3-solver # SMT・矛盾診断を利用する場合
92
93
  npm install glpk.js # GLPK ソルバーを利用する場合 (GPL-3.0)
93
94
  ```
94
95
 
96
+ *(任意)* AI コーディングエージェント(Claude Code, Antigravity, Cursor, Codex 等)向けに、`skills` CLI またはパッケージ同梱の [`skills/washi/SKILL.md`](skills/washi/SKILL.md) を導入できます:
97
+
98
+ ```bash
99
+ # skills CLI (skills.sh / vercel-labs/skills) 経由で導入
100
+ npx skills add ohtaman/washi
101
+
102
+ # またはパッケージ同梱のスキルを手動で配置
103
+ mkdir -p .agents/skills/washi
104
+ cp node_modules/@ohtaman/washi/skills/washi/SKILL.md .agents/skills/washi/SKILL.md
105
+ ```
106
+
95
107
  ---
96
108
 
97
109
  ## 🚀 使い方ガイド
98
110
 
99
- ### 1. 混合整数線形計画法 (MIP: 生産計画問題)
111
+ ### 1. 多次元変数・条件付き制約 (`m.indicator`)・解の検証 (`m.validateSolution`)
100
112
 
101
- リソース制約の下で利益を最大化する生産計画:
113
+ 1次元辞書や2次元マトリクス変数を1行で宣言し、`m.indicator()` による条件付き Big-M 制約、進捗イベント(`onProgress`)とキャンセル(`AbortSignal`)、および `m.validateSolution()` による独立解検証を行えます:
102
114
 
103
115
  ```ts
104
116
  import { Model, expr } from "@ohtaman/washi";
105
117
 
106
- const m = new Model("FactoryPlanning");
107
-
108
- // 決定変数(整数・非負)
109
- const chairs = m.integer("chairs", { lb: 0 });
110
- const tables = m.integer("tables", { lb: 0 });
111
-
112
- // テンプレートリテラル記法による自然な目的関数と制約
113
- m.maximize(expr`30 * ${chairs} + 50 * ${tables}`);
114
- m.constrain(expr`${chairs} + 2 * ${tables} <= 40`, "wood_limit");
115
- m.constrain(expr`2 * ${chairs} + ${tables} <= 50`, "labor_limit");
116
-
117
- // 標準の HiGHS WASM エンジンで求解
118
- const status = await m.optimize();
119
-
120
- console.log(`ステータス: ${status}`); // "Optimal"
121
- console.log(`最大利益: $${m.objectiveValue}`); // 1100
122
- console.log(`生産数: 椅子=${chairs.value}, 机=${tables.value}`); // 椅子 20, 机 10
123
- ```
124
-
125
- #### 🗂 多次元変数グリッド
118
+ const m = new Model("FacilityPlanning");
126
119
 
127
- キー一覧や多次元インデックスから、1 行で変数群を定義:
128
-
129
- ```ts
130
120
  // 1次元辞書形式のバイナリ変数群
131
121
  const openCity = m.binary(["Tokyo", "Osaka", "Nagoya"]);
132
122
 
133
123
  // 2次元輸送量行列: [工場] x [店舗]
134
- const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0 });
124
+ const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0, ub: 1000 });
135
125
 
136
126
  // 2次元グリッド (例: 8x8 チェス盤)
137
127
  const board = m.binary(8, 8, { prefix: "queen" });
138
- ```
139
128
 
140
- #### 🔀 Big-M インジケータ制約 (`m.indicator`)
141
-
142
- 条件分岐や固定費の発生ルールを、煩雑な手動 Big-M 計算なしに記述:
143
-
144
- ```ts
145
- // 「東京工場が稼働していない (openCity['Tokyo'] == 0) なら、生産量 flow は 0」
129
+ // 条件付き Big-M 制約: 東京拠点が閉鎖 (openCity['Tokyo'] == 0) なら flow <= 0
130
+ // (M を省略すると、変数の有限な上下限から安全な最小の Big-M が自動導出されます)
146
131
  m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
147
132
  activeValue: 0,
148
- M: 1000,
133
+ name: "tokyo_closed_no_flow",
149
134
  });
135
+
136
+ // 進捗コールバック (onProgress) と AbortSignal によるキャンセル制御
137
+ const controller = new AbortController();
138
+ await m.solve({
139
+ signal: controller.signal,
140
+ onProgress: (ev) => {
141
+ // 分枝限定法のライブ指標: ev.elapsedTime, ev.objectiveValue, ev.objectiveBound, ev.mipGap
142
+ },
143
+ });
144
+
145
+ // 求解結果が境界・整数性・線形/論理制約を満たすか独立検証
146
+ const report = m.validateSolution();
147
+ console.log("解の検証結果:", report.valid);
150
148
  ```
151
149
 
152
150
  ---
@@ -156,7 +154,7 @@ m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
156
154
  期待リターンと予算制約を満たしつつ、共分散行列に基づく投資リスクを最小化:
157
155
 
158
156
  ```ts
159
- import { Model, QuadExpr, sum, dot, expr } from "@ohtaman/washi";
157
+ import { Model, QuadExpr, sum, dot, minimize, expr } from "@ohtaman/washi";
160
158
 
161
159
  const m = new Model("MarkowitzPortfolio");
162
160
  const assets = ["Tech", "Bonds", "RealEstate"];
@@ -166,11 +164,11 @@ const returns = [0.14, 0.04, 0.08];
166
164
  const w = m.continuous(assets, { lb: 0, ub: 1 });
167
165
 
168
166
  // 1. 予算制約: 合計 100%
169
- m.constrain(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
167
+ m.constraint(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
170
168
 
171
169
  // 2. 目標リターン: 最低 8.5%
172
170
  const retMap = Object.fromEntries(assets.map((a, i) => [a, returns[i]]));
173
- m.constrain(expr`${dot(retMap, w)} >= 0.085`, "min_return");
171
+ m.constraint(expr`${dot(retMap, w)} >= 0.085`, "min_return");
174
172
 
175
173
  // 3. 共分散行列 (リスク & 相関)
176
174
  const cov = [
@@ -186,9 +184,9 @@ for (let i = 0; i < assets.length; i++) {
186
184
  risk = risk.add(w[assets[i]].mul(w[assets[j]]).mul(cov[i][j]));
187
185
  }
188
186
  }
189
- m.minimize(risk);
187
+ m.objective(minimize(risk));
190
188
 
191
- await m.optimize();
189
+ await m.solve();
192
190
  console.log(`最小分散: ${m.objectiveValue?.toFixed(6)}`);
193
191
  assets.forEach((name) => {
194
192
  console.log(` ${name}: ${((w[name].value || 0) * 100).toFixed(2)}%`);
@@ -214,13 +212,13 @@ const siren = m.bool("siren");
214
212
  const lux = m.integer("lux", { lb: 0 });
215
213
 
216
214
  // 論理ルール
217
- m.constrain(lux.le(50).implies(isNight), "dark_implies_night");
218
- m.constrain(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
215
+ m.constraint(lux.le(50).implies(isNight), "dark_implies_night");
216
+ m.constraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
219
217
 
220
218
  // 観測されたセンサー値
221
- m.constrain(expr`${lux} <= 10`, "sensor_dark");
222
- m.constrain(motion, "sensor_motion");
223
- m.constrain(alarm, "alarm_on");
219
+ m.constraint(expr`${lux} <= 10`, "sensor_dark");
220
+ m.constraint(motion, "sensor_motion");
221
+ m.constraint(alarm, "alarm_on");
224
222
 
225
223
  // 目的関数なしで制約充足可能性(SAT)を判定
226
224
  const res = await m.check({ solver: "z3" });
@@ -231,40 +229,47 @@ console.log(`サイレン作動? ${m.values.get("siren")}`); // true
231
229
 
232
230
  ---
233
231
 
234
- ### 4. 矛盾原因の自動特定 (`unsatCore`)
232
+ ### 4. 矛盾診断(`conflicts` / `unsatCore`)
235
233
 
236
- 業務ルールや制約群が互いに矛盾している場合、**矛盾を引き起こしている制約の部分集合**(Unsat Core。最小性は保証しません)を特定してデバッグ(Z3 が必要):
234
+ 業務ルールや制約群が互いに矛盾している場合、原因と修正箇所を特定してデバッグできます:
235
+ - **標準の HiGHS & GLPK**: `await m.check()` が単一 solve の弾性スラック緩和($\min \sum s_i$)を用いて追加ソルバーなしで `res.conflicts`(実行可能性を回復するために緩和が必要な**修復集合**)を特定します。
236
+ - **Z3**: `await m.check({ solver: "z3" })` が厳密な SMT **充足不能コア(Unsat Core)** を抽出し、相互に矛盾している制約部分集合(`res.unsatCore` および `res.conflicts`)を返します。
237
237
 
238
238
  ```ts
239
239
  import { Model, expr } from "@ohtaman/washi";
240
- import "@ohtaman/washi/solvers/z3";
241
240
 
242
- const m = new Model({ name: "ConflictDiagnosis", solver: "z3" });
241
+ const m = new Model("ConflictDiagnosis");
243
242
  const engineers = m.integer("engineers", { lb: 0, ub: 20 });
244
243
  const budget = m.continuous("budget", { lb: 0, ub: 100000 });
245
244
  const rushDelivery = m.bool("rushDelivery");
246
245
 
247
246
  // ルール 1: エンジニア1人あたり $8,000
248
- m.constrain(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
247
+ m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
249
248
 
250
249
  // ルール 2: 特急納品が必須
251
- m.constrain(rushDelivery, "rush_required");
250
+ m.constraint(rushDelivery, "rush_required");
252
251
 
253
252
  // ルール 3: 特急納品には最低 8 人のエンジニアが必要
254
- m.constrain(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
253
+ m.constraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
255
254
 
256
255
  // ルール 4: 役員会の予算上限 $50,000 (矛盾: 8人 * $8000 = $64,000 > $50,000!)
257
- m.constrain(expr`${budget} <= 50000`, "strict_board_budget_cap");
256
+ m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
258
257
 
259
258
  // ルール 5: 最低 1 人雇用の労働基準(無関係なルール)
260
- m.constrain(expr`${engineers} >= 1`, "minimum_one_employee");
259
+ m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
261
260
 
262
- const res = await m.check({ solver: "z3" });
261
+ // 1. 標準の HiGHS によるエラスティック修復診断
262
+ const res = await m.check();
263
263
 
264
264
  console.log(`ステータス: ${res.status}`); // "Unsat"
265
- console.log("矛盾している制約:", res.unsatCore);
266
- // 出力: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
267
- // ※ 無関係な "minimum_one_employee" は的確に除外されます
265
+ console.log("修復対象制約(緩和が必要な制約):", res.conflicts);
266
+ // HiGHS の出力: ["rush_required"]
267
+ // (rush_required を緩和すれば実行可能となるため修復対象として特定されます)
268
+
269
+ // 2. または Z3 による厳密な SMT 充足不能コア抽出(import "@ohtaman/washi/solvers/z3" が必要)
270
+ // const z3Res = await m.check({ solver: "z3" });
271
+ // console.log("相互矛盾コア:", z3Res.unsatCore);
272
+ // Z3 の出力: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
268
273
  ```
269
274
 
270
275
  ---
@@ -274,18 +279,18 @@ console.log("矛盾している制約:", res.unsatCore);
274
279
  GNU Linear Programming Kit (GLPK) ソルバープラグインを利用する場合:
275
280
 
276
281
  ```ts
277
- import { Model, expr } from "@ohtaman/washi";
282
+ import { Model, expr, maximize } from "@ohtaman/washi";
278
283
  import "@ohtaman/washi/solvers/glpk";
279
284
 
280
285
  const m = new Model({ solver: "glpk" });
281
286
  const x = m.integer("x", { lb: 0 });
282
287
  const y = m.integer("y", { lb: 0 });
283
288
 
284
- m.maximize(expr`30 * ${x} + 50 * ${y}`);
285
- m.constrain(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
286
- m.constrain(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
289
+ m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
290
+ m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
291
+ m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
287
292
 
288
- const status = await m.optimize();
293
+ const status = await m.solve();
289
294
  console.log(`ステータス: ${status}`); // "Optimal"
290
295
  console.log(`目的関数値: ${m.objectiveValue}`); // 1100
291
296
  ```
@@ -297,7 +302,7 @@ console.log(`目的関数値: ${m.objectiveValue}`); // 1100
297
302
  ネストしたループを書くことなく、1次元・2次元の線形結合を1行で記述:
298
303
 
299
304
  ```ts
300
- import { Model, dot } from "@ohtaman/washi";
305
+ import { Model, dot, minimize } from "@ohtaman/washi";
301
306
 
302
307
  const m = new Model();
303
308
  const factories = ["Tokyo", "Osaka"];
@@ -311,8 +316,8 @@ const costs = {
311
316
  const ship = m.continuous(factories, stores);
312
317
 
313
318
  // 総輸送コストを1行で最小化:
314
- m.minimize(dot(costs, ship));
315
- await m.optimize();
319
+ m.objective(minimize(dot(costs, ship)));
320
+ await m.solve();
316
321
 
317
322
  // 最適解の全変数値を取得
318
323
  const solution = m.getValues(ship);
@@ -362,23 +367,23 @@ await model.solve();
362
367
  </script>
363
368
  ```
364
369
 
365
- CDN から直接読み込む場合は、URL を `https://cdn.jsdelivr.net/npm/@ohtaman/washi@0.1.2/dist/browser/washi.min.js`(`0.1.2` 以降のバージョンを固定)に指定できます。ESM は同じ版の `dist/browser/washi.js` を指定します。CDN の URL を選ぶことが明示的な CDN 利用になり、ローカル配信が勝手に CDN へ切り替わることはありません。
370
+ CDN から直接読み込む場合は、URL を `https://cdn.jsdelivr.net/npm/@ohtaman/washi@<version>/dist/browser/washi.min.js`(対応版リリースが公開された後にそのバージョンを指定)に指定できます。ESM は同じ版の `dist/browser/washi.js` を指定します。CDN の URL を選ぶことが明示的な CDN 利用になり、ローカル配信が勝手に CDN へ切り替わることはありません。
366
371
 
367
372
  ### ブラウザ環境でのソルバープラグイン(GLPK & Z3)
368
373
 
369
- ブラウザ配布物には、`dist/browser/solvers/` 配下に事前バンドルされたスタンドアロンソルバープラグインが含まれています(CDN 利用時は `>= 0.2.0` または `@latest` で利用可能):
374
+ ブラウザ配布物には、`dist/browser/solvers/` 配下に事前バンドルされたスタンドアロンソルバープラグインが含まれています:
370
375
  - **GLPK**: `dist/browser/solvers/glpk.js` (ESM) および `dist/browser/solvers/glpk.min.js` (IIFE、グローバル `WashiGLPK` を登録)
371
376
  - **Z3**: `dist/browser/solvers/z3.js` (ESM) および `dist/browser/solvers/z3.min.js` (IIFE、グローバル `WashiZ3` を登録)、ならびに `z3-built.js` / `z3-built.wasm`
372
377
 
373
378
  #### ブラウザ ESM での GLPK 利用
374
379
  ```html
375
380
  <script type="module">
376
- import { Model } from "./washi/washi.js";
381
+ import { Model, maximize } from "./washi/washi.js";
377
382
  import "./washi/solvers/glpk.js";
378
383
 
379
384
  const model = new Model({ solver: "glpk" });
380
385
  const x = model.continuous("x", { lb: 0, ub: 10 });
381
- model.maximize(x);
386
+ model.objective(maximize(x));
382
387
  await model.solve();
383
388
  console.log("GLPK ステータス:", model.status);
384
389
  </script>
@@ -391,52 +396,34 @@ CDN から直接読み込む場合は、URL を `https://cdn.jsdelivr.net/npm/@o
391
396
  <script>
392
397
  const model = new Washi.Model({ solver: "glpk" });
393
398
  const x = model.continuous("x", { lb: 0, ub: 10 });
394
- model.maximize(x);
399
+ model.objective(Washi.maximize(x));
395
400
  model.solve().then(status => console.log("GLPK ステータス:", status));
396
401
  </script>
397
402
  ```
398
403
 
399
404
  > [!NOTE]
400
405
  > **ブラウザ環境での Z3 利用と Cross-Origin Isolation**:
401
- > Z3 の WebAssembly エンジンはマルチスレッド(`SharedArrayBuffer`)を必要とするため、ブラウザのセキュリティ仕様により Cross-Origin Isolation が必須となります。ドキュメントレスポンスに以下のヘッダーが必要です:
402
- > ```http
403
- > Cross-Origin-Opener-Policy: same-origin
404
- > Cross-Origin-Embedder-Policy: require-corp
405
- > ```
406
- > また、別オリジンからアセットを読み込む場合は `Cross-Origin-Resource-Policy: cross-origin` の返却が必要です。
407
- >
408
- > 任意のヘッダーを設定できない一般的な静的配信(GitHub Pages や CodePen 等の iframe プレビュー環境)では、追加対応なしで条件を満たすことはできません。なお、ライブラリ本体 `@ohtaman/washi` のインポートによって利用者のページに Service Worker が勝手に登録されることはありません。
409
- >
410
- > GitHub Pages で公開している公式 Web デモでは、`/washi/` スコープ限定の独立した `coi-serviceworker.js` を同オリジンに配置・登録し、初回アクセス時に1回だけ安全に再読み込みを行って `window.crossOriginIsolated === true` を確立してから Z3 を実行します。Service Worker が利用できない環境や隔離の確立に失敗した環境では、原因を明示して安全に実行を中断します。
411
-
412
- 完全なスタンドアロンサンプル(ESM、スクリプトタグ、CodePen)は `examples/distribution/` に用意されています。
413
-
414
- ### 工場内・オフライン配布、Worker、自前配信
415
-
416
- アプリのビルド出力一式、または `dist/browser` 一式をローカル HTTP サーバー・社内サーバーに配布します。必要なアセットがローカルにあれば、ブラウザのキャッシュが空でも外部ネットワークは不要です。HTML や最初の JS だけでは不足します。`file://` 実行や PWA の自動キャッシュは対象外です。
406
+ > Z3 の WebAssembly エンジンはマルチスレッド(`SharedArrayBuffer`)を必要とするため、`Cross-Origin-Opener-Policy: same-origin` および `Cross-Origin-Embedder-Policy: require-corp` ヘッダーによる Cross-Origin Isolation が必要です。静的ホスティング(`coi-serviceworker.js`)、イントラネット・オフライン配置、CSP(`worker-src blob:`)、カスタム WASM URL(`setHighsWasmUrl`)の詳細は [Cross-Origin Isolation 設定](docs/ja/getting-started/cross-origin.md) および [ランタイム環境とデプロイ構成](docs/ja/getting-started/runtimes.md) を参照してください。
417
407
 
418
- ESM はモジュール URL、script 版は読み込み時に保持した script URL からアセットを解決します。CDN の Worker は Blob のモジュールから起動します。配信元の CORS と、CSP の script・通信先許可が必要です。CDN Worker では `worker-src blob:` も許可してください。使えない場合は `useWorker: false` を指定します。Worker の読み込み・メッセージ処理に失敗した場合は、求解 Promise がエラーになります。
408
+ 完全なスタンドアロンサンプル(ESM、スクリプトタグ、CodePen)は [`examples/distribution/`](examples/distribution/README.md) に用意されています。
419
409
 
420
- 自前 URL は `setHighsWasmUrl(url)` / `options.wasmUrl` で指定できます。従来の HiGHS CDN を明示的に選ぶ場合:
421
-
422
- ```typescript
423
- import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
424
- setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
425
- ```
410
+ ---
426
411
 
427
- 初期化に失敗すると取得先を含むエラーを返し、別ホストには切り替えません。従来のオリジンルート / CDN 自動フォールバックを利用していた場合は、生成アセットを配布するか URL を明示してください。
412
+ ## 🛠️ カスタムソルバー開発と拡張性
428
413
 
429
- ### 配布物の検証
414
+ WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャを採用しています。独自の最適化エンジン(クラウド REST ソルバー、WebAssembly エンジン、商用ソルバーアダプターなど)を容易に構築・登録できます:
430
415
 
431
- `npm run test:distribution` は実際の npm tarball をインストールし、任意依存なしの Vite / webpack アプリをビルドして、生成 JS と実 WASM を VM で実行します。ブラウザ・CORS の検証とは別です。`npx playwright install chromium` 後に `RUN_DISTRIBUTION_BROWSER=1 npm run test:distribution` で Chromium の検証も実行できます。CI はそのモードを使い、別オリジン配信と外部ネットワーク遮断も確認します。
416
+ - 詳細な開発手順、API コントラクト仕様(`Solver`, `SolveModelData`, `CheckModelData`, `SolverResult`)、およびテスト手法については [カスタムソルバー開発ガイド](docs/ja/solvers/custom-solver.md) をご覧ください。
432
417
 
433
418
  ---
434
419
 
435
- ## 🛠️ カスタムソルバー開発と拡張性
420
+ ## 📚 ドキュメント
436
421
 
437
- WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャを採用しています。独自の最適化エンジン(クラウド REST ソルバー、WebAssembly エンジン、商用ソルバーアダプターなど)を容易に構築・登録できます:
422
+ 完全なドキュメントは **[ohtaman.github.io/washi/ja/](https://ohtaman.github.io/washi/ja/)** およびリポジトリの [`docs/ja/`](docs/ja/)([英語版 `docs/`](docs/))で公開されています:
438
423
 
439
- - 詳細な開発手順、API コントラクト仕様(`Solver`, `LpModelData`, `SolverResult`)、およびテスト手法については [Custom Solver Developer Guide](docs/custom-solver-guide.md) をご覧ください。
424
+ - **はじめに**: [クイックスタート](docs/ja/getting-started/index.md) · [AIエージェント向けクイックリファレンス](docs/ja/getting-started/ai-agents.md) · [ランタイム環境とデプロイ構成](docs/ja/getting-started/runtimes.md) · [Cross-Origin Isolation (Z3)](docs/ja/getting-started/cross-origin.md)
425
+ - **モデリングガイド**: [決定変数 (`Var`, `BoolVar`)](docs/ja/guide/variables.md) · [数式と集計 (`expr`, `sum`, `dot`)](docs/ja/guide/expressions.md) · [制約と論理条件 (`indicator`, SMT)](docs/ja/guide/constraints.md) · [目的関数と求解 (`solve`, `validateSolution`)](docs/ja/guide/objectives.md) · [充足可能性と Unsat Core (`check`)](docs/ja/guide/unsat-core.md) · [分解法 (`solveColumnGeneration`, `solveBenders`)](docs/ja/guide/decomposition.md)
426
+ - **ソルバー**: [ソルバー概要](docs/ja/solvers/index.md) · [HiGHS](docs/ja/solvers/highs.md) · [Z3](docs/ja/solvers/z3.md) · [GLPK](docs/ja/solvers/glpk.md) · [カスタムソルバー](docs/ja/solvers/custom-solver.md)
440
427
 
441
428
  ---
442
429
 
@@ -446,11 +433,13 @@ WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャ
446
433
 
447
434
  | カテゴリ | サンプル | 確認できること | ソルバー |
448
435
  | :--- | :--- | :--- | :--- |
449
- | **基本モデル** | [生産計画](https://github.com/ohtaman/washi/blob/main/examples/node/01-production-planning.ts) | 整数変数、利益の最大化、資源制約 (`expr`, `c`) | HiGHS |
436
+ | **基本モデル** | [生産計画](https://github.com/ohtaman/washi/blob/main/examples/node/01-production-planning.ts) | 整数変数、利益の最大化、資源制約 (`expr`) | HiGHS |
450
437
  | **基本モデル** | [輸送・物流ネットワーク](https://github.com/ohtaman/washi/blob/main/examples/node/09-transportation-problem.ts) | 2次元マトリクス変数 (`continuous(rows, cols)`) と供給・需要バランス | HiGHS |
451
438
  | **基本モデル** | [栄養問題と感度分析](https://github.com/ohtaman/washi/blob/main/examples/node/10-diet-sensitivity.ts) | 潜在価格 (`constr.dual`)、余剰 (`constr.slack`)、被約費用 (`var.reducedCost`) | HiGHS |
452
439
  | **実践応用** | [スタッフのシフト作成](https://github.com/ohtaman/washi/blob/main/examples/node/11-shift-scheduling.ts) | 週間シフト最適化と、土曜日の希望休が衝突した際の原因特定 | HiGHS + Z3 |
453
440
  | **実践応用** | [ECカートのクーポン最適適用](https://github.com/ohtaman/washi/blob/main/examples/node/12-ecommerce-coupon-optimizer.ts) | 条件付き購入金額閾値 (`indicator`)、併用不可ルール、追加購入提案 | HiGHS |
441
+ | **分解法 (OR)** | [配送計画 (CVRP)](https://github.com/ohtaman/washi/blob/main/examples/node/13-vehicle-routing-column-generation.ts) | 列生成法 (Dantzig-Wolfe): 双対変数を用いた実行可能車両ルートの動的プライシング | HiGHS |
442
+ | **分解法 (OR)** | [巡回セールスマン (TSP)](https://github.com/ohtaman/washi/blob/main/examples/node/14-traveling-salesperson-benders.ts) | ベンダーズ分解 (DFJ法): 実行可能カットによる部分巡回路除去制約の遅延生成 | HiGHS |
454
443
  | **実践応用** | [ポートフォリオ配分](https://github.com/ohtaman/washi/blob/main/examples/node/03-portfolio-qp.ts) | 凸二次目的関数 (`QuadExpr`) と配分の制約 | HiGHS |
455
444
  | **論理 / SMT** | [業務ルールの検証](https://github.com/ohtaman/washi/blob/main/examples/node/04-smt-security-rules.ts) | 数値・論理制約 (`bool`, `implies`) の充足可能性の確認 | Z3 |
456
445
  | **論理 / SMT** | [矛盾する制約の診断](https://github.com/ohtaman/washi/blob/main/examples/node/05-unsat-core-diagnosis.ts) | 名前付き制約と Unsat Core (`unsatCore`) | Z3 |
@@ -468,6 +457,8 @@ npx tsx examples/node/09-transportation-problem.ts
468
457
  npx tsx examples/node/10-diet-sensitivity.ts
469
458
  npx tsx examples/node/11-shift-scheduling.ts
470
459
  npx tsx examples/node/12-ecommerce-coupon-optimizer.ts
460
+ npx tsx examples/node/13-vehicle-routing-column-generation.ts
461
+ npx tsx examples/node/14-traveling-salesperson-benders.ts
471
462
 
472
463
  # ローカルのブラウザデモ: http://localhost:5173/demo/
473
464
  npm run dev
@@ -477,7 +468,7 @@ npm run dev
477
468
 
478
469
  ---
479
470
 
480
- ## 🧪 テスト & 信頼性
471
+ ## 🧪 テスト & 再現可能なベンチマーク
481
472
 
482
473
  リポジトリのユニット・統合テストを実行し、配布物をビルドできます:
483
474
 
@@ -489,11 +480,48 @@ npm test
489
480
  npm run build
490
481
  ```
491
482
 
483
+ ### オプションの再現可能なベンチマーク
484
+
485
+ 正しさのテストの合否閾値に実行時間を含めることなく、決定論的な `small-lp`(24変数・18制約)および `scheduling-mip`(315変数・231制約、再構築モデルの `initialValue` ウォームスタートを含む)の各フェーズを計測できます:
486
+
487
+ ```bash
488
+ # Node.js インプロセス WASM ベンチマークを実行(HiGHS・GLPK、デフォルト5回反復)
489
+ npm run bench
490
+
491
+ # 反復回数の指定や JSON 出力
492
+ node scripts/run-benchmarks.mjs --iterations=10 --json
493
+
494
+ # ヘッドレス Chromium の Web Worker 実行も計測(Playwright Chromium が必要)
495
+ node scripts/run-benchmarks.mjs --browser
496
+ ```
497
+
498
+ - **明示的な計測境界**: コールド初期化(`coldRuntimeInitMs`, `coldFirstSolveMs`)と再利用ランタイムでの反復計測(`modelBuildMs`, `validationMs`, LP文字列アダプタ向けの `lpSerializationMs` または構造化アダプタ向けの `symbolMapMs`, `MockSolver` による `resultMappingMs`, `adapterSolveMs`, `endToEndSolveMs`)を分離して直接計測します(無関係なフェーズ時間を差し引いて「ネイティブソルバー時間」とみなすことはしません)。
499
+ - **実行環境の区別**: Node.js のインプロセス WASM(`usesBrowserWorker: false`)とブラウザ Web Worker 実行(`usesBrowserWorker: true`、Worker ライフサイクルや `postMessage` 転送を含む)は異なる実行環境として区別してレポートされます。
500
+
501
+ ### HiGHS WebAssembly の再ビルドとリリース手順
502
+
503
+ 1. **HiGHS WebAssembly アセットの更新・再ビルド(`highs.config.json`)**:
504
+ ```bash
505
+ # ERGO-Code/HiGHS から public/highs.wasm と runtime.js をビルドし、highs.config.json の SHA-256 を更新
506
+ npm run build:highs-wasm
507
+
508
+ # main マージ後、オンデマンド取得(scripts/ensure-highs-wasm.mjs)のハッシュと一致するよう
509
+ # 対応する highs-<version> GitHub Release タグのアセットを上書きアップロード:
510
+ gh release upload highs-v1.15.1 public/highs.wasm src/modeler/solvers/highs/runtime.js --clobber
511
+ ```
512
+ (GitHub Actions の **Build HiGHS WebAssembly** ワークフロー `.github/workflows/build-wasm.yml` を `create_release: true` で手動実行することも可能です。その場合は生成された `highs-wasm-assets` アーティファクト(`public/highs.wasm`、`src/modeler/solvers/highs/runtime.js`、`highs.config.json`)を取得してリポジトリへコミットし、Release アセットと SHA-256 ハッシュを同期してください。)
513
+
514
+ 2. **`@ohtaman/washi` の npm パッケージリリース**:
515
+ - `main` ブランチへのマージにより、ドキュメントと Web デモは GitHub Pages(`.github/workflows/deploy-demo.yml`)へ自動デプロイされますが、npm への公開は自動では行われません。
516
+ - npm に新バージョンを公開する場合は、`package.json` の `"version"` を更新して `main` にマージした上で、GitHub Release を公開します(例: `gh release create v0.5.1 --generate-notes`)。Release 公開をトリガーに `.github/workflows/publish-npm.yml` が起動し、ユニットテストおよび Playwright 配布物検証を通過した後に OIDC Trusted Publishing で npm へ公開されます。
517
+
492
518
  ---
493
519
 
494
520
  ## 📄 ライセンス
495
521
 
496
522
  - **`@ohtaman/washi`**: [MIT ライセンス](./LICENSE)
497
- - **HiGHS**(標準エンジン): MIT ライセンス
498
- - **Z3**(オプション): MIT ライセンス
499
- - **GLPK**(オプション): GPL-3.0 ライセンス
523
+ - **HiGHS**(標準エンジン、[ERGO-Code/HiGHS](https://github.com/ERGO-Code/HiGHS)): MIT ライセンス
524
+ - **Z3**(オプション、[`z3-solver`](https://www.npmjs.com/package/z3-solver) 経由): MIT ライセンス
525
+ - **GLPK**(オプション、[`glpk.js`](https://www.npmjs.com/package/glpk.js) 経由): GPL-3.0 ライセンス
526
+
527
+ 同梱および外部ソルバーの詳細な著作権表示とライセンス全文については [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md) をご覧ください。