@ohtaman/washi 0.3.0 → 0.5.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 (97) hide show
  1. package/README.ja.md +111 -118
  2. package/README.md +109 -116
  3. package/README.zh-CN.md +112 -119
  4. package/dist/browser/highs-worker.js +21 -3
  5. package/dist/browser/loader-CtaDHef_.js +273 -0
  6. package/dist/browser/solvers/glpk.js +433 -286
  7. package/dist/browser/solvers/glpk.min.js +1 -1
  8. package/dist/browser/solvers/z3.js +1002 -981
  9. package/dist/browser/solvers/z3.min.js +2 -2
  10. package/dist/browser/washi.js +2178 -622
  11. package/dist/browser/washi.min.js +4 -4
  12. package/dist/lib/highs-worker.js +21 -3
  13. package/dist/lib/loader-DmNC_UNk.js +279 -0
  14. package/dist/lib/registry-BEfvpZW3.js +951 -0
  15. package/dist/lib/solvers/glpk.js +201 -90
  16. package/dist/lib/solvers/z3.js +399 -259
  17. package/dist/lib/washi.js +1674 -932
  18. package/dist/types/index.d.ts +4 -4
  19. package/dist/types/index.d.ts.map +1 -1
  20. package/dist/types/modeler/big-m.d.ts +75 -0
  21. package/dist/types/modeler/big-m.d.ts.map +1 -0
  22. package/dist/types/modeler/boolean-expr.d.ts +103 -14
  23. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  24. package/dist/types/modeler/constraint.d.ts +36 -9
  25. package/dist/types/modeler/constraint.d.ts.map +1 -1
  26. package/dist/types/modeler/expression.d.ts +117 -27
  27. package/dist/types/modeler/expression.d.ts.map +1 -1
  28. package/dist/types/modeler/index.d.ts +10 -6
  29. package/dist/types/modeler/index.d.ts.map +1 -1
  30. package/dist/types/modeler/lp-writer.d.ts +27 -2
  31. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  32. package/dist/types/modeler/model.d.ts +491 -140
  33. package/dist/types/modeler/model.d.ts.map +1 -1
  34. package/dist/types/modeler/objective.d.ts +59 -0
  35. package/dist/types/modeler/objective.d.ts.map +1 -0
  36. package/dist/types/modeler/parser.d.ts +39 -14
  37. package/dist/types/modeler/parser.d.ts.map +1 -1
  38. package/dist/types/modeler/quad-expr.d.ts +28 -7
  39. package/dist/types/modeler/quad-expr.d.ts.map +1 -1
  40. package/dist/types/modeler/solution-validator.d.ts +93 -0
  41. package/dist/types/modeler/solution-validator.d.ts.map +1 -0
  42. package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
  43. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  44. package/dist/types/modeler/solvers/highs/loader.d.ts +36 -11
  45. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  46. package/dist/types/modeler/solvers/highs/solver.d.ts +11 -4
  47. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  48. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  49. package/dist/types/modeler/solvers/mock.d.ts +14 -5
  50. package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
  51. package/dist/types/modeler/solvers/types.d.ts +71 -11
  52. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  53. package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
  54. package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
  55. package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
  56. package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
  57. package/dist/types/modeler/types.d.ts +60 -2
  58. package/dist/types/modeler/types.d.ts.map +1 -1
  59. package/dist/types/modeler/validation.d.ts +56 -0
  60. package/dist/types/modeler/validation.d.ts.map +1 -0
  61. package/dist/types/modeler/variable.d.ts +53 -13
  62. package/dist/types/modeler/variable.d.ts.map +1 -1
  63. package/docs/examples/diet-sensitivity.md +90 -0
  64. package/docs/examples/ecommerce-coupons.md +106 -0
  65. package/docs/examples/index.md +53 -0
  66. package/docs/examples/knapsack.md +60 -0
  67. package/docs/examples/portfolio-qp.md +83 -0
  68. package/docs/examples/production.md +51 -0
  69. package/docs/examples/shift-scheduling.md +175 -0
  70. package/docs/examples/smt-security.md +74 -0
  71. package/docs/examples/transportation.md +90 -0
  72. package/docs/examples/unsat-core.md +64 -0
  73. package/docs/getting-started/ai-agents.md +267 -0
  74. package/docs/getting-started/cross-origin.md +87 -0
  75. package/docs/getting-started/index.md +116 -0
  76. package/docs/getting-started/runtimes.md +227 -0
  77. package/docs/guide/constraints.md +177 -0
  78. package/docs/guide/expressions.md +204 -0
  79. package/docs/guide/objectives.md +170 -0
  80. package/docs/guide/unsat-core.md +147 -0
  81. package/docs/guide/variables.md +188 -0
  82. package/docs/ja/getting-started/ai-agents.md +266 -0
  83. package/docs/solvers/custom-solver.md +525 -0
  84. package/docs/solvers/glpk.md +66 -0
  85. package/docs/solvers/highs.md +93 -0
  86. package/docs/solvers/index.md +42 -0
  87. package/docs/solvers/z3.md +78 -0
  88. package/docs/zh/getting-started/ai-agents.md +266 -0
  89. package/llms-full.txt +3249 -0
  90. package/llms.txt +197 -0
  91. package/package.json +19 -6
  92. package/skills/washi/SKILL.md +197 -0
  93. package/dist/browser/loader-CVt4CloI.js +0 -58
  94. package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
  95. package/dist/lib/constraint-BRuB3ooC.js +0 -90
  96. package/dist/lib/loader-Dti0v9yC.js +0 -50
  97. package/dist/lib/registry-DksqCSjI.js +0 -32
package/README.ja.md CHANGED
@@ -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** は、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,10 @@ 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. **名前付き制約と SMT の結果**: Z3 を明示的に使って充足可能性を確認し、矛盾する制約の調査に競合制約(`conflicts` / `unsatCore`)を利用できます。返されるのは矛盾する制約の部分集合であり、最小の集合とは限りません。
56
56
 
57
57
  モデルの構築と変換は WaSHI が担当します。扱える問題クラス、数値計算の挙動、求解時間は、選択したソルバー・設定・問題に依存します。モデル構築のオーバーヘッドとソルバーの実行時間は分けて計測する必要があり、特定のモデルでの結果は一般的な性能保証ではありません。
58
58
 
@@ -92,61 +92,54 @@ npm install z3-solver # SMT・矛盾診断を利用する場合
92
92
  npm install glpk.js # GLPK ソルバーを利用する場合 (GPL-3.0)
93
93
  ```
94
94
 
95
+ *(任意)* AI コーディングエージェント(Claude Code, Antigravity, Cursor, Codex 等)向けに、[AIエージェント向けクイックリファレンス](docs/ja/getting-started/ai-agents.md)とあわせてポータブルな [`skills/washi/SKILL.md`](skills/washi/SKILL.md) を同梱しています:
96
+
97
+ ```bash
98
+ mkdir -p .agents/skills/washi
99
+ cp node_modules/@ohtaman/washi/skills/washi/SKILL.md .agents/skills/washi/SKILL.md
100
+ ```
101
+
95
102
  ---
96
103
 
97
104
  ## 🚀 使い方ガイド
98
105
 
99
- ### 1. 混合整数線形計画法 (MIP: 生産計画問題)
106
+ ### 1. 多次元変数・条件付き制約 (`m.indicator`)・解の検証 (`m.validateSolution`)
100
107
 
101
- リソース制約の下で利益を最大化する生産計画:
108
+ 1次元辞書や2次元マトリクス変数を1行で宣言し、`m.indicator()` による条件付き Big-M 制約、進捗イベント(`onProgress`)とキャンセル(`AbortSignal`)、および `m.validateSolution()` による独立解検証を行えます:
102
109
 
103
110
  ```ts
104
111
  import { Model, expr } from "@ohtaman/washi";
105
112
 
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
- ```
113
+ const m = new Model("FacilityPlanning");
124
114
 
125
- #### 🗂 多次元変数グリッド
126
-
127
- キー一覧や多次元インデックスから、1 行で変数群を定義:
128
-
129
- ```ts
130
115
  // 1次元辞書形式のバイナリ変数群
131
116
  const openCity = m.binary(["Tokyo", "Osaka", "Nagoya"]);
132
117
 
133
118
  // 2次元輸送量行列: [工場] x [店舗]
134
- const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0 });
119
+ const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0, ub: 1000 });
135
120
 
136
121
  // 2次元グリッド (例: 8x8 チェス盤)
137
122
  const board = m.binary(8, 8, { prefix: "queen" });
138
- ```
139
-
140
- #### 🔀 Big-M インジケータ制約 (`m.indicator`)
141
123
 
142
- 条件分岐や固定費の発生ルールを、煩雑な手動 Big-M 計算なしに記述:
143
-
144
- ```ts
145
- // 「東京工場が稼働していない (openCity['Tokyo'] == 0) なら、生産量 flow は 0」
124
+ // 条件付き Big-M 制約: 東京拠点が閉鎖 (openCity['Tokyo'] == 0) なら flow <= 0
125
+ // (M を省略すると、変数の有限な上下限から安全な最小の Big-M が自動導出されます)
146
126
  m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
147
127
  activeValue: 0,
148
- M: 1000,
128
+ name: "tokyo_closed_no_flow",
149
129
  });
130
+
131
+ // 進捗コールバック (onProgress) と AbortSignal によるキャンセル制御
132
+ const controller = new AbortController();
133
+ await m.solve({
134
+ signal: controller.signal,
135
+ onProgress: (ev) => {
136
+ // 分枝限定法のライブ指標: ev.elapsedTime, ev.objectiveValue, ev.objectiveBound, ev.mipGap
137
+ },
138
+ });
139
+
140
+ // 求解結果が境界・整数性・線形/論理制約を満たすか独立検証
141
+ const report = m.validateSolution();
142
+ console.log("解の検証結果:", report.valid);
150
143
  ```
151
144
 
152
145
  ---
@@ -156,7 +149,7 @@ m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
156
149
  期待リターンと予算制約を満たしつつ、共分散行列に基づく投資リスクを最小化:
157
150
 
158
151
  ```ts
159
- import { Model, QuadExpr, sum, dot, expr } from "@ohtaman/washi";
152
+ import { Model, QuadExpr, sum, dot, minimize, expr } from "@ohtaman/washi";
160
153
 
161
154
  const m = new Model("MarkowitzPortfolio");
162
155
  const assets = ["Tech", "Bonds", "RealEstate"];
@@ -166,11 +159,11 @@ const returns = [0.14, 0.04, 0.08];
166
159
  const w = m.continuous(assets, { lb: 0, ub: 1 });
167
160
 
168
161
  // 1. 予算制約: 合計 100%
169
- m.constrain(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
162
+ m.constraint(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
170
163
 
171
164
  // 2. 目標リターン: 最低 8.5%
172
165
  const retMap = Object.fromEntries(assets.map((a, i) => [a, returns[i]]));
173
- m.constrain(expr`${dot(retMap, w)} >= 0.085`, "min_return");
166
+ m.constraint(expr`${dot(retMap, w)} >= 0.085`, "min_return");
174
167
 
175
168
  // 3. 共分散行列 (リスク & 相関)
176
169
  const cov = [
@@ -186,9 +179,9 @@ for (let i = 0; i < assets.length; i++) {
186
179
  risk = risk.add(w[assets[i]].mul(w[assets[j]]).mul(cov[i][j]));
187
180
  }
188
181
  }
189
- m.minimize(risk);
182
+ m.objective(minimize(risk));
190
183
 
191
- await m.optimize();
184
+ await m.solve();
192
185
  console.log(`最小分散: ${m.objectiveValue?.toFixed(6)}`);
193
186
  assets.forEach((name) => {
194
187
  console.log(` ${name}: ${((w[name].value || 0) * 100).toFixed(2)}%`);
@@ -207,20 +200,20 @@ import "@ohtaman/washi/solvers/z3";
207
200
 
208
201
  const m = new Model({ name: "SmartSecurityRules" });
209
202
 
210
- const isNight = m.addBoolVar("isNight");
211
- const motion = m.addBoolVar("motion");
212
- const alarm = m.addBoolVar("alarm");
213
- const siren = m.addBoolVar("siren");
203
+ const isNight = m.bool("isNight");
204
+ const motion = m.bool("motion");
205
+ const alarm = m.bool("alarm");
206
+ const siren = m.bool("siren");
214
207
  const lux = m.integer("lux", { lb: 0 });
215
208
 
216
209
  // 論理ルール
217
- m.constrain(lux.le(50).implies(isNight), "dark_implies_night");
218
- m.constrain(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
210
+ m.constraint(lux.le(50).implies(isNight), "dark_implies_night");
211
+ m.constraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
219
212
 
220
213
  // 観測されたセンサー値
221
- m.constrain(expr`${lux} <= 10`, "sensor_dark");
222
- m.constrain(motion, "sensor_motion");
223
- m.constrain(alarm, "alarm_on");
214
+ m.constraint(expr`${lux} <= 10`, "sensor_dark");
215
+ m.constraint(motion, "sensor_motion");
216
+ m.constraint(alarm, "alarm_on");
224
217
 
225
218
  // 目的関数なしで制約充足可能性(SAT)を判定
226
219
  const res = await m.check({ solver: "z3" });
@@ -231,9 +224,9 @@ console.log(`サイレン作動? ${m.values.get("siren")}`); // true
231
224
 
232
225
  ---
233
226
 
234
- ### 4. 矛盾原因の自動特定 (`unsatCore`)
227
+ ### 4. 矛盾原因の自動特定 (`conflicts` / `unsatCore`)
235
228
 
236
- 業務ルールや制約群が互いに矛盾している場合、**矛盾を引き起こしている制約の部分集合**(Unsat Core。最小性は保証しません)を特定してデバッグ(Z3 が必要):
229
+ 業務ルールや制約群が互いに矛盾している場合、**矛盾を引き起こしている制約の部分集合**(Unsat Core / Conflicts。最小性は保証しません)を特定してデバッグ(Z3 が必要):
237
230
 
238
231
  ```ts
239
232
  import { Model, expr } from "@ohtaman/washi";
@@ -242,27 +235,27 @@ import "@ohtaman/washi/solvers/z3";
242
235
  const m = new Model({ name: "ConflictDiagnosis", solver: "z3" });
243
236
  const engineers = m.integer("engineers", { lb: 0, ub: 20 });
244
237
  const budget = m.continuous("budget", { lb: 0, ub: 100000 });
245
- const rushDelivery = m.addBoolVar("rushDelivery");
238
+ const rushDelivery = m.bool("rushDelivery");
246
239
 
247
240
  // ルール 1: エンジニア1人あたり $8,000
248
- m.constrain(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
241
+ m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
249
242
 
250
243
  // ルール 2: 特急納品が必須
251
- m.constrain(rushDelivery, "rush_required");
244
+ m.constraint(rushDelivery, "rush_required");
252
245
 
253
246
  // ルール 3: 特急納品には最低 8 人のエンジニアが必要
254
- m.constrain(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
247
+ m.constraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
255
248
 
256
249
  // ルール 4: 役員会の予算上限 $50,000 (矛盾: 8人 * $8000 = $64,000 > $50,000!)
257
- m.constrain(expr`${budget} <= 50000`, "strict_board_budget_cap");
250
+ m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
258
251
 
259
252
  // ルール 5: 最低 1 人雇用の労働基準(無関係なルール)
260
- m.constrain(expr`${engineers} >= 1`, "minimum_one_employee");
253
+ m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
261
254
 
262
255
  const res = await m.check({ solver: "z3" });
263
256
 
264
257
  console.log(`ステータス: ${res.status}`); // "Unsat"
265
- console.log("矛盾している制約:", res.unsatCore);
258
+ console.log("矛盾している制約:", res.conflicts);
266
259
  // 出力: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
267
260
  // ※ 無関係な "minimum_one_employee" は的確に除外されます
268
261
  ```
@@ -274,18 +267,18 @@ console.log("矛盾している制約:", res.unsatCore);
274
267
  GNU Linear Programming Kit (GLPK) ソルバープラグインを利用する場合:
275
268
 
276
269
  ```ts
277
- import { Model, expr } from "@ohtaman/washi";
270
+ import { Model, expr, maximize } from "@ohtaman/washi";
278
271
  import "@ohtaman/washi/solvers/glpk";
279
272
 
280
273
  const m = new Model({ solver: "glpk" });
281
274
  const x = m.integer("x", { lb: 0 });
282
275
  const y = m.integer("y", { lb: 0 });
283
276
 
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");
277
+ m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
278
+ m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
279
+ m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
287
280
 
288
- const status = await m.optimize();
281
+ const status = await m.solve();
289
282
  console.log(`ステータス: ${status}`); // "Optimal"
290
283
  console.log(`目的関数値: ${m.objectiveValue}`); // 1100
291
284
  ```
@@ -297,7 +290,7 @@ console.log(`目的関数値: ${m.objectiveValue}`); // 1100
297
290
  ネストしたループを書くことなく、1次元・2次元の線形結合を1行で記述:
298
291
 
299
292
  ```ts
300
- import { Model, dot } from "@ohtaman/washi";
293
+ import { Model, dot, minimize } from "@ohtaman/washi";
301
294
 
302
295
  const m = new Model();
303
296
  const factories = ["Tokyo", "Osaka"];
@@ -311,8 +304,8 @@ const costs = {
311
304
  const ship = m.continuous(factories, stores);
312
305
 
313
306
  // 総輸送コストを1行で最小化:
314
- m.minimize(dot(costs, ship));
315
- await m.optimize();
307
+ m.objective(minimize(dot(costs, ship)));
308
+ await m.solve();
316
309
 
317
310
  // 最適解の全変数値を取得
318
311
  const solution = m.getValues(ship);
@@ -362,23 +355,23 @@ await model.solve();
362
355
  </script>
363
356
  ```
364
357
 
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 へ切り替わることはありません。
358
+ 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
359
 
367
360
  ### ブラウザ環境でのソルバープラグイン(GLPK & Z3)
368
361
 
369
- ブラウザ配布物には、`dist/browser/solvers/` 配下に事前バンドルされたスタンドアロンソルバープラグインが含まれています(CDN 利用時は `>= 0.2.0` または `@latest` で利用可能):
362
+ ブラウザ配布物には、`dist/browser/solvers/` 配下に事前バンドルされたスタンドアロンソルバープラグインが含まれています:
370
363
  - **GLPK**: `dist/browser/solvers/glpk.js` (ESM) および `dist/browser/solvers/glpk.min.js` (IIFE、グローバル `WashiGLPK` を登録)
371
364
  - **Z3**: `dist/browser/solvers/z3.js` (ESM) および `dist/browser/solvers/z3.min.js` (IIFE、グローバル `WashiZ3` を登録)、ならびに `z3-built.js` / `z3-built.wasm`
372
365
 
373
366
  #### ブラウザ ESM での GLPK 利用
374
367
  ```html
375
368
  <script type="module">
376
- import { Model } from "./washi/washi.js";
369
+ import { Model, maximize } from "./washi/washi.js";
377
370
  import "./washi/solvers/glpk.js";
378
371
 
379
372
  const model = new Model({ solver: "glpk" });
380
- const x = model.addVar({ lb: 0, ub: 10 });
381
- model.maximize(x);
373
+ const x = model.continuous("x", { lb: 0, ub: 10 });
374
+ model.objective(maximize(x));
382
375
  await model.solve();
383
376
  console.log("GLPK ステータス:", model.status);
384
377
  </script>
@@ -390,53 +383,35 @@ CDN から直接読み込む場合は、URL を `https://cdn.jsdelivr.net/npm/@o
390
383
  <script src="./washi/solvers/glpk.min.js"></script>
391
384
  <script>
392
385
  const model = new Washi.Model({ solver: "glpk" });
393
- const x = model.addVar({ lb: 0, ub: 10 });
394
- model.maximize(x);
386
+ const x = model.continuous("x", { lb: 0, ub: 10 });
387
+ model.objective(Washi.maximize(x));
395
388
  model.solve().then(status => console.log("GLPK ステータス:", status));
396
389
  </script>
397
390
  ```
398
391
 
399
392
  > [!NOTE]
400
393
  > **ブラウザ環境での 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 の自動キャッシュは対象外です。
417
-
418
- ESM はモジュール URL、script 版は読み込み時に保持した script URL からアセットを解決します。CDN の Worker は Blob のモジュールから起動します。配信元の CORS と、CSP の script・通信先許可が必要です。CDN Worker では `worker-src blob:` も許可してください。使えない場合は `useWorker: false` を指定します。Worker の読み込み・メッセージ処理に失敗した場合は、求解 Promise がエラーになります。
394
+ > 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) を参照してください。
419
395
 
420
- 自前 URL は `setHighsWasmUrl(url)` / `options.wasmUrl` で指定できます。従来の HiGHS CDN を明示的に選ぶ場合:
396
+ 完全なスタンドアロンサンプル(ESM、スクリプトタグ、CodePen)は [`examples/distribution/`](examples/distribution/README.md) に用意されています。
421
397
 
422
- ```typescript
423
- import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
424
- setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
425
- ```
398
+ ---
426
399
 
427
- 初期化に失敗すると取得先を含むエラーを返し、別ホストには切り替えません。従来のオリジンルート / CDN 自動フォールバックを利用していた場合は、生成アセットを配布するか URL を明示してください。
400
+ ## 🛠️ カスタムソルバー開発と拡張性
428
401
 
429
- ### 配布物の検証
402
+ WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャを採用しています。独自の最適化エンジン(クラウド REST ソルバー、WebAssembly エンジン、商用ソルバーアダプターなど)を容易に構築・登録できます:
430
403
 
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 はそのモードを使い、別オリジン配信と外部ネットワーク遮断も確認します。
404
+ - 詳細な開発手順、API コントラクト仕様(`Solver`, `SolveModelData`, `CheckModelData`, `SolverResult`)、およびテスト手法については [カスタムソルバー開発ガイド](docs/ja/solvers/custom-solver.md) をご覧ください。
432
405
 
433
406
  ---
434
407
 
435
- ## 🛠️ カスタムソルバー開発と拡張性
408
+ ## 📚 ドキュメント
436
409
 
437
- WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャを採用しています。独自の最適化エンジン(クラウド REST ソルバー、WebAssembly エンジン、商用ソルバーアダプターなど)を容易に構築・登録できます:
410
+ 完全なドキュメントは **[ohtaman.github.io/washi/ja/](https://ohtaman.github.io/washi/ja/)** およびリポジトリの [`docs/ja/`](docs/ja/)([英語版 `docs/`](docs/))で公開されています:
438
411
 
439
- - 詳細な開発手順、API コントラクト仕様(`Solver`, `LpModelData`, `SolverResult`)、およびテスト手法については [Custom Solver Developer Guide](docs/custom-solver-guide.md) をご覧ください。
412
+ - **はじめに**: [クイックスタート](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)
413
+ - **モデリングガイド**: [決定変数 (`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)
414
+ - **ソルバー**: [ソルバー概要](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
415
 
441
416
  ---
442
417
 
@@ -446,13 +421,13 @@ WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャ
446
421
 
447
422
  | カテゴリ | サンプル | 確認できること | ソルバー |
448
423
  | :--- | :--- | :--- | :--- |
449
- | **基本モデル** | [生産計画](https://github.com/ohtaman/washi/blob/main/examples/node/01-production-planning.ts) | 整数変数、利益の最大化、資源制約 (`expr`, `c`) | HiGHS |
450
- | **基本モデル** | [輸送・物流ネットワーク](https://github.com/ohtaman/washi/blob/main/examples/node/09-transportation-problem.ts) | 2次元マトリクス変数 (`addVars(rows, cols)`) と供給・需要バランス | HiGHS |
424
+ | **基本モデル** | [生産計画](https://github.com/ohtaman/washi/blob/main/examples/node/01-production-planning.ts) | 整数変数、利益の最大化、資源制約 (`expr`) | HiGHS |
425
+ | **基本モデル** | [輸送・物流ネットワーク](https://github.com/ohtaman/washi/blob/main/examples/node/09-transportation-problem.ts) | 2次元マトリクス変数 (`continuous(rows, cols)`) と供給・需要バランス | HiGHS |
451
426
  | **基本モデル** | [栄養問題と感度分析](https://github.com/ohtaman/washi/blob/main/examples/node/10-diet-sensitivity.ts) | 潜在価格 (`constr.dual`)、余剰 (`constr.slack`)、被約費用 (`var.reducedCost`) | HiGHS |
452
427
  | **実践応用** | [スタッフのシフト作成](https://github.com/ohtaman/washi/blob/main/examples/node/11-shift-scheduling.ts) | 週間シフト最適化と、土曜日の希望休が衝突した際の原因特定 | HiGHS + Z3 |
453
- | **実践応用** | [ECカートのクーポン最適適用](https://github.com/ohtaman/washi/blob/main/examples/node/12-ecommerce-coupon-optimizer.ts) | 条件付き購入金額閾値 (`addIndicator`)、併用不可ルール、追加購入提案 | HiGHS |
428
+ | **実践応用** | [ECカートのクーポン最適適用](https://github.com/ohtaman/washi/blob/main/examples/node/12-ecommerce-coupon-optimizer.ts) | 条件付き購入金額閾値 (`indicator`)、併用不可ルール、追加購入提案 | HiGHS |
454
429
  | **実践応用** | [ポートフォリオ配分](https://github.com/ohtaman/washi/blob/main/examples/node/03-portfolio-qp.ts) | 凸二次目的関数 (`QuadExpr`) と配分の制約 | HiGHS |
455
- | **論理 / SMT** | [業務ルールの検証](https://github.com/ohtaman/washi/blob/main/examples/node/04-smt-security-rules.ts) | 数値・論理制約 (`addBoolVar`, `implies`) の充足可能性の確認 | Z3 |
430
+ | **論理 / SMT** | [業務ルールの検証](https://github.com/ohtaman/washi/blob/main/examples/node/04-smt-security-rules.ts) | 数値・論理制約 (`bool`, `implies`) の充足可能性の確認 | Z3 |
456
431
  | **論理 / SMT** | [矛盾する制約の診断](https://github.com/ohtaman/washi/blob/main/examples/node/05-unsat-core-diagnosis.ts) | 名前付き制約と Unsat Core (`unsatCore`) | Z3 |
457
432
 
458
433
  [公開ブラウザデモ](https://ohtaman.github.io/washi/demo/)では、モデルを編集して実行できます。ブラウザ標準 ESM、script タグ、CodePen での使い方は[配布形式のサンプル](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)を参照してください。Z3 は Cross-Origin Isolation が必要です。詳細は上記のブラウザ要件に記載しています。
@@ -477,7 +452,7 @@ npm run dev
477
452
 
478
453
  ---
479
454
 
480
- ## 🧪 テスト & 信頼性
455
+ ## 🧪 テスト & 再現可能なベンチマーク
481
456
 
482
457
  リポジトリのユニット・統合テストを実行し、配布物をビルドできます:
483
458
 
@@ -489,6 +464,24 @@ npm test
489
464
  npm run build
490
465
  ```
491
466
 
467
+ ### オプションの再現可能なベンチマーク
468
+
469
+ 正しさのテストの合否閾値に実行時間を含めることなく、決定論的な `small-lp`(24変数・18制約)および `scheduling-mip`(315変数・231制約、再構築モデルの `initialValue` ウォームスタートを含む)の各フェーズを計測できます:
470
+
471
+ ```bash
472
+ # Node.js インプロセス WASM ベンチマークを実行(HiGHS・GLPK、デフォルト5回反復)
473
+ npm run bench
474
+
475
+ # 反復回数の指定や JSON 出力
476
+ node scripts/run-benchmarks.mjs --iterations=10 --json
477
+
478
+ # ヘッドレス Chromium の Web Worker 実行も計測(Playwright Chromium が必要)
479
+ node scripts/run-benchmarks.mjs --browser
480
+ ```
481
+
482
+ - **明示的な計測境界**: コールド初期化(`coldRuntimeInitMs`, `coldFirstSolveMs`)と再利用ランタイムでの反復計測(`modelBuildMs`, `validationMs`, LP文字列アダプタ向けの `lpSerializationMs` または構造化アダプタ向けの `symbolMapMs`, `MockSolver` による `resultMappingMs`, `adapterSolveMs`, `endToEndSolveMs`)を分離して直接計測します(無関係なフェーズ時間を差し引いて「ネイティブソルバー時間」とみなすことはしません)。
483
+ - **実行環境の区別**: Node.js のインプロセス WASM(`usesBrowserWorker: false`)とブラウザ Web Worker 実行(`usesBrowserWorker: true`、Worker ライフサイクルや `postMessage` 転送を含む)は異なる実行環境として区別してレポートされます。
484
+
492
485
  ---
493
486
 
494
487
  ## 📄 ライセンス