@ohtaman/washi 0.3.0 → 0.5.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 (97) hide show
  1. package/README.ja.md +111 -118
  2. package/README.md +109 -116
  3. package/README.zh-CN.md +112 -119
  4. package/dist/browser/highs-worker.js +21 -3
  5. package/dist/browser/loader-CtaDHef_.js +273 -0
  6. package/dist/browser/solvers/glpk.js +433 -286
  7. package/dist/browser/solvers/glpk.min.js +1 -1
  8. package/dist/browser/solvers/z3.js +1002 -981
  9. package/dist/browser/solvers/z3.min.js +2 -2
  10. package/dist/browser/washi.js +2178 -622
  11. package/dist/browser/washi.min.js +4 -4
  12. package/dist/lib/highs-worker.js +21 -3
  13. package/dist/lib/loader-DmNC_UNk.js +279 -0
  14. package/dist/lib/registry-BEfvpZW3.js +951 -0
  15. package/dist/lib/solvers/glpk.js +201 -90
  16. package/dist/lib/solvers/z3.js +399 -259
  17. package/dist/lib/washi.js +1674 -932
  18. package/dist/types/index.d.ts +4 -4
  19. package/dist/types/index.d.ts.map +1 -1
  20. package/dist/types/modeler/big-m.d.ts +75 -0
  21. package/dist/types/modeler/big-m.d.ts.map +1 -0
  22. package/dist/types/modeler/boolean-expr.d.ts +103 -14
  23. package/dist/types/modeler/boolean-expr.d.ts.map +1 -1
  24. package/dist/types/modeler/constraint.d.ts +36 -9
  25. package/dist/types/modeler/constraint.d.ts.map +1 -1
  26. package/dist/types/modeler/expression.d.ts +117 -27
  27. package/dist/types/modeler/expression.d.ts.map +1 -1
  28. package/dist/types/modeler/index.d.ts +10 -6
  29. package/dist/types/modeler/index.d.ts.map +1 -1
  30. package/dist/types/modeler/lp-writer.d.ts +27 -2
  31. package/dist/types/modeler/lp-writer.d.ts.map +1 -1
  32. package/dist/types/modeler/model.d.ts +491 -140
  33. package/dist/types/modeler/model.d.ts.map +1 -1
  34. package/dist/types/modeler/objective.d.ts +59 -0
  35. package/dist/types/modeler/objective.d.ts.map +1 -0
  36. package/dist/types/modeler/parser.d.ts +39 -14
  37. package/dist/types/modeler/parser.d.ts.map +1 -1
  38. package/dist/types/modeler/quad-expr.d.ts +28 -7
  39. package/dist/types/modeler/quad-expr.d.ts.map +1 -1
  40. package/dist/types/modeler/solution-validator.d.ts +93 -0
  41. package/dist/types/modeler/solution-validator.d.ts.map +1 -0
  42. package/dist/types/modeler/solvers/glpk/solver.d.ts +26 -2
  43. package/dist/types/modeler/solvers/glpk/solver.d.ts.map +1 -1
  44. package/dist/types/modeler/solvers/highs/loader.d.ts +36 -11
  45. package/dist/types/modeler/solvers/highs/loader.d.ts.map +1 -1
  46. package/dist/types/modeler/solvers/highs/solver.d.ts +11 -4
  47. package/dist/types/modeler/solvers/highs/solver.d.ts.map +1 -1
  48. package/dist/types/modeler/solvers/highs/worker-client.d.ts.map +1 -1
  49. package/dist/types/modeler/solvers/mock.d.ts +14 -5
  50. package/dist/types/modeler/solvers/mock.d.ts.map +1 -1
  51. package/dist/types/modeler/solvers/types.d.ts +71 -11
  52. package/dist/types/modeler/solvers/types.d.ts.map +1 -1
  53. package/dist/types/modeler/solvers/z3/loader.d.ts +2 -3
  54. package/dist/types/modeler/solvers/z3/loader.d.ts.map +1 -1
  55. package/dist/types/modeler/solvers/z3/solver.d.ts +15 -5
  56. package/dist/types/modeler/solvers/z3/solver.d.ts.map +1 -1
  57. package/dist/types/modeler/types.d.ts +60 -2
  58. package/dist/types/modeler/types.d.ts.map +1 -1
  59. package/dist/types/modeler/validation.d.ts +56 -0
  60. package/dist/types/modeler/validation.d.ts.map +1 -0
  61. package/dist/types/modeler/variable.d.ts +53 -13
  62. package/dist/types/modeler/variable.d.ts.map +1 -1
  63. package/docs/examples/diet-sensitivity.md +90 -0
  64. package/docs/examples/ecommerce-coupons.md +106 -0
  65. package/docs/examples/index.md +53 -0
  66. package/docs/examples/knapsack.md +60 -0
  67. package/docs/examples/portfolio-qp.md +83 -0
  68. package/docs/examples/production.md +51 -0
  69. package/docs/examples/shift-scheduling.md +175 -0
  70. package/docs/examples/smt-security.md +74 -0
  71. package/docs/examples/transportation.md +90 -0
  72. package/docs/examples/unsat-core.md +64 -0
  73. package/docs/getting-started/ai-agents.md +267 -0
  74. package/docs/getting-started/cross-origin.md +87 -0
  75. package/docs/getting-started/index.md +116 -0
  76. package/docs/getting-started/runtimes.md +227 -0
  77. package/docs/guide/constraints.md +177 -0
  78. package/docs/guide/expressions.md +204 -0
  79. package/docs/guide/objectives.md +170 -0
  80. package/docs/guide/unsat-core.md +147 -0
  81. package/docs/guide/variables.md +188 -0
  82. package/docs/ja/getting-started/ai-agents.md +266 -0
  83. package/docs/solvers/custom-solver.md +525 -0
  84. package/docs/solvers/glpk.md +66 -0
  85. package/docs/solvers/highs.md +93 -0
  86. package/docs/solvers/index.md +42 -0
  87. package/docs/solvers/z3.md +78 -0
  88. package/docs/zh/getting-started/ai-agents.md +266 -0
  89. package/llms-full.txt +3249 -0
  90. package/llms.txt +197 -0
  91. package/package.json +19 -6
  92. package/skills/washi/SKILL.md +197 -0
  93. package/dist/browser/loader-CVt4CloI.js +0 -58
  94. package/dist/lib/async-mutex-Duo4jwzn.js +0 -195
  95. package/dist/lib/constraint-BRuB3ooC.js +0 -90
  96. package/dist/lib/loader-Dti0v9yC.js +0 -50
  97. package/dist/lib/registry-DksqCSjI.js +0 -32
@@ -1,235 +1,586 @@
1
- import { type VarType, type OptimizationSense, type SolverStatus, type SolveOptions, type SolverSolution } from "./types.js";
2
- import { Var, type VarOptions } from "./variable.js";
1
+ import { type OptimizationSense, type SolverStatus, type TerminationStatus, type SolveOptions, type SolverSolution, type CheckResult } from "./types.js";
2
+ import { type ObjectiveSpec } from "./objective.js";
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";
7
8
  import { type Solver } from "./solvers/index.js";
9
+ import { type SolutionValidationOptions, type SolutionValidationReport } from "./solution-validator.js";
10
+ /**
11
+ * Valid items that can be added as a constraint to a model.
12
+ * Raw mathematical expressions (LinExpr, QuadExpr) without comparisons are strictly prohibited.
13
+ */
14
+ export type ConstraintItem = Constraint | BoolExpr | string;
8
15
  export interface ModelOptions {
16
+ /** Optional model name (default: `"OptimizationModel"`). */
9
17
  name?: string;
18
+ /** Default optimization direction (`"Minimize"` or `"Maximize"`, default: `"Minimize"`). */
10
19
  sense?: OptimizationSense;
20
+ /** Solver instance or registered solver name (`"highs"`, `"glpk"`, `"z3"`, or custom solver). */
11
21
  solver?: Solver | string;
12
22
  }
13
23
  export interface BoundsOptions {
24
+ /** Explicit name for a scalar variable, or fallback prefix for indexed variable containers. */
14
25
  name?: string;
26
+ /** Name prefix when creating unnamed scalar variables or indexed variable containers (default: `"x"`). */
15
27
  prefix?: string;
28
+ /** Lower bound (default: `0`; `-Infinity` allowed for free lower bound; `+Infinity` and `NaN` rejected). */
16
29
  lb?: number;
30
+ /** Upper bound (default: `Infinity`; `-Infinity` and `NaN` rejected; must satisfy `lb <= ub`). */
17
31
  ub?: number;
32
+ /**
33
+ * Optional finite primal initial value (MIP start / warm-start hint), distinct from solved `Var.value`.
34
+ * Validated within tolerance `1e-6` against `[lb, ub]` and integer domain.
35
+ * Supported by HiGHS (`supportsWarmStart: true`); throws on solvers without warm-start support (GLPK, Z3).
36
+ */
37
+ initialValue?: number;
38
+ [Symbol.iterator]?: never;
18
39
  }
19
- export interface IndicatorOptions {
20
- activeValue?: 0 | 1;
21
- M?: number;
40
+ export interface BinaryOptions {
41
+ /** Explicit name for a scalar variable, or fallback prefix for indexed variable containers. */
22
42
  name?: string;
43
+ /** Name prefix when creating unnamed scalar variables or indexed variable containers (default: `"x"`). */
44
+ prefix?: string;
45
+ /**
46
+ * Optional finite primal initial value (`0` or `1` within tolerance `1e-6`), distinct from solved `Var.value`.
47
+ * Supported by HiGHS (`supportsWarmStart: true`); throws on solvers without warm-start support (GLPK, Z3).
48
+ */
49
+ initialValue?: number;
50
+ [Symbol.iterator]?: never;
23
51
  }
52
+ type VarNameOptions = {
53
+ prefix?: string;
54
+ name?: string;
55
+ [Symbol.iterator]?: never;
56
+ };
57
+ import { type IndicatorOptions } from "./big-m.js";
58
+ export type { IndicatorOptions };
24
59
  /**
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.
60
+ * Named propositional or SMT constraint registered on a `Model`.
27
61
  */
28
62
  export interface LogicalConstraint {
29
63
  name: string;
30
64
  expr: BoolExpr | Constraint;
31
65
  }
66
+ declare const UniqueStringTag: unique symbol;
67
+ declare global {
68
+ interface String {
69
+ readonly [UniqueStringTag]: false;
70
+ }
71
+ }
72
+ export type KeyCollection<K extends string | number> = ReadonlyArray<K> | (Iterable<K> & object & {
73
+ readonly [UniqueStringTag]?: true;
74
+ });
75
+ /**
76
+ * High-level mathematical programming and SMT model for LP, MIP, convex QP, and SAT/SMT,
77
+ * running with WebAssembly solvers (HiGHS, GLPK, Z3) in Node.js and browsers.
78
+ *
79
+ * **Solver capability notes**:
80
+ * - `"highs"` (default, built-in): Supports LP, MIP, convex QP objectives (`supportsQP: true`),
81
+ * and `initialValue` warm starts (`supportsWarmStart: true`). Also supports `m.check()` on purely
82
+ * linear models via a zero-objective feasibility fallback. Throws if `BoolVar`/logical constraints are present.
83
+ * - `"glpk"` (`import "@ohtaman/washi/solvers/glpk"`): Supports LP and MIP, and `m.check()` on purely
84
+ * linear models via LP feasibility fallback. Throws on quadratic objectives, `BoolVar`/logical constraints,
85
+ * or `initialValue` warm starts.
86
+ * - `"z3"` (`import "@ohtaman/washi/solvers/z3"`): Supports SMT satisfiability (`m.check()`), optimization (`m.solve()`),
87
+ * `BoolVar`/logical constraints (`and`, `or`, `not`, `implies`, `xor`), and unsatisfiable-core extraction
88
+ * (`conflicts` / `unsatCore`). Throws on quadratic objectives or `initialValue` warm starts.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * import { Model, expr, sum, dot, maximize } from "@ohtaman/washi";
93
+ *
94
+ * const m = new Model("ProductionPlan");
95
+ * const qty = m.integer(["desk", "chair"] as const, { lb: 0, ub: 100 });
96
+ * const setup = m.binary("setup", { initialValue: 1 });
97
+ *
98
+ * m.objective(maximize(expr`${dot({ desk: 30, chair: 50 }, qty)} - 20 * ${setup}`));
99
+ * m.constraint(expr`${dot({ desk: 1, chair: 2 }, qty)} <= 40`, "wood_cap");
100
+ * m.constraint(expr`${sum(Object.values(qty))} <= 30 * ${setup}`, "require_setup");
101
+ *
102
+ * const status = await m.solve();
103
+ * const report = m.validateSolution();
104
+ * const solvedQty = m.getValues(qty);
105
+ * ```
106
+ */
32
107
  export declare class Model {
33
108
  name: string;
34
- sense: OptimizationSense;
35
- objective: LinExpr | QuadExpr;
109
+ private _sense;
110
+ private _objectiveExpr;
36
111
  variables: Var[];
37
112
  constraints: Constraint[];
38
113
  boolVars: BoolVar[];
39
114
  logicalConstraints: LogicalConstraint[];
40
- unsatCore: string[];
115
+ unsatCore: readonly string[] | undefined;
41
116
  values: Map<string, number | boolean>;
42
117
  private varMapByName;
118
+ private varPrefixCounters;
119
+ private boolVarMapByName;
120
+ private boolVarPrefixCounters;
43
121
  private constrMapByName;
122
+ private constrPrefixCounters;
123
+ private logicalConstrMapByName;
124
+ private logicalConstrCounter;
44
125
  status: SolverStatus;
126
+ terminationStatus: TerminationStatus;
127
+ hasPrimalSolution: boolean;
45
128
  objectiveValue: number | null;
129
+ objectiveBound: number | undefined;
130
+ relGap: number | undefined;
131
+ mipGap: number | undefined;
132
+ absGap: number | undefined;
46
133
  runTime: number;
47
134
  solution: SolverSolution | null;
48
135
  solver: Solver | null;
136
+ private hasSolutionState;
137
+ private modelRevision;
138
+ private activeOperationId;
139
+ private batchDepth;
140
+ private batchMutated;
141
+ private batchUndoActions;
49
142
  constructor(nameOrOptions?: string | ModelOptions, maybeOptions?: ModelOptions);
50
- get vars(): Var[];
51
- get constrs(): Constraint[];
143
+ get sense(): OptimizationSense;
144
+ set sense(value: OptimizationSense);
145
+ get objectiveExpr(): LinExpr | QuadExpr;
146
+ set objectiveExpr(value: LinExpr | QuadExpr | Constraint);
147
+ /**
148
+ * User-friendly alias for unsatCore.
149
+ * Returns a subset of conflicting constraint names causing infeasibility,
150
+ * or undefined if not diagnosed, not applicable, or satisfiable.
151
+ */
152
+ get conflicts(): readonly string[] | undefined;
52
153
  get numVars(): number;
53
154
  get numConstrs(): number;
155
+ /**
156
+ * Clears stored solution and diagnostic fields on the model and its entities.
157
+ */
158
+ private clearSolutionFields;
159
+ /**
160
+ * Records a model mutation, invalidating any stored solution state and in-flight solver executions.
161
+ */
162
+ private recordModelMutation;
163
+ /**
164
+ * @internal Notifies the model that an owned entity (e.g., Var bounds/type or Constraint RHS/sense) was mutated.
165
+ */
166
+ notifyMutation(): void;
167
+ /**
168
+ * Reset prior solution and diagnostic state to avoid stale data leakage across runs,
169
+ * and invalidate any in-flight solve() or check() execution.
170
+ */
171
+ resetSolutionState(): void;
172
+ /**
173
+ * Executes a multi-item model mutation atomically so failed batch edits neither leave
174
+ * partial entities registered nor invalidate prior solved / in-flight state.
175
+ */
176
+ private runTransactionalBatch;
54
177
  /**
55
178
  * Internal generator for domain-specific variables
56
179
  */
57
180
  private createDomainVars;
58
181
  /**
59
- * Create 0-1 binary variable(s) (x in {0, 1}).
60
- * Supports:
61
- * - m.binary("x") -> Var
62
- * - m.binary(5) -> Var[]
63
- * - m.binary(["a", "b"]) -> Record<string, Var>
64
- * - m.binary(rows, cols) -> Record<string, Record<string, Var>>
65
- */
66
- binary(name: string): Var;
67
- binary(count: number, options?: {
68
- prefix?: string;
69
- name?: string;
70
- }): Var[];
71
- binary<K extends string | number>(keys: K[] | Iterable<K>, options?: {
72
- prefix?: string;
73
- name?: string;
74
- }): Record<K, Var>;
75
- binary<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: {
76
- prefix?: string;
77
- name?: string;
78
- }): Record<R, Record<C, Var>>;
79
- /**
80
- * Create integer variable(s) (y in Z).
81
- * Supports:
82
- * - m.integer("y", { lb: 0, ub: 10 }) -> Var
83
- * - m.integer(5, { lb: 0 }) -> Var[]
84
- * - m.integer(["a", "b"], { lb: 0, ub: 5 }) -> Record<string, Var>
85
- * - m.integer(rows, cols, options) -> Record<string, Record<string, Var>>
86
- */
87
- integer(name: string, options?: BoundsOptions): Var;
88
- integer(count: number, options?: BoundsOptions): Var[];
89
- integer<K extends string | number>(keys: K[] | Iterable<K>, options?: BoundsOptions): Record<K, Var>;
90
- integer<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: BoundsOptions): Record<R, Record<C, Var>>;
182
+ * Create 0-1 binary decision variable(s) ($x \in \{0, 1\}$).
183
+ *
184
+ * **Scalar & indexed container overloads**:
185
+ * - `m.binary("x", options?)` or `m.binary(options?)` -> `Var`
186
+ * - `m.binary(count, options?)` -> `Var[]`
187
+ * - `m.binary(keys, options?)` -> `Record<K, Var>`
188
+ * - `m.binary(rowsCount, colsCount, options?)` -> `Var[][]`
189
+ * - `m.binary(rowsCount, colKeys, options?)` -> `Array<Record<C, Var>>`
190
+ * - `m.binary(rowKeys, colsCount, options?)` -> `Record<R, Var[]>`
191
+ * - `m.binary(rowKeys, colKeys, options?)` -> `Record<R, Record<C, Var>>`
192
+ *
193
+ * **Warm-start (`initialValue`) semantics**:
194
+ * - `options.initialValue` supplies an optional finite primal hint (`0` or `1` within tolerance `1e-6`),
195
+ * kept distinct from solved `v.value`.
196
+ * - Supported by HiGHS (`supportsWarmStart: true`); throws on GLPK and Z3.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const m = new Model("BinaryVars");
201
+ * const b = m.binary("b", { initialValue: 1 }); // Var
202
+ * const vec = m.binary(4, { prefix: "u" }); // Var[]
203
+ * const grid = m.binary(2, 3, { prefix: "g" }); // Var[][]
204
+ * const byShift = m.binary(["day", "night"] as const); // Record<"day" | "night", Var>
205
+ * ```
206
+ */
207
+ binary(name?: string, options?: BinaryOptions): Var;
208
+ binary(options: BinaryOptions): Var;
209
+ binary(count: number, options?: BinaryOptions): Var[];
210
+ binary<K extends string | number>(keys: KeyCollection<K>, options?: BinaryOptions): Record<K, Var>;
211
+ binary(rows: number, cols: number, options?: BinaryOptions): Var[][];
212
+ binary<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: BinaryOptions): Array<Record<C, Var>>;
213
+ binary<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: BinaryOptions): Record<R, Var[]>;
214
+ binary<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: BinaryOptions): Record<R, Record<C, Var>>;
91
215
  /**
92
- * Create continuous (real) variable(s) (z in R).
93
- * Supports:
94
- * - m.continuous("z", { lb: 0 }) -> Var
95
- * - m.continuous(5, { lb: 0 }) -> Var[]
96
- * - m.continuous(["a", "b"], { lb: 0 }) -> Record<string, Var>
97
- * - m.continuous(rows, cols, options) -> Record<string, Record<string, Var>>
216
+ * Create integer decision variable(s) ($y \in \mathbb{Z}$, default bounds `lb: 0`, `ub: Infinity`).
217
+ *
218
+ * **Scalar & indexed container overloads**:
219
+ * - `m.integer("y", options?)` or `m.integer(options)` -> `Var`
220
+ * - `m.integer(count, options?)` -> `Var[]`
221
+ * - `m.integer(keys, options?)` -> `Record<K, Var>`
222
+ * - `m.integer(rowsCount, colsCount, options?)` -> `Var[][]`
223
+ * - `m.integer(rowsCount, colKeys, options?)` -> `Array<Record<C, Var>>`
224
+ * - `m.integer(rowKeys, colsCount, options?)` -> `Record<R, Var[]>`
225
+ * - `m.integer(rowKeys, colKeys, options?)` -> `Record<R, Record<C, Var>>`
226
+ *
227
+ * **Warm-start (`initialValue`) semantics**:
228
+ * - `options.initialValue` supplies an optional finite integer primal hint within tolerance `1e-6` of `[lb, ub]`,
229
+ * kept distinct from solved `v.value`.
230
+ * - Supported by HiGHS (`supportsWarmStart: true`); throws on GLPK and Z3.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * const m = new Model("IntegerVars");
235
+ * const n = m.integer("n", { lb: 0, ub: 10, initialValue: 4 }); // Var
236
+ * const counts = m.integer(3, { lb: 0, prefix: "k" }); // Var[]
237
+ * const matrix = m.integer(2, 2, { lb: 0, prefix: "m" }); // Var[][]
238
+ * const byItem = m.integer(["desk", "chair"] as const, { lb: 0, ub: 50 }); // Record<"desk" | "chair", Var>
239
+ * ```
98
240
  */
99
- continuous(name: string, options?: BoundsOptions): Var;
100
- continuous(count: number, options?: BoundsOptions): Var[];
101
- continuous<K extends string | number>(keys: K[] | Iterable<K>, options?: BoundsOptions): Record<K, Var>;
102
- continuous<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: BoundsOptions): Record<R, Record<C, Var>>;
241
+ integer(name?: string, options?: BoundsOptions): Var;
242
+ integer(options: BoundsOptions): Var;
243
+ integer(count: number, options?: BoundsOptions): Var[];
244
+ integer<K extends string | number>(keys: KeyCollection<K>, options?: BoundsOptions): Record<K, Var>;
245
+ integer(rows: number, cols: number, options?: BoundsOptions): Var[][];
246
+ integer<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: BoundsOptions): Array<Record<C, Var>>;
247
+ integer<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: BoundsOptions): Record<R, Var[]>;
248
+ integer<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: BoundsOptions): Record<R, Record<C, Var>>;
103
249
  /**
104
- * Add a decision variable to the model
250
+ * Create continuous (real) decision variable(s) ($z \in \mathbb{R}$, default bounds `lb: 0`, `ub: Infinity`).
251
+ *
252
+ * **Scalar & indexed container overloads**:
253
+ * - `m.continuous("z", options?)` or `m.continuous(options)` -> `Var`
254
+ * - `m.continuous(count, options?)` -> `Var[]`
255
+ * - `m.continuous(keys, options?)` -> `Record<K, Var>`
256
+ * - `m.continuous(rowsCount, colsCount, options?)` -> `Var[][]`
257
+ * - `m.continuous(rowsCount, colKeys, options?)` -> `Array<Record<C, Var>>`
258
+ * - `m.continuous(rowKeys, colsCount, options?)` -> `Record<R, Var[]>`
259
+ * - `m.continuous(rowKeys, colKeys, options?)` -> `Record<R, Record<C, Var>>`
260
+ *
261
+ * **Warm-start (`initialValue`) semantics**:
262
+ * - `options.initialValue` supplies an optional finite primal hint within tolerance `1e-6` of `[lb, ub]`,
263
+ * kept distinct from solved `v.value`.
264
+ * - Supported by HiGHS (`supportsWarmStart: true`); throws on GLPK and Z3.
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * const m = new Model("ContinuousVars");
269
+ * const x = m.continuous("x", { lb: 0, ub: 100, initialValue: 25 }); // Var
270
+ * const vec = m.continuous(4, { lb: 0, prefix: "v" }); // Var[]
271
+ * const flow = m.continuous(2, 3, { lb: 0, prefix: "f" }); // Var[][]
272
+ * const alloc = m.continuous(["A", "B"] as const, { lb: 0 }); // Record<"A" | "B", Var>
273
+ * ```
105
274
  */
106
- addVar(options?: VarOptions): Var;
107
- addVar(name: string, type?: VarType, lb?: number, ub?: number): Var;
275
+ continuous(name?: string, options?: BoundsOptions): Var;
276
+ continuous(options: BoundsOptions): Var;
277
+ continuous(count: number, options?: BoundsOptions): Var[];
278
+ continuous<K extends string | number>(keys: KeyCollection<K>, options?: BoundsOptions): Record<K, Var>;
279
+ continuous(rows: number, cols: number, options?: BoundsOptions): Var[][];
280
+ continuous<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: BoundsOptions): Array<Record<C, Var>>;
281
+ continuous<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: BoundsOptions): Record<R, Var[]>;
282
+ continuous<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: BoundsOptions): Record<R, Record<C, Var>>;
108
283
  /**
109
- * Add multiple variables at once:
110
- * - by count: addVars(5) -> Var[]
111
- * - by key array: addVars(["apples", "oranges"]) -> Record<string, Var>
112
- * - by 2D dimensions: addVars(factories, stores) -> Record<string, Record<string, Var>>
113
- * - by 2D numeric grid: addVars(8, 8) -> Record<number, Record<number, Var>>
284
+ * Create propositional Boolean variable(s) (`BoolVar`: `true` / `false`) for SMT / SAT logical modeling.
285
+ *
286
+ * **Scalar & indexed container overloads**:
287
+ * - `m.bool("flag", options?)` or `m.bool(options?)` -> `BoolVar`
288
+ * - `m.bool(count, options?)` -> `BoolVar[]`
289
+ * - `m.bool(keys, options?)` -> `Record<K, BoolVar>`
290
+ * - `m.bool(rowsCount, colsCount, options?)` -> `BoolVar[][]`
291
+ * - `m.bool(rowsCount, colKeys, options?)` -> `Array<Record<C, BoolVar>>`
292
+ * - `m.bool(rowKeys, colsCount, options?)` -> `Record<R, BoolVar[]>`
293
+ * - `m.bool(rowKeys, colKeys, options?)` -> `Record<R, Record<C, BoolVar>>`
294
+ *
295
+ * **Solver capability note**:
296
+ * - Logical constraints over `BoolVar` (`and`, `or`, `not`, `implies`, `xor`) require an SMT-capable solver
297
+ * such as Z3 (`new Model("...", { solver: "z3" })` after `import "@ohtaman/washi/solvers/z3"`).
298
+ * - LP/MIP solvers (HiGHS, GLPK) throw if logical constraints are present. For 0-1 arithmetic variables
299
+ * in LP/MIP (or Big-M `m.indicator()`), use `m.binary()` instead.
300
+ *
301
+ * @example
302
+ * ```ts
303
+ * import "@ohtaman/washi/solvers/z3";
304
+ *
305
+ * const m = new Model("BoolVars", { solver: "z3" });
306
+ * const vip = m.bool("vip"); // BoolVar
307
+ * const flags = m.bool(3, { prefix: "f" }); // BoolVar[]
308
+ * const grid = m.bool(2, 2, { prefix: "g" }); // BoolVar[][]
309
+ * const byRole = m.bool(["admin", "guest"] as const); // Record<"admin" | "guest", BoolVar>
310
+ * m.constraint(implies(vip, flags[0]), "vip_flag");
311
+ * ```
114
312
  */
115
- addVars(count: number, options?: VarOptions): Var[];
116
- addVars<R extends string | number, C extends string | number>(dim1: R[] | number, dim2: C[] | number, options?: VarOptions): Record<R, Record<C, Var>>;
117
- addVars<K extends string | number>(keys: K[], options?: VarOptions): Record<K, Var>;
313
+ bool(name?: string, options?: VarNameOptions): BoolVar;
314
+ bool(options: VarNameOptions): BoolVar;
315
+ bool(count: number, options?: VarNameOptions): BoolVar[];
316
+ bool<K extends string | number>(keys: KeyCollection<K>, options?: VarNameOptions): Record<K, BoolVar>;
317
+ bool(rows: number, cols: number, options?: VarNameOptions): BoolVar[][];
318
+ bool<C extends string | number>(rows: number, cols: KeyCollection<C>, options?: VarNameOptions): Array<Record<C, BoolVar>>;
319
+ bool<R extends string | number>(rows: KeyCollection<R>, cols: number, options?: VarNameOptions): Record<R, BoolVar[]>;
320
+ bool<R extends string | number, C extends string | number>(rows: KeyCollection<R>, cols: KeyCollection<C>, options?: VarNameOptions): Record<R, Record<C, BoolVar>>;
321
+ private hasVarName;
322
+ private getNextVarName;
323
+ private getNextBoolVarName;
324
+ private hasConstraintName;
325
+ private hasBoolVarName;
326
+ private getNextConstrName;
327
+ private getNextLogicalConstrName;
328
+ private createBoolVar;
118
329
  /**
119
- * Add a 2D matrix of decision variables: grid[row][col]
330
+ * Internal variable creator
120
331
  */
121
- addVars2D<R extends string | number, C extends string | number>(rows: R[], cols: C[], options?: VarOptions): Record<R, Record<C, Var>>;
332
+ private addVar;
122
333
  /**
123
334
  * Find variable by name
124
335
  */
125
336
  getVarByName(name: string): Var | undefined;
337
+ /**
338
+ * Find boolean variable by name
339
+ */
340
+ getBoolVarByName(name: string): BoolVar | undefined;
126
341
  /**
127
342
  * Find a constraint by its name
128
343
  */
129
344
  getConstraintByName(name: string): Constraint | undefined;
130
345
  /**
131
- * Set the objective function
346
+ * Find a logical constraint by its name
132
347
  */
133
- setObjective(expr: LinExpr | QuadExpr | Var | number | string | Constraint, sense?: OptimizationSense): void;
348
+ getLogicalConstraintByName(name: string): LogicalConstraint | undefined;
134
349
  /**
135
- * Set objective to maximize
350
+ * Get the current objective expression, or set the objective function and direction (`maximize(...)` / `minimize(...)`).
351
+ * Calling `objective(spec)` replaces any previously defined objective and returns `this` for chaining.
352
+ *
353
+ * **Formulation & solver capability notes**:
354
+ * - Supports linear (`LinExpr`, `Var`, `number`, formula string) and quadratic (`QuadExpr`: `^2`, `${x} * ${y}`) expressions.
355
+ * - Quadratic terms are supported in **objectives only** (requires a QP-capable solver such as HiGHS; throws on GLPK and Z3).
356
+ * - Passing a comparison `Constraint` (`<=`, `>=`, `==`) throws an Error.
357
+ *
358
+ * @example
359
+ * ```ts
360
+ * const m = new Model("ObjectiveExample");
361
+ * const chairs = m.integer("chairs", { lb: 0 });
362
+ * const tables = m.integer("tables", { lb: 0 });
363
+ *
364
+ * m.objective(maximize(expr`30 * ${chairs} + 50 * ${tables}`));
365
+ * const currentExpr = m.objective();
366
+ * ```
136
367
  */
137
- maximize(expr: LinExpr | QuadExpr | Var | number | string | Constraint): void;
368
+ objective(): LinExpr | QuadExpr;
369
+ objective(spec: ObjectiveSpec): this;
138
370
  /**
139
- * Set objective to minimize
371
+ * Internal objective setter.
140
372
  */
141
- minimize(expr: LinExpr | QuadExpr | Var | number | string | Constraint): void;
373
+ private applyObjective;
142
374
  /**
143
- * Constrain the model with linear, boolean, or string formula constraints (primary default API).
144
- * Supports single constraint, array of constraints, and fluent chaining:
145
- * - m.constrain(expr`${desks} + 2 * ${chairs} <= 40`, "wood")
146
- * - m.constrain(items.map(i => expr`${x[i]} <= 10`))
147
- * - m.constrain(c1).constrain(c2)
375
+ * Register linear comparison constraint(s) (`<=`, `>=`, `==`), SMT logical constraint(s) (`BoolVar`, `BoolExpr`),
376
+ * or constraint formula string(s) on the model. Chainable: returns `this`.
377
+ *
378
+ * **Formulation & solver capability notes**:
379
+ * - Rejects raw arithmetic expressions (`LinExpr`, `QuadExpr`) without a comparison operator and quadratic constraints.
380
+ * - Logical constraints (`BoolVar`, `and`, `or`, `not`, `implies`, `xor`) require an SMT-capable solver (`{ solver: "z3" }`);
381
+ * LP/MIP solvers (HiGHS, GLPK) throw if logical constraints are present.
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * const m = new Model("ConstraintExample");
386
+ * const desks = m.integer("desks", { lb: 0 });
387
+ * const chairs = m.integer("chairs", { lb: 0 });
388
+ * const x = m.continuous(3, { lb: 0, prefix: "x" });
389
+ *
390
+ * m.constraint(expr`${desks} + 2 * ${chairs} <= 40`, "wood");
391
+ * m.constraint(x.map((v) => expr`${v} <= 10`));
392
+ * m.constraint(desks.ge(2)).constraint(chairs.ge(4));
393
+ * ```
148
394
  */
149
- constrain(constraint: Constraint | LinExpr | BoolExpr | string, name?: string): this;
150
- constrain(constraints: (Constraint | LinExpr | BoolExpr | string)[]): this;
395
+ constraint(constraint: ConstraintItem | LinExpr | QuadExpr, name?: string): this;
396
+ constraint(constraints: ReadonlyArray<ConstraintItem | LinExpr | QuadExpr>): this;
151
397
  /**
152
- * Extract primal solution values directly into plain primitives:
153
- * - m.getValues(x) -> number | null
154
- * - m.getValues([x, y]) -> (number | null)[]
155
- * - m.getValues(record) -> Record<string, number | null>
156
- * - m.getValues(matrix) -> Record<string, Record<string, number | null>>
398
+ * Extract primal solution values directly into plain primitives, preserving container shapes:
399
+ * - `m.getValues(x)` -> `number | null`
400
+ * - `m.getValues(p)` -> `boolean | null`
401
+ * - `m.getValues([x, y])` -> `(number | null)[]`
402
+ * - `m.getValues([[x11, x12], [x21, x22]])` -> `(number | null)[][]`
403
+ * - `m.getValues(record)` -> `Record<K, number | boolean | null>`
404
+ * - `m.getValues(matrix)` -> `Record<R, Record<C, number | boolean | null>>`
405
+ *
406
+ * @example
407
+ * ```ts
408
+ * const m = new Model("GetValuesExample");
409
+ * const qty = m.integer(["desk", "chair"] as const, { lb: 0, ub: 10 });
410
+ * m.objective(maximize(expr`30 * ${qty.desk} + 50 * ${qty.chair}`));
411
+ * m.constraint(expr`${qty.desk} + ${qty.chair} <= 10`);
412
+ * await m.solve();
413
+ * const values = m.getValues(qty); // Record<"desk" | "chair", number | null>
414
+ * ```
157
415
  */
158
416
  getValues(item: Var): number | null;
159
- getValues(items: Var[]): (number | null)[];
417
+ getValues(item: BoolVar): boolean | null;
418
+ getValues(items: ReadonlyArray<Var>): (number | null)[];
419
+ getValues(items: ReadonlyArray<BoolVar>): (boolean | null)[];
420
+ getValues(items: ReadonlyArray<ReadonlyArray<Var>>): (number | null)[][];
421
+ getValues(items: ReadonlyArray<ReadonlyArray<BoolVar>>): (boolean | null)[][];
422
+ getValues<C extends string | number>(items: ReadonlyArray<Record<C, Var>>): Array<Record<C, number | null>>;
423
+ getValues<C extends string | number>(items: ReadonlyArray<Record<C, BoolVar>>): Array<Record<C, boolean | null>>;
424
+ getValues<R extends string | number>(items: Record<R, ReadonlyArray<Var>>): Record<R, (number | null)[]>;
425
+ getValues<R extends string | number>(items: Record<R, ReadonlyArray<BoolVar>>): Record<R, (boolean | null)[]>;
160
426
  getValues<K extends string | number>(items: Record<K, Var>): Record<K, number | null>;
427
+ getValues<K extends string | number>(items: Record<K, BoolVar>): Record<K, boolean | null>;
161
428
  getValues<R extends string | number, C extends string | number>(items: Record<R, Record<C, Var>>): Record<R, Record<C, number | null>>;
429
+ getValues<R extends string | number, C extends string | number>(items: Record<R, Record<C, BoolVar>>): Record<R, Record<C, boolean | null>>;
162
430
  /**
163
- * Add a Boolean (propositional logic) variable for SMT / SAT modeling
431
+ * Internal linear or logical constraint adder
164
432
  */
165
- addBoolVar(name?: string): BoolVar;
433
+ private addConstraint;
166
434
  /**
167
- * Add a linear or logical constraint to the model
168
- */
169
- addConstraint(constraint: Constraint | LinExpr | string, name?: string): Constraint;
170
- addConstraint(constraint: BoolExpr, name?: string): BoolExpr;
171
- addConstraint(constraint: Constraint | LinExpr | BoolExpr | string, name?: string): Constraint | BoolExpr;
172
- /**
173
- * python-mip / Gurobi style alias for addConstraint
174
- */
175
- addConstr(constraint: Constraint | BoolExpr | string, name?: string): Constraint | BoolExpr;
176
- /**
177
- * Add multiple constraints
178
- */
179
- addConstrs(constraints: (Constraint | BoolExpr | string)[]): (Constraint | BoolExpr)[];
180
- /**
181
- * Universal classical fluent adder for constraints, variables, or batches.
182
- * Enables:
183
- * - m.add(x.mul(2).add(y).le(10))
184
- * - m.add(c`${x} + ${y} <= 10`)
185
- * - m.add("x + y <= 10")
186
- * - m.add([c1, c2, c3])
187
- * - m.add(new Var(...))
188
- * - m.add(c1).add(c2) (fluent chaining)
435
+ * Add an indicator constraint via floating-point Big-M auto-linearization:
436
+ * `"If binVar === activeValue, then constraint holds"` (`activeValue` defaults to `1`).
437
+ *
438
+ * **Formulation & solver capability notes**:
439
+ * - Automatically linearizes `<=`, `>=`, and `==` constraints for LP/MIP solvers (HiGHS, GLPK)
440
+ * under standard floating-point feasibility tolerances (`==` generates `_le` and `_ge` rows).
441
+ * - Variables appearing in `constraint` must have finite bounds (`lb`/`ub`) to derive a safe tight Big-M
442
+ * automatically, unless an explicit `{ M }` override is supplied in `options`.
443
+ * - Because Big-M rows are floating-point linear relaxations, they do not guarantee exact rational
444
+ * equivalence in SMT solvers (Z3); use `implies(...)` with `m.bool(...)` when exact SMT diagnosis is required.
445
+ *
446
+ * @example
447
+ * ```ts
448
+ * const m = new Model("IndicatorExample");
449
+ * const b = m.binary("b");
450
+ * const x = m.continuous("x", { lb: 0, ub: 100 });
451
+ * // Enforces x <= 50 when b === 1 (relaxed to x <= 100 when b === 0)
452
+ * m.indicator(b, expr`${x} <= 50`, { activeValue: 1, name: "cap_when_b" });
453
+ * ```
189
454
  */
190
- add(constraint: Constraint, name?: string): this;
191
- add(expr: BoolExpr, name?: string): this;
192
- add(strConstraint: string, name?: string): this;
193
- add(variable: Var): this;
194
- add(items: (Constraint | BoolExpr | string | Var)[]): this;
455
+ indicator(binVar: Var, constraint: Constraint | LinExpr | QuadExpr, options?: IndicatorOptions): Constraint[];
195
456
  /**
196
- * Add an indicator constraint via Big-M auto-linearization:
197
- * "If binVar == activeValue, then constraint holds"
457
+ * Validate model integrity (`validateIntegrity()`) and serialize the model's linear/quadratic objective,
458
+ * linear constraints, and numeric variable bounds/domains (`Continuous`, `Integer`, `Binary`) to a
459
+ * standard CPLEX LP format string with collision-free sanitized identifiers.
460
+ *
461
+ * **Note**: Propositional `BoolVar`s and SMT `logicalConstraints` are not part of the CPLEX LP format.
198
462
  *
199
- * Automatically handles <=, >=, and == constraints.
200
- * Enables solving conditional logic in MIP solvers (HiGHS, GLPK) without requiring SMT.
463
+ * @example
464
+ * ```ts
465
+ * const m = new Model("LpExportExample");
466
+ * const x = m.integer("x", { lb: 0, ub: 10 });
467
+ * m.objective(maximize(expr`5 * ${x}`));
468
+ * m.constraint(expr`${x} <= 8`, "cap");
469
+ * const lpText = m.toLpString();
470
+ * ```
201
471
  */
202
- indicator(binVar: Var, constraint: Constraint | LinExpr, options?: IndicatorOptions): Constraint[];
472
+ toLpString(): string;
203
473
  /**
204
- * @deprecated Use m.indicator(binVar, constraint, options) instead.
474
+ * Validates model integrity: variable bounds, ownership, numeric sanity, and unique constraint names.
475
+ * Throws an Error with actionable explanation if any validation rule is violated.
205
476
  */
206
- addIndicator(binVar: Var, constraint: Constraint | LinExpr, options?: IndicatorOptions): Constraint[];
477
+ validateIntegrity(): void;
478
+ private ensureExecutionValid;
479
+ private wrapSolveOptions;
480
+ private hasPrimalFromResult;
207
481
  /**
208
- * Export model to CPLEX LP format string
482
+ * Resolves the target solver and execution options without mutating caller-owned objects,
483
+ * stripping the routing `solver` field from the returned `SolverOptions`.
209
484
  */
210
- toLpString(): string;
485
+ private resolveSolverAndOptions;
211
486
  /**
212
487
  * Solve the optimization model using the configured solver (defaults to HiGHS WASM)
213
- * or a solver passed dynamically (e.g., await model.optimize(new HiGHS()))
488
+ * or a solver passed dynamically (e.g., `await m.solve("glpk")` or `await m.solve({ timeLimit: 10 })`).
489
+ *
490
+ * **Return status (`SolverStatus`)**:
491
+ * - Built-in optimization solvers return `"Optimal"`, `"Feasible"`, `"Infeasible"`, `"Unbounded"`,
492
+ * `"TimeLimit"`, `"IterationLimit"`, `"Error"`, `"Unknown"`, or `"NotSolved"` (`"Sat"` / `"Unsat"`
493
+ * are included in `SolverStatus` for custom/satisfiability adapters).
494
+ * - When a time-, iteration-, or solution-limited run (or an interrupted solve) has found a valid
495
+ * primal incumbent, `solve()` returns `"Feasible"` (`m.hasPrimalSolution === true`, with the exact
496
+ * stopping reason in `m.terminationStatus`).
497
+ * - After solving, read primal/dual values via `x.value`, `m.getValues(...)`, `m.objectiveValue`,
498
+ * `constr.dual`, `constr.slack`, and `x.reducedCost`, and verify primal feasibility with `m.validateSolution()`.
499
+ *
500
+ * **Solver capability preconditions**:
501
+ * - Quadratic objectives (`QuadExpr`) require `supportsQP` (supported by HiGHS; throws on GLPK and Z3).
502
+ * - Logical constraints (`BoolVar`, `and`, `or`, `not`, `implies`, `xor`) require `supportsSMT`
503
+ * (supported by Z3; throws on HiGHS and GLPK).
504
+ * - Primal `initialValue` warm starts require `supportsWarmStart` (supported by HiGHS; throws on GLPK and Z3).
505
+ * - Mutating the model or starting a newer `solve()` / `check()` while in flight discards the stale
506
+ * result and rejects the pending promise.
507
+ *
508
+ * @example
509
+ * ```ts
510
+ * const m = new Model("SolveExample");
511
+ * const x = m.integer("x", { lb: 0, ub: 50, initialValue: 10 });
512
+ * const y = m.integer("y", { lb: 0, ub: 50 });
513
+ * m.objective(maximize(expr`30 * ${x} + 50 * ${y}`));
514
+ * m.constraint(expr`${x} + 2 * ${y} <= 40`, "resource_limit");
515
+ *
516
+ * const status = await m.solve();
517
+ * const report = m.validateSolution();
518
+ * const obj = m.objectiveValue;
519
+ * ```
214
520
  */
215
- optimize(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<SolverStatus>;
521
+ solve(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<SolverStatus>;
216
522
  /**
217
- * Convenience alias for optimize():
218
- * await model.solve()
219
- * await model.solve(new HiGHS())
220
- * await model.solve(new GLPK())
221
- * await model.solve(new RemoteSolver({ url: "..." }))
523
+ * Check satisfiability / feasibility of the model without requiring an objective function.
524
+ *
525
+ * Returns `CheckResult` (`{ status: "Sat" | "Unsat" | "Unknown", values, conflicts?, unsatCore?, runTime? }`).
526
+ *
527
+ * **LP feasibility fallback vs. Z3 requirement**:
528
+ * - **Linear models (`m.check()` fallback)**: For models containing only linear arithmetic constraints,
529
+ * `m.check()` works with any solver (including the default HiGHS and GLPK) by falling back to a
530
+ * zero-objective feasibility `solve()` without mutating `m.objectiveExpr` or `m.sense`.
531
+ * - **Logical constraints & `unsatCore` (requires Z3)**: Models containing `BoolVar` or logical
532
+ * constraints (`and`, `or`, `not`, `implies`, `xor`) require an SMT-capable solver (`{ solver: "z3" }`
533
+ * after `import "@ohtaman/washi/solvers/z3"`). Unsatisfiable-core extraction (`result.conflicts` /
534
+ * `result.unsatCore` and `m.conflicts` / `m.unsatCore`) is populated only when an unsat-core-capable
535
+ * solver such as Z3 returns `"Unsat"`, reporting a subset of registered constraint names that conflicts
536
+ * together with the model's fixed variable bounds/domains.
537
+ * - Mutating the model or starting a newer `solve()` / `check()` while in flight discards the stale
538
+ * result and rejects the pending promise.
539
+ *
540
+ * @example
541
+ * ```ts
542
+ * import "@ohtaman/washi/solvers/z3";
543
+ *
544
+ * // 1. Linear feasibility check (works with default HiGHS via LP fallback)
545
+ * const mLin = new Model("LinearCheck");
546
+ * const x = mLin.continuous("x", { lb: 0, ub: 10 });
547
+ * mLin.constraint(expr`${x} >= 4`, "min_x");
548
+ * const linRes = await mLin.check(); // { status: "Sat", values: { x: ... } }
549
+ *
550
+ * // 2. SMT satisfiability & unsatCore diagnosis (requires Z3)
551
+ * const mSmt = new Model("PolicyCheck", { solver: "z3" });
552
+ * const vip = mSmt.bool("vip");
553
+ * const discount = mSmt.integer("discount", { lb: 0, ub: 50 });
554
+ * mSmt.constraint(vip, "is_vip");
555
+ * mSmt.constraint(implies(vip, expr`${discount} >= 20`), "vip_min");
556
+ * mSmt.constraint(expr`${discount} <= 10`, "cap");
557
+ * const smtRes = await mSmt.check(); // { status: "Unsat", conflicts: [...], unsatCore: [...] }
558
+ * ```
222
559
  */
223
- solve(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<SolverStatus>;
560
+ check(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<CheckResult>;
224
561
  /**
225
- * Check satisfiability of the model without requiring an objective function.
226
- * Can be used to verify feasibility or diagnose infeasibilities / unsat core.
562
+ * Independently evaluate and verify variable finiteness, variable bounds (`lb`/`ub`),
563
+ * integer/binary/boolean domains, linear constraint residuals, and logical (SMT/propositional)
564
+ * constraints against the model's current solved values (or an explicit `options.values` map).
565
+ *
566
+ * Returns a `SolutionValidationReport` (`{ valid, complete, maxViolation, violations, violationsByName, uncheckedConstraints, missingVariables, summary }`).
567
+ *
568
+ * **Verification notes**:
569
+ * - Runs purely in JavaScript/TypeScript without requiring any WASM solver runtime.
570
+ * - Verifies primal feasibility of variables and constraints; does not recompute or compare `m.objectiveValue`.
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * const m = new Model("ValidateExample");
575
+ * const x = m.integer("x", { lb: 0, ub: 10 });
576
+ * m.objective(maximize(expr`3 * ${x}`));
577
+ * m.constraint(expr`${x} <= 7`, "cap");
578
+ *
579
+ * await m.solve();
580
+ * const report = m.validateSolution();
581
+ * const isFeasible = report.valid && report.maxViolation <= 1e-6;
582
+ * ```
227
583
  */
228
- check(solverOrOptions?: Solver | string | SolveOptions, options?: SolveOptions): Promise<{
229
- status: "Sat" | "Unsat" | "Unknown";
230
- values: Record<string, number | boolean>;
231
- unsatCore?: string[];
232
- runTime?: number;
233
- }>;
584
+ validateSolution(options?: SolutionValidationOptions): SolutionValidationReport;
234
585
  }
235
586
  //# sourceMappingURL=model.d.ts.map