@ohtaman/washi 0.1.1 → 0.2.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 +124 -22
- package/README.md +124 -22
- package/README.zh-CN.md +124 -22
- package/dist/browser/browser-runtime-D13gHOT5.js +4189 -0
- package/dist/browser/highs-worker.js +21 -0
- package/dist/{lib/demo → browser}/highs.wasm +0 -0
- package/dist/browser/highs.wasm.LICENSE +22 -0
- package/dist/browser/loader-CVt4CloI.js +58 -0
- package/dist/browser/solvers/glpk.js +1057 -0
- package/dist/browser/solvers/glpk.min.js +1 -0
- package/dist/browser/solvers/z3-built.js +8245 -0
- package/dist/browser/solvers/z3-built.wasm +4 -0
- package/dist/browser/solvers/z3.js +7528 -0
- package/dist/browser/solvers/z3.min.js +5 -0
- package/dist/browser/washi.js +1213 -0
- package/dist/browser/washi.min.js +5 -0
- package/dist/lib/async-mutex-Duo4jwzn.js +195 -0
- package/dist/lib/constraint-XWdn0ID9.js +93 -0
- package/dist/lib/highs-assets.js +1 -0
- package/dist/lib/highs-browser-runtime.js +4189 -0
- package/dist/lib/highs-node-runtime.js +1 -0
- package/dist/lib/highs-worker-factory.js +1 -0
- package/dist/lib/highs-worker.js +21 -0
- package/dist/lib/highs.wasm +0 -0
- package/dist/lib/loader-Dti0v9yC.js +50 -0
- package/dist/lib/registry-DksqCSjI.js +32 -0
- package/dist/lib/solvers/glpk.js +153 -2
- package/dist/lib/solvers/z3-assets.js +1 -0
- package/dist/lib/solvers/z3-built.js +8245 -0
- package/dist/lib/solvers/z3-built.wasm +4 -0
- package/dist/lib/solvers/z3.js +451 -2
- package/dist/lib/washi.js +388 -78
- package/dist/types/index.d.ts +2 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/modeler/index.d.ts +1 -1
- package/dist/types/modeler/index.d.ts.map +1 -1
- package/dist/types/modeler/model.d.ts +1 -1
- package/dist/types/modeler/model.d.ts.map +1 -1
- package/dist/types/modeler/solvers/glpk/index.d.ts +3 -0
- package/dist/types/modeler/solvers/glpk/index.d.ts.map +1 -0
- package/dist/types/modeler/solvers/glpk/loader.d.ts +16 -0
- package/dist/types/modeler/solvers/glpk/loader.d.ts.map +1 -0
- package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -0
- package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -0
- package/dist/types/modeler/solvers/glpk.d.ts +1 -21
- package/dist/types/modeler/solvers/glpk.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/assets.d.ts +3 -0
- package/dist/types/modeler/solvers/highs/assets.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -0
- package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/index.d.ts +6 -0
- package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -0
- package/dist/types/{highs-loader.d.ts → modeler/solvers/highs/loader.d.ts} +3 -3
- package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -0
- package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/solver.d.ts +33 -0
- package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/worker-client.d.ts +13 -0
- package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/worker-factory.d.ts +3 -0
- package/dist/types/modeler/solvers/highs/worker-factory.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/worker.d.ts +2 -0
- package/dist/types/modeler/solvers/highs/worker.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs.d.ts +1 -25
- package/dist/types/modeler/solvers/highs.d.ts.map +1 -1
- package/dist/types/modeler/solvers/index.d.ts +0 -2
- package/dist/types/modeler/solvers/index.d.ts.map +1 -1
- package/dist/types/modeler/solvers/mock.d.ts +1 -0
- package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
- package/dist/types/modeler/solvers/registry.d.ts +1 -2
- package/dist/types/modeler/solvers/registry.d.ts.map +1 -1
- package/dist/types/modeler/solvers/types.d.ts +1 -1
- package/dist/types/modeler/solvers/types.d.ts.map +1 -1
- package/dist/types/modeler/solvers/z3/assets.d.ts +11 -0
- package/dist/types/modeler/solvers/z3/assets.d.ts.map +1 -0
- package/dist/types/modeler/solvers/z3/index.d.ts +3 -0
- package/dist/types/modeler/solvers/z3/index.d.ts.map +1 -0
- package/dist/types/modeler/solvers/z3/loader.d.ts +44 -0
- package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -0
- package/dist/types/modeler/solvers/z3/solver.d.ts +34 -0
- package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -0
- package/dist/types/modeler/solvers/z3.d.ts +1 -36
- package/dist/types/modeler/solvers/z3.d.ts.map +1 -1
- package/package.json +35 -9
- package/dist/lib/assets/highs-worker-DeDWU6KI.js +0 -3
- package/dist/lib/glpk-BC32ZaPG.js +0 -126
- package/dist/lib/washi-logo.png +0 -0
- package/dist/lib/washi-logo.svg +0 -70
- package/dist/lib/z3-C8g-PtmP.js +0 -582
- package/dist/types/glpk-loader.d.ts +0 -8
- package/dist/types/glpk-loader.d.ts.map +0 -1
- package/dist/types/highs-loader.d.ts.map +0 -1
- package/dist/types/highs-worker-client.d.ts +0 -12
- package/dist/types/highs-worker-client.d.ts.map +0 -1
- package/dist/types/highs-worker.d.ts +0 -2
- package/dist/types/highs-worker.d.ts.map +0 -1
package/README.ja.md
CHANGED
|
@@ -51,7 +51,7 @@ console.log(`生産量: x=${x.value}, y=${y.value} (最大利益: $${m.objective
|
|
|
51
51
|
2. 📦 **ゼロ依存・単一パッケージでの圧倒的な配布容易性**: Python のランタイム管理や仮想環境、OS 別の C/C++ ネイティブソルバーバイナリの同梱に悩まされることはありません。`npm install @ohtaman/washi` だけで WebAssembly ソルバーを含めて即座に動作します。
|
|
52
52
|
3. ✍️ **直感的な代数モデリング DX(PuLP / python-mip 互換)**: 泥臭い行列のインデックス操作は不要。テンプレートリテラル記法(`c`${x} + 2 * ${y} <= 40``)や、Python 最適化開発者におなじみの構文(`LpProblem`, `lpSum`)で、教科書そのままの数式を記述できます。
|
|
53
53
|
4. 🔄 **統一マルチソルバー API(HiGHS / Z3 / GLPK)**: 定式化したモデルコードはそのまま、産業用 MIP/QP に強い **HiGHS**、論理制約・SMT に強い **Z3**、学術用途の **GLPK** をオプション1つで柔軟に切り替え可能です。
|
|
54
|
-
5. 🔍 **実行不可能(Infeasible)時の矛盾原因自動診断**:
|
|
54
|
+
5. 🔍 **実行不可能(Infeasible)時の矛盾原因自動診断**: ルール同士が矛盾して解なしになった場合、衝突している制約の部分集合(Unsat Core)を Z3 エンジン経由で特定・診断します。
|
|
55
55
|
6. ⚡ **WebAssembly 上での本格的な産業規模パフォーマンス**: 線形時間 $O(N)$ の高速式ビルダーにより、20,000 変数を超える大規模 MIP を約100msで定式化し、WebAssembly 版 HiGHS で約15秒で高速に求解します。
|
|
56
56
|
|
|
57
57
|
---
|
|
@@ -200,6 +200,7 @@ assets.forEach((name, i) => {
|
|
|
200
200
|
|
|
201
201
|
```ts
|
|
202
202
|
import { Model, not } from "@ohtaman/washi";
|
|
203
|
+
import "@ohtaman/washi/solvers/z3";
|
|
203
204
|
|
|
204
205
|
const m = new Model({ name: "SmartSecurityRules" });
|
|
205
206
|
|
|
@@ -229,7 +230,7 @@ console.log(`サイレン作動? ${m.values.get("siren")}`); // true
|
|
|
229
230
|
|
|
230
231
|
### 4. 矛盾原因の自動特定 (`unsatCore`)
|
|
231
232
|
|
|
232
|
-
|
|
233
|
+
業務ルールや制約群が互いに矛盾している場合、**矛盾を引き起こしている制約の部分集合**(Unsat Core。最小性は保証しません)を特定してデバッグ(Z3 が必要):
|
|
233
234
|
|
|
234
235
|
```ts
|
|
235
236
|
import { Model } from "@ohtaman/washi";
|
|
@@ -265,7 +266,30 @@ console.log("矛盾している制約:", res.unsatCore);
|
|
|
265
266
|
|
|
266
267
|
---
|
|
267
268
|
|
|
268
|
-
### 5.
|
|
269
|
+
### 5. GLPK ソルバーの利用 (GPL-3.0)
|
|
270
|
+
|
|
271
|
+
GNU Linear Programming Kit (GLPK) ソルバープラグインを利用する場合:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { Model, expr, c } from "@ohtaman/washi";
|
|
275
|
+
import "@ohtaman/washi/solvers/glpk";
|
|
276
|
+
|
|
277
|
+
const m = new Model({ sense: "Maximize", solver: "glpk" });
|
|
278
|
+
const x = m.addVar({ type: "Integer", lb: 0 });
|
|
279
|
+
const y = m.addVar({ type: "Integer", lb: 0 });
|
|
280
|
+
|
|
281
|
+
m.maximize(expr`30 * ${x} + 50 * ${y}`);
|
|
282
|
+
m.addConstraint(c`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
283
|
+
m.addConstraint(c`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
284
|
+
|
|
285
|
+
const status = await m.solve();
|
|
286
|
+
console.log(`ステータス: ${status}`); // "Optimal"
|
|
287
|
+
console.log(`目的関数値: ${m.objectiveValue}`); // 1100
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### 6. Python PuLP 互換レイヤー
|
|
269
293
|
|
|
270
294
|
既存の Python PuLP 最適化スクリプトをスムーズに移植:
|
|
271
295
|
|
|
@@ -300,40 +324,117 @@ console.log(`x = ${value(x)}, y = ${value(y)}`);
|
|
|
300
324
|
|
|
301
325
|
## 🌐 ブラウザ & Web Worker での利用方法
|
|
302
326
|
|
|
303
|
-
|
|
327
|
+
HiGHS はデフォルトでパッケージ同梱 WASM を使います。**CDN への自動フォールバックはありません。** Node.js はインストールされた HiGHS を使い、Vite / webpack 5 はパッケージ内の WASM・Worker 参照から配布ファイルを生成します。
|
|
304
328
|
|
|
305
|
-
###
|
|
306
|
-
ブラウザ環境において、オリジンルートに `highs.wasm` が配置されていない場合、WaSHI は自動的に公式 jsDelivr CDN から安全にバイナリを取得します。追加の設定コードやファイルコピーは一切不要です:
|
|
329
|
+
### npm とバンドラー
|
|
307
330
|
|
|
308
331
|
```typescript
|
|
309
332
|
import { Model } from "@ohtaman/washi";
|
|
310
|
-
|
|
311
333
|
const model = new Model();
|
|
312
|
-
//
|
|
313
|
-
await model.solve();
|
|
334
|
+
// 変数・目的関数・制約を定義
|
|
335
|
+
await model.solve();
|
|
314
336
|
```
|
|
315
337
|
|
|
316
|
-
|
|
317
|
-
`highs.wasm` を自分のアプリケーションの静的アセット(ハッシュ付き配信)として自前ホストしたい場合:
|
|
338
|
+
検証対象の Vite / webpack ビルドでは WASM の手動コピー・URL 指定は不要です。その他のバンドラーや SSR は個別に確認し、ブラウザの求解はクライアントで実行してください。ブラウザでは Worker をデフォルトで使用します。簡単なデモは `model.solve({ useWorker: false })` でも動きますが、長い計算中は UI が止まります。
|
|
318
339
|
|
|
319
|
-
|
|
320
|
-
import wasmUrl from "@ohtaman/washi/highs.wasm?url";
|
|
321
|
-
import { setHighsWasmUrl } from "@ohtaman/washi";
|
|
340
|
+
### ブラウザ標準 ESM(利用者側のビルド不要)
|
|
322
341
|
|
|
323
|
-
|
|
324
|
-
|
|
342
|
+
`dist/browser` 一式(JS の分割ファイル・`highs.wasm`・Worker を含む)を HTTP 配信します。Import Maps は不要です。
|
|
343
|
+
|
|
344
|
+
```html
|
|
345
|
+
<script type="module">
|
|
346
|
+
import { Model } from "./washi/washi.js";
|
|
347
|
+
const model = new Model();
|
|
348
|
+
// 変数・目的関数・制約を定義
|
|
349
|
+
await model.solve({ useWorker: false });
|
|
350
|
+
</script>
|
|
325
351
|
```
|
|
326
352
|
|
|
327
|
-
###
|
|
328
|
-
外部 CDN への通信が禁止されている社内イントラやオフライン環境の場合:
|
|
353
|
+
### 通常の script / CodePen
|
|
329
354
|
|
|
330
|
-
|
|
331
|
-
|
|
355
|
+
`dist/browser/washi.min.js` はグローバル API `Washi` を公開します。CodePen の External Scripts にその URL を追加するか、次のように読み込みます。
|
|
356
|
+
|
|
357
|
+
```html
|
|
358
|
+
<script src="./washi/washi.min.js"></script>
|
|
359
|
+
<script>
|
|
360
|
+
const model = new Washi.Model();
|
|
361
|
+
// 変数・目的関数・制約を定義
|
|
362
|
+
model.solve({ useWorker: false }).then(status => console.log(status));
|
|
363
|
+
</script>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
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 へ切り替わることはありません。
|
|
367
|
+
|
|
368
|
+
### ブラウザ環境でのソルバープラグイン(GLPK & Z3)
|
|
369
|
+
|
|
370
|
+
ブラウザ配布物には、`dist/browser/solvers/` 配下に事前バンドルされたスタンドアロンソルバープラグインが含まれています(CDN 利用時は `>= 0.2.0` または `@latest` で利用可能):
|
|
371
|
+
- **GLPK**: `dist/browser/solvers/glpk.js` (ESM) および `dist/browser/solvers/glpk.min.js` (IIFE、グローバル `WashiGLPK` を登録)
|
|
372
|
+
- **Z3**: `dist/browser/solvers/z3.js` (ESM) および `dist/browser/solvers/z3.min.js` (IIFE、グローバル `WashiZ3` を登録)、ならびに `z3-built.js` / `z3-built.wasm`
|
|
373
|
+
|
|
374
|
+
#### ブラウザ ESM での GLPK 利用
|
|
375
|
+
```html
|
|
376
|
+
<script type="module">
|
|
377
|
+
import { Model } from "./washi/washi.js";
|
|
378
|
+
import "./washi/solvers/glpk.js";
|
|
379
|
+
|
|
380
|
+
const model = new Model({ solver: "glpk" });
|
|
381
|
+
const x = model.addVar({ lb: 0, ub: 10 });
|
|
382
|
+
model.maximize(x);
|
|
383
|
+
await model.solve();
|
|
384
|
+
console.log("GLPK ステータス:", model.status);
|
|
385
|
+
</script>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### スクリプトタグでの GLPK 利用
|
|
389
|
+
```html
|
|
390
|
+
<script src="./washi/washi.min.js"></script>
|
|
391
|
+
<script src="./washi/solvers/glpk.min.js"></script>
|
|
392
|
+
<script>
|
|
393
|
+
const model = new Washi.Model({ solver: "glpk" });
|
|
394
|
+
const x = model.addVar({ lb: 0, ub: 10 });
|
|
395
|
+
model.maximize(x);
|
|
396
|
+
model.solve().then(status => console.log("GLPK ステータス:", status));
|
|
397
|
+
</script>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
> [!NOTE]
|
|
401
|
+
> **ブラウザ環境での Z3 利用について**:
|
|
402
|
+
> Z3 の WebAssembly エンジンはマルチスレッド(`SharedArrayBuffer`)を必要とするため、配信 Web サーバーがドキュメントレスポンスに以下の Cross-Origin-Isolation ヘッダーを送信する必要があります:
|
|
403
|
+
> ```http
|
|
404
|
+
> Cross-Origin-Opener-Policy: same-origin
|
|
405
|
+
> Cross-Origin-Embedder-Policy: require-corp
|
|
406
|
+
> ```
|
|
407
|
+
> また、別オリジン(CDN など)からアセットを読み込む場合は `Cross-Origin-Resource-Policy: cross-origin` の返却が必要です。
|
|
408
|
+
|
|
409
|
+
完全なスタンドアロンサンプル(ESM、スクリプトタグ、CodePen)は `examples/distribution/` に用意されています。
|
|
410
|
+
|
|
411
|
+
### 工場内・オフライン配布、Worker、自前配信
|
|
332
412
|
|
|
333
|
-
|
|
334
|
-
|
|
413
|
+
アプリのビルド出力一式、または `dist/browser` 一式をローカル HTTP サーバー・社内サーバーに配布します。必要なアセットがローカルにあれば、ブラウザのキャッシュが空でも外部ネットワークは不要です。HTML や最初の JS だけでは不足します。`file://` 実行や PWA の自動キャッシュは対象外です。
|
|
414
|
+
|
|
415
|
+
ESM はモジュール URL、script 版は読み込み時に保持した script URL からアセットを解決します。CDN の Worker は Blob のモジュールから起動します。配信元の CORS と、CSP の script・通信先許可が必要です。CDN Worker では `worker-src blob:` も許可してください。使えない場合は `useWorker: false` を指定します。Worker の読み込み・メッセージ処理に失敗した場合は、求解 Promise がエラーになります。
|
|
416
|
+
|
|
417
|
+
自前 URL は `setHighsWasmUrl(url)` / `options.wasmUrl` で指定できます。従来の HiGHS CDN を明示的に選ぶ場合:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
|
|
421
|
+
setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
|
|
335
422
|
```
|
|
336
423
|
|
|
424
|
+
初期化に失敗すると取得先を含むエラーを返し、別ホストには切り替えません。従来のオリジンルート / CDN 自動フォールバックを利用していた場合は、生成アセットを配布するか URL を明示してください。
|
|
425
|
+
|
|
426
|
+
### 配布物の検証
|
|
427
|
+
|
|
428
|
+
`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 はそのモードを使い、別オリジン配信と外部ネットワーク遮断も確認します。
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 🛠️ カスタムソルバー開発と拡張性
|
|
433
|
+
|
|
434
|
+
WaSHI は疎結合なプラグイン方式のソルバーアーキテクチャを採用しています。独自の最適化エンジン(クラウド REST ソルバー、WebAssembly エンジン、商用ソルバーアダプターなど)を容易に構築・登録できます:
|
|
435
|
+
|
|
436
|
+
- 詳細な開発手順、API コントラクト仕様(`Solver`, `LpModelData`, `SolverResult`)、およびテスト手法については [Custom Solver Developer Guide](docs/custom-solver-guide.md) をご覧ください。
|
|
437
|
+
|
|
337
438
|
---
|
|
338
439
|
|
|
339
440
|
## 💻 サンプルスクリプト & インタラクティブ Web デモ
|
|
@@ -347,6 +448,7 @@ npx tsx examples/node/03-portfolio-qp.ts
|
|
|
347
448
|
npx tsx examples/node/04-smt-security-rules.ts
|
|
348
449
|
npx tsx examples/node/05-unsat-core-diagnosis.ts
|
|
349
450
|
npx tsx examples/node/06-pulp-compatibility.ts
|
|
451
|
+
npx tsx examples/node/08-glpk-solver-plugin.ts
|
|
350
452
|
```
|
|
351
453
|
|
|
352
454
|
ブラウザで動く Web デモ (`examples/web-demo`) を起動するには:
|
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`
|
|
|
51
51
|
2. 📦 **Frictionless Zero-Dependency Distribution**: No Python runtimes, no C/C++ compilation, and no OS-dependent native solver binaries to bundle. Just `npm install @ohtaman/washi`—everything (including WebAssembly solvers) works instantly out of the box.
|
|
52
52
|
3. ✍️ **Familiar Algebraic Modeling (PuLP & python-mip DX)**: Never struggle with low-level matrix indices. Formulate models naturally using tagged template literals (`c`${x} + 2 * ${y} <= 40``) or drop-in PuLP/python-mip syntax (`LpProblem`, `lpSum`).
|
|
53
53
|
4. 🔄 **Pluggable Solvers & Unified API**: Formulate your mathematical model once; solve with **HiGHS** (industrial MIP/QP), **Z3** (SMT logic & Unsat Core diagnosis), or **GLPK** with a single parameter.
|
|
54
|
-
5. 🔍 **Automatic Conflict Diagnosis (`unsatCore`)**: When constraints are contradictory (Infeasible), diagnose root causes
|
|
54
|
+
5. 🔍 **Automatic Conflict Diagnosis (`unsatCore`)**: When constraints are contradictory (Infeasible), diagnose root causes by extracting a conflicting constraint subset (unsatisfiable core) via the Z3 engine.
|
|
55
55
|
6. ⚡ **Industrial Scale Performance in WebAssembly**: Built with an $O(N)$ linear-time expression builder. Formulate 20,000+ variable MIPs in ~100ms and solve them in ~15 seconds right inside WebAssembly via HiGHS.
|
|
56
56
|
|
|
57
57
|
---
|
|
@@ -200,6 +200,7 @@ Express boolean variables, propositional connectives (`and`, `or`, `implies`, `n
|
|
|
200
200
|
|
|
201
201
|
```ts
|
|
202
202
|
import { Model, not } from "@ohtaman/washi";
|
|
203
|
+
import "@ohtaman/washi/solvers/z3";
|
|
203
204
|
|
|
204
205
|
const m = new Model({ name: "SmartSecurityRules" });
|
|
205
206
|
|
|
@@ -229,7 +230,7 @@ console.log(`Siren active? ${m.values.get("siren")}`); // true
|
|
|
229
230
|
|
|
230
231
|
### 4. Conflict Diagnosis (`unsatCore`)
|
|
231
232
|
|
|
232
|
-
When business rules or constraints contradict each other, extract
|
|
233
|
+
When business rules or constraints contradict each other, extract a **conflicting constraint subset** (unsatisfiable core; minimality is not guaranteed) for troubleshooting (requires Z3):
|
|
233
234
|
|
|
234
235
|
```ts
|
|
235
236
|
import { Model } from "@ohtaman/washi";
|
|
@@ -265,7 +266,30 @@ console.log("Conflicting constraints:", res.unsatCore);
|
|
|
265
266
|
|
|
266
267
|
---
|
|
267
268
|
|
|
268
|
-
### 5.
|
|
269
|
+
### 5. Solving with GLPK (GPL-3.0)
|
|
270
|
+
|
|
271
|
+
To use the GNU Linear Programming Kit (GLPK) solver plugin:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { Model, expr, c } from "@ohtaman/washi";
|
|
275
|
+
import "@ohtaman/washi/solvers/glpk";
|
|
276
|
+
|
|
277
|
+
const m = new Model({ sense: "Maximize", solver: "glpk" });
|
|
278
|
+
const x = m.addVar({ type: "Integer", lb: 0 });
|
|
279
|
+
const y = m.addVar({ type: "Integer", lb: 0 });
|
|
280
|
+
|
|
281
|
+
m.maximize(expr`30 * ${x} + 50 * ${y}`);
|
|
282
|
+
m.addConstraint(c`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
283
|
+
m.addConstraint(c`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
284
|
+
|
|
285
|
+
const status = await m.solve();
|
|
286
|
+
console.log(`Status: ${status}`); // "Optimal"
|
|
287
|
+
console.log(`Objective: ${m.objectiveValue}`); // 1100
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### 6. Python PuLP Compatibility Layer
|
|
269
293
|
|
|
270
294
|
If you have existing Python PuLP code, port it with near zero friction:
|
|
271
295
|
|
|
@@ -300,40 +324,117 @@ console.log(`x = ${value(x)}, y = ${value(y)}`);
|
|
|
300
324
|
|
|
301
325
|
## 🌐 Browser & Web Worker Usage
|
|
302
326
|
|
|
303
|
-
|
|
327
|
+
The default HiGHS engine uses the package's WASM asset. There is **no automatic CDN fallback**. Node.js uses the installed HiGHS runtime; Vite and webpack 5 can discover the package-relative WASM and Worker references when building a browser application.
|
|
304
328
|
|
|
305
|
-
###
|
|
306
|
-
In browser environments, if `highs.wasm` is not found on your local origin, WaSHI automatically falls back to the official jsDelivr CDN. You can optimize without copying files or writing boilerplate:
|
|
329
|
+
### npm with a bundler
|
|
307
330
|
|
|
308
331
|
```typescript
|
|
309
332
|
import { Model } from "@ohtaman/washi";
|
|
310
|
-
|
|
311
333
|
const model = new Model();
|
|
312
|
-
//
|
|
313
|
-
await model.solve();
|
|
334
|
+
// Define variables, objective and constraints.
|
|
335
|
+
await model.solve();
|
|
314
336
|
```
|
|
315
337
|
|
|
316
|
-
|
|
317
|
-
To bundle and self-host `highs.wasm` as a versioned, hashed static asset with your application:
|
|
338
|
+
No manual WASM copy or URL configuration is needed in the tested Vite / webpack builds. Other bundlers and SSR need their own integration checks; call the browser solver on the client. Worker execution is the browser default. Use `model.solve({ useWorker: false })` for a simple main-thread demo; a long solve will block the UI.
|
|
318
339
|
|
|
319
|
-
|
|
320
|
-
import wasmUrl from "@ohtaman/washi/highs.wasm?url";
|
|
321
|
-
import { setHighsWasmUrl } from "@ohtaman/washi";
|
|
340
|
+
### Browser ESM without an application build
|
|
322
341
|
|
|
323
|
-
|
|
324
|
-
|
|
342
|
+
Serve the **entire `dist/browser` directory**, including its JS chunks, `highs.wasm` and Worker. No import map is needed:
|
|
343
|
+
|
|
344
|
+
```html
|
|
345
|
+
<script type="module">
|
|
346
|
+
import { Model } from "./washi/washi.js";
|
|
347
|
+
const model = new Model();
|
|
348
|
+
// Define variables, objective and constraints.
|
|
349
|
+
await model.solve({ useWorker: false });
|
|
350
|
+
</script>
|
|
325
351
|
```
|
|
326
352
|
|
|
327
|
-
###
|
|
328
|
-
In strict enterprise intranet or air-gapped environments where external CDN network access is disallowed:
|
|
353
|
+
### Plain script / CodePen
|
|
329
354
|
|
|
330
|
-
|
|
331
|
-
|
|
355
|
+
The package also includes `dist/browser/washi.min.js`, which exports the `Washi` global. Put that file's URL in CodePen's External Scripts, or use a script tag:
|
|
356
|
+
|
|
357
|
+
```html
|
|
358
|
+
<script src="./washi/washi.min.js"></script>
|
|
359
|
+
<script>
|
|
360
|
+
const model = new Washi.Model();
|
|
361
|
+
// Define variables, objective and constraints.
|
|
362
|
+
model.solve({ useWorker: false }).then(status => console.log(status));
|
|
363
|
+
</script>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
For CDN usage, replace the local URL with `https://cdn.jsdelivr.net/npm/@ohtaman/washi@0.1.2/dist/browser/washi.min.js` (or pin your desired release version `>= 0.1.2`). For ESM use the corresponding `dist/browser/washi.js` URL. Selecting the CDN URL is an explicit choice; local copies never switch to a CDN.
|
|
367
|
+
|
|
368
|
+
### Solver Plugins in the Browser (GLPK & Z3)
|
|
369
|
+
|
|
370
|
+
The browser distribution includes pre-bundled, standalone solver plugins under `dist/browser/solvers/` (available from `>= 0.2.0` or `@latest` on CDN):
|
|
371
|
+
- **GLPK**: `dist/browser/solvers/glpk.js` (ESM) and `dist/browser/solvers/glpk.min.js` (IIFE, registers global `WashiGLPK`)
|
|
372
|
+
- **Z3**: `dist/browser/solvers/z3.js` (ESM) and `dist/browser/solvers/z3.min.js` (IIFE, registers global `WashiZ3`), accompanied by `z3-built.js` and `z3-built.wasm`
|
|
373
|
+
|
|
374
|
+
#### Browser ESM with GLPK
|
|
375
|
+
```html
|
|
376
|
+
<script type="module">
|
|
377
|
+
import { Model } from "./washi/washi.js";
|
|
378
|
+
import "./washi/solvers/glpk.js";
|
|
379
|
+
|
|
380
|
+
const model = new Model({ solver: "glpk" });
|
|
381
|
+
const x = model.addVar({ lb: 0, ub: 10 });
|
|
382
|
+
model.maximize(x);
|
|
383
|
+
await model.solve();
|
|
384
|
+
console.log("GLPK status:", model.status);
|
|
385
|
+
</script>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### Plain Script with GLPK
|
|
389
|
+
```html
|
|
390
|
+
<script src="./washi/washi.min.js"></script>
|
|
391
|
+
<script src="./washi/solvers/glpk.min.js"></script>
|
|
392
|
+
<script>
|
|
393
|
+
const model = new Washi.Model({ solver: "glpk" });
|
|
394
|
+
const x = model.addVar({ lb: 0, ub: 10 });
|
|
395
|
+
model.maximize(x);
|
|
396
|
+
model.solve().then(status => console.log("GLPK status:", status));
|
|
397
|
+
</script>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
> [!NOTE]
|
|
401
|
+
> **Z3 in Browser Environments**:
|
|
402
|
+
> Because the Z3 WebAssembly engine uses multithreading (`SharedArrayBuffer`), the hosting web server must send Cross-Origin-Isolation delivery headers on the document response:
|
|
403
|
+
> ```http
|
|
404
|
+
> Cross-Origin-Opener-Policy: same-origin
|
|
405
|
+
> Cross-Origin-Embedder-Policy: require-corp
|
|
406
|
+
> ```
|
|
407
|
+
> And any cross-origin assets (e.g. from a CDN) must serve `Cross-Origin-Resource-Policy: cross-origin`.
|
|
408
|
+
|
|
409
|
+
Complete standalone examples for ESM, script tags, and CodePen are available in `examples/distribution/`.
|
|
410
|
+
|
|
411
|
+
### Offline distribution, Workers and custom hosting
|
|
332
412
|
|
|
333
|
-
|
|
334
|
-
|
|
413
|
+
Deploy the full application build or the entire browser distribution to a local HTTP / intranet server. With all assets locally available, an empty browser cache does not require access to the internet. Saving only the HTML or the first JS file is insufficient. `file://` execution and automatic PWA caching are not supported by this distribution.
|
|
414
|
+
|
|
415
|
+
ESM assets resolve relative to their module URL; the script build captures its own script URL at load time. CDN Workers use a Blob module bootstrap. The server must permit cross-origin JS / WASM loading; CSP must permit the selected script / connection origins and `worker-src blob:` for CDN Workers. If that is unavailable, use `useWorker: false`. Worker load / message failures reject pending solves rather than leaving them unresolved.
|
|
416
|
+
|
|
417
|
+
For custom hosting, `setHighsWasmUrl(url)` and `options.wasmUrl` remain available. To explicitly use the existing HiGHS CDN:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
|
|
421
|
+
setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
|
|
335
422
|
```
|
|
336
423
|
|
|
424
|
+
Failed initialization reports the attempted URL and does not try a different host. This changes the previous local-root/CDN fallback behavior: deploy the generated assets or configure an explicit URL.
|
|
425
|
+
|
|
426
|
+
### Verify the distributable
|
|
427
|
+
|
|
428
|
+
`npm run test:distribution` packs and installs the actual npm tarball, builds Vite / webpack consumers without optional peers, and runs their emitted JS with real WASM in a VM. This is not a browser/CORS test. For Playwright Chromium checks, run `npx playwright install chromium` and `RUN_DISTRIBUTION_BROWSER=1 npm run test:distribution`. CI also runs that browser mode, including separate-origin browser bundles with external network requests blocked.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 🛠️ Custom Solvers & Extensibility
|
|
433
|
+
|
|
434
|
+
WaSHI features a decoupled, pluggable solver architecture. You can easily build and register your own custom solver adapter (e.g., for cloud REST optimizers, WebAssembly engines, or proprietary solvers):
|
|
435
|
+
|
|
436
|
+
- See the complete [Custom Solver Developer Guide](docs/custom-solver-guide.md) for step-by-step tutorials, complete API contracts (`Solver`, `LpModelData`, `SolverResult`), and testing best practices.
|
|
437
|
+
|
|
337
438
|
---
|
|
338
439
|
|
|
339
440
|
## 💻 CLI & Interactive Demos
|
|
@@ -347,6 +448,7 @@ npx tsx examples/node/03-portfolio-qp.ts
|
|
|
347
448
|
npx tsx examples/node/04-smt-security-rules.ts
|
|
348
449
|
npx tsx examples/node/05-unsat-core-diagnosis.ts
|
|
349
450
|
npx tsx examples/node/06-pulp-compatibility.ts
|
|
451
|
+
npx tsx examples/node/08-glpk-solver-plugin.ts
|
|
350
452
|
```
|
|
351
453
|
|
|
352
454
|
To run the interactive browser showcase (`examples/web-demo`):
|
package/README.zh-CN.md
CHANGED
|
@@ -51,7 +51,7 @@ console.log(`生产计划: x=${x.value}, y=${y.value} (最大利润: $${m.object
|
|
|
51
51
|
2. 📦 **零依赖・单一包分发**:告别 Python 环境配置与繁重的跨平台 C/C++ 原生求解器编译绑定。只需 `npm install @ohtaman/washi`,包括 WebAssembly 求解器在内的所有组件即可开箱即用。
|
|
52
52
|
3. ✍️ **极简现代代数建模 DX(兼容 PuLP / python-mip)**:无需编写晦涩低级的矩阵索引。使用直观的模板字面量(`c`${x} + 2 * ${y} <= 40``)或熟悉的 Python 风格抽象(`LpProblem`, `lpSum`),像在草稿纸上写公式一样自然。
|
|
53
53
|
4. 🔄 **统一多求解器接口(HiGHS / Z3 / GLPK)**:模型代码一次编写,即可通过统一 API 自由切换:擅长工业级 MIP/QP 的 **HiGHS**、擅长逻辑求解与冲突分析的 **Z3**,以及经典学术求解器 **GLPK**。
|
|
54
|
-
5. 🔍 **不可行模型(Infeasible)冲突根因诊断**:当业务规则出现矛盾导致无解时,可通过 Z3
|
|
54
|
+
5. 🔍 **不可行模型(Infeasible)冲突根因诊断**:当业务规则出现矛盾导致无解时,可通过 Z3 引擎快速定位并提取冲突约束子集(Unsat Core)。
|
|
55
55
|
6. ⚡ **WebAssembly 原生工业级大规模性能**:搭载 $O(N)$ 线性时间高速表达式构建器,仅需约 100ms 即可构建 20,000+ 变量的大规模 MIP,并在浏览器 WebAssembly 中由 HiGHS 在约 15 秒内高效求解。
|
|
56
56
|
|
|
57
57
|
---
|
|
@@ -200,6 +200,7 @@ assets.forEach((name, i) => {
|
|
|
200
200
|
|
|
201
201
|
```ts
|
|
202
202
|
import { Model, not } from "@ohtaman/washi";
|
|
203
|
+
import "@ohtaman/washi/solvers/z3";
|
|
203
204
|
|
|
204
205
|
const m = new Model({ name: "SmartSecurityRules" });
|
|
205
206
|
|
|
@@ -229,7 +230,7 @@ console.log(`警报响起? ${m.values.get("siren")}`); // true
|
|
|
229
230
|
|
|
230
231
|
### 4. 约束冲突根因诊断 (`unsatCore`)
|
|
231
232
|
|
|
232
|
-
当规则或约束之间存在矛盾时,Z3
|
|
233
|
+
当规则或约束之间存在矛盾时,Z3 能够提取引发冲突的**矛盾约束子集**(Unsat Core,不保证最小性;需使用 Z3):
|
|
233
234
|
|
|
234
235
|
```ts
|
|
235
236
|
import { Model } from "@ohtaman/washi";
|
|
@@ -265,7 +266,30 @@ console.log("冲突约束子集:", res.unsatCore);
|
|
|
265
266
|
|
|
266
267
|
---
|
|
267
268
|
|
|
268
|
-
### 5.
|
|
269
|
+
### 5. 使用 GLPK 求解器 (GPL-3.0)
|
|
270
|
+
|
|
271
|
+
使用 GNU Linear Programming Kit (GLPK) 求解器插件:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { Model, expr, c } from "@ohtaman/washi";
|
|
275
|
+
import "@ohtaman/washi/solvers/glpk";
|
|
276
|
+
|
|
277
|
+
const m = new Model({ sense: "Maximize", solver: "glpk" });
|
|
278
|
+
const x = m.addVar({ type: "Integer", lb: 0 });
|
|
279
|
+
const y = m.addVar({ type: "Integer", lb: 0 });
|
|
280
|
+
|
|
281
|
+
m.maximize(expr`30 * ${x} + 50 * ${y}`);
|
|
282
|
+
m.addConstraint(c`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
283
|
+
m.addConstraint(c`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
284
|
+
|
|
285
|
+
const status = await m.solve();
|
|
286
|
+
console.log(`求解状态: ${status}`); // "Optimal"
|
|
287
|
+
console.log(`目标函数值: ${m.objectiveValue}`); // 1100
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### 6. Python PuLP 兼容层
|
|
269
293
|
|
|
270
294
|
若你已有 Python PuLP 代码资产,可零摩擦迁移:
|
|
271
295
|
|
|
@@ -300,40 +324,117 @@ console.log(`x = ${value(x)}, y = ${value(y)}`);
|
|
|
300
324
|
|
|
301
325
|
## 🌐 浏览器与 Web Worker 使用指南
|
|
302
326
|
|
|
303
|
-
|
|
327
|
+
HiGHS 默认使用包内的 WASM,**不会自动回退到 CDN**。Node.js 使用已安装的 HiGHS;Vite / webpack 5 可以根据包内的 WASM 和 Worker 引用生成部署文件。
|
|
304
328
|
|
|
305
|
-
###
|
|
306
|
-
在浏览器环境下,若当前域名根路径未部署 `highs.wasm`,WaSHI 会自动从官方 jsDelivr 高速 CDN 安全拉取二进制文件。您无需任何额外配置或文件复制:
|
|
329
|
+
### npm 与构建工具
|
|
307
330
|
|
|
308
331
|
```typescript
|
|
309
332
|
import { Model } from "@ohtaman/washi";
|
|
310
|
-
|
|
311
333
|
const model = new Model();
|
|
312
|
-
//
|
|
313
|
-
await model.solve();
|
|
334
|
+
// 定义变量、目标函数和约束
|
|
335
|
+
await model.solve();
|
|
314
336
|
```
|
|
315
337
|
|
|
316
|
-
|
|
317
|
-
如果您希望将 `highs.wasm` 作为项目打包产物的一部分进行自托管与版本哈希管理:
|
|
338
|
+
在验证的 Vite / webpack 构建中,不需要手动复制 WASM 或配置 URL。其他构建工具及 SSR 需要单独验证;浏览器求解应在客户端执行。浏览器默认使用 Worker。简单演示可以使用 `model.solve({ useWorker: false })`,但较长的计算会阻塞 UI。
|
|
318
339
|
|
|
319
|
-
|
|
320
|
-
import wasmUrl from "@ohtaman/washi/highs.wasm?url";
|
|
321
|
-
import { setHighsWasmUrl } from "@ohtaman/washi";
|
|
340
|
+
### 浏览器原生 ESM(无需应用构建)
|
|
322
341
|
|
|
323
|
-
|
|
324
|
-
|
|
342
|
+
通过 HTTP 提供整个 `dist/browser` 目录,包括 JS 分块、`highs.wasm` 和 Worker。不需要 Import Maps。
|
|
343
|
+
|
|
344
|
+
```html
|
|
345
|
+
<script type="module">
|
|
346
|
+
import { Model } from "./washi/washi.js";
|
|
347
|
+
const model = new Model();
|
|
348
|
+
// 定义变量、目标函数和约束
|
|
349
|
+
await model.solve({ useWorker: false });
|
|
350
|
+
</script>
|
|
325
351
|
```
|
|
326
352
|
|
|
327
|
-
###
|
|
328
|
-
针对严禁外部 CDN 请求的企业内网或纯离线环境:
|
|
353
|
+
### 普通 script / CodePen
|
|
329
354
|
|
|
330
|
-
|
|
331
|
-
|
|
355
|
+
`dist/browser/washi.min.js` 导出全局 API `Washi`。将其 URL 添加到 CodePen 的 External Scripts,或使用:
|
|
356
|
+
|
|
357
|
+
```html
|
|
358
|
+
<script src="./washi/washi.min.js"></script>
|
|
359
|
+
<script>
|
|
360
|
+
const model = new Washi.Model();
|
|
361
|
+
// 定义变量、目标函数和约束
|
|
362
|
+
model.solve({ useWorker: false }).then(status => console.log(status));
|
|
363
|
+
</script>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
若通过 CDN 加载,可使用 `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。
|
|
367
|
+
|
|
368
|
+
### 浏览器环境中的求解器插件(GLPK & Z3)
|
|
369
|
+
|
|
370
|
+
浏览器发布包在 `dist/browser/solvers/` 下提供了开箱即用的独立求解器插件(通过 CDN 引入时适用于 `>= 0.2.0` 或 `@latest`):
|
|
371
|
+
- **GLPK**: `dist/browser/solvers/glpk.js` (ESM) 与 `dist/browser/solvers/glpk.min.js` (IIFE,挂载全局 `WashiGLPK`)
|
|
372
|
+
- **Z3**: `dist/browser/solvers/z3.js` (ESM) 与 `dist/browser/solvers/z3.min.js` (IIFE,挂载全局 `WashiZ3`),附带 `z3-built.js` 和 `z3-built.wasm`
|
|
373
|
+
|
|
374
|
+
#### 浏览器 ESM 使用 GLPK
|
|
375
|
+
```html
|
|
376
|
+
<script type="module">
|
|
377
|
+
import { Model } from "./washi/washi.js";
|
|
378
|
+
import "./washi/solvers/glpk.js";
|
|
379
|
+
|
|
380
|
+
const model = new Model({ solver: "glpk" });
|
|
381
|
+
const x = model.addVar({ lb: 0, ub: 10 });
|
|
382
|
+
model.maximize(x);
|
|
383
|
+
await model.solve();
|
|
384
|
+
console.log("GLPK 求解状态:", model.status);
|
|
385
|
+
</script>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### 原生脚本标签使用 GLPK
|
|
389
|
+
```html
|
|
390
|
+
<script src="./washi/washi.min.js"></script>
|
|
391
|
+
<script src="./washi/solvers/glpk.min.js"></script>
|
|
392
|
+
<script>
|
|
393
|
+
const model = new Washi.Model({ solver: "glpk" });
|
|
394
|
+
const x = model.addVar({ lb: 0, ub: 10 });
|
|
395
|
+
model.maximize(x);
|
|
396
|
+
model.solve().then(status => console.log("GLPK 求解状态:", status));
|
|
397
|
+
</script>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
> [!NOTE]
|
|
401
|
+
> **浏览器环境中使用 Z3 的配置要求**:
|
|
402
|
+
> 由于 Z3 的 WebAssembly 引擎依托多线程 (`SharedArrayBuffer`),承载页面的 Web 服务器必须在文档响应头中附带以下 Cross-Origin-Isolation 标头:
|
|
403
|
+
> ```http
|
|
404
|
+
> Cross-Origin-Opener-Policy: same-origin
|
|
405
|
+
> Cross-Origin-Embedder-Policy: require-corp
|
|
406
|
+
> ```
|
|
407
|
+
> 此外,任何跨源静态资源(如来自 CDN)都必须返回 `Cross-Origin-Resource-Policy: cross-origin` 响应头。
|
|
408
|
+
|
|
409
|
+
完整独立的 ESM、Script 标签与 CodePen 示例请参阅 `examples/distribution/`。
|
|
410
|
+
|
|
411
|
+
### 工厂内网、离线部署、Worker 与自定义 URL
|
|
332
412
|
|
|
333
|
-
|
|
334
|
-
|
|
413
|
+
将应用构建产物或整个 `dist/browser` 目录部署到本地 HTTP / 内网服务器。所有资源可在本地访问时,即使浏览器缓存为空也不需要外部网络。仅保存 HTML 或第一个 JS 文件是不够的。不保证 `file://` 执行,也不自动提供 PWA 缓存。
|
|
414
|
+
|
|
415
|
+
ESM 根据模块 URL 解析资源;script 构建在加载时保存自身的 URL。CDN Worker 使用 Blob 模块启动,需要服务端的跨域资源许可及相应 CSP。CDN Worker 还需允许 `worker-src blob:`。不支持时可使用 `useWorker: false`。Worker 加载或消息处理失败会拒绝求解 Promise。
|
|
416
|
+
|
|
417
|
+
自定义地址仍可通过 `setHighsWasmUrl(url)` 或 `options.wasmUrl` 指定。显式选择现有 HiGHS CDN:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
|
|
421
|
+
setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
|
|
335
422
|
```
|
|
336
423
|
|
|
424
|
+
初始化失败会返回包含请求地址的错误,不会尝试其他主机。依赖旧的域名根目录 / CDN 自动回退的应用需部署生成资源或显式配置 URL。
|
|
425
|
+
|
|
426
|
+
### 验证发布产物
|
|
427
|
+
|
|
428
|
+
`npm run test:distribution` 安装实际 npm tarball,在没有可选依赖时构建 Vite / webpack 应用,并通过 VM 执行生成的 JS 和真实 WASM。这不等同于浏览器 / CORS 验证。安装 `npx playwright install chromium` 后,运行 `RUN_DISTRIBUTION_BROWSER=1 npm run test:distribution` 以验证 Chromium。CI 使用该模式,验证跨域部署及外部网络请求阻断。
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 🛠️ 自定义求解器开发与架构扩展
|
|
433
|
+
|
|
434
|
+
WaSHI 采用完全解耦的插件式求解器架构。你可以轻松编写并注册自定义求解器适配器(如云端 REST 求解服务、定制 WebAssembly 引擎或商业求解器):
|
|
435
|
+
|
|
436
|
+
- 完整开发指南、接口契约(`Solver`, `LpModelData`, `SolverResult`)与测试实践详见 [Custom Solver Developer Guide](docs/custom-solver-guide.md)。
|
|
437
|
+
|
|
337
438
|
---
|
|
338
439
|
|
|
339
440
|
## 💻 本地脚本与交互式 Web 演示
|
|
@@ -347,6 +448,7 @@ npx tsx examples/node/03-portfolio-qp.ts
|
|
|
347
448
|
npx tsx examples/node/04-smt-security-rules.ts
|
|
348
449
|
npx tsx examples/node/05-unsat-core-diagnosis.ts
|
|
349
450
|
npx tsx examples/node/06-pulp-compatibility.ts
|
|
451
|
+
npx tsx examples/node/08-glpk-solver-plugin.ts
|
|
350
452
|
```
|
|
351
453
|
|
|
352
454
|
启动浏览器端交互式可视化演示 (`examples/web-demo`):
|