@ohtaman/washi 0.5.0 → 0.7.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 (107) hide show
  1. package/README.ja.md +103 -386
  2. package/README.md +104 -387
  3. package/README.zh-CN.md +103 -386
  4. package/THIRD_PARTY_LICENSES.md +5 -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 +1 -1
  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 +3 -3
  12. package/dist/browser/solvers/glpk.min.js +1 -1
  13. package/dist/browser/solvers/z3.js +7 -1
  14. package/dist/browser/solvers/z3.min.js +1 -1
  15. package/dist/browser/washi.js +1708 -540
  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 +1 -1
  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-BEfvpZW3.js → registry-D7TZ3Wh_.js} +142 -59
  26. package/dist/lib/solvers/glpk.js +4 -4
  27. package/dist/lib/solvers/z3.js +1 -1
  28. package/dist/lib/washi.js +1576 -465
  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 +4 -3
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  40. package/dist/types/modeler/constraint.d.ts +11 -0
  41. package/dist/types/modeler/constraint.d.ts.map +1 -1
  42. package/dist/types/modeler/expression.d.ts +57 -26
  43. package/dist/types/modeler/expression.d.ts.map +1 -1
  44. package/dist/types/modeler/index.d.ts +4 -3
  45. package/dist/types/modeler/index.d.ts.map +1 -1
  46. package/dist/types/modeler/linearizer.d.ts +15 -0
  47. package/dist/types/modeler/linearizer.d.ts.map +1 -0
  48. package/dist/types/modeler/lp-writer.d.ts +3 -1
  49. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  50. package/dist/types/modeler/model.d.ts +159 -8
  51. package/dist/types/modeler/model.d.ts.map +1 -1
  52. package/dist/types/modeler/parser.d.ts +25 -5
  53. package/dist/types/modeler/parser.d.ts.map +1 -1
  54. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  55. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts +2 -1
  56. package/dist/types/modeler/solvers/highs/browser-runtime.d.ts.map +1 -1
  57. package/dist/types/modeler/solvers/highs/driver.d.ts +232 -0
  58. package/dist/types/modeler/solvers/highs/driver.d.ts.map +1 -0
  59. package/dist/types/modeler/solvers/highs/index.d.ts +1 -0
  60. package/dist/types/modeler/solvers/highs/index.d.ts.map +1 -1
  61. package/dist/types/modeler/solvers/highs/loader.d.ts +4 -4
  62. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  63. package/dist/types/modeler/solvers/highs/node-runtime.d.ts +2 -1
  64. package/dist/types/modeler/solvers/highs/node-runtime.d.ts.map +1 -1
  65. package/dist/types/modeler/solvers/highs/solver.d.ts +1 -1
  66. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  67. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  68. package/dist/types/modeler/solvers/types.d.ts +4 -0
  69. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  70. package/dist/types/modeler/types.d.ts +27 -0
  71. package/dist/types/modeler/types.d.ts.map +1 -1
  72. package/docs/examples/diet-sensitivity.md +15 -14
  73. package/docs/examples/ecommerce-coupons.md +15 -22
  74. package/docs/examples/index.md +18 -12
  75. package/docs/examples/knapsack.md +8 -7
  76. package/docs/examples/portfolio-qp.md +11 -4
  77. package/docs/examples/production.md +4 -4
  78. package/docs/examples/shift-scheduling.md +20 -7
  79. package/docs/examples/smt-security.md +16 -4
  80. package/docs/examples/transportation.md +46 -19
  81. package/docs/examples/traveling-salesperson.md +157 -0
  82. package/docs/examples/unsat-core.md +15 -5
  83. package/docs/examples/vehicle-routing.md +208 -0
  84. package/docs/getting-started/ai-agents.md +33 -18
  85. package/docs/getting-started/cross-origin.md +56 -28
  86. package/docs/getting-started/index.md +40 -69
  87. package/docs/getting-started/runtimes.md +66 -61
  88. package/docs/guide/constraints.md +47 -41
  89. package/docs/guide/decomposition.md +267 -0
  90. package/docs/guide/expressions.md +90 -39
  91. package/docs/guide/formulation-tips.md +212 -0
  92. package/docs/guide/objectives.md +7 -4
  93. package/docs/guide/unsat-core.md +28 -9
  94. package/docs/guide/variables.md +33 -38
  95. package/docs/ja/getting-started/ai-agents.md +32 -18
  96. package/docs/solvers/custom-solver.md +1 -1
  97. package/docs/solvers/highs.md +48 -5
  98. package/docs/solvers/index.md +4 -4
  99. package/docs/solvers/z3.md +3 -0
  100. package/docs/zh/getting-started/ai-agents.md +32 -18
  101. package/llms-full.txt +1536 -478
  102. package/llms.txt +28 -22
  103. package/package.json +19 -16
  104. package/skills/washi/SKILL.md +21 -17
  105. package/dist/browser/browser-runtime-D13gHOT5.js +0 -4189
  106. package/dist/browser/loader-CtaDHef_.js +0 -273
  107. package/dist/lib/loader-DmNC_UNk.js +0 -279
package/README.zh-CN.md CHANGED
@@ -2,9 +2,9 @@
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 - 面向 TypeScript 与 JavaScript 的数理优化代数建模器 (Modeler)" src="./assets/washi-header-light.png" width="420" />
5
+ <img alt="WaSHI - TypeScript 数理优化建模器" src="./assets/washi-header-light.png" width="420" />
6
6
  </picture>
7
- <p><strong>面向 TypeScript 与 JavaScript 的代数建模器</strong></p>
7
+ <p><strong>TypeScript 数理优化建模器</strong></p>
8
8
  <p>
9
9
  <em>线性规划 (LP) • 混合整数规划 (MIP) • 凸二次规划 (QP) • SMT逻辑约束与冲突诊断</em>
10
10
  </p>
@@ -16,19 +16,45 @@
16
16
  <p>
17
17
  <a href="https://www.npmjs.com/package/@ohtaman/washi"><img src="https://img.shields.io/npm/v/@ohtaman/washi?color=crimson&label=npm" alt="npm version" /></a>
18
18
  <a href="https://github.com/ohtaman/washi/actions/workflows/distribution.yml"><img src="https://github.com/ohtaman/washi/actions/workflows/distribution.yml/badge.svg" alt="CI Status" /></a>
19
- <a href="https://ohtaman.github.io/washi/zh/"><img src="https://img.shields.io/badge/docs-online-blue" alt="docs" /></a>
20
- <a href="https://ohtaman.github.io/washi/demo/"><img src="https://img.shields.io/badge/demo-playground-brightgreen" alt="live demo" /></a>
19
+ <a href="https://washi.ohtaman.dev/zh/"><img src="https://img.shields.io/badge/docs-online-blue" alt="docs" /></a>
20
+ <a href="https://washi.ohtaman.dev/demo/"><img src="https://img.shields.io/badge/demo-playground-brightgreen" alt="live demo" /></a>
21
21
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license" /></a>
22
22
  </p>
23
23
  </div>
24
24
 
25
25
  ---
26
26
 
27
- **WaSHI** (WebAssembly-backed, Solver-independent, High-level modeling Interface) 是面向 TypeScript / JavaScript 的代数建模器,受到 Python **PuLP** 和 Julia **JuMP** 的启发。你可以在应用代码中定义变量、目标函数、算术约束和逻辑约束。WaSHI 负责构建模型、转换为求解器输入,并将结果映射回变量;优化与可满足性检查由 **HiGHS**、**Z3** 和 **GLPK** 完成。
27
+ **WaSHI** (WebAssembly-backed, Solver-independent, High-level modeling Interface) 是面向 TypeScript / JavaScript 的数理优化与约束编程代数建模器,受到 Python **PuLP** 的启发。你可以在应用代码中定义变量、目标函数和约束,并在 Node.js 或浏览器本地使用 **HiGHS**(默认)、**Z3** 或 **GLPK** 求解。
28
28
 
29
- HiGHS 是默认求解器;Z3 和 GLPK 是可选插件。软件包提供 Node.js 和浏览器分发版本,并包含 WebAssembly 资源。部署要求请参阅[浏览器使用说明](#-浏览器与-web-worker-使用指南)。
29
+ ## ✨ 核心特性
30
30
 
31
- [在线文档](https://ohtaman.github.io/washi/zh/) · [试用浏览器演示](https://ohtaman.github.io/washi/demo/) · [AI 编程助手快速参考](docs/zh/getting-started/ai-agents.md) · [Agent Skill (`SKILL.md`)](skills/washi/SKILL.md) · [示例源码与运行方法](https://github.com/ohtaman/washi/blob/main/examples/README.md) · [ESM / script / CodePen 示例](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)
31
+ - **直观的代数语法**:通过 `` expr`30 * ${x} + 50 * ${y} <= 40` `` 与求和、点积(`sum`, `dot`)清晰编写公式与约束。
32
+ - **跨 Node.js 与浏览器本地运行**:基于 WebAssembly,无需单独部署 Python 服务或本地编译 C/C++。
33
+ - **求解器无关**:内置 **HiGHS**,支持按需加载 **Z3**、**GLPK** 插件或自定义求解器适配器。
34
+ - **可满足性判定与冲突诊断**:支持逻辑与算术约束的可满足性判定,并在模型不可行时定位矛盾约束。
35
+ - **大规模分解算法**:内置列生成法与 Benders 分解支持。
36
+
37
+ ## 📚 文档与链接
38
+
39
+ - **[在线文档](https://washi.ohtaman.dev/zh/)**:建模指南、求解器说明与运行环境配置
40
+ - **[案例展示](https://washi.ohtaman.dev/zh/showcase/) & [浏览器演示](https://washi.ohtaman.dev/demo/)**:在线体验交互式应用与浏览器建模演练场
41
+ - **[示例目录](https://washi.ohtaman.dev/zh/examples/)**:各类问题的建模示例与可运行源码([`examples/`](examples/README.md))
42
+ - **[AI 编程助手快速参考](docs/zh/getting-started/ai-agents.md) · [Agent Skill (`SKILL.md`)](skills/washi/SKILL.md) · [`llms.txt`](https://washi.ohtaman.dev/llms.txt) / [`llms-full.txt`](https://washi.ohtaman.dev/llms-full.txt)**:面向 AI 编程助手的速查指南、技能定义与机器可读文档
43
+
44
+ ---
45
+
46
+ ## 📦 安装与快速开始
47
+
48
+ ```bash
49
+ npm install @ohtaman/washi
50
+
51
+ # 可选求解器插件
52
+ npm install z3-solver # 用于 SMT 逻辑与冲突诊断
53
+ npm install glpk.js # 用于 GLPK 求解器 (GPL-3.0)
54
+
55
+ # 可选:为 AI 编程助手(Claude Code, Cursor, Codex 等)安装建模技能
56
+ npx skills add ohtaman/washi
57
+ ```
32
58
 
33
59
  ```ts
34
60
  import { Model, expr, maximize } from "@ohtaman/washi";
@@ -47,446 +73,137 @@ console.log(`生产计划: x=${x.value}, y=${y.value} (最大利润: $${m.object
47
73
 
48
74
  ---
49
75
 
50
- ## 🎯 WaSHI 提供的功能
51
-
52
- 1. **代数模型构建**:使用领域变量构造函数(`m.binary`、`m.integer`、`m.continuous`)、标签模板字面量(`expr`)、向量聚合函数(`dot`、`sum`)以及一等约束(`m.constraint`)构建优化模型。由于 JavaScript 不支持运算符重载,`` expr`...` `` 采用类似参数化 SQL 模板的方式,在解析中缀数学运算符(`+`、`-`、`*`、`^2`、`<=`、`>=`、`==`)的同时,将 `${...}` 插值直接绑定为强类型的 `Var` 或表达式对象引用,从而保留静态类型检查、IDE 重命名支持以及多维索引访问(`${flow[i][j]}`)。
53
- 2. **求解器适配器**:通过共同的建模 API 使用 HiGHS、Z3 或 GLPK,也可以[注册自己的适配器](https://github.com/ohtaman/washi/blob/main/docs/zh/solvers/custom-solver.md)。需要选择支持相应问题类型的求解器;不同求解器的能力并不相同。
54
- 3. **本地执行与 WebAssembly 分发**:使用本地求解器适配器时,可在应用内直接求解,无需单独的 Python 服务或本地 C/C++ 编译——这消除了在快速开发和交付交互式工具时的后端基础设施瓶颈,也便于在客户端直接与浏览器内 LLM(如 WebGPU / WebLLM)协同工作。浏览器部署需要包含生成的 JS、Worker 和 WASM 资源。离线使用还需要这些资源可在本地访问或已缓存。Z3 有额外的浏览器隔离要求。
55
- 4. **命名约束与 SMT 结果**:显式使用 Z3 检查可满足性,并通过冲突约束(`conflicts` / `unsatCore`)调查冲突约束。返回的是一个冲突约束子集,不一定是最小子集。
76
+ ## ⚙️ 求解器概览
56
77
 
57
- 模型构建与转换由 WaSHI 负责。支持的问题类型、数值行为和求解时间取决于所选求解器、配置及具体问题。建模开销与求解器执行时间应分别测量;某个模型上的结果并不构成通用性能保证。
58
-
59
- ---
60
-
61
- ## ⚙️ 求解器适配器与支持范围
62
-
63
- | 适配器 | 引擎许可证 | 支持的问题类型 | 引擎作用 | 集成方式 |
78
+ | 求解器 | 许可证 | 支持问题类型 | 主要用途 | 引入方式 |
64
79
  | :--- | :--- | :--- | :--- | :--- |
65
- | **HiGHS** | **MIT** | **LP, MIP, 凸QP** | 数值优化 | 默认内置 |
66
- | **Z3** | **MIT** | **SMT, SAT, 逻辑, MIP** | 可满足性检查、Unsat Core、优化 | 可选插件 (`@ohtaman/washi/solvers/z3`) |
67
- | **GLPK** | **GPL-3.0** | **LP, MIP** | 线性与混合整数优化 | 可选插件 (`@ohtaman/washi/solvers/glpk`) |
68
- | **Mock** | **MIT** | — | 为集成测试提供模拟响应 | 内置;不实际求解模型 |
69
-
70
- ---
80
+ | **[HiGHS](https://github.com/ERGO-Code/HiGHS)** | MIT | LP, MIP, 凸QP | 数值优化与弹性冲突修复诊断 | 默认内置 |
81
+ | **[Z3](https://github.com/Z3Prover/z3)** | MIT | SMT, SAT, 逻辑, MIP | 逻辑规则、可满足性判定与精确 `unsatCore` | `import "@ohtaman/washi/solvers/z3"` |
82
+ | **[GLPK](https://www.gnu.org/software/glpk/)** | GPL-3.0 | LP, MIP | 线性与混合整数规划 | `import "@ohtaman/washi/solvers/glpk"` |
83
+ | **Mock** | MIT | — | 单元与集成测试模拟响应 | 内置 (`MockSolver`) |
71
84
 
72
- ## 📦 安装
73
-
74
- ```bash
75
- # npm
76
- npm install @ohtaman/washi
77
-
78
- # pnpm
79
- pnpm add @ohtaman/washi
80
-
81
- # yarn
82
- yarn add @ohtaman/washi
83
-
84
- # bun
85
- bun add @ohtaman/washi
86
- ```
87
-
88
- *(可选)* 若需使用 Z3 或 GLPK 求解器,可按需安装对应依赖包:
89
-
90
- ```bash
91
- npm install z3-solver # 用于 SMT 与冲突诊断
92
- npm install glpk.js # 用于 GLPK 求解器 (GPL-3.0)
93
- ```
94
-
95
- *(可选)* 面向 AI 编程助手(Claude Code、Antigravity、Cursor、Codex 等),`@ohtaman/washi` 随包附带了可复用的 [`skills/washi/SKILL.md`](skills/washi/SKILL.md) 建模与校验技能及 [AI 编程助手快速参考](docs/zh/getting-started/ai-agents.md):
96
-
97
- ```bash
98
- mkdir -p .agents/skills/washi
99
- cp node_modules/@ohtaman/washi/skills/washi/SKILL.md .agents/skills/washi/SKILL.md
100
- ```
85
+ 导入对应插件后,可在 `new Model(...)`、`m.solve(...)` 或 `m.check(...)` 中传入 `{ solver: "z3" }` 或 `{ solver: "glpk" }`。
101
86
 
102
87
  ---
103
88
 
104
- ## 🚀 快速上手指南
89
+ ## 🚀 常用用法示例
105
90
 
106
- ### 1. 多维变量、条件指示约束 (`m.indicator`) 与解验证 (`m.validateSolution`)
91
+ ### 1. 求和、点积(`sum` / `dot`)与条件约束
107
92
 
108
- 一行代码声明一维字典或二维变量矩阵,通过 `m.indicator()` 表达 Big-M 条件规则,结合 `onProgress` / `AbortSignal` 控制求解过程,并通过 `m.validateSolution()` 独立校验解的合法性:
93
+ 使用 `dot` / `sum` 组合线性表达式,并通过 `m.indicator()` 表达条件规则:
109
94
 
110
95
  ```ts
111
- import { Model, expr } from "@ohtaman/washi";
112
-
113
- const m = new Model("FacilityPlanning");
114
-
115
- // 1D 字典形式的二元变量
116
- const openCity = m.binary(["Tokyo", "Osaka", "Nagoya"]);
117
-
118
- // 2D 运输量矩阵: [工厂] x [门店]
119
- const flow = m.continuous(["F1", "F2"], ["StoreA", "StoreB"], { lb: 0, ub: 1000 });
120
-
121
- // 2D 网格坐标 (例如 8x8 棋盘)
122
- const board = m.binary(8, 8, { prefix: "queen" });
123
-
124
- // Big-M 条件约束: 若工厂关闭 (openCity['Tokyo'] == 0),则 flow <= 0
125
- // (省略 M 时,WaSHI 会根据变量的有限上下界自动推导紧凑安全的 Big-M)
126
- m.indicator(openCity["Tokyo"], expr`${flow["F1"]["StoreA"]} <= 0`, {
127
- activeValue: 0,
128
- name: "tokyo_closed_no_flow",
129
- });
130
-
131
- // 支持实时进度回调 (onProgress) 与 AbortSignal 取消
132
- const controller = new AbortController();
133
- await m.solve({
134
- signal: controller.signal,
135
- onProgress: (ev) => {
136
- // 实时指标: ev.elapsedTime, ev.objectiveValue, ev.objectiveBound, ev.mipGap
137
- },
138
- });
139
-
140
- // 独立校验变量边界、整数性以及线性/逻辑约束
141
- const report = m.validateSolution();
142
- console.log("解验证通过?", report.valid);
143
- ```
144
-
145
- ---
96
+ import { Model, expr, minimize } from "@ohtaman/washi";
146
97
 
147
- ### 2. 投资组合风险最小化 (凸二次规划: QP)
98
+ const m = new Model("TransportPlanning");
99
+ const factories = ["Tokyo", "Osaka"];
100
+ const stores = ["StoreA", "StoreB"];
101
+ const costs = {
102
+ Tokyo: { StoreA: 4, StoreB: 7 },
103
+ Osaka: { StoreA: 8, StoreB: 3 },
104
+ };
148
105
 
149
- 在满足预期收益与预算约束的前提下,基于协方差矩阵最小化投资组合风险:
106
+ const open = m.binary(factories);
107
+ const ship = m.continuous(factories, stores, { lb: 0, ub: 500 });
150
108
 
151
- ```ts
152
- import { Model, QuadExpr, sum, dot, minimize, expr } from "@ohtaman/washi";
153
-
154
- const m = new Model("MarkowitzPortfolio");
155
- const assets = ["Tech", "Bonds", "RealEstate"];
156
- const returns = [0.14, 0.04, 0.08];
157
-
158
- // 决策变量: 各资产投资权重 (0.0 ~ 1.0)
159
- const w = m.continuous(assets, { lb: 0, ub: 1 });
160
-
161
- // 1. 预算约束: 资金分配总额 100%
162
- m.constraint(expr`${sum(assets, (a) => w[a])} == 1.0`, "budget");
163
-
164
- // 2. 目标回报率: 组合最低回报率 >= 8.5%
165
- const retMap = Object.fromEntries(assets.map((a, i) => [a, returns[i]]));
166
- m.constraint(expr`${dot(retMap, w)} >= 0.085`, "min_return");
167
-
168
- // 3. 资产协方差矩阵 (风险与相关性)
169
- const cov = [
170
- [0.040, 0.002, 0.015],
171
- [0.002, 0.005, 0.002],
172
- [0.015, 0.002, 0.025],
173
- ];
174
-
175
- // 二次目标函数: w^T * Cov * w
176
- let risk = new QuadExpr();
177
- for (let i = 0; i < assets.length; i++) {
178
- for (let j = 0; j < assets.length; j++) {
179
- risk = risk.add(w[assets[i]].mul(w[assets[j]]).mul(cov[i][j]));
180
- }
181
- }
182
- m.objective(minimize(risk));
109
+ // 若东京工厂关闭 (open["Tokyo"] == 0),从东京工厂发出的总运输量必须为 0
110
+ m.indicator(open["Tokyo"], expr`sum(${ship["Tokyo"]}) <= 0`, { activeValue: 0 });
111
+ m.constraint(expr`${ship["Tokyo"]["StoreA"]} + ${ship["Osaka"]["StoreA"]} >= 100`, "demand_A");
183
112
 
113
+ m.objective(minimize(expr`dot(${costs}, ${ship})`));
184
114
  await m.solve();
185
- console.log(`最小方差: ${m.objectiveValue?.toFixed(6)}`);
186
- assets.forEach((name) => {
187
- console.log(` ${name}: ${((w[name].value || 0) * 100).toFixed(2)}%`);
188
- });
189
- ```
190
115
 
191
- ---
116
+ console.log("Tokyo -> StoreA:", ship["Tokyo"]["StoreA"].value);
117
+ ```
192
118
 
193
- ### 3. SMT 逻辑与规则引擎 (集成 Z3)
119
+ ### 2. 凸二次规划 (QP)
194
120
 
195
- 原生支持布尔命题变量(`BoolVar`)、逻辑连接词(`and`, `or`, `implies`, `not`, `xor`)与数值算术约束的混合表达:
121
+ 在线性约束下最小化二次目标函数:
196
122
 
197
123
  ```ts
198
- import { Model, not, expr } from "@ohtaman/washi";
199
- import "@ohtaman/washi/solvers/z3";
200
-
201
- const m = new Model({ name: "SmartSecurityRules" });
202
-
203
- const isNight = m.bool("isNight");
204
- const motion = m.bool("motion");
205
- const alarm = m.bool("alarm");
206
- const siren = m.bool("siren");
207
- const lux = m.integer("lux", { lb: 0 });
124
+ import { Model, expr, minimize } from "@ohtaman/washi";
208
125
 
209
- // 规则定义
210
- m.constraint(lux.le(50).implies(isNight), "dark_implies_night");
211
- m.constraint(motion.and(isNight).and(alarm).implies(siren), "alarm_rule");
126
+ const m = new Model("PortfolioQP");
127
+ const x = m.continuous("tech", { lb: 0, ub: 1 });
128
+ const y = m.continuous("bonds", { lb: 0, ub: 1 });
212
129
 
213
- // 传感器当前输入数据
214
- m.constraint(expr`${lux} <= 10`, "sensor_dark");
215
- m.constraint(motion, "sensor_motion");
216
- m.constraint(alarm, "alarm_on");
130
+ m.constraint(expr`${x} + ${y} == 1`, "budget");
131
+ m.constraint(expr`0.14 * ${x} + 0.04 * ${y} >= 0.08`, "min_return");
132
+ m.objective(minimize(expr`0.04 * ${x}^2 + 0.005 * ${y}^2`));
217
133
 
218
- // 无目标函数直接判定可满足性 (SAT)
219
- const res = await m.check({ solver: "z3" });
220
-
221
- console.log(`系统状态满足? ${res.status === "Sat"}`); // true
222
- console.log(`警报响起? ${m.values.get("siren")}`); // true
134
+ await m.solve();
135
+ console.log(`Tech: ${x.value?.toFixed(2)}, Bonds: ${y.value?.toFixed(2)}`);
223
136
  ```
224
137
 
225
- ---
226
-
227
- ### 4. 约束冲突根因诊断 (`conflicts` / `unsatCore`)
138
+ ### 3. SMT 逻辑规则与冲突诊断 (`m.check`)
228
139
 
229
- 当规则或约束之间存在矛盾时,Z3 能够提取引发冲突的**矛盾约束子集**(Unsat Core / Conflicts,不保证最小性;需使用 Z3):
140
+ 表达逻辑蕴含规则并排查不可行原因:
141
+ - **HiGHS(默认)**:提取**弹性修复集合**(`res.conflicts`),即松弛后可恢复可行性的约束。
142
+ - **Z3**:提取精确的 **SMT 不可满足核心**(`res.unsatCore` / `res.conflicts`),即相互矛盾的约束子集。
230
143
 
231
144
  ```ts
232
- import { Model, expr } from "@ohtaman/washi";
233
- import "@ohtaman/washi/solvers/z3";
145
+ import { Model, implies, expr } from "@ohtaman/washi";
146
+ // import "@ohtaman/washi/solvers/z3"; // 可选:使用 Z3 提取精确 SMT unsatCore
234
147
 
235
- const m = new Model({ name: "ConflictDiagnosis", solver: "z3" });
148
+ const m = new Model("StaffingRules");
236
149
  const engineers = m.integer("engineers", { lb: 0, ub: 20 });
237
150
  const budget = m.continuous("budget", { lb: 0, ub: 100000 });
238
- const rushDelivery = m.bool("rushDelivery");
151
+ const rush = m.bool("rush");
239
152
 
240
- // 规则 1: 每位工程师成本 $8,000
241
153
  m.constraint(expr`8000 * ${engineers} <= ${budget}`, "budget_limit");
154
+ m.constraint(rush, "rush_required");
155
+ m.constraint(implies(rush, expr`${engineers} >= 8`), "rush_staffing");
156
+ m.constraint(expr`${budget} <= 50000`, "budget_cap"); // 冲突: 8人 * $8,000 > $50,000
157
+ m.constraint(expr`${engineers} >= 1`, "min_staff"); // 无关规则
242
158
 
243
- // 规则 2: 客户要求加急交付
244
- m.constraint(rushDelivery, "rush_required");
245
-
246
- // 规则 3: 加急交付需至少配备 8 名工程师
247
- m.constraint(rushDelivery.implies(engineers.ge(8)), "rush_staffing_rule");
248
-
249
- // 规则 4: 董事会严格预算上限 $50,000 (冲突点: 8人 * $8,000 = $64,000 > $50,000!)
250
- m.constraint(expr`${budget} <= 50000`, "strict_board_budget_cap");
251
-
252
- // 规则 5: 最低 1 名员工用工底线(无关规则)
253
- m.constraint(expr`${engineers} >= 1`, "minimum_one_employee");
254
-
255
- const res = await m.check({ solver: "z3" });
256
-
257
- console.log(`求解状态: ${res.status}`); // "Unsat"
258
- console.log("冲突约束子集:", res.conflicts);
259
- // 输出: ["budget_limit", "rush_required", "rush_staffing_rule", "strict_board_budget_cap"]
260
- // 注:无关的 "minimum_one_employee" 被准确排除在外!
261
- ```
262
-
263
- ---
264
-
265
- ### 5. 使用 GLPK 求解器 (GPL-3.0)
266
-
267
- 使用 GNU Linear Programming Kit (GLPK) 求解器插件:
268
-
269
- ```ts
270
- import { Model, expr, maximize } from "@ohtaman/washi";
271
- import "@ohtaman/washi/solvers/glpk";
272
-
273
- const m = new Model({ solver: "glpk" });
274
- const x = m.integer("x", { lb: 0 });
275
- const y = m.integer("y", { lb: 0 });
276
-
277
- m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
278
- m.constraint(expr`${x} + 2 * ${y} <= 40`, "wood_limit");
279
- m.constraint(expr`2 * ${x} + ${y} <= 50`, "labor_limit");
280
-
281
- const status = await m.solve();
282
- console.log(`求解状态: ${status}`); // "Optimal"
283
- console.log(`目标函数值: ${m.objectiveValue}`); // 1100
284
- ```
285
-
286
- ---
287
-
288
- ### 6. 向量运算与高效点积 (`dot`)
289
-
290
- 无需嵌套循环,一行代码完成一维和二维变量的线性组合:
291
-
292
- ```ts
293
- import { Model, dot, minimize } from "@ohtaman/washi";
294
-
295
- const m = new Model();
296
- const factories = ["Tokyo", "Osaka"];
297
- const stores = ["StoreA", "StoreB"];
298
-
299
- const costs = {
300
- Tokyo: { StoreA: 4, StoreB: 7 },
301
- Osaka: { StoreA: 8, StoreB: 3 },
302
- };
303
-
304
- const ship = m.continuous(factories, stores);
305
-
306
- // 一行代码最小化总运输成本:
307
- m.objective(minimize(dot(costs, ship)));
308
- await m.solve();
309
-
310
- // 获取所有变量的最优取值结构
311
- const solution = m.getValues(ship);
312
- console.log(solution);
159
+ const res = await m.check(); // 或 await m.check({ solver: "z3" })
160
+ console.log(res.status); // "Unsat"
161
+ console.log(res.conflicts); // HiGHS: ["rush_required"]
313
162
  ```
314
163
 
315
164
  ---
316
165
 
317
166
  ## 🌐 浏览器与 Web Worker 使用指南
318
167
 
319
- HiGHS 默认使用包内的 WASM,**不会自动回退到 CDN**。Node.js 使用已安装的 HiGHS;Vite / webpack 5 可以根据包内的 WASM 和 Worker 引用生成部署文件。
320
-
321
- ### npm 与构建工具
322
-
323
- ```typescript
324
- import { Model } from "@ohtaman/washi";
325
- const model = new Model();
326
- // 定义变量、目标函数和约束
327
- await model.solve();
328
- ```
329
-
330
- 在验证的 Vite / webpack 构建中,不需要手动复制 WASM 或配置 URL。其他构建工具及 SSR 需要单独验证;浏览器求解应在客户端执行。浏览器默认使用 Worker。简单演示可以使用 `model.solve({ useWorker: false })`,但较长的计算会阻塞 UI。
331
-
332
- ### 浏览器原生 ESM(无需应用构建)
333
-
334
- 通过 HTTP 提供整个 `dist/browser` 目录,包括 JS 分块、`highs.wasm` 和 Worker。不需要 Import Maps。
168
+ - **打包工具 (Vite / webpack 5)**:直接 `import { Model } from "@ohtaman/washi"` 即可。浏览器默认在 Web Worker 中求解;传入 `{ useWorker: false }` 可在主线程执行。
169
+ - **原生 ESM / Script 标签 / CDN**:部署 `dist/browser/` 目录(含 `highs.wasm` 与 Worker 资源)或从 CDN 加载。
335
170
 
336
171
  ```html
172
+ <!-- 浏览器原生 ESM -->
337
173
  <script type="module">
338
174
  import { Model } from "./washi/washi.js";
339
- const model = new Model();
340
- // 定义变量、目标函数和约束
341
- await model.solve({ useWorker: false });
175
+ // 可选求解器插件: import "./washi/solvers/glpk.js" 或 "./washi/solvers/z3.js"
176
+ const m = new Model();
177
+ await m.solve();
342
178
  </script>
343
- ```
344
-
345
- ### 普通 script / CodePen
346
179
 
347
- `dist/browser/washi.min.js` 导出全局 API `Washi`。将其 URL 添加到 CodePen 的 External Scripts,或使用:
348
-
349
- ```html
350
- <script src="./washi/washi.min.js"></script>
351
- <script>
352
- const model = new Washi.Model();
353
- // 定义变量、目标函数和约束
354
- model.solve({ useWorker: false }).then(status => console.log(status));
355
- </script>
356
- ```
357
-
358
- 若通过 CDN 加载,可使用 `https://cdn.jsdelivr.net/npm/@ohtaman/washi@<version>/dist/browser/washi.min.js`(待包含相应 API 的版本发布后指定该版本号)。ESM 使用同版本的 `dist/browser/washi.js`。选择 CDN URL 就是显式使用 CDN;本地部署不会自动改用外部 CDN。
359
-
360
- ### 浏览器环境中的求解器插件(GLPK & Z3)
361
-
362
- 浏览器发布包在 `dist/browser/solvers/` 下提供了开箱即用的独立求解器插件:
363
- - **GLPK**: `dist/browser/solvers/glpk.js` (ESM) 与 `dist/browser/solvers/glpk.min.js` (IIFE,挂载全局 `WashiGLPK`)
364
- - **Z3**: `dist/browser/solvers/z3.js` (ESM) 与 `dist/browser/solvers/z3.min.js` (IIFE,挂载全局 `WashiZ3`),附带 `z3-built.js` 和 `z3-built.wasm`
365
-
366
- #### 浏览器 ESM 使用 GLPK
367
- ```html
368
- <script type="module">
369
- import { Model, maximize } from "./washi/washi.js";
370
- import "./washi/solvers/glpk.js";
371
-
372
- const model = new Model({ solver: "glpk" });
373
- const x = model.continuous("x", { lb: 0, ub: 10 });
374
- model.objective(maximize(x));
375
- await model.solve();
376
- console.log("GLPK 求解状态:", model.status);
377
- </script>
378
- ```
379
-
380
- #### 原生脚本标签使用 GLPK
381
- ```html
382
- <script src="./washi/washi.min.js"></script>
383
- <script src="./washi/solvers/glpk.min.js"></script>
384
- <script>
385
- const model = new Washi.Model({ solver: "glpk" });
386
- const x = model.continuous("x", { lb: 0, ub: 10 });
387
- model.objective(Washi.maximize(x));
388
- model.solve().then(status => console.log("GLPK 求解状态:", status));
389
- </script>
180
+ <!-- Script 标签 / CodePen (挂载全局变量 Washi) -->
181
+ <script src="https://cdn.jsdelivr.net/npm/@ohtaman/washi/dist/browser/washi.min.js"></script>
390
182
  ```
391
183
 
392
184
  > [!NOTE]
393
- > **浏览器环境中使用 Z3 的配置要求与跨源隔离**:
394
- > 由于 Z3 的 WebAssembly 引擎依托多线程 (`SharedArrayBuffer`),现代浏览器强制要求跨源隔离(`Cross-Origin-Opener-Policy: same-origin` 与 `Cross-Origin-Embedder-Policy: require-corp`)。关于静态托管(`coi-serviceworker.js`)、离线/内网部署、CSP(`worker-src blob:`)与自定义 WASM URL(`setHighsWasmUrl`)的完整说明,请参阅 [跨源隔离指南](docs/zh/getting-started/cross-origin.md) 与 [运行时环境配置](docs/zh/getting-started/runtimes.md)。
395
-
396
- 完整独立的 ESM、Script 标签与 CodePen 示例请参阅 [`examples/distribution/`](examples/distribution/README.md)。
397
-
398
- ---
399
-
400
- ## 🛠️ 自定义求解器开发与架构扩展
401
-
402
- WaSHI 采用完全解耦的插件式求解器架构。你可以轻松编写并注册自定义求解器适配器(如云端 REST 求解服务、定制 WebAssembly 引擎或商业求解器):
403
-
404
- - 完整开发指南、接口契约(`Solver`, `SolveModelData`, `CheckModelData`, `SolverResult`)与测试实践详见 [自定义求解器开发指南](docs/zh/solvers/custom-solver.md)。
185
+ > **浏览器中使用 Z3**:Z3 的 WebAssembly 引擎依赖 `SharedArrayBuffer`,需要配置跨源隔离响应头(`COOP: same-origin` 与 `COEP: require-corp`)。详见[跨源隔离指南](docs/zh/getting-started/cross-origin.md)与 [`examples/distribution/`](examples/distribution/README.md)。
405
186
 
406
187
  ---
407
188
 
408
- ## 📚 文档指南
189
+ ## 🤝 参与开发与贡献
409
190
 
410
- 完整文档可在 **[ohtaman.github.io/washi/zh/](https://ohtaman.github.io/washi/zh/)** 及仓库的 [`docs/zh/`](docs/zh/)([英文版 `docs/`](docs/))中查阅:
411
-
412
- - **快速上手**: [快速开始](docs/zh/getting-started/index.md) · [AI 编程助手快速参考](docs/zh/getting-started/ai-agents.md) · [运行时环境配置](docs/zh/getting-started/runtimes.md) · [跨源隔离指南 (Z3)](docs/zh/getting-started/cross-origin.md)
413
- - **建模指南**: [决策变量 (`Var`, `BoolVar`)](docs/zh/guide/variables.md) · [表达式与聚合 (`expr`, `sum`, `dot`)](docs/zh/guide/expressions.md) · [约束与逻辑条件 (`indicator`, SMT)](docs/zh/guide/constraints.md) · [目标函数与求解 (`solve`, `validateSolution`)](docs/zh/guide/objectives.md) · [可满足性与 Unsat Core (`check`)](docs/zh/guide/unsat-core.md)
414
- - **求解器**: [求解器概览](docs/zh/solvers/index.md) · [HiGHS](docs/zh/solvers/highs.md) · [Z3](docs/zh/solvers/z3.md) · [GLPK](docs/zh/solvers/glpk.md) · [自定义求解器](docs/zh/solvers/custom-solver.md)
191
+ 关于本地开发、运行测试与基准测试、重新构建 WebAssembly 资源以及发布流程,请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md)。
415
192
 
416
193
  ---
417
194
 
418
- ## 💻 示例与浏览器演示
419
-
420
- [示例目录](https://github.com/ohtaman/washi/blob/main/examples/README.md)与[在线案例与 API 索引](https://ohtaman.github.io/washi/zh/examples/)按问题类别和 WaSHI API 功能汇总了源码链接及本地运行方法。
195
+ ## 🙏 致谢
421
196
 
422
- | 类别 | 示例 | 展示内容 | 求解器 |
423
- | :--- | :--- | :--- | :--- |
424
- | **基础模型** | [生产计划](https://github.com/ohtaman/washi/blob/main/examples/node/01-production-planning.ts) | 整数变量、利润目标与资源约束 (`expr`) | HiGHS |
425
- | **基础模型** | [运输与分配网络](https://github.com/ohtaman/washi/blob/main/examples/node/09-transportation-problem.ts) | 二维矩阵变量 (`continuous(rows, cols)`) 与供需平衡 | HiGHS |
426
- | **基础模型** | [营养问题与灵敏度分析](https://github.com/ohtaman/washi/blob/main/examples/node/10-diet-sensitivity.ts) | 影子价格 (`constr.dual`)、松弛量 (`constr.slack`) 与缩减成本 (`var.reducedCost`) | HiGHS |
427
- | **实战应用** | [员工排班与冲突诊断](https://github.com/ohtaman/washi/blob/main/examples/node/11-shift-scheduling.ts) | 周排班优化与周六休假冲突诊断 | HiGHS + Z3 |
428
- | **实战应用** | [购物车优惠券最优组合](https://github.com/ohtaman/washi/blob/main/examples/node/12-ecommerce-coupon-optimizer.ts) | 满减门槛条件约束 (`indicator`)、互斥规则与凑单推荐 | HiGHS |
429
- | **实战应用** | [投资组合分配](https://github.com/ohtaman/washi/blob/main/examples/node/03-portfolio-qp.ts) | 凸二次目标函数 (`QuadExpr`) 与分配约束 | HiGHS |
430
- | **逻辑 / SMT** | [业务规则检查](https://github.com/ohtaman/washi/blob/main/examples/node/04-smt-security-rules.ts) | 算术与逻辑约束 (`bool`, `implies`) 的可满足性检查 | Z3 |
431
- | **逻辑 / SMT** | [冲突诊断](https://github.com/ohtaman/washi/blob/main/examples/node/05-unsat-core-diagnosis.ts) | 命名约束与 Unsat Core (`unsatCore`) | Z3 |
432
-
433
- 在[在线浏览器演示](https://ohtaman.github.io/washi/demo/)中可以编辑并运行模型。浏览器 ESM、script 标签和 CodePen 的用法请参阅[分发示例](https://github.com/ohtaman/washi/blob/main/examples/distribution/README.md)。Z3 需要跨源隔离;具体要求见上方浏览器说明。
434
-
435
- 本地运行前,请克隆仓库并安装开发依赖:
436
-
437
- ```bash
438
- git clone https://github.com/ohtaman/washi.git
439
- cd washi
440
- npm ci
441
- npx tsx examples/node/01-production-planning.ts
442
- npx tsx examples/node/09-transportation-problem.ts
443
- npx tsx examples/node/10-diet-sensitivity.ts
444
- npx tsx examples/node/11-shift-scheduling.ts
445
- npx tsx examples/node/12-ecommerce-coupon-optimizer.ts
446
-
447
- # 本地浏览器演示: http://localhost:5173/demo/
448
- npm run dev
449
- ```
450
-
451
- 示例导入仓库的 `src/` 模块。使用已发布软件包的应用应改为导入 `@ohtaman/washi` 及所需的求解器插件。
452
-
453
- ---
454
-
455
- ## 🧪 自动化测试与可复现基准测试
456
-
457
- 运行仓库的单元测试与集成测试,并构建分发版本:
458
-
459
- ```bash
460
- # 运行全部单元与集成测试
461
- npm test
462
-
463
- # 构建生产级依赖包与 TypeScript 类型定义
464
- npm run build
465
- ```
466
-
467
- ### 可选的可复现基准测试
468
-
469
- 在不将耗时阈值混入正确性测试断言的前提下,对确定性的 `small-lp`(24 个变量、18 条约束)和 `scheduling-mip`(315 个变量、231 条约束,含重建模型的 `initialValue` 热启动)进行分阶段性能测量:
470
-
471
- ```bash
472
- # 运行 Node.js 进程内 WASM 基准测试(HiGHS 与 GLPK,默认 5 次热重复)
473
- npm run bench
474
-
475
- # 自定义重复次数或输出 JSON
476
- node scripts/run-benchmarks.mjs --iterations=10 --json
477
-
478
- # 同时测量无头 Chromium Web Worker 执行(需要安装 Playwright Chromium)
479
- node scripts/run-benchmarks.mjs --browser
480
- ```
197
+ WaSHI 的诞生离不开数理优化与自动推理开源社区的卓越工作:
481
198
 
482
- - **明确的测量边界**:将冷启动初始化(`coldRuntimeInitMs`、`coldFirstSolveMs`)与复用运行时的重复测量(`modelBuildMs`、`validationMs`、LP 字符串适配器的 `lpSerializationMs` 或结构化适配器的 `symbolMapMs`、基于 `MockSolver` 的 `resultMappingMs`、`adapterSolveMs` 以及 `endToEndSolveMs`)分开直接测量,不通过相减来臆造“原生求解器时间”。
483
- - **区分不同执行环境**:Node.js 进程内 WASM(`usesBrowserWorker: false`)与浏览器 Web Worker 执行(`usesBrowserWorker: true`,包含 Worker 生命周期与 `postMessage` 结构化克隆传输)作为不同执行环境分别报告,不可直接互换作基线。
199
+ - **[HiGHS](https://highs.dev/)**([`ERGO-Code/HiGHS`](https://github.com/ERGO-Code/HiGHS))— 发源于爱丁堡大学、由 Julian Hall、Qi Huangfu、Ivet Galabova、Leona Gottwald、Michael Feldmeier 及社区贡献者共同打造的高性能开源 LP / MIP / 凸 QP 求解器,驱动了 WaSHI 的内置默认引擎(*Huangfu & Hall, Mathematical Programming Computation, 2018*, [DOI: 10.1007/s12532-017-0130-5](https://doi.org/10.1007/s12532-017-0130-5))。
200
+ - **[Z3 Theorem Prover](https://github.com/Z3Prover/z3)**([`z3-solver`](https://www.npmjs.com/package/z3-solver))— 由 Microsoft Research 研发,驱动了 WaSHI 可选的 SMT 可满足性判定与精确 `unsatCore` 冲突诊断插件。
201
+ - **[GLPK](https://www.gnu.org/software/glpk/) & [`glpk.js`](https://github.com/jvail/glpk.js)** — 驱动了 WaSHI 可选的 GPL-3.0 LP / MIP 求解器插件。
202
+ - **[PuLP](https://github.com/coin-or/pulp)** — 启发了 WaSHI 声明式代数建模 API 的设计。
484
203
 
485
204
  ---
486
205
 
487
206
  ## 📄 许可证
488
207
 
489
208
  - **`@ohtaman/washi`**: [MIT 许可证](./LICENSE)
490
- - **HiGHS**(默认求解器): MIT 许可证
491
- - **Z3**(可选插件): MIT 许可证
492
- - **GLPK**(可选插件): GPL-3.0 许可证
209
+ - **内置与可选引擎**: HiGHS (MIT)、Z3 (MIT)、GLPK (GPL-3.0)。完整许可说明请参阅 [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md)。
@@ -8,15 +8,16 @@ Depending on the solver you choose at runtime, WaSHI interacts with the followin
8
8
 
9
9
  ## 1. HiGHS (Default Solver)
10
10
 
11
- - **Role**: Default Linear & Mixed-Integer Programming Engine (WebAssembly)
12
- - **Package**: [`highs`](https://www.npmjs.com/package/highs)
11
+ - **Role**: Default Linear, Mixed-Integer & Convex Quadratic Programming Engine (WebAssembly)
13
12
  - **License**: **MIT License**
14
- - **Upstream Repository**: [ERGO-Code/HiGHS](https://github.com/ERGO-Code/HiGHS) / [lovasoa/highs-js](https://github.com/lovasoa/highs-js)
13
+ - **Official Website**: [https://highs.dev/](https://highs.dev/)
14
+ - **Upstream Repository**: [ERGO-Code/HiGHS](https://github.com/ERGO-Code/HiGHS)
15
+ - **Compilation**: Directly compiled from official upstream source (`v1.15.1`, pure MIT build without optional METIS/AMD/BLAS dependencies) to WebAssembly via native C API (`highs_c_api.h`)
16
+ - **Embedded Third-Party Notices**: Upstream HiGHS includes permissive header-only components (such as `filereaderlp` and `pdqsort`); see upstream [`THIRD_PARTY_NOTICES.md`](https://github.com/ERGO-Code/HiGHS/blob/v1.15.1/THIRD_PARTY_NOTICES.md) for full details.
15
17
 
16
18
  ```text
17
19
  MIT License
18
20
 
19
- Copyright (c) 2023 highs-js
20
21
  Copyright (c) 2026 HiGHS
21
22
 
22
23
  Permission is hereby granted, free of charge, to any person obtaining a copy
File without changes