@ohtaman/washi 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.ja.md +381 -0
- package/README.md +380 -2
- package/README.zh-CN.md +381 -0
- package/THIRD_PARTY_LICENSES.md +83 -0
- package/dist/lib/assets/highs-worker-DeDWU6KI.js +3 -0
- package/dist/lib/demo/highs.wasm +0 -0
- package/dist/lib/glpk-BC32ZaPG.js +126 -0
- package/dist/lib/highs.wasm +0 -0
- package/dist/lib/highs.wasm.LICENSE +22 -0
- package/dist/lib/solvers/glpk.js +2 -0
- package/dist/lib/solvers/z3.js +2 -0
- package/dist/lib/washi-logo.png +0 -0
- package/dist/lib/washi-logo.svg +70 -0
- package/dist/lib/washi.js +908 -0
- package/dist/lib/z3-C8g-PtmP.js +582 -0
- package/dist/types/glpk-loader.d.ts +8 -0
- package/dist/types/glpk-loader.d.ts.map +1 -0
- package/dist/types/highs-loader.d.ts +55 -0
- package/dist/types/highs-loader.d.ts.map +1 -0
- package/dist/types/highs-worker-client.d.ts +12 -0
- package/dist/types/highs-worker-client.d.ts.map +1 -0
- package/dist/types/highs-worker.d.ts +2 -0
- package/dist/types/highs-worker.d.ts.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/modeler/boolean-expr.d.ts +38 -0
- package/dist/types/modeler/boolean-expr.d.ts.map +1 -0
- package/dist/types/modeler/constraint.d.ts +28 -0
- package/dist/types/modeler/constraint.d.ts.map +1 -0
- package/dist/types/modeler/expression.d.ts +79 -0
- package/dist/types/modeler/expression.d.ts.map +1 -0
- package/dist/types/modeler/index.d.ts +14 -0
- package/dist/types/modeler/index.d.ts.map +1 -0
- package/dist/types/modeler/lp-writer.d.ts +30 -0
- package/dist/types/modeler/lp-writer.d.ts.map +1 -0
- package/dist/types/modeler/mip-compat.d.ts +43 -0
- package/dist/types/modeler/mip-compat.d.ts.map +1 -0
- package/dist/types/modeler/model.d.ts +156 -0
- package/dist/types/modeler/model.d.ts.map +1 -0
- package/dist/types/modeler/parser.d.ts +27 -0
- package/dist/types/modeler/parser.d.ts.map +1 -0
- package/dist/types/modeler/pulp-compat.d.ts +67 -0
- package/dist/types/modeler/pulp-compat.d.ts.map +1 -0
- package/dist/types/modeler/quad-expr.d.ts +55 -0
- package/dist/types/modeler/quad-expr.d.ts.map +1 -0
- package/dist/types/modeler/solvers/glpk.d.ts +22 -0
- package/dist/types/modeler/solvers/glpk.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs.d.ts +26 -0
- package/dist/types/modeler/solvers/highs.d.ts.map +1 -0
- package/dist/types/modeler/solvers/index.d.ts +7 -0
- package/dist/types/modeler/solvers/index.d.ts.map +1 -0
- package/dist/types/modeler/solvers/mock.d.ts +25 -0
- package/dist/types/modeler/solvers/mock.d.ts.map +1 -0
- package/dist/types/modeler/solvers/registry.d.ts +20 -0
- package/dist/types/modeler/solvers/registry.d.ts.map +1 -0
- package/dist/types/modeler/solvers/types.d.ts +86 -0
- package/dist/types/modeler/solvers/types.d.ts.map +1 -0
- package/dist/types/modeler/solvers/z3.d.ts +37 -0
- package/dist/types/modeler/solvers/z3.d.ts.map +1 -0
- package/dist/types/modeler/types.d.ts +27 -0
- package/dist/types/modeler/types.d.ts.map +1 -0
- package/dist/types/modeler/variable.d.ts +52 -0
- package/dist/types/modeler/variable.d.ts.map +1 -0
- package/package.json +98 -4
package/README.md
CHANGED
|
@@ -1,3 +1,381 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="./assets/washi-header-dark.png">
|
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="./assets/washi-header-light.png">
|
|
5
|
+
<img alt="WaSHI - Algebraic Modeler for TypeScript & JavaScript" src="./assets/washi-header-light.png" width="420" />
|
|
6
|
+
</picture>
|
|
7
|
+
<p><strong>Intuitive Mathematical Optimization & Algebraic Modeler for TypeScript & JavaScript</strong></p>
|
|
8
|
+
<p>
|
|
9
|
+
<em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Quadratic Programming (QP) • SMT & Conflict Diagnosis</em>
|
|
10
|
+
</p>
|
|
11
|
+
<p>
|
|
12
|
+
<a href="./README.md">English</a> •
|
|
13
|
+
<a href="./README.ja.md">日本語</a> •
|
|
14
|
+
<a href="./README.zh-CN.md">简体中文</a>
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
<a href="https://ohtaman.github.io/washi/"><img src="https://img.shields.io/badge/docs-online-blue" alt="docs" /></a>
|
|
18
|
+
<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://www.npmjs.com/package/@ohtaman/washi"><img src="https://img.shields.io/npm/v/@ohtaman/washi?color=crimson&label=npm" alt="npm version" /></a>
|
|
20
|
+
<a href="https://bundlephobia.com/package/@ohtaman/washi"><img src="https://img.shields.io/badge/bundle%20size-7.2%20kB%20(gzip)-blue" alt="bundle size" /></a>
|
|
21
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license" /></a>
|
|
22
|
+
<img src="https://img.shields.io/badge/types-TypeScript-blue" alt="TypeScript types" />
|
|
23
|
+
<img src="https://img.shields.io/badge/tests-120%2B%20passing-brightgreen" alt="tests" />
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
2
26
|
|
|
3
|
-
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
**WaSHI** is a high-level algebraic modeling library that brings the expressive elegance of Python's **PuLP** and Julia's **JuMP** to the modern TypeScript and JavaScript ecosystem. Formulate mathematical models using natural algebraic equations and solve them instantly across Node.js, browsers, Bun, Deno, and Next.js—powered natively by WebAssembly solvers (**HiGHS**, **Z3**, and **GLPK**) with zero native compilation steps.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Model, expr, c } from "@ohtaman/washi";
|
|
33
|
+
|
|
34
|
+
const m = new Model({ sense: "Maximize" });
|
|
35
|
+
const x = m.addVar({ type: "Integer", lb: 0 });
|
|
36
|
+
const y = m.addVar({ type: "Integer", lb: 0 });
|
|
37
|
+
|
|
38
|
+
m.maximize(expr`30 * ${x} + 50 * ${y}`);
|
|
39
|
+
m.addConstraint(c`${x} + 2 * ${y} <= 40`, "wood_limit");
|
|
40
|
+
m.addConstraint(c`2 * ${x} + ${y} <= 50`, "labor_limit");
|
|
41
|
+
|
|
42
|
+
await m.solve();
|
|
43
|
+
console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🎯 Why WaSHI?
|
|
49
|
+
|
|
50
|
+
1. 🌐 **100% Client-Side & Zero Server Cost**: Run industrial-grade optimization entirely inside the user's browser, edge worker, or Electron app. Zero backend server costs, offline-ready, and 100% private (confidential business data never leaves the client device).
|
|
51
|
+
2. 📦 **Frictionless Zero-Dependency Distribution**: No Python runtimes, no C/C++ compilation, and no OS-dependent native solver binaries to bundle. Just `npm install @ohtaman/washi`—everything (including WebAssembly solvers) works instantly out of the box.
|
|
52
|
+
3. ✍️ **Familiar Algebraic Modeling (PuLP & python-mip DX)**: Never struggle with low-level matrix indices. Formulate models naturally using tagged template literals (`c`${x} + 2 * ${y} <= 40``) or drop-in PuLP/python-mip syntax (`LpProblem`, `lpSum`).
|
|
53
|
+
4. 🔄 **Pluggable Solvers & Unified API**: Formulate your mathematical model once; solve with **HiGHS** (industrial MIP/QP), **Z3** (SMT logic & Unsat Core diagnosis), or **GLPK** with a single parameter.
|
|
54
|
+
5. 🔍 **Automatic Conflict Diagnosis (`unsatCore`)**: When constraints are contradictory (Infeasible), diagnose root causes instantly by extracting the minimal conflicting constraint subset via the Z3 engine.
|
|
55
|
+
6. ⚡ **Industrial Scale Performance in WebAssembly**: Built with an $O(N)$ linear-time expression builder. Formulate 20,000+ variable MIPs in ~100ms and solve them in ~15 seconds right inside WebAssembly via HiGHS.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## ⚡ Solvers Matrix
|
|
60
|
+
|
|
61
|
+
| Solver | Engine License | Problem Classes | Best For | Status in WaSHI |
|
|
62
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
63
|
+
| **HiGHS** | **MIT** | **LP, MIP, Convex QP** | Industrial-scale optimization, production planning, finance | **Built-in default** (zero-config) |
|
|
64
|
+
| **Z3** | **MIT** | **SMT, SAT, Logic, MIP** | Propositional logic, conflict diagnosis (`unsatCore`), rule engines | Optional plugin (`@ohtaman/washi/solvers/z3`) |
|
|
65
|
+
| **GLPK** | **GPL-3.0** | **LP, MIP** | Academic benchmarks, legacy GLPK compatibility | Optional plugin (`@ohtaman/washi/solvers/glpk`) |
|
|
66
|
+
| **Mock** | **MIT** | *Any* | Ultra-fast unit testing in CI without loading WebAssembly | Built-in |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 📦 Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# npm
|
|
74
|
+
npm install @ohtaman/washi
|
|
75
|
+
|
|
76
|
+
# pnpm
|
|
77
|
+
pnpm add @ohtaman/washi
|
|
78
|
+
|
|
79
|
+
# yarn
|
|
80
|
+
yarn add @ohtaman/washi
|
|
81
|
+
|
|
82
|
+
# bun
|
|
83
|
+
bun add @ohtaman/washi
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
*(Optional)* To use Z3 or GLPK solvers, install their respective packages:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm install z3-solver # For SMT and unsat core diagnosis
|
|
90
|
+
npm install glpk.js # For GLPK solver (GPL-3.0)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 🚀 Usage Guide
|
|
96
|
+
|
|
97
|
+
### 1. Mixed-Integer Linear Programming (MIP)
|
|
98
|
+
|
|
99
|
+
Formulate production scheduling, resource allocation, and facility location problems:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { Model, c, expr } from "@ohtaman/washi";
|
|
103
|
+
|
|
104
|
+
const m = new Model({ name: "FactoryPlanning", sense: "Maximize" });
|
|
105
|
+
|
|
106
|
+
// Decision variables (Integer, non-negative)
|
|
107
|
+
const chairs = m.addVar({ name: "chairs", type: "Integer", lb: 0 });
|
|
108
|
+
const tables = m.addVar({ name: "tables", type: "Integer", lb: 0 });
|
|
109
|
+
|
|
110
|
+
// Natural formula objective & constraints via Tagged Template Literals
|
|
111
|
+
m.maximize(expr`30 * ${chairs} + 50 * ${tables}`);
|
|
112
|
+
m.addConstraint(c`${chairs} + 2 * ${tables} <= 40`, "wood_limit");
|
|
113
|
+
m.addConstraint(c`2 * ${chairs} + ${tables} <= 50`, "labor_limit");
|
|
114
|
+
|
|
115
|
+
// Solve using default HiGHS WASM engine
|
|
116
|
+
const status = await m.solve();
|
|
117
|
+
|
|
118
|
+
console.log(`Status: ${status}`); // "Optimal"
|
|
119
|
+
console.log(`Objective: $${m.objectiveValue}`); // 1100
|
|
120
|
+
console.log(`Chairs: ${chairs.value}, Tables: ${tables.value}`); // 20 chairs, 10 tables
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### 🗂 Multidimensional Variables Grid (`addVars`)
|
|
124
|
+
|
|
125
|
+
Construct variable arrays or multi-index matrices in one line (similar to PuLP's `LpVariable.dicts`):
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// 1D dictionary of binary variables
|
|
129
|
+
const openCity = m.addVars(["Tokyo", "Osaka", "Nagoya"], { type: "Binary" });
|
|
130
|
+
|
|
131
|
+
// 2D transport flow matrix: [Factory] x [Store]
|
|
132
|
+
const flow = m.addVars(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0 });
|
|
133
|
+
|
|
134
|
+
// 2D coordinate grid (e.g. 8x8 chessboard)
|
|
135
|
+
const board = m.addVars(8, 8, { type: "Binary", prefix: "queen" });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### 🔀 Big-M Indicator Constraints (`addIndicator`)
|
|
139
|
+
|
|
140
|
+
Model conditional activation rules and fixed-charge economics without manual Big-M algebra:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// If factory is closed (openCity['Tokyo'] == 0), its production capacity must be 0
|
|
144
|
+
m.addIndicator(openCity["Tokyo"], flow["F1"]["StoreA"].le(0), {
|
|
145
|
+
activeValue: 0,
|
|
146
|
+
M: 1000,
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### 2. Portfolio Risk Minimization (Convex Quadratic Programming)
|
|
153
|
+
|
|
154
|
+
Minimize investment risk based on a covariance matrix subject to return and budget constraints:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { Model, QuadExpr, sum } from "@ohtaman/washi";
|
|
158
|
+
|
|
159
|
+
const m = new Model({ name: "MarkowitzPortfolio", sense: "Minimize" });
|
|
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 = assets.map((a) => m.addVar({ name: `w_${a}`, lb: 0, ub: 1 }));
|
|
165
|
+
|
|
166
|
+
// 1. Fully invested budget: sum(w) == 1.0
|
|
167
|
+
m.addConstraint(sum(w).eq(1.0), "budget");
|
|
168
|
+
|
|
169
|
+
// 2. Minimum target return: portfolio return >= 8.5%
|
|
170
|
+
m.addConstraint(sum(w.map((v, i) => v.mul(returns[i]))).ge(0.085), "min_return");
|
|
171
|
+
|
|
172
|
+
// 3. Covariance risk matrix
|
|
173
|
+
const cov = [
|
|
174
|
+
[0.040, 0.002, 0.015],
|
|
175
|
+
[0.002, 0.005, 0.002],
|
|
176
|
+
[0.015, 0.002, 0.025],
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
// Quadratic objective: w^T * Cov * w
|
|
180
|
+
let risk = new QuadExpr();
|
|
181
|
+
for (let i = 0; i < assets.length; i++) {
|
|
182
|
+
for (let j = 0; j < assets.length; j++) {
|
|
183
|
+
risk = risk.add(w[i].mul(w[j]).mul(cov[i][j]));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
m.minimize(risk);
|
|
187
|
+
|
|
188
|
+
await m.solve();
|
|
189
|
+
console.log(`Minimal Variance: ${m.objectiveValue?.toFixed(6)}`);
|
|
190
|
+
assets.forEach((name, i) => {
|
|
191
|
+
console.log(` ${name}: ${((w[i].value || 0) * 100).toFixed(2)}%`);
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### 3. SMT Logic & Rule Engines (with Z3)
|
|
198
|
+
|
|
199
|
+
Express boolean variables, propositional connectives (`and`, `or`, `implies`, `not`, `xor`), and mixed arithmetic rules:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { Model, not } from "@ohtaman/washi";
|
|
203
|
+
|
|
204
|
+
const m = new Model({ name: "SmartSecurityRules" });
|
|
205
|
+
|
|
206
|
+
const isNight = m.addBoolVar("isNight");
|
|
207
|
+
const motion = m.addBoolVar("motion");
|
|
208
|
+
const alarm = m.addBoolVar("alarm");
|
|
209
|
+
const siren = m.addBoolVar("siren");
|
|
210
|
+
const lux = m.addVar({ name: "lux", type: "Integer", lb: 0 });
|
|
211
|
+
|
|
212
|
+
// Logical rules
|
|
213
|
+
m.addConstraint(lux.le(50).implies(isNight), "dark_implies_night");
|
|
214
|
+
m.addConstraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
|
|
215
|
+
|
|
216
|
+
// Current sensor observations
|
|
217
|
+
m.addConstraint(lux.le(10), "sensor_dark");
|
|
218
|
+
m.addConstraint(motion, "sensor_motion");
|
|
219
|
+
m.addConstraint(alarm, "alarm_on");
|
|
220
|
+
|
|
221
|
+
// Check satisfiability and deduce state without an objective function
|
|
222
|
+
const res = await m.check({ solver: "z3" });
|
|
223
|
+
|
|
224
|
+
console.log(`Satisfiable? ${res.status === "Sat"}`); // true
|
|
225
|
+
console.log(`Siren active? ${m.values.get("siren")}`); // true
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### 4. Conflict Diagnosis (`unsatCore`)
|
|
231
|
+
|
|
232
|
+
When business rules or constraints contradict each other, extract the **minimal conflicting subset** for instant troubleshooting (requires Z3):
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { Model } from "@ohtaman/washi";
|
|
236
|
+
import "@ohtaman/washi/solvers/z3";
|
|
237
|
+
|
|
238
|
+
const m = new Model({ name: "ConflictDiagnosis", solver: "z3" });
|
|
239
|
+
const engineers = m.addVar({ name: "engineers", type: "Integer", lb: 0, ub: 20 });
|
|
240
|
+
const budget = m.addVar({ name: "budget", type: "Continuous", lb: 0, ub: 100000 });
|
|
241
|
+
const rushDelivery = m.addBoolVar("rushDelivery");
|
|
242
|
+
|
|
243
|
+
// Rule 1: Each engineer costs $8,000
|
|
244
|
+
m.addConstraint(engineers.mul(8000).le(budget), "budget_limit");
|
|
245
|
+
|
|
246
|
+
// Rule 2: Customer requires rush delivery
|
|
247
|
+
m.addConstraint(rushDelivery, "rush_required");
|
|
248
|
+
|
|
249
|
+
// Rule 3: Rush delivery requires at least 8 engineers
|
|
250
|
+
m.addConstraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
|
|
251
|
+
|
|
252
|
+
// Rule 4: Hard board budget cap of $50,000 (Conflict: 8 * $8000 = $64,000 > $50,000!)
|
|
253
|
+
m.addConstraint(budget.le(50000), "strict_board_budget_cap");
|
|
254
|
+
|
|
255
|
+
// Rule 5: Innocent bystander rule
|
|
256
|
+
m.addConstraint(engineers.ge(1), "minimum_one_employee");
|
|
257
|
+
|
|
258
|
+
const res = await m.check({ solver: "z3" });
|
|
259
|
+
|
|
260
|
+
console.log(`Status: ${res.status}`); // "Unsat"
|
|
261
|
+
console.log("Conflicting constraints:", res.unsatCore);
|
|
262
|
+
// Output: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
|
|
263
|
+
// Note: "minimum_one_employee" is correctly excluded!
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
### 5. Python PuLP Compatibility Layer
|
|
269
|
+
|
|
270
|
+
If you have existing Python PuLP code, port it with near zero friction:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import {
|
|
274
|
+
LpProblem,
|
|
275
|
+
LpVariable,
|
|
276
|
+
LpMaximize,
|
|
277
|
+
LpInteger,
|
|
278
|
+
HiGHS,
|
|
279
|
+
value,
|
|
280
|
+
} from "@ohtaman/washi";
|
|
281
|
+
|
|
282
|
+
const prob = new LpProblem("PuLPDemo", LpMaximize);
|
|
283
|
+
|
|
284
|
+
const x = new LpVariable("x", 0, undefined, LpInteger);
|
|
285
|
+
const y = new LpVariable("y", 0, undefined, LpInteger);
|
|
286
|
+
|
|
287
|
+
// PuLP += equivalent
|
|
288
|
+
prob.add(x.mul(30).add(y.mul(50)));
|
|
289
|
+
prob.add(x.add(y.mul(2)).le(40));
|
|
290
|
+
prob.add(x.mul(2).add(y).le(50));
|
|
291
|
+
|
|
292
|
+
await prob.solve(new HiGHS());
|
|
293
|
+
|
|
294
|
+
console.log(`Status: ${prob.status}`);
|
|
295
|
+
console.log(`Objective: ${value(prob.objective)}`);
|
|
296
|
+
console.log(`x = ${value(x)}, y = ${value(y)}`);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 🌐 Browser & Web Worker Usage
|
|
302
|
+
|
|
303
|
+
WaSHI is engineered for seamless execution in modern browsers, frontend frameworks (Vite, Next.js, React, Vue, Svelte), and Web Workers.
|
|
304
|
+
|
|
305
|
+
### 1. Zero-Config Mode (Automatic CDN Fallback)
|
|
306
|
+
In browser environments, if `highs.wasm` is not found on your local origin, WaSHI automatically falls back to the official jsDelivr CDN. You can optimize without copying files or writing boilerplate:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { Model } from "@ohtaman/washi";
|
|
310
|
+
|
|
311
|
+
const model = new Model();
|
|
312
|
+
// ... define variables & constraints
|
|
313
|
+
await model.solve(); // Works instantly with zero setup!
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 2. Bundler-Native Asset Hosting (Vite / Webpack / Rollup)
|
|
317
|
+
To bundle and self-host `highs.wasm` as a versioned, hashed static asset with your application:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import wasmUrl from "@ohtaman/washi/highs.wasm?url";
|
|
321
|
+
import { setHighsWasmUrl } from "@ohtaman/washi";
|
|
322
|
+
|
|
323
|
+
// Configure once at app startup
|
|
324
|
+
setHighsWasmUrl(wasmUrl);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 3. Air-Gapped & Enterprise Offline Environments
|
|
328
|
+
In strict enterprise intranet or air-gapped environments where external CDN network access is disallowed:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { setHighsWasmUrl } from "@ohtaman/washi";
|
|
332
|
+
|
|
333
|
+
// Point strictly to your internal assets (completely disables CDN fallback)
|
|
334
|
+
setHighsWasmUrl("/static/highs.wasm");
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## 💻 CLI & Interactive Demos
|
|
340
|
+
|
|
341
|
+
Ready-to-run standalone examples are available in `examples/node/`:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
npx tsx examples/node/01-production-planning.ts
|
|
345
|
+
npx tsx examples/node/02-knapsack-problem.ts
|
|
346
|
+
npx tsx examples/node/03-portfolio-qp.ts
|
|
347
|
+
npx tsx examples/node/04-smt-security-rules.ts
|
|
348
|
+
npx tsx examples/node/05-unsat-core-diagnosis.ts
|
|
349
|
+
npx tsx examples/node/06-pulp-compatibility.ts
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
To run the interactive browser showcase (`examples/web-demo`):
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
npm run dev
|
|
356
|
+
# Documentation: http://localhost:5173/
|
|
357
|
+
# Interactive Demo: http://localhost:5173/demo/
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## 🧪 Testing
|
|
363
|
+
|
|
364
|
+
WaSHI is developed with strict Test-Driven Development (TDD) across 120+ unit, integration, and large-scale test cases:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# Run all unit and integration tests
|
|
368
|
+
npm test
|
|
369
|
+
|
|
370
|
+
# Build production bundle and TypeScript declarations
|
|
371
|
+
npm run build
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## 📄 License
|
|
377
|
+
|
|
378
|
+
- **`@ohtaman/washi`**: [MIT License](./LICENSE)
|
|
379
|
+
- **HiGHS** (default engine): MIT License
|
|
380
|
+
- **Z3** (optional plugin): MIT License
|
|
381
|
+
- **GLPK** (optional plugin): GPL-3.0 License
|