@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.
- package/README.ja.md +111 -118
- package/README.md +109 -116
- package/README.zh-CN.md +112 -119
- package/dist/browser/highs-worker.js +21 -3
- package/dist/browser/loader-CtaDHef_.js +273 -0
- package/dist/browser/solvers/glpk.js +433 -286
- package/dist/browser/solvers/glpk.min.js +1 -1
- package/dist/browser/solvers/z3.js +1002 -981
- package/dist/browser/solvers/z3.min.js +2 -2
- package/dist/browser/washi.js +2178 -622
- package/dist/browser/washi.min.js +4 -4
- package/dist/lib/highs-worker.js +21 -3
- package/dist/lib/loader-DmNC_UNk.js +279 -0
- package/dist/lib/registry-BEfvpZW3.js +951 -0
- package/dist/lib/solvers/glpk.js +201 -90
- package/dist/lib/solvers/z3.js +399 -259
- package/dist/lib/washi.js +1674 -932
- package/dist/types/index.d.ts +4 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/modeler/big-m.d.ts +75 -0
- package/dist/types/modeler/big-m.d.ts.map +1 -0
- package/dist/types/modeler/boolean-expr.d.ts +103 -14
- package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
- package/dist/types/modeler/constraint.d.ts +36 -9
- package/dist/types/modeler/constraint.d.ts.map +1 -1
- package/dist/types/modeler/expression.d.ts +117 -27
- package/dist/types/modeler/expression.d.ts.map +1 -1
- package/dist/types/modeler/index.d.ts +10 -6
- package/dist/types/modeler/index.d.ts.map +1 -1
- package/dist/types/modeler/lp-writer.d.ts +27 -2
- package/dist/types/modeler/lp-writer.d.ts.map +1 -1
- package/dist/types/modeler/model.d.ts +491 -140
- package/dist/types/modeler/model.d.ts.map +1 -1
- package/dist/types/modeler/objective.d.ts +59 -0
- package/dist/types/modeler/objective.d.ts.map +1 -0
- package/dist/types/modeler/parser.d.ts +39 -14
- package/dist/types/modeler/parser.d.ts.map +1 -1
- package/dist/types/modeler/quad-expr.d.ts +28 -7
- package/dist/types/modeler/quad-expr.d.ts.map +1 -1
- package/dist/types/modeler/solution-validator.d.ts +93 -0
- package/dist/types/modeler/solution-validator.d.ts.map +1 -0
- package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
- package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/loader.d.ts +36 -11
- package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/solver.d.ts +11 -4
- package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
- package/dist/types/modeler/solvers/mock.d.ts +14 -5
- package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
- package/dist/types/modeler/solvers/types.d.ts +71 -11
- package/dist/types/modeler/solvers/types.d.ts.map +1 -1
- package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
- package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
- package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
- package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
- package/dist/types/modeler/types.d.ts +60 -2
- package/dist/types/modeler/types.d.ts.map +1 -1
- package/dist/types/modeler/validation.d.ts +56 -0
- package/dist/types/modeler/validation.d.ts.map +1 -0
- package/dist/types/modeler/variable.d.ts +53 -13
- package/dist/types/modeler/variable.d.ts.map +1 -1
- package/docs/examples/diet-sensitivity.md +90 -0
- package/docs/examples/ecommerce-coupons.md +106 -0
- package/docs/examples/index.md +53 -0
- package/docs/examples/knapsack.md +60 -0
- package/docs/examples/portfolio-qp.md +83 -0
- package/docs/examples/production.md +51 -0
- package/docs/examples/shift-scheduling.md +175 -0
- package/docs/examples/smt-security.md +74 -0
- package/docs/examples/transportation.md +90 -0
- package/docs/examples/unsat-core.md +64 -0
- package/docs/getting-started/ai-agents.md +267 -0
- package/docs/getting-started/cross-origin.md +87 -0
- package/docs/getting-started/index.md +116 -0
- package/docs/getting-started/runtimes.md +227 -0
- package/docs/guide/constraints.md +177 -0
- package/docs/guide/expressions.md +204 -0
- package/docs/guide/objectives.md +170 -0
- package/docs/guide/unsat-core.md +147 -0
- package/docs/guide/variables.md +188 -0
- package/docs/ja/getting-started/ai-agents.md +266 -0
- package/docs/solvers/custom-solver.md +525 -0
- package/docs/solvers/glpk.md +66 -0
- package/docs/solvers/highs.md +93 -0
- package/docs/solvers/index.md +42 -0
- package/docs/solvers/z3.md +78 -0
- package/docs/zh/getting-started/ai-agents.md +266 -0
- package/llms-full.txt +3249 -0
- package/llms.txt +197 -0
- package/package.json +19 -6
- package/skills/washi/SKILL.md +197 -0
- package/dist/browser/loader-CVt4CloI.js +0 -58
- package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
- package/dist/lib/constraint-BRuB3ooC.js +0 -90
- package/dist/lib/loader-Dti0v9yC.js +0 -50
- 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
|
|
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.
|
|
42
|
-
m.
|
|
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.
|
|
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. **代数的なモデル記述**:
|
|
53
|
-
2. **ソルバーアダプター**: 共通のモデリング API から HiGHS・Z3・GLPK を利用でき、[独自アダプターも登録できます](https://github.com/ohtaman/washi/blob/main/docs/custom-solver
|
|
54
|
-
3. **ローカル実行と WebAssembly の配布**: ローカルソルバーのアダプターでは、別途 Python サービスや C/C++
|
|
55
|
-
4. **名前付き制約と SMT の結果**: Z3
|
|
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.
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
211
|
-
const motion = m.
|
|
212
|
-
const alarm = m.
|
|
213
|
-
const siren = m.
|
|
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.
|
|
218
|
-
m.
|
|
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.
|
|
222
|
-
m.
|
|
223
|
-
m.
|
|
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.
|
|
238
|
+
const rushDelivery = m.bool("rushDelivery");
|
|
246
239
|
|
|
247
240
|
// ルール 1: エンジニア1人あたり $8,000
|
|
248
|
-
m.
|
|
241
|
+
m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
|
|
249
242
|
|
|
250
243
|
// ルール 2: 特急納品が必須
|
|
251
|
-
m.
|
|
244
|
+
m.constraint(rushDelivery, "rush_required");
|
|
252
245
|
|
|
253
246
|
// ルール 3: 特急納品には最低 8 人のエンジニアが必要
|
|
254
|
-
m.
|
|
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.
|
|
250
|
+
m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
|
|
258
251
|
|
|
259
252
|
// ルール 5: 最低 1 人雇用の労働基準(無関係なルール)
|
|
260
|
-
m.
|
|
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.
|
|
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.
|
|
286
|
-
m.
|
|
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.
|
|
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.
|
|
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
|
|
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/`
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
396
|
+
完全なスタンドアロンサンプル(ESM、スクリプトタグ、CodePen)は [`examples/distribution/`](examples/distribution/README.md) に用意されています。
|
|
421
397
|
|
|
422
|
-
|
|
423
|
-
import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
|
|
424
|
-
setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
|
|
425
|
-
```
|
|
398
|
+
---
|
|
426
399
|
|
|
427
|
-
|
|
400
|
+
## 🛠️ カスタムソルバー開発と拡張性
|
|
428
401
|
|
|
429
|
-
|
|
402
|
+
WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャを採用しています。独自の最適化エンジン(クラウド REST ソルバー、WebAssembly エンジン、商用ソルバーアダプターなど)を容易に構築・登録できます:
|
|
430
403
|
|
|
431
|
-
|
|
404
|
+
- 詳細な開発手順、API コントラクト仕様(`Solver`, `SolveModelData`, `CheckModelData`, `SolverResult`)、およびテスト手法については [カスタムソルバー開発ガイド](docs/ja/solvers/custom-solver.md) をご覧ください。
|
|
432
405
|
|
|
433
406
|
---
|
|
434
407
|
|
|
435
|
-
##
|
|
408
|
+
## 📚 ドキュメント
|
|
436
409
|
|
|
437
|
-
|
|
410
|
+
完全なドキュメントは **[ohtaman.github.io/washi/ja/](https://ohtaman.github.io/washi/ja/)** およびリポジトリの [`docs/ja/`](docs/ja/)([英語版 `docs/`](docs/))で公開されています:
|
|
438
411
|
|
|
439
|
-
-
|
|
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
|
|
450
|
-
| **基本モデル** | [輸送・物流ネットワーク](https://github.com/ohtaman/washi/blob/main/examples/node/09-transportation-problem.ts) | 2次元マトリクス変数 (`
|
|
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) | 条件付き購入金額閾値 (`
|
|
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) | 数値・論理制約 (`
|
|
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
|
## 📄 ライセンス
|