@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
@@ -1,131 +1,364 @@
1
- import { type OptimizationSense, type SolverStatus, type SolveOptions, type SolverSolution } from "./types.js";
1
+ import { type OptimizationSense, type SolverStatus, type TerminationStatus, type SolveOptions, type SolverSolution, type CheckResult, type ColumnDefinition } from "./types.js";
2
+ import { type ObjectiveSpec } from "./objective.js";
2
3
  import { Var } from "./variable.js";
3
4
  import { LinExpr } from "./expression.js";
4
5
  import { QuadExpr } from "./quad-expr.js";
5
6
  import { Constraint } from "./constraint.js";
6
7
  import { BoolVar, BoolExpr } from "./boolean-expr.js";
8
+ import { type ParsedExpr } from "./parser.js";
7
9
  import { type Solver } from "./solvers/index.js";
10
+ import { type SolutionValidationOptions, type SolutionValidationReport } from "./solution-validator.js";
11
+ import { type BendersOptions, type BendersResult, type ColumnGenerationOptions, type ColumnGenerationResult } from "../decomposition/index.js";
12
+ /**
13
+ * Valid items that can be added as a constraint to a model.
14
+ * Raw mathematical expressions (LinExpr, QuadExpr) without comparisons are strictly prohibited.
15
+ */
16
+ export type ConstraintItem = Constraint | BoolExpr | string;
8
17
  export interface ModelOptions {
18
+ /** Optional model name (default: `"OptimizationModel"`). */
9
19
  name?: string;
20
+ /** Default optimization direction (`"Minimize"` or `"Maximize"`, default: `"Minimize"`). */
10
21
  sense?: OptimizationSense;
22
+ /** Solver instance or registered solver name (`"highs"`, `"glpk"`, `"z3"`, or custom solver). */
11
23
  solver?: Solver | string;
12
24
  }
13
25
  export interface BoundsOptions {
26
+ /** Explicit name for a scalar variable, or fallback prefix for indexed variable containers. */
14
27
  name?: string;
28
+ /** Name prefix when creating unnamed scalar variables or indexed variable containers (default: `"x"`). */
15
29
  prefix?: string;
30
+ /** Lower bound (default: `0`; `-Infinity` allowed for free lower bound; `+Infinity` and `NaN` rejected). */
16
31
  lb?: number;
32
+ /** Upper bound (default: `Infinity`; `-Infinity` and `NaN` rejected; must satisfy `lb <= ub`). */
17
33
  ub?: number;
34
+ /**
35
+ * Optional finite primal initial value (MIP start / warm-start hint), distinct from solved `Var.value`.
36
+ * Validated within tolerance `1e-6` against `[lb, ub]` and integer domain.
37
+ * Supported by HiGHS (`supportsWarmStart: true`); throws on solvers without warm-start support (GLPK, Z3).
38
+ */
39
+ initialValue?: number;
40
+ [Symbol.iterator]?: never;
18
41
  }
19
- export interface IndicatorOptions {
20
- activeValue?: 0 | 1;
21
- M?: number;
42
+ export interface BinaryOptions {
43
+ /** Explicit name for a scalar variable, or fallback prefix for indexed variable containers. */
22
44
  name?: string;
45
+ /** Name prefix when creating unnamed scalar variables or indexed variable containers (default: `"x"`). */
46
+ prefix?: string;
47
+ /**
48
+ * Optional finite primal initial value (`0` or `1` within tolerance `1e-6`), distinct from solved `Var.value`.
49
+ * Supported by HiGHS (`supportsWarmStart: true`); throws on solvers without warm-start support (GLPK, Z3).
50
+ */
51
+ initialValue?: number;
52
+ [Symbol.iterator]?: never;
23
53
  }
54
+ type VarNameOptions = {
55
+ prefix?: string;
56
+ name?: string;
57
+ [Symbol.iterator]?: never;
58
+ };
59
+ import { type IndicatorOptions } from "./big-m.js";
60
+ export type { IndicatorOptions };
24
61
  /**
25
- * High-level mathematical programming model for LP, MIP, QP, and SMT,
26
- * running directly with WebAssembly solvers (HiGHS, Z3, GLPK) in browser and Node.js.
62
+ * Named propositional or SMT constraint registered on a `Model`.
27
63
  */
28
64
  export interface LogicalConstraint {
29
65
  name: string;
30
66
  expr: BoolExpr | Constraint;
31
67
  }
68
+ declare const UniqueStringTag: unique symbol;
69
+ declare global {
70
+ interface String {
71
+ readonly [UniqueStringTag]: false;
72
+ }
73
+ }
74
+ export type KeyCollection<K extends string | number> = ReadonlyArray<K> | (Iterable<K> & object & {
75
+ readonly [UniqueStringTag]?: true;
76
+ });
77
+ /**
78
+ * High-level mathematical programming and SMT model for LP, MIP, convex QP, and SAT/SMT,
79
+ * running with WebAssembly solvers (HiGHS, GLPK, Z3) in Node.js and browsers.
80
+ *
81
+ * **Solver capability notes**:
82
+ * - `"highs"` (default, built-in): Supports LP, MIP, convex QP objectives (`supportsQP: true`),
83
+ * and `initialValue` warm starts (`supportsWarmStart: true`). Also supports `m.check()` on purely
84
+ * linear models via a zero-objective feasibility fallback. Throws if `BoolVar`/logical constraints are present.
85
+ * - `"glpk"` (`import "@ohtaman/washi/solvers/glpk"`): Supports LP and MIP, and `m.check()` on purely
86
+ * linear models via LP feasibility fallback. Throws on quadratic objectives, `BoolVar`/logical constraints,
87
+ * or `initialValue` warm starts.
88
+ * - `"z3"` (`import "@ohtaman/washi/solvers/z3"`): Supports SMT satisfiability (`m.check()`), optimization (`m.solve()`),
89
+ * `BoolVar`/logical constraints (`and`, `or`, `not`, `implies`, `xor`), and unsatisfiable-core extraction
90
+ * (`conflicts` / `unsatCore`). Throws on quadratic objectives or `initialValue` warm starts.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { Model, expr, sum, dot, maximize } from "@ohtaman/washi";
95
+ *
96
+ * const m = new Model("ProductionPlan");
97
+ * const qty = m.integer(["desk", "chair"] as const, { lb: 0, ub: 100 });
98
+ * const setup = m.binary("setup", { initialValue: 1 });
99
+ *
100
+ * m.objective(maximize(expr`${dot({ desk: 30, chair: 50 }, qty)} - 20 * ${setup}`));
101
+ * m.constraint(expr`${dot({ desk: 1, chair: 2 }, qty)} <= 40`, "wood_cap");
102
+ * m.constraint(expr`${sum(Object.values(qty))} <= 30 * ${setup}`, "require_setup");
103
+ *
104
+ * const status = await m.solve();
105
+ * const report = m.validateSolution();
106
+ * const solvedQty = m.getValues(qty);
107
+ * ```
108
+ */
32
109
  export declare class Model {
33
110
  name: string;
34
- sense: OptimizationSense;
35
- objective: LinExpr | QuadExpr;
111
+ private _sense;
112
+ private _objectiveExpr;
36
113
  variables: Var[];
37
114
  constraints: Constraint[];
38
115
  boolVars: BoolVar[];
39
116
  logicalConstraints: LogicalConstraint[];
40
- unsatCore: string[];
117
+ unsatCore: readonly string[] | undefined;
118
+ private _conflicts;
41
119
  values: Map<string, number | boolean>;
42
120
  private varMapByName;
121
+ private varPrefixCounters;
43
122
  private boolVarMapByName;
44
123
  private boolVarPrefixCounters;
45
124
  private constrMapByName;
125
+ private constrPrefixCounters;
126
+ private logicalConstrMapByName;
127
+ private logicalConstrCounter;
46
128
  status: SolverStatus;
129
+ terminationStatus: TerminationStatus;
130
+ hasPrimalSolution: boolean;
47
131
  objectiveValue: number | null;
132
+ objectiveBound: number | undefined;
133
+ relGap: number | undefined;
134
+ mipGap: number | undefined;
135
+ absGap: number | undefined;
48
136
  runTime: number;
49
137
  solution: SolverSolution | null;
50
138
  solver: Solver | null;
139
+ /**
140
+ * Latest decomposition result if solved using Benders decomposition or Column Generation.
141
+ */
142
+ decomposition?: BendersResult | ColumnGenerationResult;
143
+ private _linkingConstraints?;
144
+ private hasSolutionState;
145
+ private modelRevision;
146
+ private activeOperationId;
147
+ private batchDepth;
148
+ private batchMutated;
149
+ private batchUndoActions;
51
150
  constructor(nameOrOptions?: string | ModelOptions, maybeOptions?: ModelOptions);
52
- get vars(): Var[];
53
- get constrs(): Constraint[];
151
+ get sense(): OptimizationSense;
152
+ set sense(value: OptimizationSense);
153
+ get objectiveExpr(): LinExpr | QuadExpr;
154
+ set objectiveExpr(value: LinExpr | QuadExpr | Constraint);
155
+ /**
156
+ * User-friendly alias for unsatCore or MIP elastic conflict diagnosis.
157
+ * Returns a subset of conflicting constraint names causing infeasibility,
158
+ * or undefined if not diagnosed, not applicable, or satisfiable.
159
+ */
160
+ get conflicts(): readonly string[] | undefined;
161
+ set conflicts(value: readonly string[] | undefined);
54
162
  get numVars(): number;
55
163
  get numConstrs(): number;
164
+ /**
165
+ * Clears stored solution and diagnostic fields on the model and its entities.
166
+ */
167
+ private clearSolutionFields;
168
+ /**
169
+ * Records a model mutation, invalidating any stored solution state and in-flight solver executions.
170
+ */
171
+ private recordModelMutation;
172
+ /**
173
+ * @internal Notifies the model that an owned entity (e.g., Var bounds/type or Constraint RHS/sense) was mutated.
174
+ */
175
+ notifyMutation(): void;
176
+ /**
177
+ * Reset prior solution and diagnostic state to avoid stale data leakage across runs,
178
+ * and invalidate any in-flight solve() or check() execution.
179
+ */
180
+ resetSolutionState(): void;
181
+ /**
182
+ * Executes a multi-item model mutation atomically so failed batch edits neither leave
183
+ * partial entities registered nor invalidate prior solved / in-flight state.
184
+ */
185
+ private runTransactionalBatch;
56
186
  /**
57
187
  * Internal generator for domain-specific variables
58
188
  */
59
189
  private createDomainVars;
60
190
  /**
61
- * Create 0-1 binary variable(s) (x in {0, 1}).
62
- * Supports:
63
- * - m.binary("x") -> Var
64
- * - m.binary(5) -> Var[]
65
- * - m.binary(["a", "b"]) -> Record<string, Var>
66
- * - m.binary(rows, cols) -> Record<string, Record<string, Var>>
67
- */
68
- binary(name: string): Var;
69
- binary(count: number, options?: {
70
- prefix?: string;
71
- name?: string;
72
- }): Var[];
73
- binary<K extends string | number>(keys: K[] | Iterable<K>, options?: {
74
- prefix?: string;
75
- name?: string;
76
- }): Record<K, Var>;
77
- binary<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: {
78
- prefix?: string;
79
- name?: string;
80
- }): Record<R, Record<C, Var>>;
81
- /**
82
- * Create integer variable(s) (y in Z).
83
- * Supports:
84
- * - m.integer("y", { lb: 0, ub: 10 }) -> Var
85
- * - m.integer(5, { lb: 0 }) -> Var[]
86
- * - m.integer(["a", "b"], { lb: 0, ub: 5 }) -> Record<string, Var>
87
- * - m.integer(rows, cols, options) -> Record<string, Record<string, Var>>
88
- */
89
- integer(name: string, options?: BoundsOptions): Var;
191
+ * Create 0-1 binary decision variable(s) ($x \in \{0, 1\}$).
192
+ *
193
+ * **Scalar & indexed container overloads**:
194
+ * - `m.binary("x", options?)` or `m.binary(options?)` -> `Var`
195
+ * - `m.binary(count, options?)` -> `Var[]`
196
+ * - `m.binary(keys, options?)` -> `Record<K, Var>`
197
+ * - `m.binary(rowsCount, colsCount, options?)` -> `Var[][]`
198
+ * - `m.binary(rowsCount, colKeys, options?)` -> `Array<Record<C, Var>>`
199
+ * - `m.binary(rowKeys, colsCount, options?)` -> `Record<R, Var[]>`
200
+ * - `m.binary(rowKeys, colKeys, options?)` -> `Record<R, Record<C, Var>>`
201
+ *
202
+ * **Warm-start (`initialValue`) semantics**:
203
+ * - `options.initialValue` supplies an optional finite primal hint (`0` or `1` within tolerance `1e-6`),
204
+ * kept distinct from solved `v.value`.
205
+ * - Supported by HiGHS (`supportsWarmStart: true`); throws on GLPK and Z3.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const m = new Model("BinaryVars");
210
+ * const b = m.binary("b", { initialValue: 1 }); // Var
211
+ * const vec = m.binary(4, { prefix: "u" }); // Var[]
212
+ * const grid = m.binary(2, 3, { prefix: "g" }); // Var[][]
213
+ * const byShift = m.binary(["day", "night"] as const); // Record<"day" | "night", Var>
214
+ * ```
215
+ */
216
+ binary(name?: string, options?: BinaryOptions): Var;
217
+ binary(options: BinaryOptions): Var;
218
+ binary(count: number, options?: BinaryOptions): Var[];
219
+ binary<K extends string | number>(keys: KeyCollection<K>, options?: BinaryOptions): Record<K, Var>;
220
+ binary(rows: number, cols: number, options?: BinaryOptions): Var[][];
221
+ binary<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: BinaryOptions): Array<Record<C, Var>>;
222
+ binary<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: BinaryOptions): Record<R, Var[]>;
223
+ binary<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: BinaryOptions): Record<R, Record<C, Var>>;
224
+ /**
225
+ * Create integer decision variable(s) ($y \in \mathbb{Z}$, default bounds `lb: 0`, `ub: Infinity`).
226
+ *
227
+ * **Scalar & indexed container overloads**:
228
+ * - `m.integer("y", options?)` or `m.integer(options)` -> `Var`
229
+ * - `m.integer(count, options?)` -> `Var[]`
230
+ * - `m.integer(keys, options?)` -> `Record<K, Var>`
231
+ * - `m.integer(rowsCount, colsCount, options?)` -> `Var[][]`
232
+ * - `m.integer(rowsCount, colKeys, options?)` -> `Array<Record<C, Var>>`
233
+ * - `m.integer(rowKeys, colsCount, options?)` -> `Record<R, Var[]>`
234
+ * - `m.integer(rowKeys, colKeys, options?)` -> `Record<R, Record<C, Var>>`
235
+ *
236
+ * **Warm-start (`initialValue`) semantics**:
237
+ * - `options.initialValue` supplies an optional finite integer primal hint within tolerance `1e-6` of `[lb, ub]`,
238
+ * kept distinct from solved `v.value`.
239
+ * - Supported by HiGHS (`supportsWarmStart: true`); throws on GLPK and Z3.
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * const m = new Model("IntegerVars");
244
+ * const n = m.integer("n", { lb: 0, ub: 10, initialValue: 4 }); // Var
245
+ * const counts = m.integer(3, { lb: 0, prefix: "k" }); // Var[]
246
+ * const matrix = m.integer(2, 2, { lb: 0, prefix: "m" }); // Var[][]
247
+ * const byItem = m.integer(["desk", "chair"] as const, { lb: 0, ub: 50 }); // Record<"desk" | "chair", Var>
248
+ * ```
249
+ */
250
+ integer(name?: string, options?: BoundsOptions): Var;
251
+ integer(options: BoundsOptions): Var;
90
252
  integer(count: number, options?: BoundsOptions): Var[];
91
- integer<K extends string | number>(keys: K[] | Iterable<K>, options?: BoundsOptions): Record<K, Var>;
92
- integer<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: BoundsOptions): Record<R, Record<C, Var>>;
253
+ integer<K extends string | number>(keys: KeyCollection<K>, options?: BoundsOptions): Record<K, Var>;
254
+ integer(rows: number, cols: number, options?: BoundsOptions): Var[][];
255
+ integer<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: BoundsOptions): Array<Record<C, Var>>;
256
+ integer<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: BoundsOptions): Record<R, Var[]>;
257
+ integer<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: BoundsOptions): Record<R, Record<C, Var>>;
93
258
  /**
94
- * Create continuous (real) variable(s) (z in R).
95
- * Supports:
96
- * - m.continuous("z", { lb: 0 }) -> Var
97
- * - m.continuous(5, { lb: 0 }) -> Var[]
98
- * - m.continuous(["a", "b"], { lb: 0 }) -> Record<string, Var>
99
- * - m.continuous(rows, cols, options) -> Record<string, Record<string, Var>>
259
+ * Create continuous (real) decision variable(s) ($z \in \mathbb{R}$, default bounds `lb: 0`, `ub: Infinity`).
260
+ *
261
+ * **Scalar & indexed container overloads**:
262
+ * - `m.continuous("z", options?)` or `m.continuous(options)` -> `Var`
263
+ * - `m.continuous(count, options?)` -> `Var[]`
264
+ * - `m.continuous(keys, options?)` -> `Record<K, Var>`
265
+ * - `m.continuous(rowsCount, colsCount, options?)` -> `Var[][]`
266
+ * - `m.continuous(rowsCount, colKeys, options?)` -> `Array<Record<C, Var>>`
267
+ * - `m.continuous(rowKeys, colsCount, options?)` -> `Record<R, Var[]>`
268
+ * - `m.continuous(rowKeys, colKeys, options?)` -> `Record<R, Record<C, Var>>`
269
+ *
270
+ * **Warm-start (`initialValue`) semantics**:
271
+ * - `options.initialValue` supplies an optional finite primal hint within tolerance `1e-6` of `[lb, ub]`,
272
+ * kept distinct from solved `v.value`.
273
+ * - Supported by HiGHS (`supportsWarmStart: true`); throws on GLPK and Z3.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * const m = new Model("ContinuousVars");
278
+ * const x = m.continuous("x", { lb: 0, ub: 100, initialValue: 25 }); // Var
279
+ * const vec = m.continuous(4, { lb: 0, prefix: "v" }); // Var[]
280
+ * const flow = m.continuous(2, 3, { lb: 0, prefix: "f" }); // Var[][]
281
+ * const alloc = m.continuous(["A", "B"] as const, { lb: 0 }); // Record<"A" | "B", Var>
282
+ * ```
100
283
  */
101
- continuous(name: string, options?: BoundsOptions): Var;
284
+ continuous(name?: string, options?: BoundsOptions): Var;
285
+ continuous(options: BoundsOptions): Var;
102
286
  continuous(count: number, options?: BoundsOptions): Var[];
103
- continuous<K extends string | number>(keys: K[] | Iterable<K>, options?: BoundsOptions): Record<K, Var>;
104
- continuous<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: BoundsOptions): Record<R, Record<C, Var>>;
105
- /**
106
- * Create Boolean (propositional logic) variable(s) for SMT / SAT modeling.
107
- * Supports:
108
- * - m.bool("isNight") -> BoolVar
109
- * - m.bool() -> BoolVar
110
- * - m.bool(5) -> BoolVar[]
111
- * - m.bool(["flagA", "flagB"]) -> Record<string, BoolVar>
112
- * - m.bool(rows, cols) -> Record<string, Record<string, BoolVar>>
113
- */
114
- bool(name?: string): BoolVar;
115
- bool(count: number, options?: {
116
- prefix?: string;
117
- name?: string;
118
- }): BoolVar[];
119
- bool<K extends string | number>(keys: K[] | Iterable<K>, options?: {
120
- prefix?: string;
121
- name?: string;
122
- }): Record<K, BoolVar>;
123
- bool<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: {
124
- prefix?: string;
125
- name?: string;
126
- }): Record<R, Record<C, BoolVar>>;
287
+ continuous<K extends string | number>(keys: KeyCollection<K>, options?: BoundsOptions): Record<K, Var>;
288
+ continuous(rows: number, cols: number, options?: BoundsOptions): Var[][];
289
+ continuous<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: BoundsOptions): Array<Record<C, Var>>;
290
+ continuous<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: BoundsOptions): Record<R, Var[]>;
291
+ continuous<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: BoundsOptions): Record<R, Record<C, Var>>;
292
+ /**
293
+ * Dynamically add a column (decision variable + constraint coefficients + objective cost) to the model.
294
+ * This is the core primitive for Column Generation and Dantzig-Wolfe decomposition.
295
+ *
296
+ * @param options Column definition including variable type/bounds, objective cost, and constraint coefficients
297
+ * @returns The newly created decision variable `Var`
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * const m = new Model("ColumnExample");
302
+ * m.constraint(expr`0 <= 10`, "demand_itemA");
303
+ * m.constraint(expr`0 <= 10`, "demand_itemB");
304
+ * const colVar = m.addColumn({
305
+ * name: "pattern_1",
306
+ * type: "Continuous",
307
+ * lb: 0,
308
+ * cost: 1,
309
+ * coefficients: {
310
+ * demand_itemA: 2,
311
+ * demand_itemB: 1,
312
+ * },
313
+ * });
314
+ * ```
315
+ */
316
+ addColumn(options: ColumnDefinition): Var;
317
+ /**
318
+ * Create propositional Boolean variable(s) (`BoolVar`: `true` / `false`) for SMT / SAT logical modeling.
319
+ *
320
+ * **Scalar & indexed container overloads**:
321
+ * - `m.bool("flag", options?)` or `m.bool(options?)` -> `BoolVar`
322
+ * - `m.bool(count, options?)` -> `BoolVar[]`
323
+ * - `m.bool(keys, options?)` -> `Record<K, BoolVar>`
324
+ * - `m.bool(rowsCount, colsCount, options?)` -> `BoolVar[][]`
325
+ * - `m.bool(rowsCount, colKeys, options?)` -> `Array<Record<C, BoolVar>>`
326
+ * - `m.bool(rowKeys, colsCount, options?)` -> `Record<R, BoolVar[]>`
327
+ * - `m.bool(rowKeys, colKeys, options?)` -> `Record<R, Record<C, BoolVar>>`
328
+ *
329
+ * **Solver capability note**:
330
+ * - Logical constraints over `BoolVar` (`and`, `or`, `not`, `implies`, `xor`) require an SMT-capable solver
331
+ * such as Z3 (`new Model("...", { solver: "z3" })` after `import "@ohtaman/washi/solvers/z3"`).
332
+ * - LP/MIP solvers (HiGHS, GLPK) throw if logical constraints are present. For 0-1 arithmetic variables
333
+ * in LP/MIP (or Big-M `m.indicator()`), use `m.binary()` instead.
334
+ *
335
+ * @example
336
+ * ```ts
337
+ * import "@ohtaman/washi/solvers/z3";
338
+ *
339
+ * const m = new Model("BoolVars", { solver: "z3" });
340
+ * const vip = m.bool("vip"); // BoolVar
341
+ * const flags = m.bool(3, { prefix: "f" }); // BoolVar[]
342
+ * const grid = m.bool(2, 2, { prefix: "g" }); // BoolVar[][]
343
+ * const byRole = m.bool(["admin", "guest"] as const); // Record<"admin" | "guest", BoolVar>
344
+ * m.constraint(implies(vip, flags[0]), "vip_flag");
345
+ * ```
346
+ */
347
+ bool(name?: string, options?: VarNameOptions): BoolVar;
348
+ bool(options: VarNameOptions): BoolVar;
349
+ bool(count: number, options?: VarNameOptions): BoolVar[];
350
+ bool<K extends string | number>(keys: KeyCollection<K>, options?: VarNameOptions): Record<K, BoolVar>;
351
+ bool(rows: number, cols: number, options?: VarNameOptions): BoolVar[][];
352
+ bool<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: VarNameOptions): Array<Record<C, BoolVar>>;
353
+ bool<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: VarNameOptions): Record<R, BoolVar[]>;
354
+ bool<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: VarNameOptions): Record<R, Record<C, BoolVar>>;
127
355
  private hasVarName;
356
+ private getNextVarName;
128
357
  private getNextBoolVarName;
358
+ private hasConstraintName;
359
+ private hasBoolVarName;
360
+ private getNextConstrName;
361
+ private getNextLogicalConstrName;
129
362
  private createBoolVar;
130
363
  /**
131
364
  * Internal variable creator
@@ -144,39 +377,86 @@ export declare class Model {
144
377
  */
145
378
  getConstraintByName(name: string): Constraint | undefined;
146
379
  /**
147
- * Set the objective function
380
+ * Find a logical constraint by its name
148
381
  */
149
- setObjective(expr: LinExpr | QuadExpr | Var | number | string | Constraint, sense?: OptimizationSense): void;
382
+ getLogicalConstraintByName(name: string): LogicalConstraint | undefined;
150
383
  /**
151
- * Set objective to maximize
384
+ * Get the current objective expression, or set the objective function and direction (`maximize(...)` / `minimize(...)`).
385
+ * Calling `objective(spec)` replaces any previously defined objective and returns `this` for chaining.
386
+ *
387
+ * **Formulation & solver capability notes**:
388
+ * - Supports linear (`LinExpr`, `Var`, `number`, formula string) and quadratic (`QuadExpr`: `^2`, `${x} * ${y}`) expressions.
389
+ * - Quadratic terms are supported in **objectives only** (requires a QP-capable solver such as HiGHS; throws on GLPK and Z3).
390
+ * - Passing a comparison `Constraint` (`<=`, `>=`, `==`) throws an Error.
391
+ *
392
+ * @example
393
+ * ```ts
394
+ * const m = new Model("ObjectiveExample");
395
+ * const chairs = m.integer("chairs", { lb: 0 });
396
+ * const tables = m.integer("tables", { lb: 0 });
397
+ *
398
+ * m.objective(maximize(expr`30 * ${chairs} + 50 * ${tables}`));
399
+ * const currentExpr = m.objective();
400
+ * ```
152
401
  */
153
- maximize(expr: LinExpr | QuadExpr | Var | number | string | Constraint): void;
402
+ objective(): LinExpr | QuadExpr;
403
+ objective(spec: ObjectiveSpec): this;
154
404
  /**
155
- * Set objective to minimize
405
+ * Internal objective setter.
156
406
  */
157
- minimize(expr: LinExpr | QuadExpr | Var | number | string | Constraint): void;
407
+ private applyObjective;
158
408
  /**
159
- * Constrain the model with linear, boolean, or string formula constraints (primary default API).
160
- * Supports single constraint, array of constraints, and fluent chaining:
161
- * - m.constrain(expr`${desks} + 2 * ${chairs} <= 40`, "wood")
162
- * - m.constrain(items.map(i => expr`${x[i]} <= 10`))
163
- * - m.constrain(c1).constrain(c2)
409
+ * Register linear comparison constraint(s) (`<=`, `>=`, `==`), SMT logical constraint(s) (`BoolVar`, `BoolExpr`),
410
+ * or constraint formula string(s) on the model. Chainable: returns `this`.
411
+ *
412
+ * **Formulation & solver capability notes**:
413
+ * - Rejects raw arithmetic expressions (`LinExpr`, `QuadExpr`) without a comparison operator and quadratic constraints.
414
+ * - Logical constraints (`BoolVar`, `and`, `or`, `not`, `implies`, `xor`) require an SMT-capable solver (`{ solver: "z3" }`);
415
+ * LP/MIP solvers (HiGHS, GLPK) throw if logical constraints are present.
416
+ *
417
+ * @example
418
+ * ```ts
419
+ * const m = new Model("ConstraintExample");
420
+ * const desks = m.integer("desks", { lb: 0 });
421
+ * const chairs = m.integer("chairs", { lb: 0 });
422
+ * const x = m.continuous(3, { lb: 0, prefix: "x" });
423
+ *
424
+ * m.constraint(expr`${desks} + 2 * ${chairs} <= 40`, "wood");
425
+ * m.constraint(x.map((v) => expr`${v} <= 10`));
426
+ * m.constraint(desks.ge(2)).constraint(chairs.ge(4));
427
+ * ```
164
428
  */
165
- constrain(constraint: Constraint | LinExpr | QuadExpr | BoolExpr | string, name?: string): this;
166
- constrain(constraints: (Constraint | LinExpr | QuadExpr | BoolExpr | string)[]): this;
429
+ constraint(constraint: ConstraintItem | LinExpr | QuadExpr, name?: string): this;
430
+ constraint(constraints: ReadonlyArray<ConstraintItem | LinExpr | QuadExpr>): this;
167
431
  /**
168
- * Extract primal solution values directly into plain primitives:
169
- * - m.getValues(x) -> number | null
170
- * - m.getValues(p) -> boolean | null
171
- * - m.getValues([x, y]) -> (number | null)[]
172
- * - m.getValues([p, q]) -> (boolean | null)[]
173
- * - m.getValues(record) -> Record<string, number | boolean | null>
174
- * - m.getValues(matrix) -> Record<string, Record<string, number | boolean | null>>
432
+ * Extract primal solution values directly into plain primitives, preserving container shapes:
433
+ * - `m.getValues(x)` -> `number | null`
434
+ * - `m.getValues(p)` -> `boolean | null`
435
+ * - `m.getValues([x, y])` -> `(number | null)[]`
436
+ * - `m.getValues([[x11, x12], [x21, x22]])` -> `(number | null)[][]`
437
+ * - `m.getValues(record)` -> `Record<K, number | boolean | null>`
438
+ * - `m.getValues(matrix)` -> `Record<R, Record<C, number | boolean | null>>`
439
+ *
440
+ * @example
441
+ * ```ts
442
+ * const m = new Model("GetValuesExample");
443
+ * const qty = m.integer(["desk", "chair"] as const, { lb: 0, ub: 10 });
444
+ * m.objective(maximize(expr`30 * ${qty.desk} + 50 * ${qty.chair}`));
445
+ * m.constraint(expr`${qty.desk} + ${qty.chair} <= 10`);
446
+ * await m.solve();
447
+ * const values = m.getValues(qty); // Record<"desk" | "chair", number | null>
448
+ * ```
175
449
  */
176
450
  getValues(item: Var): number | null;
177
451
  getValues(item: BoolVar): boolean | null;
178
- getValues(items: Var[]): (number | null)[];
179
- getValues(items: BoolVar[]): (boolean | null)[];
452
+ getValues(items: ReadonlyArray<Var>): (number | null)[];
453
+ getValues(items: ReadonlyArray<BoolVar>): (boolean | null)[];
454
+ getValues(items: ReadonlyArray<ReadonlyArray<Var>>): (number | null)[][];
455
+ getValues(items: ReadonlyArray<ReadonlyArray<BoolVar>>): (boolean | null)[][];
456
+ getValues<C extends string | number>(items: ReadonlyArray<Record<C, Var>>): Array<Record<C, number | null>>;
457
+ getValues<C extends string | number>(items: ReadonlyArray<Record<C, BoolVar>>): Array<Record<C, boolean | null>>;
458
+ getValues<R extends string | number>(items: Record<R, ReadonlyArray<Var>>): Record<R, (number | null)[]>;
459
+ getValues<R extends string | number>(items: Record<R, ReadonlyArray<BoolVar>>): Record<R, (boolean | null)[]>;
180
460
  getValues<K extends string | number>(items: Record<K, Var>): Record<K, number | null>;
181
461
  getValues<K extends string | number>(items: Record<K, BoolVar>): Record<K, boolean | null>;
182
462
  getValues<R extends string | number, C extends string | number>(items: Record<R, Record<C, Var>>): Record<R, Record<C, number | null>>;
@@ -186,54 +466,266 @@ export declare class Model {
186
466
  */
187
467
  private addConstraint;
188
468
  /**
189
- * Universal classical fluent adder for constraints, variables, or batches.
190
- * Enables:
191
- * - m.add(x.mul(2).add(y).le(10))
192
- * - m.add(c`${x} + ${y} <= 10`)
193
- * - m.add("x + y <= 10")
194
- * - m.add([c1, c2, c3])
195
- * - m.add(new Var(...))
196
- * - m.add(c1).add(c2) (fluent chaining)
197
- */
198
- add(constraint: Constraint, name?: string): this;
199
- add(expr: BoolExpr, name?: string): this;
200
- add(strConstraint: string, name?: string): this;
201
- add(variable: Var): this;
202
- add(items: (Constraint | BoolExpr | string | Var)[]): this;
203
- /**
204
- * Add an indicator constraint via Big-M auto-linearization:
205
- * "If binVar == activeValue, then constraint holds"
469
+ * Add an indicator constraint via floating-point Big-M auto-linearization:
470
+ * `"If binVar === activeValue, then constraint holds"` (`activeValue` defaults to `1`).
471
+ *
472
+ * **Formulation & solver capability notes**:
473
+ * - Automatically linearizes `<=`, `>=`, and `==` constraints for LP/MIP solvers (HiGHS, GLPK)
474
+ * under standard floating-point feasibility tolerances (`==` generates `_le` and `_ge` rows).
475
+ * - Variables appearing in `constraint` must have finite bounds (`lb`/`ub`) to derive a safe tight Big-M
476
+ * automatically, unless an explicit `{ M }` override is supplied in `options`.
477
+ * - Because Big-M rows are floating-point linear relaxations, they do not guarantee exact rational
478
+ * equivalence in SMT solvers (Z3); use `implies(...)` with `m.bool(...)` when exact SMT diagnosis is required.
206
479
  *
207
- * Automatically handles <=, >=, and == constraints.
208
- * Enables solving conditional logic in MIP solvers (HiGHS, GLPK) without requiring SMT.
480
+ * @example
481
+ * ```ts
482
+ * const m = new Model("IndicatorExample");
483
+ * const b = m.binary("b");
484
+ * const x = m.continuous("x", { lb: 0, ub: 100 });
485
+ * // Enforces x <= 50 when b === 1 (relaxed to x <= 100 when b === 0)
486
+ * m.indicator(b, expr`${x} <= 50`, { activeValue: 1, name: "cap_when_b" });
487
+ * ```
209
488
  */
210
489
  indicator(binVar: Var, constraint: Constraint | LinExpr | QuadExpr, options?: IndicatorOptions): Constraint[];
211
490
  /**
212
- * Export model to CPLEX LP format string
491
+ * Validate model integrity (`validateIntegrity()`) and serialize the model's linear/quadratic objective,
492
+ * linear constraints, and numeric variable bounds/domains (`Continuous`, `Integer`, `Binary`) to a
493
+ * standard CPLEX LP format string with collision-free sanitized identifiers.
494
+ *
495
+ * **Note**: Propositional `BoolVar`s and SMT `logicalConstraints` are not part of the CPLEX LP format.
496
+ *
497
+ * @example
498
+ * ```ts
499
+ * const m = new Model("LpExportExample");
500
+ * const x = m.integer("x", { lb: 0, ub: 10 });
501
+ * m.objective(maximize(expr`5 * ${x}`));
502
+ * m.constraint(expr`${x} <= 8`, "cap");
503
+ * const lpText = m.toLpString();
504
+ * ```
213
505
  */
214
506
  toLpString(): string;
215
507
  /**
216
- * Solve the optimization model using the configured solver (defaults to HiGHS WASM)
217
- * or a solver passed dynamically (e.g., await model.optimize(new HiGHS()))
508
+ * Validates model integrity: variable bounds, ownership, numeric sanity, and unique constraint names.
509
+ * Throws an Error with actionable explanation if any validation rule is violated.
510
+ */
511
+ validateIntegrity(): void;
512
+ private ensureExecutionValid;
513
+ private wrapSolveOptions;
514
+ private hasPrimalFromResult;
515
+ /**
516
+ * Resolves the target solver and execution options without mutating caller-owned objects,
517
+ * stripping the routing `solver` field from the returned `SolverOptions`.
518
+ */
519
+ private resolveSolverAndOptions;
520
+ /**
521
+ * Solve the continuous (LP) relaxation of the model.
522
+ * Treats all integer and binary decision variables as continuous variables within their bounds.
523
+ * This computes theoretical lower/upper dual bounds and extracts constraint shadow prices (duals),
524
+ * which is the foundational building block for Column Generation, Dantzig-Wolfe, and Benders decomposition.
525
+ *
526
+ * @param solverOrOptions Optional solver instance, name, or solve options
527
+ * @param options Solve options
528
+ * @returns Solver status ("Optimal", "Infeasible", etc.)
529
+ *
530
+ * @example
531
+ * ```ts
532
+ * const m = new Model("RelaxExample");
533
+ * const x = m.integer("x", { lb: 0, ub: 10 });
534
+ * m.objective(maximize(x));
535
+ * m.constraint(expr`${x} <= 5.5`, "cap");
536
+ * const status = await m.relax();
537
+ * if (status === "Optimal") {
538
+ * const bound = m.objectiveValue;
539
+ * const shadow = m.solution?.duals?.cap;
540
+ * }
541
+ * ```
218
542
  */
219
- optimize(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<SolverStatus>;
543
+ relax(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<SolverStatus>;
220
544
  /**
221
- * Convenience alias for optimize():
222
- * await model.solve()
223
- * await model.solve(new HiGHS())
224
- * await model.solve(new GLPK())
225
- * await model.solve(new RemoteSolver({ url: "..." }))
545
+ * Solve the optimization model using the configured solver (defaults to HiGHS WASM)
546
+ * or a solver passed dynamically (e.g., `await m.solve("glpk")` or `await m.solve({ timeLimit: 10 })`).
547
+ *
548
+ * **Return status (`SolverStatus`)**:
549
+ * - Built-in optimization solvers return `"Optimal"`, `"Feasible"`, `"Infeasible"`, `"Unbounded"`,
550
+ * `"TimeLimit"`, `"IterationLimit"`, `"Error"`, `"Unknown"`, or `"NotSolved"` (`"Sat"` / `"Unsat"`
551
+ * are included in `SolverStatus` for custom/satisfiability adapters).
552
+ * - When a time-, iteration-, or solution-limited run (or an interrupted solve) has found a valid
553
+ * primal incumbent, `solve()` returns `"Feasible"` (`m.hasPrimalSolution === true`, with the exact
554
+ * stopping reason in `m.terminationStatus`).
555
+ * - After solving, read primal/dual values via `x.value`, `m.getValues(...)`, `m.objectiveValue`,
556
+ * `constr.dual`, `constr.slack`, and `x.reducedCost`, and verify primal feasibility with `m.validateSolution()`.
557
+ *
558
+ * **Solver capability preconditions**:
559
+ * - Quadratic objectives (`QuadExpr`) require `supportsQP` (supported by HiGHS; throws on GLPK and Z3).
560
+ * - Logical constraints (`BoolVar`, `and`, `or`, `not`, `implies`, `xor`) require `supportsSMT`
561
+ * (supported by Z3; throws on HiGHS and GLPK).
562
+ * - Primal `initialValue` warm starts require `supportsWarmStart` (supported by HiGHS; throws on GLPK and Z3).
563
+ * - Mutating the model or starting a newer `solve()` / `check()` while in flight discards the stale
564
+ * result and rejects the pending promise.
565
+ *
566
+ * @example
567
+ * ```ts
568
+ * const m = new Model("SolveExample");
569
+ * const x = m.integer("x", { lb: 0, ub: 50, initialValue: 10 });
570
+ * const y = m.integer("y", { lb: 0, ub: 50 });
571
+ * m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
572
+ * m.constraint(expr`${x} + 2 * ${y} <= 40`, "resource_limit");
573
+ *
574
+ * const status = await m.solve();
575
+ * const report = m.validateSolution();
576
+ * const obj = m.objectiveValue;
577
+ * ```
226
578
  */
227
579
  solve(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<SolverStatus>;
228
580
  /**
229
- * Check satisfiability of the model without requiring an objective function.
230
- * Can be used to verify feasibility or diagnose infeasibilities / unsat core.
581
+ * Check satisfiability / feasibility of the model without requiring an objective function.
582
+ *
583
+ * Returns `CheckResult` (`{ status: "Sat" | "Unsat" | "Unknown", values, conflicts?, unsatCore?, runTime? }`).
584
+ *
585
+ * **LP feasibility fallback vs. Z3 requirement**:
586
+ * - **Linear models (`m.check()` fallback)**: For models containing only linear arithmetic constraints,
587
+ * `m.check()` works with any solver (including the default HiGHS and GLPK) by falling back to a
588
+ * zero-objective feasibility `solve()` without mutating `m.objectiveExpr` or `m.sense`.
589
+ * - **Logical constraints & `unsatCore` (requires Z3)**: Models containing `BoolVar` or logical
590
+ * constraints (`and`, `or`, `not`, `implies`, `xor`) require an SMT-capable solver (`{ solver: "z3" }`
591
+ * after `import "@ohtaman/washi/solvers/z3"`). Unsatisfiable-core extraction (`result.conflicts` /
592
+ * `result.unsatCore` and `m.conflicts` / `m.unsatCore`) is populated only when an unsat-core-capable
593
+ * solver such as Z3 returns `"Unsat"`, reporting a subset of registered constraint names that conflicts
594
+ * together with the model's fixed variable bounds/domains.
595
+ * - Mutating the model or starting a newer `solve()` / `check()` while in flight discards the stale
596
+ * result and rejects the pending promise.
597
+ *
598
+ * @example
599
+ * ```ts
600
+ * import "@ohtaman/washi/solvers/z3";
601
+ *
602
+ * // 1. Linear feasibility check (works with default HiGHS via LP fallback)
603
+ * const mLin = new Model("LinearCheck");
604
+ * const x = mLin.continuous("x", { lb: 0, ub: 10 });
605
+ * mLin.constraint(expr`${x} >= 4`, "min_x");
606
+ * const linRes = await mLin.check(); // { status: "Sat", values: { x: ... } }
607
+ *
608
+ * // 2. SMT satisfiability & unsatCore diagnosis (requires Z3)
609
+ * const mSmt = new Model("PolicyCheck", { solver: "z3" });
610
+ * const vip = mSmt.bool("vip");
611
+ * const discount = mSmt.integer("discount", { lb: 0, ub: 50 });
612
+ * mSmt.constraint(vip, "is_vip");
613
+ * mSmt.constraint(implies(vip, expr`${discount} >= 20`), "vip_min");
614
+ * mSmt.constraint(expr`${discount} <= 10`, "cap");
615
+ * const smtRes = await mSmt.check(); // { status: "Unsat", conflicts: [...], unsatCore: [...] }
616
+ * ```
617
+ */
618
+ check(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<CheckResult>;
619
+ /**
620
+ * Independently evaluate and verify variable finiteness, variable bounds (`lb`/`ub`),
621
+ * integer/binary/boolean domains, linear constraint residuals, and logical (SMT/propositional)
622
+ * constraints against the model's current solved values (or an explicit `options.values` map).
623
+ *
624
+ * Returns a `SolutionValidationReport` (`{ valid, complete, maxViolation, violations, violationsByName, uncheckedConstraints, missingVariables, summary }`).
625
+ *
626
+ * **Verification notes**:
627
+ * - Runs purely in JavaScript/TypeScript without requiring any WASM solver runtime.
628
+ * - Verifies primal feasibility of variables and constraints; does not recompute or compare `m.objectiveValue`.
629
+ *
630
+ * @example
631
+ * ```ts
632
+ * const m = new Model("ValidateExample");
633
+ * const x = m.integer("x", { lb: 0, ub: 10 });
634
+ * m.objective(maximize(expr`3 * ${x}`));
635
+ * m.constraint(expr`${x} <= 7`, "cap");
636
+ *
637
+ * await m.solve();
638
+ * const report = m.validateSolution();
639
+ * const isFeasible = report.valid && report.maxViolation <= 1e-6;
640
+ * ```
641
+ */
642
+ validateSolution(options?: SolutionValidationOptions): SolutionValidationReport;
643
+ /**
644
+ * Register a linking constraint (row) that connects this subproblem model's variables
645
+ * with decision variables in the master problem for Benders decomposition.
646
+ *
647
+ * @param constraint The linear constraint connecting subproblem variables with master variables
648
+ * @returns The registered linking constraint
649
+ *
650
+ * @example
651
+ * ```ts
652
+ * const master = new Model("Master");
653
+ * const y = master.binary("y");
654
+ *
655
+ * const sub = new Model("Subproblem");
656
+ * const x = sub.continuous("x", { lb: 0 });
657
+ * sub.link(expr`${x} <= 50 * ${y}`);
658
+ * ```
659
+ */
660
+ link(constraint: Constraint | ParsedExpr): Constraint;
661
+ /**
662
+ * Linking constraints registered on this model for Benders decomposition.
663
+ */
664
+ get linkingConstraints(): ReadonlyArray<Constraint>;
665
+ /**
666
+ * Solve this model as a Master Problem using Benders Decomposition.
667
+ *
668
+ * Partitions the problem into a master problem (this model) and a subproblem.
669
+ * On each iteration, the subproblem is evaluated: either automatically (generating Benders cuts
670
+ * from a returned subproblem model and its linking constraints) or manually via a cut callback.
671
+ *
672
+ * @param options Benders options including subproblem callback
673
+ * @returns BendersResult containing status, objective, bounds, gap, iterations, cuts, and decision values
674
+ *
675
+ * @example
676
+ * ```ts
677
+ * const master = new Model("FacilityMaster");
678
+ * const y = master.binary("open");
679
+ * master.constraint(expr`${y} >= 1`);
680
+ * master.objective(minimize(expr`100 * ${y}`));
681
+ *
682
+ * const result = await master.solveBenders({
683
+ * subproblem: () => {
684
+ * const sub = new Model("Transport");
685
+ * const x = sub.continuous("x", { lb: 0 });
686
+ * sub.objective(minimize(expr`2 * ${x}`));
687
+ * sub.constraint(expr`${x} >= 30`, "demand");
688
+ * sub.link(expr`${x} <= 50 * ${y}`);
689
+ * return sub;
690
+ * },
691
+ * });
692
+ * ```
693
+ */
694
+ solveBenders(options: Omit<BendersOptions, "master">): Promise<BendersResult>;
695
+ /**
696
+ * Solve this model as a Restricted Master Problem using Column Generation (Dantzig-Wolfe decomposition).
697
+ *
698
+ * Solves the continuous LP relaxation to obtain dual shadow prices, evaluates the pricing subproblem
699
+ * to dynamically generate columns with negative reduced costs, and optionally finalizes as a MIP.
700
+ *
701
+ * @param options Column generation options including custom pricing callback
702
+ * @returns ColumnGenerationResult containing status, objective, iterations, columns, and values
703
+ *
704
+ * @example
705
+ * ```ts
706
+ * const master = new Model("CuttingStock");
707
+ * master.objective(minimize(0));
708
+ * master.constraint(expr`0 >= 10`, "demand");
709
+ * const [demand] = master.constraints;
710
+ * master.addColumn({ cost: 1, type: "Integer", coefficients: { demand: 2 } });
711
+ *
712
+ * const result = await master.solveColumnGeneration({
713
+ * finalizeAsMIP: true,
714
+ * pricing: async () => {
715
+ * const sub = new Model("Pricing");
716
+ * const a = sub.integer("a", { lb: 0, ub: 5 });
717
+ * sub.objective(maximize(expr`${(demand.dual ?? 0)} * ${a}`));
718
+ * sub.constraint(expr`${a} <= 5`);
719
+ * await sub.solve();
720
+ * return {
721
+ * cost: 1,
722
+ * type: "Integer",
723
+ * coefficients: { demand: a.value ?? 0 },
724
+ * };
725
+ * },
726
+ * });
727
+ * ```
231
728
  */
232
- check(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<{
233
- status: "Sat" | "Unsat" | "Unknown";
234
- values: Record<string, number | boolean>;
235
- unsatCore?: string[];
236
- runTime?: number;
237
- }>;
729
+ solveColumnGeneration<TColumnMeta = unknown>(options: Omit<ColumnGenerationOptions<TColumnMeta>, "master">): Promise<ColumnGenerationResult<TColumnMeta>>;
238
730
  }
239
731
  //# sourceMappingURL=model.d.ts.map