@ohtaman/washi 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.ja.md +149 -121
  2. package/README.md +149 -121
  3. package/README.zh-CN.md +150 -122
  4. package/THIRD_PARTY_LICENSES.md +3 -4
  5. package/dist/browser/_washi-node-only-BvRk9kiK.js +0 -0
  6. package/dist/browser/browser-runtime-TLCY0hkc.js +1793 -0
  7. package/dist/browser/highs-worker.js +21 -3
  8. package/dist/browser/highs.wasm +0 -0
  9. package/dist/browser/highs.wasm.LICENSE +1 -2
  10. package/dist/browser/loader-DewlSho8.js +643 -0
  11. package/dist/browser/solvers/glpk.js +433 -286
  12. package/dist/browser/solvers/glpk.min.js +1 -1
  13. package/dist/browser/solvers/z3.js +1008 -981
  14. package/dist/browser/solvers/z3.min.js +2 -2
  15. package/dist/browser/washi.js +2778 -673
  16. package/dist/browser/washi.min.js +3 -4
  17. package/dist/lib/_washi-node-only-BvRk9kiK.js +0 -0
  18. package/dist/lib/highs-browser-runtime.js +2120 -4124
  19. package/dist/lib/highs-node-runtime.js +24 -1
  20. package/dist/lib/highs-runtime.js +2 -0
  21. package/dist/lib/highs-worker.js +21 -3
  22. package/dist/lib/highs.wasm +0 -0
  23. package/dist/lib/highs.wasm.LICENSE +1 -2
  24. package/dist/lib/loader-BZUQNvh4.js +649 -0
  25. package/dist/lib/registry-C20sxTwm.js +964 -0
  26. package/dist/lib/solvers/glpk.js +201 -90
  27. package/dist/lib/solvers/z3.js +399 -259
  28. package/dist/lib/washi.js +2363 -1056
  29. package/dist/types/decomposition/benders.d.ts +32 -0
  30. package/dist/types/decomposition/benders.d.ts.map +1 -0
  31. package/dist/types/decomposition/column-generation.d.ts +34 -0
  32. package/dist/types/decomposition/column-generation.d.ts.map +1 -0
  33. package/dist/types/decomposition/index.d.ts +4 -0
  34. package/dist/types/decomposition/index.d.ts.map +1 -0
  35. package/dist/types/decomposition/types.d.ts +230 -0
  36. package/dist/types/decomposition/types.d.ts.map +1 -0
  37. package/dist/types/index.d.ts +5 -4
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/modeler/big-m.d.ts +75 -0
  40. package/dist/types/modeler/big-m.d.ts.map +1 -0
  41. package/dist/types/modeler/boolean-expr.d.ts +103 -14
  42. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  43. package/dist/types/modeler/constraint.d.ts +47 -9
  44. package/dist/types/modeler/constraint.d.ts.map +1 -1
  45. package/dist/types/modeler/expression.d.ts +123 -28
  46. package/dist/types/modeler/expression.d.ts.map +1 -1
  47. package/dist/types/modeler/index.d.ts +11 -6
  48. package/dist/types/modeler/index.d.ts.map +1 -1
  49. package/dist/types/modeler/linearizer.d.ts +15 -0
  50. package/dist/types/modeler/linearizer.d.ts.map +1 -0
  51. package/dist/types/modeler/lp-writer.d.ts +30 -3
  52. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  53. package/dist/types/modeler/model.d.ts +623 -131
  54. package/dist/types/modeler/model.d.ts.map +1 -1
  55. package/dist/types/modeler/objective.d.ts +59 -0
  56. package/dist/types/modeler/objective.d.ts.map +1 -0
  57. package/dist/types/modeler/parser.d.ts +34 -10
  58. package/dist/types/modeler/parser.d.ts.map +1 -1
  59. package/dist/types/modeler/quad-expr.d.ts +26 -7
  60. package/dist/types/modeler/quad-expr.d.ts.map +1 -1
  61. package/dist/types/modeler/solution-validator.d.ts +93 -0
  62. package/dist/types/modeler/solution-validator.d.ts.map +1 -0
  63. package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
  64. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  65. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -1
  66. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -1
  67. package/dist/types/modeler/solvers/highs/driver.d.ts +232 -0
  68. package/dist/types/modeler/solvers/highs/driver.d.ts.map +1 -0
  69. package/dist/types/modeler/solvers/highs/index.d.ts +1 -0
  70. package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -1
  71. package/dist/types/modeler/solvers/highs/loader.d.ts +38 -13
  72. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  73. package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -1
  74. package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -1
  75. package/dist/types/modeler/solvers/highs/solver.d.ts +12 -5
  76. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  77. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  78. package/dist/types/modeler/solvers/mock.d.ts +14 -5
  79. package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
  80. package/dist/types/modeler/solvers/types.d.ts +73 -11
  81. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  82. package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
  83. package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
  84. package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
  85. package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
  86. package/dist/types/modeler/types.d.ts +86 -1
  87. package/dist/types/modeler/types.d.ts.map +1 -1
  88. package/dist/types/modeler/validation.d.ts +56 -0
  89. package/dist/types/modeler/validation.d.ts.map +1 -0
  90. package/dist/types/modeler/variable.d.ts +48 -14
  91. package/dist/types/modeler/variable.d.ts.map +1 -1
  92. package/docs/examples/diet-sensitivity.md +90 -0
  93. package/docs/examples/ecommerce-coupons.md +106 -0
  94. package/docs/examples/index.md +57 -0
  95. package/docs/examples/knapsack.md +60 -0
  96. package/docs/examples/portfolio-qp.md +83 -0
  97. package/docs/examples/production.md +51 -0
  98. package/docs/examples/shift-scheduling.md +175 -0
  99. package/docs/examples/smt-security.md +74 -0
  100. package/docs/examples/transportation.md +90 -0
  101. package/docs/examples/traveling-salesperson.md +173 -0
  102. package/docs/examples/unsat-core.md +64 -0
  103. package/docs/examples/vehicle-routing.md +206 -0
  104. package/docs/getting-started/ai-agents.md +271 -0
  105. package/docs/getting-started/cross-origin.md +87 -0
  106. package/docs/getting-started/index.md +116 -0
  107. package/docs/getting-started/runtimes.md +227 -0
  108. package/docs/guide/constraints.md +177 -0
  109. package/docs/guide/decomposition.md +399 -0
  110. package/docs/guide/expressions.md +204 -0
  111. package/docs/guide/objectives.md +170 -0
  112. package/docs/guide/unsat-core.md +154 -0
  113. package/docs/guide/variables.md +188 -0
  114. package/docs/ja/getting-started/ai-agents.md +270 -0
  115. package/docs/solvers/custom-solver.md +525 -0
  116. package/docs/solvers/glpk.md +66 -0
  117. package/docs/solvers/highs.md +115 -0
  118. package/docs/solvers/index.md +42 -0
  119. package/docs/solvers/z3.md +78 -0
  120. package/docs/zh/getting-started/ai-agents.md +270 -0
  121. package/llms-full.txt +4065 -0
  122. package/llms.txt +201 -0
  123. package/package.json +32 -16
  124. package/skills/washi/SKILL.md +197 -0
  125. package/dist/browser/browser-runtime-D13gHOT5.js +0 -4189
  126. package/dist/browser/loader-CVt4CloI.js +0 -58
  127. package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
  128. package/dist/lib/constraint-BRuB3ooC.js +0 -90
  129. package/dist/lib/loader-Dti0v9yC.js +0 -50
  130. package/dist/lib/registry-DksqCSjI.js +0 -32
@@ -0,0 +1,115 @@
1
+ # HiGHS Solver Engine
2
+
3
+ **HiGHS** is high-performance mathematical optimization software developed at the University of Edinburgh ([ERGO-Code/HiGHS](https://github.com/ERGO-Code/HiGHS)). It is widely recognized as the state-of-the-art open-source solver for Linear Programming (LP), Mixed-Integer Programming (MIP), and Convex Quadratic Programming (QP).
4
+
5
+ In `@ohtaman/washi`, HiGHS runs entirely in WebAssembly compiled directly from official upstream source ([ERGO-Code/HiGHS](https://github.com/ERGO-Code/HiGHS)) using its native C API (`highs_c_api.h`), providing zero-config execution across Node.js, Web Workers, and browsers without third-party wrapper dependencies.
6
+
7
+ ---
8
+
9
+ ## Key Features in WaSHI
10
+
11
+ - **Built-in & Zero-Config**: Bundled directly with `@ohtaman/washi` with zero external runtime dependencies. No separate peer dependency or C++ compiler required.
12
+ - **Permissive MIT License**: 100% commercially friendly.
13
+ - **Web Worker Multithreading**: Automatically offloads intensive solving to background Web Workers in browser environments to avoid freezing the UI thread.
14
+ - **Warm-Start Support (`initialValue`)**: Supports passing variable primal initial value hints (`initialValue`) to HiGHS `setSolution` for warm starting.
15
+ - **Fast WASM Execution**: Runs the directly compiled 32-bit WebAssembly binary at near-native speed.
16
+
17
+ ---
18
+
19
+ ## Architecture & WebAssembly Runtime
20
+
21
+ WaSHI compiles official HiGHS C++ source directly to WebAssembly (`highs.wasm`) with `-fwasm-exceptions` and single-threaded universal portability (`-DHIGHS_NO_DEFAULT_THREADS=ON`). By interfacing directly with HiGHS's native C API (`highs_c_api.h`), WaSHI achieves zero-copy solution extraction via typed array views on WebAssembly linear memory, rapid upstream release tracking, and a self-contained zero-dependency distribution.
22
+
23
+ ### Callbacks & Solver Interactivity
24
+
25
+ - **Progress Monitoring & Cancellation (Supported)**:
26
+ Through HiGHS's internal logging and interrupt hooks, WaSHI provides real-time progress callbacks (`onProgress`) and cooperative mid-solve cancellation via standard `AbortSignal`.
27
+ - **Dynamic Cuts / Lazy Constraints (Not Supported in WASM Wrapper)**:
28
+ The upstream WebAssembly wrapper intentionally excludes low-level native callback type 8 (`MIP lazy constraints`) due to WASM-to-JS boundary and synchronous memory safety constraints. For problems requiring subtour elimination or Benders decomposition, use an **iterative cutting-plane loop** (solve $\to$ detect violation $\to$ `model.constraint(...)` $\to$ re-solve).
29
+
30
+ ### WebAssembly Asset Lifecycle & On-Demand Builds
31
+
32
+ HiGHS's WebAssembly binary (`public/highs.wasm`) and runtime driver (`runtime.js`) are managed via `highs.config.json` with cryptographic SHA-256 verification:
33
+ - **Zero-Friction Verification**: Build and distribution pipelines run `node scripts/ensure-highs-wasm.mjs`, verifying local assets in milliseconds before bundling.
34
+ - **On-Demand Release Download**: When assets are missing or updated, `ensure-highs-wasm.mjs` automatically downloads verified binaries from official GitHub Release assets.
35
+ - **Compiling from Source**: When updating HiGHS or modifying compiler flags, run `npm run build:highs-wasm` with the Emscripten SDK (`emcc`), CMake, and Ninja installed, and upload the rebuilt assets to the `highs-<version>` GitHub Release tag (`gh release upload highs-v1.15.1 public/highs.wasm src/modeler/solvers/highs/runtime.js --clobber`) so on-demand downloads match `highs.config.json` (or run `.github/workflows/build-wasm.yml` and commit the `highs-wasm-assets` artifact including `highs.config.json`).
36
+
37
+ ---
38
+
39
+ ## Supported Problem Classes
40
+
41
+ ### 1. Linear Programming (LP)
42
+ Solves continuous problems via Primal and Dual Simplex algorithms or Interior Point (IPM) methods. After solving, you can inspect constraint shadow prices (`c.dual`), slacks (`c.slack`), and variable reduced costs (`v.reducedCost`):
43
+
44
+ ```typescript
45
+ import { Model, expr, maximize } from "@ohtaman/washi";
46
+
47
+ const model = new Model({ solver: "highs" });
48
+ const x = model.continuous("x", { lb: 0 });
49
+ const y = model.continuous("y", { lb: 0 });
50
+ model.objective(maximize(expr`10 * ${x} + 6 * ${y}`));
51
+ model.constraint(expr`${x} + ${y} <= 100`, "cap");
52
+ await model.solve();
53
+ ```
54
+
55
+ ### 2. Mixed-Integer Linear Programming (MIP)
56
+ Solves discrete problems using Branch-and-Bound and cutting plane techniques:
57
+
58
+ ```typescript
59
+ const count = model.integer("count", { lb: 0, ub: 50 });
60
+ ```
61
+
62
+ ### 3. Convex Quadratic Programming (QP)
63
+ Solves quadratic objective functions subject to linear constraints:
64
+
65
+ ```typescript
66
+ // Minimize risk/variance uniformly using expr:
67
+ model.objective(minimize(expr`0.5 * ${x}^2 + 0.5 * ${y}^2`));
68
+
69
+ // Or programmatically with .mul():
70
+ // const quadObj = x.mul(x).mul(0.5).add(y.mul(y).mul(0.5));
71
+ // model.objective(minimize(quadObj));
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Solver Options
77
+
78
+ Pass solver options into `model.solve()`:
79
+
80
+ ```typescript
81
+ const controller = new AbortController();
82
+
83
+ await model.solve({
84
+ timeLimit: 60.0, // Maximum solve time in seconds
85
+ mipGap: 0.001, // Terminate when within 0.1% of global optimality (alias: relGap)
86
+ absGap: 1.0, // Absolute MIP optimality gap tolerance
87
+ presolve: "on", // Presolve control ("on" | "off" | "choose")
88
+ outputFlag: true, // Output internal simplex / branch-and-bound logs (output_flag)
89
+ signal: controller.signal,
90
+ onProgress: (ev) => {
91
+ console.log(`Elapsed: ${ev.elapsedTime.toFixed(1)}s, Best: ${ev.objectiveValue}`);
92
+ },
93
+ });
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Worker & WASM Configuration in Browsers
99
+
100
+ By default in browsers, WaSHI spawns a dedicated Web Worker to execute HiGHS in the background so UI rendering stays responsive and `onProgress` / `AbortSignal` work seamlessly.
101
+
102
+ If you need to run synchronously on the main thread (or in environments where Web Workers are prohibited), pass `useWorker: false` to `model.solve()` or instantiate `HighsSolver`:
103
+
104
+ ```typescript
105
+ import { Model, HighsSolver } from "@ohtaman/washi";
106
+
107
+ // Option 1: Per solve call
108
+ await model.solve({ useWorker: false });
109
+
110
+ // Option 2: When instantiating the solver
111
+ const syncModel = new Model({
112
+ solver: new HighsSolver({ useWorker: false }),
113
+ });
114
+ ```
115
+
@@ -0,0 +1,42 @@
1
+ # Solvers Overview
2
+
3
+ WaSHI is designed around a pluggable, decoupled solver architecture. You formulate mathematical models using a unified algebraic API, then solve them with the engine best suited for your problem class.
4
+
5
+ ---
6
+
7
+ ## ⚡ Solvers Matrix & Comparison
8
+
9
+ | Solver | Engine License | Problem Classes | Best For | Status in WaSHI |
10
+ | :--- | :--- | :--- | :--- | :--- |
11
+ | **[HiGHS](https://github.com/ERGO-Code/HiGHS)** | **MIT** | **LP, MIP, Convex QP** | Industrial-scale optimization, operations research, finance | **Built-in default** (zero configuration) |
12
+ | **[Z3](https://github.com/Z3Prover/z3)** (via [`z3-solver`](https://www.npmjs.com/package/z3-solver)) | **MIT** | **SMT, SAT, Logic, MIP** | Propositional logic, conflict diagnosis (`unsatCore`), rule engines | Optional plugin (`@ohtaman/washi/solvers/z3`) |
13
+ | **[GLPK](https://www.gnu.org/software/glpk/)** (via [`glpk.js`](https://www.npmjs.com/package/glpk.js)) | **GPL-3.0** | **LP, MIP** | Academic benchmarks, legacy GLPK compatibility | Optional plugin (`@ohtaman/washi/solvers/glpk`) |
14
+ | **Mock** | **MIT** | *Any* | Ultra-fast unit testing in CI without loading WebAssembly | Built-in |
15
+
16
+ ---
17
+
18
+ ## Selecting a Solver
19
+
20
+ Specify the solver by name when creating your `Model`:
21
+
22
+ ```typescript
23
+ // 1. Built-in HiGHS (default)
24
+ const m1 = new Model({ solver: "highs" });
25
+
26
+ // 2. Z3 SMT solver
27
+ import "@ohtaman/washi/solvers/z3";
28
+ const m2 = new Model({ solver: "z3" });
29
+
30
+ // 3. GLPK solver
31
+ import "@ohtaman/washi/solvers/glpk";
32
+ const m3 = new Model({ solver: "glpk" });
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Solver Guides
38
+
39
+ - [HiGHS Guide](./highs.md): Web Worker multithreading, MIP tolerances, and QP solving.
40
+ - [Z3 Guide](./z3.md): SMT assertions, logic modeling, and Unsat Core analysis.
41
+ - [GLPK Guide](./glpk.md): GLPK adapter, GPL-3.0 licensing considerations, and browser usage.
42
+ - [Custom Solvers](./custom-solver.md): Step-by-step developer tutorial for integrating proprietary or cloud REST solvers.
@@ -0,0 +1,78 @@
1
+ # Z3 SMT Solver Engine
2
+
3
+ **Z3** is a theorem prover and Satisfiability Modulo Theories (SMT) solver developed by Microsoft Research. In WaSHI, Z3 provides advanced propositional logic reasoning and conflict diagnosis.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ Z3 is provided as an optional peer dependency:
10
+
11
+ ```bash
12
+ npm install z3-solver
13
+ ```
14
+
15
+ ---
16
+
17
+ ## When to Use Z3?
18
+
19
+ 1. **Unsat Core Conflict Diagnosis**:
20
+ When your MIP model is infeasible and you need to isolate which constraints are contradictory.
21
+ 2. **Boolean / Logical Formulations**:
22
+ When solving problems containing complex logical relations (implications, XOR, nested Boolean expressions).
23
+ 3. **Exact Mathematical Guarantees**:
24
+ Z3 can perform exact rational arithmetic without floating-point inaccuracies.
25
+
26
+ ---
27
+
28
+ ## Basic Usage
29
+
30
+ ```typescript
31
+ import { Model, expr } from "@ohtaman/washi";
32
+ import "@ohtaman/washi/solvers/z3";
33
+
34
+ const model = new Model({ solver: "z3" });
35
+
36
+ const x = model.integer("x", { lb: 0, ub: 10 });
37
+ const y = model.integer("y", { lb: 0, ub: 10 });
38
+
39
+ model.constraint(expr`${x} + ${y} == 15`, "sum_15");
40
+ model.constraint(expr`${x} >= 8`, "x_at_least_8");
41
+
42
+ await model.solve();
43
+ console.log(`x = ${x.value}, y = ${y.value}`);
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Unsat Core Extraction (`model.check()`)
49
+
50
+ Use `await model.check()` with Z3 to verify satisfiability and extract the conflicting subset of named constraints (`conflicts` / `unsatCore`) when a model is unsatisfiable:
51
+
52
+ ```typescript
53
+ const model = new Model({ solver: "z3" });
54
+ const a = model.continuous("a");
55
+
56
+ model.constraint(expr`${a} >= 10`, "req_A");
57
+ model.constraint(expr`${a} <= 5`, "req_B");
58
+
59
+ const result = await model.check();
60
+
61
+ if (result.status === "Unsat") {
62
+ console.log("Conflicting constraints:", result.conflicts); // or model.conflicts / model.unsatCore
63
+ // ["req_A", "req_B"]
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Browser Security Note
70
+
71
+ In browser environments, the Z3 WASM engine relies on pthreads and `SharedArrayBuffer`, which require cross-origin isolation headers (`COOP` / `COEP`). See the [Cross-Origin Isolation Guide](../getting-started/cross-origin.md) for detailed setup.
72
+
73
+ ## Runtime Failures and Thread Cleanup
74
+
75
+ If a Z3 worker encounters a fatal runtime error, pending operations on that instance reject with an error and the broken runtime is invalidated; subsequent `solve()` or `check()` calls automatically initialize a fresh Z3 runtime rather than misclassifying failures as `Sat` or `Unsat`.
76
+
77
+ To explicitly terminate Z3 pthread workers when tearing down a browser view or worker pool, call `killZ3Threads()` from `@ohtaman/washi/solvers/z3`.
78
+
@@ -0,0 +1,270 @@
1
+ # AI 编程助手快速参考指南
2
+
3
+ > **范围说明**:本文档是面向使用 `@ohtaman/washi` 构建数理优化或可满足性(SAT/SMT)模型的 AI 编程助手与开发者的**使用端建模快速参考**。
4
+ > - **可移植 Agent Skill (`SKILL.md`)**:如需涵盖端到端建模、`toLpString()` 导出检查、`validateSolution()` 解验证以及 Z3 `unsatCore` 冲突诊断的完整工作流技能(兼容 Claude Code、Antigravity、Cursor、Codex 等支持 `SKILL.md` 的代理),请使用 [`skills/washi/SKILL.md`](https://github.com/ohtaman/washi/blob/main/skills/washi/SKILL.md)(同时随 npm 包分发于 `node_modules/@ohtaman/washi/skills/washi/SKILL.md`):
5
+ > ```bash
6
+ > # 通过 skills CLI (skills.sh / vercel-labs/skills) 直接安装
7
+ > npx skills add ohtaman/washi
8
+ >
9
+ > # 或将随包附带的技能复制到项目的代理技能目录中
10
+ > mkdir -p .agents/skills/washi
11
+ > cp node_modules/@ohtaman/washi/skills/washi/SKILL.md .agents/skills/washi/SKILL.md
12
+ > ```
13
+ > - **仓库审查规范**:关于仓库贡献规范与 Pull Request 代码审查规则,请参阅仓库根目录下的 [`AGENTS.md`](https://github.com/ohtaman/washi/blob/main/AGENTS.md)。
14
+
15
+ ---
16
+
17
+ ## 1. 包导入与部署规范
18
+
19
+ ### Node.js / 构建工具 (Vite, Webpack, Next.js, Bun)
20
+
21
+ ```ts
22
+ import {
23
+ Model,
24
+ expr,
25
+ sum,
26
+ dot,
27
+ maximize,
28
+ minimize,
29
+ and,
30
+ or,
31
+ not,
32
+ implies,
33
+ xor,
34
+ setHighsWasmUrl,
35
+ } from "@ohtaman/washi";
36
+
37
+ // 可选求解器插件(需先安装对应的 peer dependencies):
38
+ // npm install z3-solver
39
+ import "@ohtaman/washi/solvers/z3";
40
+ // npm install glpk.js
41
+ import "@ohtaman/washi/solvers/glpk";
42
+ ```
43
+
44
+ ### 浏览器原生 ESM 与普通 `<script>` 标签
45
+
46
+ - **浏览器 ESM**:
47
+ - 核心包(内置 HiGHS): `import { Model, expr, maximize } from "./washi/washi.js";`
48
+ - GLPK 插件: `import "./washi/solvers/glpk.js";`
49
+ - Z3 插件: `import "./washi/solvers/z3.js";`
50
+ - **普通 `<script>` / CodePen (IIFE)**:
51
+ - 核心包: `<script src="./washi/washi.min.js"></script>`(挂载全局变量 `Washi`)
52
+ - GLPK 插件: `<script src="./washi/solvers/glpk.min.js"></script>`(注册 `"glpk"`,挂载 `WashiGLPK`)
53
+ - Z3 插件: `<script src="./washi/solvers/z3.min.js"></script>`(注册 `"z3"`,挂载 `WashiZ3`)
54
+
55
+ ### WebAssembly 资源与运行时规则
56
+
57
+ 1. **默认使用随包内置的 WASM(无隐式 CDN 回退)**:
58
+ - 在 Node.js 和现代构建工具中,WebAssembly 资源会自动相对于模块路径解析。
59
+ - 在浏览器部署中,请将完整的 `dist/browser/` 目录(包含 JS、Worker 和 `.wasm` 文件)一起部署。如果本地 WASM URL 缺失或返回了 HTML SPA 回退页面,初始化会立即失败并报告所尝试的 URL——**绝不会**静默切换到公共 CDN。
60
+ - 在浏览器运行时中,若需从自定义位置加载 `highs.wasm`,请调用 `setHighsWasmUrl("https://.../highs.wasm")` 或传入 `await m.solve({ wasmUrl: "https://.../highs.wasm" })`(在 Node.js 中会被忽略,Node.js 始终加载本地安装的 `highs` 包运行时)。
61
+ 2. **浏览器中运行 Z3 需要安全上下文、跨源隔离(Cross-Origin Isolation)与 CORS**:
62
+ - Z3 WebAssembly 依赖 `SharedArrayBuffer` 多线程。运行 Z3 的浏览器页面必须处于安全上下文(`window.isSecureContext === true`,即 HTTPS 或 `localhost`)且开启跨源隔离(`window.crossOriginIsolated === true`),服务端需配置以下响应头:
63
+ - `Cross-Origin-Opener-Policy: same-origin`(主文档响应)
64
+ - `Cross-Origin-Embedder-Policy: require-corp`(主文档响应)
65
+ - `Cross-Origin-Resource-Policy: cross-origin` 与 `Access-Control-Allow-Origin`(跨域脚本 / WASM 资源响应)
66
+ - 导入 `@ohtaman/washi` 绝不会自动注入 Service Worker。当浏览器缺少安全上下文或跨源隔离时,Z3 会立即以明确说明原因的错误 reject。
67
+ 3. **Web Worker 与取消控制 (`AbortSignal`)**:
68
+ - 求解前取消(执行开始前 `signal.aborted` 已为真)在所有求解器中均受支持。
69
+ - 在支持 `Worker` 的浏览器环境中,HiGHS 默认在专用 Web Worker 中运行(`useWorker: true`)。向 `m.solve({ signal })` 传入 `{ signal }` 可以抢占式终止正在运行的 Worker 求解(或取消排队中的 Worker 请求)。
70
+ - 在主线程运行 HiGHS 时(`useWorker: false` 或在 Node.js 中),支持在求解器中断检查点触发的 `onProgress` 回调内同步调用 `abort()` 实现协作式中途取消;来自外部的任意异步取消在同步 WASM 线程到达检查点或执行完成前无法抢占阻塞线程。无中断钩子的单线程 WASM 求解器(GLPK、Z3)会在执行开始前检查 `signal`。
71
+
72
+ ---
73
+
74
+ ## 2. 标准建模流程
75
+
76
+ 构建并求解一个模型包含四个明确步骤:
77
+
78
+ ### 步骤 1:创建模型与决策变量
79
+
80
+ ```ts
81
+ const m = new Model("ExampleModel");
82
+
83
+ // 标量变量
84
+ const x = m.continuous("x", { lb: 0, ub: 100 });
85
+ const n = m.integer("n", { lb: 0, ub: 10 });
86
+ const b = m.binary("b");
87
+ const flag = m.bool("flag"); // 命题布尔变量(用于 Z3 / 逻辑约束)
88
+
89
+ // 多维索引变量容器:
90
+ // - 数字维度 -> Array
91
+ // - 键数组维度 -> Record
92
+ const vec = m.continuous(4, { lb: 0, prefix: "v" }); // Var[] (长度 4)
93
+ const grid = m.binary(2, 3, { prefix: "g" }); // Var[][] (2x3)
94
+ const byItem = m.integer(["desk", "chair"] as const, { lb: 0 }); // Record<"desk" | "chair", Var>
95
+ const mixed = m.binary(["A", "B"] as const, 3); // Record<"A" | "B", Var[]>
96
+ ```
97
+
98
+ - **可选的原问题初始值提示 (`initialValue?: number`)**:
99
+ - 数值变量构造函数(`m.continuous`、`m.integer`、`m.binary` 与 `new Var`)以及可变的 `v.initialValue` 属性支持传入可选的有限原问题初始值提示(以 `1e-6` 容差校验上下界 `[lb, ub]` 及整数/二元域),并与求解结果 `v.value` 保持独立。
100
+ - 初始值可以是完整的或部分的(例如在重建 MIP 模型时按固定变量名传递)。提供 `initialValue` 仅作为求解提示——**不会**固定变量、添加硬约束,也不保证被求解器采纳或加速求解。
101
+ - 受 **HiGHS**(`supportsWarmStart: true`)支持;不支持热启动的求解器(**GLPK**、**Z3**)在任何变量设置了 `initialValue !== undefined` 时会抛出错误。
102
+
103
+ ### 步骤 2:构建表达式 (`expr`, `sum`, `dot`)
104
+
105
+ - **统一 `expr` 模板标签**:
106
+ - 线性表达式(`LinExpr`): `` expr`30 * ${x} + 50 * ${n} - 10` ``
107
+ - 二次表达式(`QuadExpr`): `` expr`${x}^2 + 2 * ${x} * ${n} + ${n}^2` ``(支持凸 QP 目标函数中的变量相乘与平方项)。
108
+ - 线性约束(`Constraint`): `` expr`${x} + 2 * ${n} <= 40` ``(支持 `<=`、`>=`、`==` 和 `=`)。
109
+ - **聚合函数**:
110
+ - `sum(iterable, mapper?)` 对 `Var` / `LinExpr` / `number` 的 `Iterable`(例如 `Var[]`、`grid.flat()` 或 `Object.values(record)`)求和。
111
+ - `dot(a, b)` 计算匹配的一维数组、二维数组(`number[][]` / `Var[][]`)、普通字典(`Record<string, ...>`)或混合 Array/Record 容器的内积,操作数顺序不限。
112
+ - **链式方法**:
113
+ - 算术运算(在 `LinExpr` 和 `QuadExpr` 上支持;`Var` 支持 `.add()`、`.sub()`、`.mul()`、`.neg()`): `.add(other)`、`.sub(other)`、`.mul(other)`、`.div(scalar)`、`.neg()`
114
+ - 比较运算(在 `Var` 和 `LinExpr` 上支持): `.le(rhs, name?)`、`.ge(rhs, name?)`、`.eq(rhs, name?)`
115
+ - 布尔逻辑(面向 Z3 的 `BoolVar`、`BoolExpr` 和 `Constraint`): 独立函数 `and(...)`、`or(...)`、`not(...)`、`implies(a, b)`、`xor(a, b)` 以及链式方法 `.and(...)`、`.or(...)`、`.not()`、`.implies(...)`、`.xor(...)`
116
+
117
+ ### 步骤 3:向模型注册目标函数与约束条件
118
+
119
+ 仅构造 `expr` 或 `.le()` 对象**不会**将其挂载到模型上,必须传入 `m.constraint(...)` 或 `m.objective(...)` 完成注册:
120
+
121
+ ```ts
122
+ // 使用 maximize() 或 minimize() 设置目标函数
123
+ m.objective(maximize(expr`30 * ${x} + 50 * ${n}`));
124
+
125
+ // 将约束注册到模型
126
+ m.constraint(expr`${x} + 2 * ${n} <= 40`, "resource_limit");
127
+
128
+ // 指示约束 (MIP Big-M 转化: b === 1 => x <= 50)
129
+ m.indicator(b, expr`${x} <= 50`, { activeValue: 1, name: "cap_when_b" });
130
+ ```
131
+
132
+ ### 步骤 4:执行求解 (`solve` 或 `check`) 与校验解 (`validateSolution`)
133
+
134
+ - **数理优化 (`await m.solve(options?)`)**:
135
+ - 返回 `SolverStatus`(`"Optimal"`、`"Feasible"`、`"Infeasible"`、`"Unbounded"`、`"TimeLimit"`、`"IterationLimit"`、`"Error"`、`"Unknown"`、`"NotSolved"`、`"Sat"` 或 `"Unsat"`)。内置优化求解器返回优化状态(`"Optimal"`、`"Feasible"`、`"Infeasible"`、`"Unbounded"`、`"TimeLimit"`、`"IterationLimit"`、`"Error"`、`"Unknown"`、`"NotSolved"`),其中因时间、迭代或解数量上限(或中断)停止但已找到合法可行解(Incumbent)的运行会返回 `"Feasible"`;`"Sat"` 与 `"Unsat"` 包含在 `SolverStatus` 中以供可满足性/自定义求解器适配器(如 `MockSolver`)使用。
136
+ - 通过 `x.value`、`expr`...`.value`、`m.getValues(x)`、`m.getValues(container)`、`m.objectiveValue`、`constr.dual`、`constr.slack` 和 `x.reducedCost` 读取结果。
137
+ - 求解后调用 `m.validateSolution()` 可独立校验变量有限性、变量上下界(`lb`/`ub`)、整数/二元/布尔域、线性约束残差以及逻辑约束(`SolutionValidationReport`:`{ valid, complete, maxViolation, violations, ... }`)。注意:`validateSolution()` 校验变量与约束的原问题可行性,不会重新计算或比对 `m.objectiveValue`。
138
+ - **可满足性检查与冲突诊断 (`await m.check(options?)`)**:
139
+ - 返回 `CheckResult`(`{ status: "Sat" | "Unsat" | "Unknown", values: Record<string, number | boolean>, conflicts?: readonly string[], unsatCore?: readonly string[], runTime?: number }`),其中在模型证明为 `"Unsat"` 时填充 `conflicts`。
140
+ - 对于包含线性算术约束或逻辑约束(`BoolVar`、`and`、`or`、`implies`、`xor`)的模型,`m.check()` 均可在任意求解器(包括默认的 HiGHS 与 GLPK,通过单次求解弹性松弛模型,并将逻辑约束自动线性化为 0-1 MIP)上运行,并在不可行时将需要松弛的冲突约束诊断为 `conflicts`(为保证语义严谨,`unsatCore: undefined`)。
141
+ - 精确的 SMT 不满足核提取(`result.unsatCore` 与 `result.conflicts`)在选用 SMT 求解器(`{ solver: "z3" }`)时提供。由于 Z3 在跟踪已注册约束的同时直接断言变量边界(`lb`/`ub`)和二元 `{0, 1}` 域,`unsatCore` 报告的是*结合模型固定变量边界/域时*发生冲突的已注册约束名称子集(不保证最小性)。
142
+ - **异步 `solve` / `check` 期间禁止并发修改模型**:
143
+ - 在 `await m.solve()` 或 `await m.check()` 挂起期间修改变量、约束或目标函数会使当前执行失效,并使过期的 Promise 被 reject。
144
+
145
+ ---
146
+
147
+ ## 3. 可直接运行的示例
148
+
149
+ ### 示例 A:使用多维容器与解校验的混合整数线性规划 (MIP)
150
+
151
+ ```ts
152
+ import { Model, expr, dot, sum, maximize } from "@ohtaman/washi";
153
+
154
+ export async function runMipExample() {
155
+ const m = new Model("ProductionMIP");
156
+ const products = ["desk", "chair"] as const;
157
+ const profit = { desk: 30, chair: 50 };
158
+ const woodPerUnit = { desk: 1, chair: 2 };
159
+ const laborPerUnit = { desk: 2, chair: 1 };
160
+
161
+ const qty = m.integer(products, { lb: 0, ub: 100 });
162
+ const setup = m.binary("setup");
163
+
164
+ m.objective(maximize(expr`${dot(profit, qty)} - 20 * ${setup}`));
165
+ m.constraint(expr`${dot(woodPerUnit, qty)} <= 40`, "wood_cap");
166
+ m.constraint(expr`${dot(laborPerUnit, qty)} <= 50`, "labor_cap");
167
+ m.constraint(expr`${sum(Object.values(qty))} <= 30 * ${setup}`, "require_setup");
168
+
169
+ const status = await m.solve();
170
+ const report = m.validateSolution();
171
+ const values = m.getValues(qty);
172
+
173
+ return {
174
+ status,
175
+ objectiveValue: m.objectiveValue,
176
+ values,
177
+ setup: setup.value,
178
+ valid: report.valid,
179
+ maxViolation: report.maxViolation,
180
+ };
181
+ }
182
+ ```
183
+
184
+ ### 示例 B:凸二次规划 (QP)
185
+
186
+ 二次目标函数支持在 `expr` 中直接书写变量乘积(`${x} * ${y}`)与平方项(`${x}^2`):
187
+
188
+ ```ts
189
+ import { Model, expr, minimize } from "@ohtaman/washi";
190
+
191
+ export async function runQpExample() {
192
+ const m = new Model("PortfolioQP");
193
+ const x = m.continuous("x", { lb: 0 });
194
+ const y = m.continuous("y", { lb: 0 });
195
+
196
+ // 在 x + y >= 4 约束下最小化 x^2 + y^2
197
+ m.objective(minimize(expr`${x}^2 + ${y}^2`));
198
+ m.constraint(expr`${x} + ${y} >= 4`, "budget");
199
+
200
+ const status = await m.solve();
201
+ const report = m.validateSolution();
202
+
203
+ return {
204
+ status,
205
+ objectiveValue: m.objectiveValue,
206
+ x: x.value,
207
+ y: y.value,
208
+ valid: report.valid,
209
+ };
210
+ }
211
+ ```
212
+
213
+ ### 示例 C:Z3 可满足性检查与 Unsat Core 冲突诊断
214
+
215
+ ```ts
216
+ import { Model, expr, implies } from "@ohtaman/washi";
217
+ import "@ohtaman/washi/solvers/z3";
218
+
219
+ export async function runZ3Example() {
220
+ const m = new Model("PolicyCheck", { solver: "z3" });
221
+ const vip = m.bool("vip");
222
+ const discount = m.integer("discount", { lb: 0, ub: 50 });
223
+
224
+ m.constraint(vip, "customer_is_vip");
225
+ m.constraint(implies(vip, expr`${discount} >= 20`), "vip_min_discount");
226
+ m.constraint(expr`${discount} <= 10`, "campaign_cap");
227
+
228
+ const unsatResult = await m.check();
229
+ return {
230
+ status: unsatResult.status,
231
+ conflicts: unsatResult.conflicts,
232
+ };
233
+ }
234
+ ```
235
+
236
+ ---
237
+
238
+ ## 4. 反面示例与常见陷阱
239
+
240
+ 请避免以下无效写法和已废弃的旧别名:
241
+
242
+ ```ts
243
+ import { Model, expr, maximize } from "@ohtaman/washi";
244
+
245
+ export function verifyNegativePatterns() {
246
+ const m = new Model("NegativeExamples");
247
+ const x = m.continuous("x", { lb: 0 });
248
+ const y = m.continuous("y", { lb: 0 });
249
+
250
+ // 1. 切勿将 Constraint 传给 m.objective() 或算术运算方法:
251
+ // 错误: m.objective(maximize(expr`${x} <= 10` as any)); -> 抛出 Error
252
+ // 错误: expr`${x} + 1`.add(expr`${y} <= 5` as any); -> 抛出 Error
253
+
254
+ // 2. 切勿将不含比较运算符 (<=, >=, ==) 的算术表达式传给 m.constraint():
255
+ // 错误: m.constraint(expr`${x} + ${y}` as any); -> 抛出 Error
256
+
257
+ // 3. 切勿在线性约束中包含二次项:
258
+ // 错误: expr`${x}^2 <= 10`; -> 抛出 Error ("Quadratic constraints are not supported")
259
+
260
+ // 4. 切勿使用已移除的旧别名(已在 #59 / #81 中移除):
261
+ // - 使用 `expr` 代替已废弃的 `c` 模板标签
262
+ // - 使用 `m.constraint(...)` 代替 `m.constrain(...)` 或 `m.add(...)`
263
+ // - 使用 `m.objective(maximize(...))` / `m.objective(minimize(...))` 代替 `m.maximize(...)` / `m.minimize(...)` / `m.setObjective(...)`
264
+ // - 使用 `await m.solve()` 代替 `await m.optimize()`
265
+ // - 使用 `.le()`, `.ge()`, `.eq()` 代替 `.lessThanOrEqual()`, `.greaterThanOrEqual()`, `.equalTo()`
266
+ // - 使用 `.value` 或 `m.getValues(...)` 代替 `expr.getValue()`
267
+ // - 使用 `m.variables` / `m.constraints` 代替 `m.vars` / `m.constrs`
268
+ return { m, x, y, maximize };
269
+ }
270
+ ```