@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.
- package/README.ja.md +149 -121
- package/README.md +149 -121
- package/README.zh-CN.md +150 -122
- package/THIRD_PARTY_LICENSES.md +3 -4
- package/dist/browser/_washi-node-only-BvRk9kiK.js +0 -0
- package/dist/browser/browser-runtime-TLCY0hkc.js +1793 -0
- package/dist/browser/highs-worker.js +21 -3
- package/dist/browser/highs.wasm +0 -0
- package/dist/browser/highs.wasm.LICENSE +1 -2
- package/dist/browser/loader-DewlSho8.js +643 -0
- package/dist/browser/solvers/glpk.js +433 -286
- package/dist/browser/solvers/glpk.min.js +1 -1
- package/dist/browser/solvers/z3.js +1008 -981
- package/dist/browser/solvers/z3.min.js +2 -2
- package/dist/browser/washi.js +2778 -673
- package/dist/browser/washi.min.js +3 -4
- package/dist/lib/_washi-node-only-BvRk9kiK.js +0 -0
- package/dist/lib/highs-browser-runtime.js +2120 -4124
- package/dist/lib/highs-node-runtime.js +24 -1
- package/dist/lib/highs-runtime.js +2 -0
- package/dist/lib/highs-worker.js +21 -3
- package/dist/lib/highs.wasm +0 -0
- package/dist/lib/highs.wasm.LICENSE +1 -2
- package/dist/lib/loader-BZUQNvh4.js +649 -0
- package/dist/lib/registry-C20sxTwm.js +964 -0
- package/dist/lib/solvers/glpk.js +201 -90
- package/dist/lib/solvers/z3.js +399 -259
- package/dist/lib/washi.js +2363 -1056
- package/dist/types/decomposition/benders.d.ts +32 -0
- package/dist/types/decomposition/benders.d.ts.map +1 -0
- package/dist/types/decomposition/column-generation.d.ts +34 -0
- package/dist/types/decomposition/column-generation.d.ts.map +1 -0
- package/dist/types/decomposition/index.d.ts +4 -0
- package/dist/types/decomposition/index.d.ts.map +1 -0
- package/dist/types/decomposition/types.d.ts +230 -0
- package/dist/types/decomposition/types.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/modeler/big-m.d.ts +75 -0
- package/dist/types/modeler/big-m.d.ts.map +1 -0
- package/dist/types/modeler/boolean-expr.d.ts +103 -14
- package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
- package/dist/types/modeler/constraint.d.ts +47 -9
- package/dist/types/modeler/constraint.d.ts.map +1 -1
- package/dist/types/modeler/expression.d.ts +123 -28
- package/dist/types/modeler/expression.d.ts.map +1 -1
- package/dist/types/modeler/index.d.ts +11 -6
- package/dist/types/modeler/index.d.ts.map +1 -1
- package/dist/types/modeler/linearizer.d.ts +15 -0
- package/dist/types/modeler/linearizer.d.ts.map +1 -0
- package/dist/types/modeler/lp-writer.d.ts +30 -3
- package/dist/types/modeler/lp-writer.d.ts.map +1 -1
- package/dist/types/modeler/model.d.ts +623 -131
- package/dist/types/modeler/model.d.ts.map +1 -1
- package/dist/types/modeler/objective.d.ts +59 -0
- package/dist/types/modeler/objective.d.ts.map +1 -0
- package/dist/types/modeler/parser.d.ts +34 -10
- package/dist/types/modeler/parser.d.ts.map +1 -1
- package/dist/types/modeler/quad-expr.d.ts +26 -7
- package/dist/types/modeler/quad-expr.d.ts.map +1 -1
- package/dist/types/modeler/solution-validator.d.ts +93 -0
- package/dist/types/modeler/solution-validator.d.ts.map +1 -0
- package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
- package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -1
- package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/driver.d.ts +232 -0
- package/dist/types/modeler/solvers/highs/driver.d.ts.map +1 -0
- package/dist/types/modeler/solvers/highs/index.d.ts +1 -0
- package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/loader.d.ts +38 -13
- package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -1
- package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/solver.d.ts +12 -5
- package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
- package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
- package/dist/types/modeler/solvers/mock.d.ts +14 -5
- package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
- package/dist/types/modeler/solvers/types.d.ts +73 -11
- package/dist/types/modeler/solvers/types.d.ts.map +1 -1
- package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
- package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
- package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
- package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
- package/dist/types/modeler/types.d.ts +86 -1
- package/dist/types/modeler/types.d.ts.map +1 -1
- package/dist/types/modeler/validation.d.ts +56 -0
- package/dist/types/modeler/validation.d.ts.map +1 -0
- package/dist/types/modeler/variable.d.ts +48 -14
- package/dist/types/modeler/variable.d.ts.map +1 -1
- package/docs/examples/diet-sensitivity.md +90 -0
- package/docs/examples/ecommerce-coupons.md +106 -0
- package/docs/examples/index.md +57 -0
- package/docs/examples/knapsack.md +60 -0
- package/docs/examples/portfolio-qp.md +83 -0
- package/docs/examples/production.md +51 -0
- package/docs/examples/shift-scheduling.md +175 -0
- package/docs/examples/smt-security.md +74 -0
- package/docs/examples/transportation.md +90 -0
- package/docs/examples/traveling-salesperson.md +173 -0
- package/docs/examples/unsat-core.md +64 -0
- package/docs/examples/vehicle-routing.md +206 -0
- package/docs/getting-started/ai-agents.md +271 -0
- package/docs/getting-started/cross-origin.md +87 -0
- package/docs/getting-started/index.md +116 -0
- package/docs/getting-started/runtimes.md +227 -0
- package/docs/guide/constraints.md +177 -0
- package/docs/guide/decomposition.md +399 -0
- package/docs/guide/expressions.md +204 -0
- package/docs/guide/objectives.md +170 -0
- package/docs/guide/unsat-core.md +154 -0
- package/docs/guide/variables.md +188 -0
- package/docs/ja/getting-started/ai-agents.md +270 -0
- package/docs/solvers/custom-solver.md +525 -0
- package/docs/solvers/glpk.md +66 -0
- package/docs/solvers/highs.md +115 -0
- package/docs/solvers/index.md +42 -0
- package/docs/solvers/z3.md +78 -0
- package/docs/zh/getting-started/ai-agents.md +270 -0
- package/llms-full.txt +4065 -0
- package/llms.txt +201 -0
- package/package.json +32 -16
- package/skills/washi/SKILL.md +197 -0
- package/dist/browser/browser-runtime-D13gHOT5.js +0 -4189
- package/dist/browser/loader-CVt4CloI.js +0 -58
- package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
- package/dist/lib/constraint-BRuB3ooC.js +0 -90
- package/dist/lib/loader-Dti0v9yC.js +0 -50
- 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
|
|
5
|
+
<img alt="WaSHI - Algebraic Modeler for Mathematical Optimization & Constraint Programming" src="./assets/washi-header-light.png" width="420" />
|
|
6
6
|
</picture>
|
|
7
|
-
<p><strong>Algebraic
|
|
7
|
+
<p><strong>Algebraic Modeler for Mathematical Optimization & Constraint Programming</strong></p>
|
|
8
8
|
<p>
|
|
9
|
-
<em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Quadratic Programming (QP) • SMT & Conflict Diagnosis</em>
|
|
9
|
+
<em>Linear Programming (LP) • Mixed-Integer Programming (MIP) • Convex Quadratic Programming (QP) • SMT & 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.
|
|
42
|
-
m.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
111
|
+
### 1. Multidimensional Variables, Indicator Constraints & Solution Validation
|
|
100
112
|
|
|
101
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
218
|
-
m.
|
|
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.
|
|
222
|
-
m.
|
|
223
|
-
m.
|
|
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,
|
|
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(
|
|
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.
|
|
247
|
+
m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
|
|
249
248
|
|
|
250
249
|
// Rule 2: Customer requires rush delivery
|
|
251
|
-
m.
|
|
250
|
+
m.constraint(rushDelivery, "rush_required");
|
|
252
251
|
|
|
253
252
|
// Rule 3: Rush delivery requires at least 8 engineers
|
|
254
|
-
m.
|
|
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.
|
|
256
|
+
m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
|
|
258
257
|
|
|
259
258
|
// Rule 5: Innocent bystander rule
|
|
260
|
-
m.
|
|
259
|
+
m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
|
|
261
260
|
|
|
262
|
-
|
|
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("
|
|
266
|
-
// Output: ["
|
|
267
|
-
//
|
|
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.
|
|
286
|
-
m.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
408
|
+
Complete standalone examples for ESM, script tags, and CodePen are available in [`examples/distribution/`](examples/distribution/README.md).
|
|
419
409
|
|
|
420
|
-
|
|
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
|
-
|
|
412
|
+
## 🛠️ Custom Solvers & Extensibility
|
|
428
413
|
|
|
429
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
420
|
+
## 📚 Documentation
|
|
436
421
|
|
|
437
|
-
|
|
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
|
-
-
|
|
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
|
|
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.
|