@ohtaman/washi 0.6.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.
- package/README.ja.md +103 -421
- package/README.md +103 -421
- package/README.zh-CN.md +103 -421
- package/THIRD_PARTY_LICENSES.md +5 -3
- package/dist/browser/solvers/glpk.js +2 -2
- package/dist/browser/solvers/glpk.min.js +1 -1
- package/dist/browser/washi.js +777 -261
- package/dist/browser/washi.min.js +3 -3
- package/dist/lib/{registry-C20sxTwm.js → registry-D7TZ3Wh_.js} +88 -18
- package/dist/lib/solvers/glpk.js +3 -3
- package/dist/lib/solvers/z3.js +1 -1
- package/dist/lib/washi.js +864 -421
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/modeler/expression.d.ts +47 -26
- package/dist/types/modeler/expression.d.ts.map +1 -1
- package/dist/types/modeler/index.d.ts +2 -2
- package/dist/types/modeler/index.d.ts.map +1 -1
- package/dist/types/modeler/model.d.ts +12 -6
- package/dist/types/modeler/model.d.ts.map +1 -1
- package/dist/types/modeler/parser.d.ts +25 -5
- package/dist/types/modeler/parser.d.ts.map +1 -1
- package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
- package/dist/types/modeler/solvers/types.d.ts +2 -0
- package/dist/types/modeler/solvers/types.d.ts.map +1 -1
- package/docs/examples/diet-sensitivity.md +15 -14
- package/docs/examples/ecommerce-coupons.md +15 -22
- package/docs/examples/index.md +14 -12
- package/docs/examples/knapsack.md +8 -7
- package/docs/examples/portfolio-qp.md +11 -4
- package/docs/examples/production.md +4 -4
- package/docs/examples/shift-scheduling.md +20 -7
- package/docs/examples/smt-security.md +16 -4
- package/docs/examples/transportation.md +46 -19
- package/docs/examples/traveling-salesperson.md +28 -44
- package/docs/examples/unsat-core.md +15 -5
- package/docs/examples/vehicle-routing.md +10 -8
- package/docs/getting-started/ai-agents.md +25 -14
- package/docs/getting-started/cross-origin.md +56 -28
- package/docs/getting-started/index.md +40 -69
- package/docs/getting-started/runtimes.md +66 -61
- package/docs/guide/constraints.md +46 -40
- package/docs/guide/decomposition.md +127 -259
- package/docs/guide/expressions.md +90 -39
- package/docs/guide/formulation-tips.md +212 -0
- package/docs/guide/objectives.md +7 -4
- package/docs/guide/unsat-core.md +18 -6
- package/docs/guide/variables.md +33 -38
- package/docs/ja/getting-started/ai-agents.md +24 -14
- package/docs/solvers/highs.md +27 -6
- package/docs/solvers/index.md +1 -1
- package/docs/solvers/z3.md +3 -0
- package/docs/zh/getting-started/ai-agents.md +24 -14
- package/llms-full.txt +929 -687
- package/llms.txt +21 -19
- package/package.json +3 -3
- package/skills/washi/SKILL.md +14 -10
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
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 -
|
|
5
|
+
<img alt="WaSHI - Mathematical Optimization Modeler in TypeScript" src="./assets/washi-header-light.png" width="420" />
|
|
6
6
|
</picture>
|
|
7
|
-
<p><strong>
|
|
7
|
+
<p><strong>Mathematical Optimization Modeler in TypeScript</strong></p>
|
|
8
8
|
<p>
|
|
9
9
|
<em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Convex Quadratic Programming (QP) • SMT & Conflict Diagnosis</em>
|
|
10
10
|
</p>
|
|
@@ -16,512 +16,194 @@
|
|
|
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.
|
|
20
|
-
<a href="https://ohtaman.
|
|
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 mathematical optimization and constraint programming in TypeScript and JavaScript, inspired by Python's **PuLP
|
|
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
|
-
|
|
29
|
+
## ✨ Key Features
|
|
30
30
|
|
|
31
|
-
|
|
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.
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
import { Model, expr, maximize } from "@ohtaman/washi";
|
|
35
|
-
|
|
36
|
-
const m = new Model("WoodworkingProfit");
|
|
37
|
-
const x = m.integer("desks", { lb: 0 });
|
|
38
|
-
const y = m.integer("chairs", { lb: 0 });
|
|
39
|
-
|
|
40
|
-
m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
|
|
41
|
-
m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
42
|
-
m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
43
|
-
|
|
44
|
-
await m.solve();
|
|
45
|
-
console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`);
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
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 conflict diagnosis**: Explicitly check satisfiability (`m.check()`) and inspect conflicting constraints (`conflicts` / `unsatCore`) to investigate infeasibilities: built-in HiGHS isolates an **elastic repair set** (`conflicts`, constraints requiring relaxation to restore feasibility), while Z3 extracts an **unsatisfiable core** (`unsatCore` / `conflicts`, a mutually contradictory constraint subset; minimality is not guaranteed).
|
|
56
|
-
5. **Advanced decomposition methods**: Overcome combinatorial bottlenecks in monolithic MIP formulations (cutting stock, vehicle routing/VRP, traveling salesperson/TSP, and two-stage facility planning) using first-class decomposition orchestrators (`master.solveColumnGeneration`, `master.solveBenders`). WaSHI coordinates dual shadow prices, pricing subproblems, and Benders cut generation via clean callback orchestrators and linking constraints (`sub.link(...)`).
|
|
57
|
-
|
|
58
|
-
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.
|
|
37
|
+
## 📚 Documentation & Links
|
|
59
38
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| Adapter | Engine license | Supported problem classes | Engine role | Integration |
|
|
65
|
-
| :--- | :--- | :--- | :--- | :--- |
|
|
66
|
-
| **[HiGHS](https://github.com/ERGO-Code/HiGHS)** | **MIT** | **LP, MIP, convex QP** | Numerical optimization | Built-in default |
|
|
67
|
-
| **[Z3](https://github.com/Z3Prover/z3)** (via [`z3-solver`](https://www.npmjs.com/package/z3-solver)) | **MIT** | **SMT, SAT, logic, MIP** | Satisfiability checks, unsatisfiable cores, optimization | Optional plugin (`@ohtaman/washi/solvers/z3`) |
|
|
68
|
-
| **[GLPK](https://www.gnu.org/software/glpk/)** (via [`glpk.js`](https://www.npmjs.com/package/glpk.js)) | **GPL-3.0** | **LP, MIP** | Linear and mixed-integer optimization | Optional plugin (`@ohtaman/washi/solvers/glpk`) |
|
|
69
|
-
| **Mock** | **MIT** | — | Simulated solver responses for integration tests | Built-in; does not solve the model |
|
|
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
|
|
70
43
|
|
|
71
44
|
---
|
|
72
45
|
|
|
73
|
-
## 📦 Installation
|
|
46
|
+
## 📦 Installation & Quick Start
|
|
74
47
|
|
|
75
48
|
```bash
|
|
76
|
-
# npm
|
|
77
49
|
npm install @ohtaman/washi
|
|
78
50
|
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# yarn
|
|
83
|
-
yarn add @ohtaman/washi
|
|
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)
|
|
84
54
|
|
|
85
|
-
#
|
|
86
|
-
bun add @ohtaman/washi
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
*(Optional)* To use Z3 or GLPK solvers, install their respective packages:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npm install z3-solver # For SMT and unsat core diagnosis
|
|
93
|
-
npm install glpk.js # For GLPK solver (GPL-3.0)
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
*(Optional)* For AI coding agents (Claude Code, Antigravity, Cursor, Codex, etc.), install the portable [`skills/washi/SKILL.md`](skills/washi/SKILL.md) workflow directly via the `skills` CLI or copy it from the installed package:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
# Install via the open skills CLI (skills.sh / vercel-labs/skills)
|
|
55
|
+
# Optional skill for AI coding agents (Claude Code, Cursor, Codex, etc.)
|
|
100
56
|
npx skills add ohtaman/washi
|
|
101
|
-
|
|
102
|
-
# Or manually copy the packaged skill into your project's agent directory
|
|
103
|
-
mkdir -p .agents/skills/washi
|
|
104
|
-
cp node_modules/@ohtaman/washi/skills/washi/SKILL.md .agents/skills/washi/SKILL.md
|
|
105
57
|
```
|
|
106
58
|
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## 🚀 Usage Guide
|
|
110
|
-
|
|
111
|
-
### 1. Multidimensional Variables, Indicator Constraints & Solution Validation
|
|
112
|
-
|
|
113
|
-
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()`:
|
|
114
|
-
|
|
115
59
|
```ts
|
|
116
|
-
import { Model, expr } from "@ohtaman/washi";
|
|
117
|
-
|
|
118
|
-
const m = new Model("FacilityPlanning");
|
|
119
|
-
|
|
120
|
-
// 1D dictionary of binary variables
|
|
121
|
-
const openCity = m.binary(["Tokyo", "Osaka", "Nagoya"]);
|
|
122
|
-
|
|
123
|
-
// 2D transport flow matrix: [Factory] x [Store]
|
|
124
|
-
const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0, ub: 1000 });
|
|
125
|
-
|
|
126
|
-
// 2D coordinate grid (e.g. 8x8 chessboard)
|
|
127
|
-
const board = m.binary(8, 8, { prefix: "queen" });
|
|
128
|
-
|
|
129
|
-
// Conditional Big-M constraint: if openCity["Tokyo"] == 0, flow["F1"]["StoreA"] <= 0
|
|
130
|
-
// (Omit M to let WaSHI automatically derive a tight Big-M from finite variable bounds)
|
|
131
|
-
m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
|
|
132
|
-
activeValue: 0,
|
|
133
|
-
name: "tokyo_closed_no_flow",
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Solve with optional progress callback & AbortSignal cancellation
|
|
137
|
-
const controller = new AbortController();
|
|
138
|
-
await m.solve({
|
|
139
|
-
signal: controller.signal,
|
|
140
|
-
onProgress: (ev) => {
|
|
141
|
-
// Live branch-and-bound metrics: ev.elapsedTime, ev.objectiveValue, ev.objectiveBound, ev.mipGap
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Independently verify variable bounds, integrality, and linear/logical constraints
|
|
146
|
-
const report = m.validateSolution();
|
|
147
|
-
console.log("Solution valid?", report.valid);
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
### 2. Portfolio Risk Minimization (Convex Quadratic Programming)
|
|
60
|
+
import { Model, expr, maximize } from "@ohtaman/washi";
|
|
153
61
|
|
|
154
|
-
|
|
62
|
+
const m = new Model("WoodworkingProfit");
|
|
63
|
+
const x = m.integer("desks", { lb: 0 });
|
|
64
|
+
const y = m.integer("chairs", { lb: 0 });
|
|
155
65
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const m = new Model("MarkowitzPortfolio");
|
|
160
|
-
const assets = ["Tech", "Bonds", "RealEstate"];
|
|
161
|
-
const returns = [0.14, 0.04, 0.08];
|
|
162
|
-
|
|
163
|
-
// Decision variables: portfolio allocation weights (0 to 1)
|
|
164
|
-
const w = m.continuous(assets, { lb: 0, ub: 1 });
|
|
165
|
-
|
|
166
|
-
// 1. Fully invested budget: sum(w) == 1.0
|
|
167
|
-
m.constraint(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
|
|
168
|
-
|
|
169
|
-
// 2. Minimum target return: portfolio return >= 8.5%
|
|
170
|
-
const retMap = Object.fromEntries(assets.map((a, i) => [a, returns[i]]));
|
|
171
|
-
m.constraint(expr`${dot(retMap, w)} >= 0.085`, "min_return");
|
|
172
|
-
|
|
173
|
-
// 3. Covariance risk matrix
|
|
174
|
-
const cov = [
|
|
175
|
-
[0.040, 0.002, 0.015],
|
|
176
|
-
[0.002, 0.005, 0.002],
|
|
177
|
-
[0.015, 0.002, 0.025],
|
|
178
|
-
];
|
|
179
|
-
|
|
180
|
-
// Quadratic objective: w^T * Cov * w
|
|
181
|
-
let risk = new QuadExpr();
|
|
182
|
-
for (let i = 0; i < assets.length; i++) {
|
|
183
|
-
for (let j = 0; j < assets.length; j++) {
|
|
184
|
-
risk = risk.add(w[assets[i]].mul(w[assets[j]]).mul(cov[i][j]));
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
m.objective(minimize(risk));
|
|
66
|
+
m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
|
|
67
|
+
m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
68
|
+
m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
188
69
|
|
|
189
70
|
await m.solve();
|
|
190
|
-
console.log(`
|
|
191
|
-
assets.forEach((name) => {
|
|
192
|
-
console.log(` ${name}: ${((w[name].value || 0) * 100).toFixed(2)}%`);
|
|
193
|
-
});
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
### 3. SMT Logic & Rule Engines (with Z3)
|
|
199
|
-
|
|
200
|
-
Express boolean variables, propositional connectives (`and`, `or`, `implies`, `not`, `xor`), and mixed arithmetic rules:
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
import { Model, not, expr } from "@ohtaman/washi";
|
|
204
|
-
import "@ohtaman/washi/solvers/z3";
|
|
205
|
-
|
|
206
|
-
const m = new Model({ name: "SmartSecurityRules" });
|
|
207
|
-
|
|
208
|
-
const isNight = m.bool("isNight");
|
|
209
|
-
const motion = m.bool("motion");
|
|
210
|
-
const alarm = m.bool("alarm");
|
|
211
|
-
const siren = m.bool("siren");
|
|
212
|
-
const lux = m.integer("lux", { lb: 0 });
|
|
213
|
-
|
|
214
|
-
// Logical rules
|
|
215
|
-
m.constraint(lux.le(50).implies(isNight), "dark_implies_night");
|
|
216
|
-
m.constraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
|
|
217
|
-
|
|
218
|
-
// Current sensor observations
|
|
219
|
-
m.constraint(expr`${lux} <= 10`, "sensor_dark");
|
|
220
|
-
m.constraint(motion, "sensor_motion");
|
|
221
|
-
m.constraint(alarm, "alarm_on");
|
|
222
|
-
|
|
223
|
-
// Check satisfiability and deduce state without an objective function
|
|
224
|
-
const res = await m.check({ solver: "z3" });
|
|
225
|
-
|
|
226
|
-
console.log(`Satisfiable? ${res.status === "Sat"}`); // true
|
|
227
|
-
console.log(`Siren active? ${m.values.get("siren")}`); // true
|
|
71
|
+
console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`);
|
|
228
72
|
```
|
|
229
73
|
|
|
230
74
|
---
|
|
231
75
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
When business rules or constraints contradict each other, diagnose infeasibilities to pinpoint where to intervene:
|
|
235
|
-
- **Built-in HiGHS & GLPK**: `await m.check()` uses a single-solve elastic slack formulation ($\min \sum s_i$) to isolate `res.conflicts`—the **repair set** of constraints whose relaxation restores feasibility, without requiring extra dependencies.
|
|
236
|
-
- **Z3**: `await m.check({ solver: "z3" })` provides exact SMT **unsatisfiable-core** extraction (`res.unsatCore` and `res.conflicts`), returning a mutually contradictory constraint subset.
|
|
237
|
-
|
|
238
|
-
```ts
|
|
239
|
-
import { Model, expr } from "@ohtaman/washi";
|
|
240
|
-
|
|
241
|
-
const m = new Model("ConflictDiagnosis");
|
|
242
|
-
const engineers = m.integer("engineers", { lb: 0, ub: 20 });
|
|
243
|
-
const budget = m.continuous("budget", { lb: 0, ub: 100000 });
|
|
244
|
-
const rushDelivery = m.bool("rushDelivery");
|
|
245
|
-
|
|
246
|
-
// Rule 1: Each engineer costs $8,000
|
|
247
|
-
m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
|
|
248
|
-
|
|
249
|
-
// Rule 2: Customer requires rush delivery
|
|
250
|
-
m.constraint(rushDelivery, "rush_required");
|
|
251
|
-
|
|
252
|
-
// Rule 3: Rush delivery requires at least 8 engineers
|
|
253
|
-
m.constraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
|
|
254
|
-
|
|
255
|
-
// Rule 4: Hard board budget cap of $50,000 (Conflict: 8 * $8000 = $64,000 > $50,000!)
|
|
256
|
-
m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
|
|
257
|
-
|
|
258
|
-
// Rule 5: Innocent bystander rule
|
|
259
|
-
m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
|
|
76
|
+
## ⚙️ Solvers
|
|
260
77
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// (Relaxing rush_required restores feasibility; innocent rules like minimum_one_employee are untouched)
|
|
78
|
+
| Solver | License | Problem Classes | Primary Use Case | Setup |
|
|
79
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
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`) |
|
|
268
84
|
|
|
269
|
-
|
|
270
|
-
// const z3Res = await m.check({ solver: "z3" });
|
|
271
|
-
// console.log("Contradictory core:", z3Res.unsatCore);
|
|
272
|
-
// Output with Z3: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
|
|
273
|
-
```
|
|
85
|
+
Pass `{ solver: "z3" }` or `{ solver: "glpk" }` to `new Model(...)`, `m.solve(...)`, or `m.check(...)` after importing the plugin.
|
|
274
86
|
|
|
275
87
|
---
|
|
276
88
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
To use the GNU Linear Programming Kit (GLPK) solver plugin:
|
|
280
|
-
|
|
281
|
-
```ts
|
|
282
|
-
import { Model, expr, maximize } from "@ohtaman/washi";
|
|
283
|
-
import "@ohtaman/washi/solvers/glpk";
|
|
89
|
+
## 🚀 Usage Examples
|
|
284
90
|
|
|
285
|
-
|
|
286
|
-
const x = m.integer("x", { lb: 0 });
|
|
287
|
-
const y = m.integer("y", { lb: 0 });
|
|
91
|
+
### 1. Sums, Dot Products (`sum` / `dot`) & Conditional Constraints
|
|
288
92
|
|
|
289
|
-
|
|
290
|
-
m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
291
|
-
m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
292
|
-
|
|
293
|
-
const status = await m.solve();
|
|
294
|
-
console.log(`Status: ${status}`); // "Optimal"
|
|
295
|
-
console.log(`Objective: ${m.objectiveValue}`); // 1100
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
### 6. Vector Operations & Fast Dot Product
|
|
301
|
-
|
|
302
|
-
Cleanly compute 1D/2D linear combinations without nested loops:
|
|
93
|
+
Combine indexed variables with `dot` / `sum` and express conditional rules with `m.indicator()`:
|
|
303
94
|
|
|
304
95
|
```ts
|
|
305
|
-
import { Model,
|
|
96
|
+
import { Model, expr, minimize } from "@ohtaman/washi";
|
|
306
97
|
|
|
307
|
-
const m = new Model();
|
|
98
|
+
const m = new Model("TransportPlanning");
|
|
308
99
|
const factories = ["Tokyo", "Osaka"];
|
|
309
100
|
const stores = ["StoreA", "StoreB"];
|
|
310
|
-
|
|
311
101
|
const costs = {
|
|
312
102
|
Tokyo: { StoreA: 4, StoreB: 7 },
|
|
313
103
|
Osaka: { StoreA: 8, StoreB: 3 },
|
|
314
104
|
};
|
|
315
105
|
|
|
316
|
-
const
|
|
106
|
+
const open = m.binary(factories);
|
|
107
|
+
const ship = m.continuous(factories, stores, { lb: 0, ub: 500 });
|
|
317
108
|
|
|
318
|
-
//
|
|
319
|
-
m.
|
|
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");
|
|
112
|
+
|
|
113
|
+
m.objective(minimize(expr`dot(${costs}, ${ship})`));
|
|
320
114
|
await m.solve();
|
|
321
115
|
|
|
322
|
-
|
|
323
|
-
const solution = m.getValues(ship);
|
|
324
|
-
console.log(solution);
|
|
116
|
+
console.log("Tokyo -> StoreA:", ship["Tokyo"]["StoreA"].value);
|
|
325
117
|
```
|
|
326
118
|
|
|
327
|
-
|
|
119
|
+
### 2. Convex Quadratic Programming (QP)
|
|
328
120
|
|
|
329
|
-
|
|
121
|
+
Minimize a quadratic objective subject to linear constraints:
|
|
330
122
|
|
|
331
|
-
|
|
123
|
+
```ts
|
|
124
|
+
import { Model, expr, minimize } from "@ohtaman/washi";
|
|
332
125
|
|
|
333
|
-
|
|
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 });
|
|
334
129
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// Define variables, objective and constraints.
|
|
339
|
-
await model.solve();
|
|
340
|
-
```
|
|
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`));
|
|
341
133
|
|
|
342
|
-
|
|
134
|
+
await m.solve();
|
|
135
|
+
console.log(`Tech: ${x.value?.toFixed(2)}, Bonds: ${y.value?.toFixed(2)}`);
|
|
136
|
+
```
|
|
343
137
|
|
|
344
|
-
###
|
|
138
|
+
### 3. SMT Logic & Conflict Diagnosis (`m.check`)
|
|
345
139
|
|
|
346
|
-
|
|
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.
|
|
347
143
|
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const model = new Model();
|
|
352
|
-
// Define variables, objective and constraints.
|
|
353
|
-
await model.solve({ useWorker: false });
|
|
354
|
-
</script>
|
|
355
|
-
```
|
|
144
|
+
```ts
|
|
145
|
+
import { Model, implies, expr } from "@ohtaman/washi";
|
|
146
|
+
// import "@ohtaman/washi/solvers/z3"; // Optional: for exact SMT unsatCore
|
|
356
147
|
|
|
357
|
-
|
|
148
|
+
const m = new Model("StaffingRules");
|
|
149
|
+
const engineers = m.integer("engineers", { lb: 0, ub: 20 });
|
|
150
|
+
const budget = m.continuous("budget", { lb: 0, ub: 100000 });
|
|
151
|
+
const rush = m.bool("rush");
|
|
358
152
|
|
|
359
|
-
|
|
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
|
|
360
158
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const model = new Washi.Model();
|
|
365
|
-
// Define variables, objective and constraints.
|
|
366
|
-
model.solve({ useWorker: false }).then(status => console.log(status));
|
|
367
|
-
</script>
|
|
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"]
|
|
368
162
|
```
|
|
369
163
|
|
|
370
|
-
|
|
164
|
+
---
|
|
371
165
|
|
|
372
|
-
|
|
166
|
+
## 🌐 Browser & Web Worker Usage
|
|
373
167
|
|
|
374
|
-
|
|
375
|
-
- **
|
|
376
|
-
- **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`
|
|
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.
|
|
377
170
|
|
|
378
|
-
#### Browser ESM with GLPK
|
|
379
171
|
```html
|
|
172
|
+
<!-- Browser ESM -->
|
|
380
173
|
<script type="module">
|
|
381
|
-
import { Model
|
|
382
|
-
import "./washi/solvers/glpk.js"
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const x = model.continuous("x", { lb: 0, ub: 10 });
|
|
386
|
-
model.objective(maximize(x));
|
|
387
|
-
await model.solve();
|
|
388
|
-
console.log("GLPK status:", model.status);
|
|
174
|
+
import { Model } from "./washi/washi.js";
|
|
175
|
+
// Optional solver plugins: import "./washi/solvers/glpk.js" or "./washi/solvers/z3.js"
|
|
176
|
+
const m = new Model();
|
|
177
|
+
await m.solve();
|
|
389
178
|
</script>
|
|
390
|
-
```
|
|
391
179
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
<script src="./washi/washi.min.js"></script>
|
|
395
|
-
<script src="./washi/solvers/glpk.min.js"></script>
|
|
396
|
-
<script>
|
|
397
|
-
const model = new Washi.Model({ solver: "glpk" });
|
|
398
|
-
const x = model.continuous("x", { lb: 0, ub: 10 });
|
|
399
|
-
model.objective(Washi.maximize(x));
|
|
400
|
-
model.solve().then(status => console.log("GLPK status:", status));
|
|
401
|
-
</script>
|
|
180
|
+
<!-- Classic Script / CodePen (exposes global Washi) -->
|
|
181
|
+
<script src="https://cdn.jsdelivr.net/npm/@ohtaman/washi/dist/browser/washi.min.js"></script>
|
|
402
182
|
```
|
|
403
183
|
|
|
404
184
|
> [!NOTE]
|
|
405
|
-
> **Z3 in Browser
|
|
406
|
-
> 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`).
|
|
407
|
-
|
|
408
|
-
Complete standalone examples for ESM, script tags, and CodePen are available in [`examples/distribution/`](examples/distribution/README.md).
|
|
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).
|
|
409
186
|
|
|
410
187
|
---
|
|
411
188
|
|
|
412
|
-
##
|
|
413
|
-
|
|
414
|
-
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):
|
|
189
|
+
## 🤝 Contributing
|
|
415
190
|
|
|
416
|
-
|
|
191
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for local development setup, running tests and benchmarks, rebuilding WebAssembly assets, and the release workflow.
|
|
417
192
|
|
|
418
193
|
---
|
|
419
194
|
|
|
420
|
-
##
|
|
195
|
+
## 🙏 Acknowledgments
|
|
421
196
|
|
|
422
|
-
|
|
197
|
+
WaSHI stands on the shoulders of the open-source mathematical optimization and automated reasoning communities:
|
|
423
198
|
|
|
424
|
-
- **
|
|
425
|
-
- **
|
|
426
|
-
- **
|
|
427
|
-
|
|
428
|
-
---
|
|
429
|
-
|
|
430
|
-
## 💻 Examples and browser demo
|
|
431
|
-
|
|
432
|
-
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.
|
|
433
|
-
|
|
434
|
-
| Category | Example | What it demonstrates | Solver |
|
|
435
|
-
| :--- | :--- | :--- | :--- |
|
|
436
|
-
| **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 |
|
|
437
|
-
| **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 |
|
|
438
|
-
| **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 |
|
|
439
|
-
| **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 |
|
|
440
|
-
| **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 |
|
|
441
|
-
| **Decomposition** | [Vehicle routing (CVRP)](https://github.com/ohtaman/washi/blob/main/examples/node/13-vehicle-routing-column-generation.ts) | Column Generation (Dantzig-Wolfe): pricing feasible vehicle routes from dual shadow prices | HiGHS |
|
|
442
|
-
| **Decomposition** | [Traveling salesperson (TSP)](https://github.com/ohtaman/washi/blob/main/examples/node/14-traveling-salesperson-benders.ts) | Benders Decomposition (DFJ): lazy subtour elimination constraints via feasibility cuts | HiGHS |
|
|
443
|
-
| **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 |
|
|
444
|
-
| **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 |
|
|
445
|
-
| **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 |
|
|
446
|
-
|
|
447
|
-
[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.
|
|
448
|
-
|
|
449
|
-
To run examples locally, clone the repository and install its development dependencies first:
|
|
450
|
-
|
|
451
|
-
```bash
|
|
452
|
-
git clone https://github.com/ohtaman/washi.git
|
|
453
|
-
cd washi
|
|
454
|
-
npm ci
|
|
455
|
-
npx tsx examples/node/01-production-planning.ts
|
|
456
|
-
npx tsx examples/node/09-transportation-problem.ts
|
|
457
|
-
npx tsx examples/node/10-diet-sensitivity.ts
|
|
458
|
-
npx tsx examples/node/11-shift-scheduling.ts
|
|
459
|
-
npx tsx examples/node/12-ecommerce-coupon-optimizer.ts
|
|
460
|
-
npx tsx examples/node/13-vehicle-routing-column-generation.ts
|
|
461
|
-
npx tsx examples/node/14-traveling-salesperson-benders.ts
|
|
462
|
-
|
|
463
|
-
# Local browser demo: http://localhost:5173/demo/
|
|
464
|
-
npm run dev
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
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.
|
|
468
|
-
|
|
469
|
-
---
|
|
470
|
-
|
|
471
|
-
## 🧪 Testing & Reproducible Benchmarks
|
|
472
|
-
|
|
473
|
-
Run the repository's unit and integration tests, and build the distributions:
|
|
474
|
-
|
|
475
|
-
```bash
|
|
476
|
-
# Run all unit and integration tests
|
|
477
|
-
npm test
|
|
478
|
-
|
|
479
|
-
# Build production bundle and TypeScript declarations
|
|
480
|
-
npm run build
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
### Optional Reproducible Benchmarks
|
|
484
|
-
|
|
485
|
-
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:
|
|
486
|
-
|
|
487
|
-
```bash
|
|
488
|
-
# Run Node.js in-process WASM benchmarks (HiGHS and GLPK, 5 warm iterations by default)
|
|
489
|
-
npm run bench
|
|
490
|
-
|
|
491
|
-
# Customize iteration count or emit structured JSON
|
|
492
|
-
node scripts/run-benchmarks.mjs --iterations=10 --json
|
|
493
|
-
|
|
494
|
-
# Also measure headless Chromium Web Worker execution (requires Playwright Chromium)
|
|
495
|
-
node scripts/run-benchmarks.mjs --browser
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
- **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".
|
|
499
|
-
- **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.
|
|
500
|
-
|
|
501
|
-
### Rebuilding HiGHS WebAssembly & Release Workflow
|
|
502
|
-
|
|
503
|
-
1. **Rebuilding / Updating HiGHS WebAssembly Assets (`highs.config.json`)**:
|
|
504
|
-
```bash
|
|
505
|
-
# Compile ERGO-Code/HiGHS to public/highs.wasm + runtime.js and update SHA-256 hashes in highs.config.json
|
|
506
|
-
npm run build:highs-wasm
|
|
507
|
-
|
|
508
|
-
# After merging to main, upload the updated assets to the highs-<version> GitHub Release tag
|
|
509
|
-
# so on-demand downloads (scripts/ensure-highs-wasm.mjs) match highs.config.json SHA-256 hashes:
|
|
510
|
-
gh release upload highs-v1.15.1 public/highs.wasm src/modeler/solvers/highs/runtime.js --clobber
|
|
511
|
-
```
|
|
512
|
-
Alternatively, trigger the **Build HiGHS WebAssembly** workflow (`.github/workflows/build-wasm.yml`) via `workflow_dispatch` with `create_release: true`, then download the `highs-wasm-assets` workflow artifact (`public/highs.wasm`, `src/modeler/solvers/highs/runtime.js`, and `highs.config.json`) and commit those updated files to `main` so repository checksums stay synchronized with the published Release assets.
|
|
513
|
-
|
|
514
|
-
2. **Publishing `@ohtaman/washi` to npm**:
|
|
515
|
-
- Merging to `main` automatically deploys the updated documentation and web demo to GitHub Pages (`.github/workflows/deploy-demo.yml`), but does **not** publish to npm automatically.
|
|
516
|
-
- To publish a new version to npm, bump `"version"` in `package.json` on `main` and publish a GitHub Release (e.g., `gh release create v0.5.1 --generate-notes`). Publishing the GitHub Release triggers `.github/workflows/publish-npm.yml`, which runs unit and Playwright distribution checks before publishing to npm via OIDC Trusted Publishing.
|
|
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.
|
|
517
203
|
|
|
518
204
|
---
|
|
519
205
|
|
|
520
206
|
## 📄 License
|
|
521
207
|
|
|
522
208
|
- **`@ohtaman/washi`**: [MIT License](./LICENSE)
|
|
523
|
-
- **HiGHS
|
|
524
|
-
- **Z3** (optional plugin via [`z3-solver`](https://www.npmjs.com/package/z3-solver)): MIT License
|
|
525
|
-
- **GLPK** (optional plugin via [`glpk.js`](https://www.npmjs.com/package/glpk.js)): GPL-3.0 License
|
|
526
|
-
|
|
527
|
-
See [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md) for full attribution and license details of bundled/peer optimization engines.
|
|
209
|
+
- **Bundled / Optional Engines**: HiGHS (MIT), Z3 (MIT), GLPK (GPL-3.0). See [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md) for full details.
|