@ohtaman/washi 0.5.0 → 0.7.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 (107) hide show
  1. package/README.ja.md +103 -386
  2. package/README.md +104 -387
  3. package/README.zh-CN.md +103 -386
  4. package/THIRD_PARTY_LICENSES.md +5 -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 +1 -1
  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 +3 -3
  12. package/dist/browser/solvers/glpk.min.js +1 -1
  13. package/dist/browser/solvers/z3.js +7 -1
  14. package/dist/browser/solvers/z3.min.js +1 -1
  15. package/dist/browser/washi.js +1708 -540
  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 +1 -1
  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-BEfvpZW3.js → registry-D7TZ3Wh_.js} +142 -59
  26. package/dist/lib/solvers/glpk.js +4 -4
  27. package/dist/lib/solvers/z3.js +1 -1
  28. package/dist/lib/washi.js +1576 -465
  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 +4 -3
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  40. package/dist/types/modeler/constraint.d.ts +11 -0
  41. package/dist/types/modeler/constraint.d.ts.map +1 -1
  42. package/dist/types/modeler/expression.d.ts +57 -26
  43. package/dist/types/modeler/expression.d.ts.map +1 -1
  44. package/dist/types/modeler/index.d.ts +4 -3
  45. package/dist/types/modeler/index.d.ts.map +1 -1
  46. package/dist/types/modeler/linearizer.d.ts +15 -0
  47. package/dist/types/modeler/linearizer.d.ts.map +1 -0
  48. package/dist/types/modeler/lp-writer.d.ts +3 -1
  49. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  50. package/dist/types/modeler/model.d.ts +159 -8
  51. package/dist/types/modeler/model.d.ts.map +1 -1
  52. package/dist/types/modeler/parser.d.ts +25 -5
  53. package/dist/types/modeler/parser.d.ts.map +1 -1
  54. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  55. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -1
  56. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -1
  57. package/dist/types/modeler/solvers/highs/driver.d.ts +232 -0
  58. package/dist/types/modeler/solvers/highs/driver.d.ts.map +1 -0
  59. package/dist/types/modeler/solvers/highs/index.d.ts +1 -0
  60. package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -1
  61. package/dist/types/modeler/solvers/highs/loader.d.ts +4 -4
  62. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  63. package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -1
  64. package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -1
  65. package/dist/types/modeler/solvers/highs/solver.d.ts +1 -1
  66. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  67. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  68. package/dist/types/modeler/solvers/types.d.ts +4 -0
  69. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  70. package/dist/types/modeler/types.d.ts +27 -0
  71. package/dist/types/modeler/types.d.ts.map +1 -1
  72. package/docs/examples/diet-sensitivity.md +15 -14
  73. package/docs/examples/ecommerce-coupons.md +15 -22
  74. package/docs/examples/index.md +18 -12
  75. package/docs/examples/knapsack.md +8 -7
  76. package/docs/examples/portfolio-qp.md +11 -4
  77. package/docs/examples/production.md +4 -4
  78. package/docs/examples/shift-scheduling.md +20 -7
  79. package/docs/examples/smt-security.md +16 -4
  80. package/docs/examples/transportation.md +46 -19
  81. package/docs/examples/traveling-salesperson.md +157 -0
  82. package/docs/examples/unsat-core.md +15 -5
  83. package/docs/examples/vehicle-routing.md +208 -0
  84. package/docs/getting-started/ai-agents.md +33 -18
  85. package/docs/getting-started/cross-origin.md +56 -28
  86. package/docs/getting-started/index.md +40 -69
  87. package/docs/getting-started/runtimes.md +66 -61
  88. package/docs/guide/constraints.md +47 -41
  89. package/docs/guide/decomposition.md +267 -0
  90. package/docs/guide/expressions.md +90 -39
  91. package/docs/guide/formulation-tips.md +212 -0
  92. package/docs/guide/objectives.md +7 -4
  93. package/docs/guide/unsat-core.md +28 -9
  94. package/docs/guide/variables.md +33 -38
  95. package/docs/ja/getting-started/ai-agents.md +32 -18
  96. package/docs/solvers/custom-solver.md +1 -1
  97. package/docs/solvers/highs.md +48 -5
  98. package/docs/solvers/index.md +4 -4
  99. package/docs/solvers/z3.md +3 -0
  100. package/docs/zh/getting-started/ai-agents.md +32 -18
  101. package/llms-full.txt +1536 -478
  102. package/llms.txt +28 -22
  103. package/package.json +19 -16
  104. package/skills/washi/SKILL.md +21 -17
  105. package/dist/browser/browser-runtime-D13gHOT5.js +0 -4189
  106. package/dist/browser/loader-CtaDHef_.js +0 -273
  107. package/dist/lib/loader-DmNC_UNk.js +0 -279
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="./assets/washi-header-dark.png">
4
4
  <source media="(prefers-color-scheme: light)" srcset="./assets/washi-header-light.png">
5
- <img alt="WaSHI - Algebraic Modeler for TypeScript &amp; JavaScript" src="./assets/washi-header-light.png" width="420" />
5
+ <img alt="WaSHI - Mathematical Optimization Modeler in TypeScript" src="./assets/washi-header-light.png" width="420" />
6
6
  </picture>
7
- <p><strong>Algebraic Modeling for TypeScript &amp; JavaScript</strong></p>
7
+ <p><strong>Mathematical Optimization Modeler in TypeScript</strong></p>
8
8
  <p>
9
- <em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Quadratic Programming (QP) • SMT &amp; Conflict Diagnosis</em>
9
+ <em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Convex Quadratic Programming (QP) • SMT &amp; Conflict Diagnosis</em>
10
10
  </p>
11
11
  <p>
12
12
  <a href="./README.md">English</a> •
@@ -16,19 +16,45 @@
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>
20
- <a href="https://ohtaman.github.io/washi/demo/"><img src="https://img.shields.io/badge/demo-playground-brightgreen" alt="live demo" /></a>
19
+ <a href="https://washi.ohtaman.dev/"><img src="https://img.shields.io/badge/docs-online-blue" alt="docs" /></a>
20
+ <a href="https://washi.ohtaman.dev/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>
23
23
  </div>
24
24
 
25
25
  ---
26
26
 
27
- **WaSHI** (WebAssembly-backed, Solver-independent, High-level modeling Interface) is an algebraic modeler for TypeScript and JavaScript, inspired by Python's **PuLP** and Julia's **JuMP**. Define variables, objectives, and arithmetic or logical constraints in application code. WaSHI constructs the model, translates it for a solver adapter, and maps results back to your variables. **HiGHS**, **Z3**, and **GLPK** perform the optimization or satisfiability checks.
27
+ **WaSHI** (WebAssembly-backed, Solver-independent, High-level modeling Interface) is an algebraic modeler for mathematical optimization and constraint programming in TypeScript and JavaScript, inspired by Python's **PuLP**. Define variables, objectives, and constraints in application code, and solve them locally in Node.js or the browser using **HiGHS** (default), **Z3**, or **GLPK**.
28
28
 
29
- HiGHS is the default solver; Z3 and GLPK are optional plugins. The package provides Node.js and browser distributions, including WebAssembly assets. See the [browser integration guide](#-browser--web-worker-usage) for deployment requirements.
29
+ ## ✨ Key Features
30
30
 
31
- [Documentation](https://ohtaman.github.io/washi/) · [Try the browser demo](https://ohtaman.github.io/washi/demo/) · [AI Agent Quick-Reference](docs/getting-started/ai-agents.md) · [Agent Skill (`SKILL.md`)](skills/washi/SKILL.md) · [Browse example source code](https://github.com/ohtaman/washi/blob/main/examples/README.md) · [ESM / script / CodePen examples](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)
31
+ - **Natural Algebraic Syntax**: Write expressions and constraints directly with `` expr`30 * ${x} + 50 * ${y} <= 40` ``, `sum`, and `dot`.
32
+ - **Runs in Node.js & Browsers**: Powered by WebAssembly—no Python service or native C/C++ build required.
33
+ - **Solver-Independent**: Use built-in **HiGHS**, optional **Z3** and **GLPK** plugins, or custom solver adapters.
34
+ - **Satisfiability & Conflict Diagnosis**: Check logical/arithmetic feasibility and pinpoint contradictory constraints when a model is infeasible.
35
+ - **Decomposition Methods**: Built-in support for Column Generation and Benders Decomposition on large-scale problems.
36
+
37
+ ## 📚 Documentation & Links
38
+
39
+ - **[Documentation](https://washi.ohtaman.dev/)**: Modeling guide, solver references, and deployment guides
40
+ - **[Interactive Showcase](https://washi.ohtaman.dev/showcase/) & [Browser Playground](https://washi.ohtaman.dev/demo/)**: Try live optimization apps and edit models in the browser
41
+ - **[Examples](https://washi.ohtaman.dev/examples/)**: Problem recipes and runnable Node.js / browser scripts ([`examples/`](examples/README.md))
42
+ - **[AI Agent Guide](docs/getting-started/ai-agents.md) · [Agent Skill (`SKILL.md`)](skills/washi/SKILL.md) · [`llms.txt`](https://washi.ohtaman.dev/llms.txt) / [`llms-full.txt`](https://washi.ohtaman.dev/llms-full.txt)**: Compact reference, workflow skill, and machine-readable docs for AI coding agents
43
+
44
+ ---
45
+
46
+ ## 📦 Installation & Quick Start
47
+
48
+ ```bash
49
+ npm install @ohtaman/washi
50
+
51
+ # Optional solver plugins
52
+ npm install z3-solver # For SMT logic & unsat core diagnosis
53
+ npm install glpk.js # For GLPK solver (GPL-3.0)
54
+
55
+ # Optional skill for AI coding agents (Claude Code, Cursor, Codex, etc.)
56
+ npx skills add ohtaman/washi
57
+ ```
32
58
 
33
59
  ```ts
34
60
  import { Model, expr, maximize } from "@ohtaman/washi";
@@ -47,446 +73,137 @@ console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`
47
73
 
48
74
  ---
49
75
 
50
- ## 🎯 What WaSHI provides
51
-
52
- 1. **Algebraic model construction**: Formulate optimization models using domain-specific variable constructors (`m.binary`, `m.integer`, `m.continuous`), unified tagged template literals (`expr`), vector aggregations (`dot`, `sum`), and first-class constraints (`m.constraint`). Because JavaScript lacks operator overloading, `` expr`...` `` parses infix math (`+`, `-`, `*`, `^2`, `<=`, `>=`, `==`) while binding interpolated `${...}` values directly as typed `Var` or expression references—similar to parameterized SQL templates—preserving static type checking, IDE refactoring, and indexed variable lookups (`${flow[i][j]}`).
53
- 2. **Solver adapters**: Use a common modeling API with HiGHS, Z3, or GLPK, or [register your own adapter](https://github.com/ohtaman/washi/blob/main/docs/solvers/custom-solver.md). Choose an engine that supports the model's problem class; the adapters do not make every solver support every feature.
54
- 3. **Local execution and WebAssembly distribution**: With the local solver adapters, solving runs in the application without a separate Python service or native C/C++ build—removing backend provisioning as a bottleneck when shipping interactive tools or combining mathematical optimization with in-browser LLMs (e.g., WebGPU / WebLLM) on the client. Browser deployments must include the generated JS, Worker, and WASM assets. Offline use also requires those resources to be available locally or cached. Z3 has additional browser isolation requirements.
55
- 4. **Named constraints and SMT results**: With Z3, explicitly check satisfiability and inspect conflicting constraints (`conflicts` / `unsatCore`) to investigate infeasibilities. A returned core is a conflicting subset, not necessarily the smallest one.
76
+ ## ⚙️ Solvers
56
77
 
57
- Model construction and translation are WaSHI's responsibility. Supported problem classes, numerical behavior, and solve time depend on the selected solver, its settings, and the problem. Modeling overhead and solver execution time should be measured separately; a result for one model is not a general performance guarantee.
58
-
59
- ---
60
-
61
- ## ⚙️ Solver adapters and capabilities
62
-
63
- | Adapter | Engine license | Supported problem classes | Engine role | Integration |
78
+ | Solver | License | Problem Classes | Primary Use Case | Setup |
64
79
  | :--- | :--- | :--- | :--- | :--- |
65
- | **HiGHS** | **MIT** | **LP, MIP, convex QP** | Numerical optimization | Built-in default |
66
- | **Z3** | **MIT** | **SMT, SAT, logic, MIP** | Satisfiability checks, unsatisfiable cores, optimization | Optional plugin (`@ohtaman/washi/solvers/z3`) |
67
- | **GLPK** | **GPL-3.0** | **LP, MIP** | Linear and mixed-integer optimization | Optional plugin (`@ohtaman/washi/solvers/glpk`) |
68
- | **Mock** | **MIT** | — | Simulated solver responses for integration tests | Built-in; does not solve the model |
69
-
70
- ---
80
+ | **[HiGHS](https://github.com/ERGO-Code/HiGHS)** | MIT | LP, MIP, Convex QP | Numerical optimization & elastic conflict repair | Built-in default |
81
+ | **[Z3](https://github.com/Z3Prover/z3)** | MIT | SMT, SAT, Logic, MIP | Logical rules, satisfiability & exact `unsatCore` | `import "@ohtaman/washi/solvers/z3"` |
82
+ | **[GLPK](https://www.gnu.org/software/glpk/)** | GPL-3.0 | LP, MIP | Linear & mixed-integer optimization | `import "@ohtaman/washi/solvers/glpk"` |
83
+ | **Mock** | MIT | — | Deterministic unit & integration testing | Built-in (`MockSolver`) |
71
84
 
72
- ## 📦 Installation
73
-
74
- ```bash
75
- # npm
76
- npm install @ohtaman/washi
77
-
78
- # pnpm
79
- pnpm add @ohtaman/washi
80
-
81
- # yarn
82
- yarn add @ohtaman/washi
83
-
84
- # bun
85
- bun add @ohtaman/washi
86
- ```
87
-
88
- *(Optional)* To use Z3 or GLPK solvers, install their respective packages:
89
-
90
- ```bash
91
- npm install z3-solver # For SMT and unsat core diagnosis
92
- npm install glpk.js # For GLPK solver (GPL-3.0)
93
- ```
94
-
95
- *(Optional)* For AI coding agents (Claude Code, Antigravity, Cursor, Codex, etc.), `@ohtaman/washi` ships a portable [`skills/washi/SKILL.md`](skills/washi/SKILL.md) workflow alongside the [AI Agent Quick-Reference](docs/getting-started/ai-agents.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
- ```
85
+ Pass `{ solver: "z3" }` or `{ solver: "glpk" }` to `new Model(...)`, `m.solve(...)`, or `m.check(...)` after importing the plugin.
101
86
 
102
87
  ---
103
88
 
104
- ## 🚀 Usage Guide
89
+ ## 🚀 Usage Examples
105
90
 
106
- ### 1. Multidimensional Variables, Indicator Constraints & Solution Validation
91
+ ### 1. Sums, Dot Products (`sum` / `dot`) & Conditional Constraints
107
92
 
108
- Construct 1D dictionaries or 2D matrices of variables in a single call, express conditional Big-M logic with `m.indicator()`, monitor live progress (`onProgress`), and independently verify solutions with `m.validateSolution()`:
93
+ Combine indexed variables with `dot` / `sum` and express conditional rules with `m.indicator()`:
109
94
 
110
95
  ```ts
111
- import { Model, expr } from "@ohtaman/washi";
112
-
113
- const m = new Model("FacilityPlanning");
114
-
115
- // 1D dictionary of binary variables
116
- const openCity = m.binary(["Tokyo", "Osaka", "Nagoya"]);
117
-
118
- // 2D transport flow matrix: [Factory] x [Store]
119
- const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0, ub: 1000 });
120
-
121
- // 2D coordinate grid (e.g. 8x8 chessboard)
122
- const board = m.binary(8, 8, { prefix: "queen" });
123
-
124
- // Conditional Big-M constraint: if openCity["Tokyo"] == 0, flow["F1"]["StoreA"] <= 0
125
- // (Omit M to let WaSHI automatically derive a tight Big-M from finite variable bounds)
126
- m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
127
- activeValue: 0,
128
- name: "tokyo_closed_no_flow",
129
- });
130
-
131
- // Solve with optional progress callback & AbortSignal cancellation
132
- const controller = new AbortController();
133
- await m.solve({
134
- signal: controller.signal,
135
- onProgress: (ev) => {
136
- // Live branch-and-bound metrics: ev.elapsedTime, ev.objectiveValue, ev.objectiveBound, ev.mipGap
137
- },
138
- });
139
-
140
- // Independently verify variable bounds, integrality, and linear/logical constraints
141
- const report = m.validateSolution();
142
- console.log("Solution valid?", report.valid);
143
- ```
144
-
145
- ---
96
+ import { Model, expr, minimize } from "@ohtaman/washi";
146
97
 
147
- ### 2. Portfolio Risk Minimization (Convex Quadratic Programming)
98
+ const m = new Model("TransportPlanning");
99
+ const factories = ["Tokyo", "Osaka"];
100
+ const stores = ["StoreA", "StoreB"];
101
+ const costs = {
102
+ Tokyo: { StoreA: 4, StoreB: 7 },
103
+ Osaka: { StoreA: 8, StoreB: 3 },
104
+ };
148
105
 
149
- Minimize investment risk based on a covariance matrix subject to return and budget constraints:
106
+ const open = m.binary(factories);
107
+ const ship = m.continuous(factories, stores, { lb: 0, ub: 500 });
150
108
 
151
- ```ts
152
- import { Model, QuadExpr, sum, dot, minimize, expr } from "@ohtaman/washi";
153
-
154
- const m = new Model("MarkowitzPortfolio");
155
- const assets = ["Tech", "Bonds", "RealEstate"];
156
- const returns = [0.14, 0.04, 0.08];
157
-
158
- // Decision variables: portfolio allocation weights (0 to 1)
159
- const w = m.continuous(assets, { lb: 0, ub: 1 });
160
-
161
- // 1. Fully invested budget: sum(w) == 1.0
162
- m.constraint(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
163
-
164
- // 2. Minimum target return: portfolio return >= 8.5%
165
- const retMap = Object.fromEntries(assets.map((a, i) => [a, returns[i]]));
166
- m.constraint(expr`${dot(retMap, w)} >= 0.085`, "min_return");
167
-
168
- // 3. Covariance risk matrix
169
- const cov = [
170
- [0.040, 0.002, 0.015],
171
- [0.002, 0.005, 0.002],
172
- [0.015, 0.002, 0.025],
173
- ];
174
-
175
- // Quadratic objective: w^T * Cov * w
176
- let risk = new QuadExpr();
177
- for (let i = 0; i < assets.length; i++) {
178
- for (let j = 0; j < assets.length; j++) {
179
- risk = risk.add(w[assets[i]].mul(w[assets[j]]).mul(cov[i][j]));
180
- }
181
- }
182
- m.objective(minimize(risk));
109
+ // If Tokyo is closed (open["Tokyo"] == 0), total shipment from Tokyo must be 0
110
+ m.indicator(open["Tokyo"], expr`sum(${ship["Tokyo"]}) <= 0`, { activeValue: 0 });
111
+ m.constraint(expr`${ship["Tokyo"]["StoreA"]} + ${ship["Osaka"]["StoreA"]} >= 100`, "demand_A");
183
112
 
113
+ m.objective(minimize(expr`dot(${costs}, ${ship})`));
184
114
  await m.solve();
185
- console.log(`Minimal Variance: ${m.objectiveValue?.toFixed(6)}`);
186
- assets.forEach((name) => {
187
- console.log(` ${name}: ${((w[name].value || 0) * 100).toFixed(2)}%`);
188
- });
189
- ```
190
115
 
191
- ---
116
+ console.log("Tokyo -> StoreA:", ship["Tokyo"]["StoreA"].value);
117
+ ```
192
118
 
193
- ### 3. SMT Logic & Rule Engines (with Z3)
119
+ ### 2. Convex Quadratic Programming (QP)
194
120
 
195
- Express boolean variables, propositional connectives (`and`, `or`, `implies`, `not`, `xor`), and mixed arithmetic rules:
121
+ Minimize a quadratic objective subject to linear constraints:
196
122
 
197
123
  ```ts
198
- import { Model, not, expr } from "@ohtaman/washi";
199
- import "@ohtaman/washi/solvers/z3";
200
-
201
- const m = new Model({ name: "SmartSecurityRules" });
202
-
203
- const isNight = m.bool("isNight");
204
- const motion = m.bool("motion");
205
- const alarm = m.bool("alarm");
206
- const siren = m.bool("siren");
207
- const lux = m.integer("lux", { lb: 0 });
124
+ import { Model, expr, minimize } from "@ohtaman/washi";
208
125
 
209
- // Logical rules
210
- m.constraint(lux.le(50).implies(isNight), "dark_implies_night");
211
- m.constraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
126
+ const m = new Model("PortfolioQP");
127
+ const x = m.continuous("tech", { lb: 0, ub: 1 });
128
+ const y = m.continuous("bonds", { lb: 0, ub: 1 });
212
129
 
213
- // Current sensor observations
214
- m.constraint(expr`${lux} <= 10`, "sensor_dark");
215
- m.constraint(motion, "sensor_motion");
216
- m.constraint(alarm, "alarm_on");
130
+ m.constraint(expr`${x} + ${y} == 1`, "budget");
131
+ m.constraint(expr`0.14 * ${x} + 0.04 * ${y} >= 0.08`, "min_return");
132
+ m.objective(minimize(expr`0.04 * ${x}^2 + 0.005 * ${y}^2`));
217
133
 
218
- // Check satisfiability and deduce state without an objective function
219
- const res = await m.check({ solver: "z3" });
220
-
221
- console.log(`Satisfiable? ${res.status === "Sat"}`); // true
222
- console.log(`Siren active? ${m.values.get("siren")}`); // true
134
+ await m.solve();
135
+ console.log(`Tech: ${x.value?.toFixed(2)}, Bonds: ${y.value?.toFixed(2)}`);
223
136
  ```
224
137
 
225
- ---
226
-
227
- ### 4. Conflict Diagnosis (`conflicts` / `unsatCore`)
138
+ ### 3. SMT Logic & Conflict Diagnosis (`m.check`)
228
139
 
229
- When business rules or constraints contradict each other, extract a **conflicting constraint subset** (unsatisfiable core; minimality is not guaranteed) for troubleshooting (requires Z3):
140
+ Express logical implications and diagnose why a model is infeasible:
141
+ - **HiGHS (default)**: Isolates an **elastic repair set** (`res.conflicts`)—constraints whose relaxation restores feasibility.
142
+ - **Z3**: Extracts an **unsatisfiable core** (`res.unsatCore` / `res.conflicts`)—a mutually contradictory subset of constraints.
230
143
 
231
144
  ```ts
232
- import { Model, expr } from "@ohtaman/washi";
233
- import "@ohtaman/washi/solvers/z3";
145
+ import { Model, implies, expr } from "@ohtaman/washi";
146
+ // import "@ohtaman/washi/solvers/z3"; // Optional: for exact SMT unsatCore
234
147
 
235
- const m = new Model({ name: "ConflictDiagnosis", solver: "z3" });
148
+ const m = new Model("StaffingRules");
236
149
  const engineers = m.integer("engineers", { lb: 0, ub: 20 });
237
150
  const budget = m.continuous("budget", { lb: 0, ub: 100000 });
238
- const rushDelivery = m.bool("rushDelivery");
151
+ const rush = m.bool("rush");
239
152
 
240
- // Rule 1: Each engineer costs $8,000
241
153
  m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
154
+ m.constraint(rush, "rush_required");
155
+ m.constraint(implies(rush, expr`${engineers} >= 8`), "rush_staffing");
156
+ m.constraint(expr`${budget} <= 50000`, "budget_cap"); // Conflict: 8 * $8,000 > $50,000
157
+ m.constraint(expr`${engineers} >= 1`, "min_staff"); // Unrelated rule
242
158
 
243
- // Rule 2: Customer requires rush delivery
244
- m.constraint(rushDelivery, "rush_required");
245
-
246
- // Rule 3: Rush delivery requires at least 8 engineers
247
- m.constraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
248
-
249
- // Rule 4: Hard board budget cap of $50,000 (Conflict: 8 * $8000 = $64,000 > $50,000!)
250
- m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
251
-
252
- // Rule 5: Innocent bystander rule
253
- m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
254
-
255
- const res = await m.check({ solver: "z3" });
256
-
257
- console.log(`Status: ${res.status}`); // "Unsat"
258
- console.log("Conflicting constraints:", res.conflicts);
259
- // Output: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
260
- // Note: "minimum_one_employee" is correctly excluded!
261
- ```
262
-
263
- ---
264
-
265
- ### 5. Solving with GLPK (GPL-3.0)
266
-
267
- To use the GNU Linear Programming Kit (GLPK) solver plugin:
268
-
269
- ```ts
270
- import { Model, expr, maximize } from "@ohtaman/washi";
271
- import "@ohtaman/washi/solvers/glpk";
272
-
273
- const m = new Model({ solver: "glpk" });
274
- const x = m.integer("x", { lb: 0 });
275
- const y = m.integer("y", { lb: 0 });
276
-
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");
280
-
281
- const status = await m.solve();
282
- console.log(`Status: ${status}`); // "Optimal"
283
- console.log(`Objective: ${m.objectiveValue}`); // 1100
284
- ```
285
-
286
- ---
287
-
288
- ### 6. Vector Operations & Fast Dot Product
289
-
290
- Cleanly compute 1D/2D linear combinations without nested loops:
291
-
292
- ```ts
293
- import { Model, dot, minimize } from "@ohtaman/washi";
294
-
295
- const m = new Model();
296
- const factories = ["Tokyo", "Osaka"];
297
- const stores = ["StoreA", "StoreB"];
298
-
299
- const costs = {
300
- Tokyo: { StoreA: 4, StoreB: 7 },
301
- Osaka: { StoreA: 8, StoreB: 3 },
302
- };
303
-
304
- const ship = m.continuous(factories, stores);
305
-
306
- // Total transportation cost in one line:
307
- m.objective(minimize(dot(costs, ship)));
308
- await m.solve();
309
-
310
- // Extract all optimal variable values as a structured object
311
- const solution = m.getValues(ship);
312
- console.log(solution);
159
+ const res = await m.check(); // Or: await m.check({ solver: "z3" })
160
+ console.log(res.status); // "Unsat"
161
+ console.log(res.conflicts); // HiGHS: ["rush_required"]
313
162
  ```
314
163
 
315
164
  ---
316
165
 
317
166
  ## 🌐 Browser & Web Worker Usage
318
167
 
319
- 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.
320
-
321
- ### npm with a bundler
322
-
323
- ```typescript
324
- import { Model } from "@ohtaman/washi";
325
- const model = new Model();
326
- // Define variables, objective and constraints.
327
- await model.solve();
328
- ```
329
-
330
- 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.
331
-
332
- ### Browser ESM without an application build
333
-
334
- Serve the **entire `dist/browser` directory**, including its JS chunks, `highs.wasm` and Worker. No import map is needed:
168
+ - **Bundlers (Vite / webpack 5)**: `import { Model } from "@ohtaman/washi"` works out of the box. Solves run in a Web Worker by default; pass `{ useWorker: false }` for main-thread execution.
169
+ - **Standalone ESM / Script Tag / CDN**: Serve `dist/browser/` (including `highs.wasm` and worker chunks) or load from a CDN.
335
170
 
336
171
  ```html
172
+ <!-- Browser ESM -->
337
173
  <script type="module">
338
174
  import { Model } from "./washi/washi.js";
339
- const model = new Model();
340
- // Define variables, objective and constraints.
341
- await model.solve({ useWorker: false });
175
+ // Optional solver plugins: import "./washi/solvers/glpk.js" or "./washi/solvers/z3.js"
176
+ const m = new Model();
177
+ await m.solve();
342
178
  </script>
343
- ```
344
-
345
- ### Plain script / CodePen
346
179
 
347
- 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:
348
-
349
- ```html
350
- <script src="./washi/washi.min.js"></script>
351
- <script>
352
- const model = new Washi.Model();
353
- // Define variables, objective and constraints.
354
- model.solve({ useWorker: false }).then(status => console.log(status));
355
- </script>
356
- ```
357
-
358
- For CDN usage, replace the local URL with `https://cdn.jsdelivr.net/npm/@ohtaman/washi@<version>/dist/browser/washi.min.js` (specifying a compatible release version once published). 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.
359
-
360
- ### Solver Plugins in the Browser (GLPK & Z3)
361
-
362
- The browser distribution includes pre-bundled, standalone solver plugins under `dist/browser/solvers/`:
363
- - **GLPK**: `dist/browser/solvers/glpk.js` (ESM) and `dist/browser/solvers/glpk.min.js` (IIFE, registers global `WashiGLPK`)
364
- - **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`
365
-
366
- #### Browser ESM with GLPK
367
- ```html
368
- <script type="module">
369
- import { Model, maximize } from "./washi/washi.js";
370
- import "./washi/solvers/glpk.js";
371
-
372
- const model = new Model({ solver: "glpk" });
373
- const x = model.continuous("x", { lb: 0, ub: 10 });
374
- model.objective(maximize(x));
375
- await model.solve();
376
- console.log("GLPK status:", model.status);
377
- </script>
378
- ```
379
-
380
- #### Plain Script with GLPK
381
- ```html
382
- <script src="./washi/washi.min.js"></script>
383
- <script src="./washi/solvers/glpk.min.js"></script>
384
- <script>
385
- const model = new Washi.Model({ solver: "glpk" });
386
- const x = model.continuous("x", { lb: 0, ub: 10 });
387
- model.objective(Washi.maximize(x));
388
- model.solve().then(status => console.log("GLPK status:", status));
389
- </script>
180
+ <!-- Classic Script / CodePen (exposes global Washi) -->
181
+ <script src="https://cdn.jsdelivr.net/npm/@ohtaman/washi/dist/browser/washi.min.js"></script>
390
182
  ```
391
183
 
392
184
  > [!NOTE]
393
- > **Z3 in Browser Environments & Cross-Origin Isolation**:
394
- > Because the Z3 WebAssembly engine uses multithreading (`SharedArrayBuffer`), modern browsers require cross-origin isolation (`Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp`). See the [Cross-Origin Isolation Guide](docs/getting-started/cross-origin.md) and [Runtime Environments Guide](docs/getting-started/runtimes.md) for full details on static hosting, `coi-serviceworker.js`, offline/intranet deployment, CSP (`worker-src blob:`), and custom WASM URLs (`setHighsWasmUrl`).
395
-
396
- Complete standalone examples for ESM, script tags, and CodePen are available in [`examples/distribution/`](examples/distribution/README.md).
397
-
398
- ---
399
-
400
- ## 🛠️ Custom Solvers & Extensibility
401
-
402
- 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):
403
-
404
- - See the complete [Custom Solver Developer Guide](docs/solvers/custom-solver.md) for step-by-step tutorials, complete API contracts (`Solver`, `SolveModelData`, `CheckModelData`, `SolverResult`), and testing best practices.
185
+ > **Z3 in the Browser**: Because Z3's WebAssembly build uses `SharedArrayBuffer`, browsers require Cross-Origin Isolation (`COOP: same-origin` and `COEP: require-corp`). See the [Cross-Origin Isolation Guide](docs/getting-started/cross-origin.md) and [`examples/distribution/`](examples/distribution/README.md).
405
186
 
406
187
  ---
407
188
 
408
- ## 📚 Documentation
189
+ ## 🤝 Contributing
409
190
 
410
- Full documentation is available at **[ohtaman.github.io/washi](https://ohtaman.github.io/washi/)** and in the repository's [`docs/`](docs/) directory:
411
-
412
- - **Getting Started**: [Quick Start](docs/getting-started/index.md) · [AI Agent Quick-Reference](docs/getting-started/ai-agents.md) · [Runtime Environments](docs/getting-started/runtimes.md) · [Cross-Origin Isolation (Z3)](docs/getting-started/cross-origin.md)
413
- - **Modeling Guide**: [Variables (`Var`, `BoolVar`)](docs/guide/variables.md) · [Expressions (`expr`, `sum`, `dot`)](docs/guide/expressions.md) · [Constraints & Logic (`indicator`, SMT)](docs/guide/constraints.md) · [Objectives & Solving (`solve`, `validateSolution`)](docs/guide/objectives.md) · [Feasibility & Unsat Core (`check`)](docs/guide/unsat-core.md)
414
- - **Solvers**: [Solver Overview](docs/solvers/index.md) · [HiGHS](docs/solvers/highs.md) · [Z3](docs/solvers/z3.md) · [GLPK](docs/solvers/glpk.md) · [Custom Solvers](docs/solvers/custom-solver.md)
191
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for local development setup, running tests and benchmarks, rebuilding WebAssembly assets, and the release workflow.
415
192
 
416
193
  ---
417
194
 
418
- ## 💻 Examples and browser demo
419
-
420
- Start with the [example gallery](https://github.com/ohtaman/washi/blob/main/examples/README.md) or the [online Example & Feature Finder](https://ohtaman.github.io/washi/examples/), which organize examples by problem category and by WaSHI API feature.
195
+ ## 🙏 Acknowledgments
421
196
 
422
- | Category | Example | What it demonstrates | Solver |
423
- | :--- | :--- | :--- | :--- |
424
- | **Fundamentals** | [Production planning](https://github.com/ohtaman/washi/blob/main/examples/node/01-production-planning.ts) | Integer variables, a profit objective, and resource constraints (`expr`) | HiGHS |
425
- | **Fundamentals** | [Transportation network](https://github.com/ohtaman/washi/blob/main/examples/node/09-transportation-problem.ts) | 2D origin-destination variable matrix (`continuous(rows, cols)`) and flow balance | HiGHS |
426
- | **Fundamentals** | [Diet & sensitivity analysis](https://github.com/ohtaman/washi/blob/main/examples/node/10-diet-sensitivity.ts) | Shadow prices (`constr.dual`), surpluses (`constr.slack`), and reduced costs (`var.reducedCost`) | HiGHS |
427
- | **Practical** | [Staff shift scheduling](https://github.com/ohtaman/washi/blob/main/examples/node/11-shift-scheduling.ts) | Weekly roster optimization + diagnosing conflicting Saturday vacation requests | HiGHS + Z3 |
428
- | **Practical** | [E-commerce coupon optimizer](https://github.com/ohtaman/washi/blob/main/examples/node/12-ecommerce-coupon-optimizer.ts) | Conditional spend thresholds (`indicator`), coupon exclusivity, and add-on recommendations | HiGHS |
429
- | **Practical** | [Portfolio allocation](https://github.com/ohtaman/washi/blob/main/examples/node/03-portfolio-qp.ts) | A convex quadratic objective (`QuadExpr`) with allocation constraints | HiGHS |
430
- | **Logic / SMT** | [Business rule checks](https://github.com/ohtaman/washi/blob/main/examples/node/04-smt-security-rules.ts) | Arithmetic and Boolean constraints (`bool`, `implies`) with satisfiability checking | Z3 |
431
- | **Logic / SMT** | [Conflict diagnosis](https://github.com/ohtaman/washi/blob/main/examples/node/05-unsat-core-diagnosis.ts) | Named constraints and an unsatisfiable core (`unsatCore`) | Z3 |
432
-
433
- [Open the hosted demo](https://ohtaman.github.io/washi/demo/) to edit and run models in your browser. For browser ESM, classic script tags, and CodePen, see the [distribution examples](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md). Z3 requires cross-origin isolation; see the browser requirements above.
434
-
435
- To run examples locally, clone the repository and install its development dependencies first:
436
-
437
- ```bash
438
- git clone https://github.com/ohtaman/washi.git
439
- cd washi
440
- npm ci
441
- npx tsx examples/node/01-production-planning.ts
442
- npx tsx examples/node/09-transportation-problem.ts
443
- npx tsx examples/node/10-diet-sensitivity.ts
444
- npx tsx examples/node/11-shift-scheduling.ts
445
- npx tsx examples/node/12-ecommerce-coupon-optimizer.ts
446
-
447
- # Local browser demo: http://localhost:5173/demo/
448
- npm run dev
449
- ```
450
-
451
- The example sources import the repository's `src/` modules. In an application using the published package, import `@ohtaman/washi` and the required solver plugin instead.
452
-
453
- ---
454
-
455
- ## 🧪 Testing & Reproducible Benchmarks
456
-
457
- Run the repository's unit and integration tests, and build the distributions:
458
-
459
- ```bash
460
- # Run all unit and integration tests
461
- npm test
462
-
463
- # Build production bundle and TypeScript declarations
464
- npm run build
465
- ```
466
-
467
- ### Optional Reproducible Benchmarks
468
-
469
- To measure modeling and solver phases on deterministic `small-lp` (24 variables, 18 constraints) and `scheduling-mip` (315 variables, 231 constraints, including rebuilt `initialValue` warm starts) scenarios without mixing performance timings into correctness test thresholds:
470
-
471
- ```bash
472
- # Run Node.js in-process WASM benchmarks (HiGHS and GLPK, 5 warm iterations by default)
473
- npm run bench
474
-
475
- # Customize iteration count or emit structured JSON
476
- node scripts/run-benchmarks.mjs --iterations=10 --json
477
-
478
- # Also measure headless Chromium Web Worker execution (requires Playwright Chromium)
479
- node scripts/run-benchmarks.mjs --browser
480
- ```
197
+ WaSHI stands on the shoulders of the open-source mathematical optimization and automated reasoning communities:
481
198
 
482
- - **Explicit phase boundaries**: Reports cold runtime initialization (`coldRuntimeInitMs`, `coldFirstSolveMs`) separately from repeated warm-runtime measurements (`modelBuildMs`, `validationMs`, `lpSerializationMs` for LP-string adapters or `symbolMapMs` for structured adapters, `resultMappingMs` via `MockSolver`, `adapterSolveMs`, and `endToEndSolveMs`). Timings are measured directly per phase and are never subtracted to estimate "native solver time".
483
- - **Distinct execution environments**: Node.js in-process WASM (`usesBrowserWorker: false`) and Browser Web Worker execution (`usesBrowserWorker: true`, which includes Worker lifecycle and `postMessage` structured-clone transfer) are reported as separate environments, not interchangeable baselines.
199
+ - **[HiGHS](https://highs.dev/)** ([`ERGO-Code/HiGHS`](https://github.com/ERGO-Code/HiGHS)) — High-performance open-source optimization software for LP, MIP, and Convex QP initiated at the University of Edinburgh by Julian Hall, Qi Huangfu, Ivet Galabova, Leona Gottwald, Michael Feldmeier, and contributors, which powers WaSHI's built-in default solver (*Huangfu & Hall, Mathematical Programming Computation, 2018*, [DOI: 10.1007/s12532-017-0130-5](https://doi.org/10.1007/s12532-017-0130-5)).
200
+ - **[Z3 Theorem Prover](https://github.com/Z3Prover/z3)** ([`z3-solver`](https://www.npmjs.com/package/z3-solver)) — Developed at Microsoft Research, powering WaSHI's optional SMT satisfiability and exact `unsatCore` conflict diagnosis plugin.
201
+ - **[GLPK](https://www.gnu.org/software/glpk/) & [`glpk.js`](https://github.com/jvail/glpk.js)** — Powering WaSHI's optional GPL-3.0 LP/MIP solver plugin.
202
+ - **[PuLP](https://github.com/coin-or/pulp)** — Inspiring WaSHI's declarative algebraic modeling ergonomics.
484
203
 
485
204
  ---
486
205
 
487
206
  ## 📄 License
488
207
 
489
208
  - **`@ohtaman/washi`**: [MIT License](./LICENSE)
490
- - **HiGHS** (default engine): MIT License
491
- - **Z3** (optional plugin): MIT License
492
- - **GLPK** (optional plugin): GPL-3.0 License
209
+ - **Bundled / Optional Engines**: HiGHS (MIT), Z3 (MIT), GLPK (GPL-3.0). See [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md) for full details.