@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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +381 -0
  3. package/README.md +380 -2
  4. package/README.zh-CN.md +381 -0
  5. package/THIRD_PARTY_LICENSES.md +83 -0
  6. package/dist/lib/assets/highs-worker-DeDWU6KI.js +3 -0
  7. package/dist/lib/demo/highs.wasm +0 -0
  8. package/dist/lib/glpk-BC32ZaPG.js +126 -0
  9. package/dist/lib/highs.wasm +0 -0
  10. package/dist/lib/highs.wasm.LICENSE +22 -0
  11. package/dist/lib/solvers/glpk.js +2 -0
  12. package/dist/lib/solvers/z3.js +2 -0
  13. package/dist/lib/washi-logo.png +0 -0
  14. package/dist/lib/washi-logo.svg +70 -0
  15. package/dist/lib/washi.js +908 -0
  16. package/dist/lib/z3-C8g-PtmP.js +582 -0
  17. package/dist/types/glpk-loader.d.ts +8 -0
  18. package/dist/types/glpk-loader.d.ts.map +1 -0
  19. package/dist/types/highs-loader.d.ts +55 -0
  20. package/dist/types/highs-loader.d.ts.map +1 -0
  21. package/dist/types/highs-worker-client.d.ts +12 -0
  22. package/dist/types/highs-worker-client.d.ts.map +1 -0
  23. package/dist/types/highs-worker.d.ts +2 -0
  24. package/dist/types/highs-worker.d.ts.map +1 -0
  25. package/dist/types/index.d.ts +14 -0
  26. package/dist/types/index.d.ts.map +1 -0
  27. package/dist/types/modeler/boolean-expr.d.ts +38 -0
  28. package/dist/types/modeler/boolean-expr.d.ts.map +1 -0
  29. package/dist/types/modeler/constraint.d.ts +28 -0
  30. package/dist/types/modeler/constraint.d.ts.map +1 -0
  31. package/dist/types/modeler/expression.d.ts +79 -0
  32. package/dist/types/modeler/expression.d.ts.map +1 -0
  33. package/dist/types/modeler/index.d.ts +14 -0
  34. package/dist/types/modeler/index.d.ts.map +1 -0
  35. package/dist/types/modeler/lp-writer.d.ts +30 -0
  36. package/dist/types/modeler/lp-writer.d.ts.map +1 -0
  37. package/dist/types/modeler/mip-compat.d.ts +43 -0
  38. package/dist/types/modeler/mip-compat.d.ts.map +1 -0
  39. package/dist/types/modeler/model.d.ts +156 -0
  40. package/dist/types/modeler/model.d.ts.map +1 -0
  41. package/dist/types/modeler/parser.d.ts +27 -0
  42. package/dist/types/modeler/parser.d.ts.map +1 -0
  43. package/dist/types/modeler/pulp-compat.d.ts +67 -0
  44. package/dist/types/modeler/pulp-compat.d.ts.map +1 -0
  45. package/dist/types/modeler/quad-expr.d.ts +55 -0
  46. package/dist/types/modeler/quad-expr.d.ts.map +1 -0
  47. package/dist/types/modeler/solvers/glpk.d.ts +22 -0
  48. package/dist/types/modeler/solvers/glpk.d.ts.map +1 -0
  49. package/dist/types/modeler/solvers/highs.d.ts +26 -0
  50. package/dist/types/modeler/solvers/highs.d.ts.map +1 -0
  51. package/dist/types/modeler/solvers/index.d.ts +7 -0
  52. package/dist/types/modeler/solvers/index.d.ts.map +1 -0
  53. package/dist/types/modeler/solvers/mock.d.ts +25 -0
  54. package/dist/types/modeler/solvers/mock.d.ts.map +1 -0
  55. package/dist/types/modeler/solvers/registry.d.ts +20 -0
  56. package/dist/types/modeler/solvers/registry.d.ts.map +1 -0
  57. package/dist/types/modeler/solvers/types.d.ts +86 -0
  58. package/dist/types/modeler/solvers/types.d.ts.map +1 -0
  59. package/dist/types/modeler/solvers/z3.d.ts +37 -0
  60. package/dist/types/modeler/solvers/z3.d.ts.map +1 -0
  61. package/dist/types/modeler/types.d.ts +27 -0
  62. package/dist/types/modeler/types.d.ts.map +1 -0
  63. package/dist/types/modeler/variable.d.ts +52 -0
  64. package/dist/types/modeler/variable.d.ts.map +1 -0
  65. package/package.json +98 -4
package/README.md CHANGED
@@ -1,3 +1,381 @@
1
- # Temporary Holding Version
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 &amp; JavaScript" src="./assets/washi-header-light.png" width="420" />
6
+ </picture>
7
+ <p><strong>Intuitive Mathematical Optimization &amp; Algebraic Modeler for TypeScript &amp; JavaScript</strong></p>
8
+ <p>
9
+ <em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Quadratic Programming (QP) • SMT &amp; 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
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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