@ohtaman/washi 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.ja.md +149 -121
  2. package/README.md +149 -121
  3. package/README.zh-CN.md +150 -122
  4. package/THIRD_PARTY_LICENSES.md +3 -4
  5. package/dist/browser/_washi-node-only-BvRk9kiK.js +0 -0
  6. package/dist/browser/browser-runtime-TLCY0hkc.js +1793 -0
  7. package/dist/browser/highs-worker.js +21 -3
  8. package/dist/browser/highs.wasm +0 -0
  9. package/dist/browser/highs.wasm.LICENSE +1 -2
  10. package/dist/browser/loader-DewlSho8.js +643 -0
  11. package/dist/browser/solvers/glpk.js +433 -286
  12. package/dist/browser/solvers/glpk.min.js +1 -1
  13. package/dist/browser/solvers/z3.js +1008 -981
  14. package/dist/browser/solvers/z3.min.js +2 -2
  15. package/dist/browser/washi.js +2778 -673
  16. package/dist/browser/washi.min.js +3 -4
  17. package/dist/lib/_washi-node-only-BvRk9kiK.js +0 -0
  18. package/dist/lib/highs-browser-runtime.js +2120 -4124
  19. package/dist/lib/highs-node-runtime.js +24 -1
  20. package/dist/lib/highs-runtime.js +2 -0
  21. package/dist/lib/highs-worker.js +21 -3
  22. package/dist/lib/highs.wasm +0 -0
  23. package/dist/lib/highs.wasm.LICENSE +1 -2
  24. package/dist/lib/loader-BZUQNvh4.js +649 -0
  25. package/dist/lib/registry-C20sxTwm.js +964 -0
  26. package/dist/lib/solvers/glpk.js +201 -90
  27. package/dist/lib/solvers/z3.js +399 -259
  28. package/dist/lib/washi.js +2363 -1056
  29. package/dist/types/decomposition/benders.d.ts +32 -0
  30. package/dist/types/decomposition/benders.d.ts.map +1 -0
  31. package/dist/types/decomposition/column-generation.d.ts +34 -0
  32. package/dist/types/decomposition/column-generation.d.ts.map +1 -0
  33. package/dist/types/decomposition/index.d.ts +4 -0
  34. package/dist/types/decomposition/index.d.ts.map +1 -0
  35. package/dist/types/decomposition/types.d.ts +230 -0
  36. package/dist/types/decomposition/types.d.ts.map +1 -0
  37. package/dist/types/index.d.ts +5 -4
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/modeler/big-m.d.ts +75 -0
  40. package/dist/types/modeler/big-m.d.ts.map +1 -0
  41. package/dist/types/modeler/boolean-expr.d.ts +103 -14
  42. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  43. package/dist/types/modeler/constraint.d.ts +47 -9
  44. package/dist/types/modeler/constraint.d.ts.map +1 -1
  45. package/dist/types/modeler/expression.d.ts +123 -28
  46. package/dist/types/modeler/expression.d.ts.map +1 -1
  47. package/dist/types/modeler/index.d.ts +11 -6
  48. package/dist/types/modeler/index.d.ts.map +1 -1
  49. package/dist/types/modeler/linearizer.d.ts +15 -0
  50. package/dist/types/modeler/linearizer.d.ts.map +1 -0
  51. package/dist/types/modeler/lp-writer.d.ts +30 -3
  52. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  53. package/dist/types/modeler/model.d.ts +623 -131
  54. package/dist/types/modeler/model.d.ts.map +1 -1
  55. package/dist/types/modeler/objective.d.ts +59 -0
  56. package/dist/types/modeler/objective.d.ts.map +1 -0
  57. package/dist/types/modeler/parser.d.ts +34 -10
  58. package/dist/types/modeler/parser.d.ts.map +1 -1
  59. package/dist/types/modeler/quad-expr.d.ts +26 -7
  60. package/dist/types/modeler/quad-expr.d.ts.map +1 -1
  61. package/dist/types/modeler/solution-validator.d.ts +93 -0
  62. package/dist/types/modeler/solution-validator.d.ts.map +1 -0
  63. package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
  64. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  65. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -1
  66. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -1
  67. package/dist/types/modeler/solvers/highs/driver.d.ts +232 -0
  68. package/dist/types/modeler/solvers/highs/driver.d.ts.map +1 -0
  69. package/dist/types/modeler/solvers/highs/index.d.ts +1 -0
  70. package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -1
  71. package/dist/types/modeler/solvers/highs/loader.d.ts +38 -13
  72. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  73. package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -1
  74. package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -1
  75. package/dist/types/modeler/solvers/highs/solver.d.ts +12 -5
  76. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  77. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  78. package/dist/types/modeler/solvers/mock.d.ts +14 -5
  79. package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
  80. package/dist/types/modeler/solvers/types.d.ts +73 -11
  81. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  82. package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
  83. package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
  84. package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
  85. package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
  86. package/dist/types/modeler/types.d.ts +86 -1
  87. package/dist/types/modeler/types.d.ts.map +1 -1
  88. package/dist/types/modeler/validation.d.ts +56 -0
  89. package/dist/types/modeler/validation.d.ts.map +1 -0
  90. package/dist/types/modeler/variable.d.ts +48 -14
  91. package/dist/types/modeler/variable.d.ts.map +1 -1
  92. package/docs/examples/diet-sensitivity.md +90 -0
  93. package/docs/examples/ecommerce-coupons.md +106 -0
  94. package/docs/examples/index.md +57 -0
  95. package/docs/examples/knapsack.md +60 -0
  96. package/docs/examples/portfolio-qp.md +83 -0
  97. package/docs/examples/production.md +51 -0
  98. package/docs/examples/shift-scheduling.md +175 -0
  99. package/docs/examples/smt-security.md +74 -0
  100. package/docs/examples/transportation.md +90 -0
  101. package/docs/examples/traveling-salesperson.md +173 -0
  102. package/docs/examples/unsat-core.md +64 -0
  103. package/docs/examples/vehicle-routing.md +206 -0
  104. package/docs/getting-started/ai-agents.md +271 -0
  105. package/docs/getting-started/cross-origin.md +87 -0
  106. package/docs/getting-started/index.md +116 -0
  107. package/docs/getting-started/runtimes.md +227 -0
  108. package/docs/guide/constraints.md +177 -0
  109. package/docs/guide/decomposition.md +399 -0
  110. package/docs/guide/expressions.md +204 -0
  111. package/docs/guide/objectives.md +170 -0
  112. package/docs/guide/unsat-core.md +154 -0
  113. package/docs/guide/variables.md +188 -0
  114. package/docs/ja/getting-started/ai-agents.md +270 -0
  115. package/docs/solvers/custom-solver.md +525 -0
  116. package/docs/solvers/glpk.md +66 -0
  117. package/docs/solvers/highs.md +115 -0
  118. package/docs/solvers/index.md +42 -0
  119. package/docs/solvers/z3.md +78 -0
  120. package/docs/zh/getting-started/ai-agents.md +270 -0
  121. package/llms-full.txt +4065 -0
  122. package/llms.txt +201 -0
  123. package/package.json +32 -16
  124. package/skills/washi/SKILL.md +197 -0
  125. package/dist/browser/browser-runtime-D13gHOT5.js +0 -4189
  126. package/dist/browser/loader-CVt4CloI.js +0 -58
  127. package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
  128. package/dist/lib/constraint-BRuB3ooC.js +0 -90
  129. package/dist/lib/loader-Dti0v9yC.js +0 -50
  130. package/dist/lib/registry-DksqCSjI.js +0 -32
package/llms.txt ADDED
@@ -0,0 +1,201 @@
1
+ # WaSHI (@ohtaman/washi)
2
+
3
+ > WaSHI: WebAssembly-backed Algebraic Modeler for Mathematical Optimization & Constraint Programming (LP, MIP, QP, SMT)
4
+
5
+ WaSHI brings the algebraic modeling style of Python's **PuLP** and Julia's **JuMP** to the TypeScript and JavaScript ecosystem.
6
+
7
+ - Full single-file documentation reference: [llms-full.txt](llms-full.txt)
8
+ - Canonical AI agent modeling quick-reference: [docs/getting-started/ai-agents.md](docs/getting-started/ai-agents.md)
9
+
10
+ ---
11
+
12
+ ## 1. Package Imports & Deployment
13
+
14
+ ### Node.js / Bundlers (Vite, Webpack, Next.js, Bun)
15
+
16
+ ```ts
17
+ import {
18
+ Model,
19
+ expr,
20
+ sum,
21
+ dot,
22
+ maximize,
23
+ minimize,
24
+ and,
25
+ or,
26
+ not,
27
+ implies,
28
+ xor,
29
+ setHighsWasmUrl,
30
+ } from "@ohtaman/washi";
31
+
32
+ // Optional solver plugins (require installing peer dependencies first):
33
+ // npm install z3-solver
34
+ import "@ohtaman/washi/solvers/z3";
35
+ // npm install glpk.js
36
+ import "@ohtaman/washi/solvers/glpk";
37
+ ```
38
+
39
+ ### Browser ESM & Classic Script Tags
40
+
41
+ - **Browser ESM**:
42
+ - Core (HiGHS built-in): `import { Model, expr, maximize } from "./washi/washi.js";`
43
+ - GLPK plugin: `import "./washi/solvers/glpk.js";`
44
+ - Z3 plugin: `import "./washi/solvers/z3.js";`
45
+ - **Classic `<script>` / CodePen (IIFE)**:
46
+ - Core: `<script src="./washi/washi.min.js"></script>` (exposes global `Washi`)
47
+ - GLPK plugin: `<script src="./washi/solvers/glpk.min.js"></script>` (registers `"glpk"`, exposes `WashiGLPK`)
48
+ - Z3 plugin: `<script src="./washi/solvers/z3.min.js"></script>` (registers `"z3"`, exposes `WashiZ3`)
49
+
50
+ ### WebAssembly Asset & Runtime Rules
51
+
52
+ 1. **Bundled WASM by default (no implicit CDN fallback)**:
53
+ - In Node.js and modern bundlers, WebAssembly assets resolve relative to the module automatically.
54
+ - In browser deployments, deploy the full `dist/browser/` directory (JS, Worker, and `.wasm` files) together. If a local WASM URL is missing or returns an HTML SPA fallback, initialization fails immediately and reports the attempted URL—it **never** silently switches to a public CDN.
55
+ - In browser runtimes, to load `highs.wasm` from a custom location, call `setHighsWasmUrl("https://.../highs.wasm")` or pass `await m.solve({ wasmUrl: "https://.../highs.wasm" })` (ignored in Node.js, which always loads the local Node `highs` package runtime).
56
+ 2. **Z3 requires a Secure Context, Cross-Origin Isolation, and CORS in browsers**:
57
+ - Z3 WebAssembly uses `SharedArrayBuffer` multithreading. Browser pages running Z3 must run in a secure context (`window.isSecureContext === true`, i.e., HTTPS or `localhost`) and be cross-origin isolated (`window.crossOriginIsolated === true`) by serving:
58
+ - `Cross-Origin-Opener-Policy: same-origin` (on the document)
59
+ - `Cross-Origin-Embedder-Policy: require-corp` (on the document)
60
+ - `Cross-Origin-Resource-Policy: cross-origin` and `Access-Control-Allow-Origin` (on cross-origin script/WASM asset responses)
61
+ - Importing `@ohtaman/washi` never auto-injects a Service Worker. When a secure context or cross-origin isolation is absent in a browser, Z3 rejects immediately with a descriptive error.
62
+ 3. **Web Workers & Cancellation (`AbortSignal`)**:
63
+ - Pre-solve cancellation (`signal.aborted` before execution starts) is supported across all solvers.
64
+ - In browsers where `Worker` is available, HiGHS runs in a dedicated Web Worker by default (`useWorker: true`). Passing `{ signal }` to `m.solve({ signal })` preemptively terminates an in-flight Worker solve (or cancels queued Worker requests).
65
+ - On main-thread HiGHS (`useWorker: false` or in Node.js), cooperative mid-solve cancellation is supported when `abort()` is triggered synchronously inside an `onProgress` callback at a solver interrupt checkpoint; arbitrary asynchronous aborts from outside cannot preempt a blocked synchronous WASM thread until a callback checkpoint or completion. Single-threaded WASM solvers without interrupt hooks (GLPK, Z3) check `signal` before execution starts.
66
+
67
+ ---
68
+
69
+ ## 2. Canonical Modeling Workflow & Key Rules
70
+
71
+ Formulating and solving a model consists of four distinct steps:
72
+
73
+ ### Step 1: Create Model & Decision Variables
74
+
75
+ ```ts
76
+ const m = new Model("ExampleModel");
77
+
78
+ // Scalar variables
79
+ const x = m.continuous("x", { lb: 0, ub: 100 });
80
+ const n = m.integer("n", { lb: 0, ub: 10 });
81
+ const b = m.binary("b");
82
+ const flag = m.bool("flag"); // Propositional boolean (for Z3 / logical constraints)
83
+
84
+ // Indexed variable containers:
85
+ // - Numeric dimension -> Array
86
+ // - Key-array dimension -> Record
87
+ const vec = m.continuous(4, { lb: 0, prefix: "v" }); // Var[] (length 4)
88
+ const grid = m.binary(2, 3, { prefix: "g" }); // Var[][] (2x3)
89
+ const byItem = m.integer(["desk", "chair"] as const, { lb: 0 }); // Record<"desk" | "chair", Var>
90
+ const mixed = m.binary(["A", "B"] as const, 3); // Record<"A" | "B", Var[]>
91
+ ```
92
+
93
+ - **Optional Primal Initial Values (`initialValue?: number`)**:
94
+ - Numeric variable constructors (`m.continuous`, `m.integer`, `m.binary`, and `new Var`) and the mutable `v.initialValue` property accept an optional finite primal hint (validated with tolerance `1e-6` against bounds `[lb, ub]` and integer/binary domains), kept distinct from the solved `v.value`.
95
+ - A start may be complete or partial (e.g., transferred by stable variable names when rebuilding a MIP model). Supplying `initialValue` is a hint only—it does **not** fix the variable, add a hard constraint, or guarantee solver acceptance or a runtime speedup.
96
+ - Supported by **HiGHS** (`supportsWarmStart: true`); solvers without warm-start support (**GLPK**, **Z3**) throw an error if any variable has `initialValue !== undefined`.
97
+
98
+ ### Step 2: Build Expressions (`expr`, `sum`, `dot`)
99
+
100
+ - **Unified `expr` template tag**:
101
+ - Linear expression (`LinExpr`): `` expr`30 * ${x} + 50 * ${n} - 10` ``
102
+ - Quadratic expression (`QuadExpr`): `` expr`${x}^2 + 2 * ${x} * ${n} + ${n}^2` `` (variable-by-variable products and squares are supported for convex QP objectives).
103
+ - Linear constraint (`Constraint`): `` expr`${x} + 2 * ${n} <= 40` `` (supports `<=`, `>=`, `==`, and `=`).
104
+ - **Aggregations**:
105
+ - `sum(iterable, mapper?)` sums an `Iterable` of `Var` / `LinExpr` / `number` (e.g., `Var[]`, `grid.flat()`, or `Object.values(record)`).
106
+ - `dot(a, b)` computes the inner product of matching 1D arrays, 2D arrays (`number[][]` / `Var[][]`), plain records (`Record<string, ...>`), or mixed Array/Record containers in either operand order.
107
+ - **Fluent methods**:
108
+ - Arithmetic (on `LinExpr` and `QuadExpr`; `Var` supports `.add()`, `.sub()`, `.mul()`, `.neg()`): `.add(other)`, `.sub(other)`, `.mul(other)`, `.div(scalar)`, `.neg()`
109
+ - Comparisons (on `Var` and `LinExpr`): `.le(rhs, name?)`, `.ge(rhs, name?)`, `.eq(rhs, name?)`
110
+ - Boolean logic (on `BoolVar`, `BoolExpr`, and `Constraint` for Z3): standalone `and(...)`, `or(...)`, `not(...)`, `implies(a, b)`, `xor(a, b)` and fluent `.and(...)`, `.or(...)`, `.not()`, `.implies(...)`, `.xor(...)`
111
+
112
+ ### Step 3: Register Objective & Constraints on the Model
113
+
114
+ Constructing an `expr` or `.le()` object does **not** attach it to the model until you pass it to `m.constraint(...)` or `m.objective(...)`:
115
+
116
+ ```ts
117
+ // Set objective using maximize() or minimize()
118
+ m.objective(maximize(expr`30 * ${x} + 50 * ${n}`));
119
+
120
+ // Register constraints on the model
121
+ m.constraint(expr`${x} + 2 * ${n} <= 40`, "resource_limit");
122
+
123
+ // Indicator constraint (MIP Big-M reformulation: b === 1 => x <= 50)
124
+ m.indicator(b, expr`${x} <= 50`, { activeValue: 1, name: "cap_when_b" });
125
+ ```
126
+
127
+ ### Step 4: Execute (`solve` or `check`) & Verify (`validateSolution`)
128
+
129
+ - **Optimization (`await m.solve(options?)`)**:
130
+ - Returns `SolverStatus` (`"Optimal"`, `"Feasible"`, `"Infeasible"`, `"Unbounded"`, `"TimeLimit"`, `"IterationLimit"`, `"Error"`, `"Unknown"`, `"NotSolved"`, `"Sat"`, or `"Unsat"`). Built-in optimization solvers return the optimization statuses (`"Optimal"`, `"Feasible"`, `"Infeasible"`, `"Unbounded"`, `"TimeLimit"`, `"IterationLimit"`, `"Error"`, `"Unknown"`, `"NotSolved"`), where a time-, iteration-, or solution-limited run (or an interrupted solve) that found a valid primal incumbent returns `"Feasible"`; `"Sat"` and `"Unsat"` are included in `SolverStatus` for satisfiability/custom solver adapters (such as `MockSolver`).
131
+ - Read values via `x.value`, `expr`...`.value`, `m.getValues(x)`, `m.getValues(container)`, `m.objectiveValue`, `constr.dual`, `constr.slack`, and `x.reducedCost`.
132
+ - Call `m.validateSolution()` after solving to independently verify variable finiteness, variable bounds (`lb`/`ub`), integer/binary/boolean domains, linear constraint residuals, and logical constraints (`SolutionValidationReport`: `{ valid, complete, maxViolation, violations, ... }`). Note that `validateSolution()` checks primal feasibility of variables and constraints; it does not recompute or compare `m.objectiveValue`.
133
+ - **Satisfiability & Conflict Diagnosis (`await m.check(options?)`)**:
134
+ - Returns `CheckResult` (`{ status: "Sat" | "Unsat" | "Unknown", values: Record<string, number | boolean>, conflicts?: readonly string[], unsatCore?: readonly string[], runTime?: number }`), where `conflicts` is populated when the model is proven `"Unsat"`.
135
+ - For models containing linear arithmetic constraints or logical constraints (`BoolVar`, `and`, `or`, `implies`, `xor`), `m.check()` works with any solver (including the default HiGHS and GLPK via single-solve elastic slack relaxation, automatically linearizing logical constraints into 0-1 MIP formulation) and diagnoses conflicting constraints as `conflicts` (leaving `unsatCore: undefined` for semantic rigor).
136
+ - Exact SMT unsatisfiable-core extraction (`result.unsatCore` and `result.conflicts`) is provided when using an SMT-capable solver (`{ solver: "z3" }`). Because Z3 asserts variable bounds (`lb`/`ub`) and binary `{0, 1}` domains directly while tracking registered constraints, `unsatCore` reports a subset of registered constraint names that conflicts *together with the model's fixed variable bounds/domains* (and is not guaranteed minimal).
137
+ - **No concurrent mutation during async solve/check**:
138
+ - Mutating variables, constraints, or the objective while `await m.solve()` or `await m.check()` is pending invalidates the in-flight execution and rejects the stale promise.
139
+
140
+ ---
141
+
142
+ ## 3. Common Pitfalls & Retired Aliases
143
+
144
+ 1. DO NOT pass a Constraint to m.objective() or arithmetic methods:
145
+ - `` WRONG: m.objective(maximize(expr`${x} <= 10` as any)); -> throws Error ``
146
+ - `` WRONG: expr`${x} + 1`.add(expr`${y} <= 5` as any); -> throws Error ``
147
+ 2. DO NOT pass an arithmetic expression without (<=, >=, ==) to m.constraint():
148
+ - `` WRONG: m.constraint(expr`${x} + ${y}` as any); -> throws Error ``
149
+ 3. DO NOT use quadratic terms inside linear constraints:
150
+ - `` WRONG: expr`${x}^2 <= 10`; -> throws Error ("Quadratic constraints are not supported") ``
151
+ 4. DO NOT use retired aliases (removed in #59 / #81):
152
+ - Use `expr` instead of retired `c` template tag
153
+ - Use `m.constraint(...)` instead of `m.constrain(...)` or `m.add(...)`
154
+ - Use `m.objective(maximize(...))` / `m.objective(minimize(...))` instead of `m.maximize(...)` / `m.minimize(...)` / `m.setObjective(...)`
155
+ - Use `await m.solve()` instead of `await m.optimize()`
156
+ - Use `.le()`, `.ge()`, `.eq()` instead of `.lessThanOrEqual()`, `.greaterThanOrEqual()`, `.equalTo()`
157
+ - Use `.value` or `m.getValues(...)` instead of `expr.getValue()`
158
+ - Use `m.variables` / `m.constraints` instead of `m.vars` / `m.constrs`
159
+
160
+ ---
161
+
162
+ ## 4. Canonical English Documentation Index
163
+
164
+ ### Getting Started
165
+
166
+ - [AI Coding Agent Quick-Reference Guide](docs/getting-started/ai-agents.md): This document is the **consumer modeling quick-reference** for AI coding agents and developers building optimization or satisfiability models with `@ohtaman/washi`.
167
+ - [Introduction & Installation](docs/getting-started/index.md): WaSHI brings the algebraic modeling style of Python's **PuLP** and Julia's **JuMP** to the TypeScript and JavaScript ecosystem.
168
+ - [Runtime Environments & Deployment](docs/getting-started/runtimes.md): WaSHI runs across JavaScript and TypeScript runtimes—from Node.js, Bun, and Deno to modern bundlers, vanilla browser `<script>` tags, and air-gapped intranets—without native C/C++ compilation.
169
+ - [Cross-Origin Isolation (Z3 & Browsers)](docs/getting-started/cross-origin.md): When running the **Z3** solver engine inside a web browser, special HTTP headers are required due to browser security models.
170
+
171
+ ### Modeling Guide
172
+
173
+ - [Variables & Domains](docs/guide/variables.md): Variables represent the mathematical decision variables in your optimization model.
174
+ - [Expressions (`expr`, `sum`, `dot`)](docs/guide/expressions.md): Mathematical expressions are linear or quadratic combinations of decision variables and numeric coefficients.
175
+ - [Constraints (`m.constraint` & `m.indicator`)](docs/guide/constraints.md): Constraints restrict the feasible solution space of your mathematical or logical model.
176
+ - [Objectives & Solving (`m.solve` & `m.check`)](docs/guide/objectives.md): The objective function defines the goal of mathematical optimization—whether to maximize profits, minimize costs, or minimize quadratic risk variance.
177
+ - [Conflict Diagnosis (`unsatCore`) & Solution Validation](docs/guide/unsat-core.md): WaSHI provides built-in tools for both: **Unsat Core conflict diagnosis** via Z3 (`m.check()`), **independent solution validation** (`m.validateSolution()`), **LP formulation export** (`m.toLpString()`), and **fast unit testing** (`MockSolver`).
178
+ - [Advanced Decomposition Methods & Best Practices](docs/guide/decomposition.md): Mixed-Integer Programming (MIP) formulations can become slow when symmetry or coupling constraints cause branch-and-bound trees to grow large.
179
+
180
+ ### Solvers
181
+
182
+ - [Solvers Overview](docs/solvers/index.md): WaSHI is designed around a pluggable, decoupled solver architecture.
183
+ - [HiGHS Solver Engine](docs/solvers/highs.md): **HiGHS** is high-performance mathematical optimization software developed at the University of Edinburgh (ERGO-Code/HiGHS).
184
+ - [Z3 SMT Solver Engine](docs/solvers/z3.md): **Z3** is a theorem prover and Satisfiability Modulo Theories (SMT) solver developed by Microsoft Research.
185
+ - [GLPK Solver Engine](docs/solvers/glpk.md): The **GNU Linear Programming Kit (GLPK)** is a long-standing, robust simplex and interior-point solver package intended for solving large-scale linear programming (LP) and mixed integer programming (MIP).
186
+ - [WaSHI Custom Solver Developer Guide](docs/solvers/custom-solver.md): This guide describes how to build, test, and distribute custom solver plugins for **WaSHI** (`@ohtaman/washi`).
187
+
188
+ ### Examples
189
+
190
+ - [Examples & API Finder](docs/examples/index.md): Explore runnable optimization and logic formulations built with WaSHI.
191
+ - [Production Planning (LP / MIP)](docs/examples/production.md): Production planning is a classic Linear Programming problem: determine the optimal quantity of each product to manufacture in order to maximize total profit while respecting resource constraints (raw materials, machine time, labor hours).
192
+ - [0-1 Knapsack Problem (Binary MIP)](docs/examples/knapsack.md): The 0-1 Knapsack problem is an essential combinatorial optimization problem: given a set of items, each with a weight and a value, determine which items to include in a collection so that the total weight does not exceed a given limit and the total value is maximized.
193
+ - [Transportation & Allocation Network (LP)](docs/examples/transportation.md): The transportation problem is one of the most fundamental network optimization models: determine how many units to ship from each supply origin (factory) to each destination (store) to minimize total shipping cost while respecting factory supply limits and meeting store demands.
194
+ - [Diet Problem & Sensitivity Analysis (LP Duals & Slacks)](docs/examples/diet-sensitivity.md): When solving a Linear Programming model, the optimal variable values (`var.value`) are only half the story.
195
+ - [Staff Shift Scheduling & Conflict Diagnosis (MIP + Z3)](docs/examples/shift-scheduling.md): Staff shift scheduling is a classic real-world application of Mixed-Integer Programming in SaaS and workforce management tools.
196
+ - [E-Commerce Cart & Coupon Optimizer (Indicator MIP)](docs/examples/ecommerce-coupons.md): Modern e-commerce checkouts often feature complex promotional rules: minimum spend thresholds, category-specific discounts, stackable vs. mutually exclusive coupons, and maximum coupon limits per order.
197
+ - [Portfolio Optimization (QP)](docs/examples/portfolio-qp.md): The Markowitz mean-variance portfolio optimization problem is the foundation of modern quantitative finance: allocate capital across risky assets to minimize overall portfolio variance (risk) subject to achieving a minimum required expected return.
198
+ - [SMT Security Policies](docs/examples/smt-security.md): Satisfiability Modulo Theories (SMT) combines propositional logic (Boolean SAT) with first-order theories (linear integer arithmetic, real inequalities).
199
+ - [Conflict Diagnosis (Unsat Core)](docs/examples/unsat-core.md): When business rules, budgets, and operational policies grow complex, constraints inevitably conflict.
200
+ - [Capacitated Vehicle Routing Problem (CVRP, 50 Customers via Column Generation)](docs/examples/vehicle-routing.md): The **Capacitated Vehicle Routing Problem (CVRP)** determines the minimum number of delivery vehicles needed to visit a set of customers, subject to vehicle cargo capacity, maximum customer stops per shift, and route duration limits.
201
+ - [Traveling Salesperson Problem (TSP via Benders Decomposition)](docs/examples/traveling-salesperson.md): The **Traveling Salesperson Problem (TSP)** seeks the shortest closed tour that visits every city in a given set once and returns to the starting city.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ohtaman/washi",
3
- "version": "0.4.0",
4
- "description": "WaSHI: WebAssembly-backed, Solver-independent, High-level modeling Interface (LP, MIP, QP, SMT)",
3
+ "version": "0.6.0",
4
+ "description": "WaSHI: WebAssembly-backed Algebraic Modeler for Mathematical Optimization & Constraint Programming (LP, MIP, QP, SMT)",
5
5
  "type": "module",
6
6
  "main": "./dist/lib/washi.js",
7
7
  "module": "./dist/lib/washi.js",
@@ -48,6 +48,16 @@
48
48
  "dist/lib",
49
49
  "dist/browser",
50
50
  "dist/types",
51
+ "docs/getting-started/ai-agents.md",
52
+ "docs/ja/getting-started/ai-agents.md",
53
+ "docs/zh/getting-started/ai-agents.md",
54
+ "docs/getting-started",
55
+ "docs/guide",
56
+ "docs/solvers",
57
+ "docs/examples",
58
+ "skills/washi/SKILL.md",
59
+ "llms.txt",
60
+ "llms-full.txt",
51
61
  "README.md",
52
62
  "README.ja.md",
53
63
  "README.zh-CN.md",
@@ -56,33 +66,39 @@
56
66
  ],
57
67
  "scripts": {
58
68
  "dev": "vite",
69
+ "ensure:highs": "node scripts/ensure-highs-wasm.mjs",
70
+ "build:highs-wasm": "bash scripts/build-highs-wasm.sh",
59
71
  "build:demo": "vite build",
60
72
  "build:types": "node -e \"import('node:fs').then(fs => fs.rmSync('dist/types', { recursive: true, force: true }))\" && tsc -p tsconfig.lib.json",
61
- "build:lib": "node scripts/build-library.mjs",
62
- "build": "npm run build:types && npm run build:lib && npm run build:browser && npm run build:demo",
63
- "build:site": "node scripts/build-site.mjs",
73
+ "build:lib": "node scripts/ensure-highs-wasm.mjs && node scripts/build-library.mjs",
74
+ "build:llms": "node scripts/generate-llms.mjs",
75
+ "build": "node scripts/ensure-highs-wasm.mjs && npm run build:types && npm run build:lib && npm run build:browser && npm run build:demo",
76
+ "build:site": "node scripts/ensure-highs-wasm.mjs && node scripts/build-site.mjs",
64
77
  "docs:dev": "vitepress dev docs",
65
- "docs:build": "vitepress build docs",
78
+ "docs:build": "vitepress build docs && node scripts/generate-llms.mjs",
66
79
  "docs:preview": "vitepress preview docs",
67
80
  "preview": "vite preview",
68
81
  "test": "vitest run",
69
82
  "test:e2e": "RUN_E2E=1 vitest run test/e2e.test.ts",
70
- "prepublishOnly": "npm run build:types && npm run build:lib && npm run build:browser",
71
- "build:browser": "node scripts/build-browser.mjs",
72
- "test:distribution": "npm run build:types && npm run build:lib && npm run build:browser && node --experimental-vm-modules scripts/check-distribution.mjs",
73
- "test:demo": "BASE_URL=/washi/ npm run build:demo && node scripts/check-demo-browser.mjs"
83
+ "prepublishOnly": "node scripts/ensure-highs-wasm.mjs && npm run build:types && npm run build:lib && npm run build:browser && npm run build:llms",
84
+ "build:browser": "node scripts/ensure-highs-wasm.mjs && node scripts/build-browser.mjs",
85
+ "test:distribution": "node scripts/ensure-highs-wasm.mjs && npm run build:types && npm run build:lib && npm run build:browser && npm run build:llms && node --experimental-vm-modules scripts/check-distribution.mjs",
86
+ "test:demo": "BASE_URL=/washi/ npm run build:demo && node scripts/check-demo-browser.mjs",
87
+ "bench": "node scripts/run-benchmarks.mjs"
74
88
  },
75
89
  "keywords": [
76
90
  "optimization",
77
91
  "mathematical-programming",
92
+ "constraint-programming",
78
93
  "linear-programming",
79
94
  "mixed-integer-programming",
80
95
  "quadratic-programming",
81
- "lp",
82
- "mip",
83
- "qp",
96
+ "smt",
97
+ "z3",
84
98
  "highs",
85
99
  "pulp",
100
+ "jump",
101
+ "operations-research",
86
102
  "wasm",
87
103
  "webassembly",
88
104
  "typescript"
@@ -101,9 +117,7 @@
101
117
  "url": "https://github.com/ohtaman/washi/issues"
102
118
  },
103
119
  "homepage": "https://github.com/ohtaman/washi#readme",
104
- "dependencies": {
105
- "highs": "^1.15.3"
106
- },
120
+ "dependencies": {},
107
121
  "peerDependencies": {
108
122
  "glpk.js": "^5.0.0",
109
123
  "z3-solver": "^5.2.0"
@@ -118,7 +132,9 @@
118
132
  },
119
133
  "devDependencies": {
120
134
  "@types/node": "^26.6.3",
135
+ "async-mutex": "^0.3.2",
121
136
  "glpk.js": "^5.0.0",
137
+ "katex": "^0.16.21",
122
138
  "mermaid": "^11.17.2",
123
139
  "playwright": "^1.63.0",
124
140
  "typescript": "^7.0.2",
@@ -0,0 +1,197 @@
1
+ ---
2
+ name: washi
3
+ description: Formulate, inspect, solve, and verify mathematical optimization (LP, MIP, convex QP) and SMT satisfiability or unsat-core models in TypeScript and JavaScript using @ohtaman/washi (HiGHS, Z3, GLPK).
4
+ ---
5
+
6
+ # WaSHI Modeling & Verification Skill
7
+
8
+ Use this skill when building, debugging, or verifying mathematical optimization (LP, MIP, convex QP) or satisfiability (SMT / SAT) models with `@ohtaman/washi`. For the full API reference and browser/bundler deployment rules, see [`docs/getting-started/ai-agents.md`](https://github.com/ohtaman/washi/blob/main/docs/getting-started/ai-agents.md) (available locally at `node_modules/@ohtaman/washi/docs/getting-started/ai-agents.md` in consumer projects or `docs/getting-started/ai-agents.md` in the repository).
9
+
10
+ ---
11
+
12
+ ## 1. Problem & Solver Selection
13
+
14
+ Select the solver engine and execution method that match the problem class:
15
+
16
+ | Problem Class | Recommended Solver | Setup & Import | Execution Method |
17
+ | :--- | :--- | :--- | :--- |
18
+ | **LP / MIP / Convex QP** (including `m.indicator()` Big-M and `initialValue` warm starts) | **HiGHS** (default) | Built-in (`import { Model } from "@ohtaman/washi"`) | `await m.solve()` |
19
+ | **SMT Propositional Logic** (`BoolVar`, `and`, `or`, `not`, `implies`, `xor`) | **HiGHS** (auto 0-1 MIP) or **Z3** (native SMT) | Built-in for HiGHS; `npm install z3-solver` + `import "@ohtaman/washi/solvers/z3"` for Z3 | `await m.solve()` or `await m.check()` |
20
+ | **Infeasibility / Conflict Diagnosis** (`conflicts` / `unsatCore`) | **HiGHS** (elastic slack `conflicts`) or **Z3** (`unsatCore`) | Built-in for HiGHS; `import "@ohtaman/washi/solvers/z3"` for Z3 | `await m.check()` |
21
+ | **LP / MIP with GLPK** (GPL-3.0; no QP or `initialValue`) | **GLPK** plugin | `npm install glpk.js` + `import "@ohtaman/washi/solvers/glpk"` | `await m.solve({ solver: "glpk" })` |
22
+
23
+ - **Browser runtime notes**:
24
+ - HiGHS and GLPK load local `.wasm` and Worker assets relative to the bundle (never falling back to an external CDN).
25
+ - Z3 uses `SharedArrayBuffer` pthreads and requires `window.isSecureContext === true` and `window.crossOriginIsolated === true` (`Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp`).
26
+
27
+ ---
28
+
29
+ ## 2. Algebraic Formulation Workflow
30
+
31
+ 1. **Define decision variables on `Model`**:
32
+ - Numeric (`Var`): `m.continuous(nameOrDims, { lb, ub, initialValue })`, `m.integer(...)`, `m.binary(...)`
33
+ - Propositional boolean (`BoolVar`): `m.bool(nameOrDims)`
34
+ - Indexed containers: pass a count (`number` -> `Var[]` / `Var[][]`) or key array (`readonly string[]` -> `Record<K, Var>` / `Record<R, Record<C, Var>>`).
35
+ - Provide finite `lb` and `ub` bounds whenever known—especially on variables used in `m.indicator()` so WaSHI can automatically derive a tight Big-M when `M` is omitted.
36
+ 2. **Build expressions (`expr`, `sum`, `dot`)**:
37
+ - Linear expression (`LinExpr`): `` expr`30 * ${x} + 50 * ${y} - 10` ``
38
+ - Quadratic expression (`QuadExpr`, **objectives only**): `` expr`${x}^2 + 2 * ${x} * ${y} + ${y}^2` ``
39
+ - Linear constraint (`Constraint`): `` expr`${x} + 2 * ${y} <= 40` `` (supports `<=`, `>=`, `==`, `=`)
40
+ - Vector aggregations: `sum(iterable, mapper?)` and `dot(a, b)`
41
+ - Propositional logic: `and(...)`, `or(...)`, `not(...)`, `implies(a, b)`, `xor(a, b)` (or fluent `.and()`, `.or()`, `.not()`, `.implies()`, `.xor()`). Automatically linearized into 0-1 MIP binary variables and linear constraints on HiGHS/GLPK, or solved natively on Z3.
42
+ 3. **Register objective and named constraints**:
43
+ - Constructing an `expr` or `.le()` / `.ge()` / `.eq()` object does **not** attach it to the model.
44
+ - Register the objective with `m.objective(maximize(e))` or `m.objective(minimize(e))`.
45
+ - Register every constraint with a descriptive name: `m.constraint(c, "resource_limit")` or `m.indicator(binVar, c, { activeValue: 1, name: "cap_when_open" })`. Descriptive names are essential for LP inspection and conflict diagnosis (`conflicts` / `unsatCore`).
46
+
47
+ ---
48
+
49
+ ## 3. Model Inspection (`validateIntegrity` & `toLpString`)
50
+
51
+ Before solving or when debugging formulation bugs:
52
+
53
+ - **`m.validateIntegrity()`**: Validates variable ownership (variables and constraints cannot be shared across `Model` instances), bound sanity (`lb <= ub`, finite `initialValue` within bounds/integrality tolerance `1e-6`), and finite objective/constraint coefficients.
54
+ - **`m.toLpString()`**: Calls `validateIntegrity()` and exports the numeric LP / MIP / QP formulation in standard CPLEX LP format (`Maximize` / `Minimize`, `Subject To`, `Bounds`, `General`, `Binary`, `End`). Inspect this string to confirm objective sense, signs, coefficients, and right-hand sides match the mathematical specification.
55
+
56
+ ---
57
+
58
+ ## 4. Execution & Mandatory Self-Verification
59
+
60
+ Never report a model solution as final without executing the verification step:
61
+
62
+ 1. **After `const status = await m.solve(options?)`**:
63
+ - Verify `status === "Optimal"` (or `status === "Feasible"` / `m.hasPrimalSolution === true` when solver limits apply).
64
+ - Call `const report = m.validateSolution()` and assert `report.valid === true`.
65
+ - If `report.valid === false`, inspect `report.violations`, `report.missingVariables`, `report.uncheckedConstraints`, and `report.maxViolation`.
66
+ - Note: `m.validateSolution()` independently checks variable finiteness, variable bounds (`lb`/`ub`), integrality (`Integer`, `Binary`, `BoolVar`), linear constraint residuals, and logical constraints. It does **not** recompute `m.objectiveValue` or verify natural-language problem coverage.
67
+ 2. **When a model is infeasible (`"Infeasible"` or `"Unsat"`)**:
68
+ - Run `const res = await m.check()` (diagnoses with built-in HiGHS via single-solve elastic slack relaxation) or `const res = await m.check({ solver: "z3" })` (for exact SMT unsat core).
69
+ - When `res.status === "Unsat"`, inspect `res.conflicts` (also stored on `m.conflicts`). On HiGHS, `conflicts` reports the subset of constraints requiring relaxation to restore feasibility (with `unsatCore: undefined` for semantic rigor). On Z3, both `unsatCore` and `conflicts` report the contradictory constraint subset.
70
+ - Because Z3 asserts variable bounds (`lb`/`ub`) and binary `{0, 1}` domains directly while tracking named constraints, `res.conflicts` returns a subset of registered constraint names that conflicts *together with the model's fixed variable bounds/domains* (not guaranteed minimal).
71
+
72
+ ---
73
+
74
+ ## 5. Executable Workflow Recipes
75
+
76
+ ### Recipe 1: MIP Formulation, `toLpString()` Inspection & `validateSolution()` Verification
77
+
78
+ ```ts
79
+ import { Model, expr, dot, sum, maximize } from "@ohtaman/washi";
80
+
81
+ export async function solveAndVerifyMip() {
82
+ const m = new Model("ProductionMIP");
83
+ const products = ["desk", "chair"] as const;
84
+ const profit = { desk: 30, chair: 50 };
85
+ const woodPerUnit = { desk: 1, chair: 2 };
86
+ const laborPerUnit = { desk: 2, chair: 1 };
87
+
88
+ const qty = m.integer(products, { lb: 0, ub: 100 });
89
+ const lineOpen = m.binary("lineOpen");
90
+
91
+ m.objective(maximize(expr`${dot(profit, qty)} - 20 * ${lineOpen}`));
92
+ m.constraint(expr`${dot(woodPerUnit, qty)} <= 40`, "wood_limit");
93
+ m.constraint(expr`${dot(laborPerUnit, qty)} <= 50`, "labor_limit");
94
+ m.indicator(lineOpen, expr`${sum(Object.values(qty))} <= 0`, {
95
+ activeValue: 0,
96
+ name: "closed_line_zero_output",
97
+ });
98
+
99
+ // 1. Pre-solve integrity & LP formulation inspection
100
+ m.validateIntegrity();
101
+ const lpText = m.toLpString();
102
+
103
+ // 2. Solve with default HiGHS solver
104
+ const status = await m.solve();
105
+
106
+ // 3. Mandatory independent solution verification
107
+ const report = m.validateSolution();
108
+
109
+ return {
110
+ status,
111
+ lpText,
112
+ objectiveValue: m.objectiveValue,
113
+ quantities: m.getValues(qty),
114
+ lineOpen: lineOpen.value,
115
+ valid: report.valid,
116
+ maxViolation: report.maxViolation,
117
+ };
118
+ }
119
+ ```
120
+
121
+ ### Recipe 2: Convex Quadratic Programming (QP) & Verification
122
+
123
+ ```ts
124
+ import { Model, expr, minimize } from "@ohtaman/washi";
125
+
126
+ export async function solveAndVerifyQp() {
127
+ const m = new Model("AllocationQP");
128
+ const x = m.continuous("x", { lb: 0 });
129
+ const y = m.continuous("y", { lb: 0 });
130
+
131
+ m.objective(minimize(expr`${x}^2 + ${y}^2`));
132
+ m.constraint(expr`${x} + ${y} >= 4`, "target_total");
133
+
134
+ const lpText = m.toLpString();
135
+ const status = await m.solve();
136
+ const report = m.validateSolution();
137
+
138
+ return {
139
+ status,
140
+ lpText,
141
+ objectiveValue: m.objectiveValue,
142
+ x: x.value,
143
+ y: y.value,
144
+ valid: report.valid,
145
+ };
146
+ }
147
+ ```
148
+
149
+ ### Recipe 3: Z3 Propositional Logic, `unsatCore` Conflict Diagnosis & Verification
150
+
151
+ ```ts
152
+ import { Model, expr, implies } from "@ohtaman/washi";
153
+ import "@ohtaman/washi/solvers/z3";
154
+
155
+ export async function diagnoseAndVerifyWithZ3() {
156
+ // Part A: Diagnose conflicting rules with Z3 unsatCore / conflicts
157
+ const unsatModel = new Model("PolicyConflict", { solver: "z3" });
158
+ const vip = unsatModel.bool("vip");
159
+ const discount = unsatModel.integer("discount", { lb: 0, ub: 50 });
160
+
161
+ unsatModel.constraint(vip, "customer_is_vip");
162
+ unsatModel.constraint(implies(vip, expr`${discount} >= 20`), "vip_min_discount");
163
+ unsatModel.constraint(expr`${discount} <= 10`, "campaign_cap");
164
+
165
+ const unsatRes = await unsatModel.check();
166
+
167
+ // Part B: Relax the conflicting cap and verify the satisfiable assignment
168
+ const satModel = new Model("PolicyResolved", { solver: "z3" });
169
+ const vipSat = satModel.bool("vip");
170
+ const discountSat = satModel.integer("discount", { lb: 0, ub: 50 });
171
+
172
+ satModel.constraint(vipSat, "customer_is_vip");
173
+ satModel.constraint(implies(vipSat, expr`${discountSat} >= 20`), "vip_min_discount");
174
+ satModel.constraint(expr`${discountSat} <= 25`, "campaign_cap");
175
+
176
+ const satRes = await satModel.check();
177
+ const report = satModel.validateSolution();
178
+
179
+ return {
180
+ unsatStatus: unsatRes.status,
181
+ conflicts: unsatRes.conflicts,
182
+ unsatCore: unsatRes.unsatCore,
183
+ satStatus: satRes.status,
184
+ resolvedDiscount: discountSat.value,
185
+ valid: report.valid,
186
+ };
187
+ }
188
+ ```
189
+
190
+ ---
191
+
192
+ ## 6. Guardrails & Common Pitfalls
193
+
194
+ - **No quadratic constraints**: Quadratic terms (`${x}^2`, `${x} * ${y}`) are only valid in `m.objective(...)`. Using them in `m.constraint(...)` throws `"Quadratic constraints are not supported"`.
195
+ - **Do not confuse `LinExpr` and `Constraint`**: `m.objective(maximize(...))` requires a `LinExpr`, `QuadExpr`, `Var`, or `number` (never a `Constraint`). `m.constraint(...)` requires a `Constraint` (with `<=`, `>=`, `==`, or `=`) or `BoolExpr` (never a bare `LinExpr`).
196
+ - **Do not mutate during async solve/check**: Modifying variables, bounds, constraints, or the objective while `await m.solve()` or `await m.check()` is pending invalidates the in-flight run and rejects the promise.
197
+ - **Do not use retired aliases**: Always use `expr` (not `c`), `m.constraint(...)` (not `m.constrain` or `m.add`), `m.objective(maximize(...))` (not `m.maximize` or `m.setObjective`), `await m.solve()` (not `m.optimize`), `.le()` / `.ge()` / `.eq()` (not `.lessThanOrEqual()`), `.value` / `m.getValues(...)` (not `.getValue()`), and `m.variables` / `m.constraints` (not `m.vars` / `m.constrs`).