@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/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="./assets/washi-header-dark.png">
4
4
  <source media="(prefers-color-scheme: light)" srcset="./assets/washi-header-light.png">
5
- <img alt="WaSHI - Algebraic Modeler for TypeScript &amp; JavaScript" src="./assets/washi-header-light.png" width="420" />
5
+ <img alt="WaSHI - Algebraic Modeler for Mathematical Optimization &amp; Constraint Programming" src="./assets/washi-header-light.png" width="420" />
6
6
  </picture>
7
- <p><strong>Algebraic Modeling for TypeScript &amp; JavaScript</strong></p>
7
+ <p><strong>Algebraic Modeler for Mathematical Optimization &amp; Constraint Programming</strong></p>
8
8
  <p>
9
- <em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Quadratic Programming (QP) • SMT &amp; Conflict Diagnosis</em>
9
+ <em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Convex Quadratic Programming (QP) • SMT &amp; Conflict Diagnosis</em>
10
10
  </p>
11
11
  <p>
12
12
  <a href="./README.md">English</a> •
@@ -24,24 +24,24 @@
24
24
 
25
25
  ---
26
26
 
27
- **WaSHI** (WebAssembly-backed, Solver-independent, High-level modeling Interface) is an algebraic modeler for TypeScript and JavaScript, inspired by Python's **PuLP** and Julia's **JuMP**. Define variables, objectives, and arithmetic or logical constraints in application code. WaSHI constructs the model, translates it for a solver adapter, and maps results back to your variables. **HiGHS**, **Z3**, and **GLPK** perform the optimization or satisfiability checks.
27
+ **WaSHI** (WebAssembly-backed, Solver-independent, High-level modeling Interface) is an algebraic modeler for mathematical optimization and constraint programming in TypeScript and JavaScript, inspired by Python's **PuLP** and Julia's **JuMP**. Define variables, objectives, and arithmetic or logical constraints in application code. WaSHI constructs the model, translates it for a solver adapter, and maps results back to your variables. **HiGHS**, **Z3**, and **GLPK** perform the optimization or satisfiability checks.
28
28
 
29
29
  HiGHS is the default solver; Z3 and GLPK are optional plugins. The package provides Node.js and browser distributions, including WebAssembly assets. See the [browser integration guide](#-browser--web-worker-usage) for deployment requirements.
30
30
 
31
- [Try the browser demo](https://ohtaman.github.io/washi/demo/) · [Browse example source code](https://github.com/ohtaman/washi/blob/main/examples/README.md) · [ESM / script / CodePen examples](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)
31
+ [Documentation](https://ohtaman.github.io/washi/) · [Try the browser demo](https://ohtaman.github.io/washi/demo/) · [AI Agent Quick-Reference](docs/getting-started/ai-agents.md) · [Agent Skill (`SKILL.md`)](skills/washi/SKILL.md) · [Browse example source code](https://github.com/ohtaman/washi/blob/main/examples/README.md) · [ESM / script / CodePen examples](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)
32
32
 
33
33
  ```ts
34
- import { Model, expr } from "@ohtaman/washi";
34
+ import { Model, expr, maximize } from "@ohtaman/washi";
35
35
 
36
36
  const m = new Model("WoodworkingProfit");
37
37
  const x = m.integer("desks", { lb: 0 });
38
38
  const y = m.integer("chairs", { lb: 0 });
39
39
 
40
- m.maximize(expr`30 * ${x} + 50 * ${y}`);
41
- m.constrain(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
42
- m.constrain(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
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
43
 
44
- await m.optimize();
44
+ await m.solve();
45
45
  console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`);
46
46
  ```
47
47
 
@@ -49,10 +49,11 @@ console.log(`Produce: x=${x.value}, y=${y.value} (Profit: $${m.objectiveValue})`
49
49
 
50
50
  ## 🎯 What WaSHI provides
51
51
 
52
- 1. **Algebraic model construction**: Formulate optimization models using domain-specific variable constructors (`m.binary`, `m.integer`, `m.continuous`), tagged template literals (`expr`), vector aggregations (`dot`, `sum`), and first-class constraints (`m.constrain`).
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/custom-solver-guide.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. Browser deployments must include the generated JS, Worker, and WASM assets. Offline use also requires those resources to be available locally or cached. Z3 has additional browser isolation requirements.
55
- 4. **Named constraints and SMT results**: With Z3, explicitly check satisfiability and inspect an unsatisfiable core to investigate conflicting constraints. A returned core is a conflicting subset, not necessarily the smallest one.
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(...)`).
56
57
 
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.
58
59
 
@@ -62,9 +63,9 @@ Model construction and translation are WaSHI's responsibility. Supported problem
62
63
 
63
64
  | Adapter | Engine license | Supported problem classes | Engine role | Integration |
64
65
  | :--- | :--- | :--- | :--- | :--- |
65
- | **HiGHS** | **MIT** | **LP, MIP, convex QP** | Numerical optimization | Built-in default |
66
- | **Z3** | **MIT** | **SMT, SAT, logic, MIP** | Satisfiability checks, unsatisfiable cores, optimization | Optional plugin (`@ohtaman/washi/solvers/z3`) |
67
- | **GLPK** | **GPL-3.0** | **LP, MIP** | Linear and mixed-integer optimization | Optional plugin (`@ohtaman/washi/solvers/glpk`) |
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`) |
68
69
  | **Mock** | **MIT** | — | Simulated solver responses for integration tests | Built-in; does not solve the model |
69
70
 
70
71
  ---
@@ -92,61 +93,58 @@ npm install z3-solver # For SMT and unsat core diagnosis
92
93
  npm install glpk.js # For GLPK solver (GPL-3.0)
93
94
  ```
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)
100
+ 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
+ ```
106
+
95
107
  ---
96
108
 
97
109
  ## 🚀 Usage Guide
98
110
 
99
- ### 1. Mixed-Integer Linear Programming (MIP)
111
+ ### 1. Multidimensional Variables, Indicator Constraints & Solution Validation
100
112
 
101
- Formulate production scheduling, resource allocation, and facility location problems:
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()`:
102
114
 
103
115
  ```ts
104
116
  import { Model, expr } from "@ohtaman/washi";
105
117
 
106
- const m = new Model("FactoryPlanning");
107
-
108
- // Decision variables (Integer, non-negative)
109
- const chairs = m.integer("chairs", { lb: 0 });
110
- const tables = m.integer("tables", { lb: 0 });
111
-
112
- // Natural formula objective & constraints via Tagged Template Literals
113
- m.maximize(expr`30 * ${chairs} + 50 * ${tables}`);
114
- m.constrain(expr`${chairs} + 2 * ${tables} <= 40`, "wood_limit");
115
- m.constrain(expr`2 * ${chairs} + ${tables} <= 50`, "labor_limit");
116
-
117
- // Solve using default HiGHS WASM engine
118
- const status = await m.optimize();
119
-
120
- console.log(`Status: ${status}`); // "Optimal"
121
- console.log(`Objective: $${m.objectiveValue}`); // 1100
122
- console.log(`Chairs: ${chairs.value}, Tables: ${tables.value}`); // 20 chairs, 10 tables
123
- ```
124
-
125
- #### 🗂 Multidimensional Variables Grid
118
+ const m = new Model("FacilityPlanning");
126
119
 
127
- Construct variable arrays or multi-index matrices in one call:
128
-
129
- ```ts
130
120
  // 1D dictionary of binary variables
131
121
  const openCity = m.binary(["Tokyo", "Osaka", "Nagoya"]);
132
122
 
133
123
  // 2D transport flow matrix: [Factory] x [Store]
134
- const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0 });
124
+ const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0, ub: 1000 });
135
125
 
136
126
  // 2D coordinate grid (e.g. 8x8 chessboard)
137
127
  const board = m.binary(8, 8, { prefix: "queen" });
138
- ```
139
128
 
140
- #### 🔀 Big-M Indicator Constraints (`m.indicator`)
141
-
142
- Model conditional activation rules and fixed-charge economics without manual Big-M algebra:
143
-
144
- ```ts
145
- // If factory is closed (openCity['Tokyo'] == 0), its production capacity must be 0
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)
146
131
  m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
147
132
  activeValue: 0,
148
- M: 1000,
133
+ name: "tokyo_closed_no_flow",
149
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);
150
148
  ```
151
149
 
152
150
  ---
@@ -156,7 +154,7 @@ m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
156
154
  Minimize investment risk based on a covariance matrix subject to return and budget constraints:
157
155
 
158
156
  ```ts
159
- import { Model, QuadExpr, sum, dot, expr } from "@ohtaman/washi";
157
+ import { Model, QuadExpr, sum, dot, minimize, expr } from "@ohtaman/washi";
160
158
 
161
159
  const m = new Model("MarkowitzPortfolio");
162
160
  const assets = ["Tech", "Bonds", "RealEstate"];
@@ -166,11 +164,11 @@ const returns = [0.14, 0.04, 0.08];
166
164
  const w = m.continuous(assets, { lb: 0, ub: 1 });
167
165
 
168
166
  // 1. Fully invested budget: sum(w) == 1.0
169
- m.constrain(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
167
+ m.constraint(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
170
168
 
171
169
  // 2. Minimum target return: portfolio return >= 8.5%
172
170
  const retMap = Object.fromEntries(assets.map((a, i) => [a, returns[i]]));
173
- m.constrain(expr`${dot(retMap, w)} >= 0.085`, "min_return");
171
+ m.constraint(expr`${dot(retMap, w)} >= 0.085`, "min_return");
174
172
 
175
173
  // 3. Covariance risk matrix
176
174
  const cov = [
@@ -186,9 +184,9 @@ for (let i = 0; i < assets.length; i++) {
186
184
  risk = risk.add(w[assets[i]].mul(w[assets[j]]).mul(cov[i][j]));
187
185
  }
188
186
  }
189
- m.minimize(risk);
187
+ m.objective(minimize(risk));
190
188
 
191
- await m.optimize();
189
+ await m.solve();
192
190
  console.log(`Minimal Variance: ${m.objectiveValue?.toFixed(6)}`);
193
191
  assets.forEach((name) => {
194
192
  console.log(` ${name}: ${((w[name].value || 0) * 100).toFixed(2)}%`);
@@ -214,13 +212,13 @@ const siren = m.bool("siren");
214
212
  const lux = m.integer("lux", { lb: 0 });
215
213
 
216
214
  // Logical rules
217
- m.constrain(lux.le(50).implies(isNight), "dark_implies_night");
218
- m.constrain(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
215
+ m.constraint(lux.le(50).implies(isNight), "dark_implies_night");
216
+ m.constraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
219
217
 
220
218
  // Current sensor observations
221
- m.constrain(expr`${lux} <= 10`, "sensor_dark");
222
- m.constrain(motion, "sensor_motion");
223
- m.constrain(alarm, "alarm_on");
219
+ m.constraint(expr`${lux} <= 10`, "sensor_dark");
220
+ m.constraint(motion, "sensor_motion");
221
+ m.constraint(alarm, "alarm_on");
224
222
 
225
223
  // Check satisfiability and deduce state without an objective function
226
224
  const res = await m.check({ solver: "z3" });
@@ -231,40 +229,47 @@ console.log(`Siren active? ${m.values.get("siren")}`); // true
231
229
 
232
230
  ---
233
231
 
234
- ### 4. Conflict Diagnosis (`unsatCore`)
232
+ ### 4. Conflict Diagnosis (`conflicts` / `unsatCore`)
235
233
 
236
- When business rules or constraints contradict each other, extract a **conflicting constraint subset** (unsatisfiable core; minimality is not guaranteed) for troubleshooting (requires Z3):
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
237
 
238
238
  ```ts
239
239
  import { Model, expr } from "@ohtaman/washi";
240
- import "@ohtaman/washi/solvers/z3";
241
240
 
242
- const m = new Model({ name: "ConflictDiagnosis", solver: "z3" });
241
+ const m = new Model("ConflictDiagnosis");
243
242
  const engineers = m.integer("engineers", { lb: 0, ub: 20 });
244
243
  const budget = m.continuous("budget", { lb: 0, ub: 100000 });
245
244
  const rushDelivery = m.bool("rushDelivery");
246
245
 
247
246
  // Rule 1: Each engineer costs $8,000
248
- m.constrain(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
247
+ m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
249
248
 
250
249
  // Rule 2: Customer requires rush delivery
251
- m.constrain(rushDelivery, "rush_required");
250
+ m.constraint(rushDelivery, "rush_required");
252
251
 
253
252
  // Rule 3: Rush delivery requires at least 8 engineers
254
- m.constrain(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
253
+ m.constraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
255
254
 
256
255
  // Rule 4: Hard board budget cap of $50,000 (Conflict: 8 * $8000 = $64,000 > $50,000!)
257
- m.constrain(expr`${budget} <= 50000`, "strict_board_budget_cap");
256
+ m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
258
257
 
259
258
  // Rule 5: Innocent bystander rule
260
- m.constrain(expr`${engineers} >= 1`, "minimum_one_employee");
259
+ m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
261
260
 
262
- const res = await m.check({ solver: "z3" });
261
+ // 1. Elastic repair diagnosis with default HiGHS
262
+ const res = await m.check();
263
263
 
264
264
  console.log(`Status: ${res.status}`); // "Unsat"
265
- console.log("Conflicting constraints:", res.unsatCore);
266
- // Output: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
267
- // Note: "minimum_one_employee" is correctly excluded!
265
+ console.log("Repair set (relaxed constraints):", res.conflicts);
266
+ // Output with HiGHS: ["rush_required"]
267
+ // (Relaxing rush_required restores feasibility; innocent rules like minimum_one_employee are untouched)
268
+
269
+ // 2. Or exact SMT unsatisfiable core with Z3 (import "@ohtaman/washi/solvers/z3")
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"]
268
273
  ```
269
274
 
270
275
  ---
@@ -274,18 +279,18 @@ console.log("Conflicting constraints:", res.unsatCore);
274
279
  To use the GNU Linear Programming Kit (GLPK) solver plugin:
275
280
 
276
281
  ```ts
277
- import { Model, expr } from "@ohtaman/washi";
282
+ import { Model, expr, maximize } from "@ohtaman/washi";
278
283
  import "@ohtaman/washi/solvers/glpk";
279
284
 
280
285
  const m = new Model({ solver: "glpk" });
281
286
  const x = m.integer("x", { lb: 0 });
282
287
  const y = m.integer("y", { lb: 0 });
283
288
 
284
- m.maximize(expr`30 * ${x} + 50 * ${y}`);
285
- m.constrain(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
286
- m.constrain(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
289
+ m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
290
+ m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
291
+ m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
287
292
 
288
- const status = await m.optimize();
293
+ const status = await m.solve();
289
294
  console.log(`Status: ${status}`); // "Optimal"
290
295
  console.log(`Objective: ${m.objectiveValue}`); // 1100
291
296
  ```
@@ -297,7 +302,7 @@ console.log(`Objective: ${m.objectiveValue}`); // 1100
297
302
  Cleanly compute 1D/2D linear combinations without nested loops:
298
303
 
299
304
  ```ts
300
- import { Model, dot } from "@ohtaman/washi";
305
+ import { Model, dot, minimize } from "@ohtaman/washi";
301
306
 
302
307
  const m = new Model();
303
308
  const factories = ["Tokyo", "Osaka"];
@@ -311,8 +316,8 @@ const costs = {
311
316
  const ship = m.continuous(factories, stores);
312
317
 
313
318
  // Total transportation cost in one line:
314
- m.minimize(dot(costs, ship));
315
- await m.optimize();
319
+ m.objective(minimize(dot(costs, ship)));
320
+ await m.solve();
316
321
 
317
322
  // Extract all optimal variable values as a structured object
318
323
  const solution = m.getValues(ship);
@@ -362,23 +367,23 @@ The package also includes `dist/browser/washi.min.js`, which exports the `Washi`
362
367
  </script>
363
368
  ```
364
369
 
365
- For CDN usage, replace the local URL with `https://cdn.jsdelivr.net/npm/@ohtaman/washi@0.1.2/dist/browser/washi.min.js` (or pin your desired release version `>= 0.1.2`). For ESM use the corresponding `dist/browser/washi.js` URL. Selecting the CDN URL is an explicit choice; local copies never switch to a CDN.
370
+ For CDN usage, replace the local URL with `https://cdn.jsdelivr.net/npm/@ohtaman/washi@<version>/dist/browser/washi.min.js` (specifying a compatible release version once published). For ESM use the corresponding `dist/browser/washi.js` URL. Selecting the CDN URL is an explicit choice; local copies never switch to a CDN.
366
371
 
367
372
  ### Solver Plugins in the Browser (GLPK & Z3)
368
373
 
369
- The browser distribution includes pre-bundled, standalone solver plugins under `dist/browser/solvers/` (available from `>= 0.2.0` or `@latest` on CDN):
374
+ The browser distribution includes pre-bundled, standalone solver plugins under `dist/browser/solvers/`:
370
375
  - **GLPK**: `dist/browser/solvers/glpk.js` (ESM) and `dist/browser/solvers/glpk.min.js` (IIFE, registers global `WashiGLPK`)
371
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`
372
377
 
373
378
  #### Browser ESM with GLPK
374
379
  ```html
375
380
  <script type="module">
376
- import { Model } from "./washi/washi.js";
381
+ import { Model, maximize } from "./washi/washi.js";
377
382
  import "./washi/solvers/glpk.js";
378
383
 
379
384
  const model = new Model({ solver: "glpk" });
380
385
  const x = model.continuous("x", { lb: 0, ub: 10 });
381
- model.maximize(x);
386
+ model.objective(maximize(x));
382
387
  await model.solve();
383
388
  console.log("GLPK status:", model.status);
384
389
  </script>
@@ -391,52 +396,34 @@ The browser distribution includes pre-bundled, standalone solver plugins under `
391
396
  <script>
392
397
  const model = new Washi.Model({ solver: "glpk" });
393
398
  const x = model.continuous("x", { lb: 0, ub: 10 });
394
- model.maximize(x);
399
+ model.objective(Washi.maximize(x));
395
400
  model.solve().then(status => console.log("GLPK status:", status));
396
401
  </script>
397
402
  ```
398
403
 
399
404
  > [!NOTE]
400
405
  > **Z3 in Browser Environments & Cross-Origin Isolation**:
401
- > Because the Z3 WebAssembly engine uses multithreading (`SharedArrayBuffer`), modern browsers require cross-origin isolation. The HTML document response must send:
402
- > ```http
403
- > Cross-Origin-Opener-Policy: same-origin
404
- > Cross-Origin-Embedder-Policy: require-corp
405
- > ```
406
- > And any cross-origin assets must serve `Cross-Origin-Resource-Policy: cross-origin`.
407
- >
408
- > General static sites without custom header control (e.g. standard GitHub Pages or preview platforms) and third-party iframe embeds (e.g. CodePen / JSFiddle) do not satisfy these requirements without extra setup. Importing `@ohtaman/washi` never automatically injects or registers a Service Worker into consumer applications.
409
- >
410
- > To enable Z3 on GitHub Pages, the official WaSHI Web Demo bundles a dedicated `coi-serviceworker.js` strictly scoped to `/washi/`. On fresh browser sessions, it registers the worker and reloads once to establish `window.crossOriginIsolated === true` before running Z3. Environments lacking Service Workers or failing isolation abort Z3 execution with a descriptive explanation rather than hanging.
411
-
412
- Complete standalone examples for ESM, script tags, and CodePen are available in `examples/distribution/`.
413
-
414
- ### Offline distribution, Workers and custom hosting
415
-
416
- Deploy the full application build or the entire browser distribution to a local HTTP / intranet server. With all assets locally available, an empty browser cache does not require access to the internet. Saving only the HTML or the first JS file is insufficient. `file://` execution and automatic PWA caching are not supported by this distribution.
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`).
417
407
 
418
- ESM assets resolve relative to their module URL; the script build captures its own script URL at load time. CDN Workers use a Blob module bootstrap. The server must permit cross-origin JS / WASM loading; CSP must permit the selected script / connection origins and `worker-src blob:` for CDN Workers. If that is unavailable, use `useWorker: false`. Worker load / message failures reject pending solves rather than leaving them unresolved.
408
+ Complete standalone examples for ESM, script tags, and CodePen are available in [`examples/distribution/`](examples/distribution/README.md).
419
409
 
420
- For custom hosting, `setHighsWasmUrl(url)` and `options.wasmUrl` remain available. To explicitly use the existing HiGHS CDN:
421
-
422
- ```typescript
423
- import { setHighsWasmUrl, OFFICIAL_HIGHS_CDN_WASM_URL } from "@ohtaman/washi";
424
- setHighsWasmUrl(OFFICIAL_HIGHS_CDN_WASM_URL);
425
- ```
410
+ ---
426
411
 
427
- Failed initialization reports the attempted URL and does not try a different host. This changes the previous local-root/CDN fallback behavior: deploy the generated assets or configure an explicit URL.
412
+ ## 🛠️ Custom Solvers & Extensibility
428
413
 
429
- ### Verify the distributable
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):
430
415
 
431
- `npm run test:distribution` packs and installs the actual npm tarball, builds Vite / webpack consumers without optional peers, and runs their emitted JS with real WASM in a VM. This is not a browser/CORS test. For Playwright Chromium checks, run `npx playwright install chromium` and `RUN_DISTRIBUTION_BROWSER=1 npm run test:distribution`. CI also runs that browser mode, including separate-origin browser bundles with external network requests blocked.
416
+ - See the complete [Custom Solver Developer Guide](docs/solvers/custom-solver.md) for step-by-step tutorials, complete API contracts (`Solver`, `SolveModelData`, `CheckModelData`, `SolverResult`), and testing best practices.
432
417
 
433
418
  ---
434
419
 
435
- ## 🛠️ Custom Solvers & Extensibility
420
+ ## 📚 Documentation
436
421
 
437
- 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):
422
+ Full documentation is available at **[ohtaman.github.io/washi](https://ohtaman.github.io/washi/)** and in the repository's [`docs/`](docs/) directory:
438
423
 
439
- - See the complete [Custom Solver Developer Guide](docs/custom-solver-guide.md) for step-by-step tutorials, complete API contracts (`Solver`, `LpModelData`, `SolverResult`), and testing best practices.
424
+ - **Getting Started**: [Quick Start](docs/getting-started/index.md) · [AI Agent Quick-Reference](docs/getting-started/ai-agents.md) · [Runtime Environments](docs/getting-started/runtimes.md) · [Cross-Origin Isolation (Z3)](docs/getting-started/cross-origin.md)
425
+ - **Modeling Guide**: [Variables (`Var`, `BoolVar`)](docs/guide/variables.md) · [Expressions (`expr`, `sum`, `dot`)](docs/guide/expressions.md) · [Constraints & Logic (`indicator`, SMT)](docs/guide/constraints.md) · [Objectives & Solving (`solve`, `validateSolution`)](docs/guide/objectives.md) · [Feasibility & Unsat Core (`check`)](docs/guide/unsat-core.md) · [Decomposition (`solveColumnGeneration`, `solveBenders`)](docs/guide/decomposition.md)
426
+ - **Solvers**: [Solver Overview](docs/solvers/index.md) · [HiGHS](docs/solvers/highs.md) · [Z3](docs/solvers/z3.md) · [GLPK](docs/solvers/glpk.md) · [Custom Solvers](docs/solvers/custom-solver.md)
440
427
 
441
428
  ---
442
429
 
@@ -446,11 +433,13 @@ Start with the [example gallery](https://github.com/ohtaman/washi/blob/main/exam
446
433
 
447
434
  | Category | Example | What it demonstrates | Solver |
448
435
  | :--- | :--- | :--- | :--- |
449
- | **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`, `c`) | HiGHS |
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 |
450
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 |
451
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 |
452
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 |
453
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 |
454
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 |
455
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 |
456
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 |
@@ -468,6 +457,8 @@ npx tsx examples/node/09-transportation-problem.ts
468
457
  npx tsx examples/node/10-diet-sensitivity.ts
469
458
  npx tsx examples/node/11-shift-scheduling.ts
470
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
471
462
 
472
463
  # Local browser demo: http://localhost:5173/demo/
473
464
  npm run dev
@@ -477,7 +468,7 @@ The example sources import the repository's `src/` modules. In an application us
477
468
 
478
469
  ---
479
470
 
480
- ## 🧪 Testing
471
+ ## 🧪 Testing & Reproducible Benchmarks
481
472
 
482
473
  Run the repository's unit and integration tests, and build the distributions:
483
474
 
@@ -489,11 +480,48 @@ npm test
489
480
  npm run build
490
481
  ```
491
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.
517
+
492
518
  ---
493
519
 
494
520
  ## 📄 License
495
521
 
496
522
  - **`@ohtaman/washi`**: [MIT License](./LICENSE)
497
- - **HiGHS** (default engine): MIT License
498
- - **Z3** (optional plugin): MIT License
499
- - **GLPK** (optional plugin): GPL-3.0 License
523
+ - **HiGHS** (default engine, [ERGO-Code/HiGHS](https://github.com/ERGO-Code/HiGHS)): MIT License
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.