@stevenvo780/st-lang 2.7.1 → 3.0.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 (92) hide show
  1. package/dist/ast/nodes.d.ts +35 -2
  2. package/dist/ast/nodes.d.ts.map +1 -1
  3. package/dist/index.d.ts +5 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +14 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/lexer/lexer.d.ts.map +1 -1
  8. package/dist/lexer/lexer.js +4 -0
  9. package/dist/lexer/lexer.js.map +1 -1
  10. package/dist/lexer/tokens.d.ts +8 -0
  11. package/dist/lexer/tokens.d.ts.map +1 -1
  12. package/dist/lexer/tokens.js +23 -0
  13. package/dist/lexer/tokens.js.map +1 -1
  14. package/dist/parser/parser.d.ts +6 -0
  15. package/dist/parser/parser.d.ts.map +1 -1
  16. package/dist/parser/parser.js +171 -6
  17. package/dist/parser/parser.js.map +1 -1
  18. package/dist/profiles/classical/cdcl.d.ts +34 -0
  19. package/dist/profiles/classical/cdcl.d.ts.map +1 -0
  20. package/dist/profiles/classical/cdcl.js +843 -0
  21. package/dist/profiles/classical/cdcl.js.map +1 -0
  22. package/dist/profiles/classical/dpll.d.ts +11 -1
  23. package/dist/profiles/classical/dpll.d.ts.map +1 -1
  24. package/dist/profiles/classical/dpll.js +54 -17
  25. package/dist/profiles/classical/dpll.js.map +1 -1
  26. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  27. package/dist/profiles/classical/first-order.js +20 -10
  28. package/dist/profiles/classical/first-order.js.map +1 -1
  29. package/dist/profiles/classical/parallel-sat.d.ts +62 -0
  30. package/dist/profiles/classical/parallel-sat.d.ts.map +1 -0
  31. package/dist/profiles/classical/parallel-sat.js +630 -0
  32. package/dist/profiles/classical/parallel-sat.js.map +1 -0
  33. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  34. package/dist/profiles/classical/propositional.js +35 -19
  35. package/dist/profiles/classical/propositional.js.map +1 -1
  36. package/dist/profiles/classical/sat-preprocess.d.ts +17 -0
  37. package/dist/profiles/classical/sat-preprocess.d.ts.map +1 -0
  38. package/dist/profiles/classical/sat-preprocess.js +372 -0
  39. package/dist/profiles/classical/sat-preprocess.js.map +1 -0
  40. package/dist/profiles/classical/undecidability-detector.d.ts +13 -0
  41. package/dist/profiles/classical/undecidability-detector.d.ts.map +1 -0
  42. package/dist/profiles/classical/undecidability-detector.js +277 -0
  43. package/dist/profiles/classical/undecidability-detector.js.map +1 -0
  44. package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
  45. package/dist/profiles/paraconsistent/belnap.js +4 -2
  46. package/dist/profiles/paraconsistent/belnap.js.map +1 -1
  47. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  48. package/dist/profiles/shared/tableau-engine.js +3 -1
  49. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  50. package/dist/protocol/handler.d.ts.map +1 -1
  51. package/dist/protocol/handler.js +327 -88
  52. package/dist/protocol/handler.js.map +1 -1
  53. package/dist/runtime/formula-factory.d.ts.map +1 -1
  54. package/dist/runtime/formula-factory.js +3 -2
  55. package/dist/runtime/formula-factory.js.map +1 -1
  56. package/dist/runtime/interpreter.d.ts +33 -0
  57. package/dist/runtime/interpreter.d.ts.map +1 -1
  58. package/dist/runtime/interpreter.js +516 -5
  59. package/dist/runtime/interpreter.js.map +1 -1
  60. package/dist/tests/benchmark-cdcl.test.d.ts +2 -0
  61. package/dist/tests/benchmark-cdcl.test.d.ts.map +1 -0
  62. package/dist/tests/benchmark-cdcl.test.js +172 -0
  63. package/dist/tests/benchmark-cdcl.test.js.map +1 -0
  64. package/dist/tests/limits.test.js +11 -4
  65. package/dist/tests/limits.test.js.map +1 -1
  66. package/dist/tests/parallel-sat.test.d.ts +2 -0
  67. package/dist/tests/parallel-sat.test.d.ts.map +1 -0
  68. package/dist/tests/parallel-sat.test.js +351 -0
  69. package/dist/tests/parallel-sat.test.js.map +1 -0
  70. package/dist/tests/stress-cdcl.test.d.ts +2 -0
  71. package/dist/tests/stress-cdcl.test.d.ts.map +1 -0
  72. package/dist/tests/stress-cdcl.test.js +792 -0
  73. package/dist/tests/stress-cdcl.test.js.map +1 -0
  74. package/dist/tests/stress-extreme.test.d.ts +2 -0
  75. package/dist/tests/stress-extreme.test.d.ts.map +1 -0
  76. package/dist/tests/stress-extreme.test.js +1005 -0
  77. package/dist/tests/stress-extreme.test.js.map +1 -0
  78. package/dist/tests/v3-features.test.d.ts +2 -0
  79. package/dist/tests/v3-features.test.d.ts.map +1 -0
  80. package/dist/tests/v3-features.test.js +529 -0
  81. package/dist/tests/v3-features.test.js.map +1 -0
  82. package/dist/tests/v3-stress.test.d.ts +2 -0
  83. package/dist/tests/v3-stress.test.d.ts.map +1 -0
  84. package/dist/tests/v3-stress.test.js +755 -0
  85. package/dist/tests/v3-stress.test.js.map +1 -0
  86. package/dist/text-layer/compiler.d.ts +4 -1
  87. package/dist/text-layer/compiler.d.ts.map +1 -1
  88. package/dist/text-layer/compiler.js +35 -0
  89. package/dist/text-layer/compiler.js.map +1 -1
  90. package/dist/types/index.d.ts +27 -1
  91. package/dist/types/index.d.ts.map +1 -1
  92. package/package.json +1 -1
@@ -0,0 +1,843 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // CDCL SAT Solver — Conflict-Driven Clause Learning
4
+ // Implements: Watched Literals, 1UIP Conflict Analysis,
5
+ // Non-Chronological Backtracking, VSIDS, Luby Restarts,
6
+ // Phase Saving, Preprocessing Integration,
7
+ // Pattern Detection (PHP, Parity, Symmetry)
8
+ // ============================================================
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.detectPatterns = detectPatterns;
11
+ exports.addSymmetryBreaking = addSymmetryBreaking;
12
+ exports.cdcl = cdcl;
13
+ exports.cdclAsync = cdclAsync;
14
+ const propositional_1 = require("./propositional");
15
+ const sat_preprocess_1 = require("./sat-preprocess");
16
+ const parallel_sat_1 = require("./parallel-sat");
17
+ const TRUE_FORMULA = { kind: 'atom', name: '__TRUE__' };
18
+ const FALSE_FORMULA = { kind: 'atom', name: '__FALSE__' };
19
+ // ============================================================
20
+ // Tseitin CNF Encoder
21
+ // ============================================================
22
+ function simplifyNNF(f) {
23
+ switch (f.kind) {
24
+ case 'atom':
25
+ case 'not':
26
+ return f;
27
+ case 'and': {
28
+ if (!f.args || f.args.length < 2)
29
+ return f;
30
+ const left = simplifyNNF(f.args[0]);
31
+ const right = simplifyNNF(f.args[1]);
32
+ if (left === FALSE_FORMULA || right === FALSE_FORMULA)
33
+ return FALSE_FORMULA;
34
+ if (left === TRUE_FORMULA)
35
+ return right;
36
+ if (right === TRUE_FORMULA)
37
+ return left;
38
+ if (isComplement(left, right))
39
+ return FALSE_FORMULA;
40
+ if (left === f.args[0] && right === f.args[1])
41
+ return f;
42
+ return { kind: 'and', args: [left, right] };
43
+ }
44
+ case 'or': {
45
+ if (!f.args || f.args.length < 2)
46
+ return f;
47
+ const left = simplifyNNF(f.args[0]);
48
+ const right = simplifyNNF(f.args[1]);
49
+ if (left === TRUE_FORMULA || right === TRUE_FORMULA)
50
+ return TRUE_FORMULA;
51
+ if (left === FALSE_FORMULA)
52
+ return right;
53
+ if (right === FALSE_FORMULA)
54
+ return left;
55
+ if (isComplement(left, right))
56
+ return TRUE_FORMULA;
57
+ if (left === f.args[0] && right === f.args[1])
58
+ return f;
59
+ return { kind: 'or', args: [left, right] };
60
+ }
61
+ default:
62
+ return f;
63
+ }
64
+ }
65
+ function isComplement(a, b) {
66
+ if (a.kind === 'not' && a.args?.[0]?.kind === 'atom' && b.kind === 'atom') {
67
+ return a.args[0].name === b.name;
68
+ }
69
+ if (b.kind === 'not' && b.args?.[0]?.kind === 'atom' && a.kind === 'atom') {
70
+ return b.args[0].name === a.name;
71
+ }
72
+ return false;
73
+ }
74
+ class TseitinEncoder {
75
+ atomMap = new Map();
76
+ atomNames = [];
77
+ nextVar = 1;
78
+ rawClauses = [];
79
+ getVar(name) {
80
+ let v = this.atomMap.get(name);
81
+ if (v !== undefined)
82
+ return v;
83
+ v = this.nextVar++;
84
+ this.atomMap.set(name, v);
85
+ this.atomNames.push(name);
86
+ return v;
87
+ }
88
+ freshVar() {
89
+ const v = this.nextVar++;
90
+ this.atomNames.push(`_t${v}`);
91
+ return v;
92
+ }
93
+ get numVars() {
94
+ return this.nextVar - 1;
95
+ }
96
+ get clauses() {
97
+ return this.rawClauses.map((c) => new Int32Array(c));
98
+ }
99
+ encode(formula) {
100
+ const nnf = simplifyNNF((0, propositional_1.toNNF)(formula));
101
+ if (nnf === FALSE_FORMULA) {
102
+ this.rawClauses.push([]);
103
+ return;
104
+ }
105
+ if (nnf === TRUE_FORMULA) {
106
+ return;
107
+ }
108
+ const topLit = this.encodeNode(nnf);
109
+ this.rawClauses.push([topLit]);
110
+ }
111
+ encodeNode(f) {
112
+ switch (f.kind) {
113
+ case 'atom':
114
+ return f.name ? this.getVar(f.name) : this.freshVar();
115
+ case 'not': {
116
+ const args = f.args ?? [];
117
+ if (args[0]?.kind === 'atom' && args[0].name) {
118
+ return -this.getVar(args[0].name);
119
+ }
120
+ return -this.encodeNode(args[0]);
121
+ }
122
+ case 'and': {
123
+ const children = this.flattenAssoc(f, 'and');
124
+ if (children.length === 1)
125
+ return this.encodeNode(children[0]);
126
+ const lits = children.map((c) => this.encodeNode(c));
127
+ const g = this.freshVar();
128
+ for (const l of lits)
129
+ this.rawClauses.push([-g, l]);
130
+ this.rawClauses.push([...lits.map((l) => -l), g]);
131
+ return g;
132
+ }
133
+ case 'or': {
134
+ const children = this.flattenAssoc(f, 'or');
135
+ if (children.length === 1)
136
+ return this.encodeNode(children[0]);
137
+ const lits = children.map((c) => this.encodeNode(c));
138
+ const g = this.freshVar();
139
+ this.rawClauses.push([-g, ...lits]);
140
+ for (const l of lits)
141
+ this.rawClauses.push([-l, g]);
142
+ return g;
143
+ }
144
+ case 'implies': {
145
+ const [left, right] = f.args ?? [];
146
+ const a = this.encodeNode(left);
147
+ const b = this.encodeNode(right);
148
+ const g = this.freshVar();
149
+ this.rawClauses.push([-g, -a, b]);
150
+ this.rawClauses.push([a, g]);
151
+ this.rawClauses.push([-b, g]);
152
+ return g;
153
+ }
154
+ case 'biconditional': {
155
+ const [left, right] = f.args ?? [];
156
+ const a = this.encodeNode(left);
157
+ const b = this.encodeNode(right);
158
+ const g = this.freshVar();
159
+ this.rawClauses.push([-g, -a, b]);
160
+ this.rawClauses.push([-g, -b, a]);
161
+ this.rawClauses.push([a, b, g]);
162
+ this.rawClauses.push([-a, -b, g]);
163
+ return g;
164
+ }
165
+ case 'xor': {
166
+ const [left, right] = f.args ?? [];
167
+ const a = this.encodeNode(left);
168
+ const b = this.encodeNode(right);
169
+ const g = this.freshVar();
170
+ this.rawClauses.push([-g, a, b]);
171
+ this.rawClauses.push([-g, -a, -b]);
172
+ this.rawClauses.push([g, -a, b]);
173
+ this.rawClauses.push([g, a, -b]);
174
+ return g;
175
+ }
176
+ case 'nand': {
177
+ const [left, right] = f.args ?? [];
178
+ const a = this.encodeNode(left);
179
+ const b = this.encodeNode(right);
180
+ const g = this.freshVar();
181
+ this.rawClauses.push([-g, -a, -b]);
182
+ this.rawClauses.push([a, g]);
183
+ this.rawClauses.push([b, g]);
184
+ return g;
185
+ }
186
+ case 'nor': {
187
+ const [left, right] = f.args ?? [];
188
+ const a = this.encodeNode(left);
189
+ const b = this.encodeNode(right);
190
+ const g = this.freshVar();
191
+ this.rawClauses.push([-g, -a]);
192
+ this.rawClauses.push([-g, -b]);
193
+ this.rawClauses.push([a, b, g]);
194
+ return g;
195
+ }
196
+ default:
197
+ return this.freshVar();
198
+ }
199
+ }
200
+ flattenAssoc(f, kind) {
201
+ if (f.kind !== kind || !f.args)
202
+ return [f];
203
+ const result = [];
204
+ for (const arg of f.args) {
205
+ if (arg) {
206
+ if (arg.kind === kind) {
207
+ result.push(...this.flattenAssoc(arg, kind));
208
+ }
209
+ else {
210
+ result.push(arg);
211
+ }
212
+ }
213
+ }
214
+ return result;
215
+ }
216
+ }
217
+ // ============================================================
218
+ // CDCL Solver Core — Clean MiniSat-style implementation
219
+ // ============================================================
220
+ /** Luby sequence for restarts */
221
+ function luby(i) {
222
+ let size = 1;
223
+ let seq = 0;
224
+ while (size < i + 1) {
225
+ size = 2 * size + 1;
226
+ }
227
+ while (size - 1 !== i) {
228
+ size = (size - 1) >> 1;
229
+ seq++;
230
+ if (i >= size) {
231
+ i -= size;
232
+ }
233
+ }
234
+ return 1 << seq;
235
+ }
236
+ /** Map literal to a non-negative index for watch list arrays.
237
+ * positive lit l → l - 1
238
+ * negative lit -l → nv + l - 1
239
+ */
240
+ function litToIdx(lit, nv) {
241
+ return lit > 0 ? lit - 1 : nv + -lit - 1;
242
+ }
243
+ /**
244
+ * Core CDCL solver operating on raw clauses.
245
+ */
246
+ function cdclSolve(inputClauses, numVars, atomNames, timeoutMs) {
247
+ const startTime = Date.now();
248
+ const stats = {
249
+ decisions: 0,
250
+ propagations: 0,
251
+ conflicts: 0,
252
+ learnedClauses: 0,
253
+ restarts: 0,
254
+ preprocessEliminated: 0,
255
+ solveTimeMs: 0,
256
+ };
257
+ // --- Preprocessing ---
258
+ const ppResult = (0, sat_preprocess_1.preprocess)(inputClauses, numVars);
259
+ if (ppResult.trivialUnsat) {
260
+ stats.solveTimeMs = Date.now() - startTime;
261
+ return { satisfiable: false, stats };
262
+ }
263
+ // Mutable clause array: original + learned
264
+ const allClauses = [...ppResult.clauses];
265
+ stats.preprocessEliminated = inputClauses.length - allClauses.length;
266
+ const originalCount = allClauses.length;
267
+ if (allClauses.length === 0) {
268
+ const model = buildModelFromForced(ppResult.forcedLiterals, numVars, atomNames);
269
+ stats.solveTimeMs = Date.now() - startTime;
270
+ return { satisfiable: true, model, stats };
271
+ }
272
+ // --- Variable state arrays (1-indexed) ---
273
+ const varVal = new Int8Array(numVars + 1); // 0=undef, 1=true, -1=false
274
+ const varLevel = new Int32Array(numVars + 1).fill(-1);
275
+ const varAnte = new Int32Array(numVars + 1).fill(-1);
276
+ // Trail
277
+ const trail = [];
278
+ const trailLim = [];
279
+ let qHead = 0;
280
+ // Phase saving
281
+ const phase = new Int8Array(numVars + 1);
282
+ // --- 2-Literal Watching (MiniSat convention) ---
283
+ // watches[litToIdx(l)] = list of clause indices watching literal l
284
+ // Invariant: allClauses[ci][0] and allClauses[ci][1] are the two watched lits
285
+ const watchSize = 2 * numVars;
286
+ const watches = Array.from({ length: watchSize });
287
+ function rebuildWatches() {
288
+ for (let i = 0; i < watchSize; i++)
289
+ watches[i] = [];
290
+ for (let ci = 0; ci < allClauses.length; ci++) {
291
+ const c = allClauses[ci];
292
+ if (c.length >= 2) {
293
+ watches[litToIdx(c[0], numVars)].push(ci);
294
+ watches[litToIdx(c[1], numVars)].push(ci);
295
+ }
296
+ }
297
+ }
298
+ rebuildWatches();
299
+ // --- VSIDS ---
300
+ const activity = new Float64Array(numVars + 1);
301
+ let varInc = 1.0;
302
+ const VAR_DECAY = 0.95;
303
+ for (const c of allClauses) {
304
+ for (let i = 0; i < c.length; i++)
305
+ activity[Math.abs(c[i])] += 1.0;
306
+ }
307
+ function bumpVar(v) {
308
+ activity[v] += varInc;
309
+ if (activity[v] > 1e100) {
310
+ for (let i = 1; i <= numVars; i++)
311
+ activity[i] *= 1e-100;
312
+ varInc *= 1e-100;
313
+ }
314
+ }
315
+ function decayAct() {
316
+ varInc /= VAR_DECAY;
317
+ }
318
+ // --- Helpers ---
319
+ function currentDL() {
320
+ return trailLim.length;
321
+ }
322
+ function litIsTrue(lit) {
323
+ return lit > 0 ? varVal[lit] === 1 : varVal[-lit] === -1;
324
+ }
325
+ function litIsFalse(lit) {
326
+ return lit > 0 ? varVal[lit] === -1 : varVal[-lit] === 1;
327
+ }
328
+ function enqueue(lit, lev, reason) {
329
+ const v = Math.abs(lit);
330
+ if (varVal[v] !== 0)
331
+ return litIsTrue(lit);
332
+ varVal[v] = lit > 0 ? 1 : -1;
333
+ varLevel[v] = lev;
334
+ varAnte[v] = reason;
335
+ trail.push(lit);
336
+ phase[v] = lit > 0 ? 1 : 0;
337
+ stats.propagations++;
338
+ return true;
339
+ }
340
+ // --- BCP (Boolean Constraint Propagation) ---
341
+ function propagate() {
342
+ while (qHead < trail.length) {
343
+ const p = trail[qHead++];
344
+ const falseLit = -p;
345
+ const wIdx = litToIdx(falseLit, numVars);
346
+ const wl = watches[wIdx];
347
+ let j = 0;
348
+ for (let i = 0; i < wl.length; i++) {
349
+ const ci = wl[i];
350
+ const c = allClauses[ci];
351
+ if (!c || c.length < 2) {
352
+ wl[j++] = ci;
353
+ continue;
354
+ }
355
+ // Ensure falseLit is at position 1
356
+ if (c[0] === falseLit) {
357
+ c[0] = c[1];
358
+ c[1] = falseLit;
359
+ }
360
+ // If c[0] is true, clause satisfied
361
+ if (litIsTrue(c[0])) {
362
+ wl[j++] = ci;
363
+ continue;
364
+ }
365
+ // Find replacement for c[1]
366
+ let found = false;
367
+ for (let k = 2; k < c.length; k++) {
368
+ if (!litIsFalse(c[k])) {
369
+ c[1] = c[k];
370
+ c[k] = falseLit;
371
+ watches[litToIdx(c[1], numVars)].push(ci);
372
+ found = true;
373
+ break;
374
+ }
375
+ }
376
+ if (found)
377
+ continue;
378
+ // No replacement found
379
+ wl[j++] = ci;
380
+ if (litIsFalse(c[0])) {
381
+ // CONFLICT
382
+ for (let r = i + 1; r < wl.length; r++)
383
+ wl[j++] = wl[r];
384
+ wl.length = j;
385
+ qHead = trail.length;
386
+ return ci;
387
+ }
388
+ // Unit propagation
389
+ if (!enqueue(c[0], currentDL(), ci)) {
390
+ for (let r = i + 1; r < wl.length; r++)
391
+ wl[j++] = wl[r];
392
+ wl.length = j;
393
+ qHead = trail.length;
394
+ return ci;
395
+ }
396
+ }
397
+ wl.length = j;
398
+ }
399
+ return -1;
400
+ }
401
+ // --- 1UIP Conflict Analysis ---
402
+ function analyzeConflict(conflictCi) {
403
+ const dl = currentDL();
404
+ const seen = new Uint8Array(numVars + 1);
405
+ const outLits = [];
406
+ let counter = 0;
407
+ let btLevel = 0;
408
+ function addLits(ci, skipVar) {
409
+ const c = allClauses[ci];
410
+ for (let i = 0; i < c.length; i++) {
411
+ const v = Math.abs(c[i]);
412
+ if (v === skipVar || seen[v])
413
+ continue;
414
+ seen[v] = 1;
415
+ bumpVar(v);
416
+ if (varLevel[v] === dl) {
417
+ counter++;
418
+ }
419
+ else if (varLevel[v] > 0) {
420
+ outLits.push(c[i]);
421
+ if (varLevel[v] > btLevel)
422
+ btLevel = varLevel[v];
423
+ }
424
+ }
425
+ }
426
+ addLits(conflictCi, 0);
427
+ let ti = trail.length - 1;
428
+ let assertLit = 0;
429
+ while (counter > 0) {
430
+ while (ti >= 0 && !seen[Math.abs(trail[ti])])
431
+ ti--;
432
+ if (ti < 0)
433
+ break;
434
+ const p = trail[ti--];
435
+ const v = Math.abs(p);
436
+ seen[v] = 0;
437
+ counter--;
438
+ if (counter === 0) {
439
+ assertLit = -p;
440
+ }
441
+ else if (varAnte[v] >= 0) {
442
+ addLits(varAnte[v], v);
443
+ }
444
+ }
445
+ const learned = [];
446
+ if (assertLit !== 0)
447
+ learned.push(assertLit);
448
+ learned.push(...outLits);
449
+ if (learned.length === 0) {
450
+ return { learned: new Int32Array(0), btLevel: -1 };
451
+ }
452
+ if (learned.length <= 1)
453
+ btLevel = 0;
454
+ // Put highest-level non-asserting literal at position 1
455
+ if (learned.length >= 3) {
456
+ let mx = 1;
457
+ for (let i = 2; i < learned.length; i++) {
458
+ if (varLevel[Math.abs(learned[i])] > varLevel[Math.abs(learned[mx])]) {
459
+ mx = i;
460
+ }
461
+ }
462
+ if (mx !== 1) {
463
+ const tmp = learned[1];
464
+ learned[1] = learned[mx];
465
+ learned[mx] = tmp;
466
+ }
467
+ }
468
+ decayAct();
469
+ return { learned: new Int32Array(learned), btLevel };
470
+ }
471
+ // --- Backtrack ---
472
+ function backtrack(target) {
473
+ if (currentDL() <= target)
474
+ return;
475
+ const btPoint = target < trailLim.length ? trailLim[target] : trail.length;
476
+ for (let i = trail.length - 1; i >= btPoint; i--) {
477
+ const v = Math.abs(trail[i]);
478
+ varVal[v] = 0;
479
+ varLevel[v] = -1;
480
+ varAnte[v] = -1;
481
+ }
482
+ trail.length = btPoint;
483
+ trailLim.length = target;
484
+ qHead = btPoint;
485
+ }
486
+ // --- Pick branch variable ---
487
+ function pickBranch() {
488
+ let best = 0;
489
+ let bestAct = -1;
490
+ for (let v = 1; v <= numVars; v++) {
491
+ if (varVal[v] === 0 && activity[v] > bestAct) {
492
+ bestAct = activity[v];
493
+ best = v;
494
+ }
495
+ }
496
+ return best;
497
+ }
498
+ // --- Add learned clause ---
499
+ function addLearned(learned) {
500
+ const ci = allClauses.length;
501
+ allClauses.push(learned);
502
+ stats.learnedClauses++;
503
+ if (learned.length >= 2) {
504
+ watches[litToIdx(learned[0], numVars)].push(ci);
505
+ watches[litToIdx(learned[1], numVars)].push(ci);
506
+ }
507
+ return ci;
508
+ }
509
+ // --- Clause database reduction ---
510
+ function reduceDB() {
511
+ const learnedCount = allClauses.length - originalCount;
512
+ if (learnedCount < 100)
513
+ return;
514
+ const locked = new Uint8Array(allClauses.length);
515
+ for (let v = 1; v <= numVars; v++) {
516
+ if (varVal[v] !== 0 && varAnte[v] >= originalCount) {
517
+ locked[varAnte[v]] = 1;
518
+ }
519
+ }
520
+ const removable = [];
521
+ for (let ci = originalCount; ci < allClauses.length; ci++) {
522
+ if (!locked[ci] && allClauses[ci].length > 2)
523
+ removable.push(ci);
524
+ }
525
+ if (removable.length < 50)
526
+ return;
527
+ removable.sort((a, b) => allClauses[b].length - allClauses[a].length);
528
+ const toRemove = new Set(removable.slice(0, Math.floor(removable.length / 2)));
529
+ const oldToNew = new Map();
530
+ const newClauses = [];
531
+ for (let ci = 0; ci < allClauses.length; ci++) {
532
+ if (!toRemove.has(ci)) {
533
+ oldToNew.set(ci, newClauses.length);
534
+ newClauses.push(allClauses[ci]);
535
+ }
536
+ }
537
+ for (let v = 1; v <= numVars; v++) {
538
+ if (varAnte[v] >= 0) {
539
+ const mapped = oldToNew.get(varAnte[v]);
540
+ varAnte[v] = mapped !== undefined ? mapped : -1;
541
+ }
542
+ }
543
+ allClauses.length = 0;
544
+ allClauses.push(...newClauses);
545
+ rebuildWatches();
546
+ }
547
+ // --- Apply forced literals from preprocessing ---
548
+ for (const lit of ppResult.forcedLiterals) {
549
+ if (!enqueue(lit, 0, -1)) {
550
+ stats.solveTimeMs = Date.now() - startTime;
551
+ return { satisfiable: false, stats };
552
+ }
553
+ }
554
+ // --- Process initial unit clauses ---
555
+ for (let ci = 0; ci < allClauses.length; ci++) {
556
+ if (allClauses[ci].length === 1) {
557
+ if (!enqueue(allClauses[ci][0], 0, ci)) {
558
+ stats.solveTimeMs = Date.now() - startTime;
559
+ return { satisfiable: false, stats };
560
+ }
561
+ }
562
+ }
563
+ let conflict = propagate();
564
+ if (conflict !== -1) {
565
+ stats.solveTimeMs = Date.now() - startTime;
566
+ return { satisfiable: false, stats };
567
+ }
568
+ // --- Main CDCL Loop ---
569
+ let lubyIdx = 0;
570
+ const RESTART_BASE = 100;
571
+ let conflictsUntilRestart = RESTART_BASE * luby(lubyIdx);
572
+ let conflictsSinceRestart = 0;
573
+ let nextReduceDB = originalCount * 3;
574
+ while (true) {
575
+ if (Date.now() - startTime > timeoutMs) {
576
+ stats.solveTimeMs = Date.now() - startTime;
577
+ return { satisfiable: false, stats };
578
+ }
579
+ const v = pickBranch();
580
+ if (v === 0)
581
+ break; // SAT
582
+ stats.decisions++;
583
+ trailLim.push(trail.length);
584
+ const lit = phase[v] === 1 ? v : -v;
585
+ enqueue(lit, currentDL(), -1);
586
+ conflict = propagate();
587
+ while (conflict !== -1) {
588
+ stats.conflicts++;
589
+ conflictsSinceRestart++;
590
+ if (currentDL() === 0) {
591
+ stats.solveTimeMs = Date.now() - startTime;
592
+ return { satisfiable: false, stats };
593
+ }
594
+ const { learned, btLevel } = analyzeConflict(conflict);
595
+ if (learned.length === 0 || btLevel < 0) {
596
+ stats.solveTimeMs = Date.now() - startTime;
597
+ return { satisfiable: false, stats };
598
+ }
599
+ backtrack(btLevel);
600
+ const lci = addLearned(learned);
601
+ enqueue(learned[0], learned.length === 1 ? 0 : btLevel, lci);
602
+ conflict = propagate();
603
+ }
604
+ if (conflictsSinceRestart >= conflictsUntilRestart) {
605
+ stats.restarts++;
606
+ lubyIdx++;
607
+ conflictsUntilRestart = RESTART_BASE * luby(lubyIdx);
608
+ conflictsSinceRestart = 0;
609
+ backtrack(0);
610
+ }
611
+ if (allClauses.length > nextReduceDB) {
612
+ reduceDB();
613
+ nextReduceDB = originalCount + allClauses.length * 2;
614
+ }
615
+ }
616
+ const model = buildModel(varVal, numVars, atomNames);
617
+ stats.solveTimeMs = Date.now() - startTime;
618
+ return { satisfiable: true, model, stats };
619
+ }
620
+ // ============================================================
621
+ // Model building
622
+ // ============================================================
623
+ function buildModel(vals, numVars, atomNames) {
624
+ const model = {};
625
+ for (let i = 0; i < atomNames.length && i < numVars; i++) {
626
+ model[atomNames[i]] = vals[i + 1] === 1 || vals[i + 1] === 0;
627
+ }
628
+ return model;
629
+ }
630
+ function buildModelFromForced(forced, numVars, atomNames) {
631
+ const model = {};
632
+ const v = new Int8Array(numVars + 1);
633
+ for (const lit of forced) {
634
+ v[Math.abs(lit)] = lit > 0 ? 1 : -1;
635
+ }
636
+ for (let i = 0; i < atomNames.length && i < numVars; i++) {
637
+ model[atomNames[i]] = v[i + 1] === 1 || v[i + 1] === 0;
638
+ }
639
+ return model;
640
+ }
641
+ function detectPatterns(clauses, numVars) {
642
+ const units = new Map();
643
+ for (const c of clauses) {
644
+ if (c.length === 0) {
645
+ return {
646
+ pattern: 'empty_clause',
647
+ description: 'Empty clause — trivially unsatisfiable',
648
+ unsatisfiable: true,
649
+ };
650
+ }
651
+ if (c.length === 1) {
652
+ if (units.has(-c[0])) {
653
+ return {
654
+ pattern: 'contradiction',
655
+ description: 'Direct contradiction: unit clauses P and ¬P',
656
+ unsatisfiable: true,
657
+ };
658
+ }
659
+ units.set(c[0], true);
660
+ }
661
+ }
662
+ const php = detectPigeonhole(clauses, numVars);
663
+ if (php)
664
+ return php;
665
+ return null;
666
+ }
667
+ function detectPigeonhole(clauses, numVars) {
668
+ if (numVars < 6 || clauses.length < 10)
669
+ return null;
670
+ const aloClauses = [];
671
+ let amoCount = 0;
672
+ for (const c of clauses) {
673
+ if (c.length >= 2) {
674
+ let allPos = true;
675
+ let allNeg = true;
676
+ for (let i = 0; i < c.length; i++) {
677
+ if (c[i] < 0)
678
+ allPos = false;
679
+ if (c[i] > 0)
680
+ allNeg = false;
681
+ }
682
+ if (allPos)
683
+ aloClauses.push(c);
684
+ if (allNeg && c.length === 2)
685
+ amoCount++;
686
+ }
687
+ }
688
+ if (aloClauses.length < 2 || amoCount < 3)
689
+ return null;
690
+ const colCount = aloClauses[0].length;
691
+ if (!aloClauses.every((c) => c.length === colCount))
692
+ return null;
693
+ const rowCount = aloClauses.length;
694
+ if (rowCount > colCount) {
695
+ const expectedAMO = (colCount * rowCount * (rowCount - 1)) / 2;
696
+ if (amoCount >= expectedAMO * 0.8) {
697
+ return {
698
+ pattern: 'pigeonhole',
699
+ description: `Pigeonhole Principle: ${rowCount} pigeons, ${colCount} holes — UNSAT (Haken 1985)`,
700
+ unsatisfiable: true,
701
+ };
702
+ }
703
+ }
704
+ return null;
705
+ }
706
+ // ============================================================
707
+ // Symmetry Breaking
708
+ // ============================================================
709
+ function addSymmetryBreaking(clauses, numVars) {
710
+ if (numVars > 100)
711
+ return clauses;
712
+ const sig = new Map();
713
+ for (let v = 1; v <= numVars; v++) {
714
+ const posLens = [];
715
+ const negLens = [];
716
+ for (const c of clauses) {
717
+ for (let i = 0; i < c.length; i++) {
718
+ if (c[i] === v)
719
+ posLens.push(c.length);
720
+ if (c[i] === -v)
721
+ negLens.push(c.length);
722
+ }
723
+ }
724
+ const key = `${posLens.sort().join(',')}|${negLens.sort().join(',')}`;
725
+ const group = sig.get(key);
726
+ if (group)
727
+ group.push(v);
728
+ else
729
+ sig.set(key, [v]);
730
+ }
731
+ const extra = [];
732
+ for (const [, group] of sig) {
733
+ if (group.length < 2 || group.length > 20)
734
+ continue;
735
+ for (let i = 0; i < group.length - 1; i++) {
736
+ extra.push(new Int32Array([-group[i], group[i + 1]]));
737
+ }
738
+ }
739
+ return extra.length > 0 ? [...clauses, ...extra] : clauses;
740
+ }
741
+ // ============================================================
742
+ // Public API
743
+ // ============================================================
744
+ function cdcl(formula, timeoutMs = 30000) {
745
+ const encoder = new TseitinEncoder();
746
+ encoder.encode(formula);
747
+ const pattern = detectPatterns(encoder.clauses, encoder.numVars);
748
+ if (pattern?.unsatisfiable) {
749
+ return {
750
+ satisfiable: false,
751
+ stats: {
752
+ decisions: 0,
753
+ propagations: 0,
754
+ conflicts: 0,
755
+ learnedClauses: 0,
756
+ restarts: 0,
757
+ preprocessEliminated: 0,
758
+ solveTimeMs: 0,
759
+ },
760
+ };
761
+ }
762
+ let clauseSet = encoder.clauses;
763
+ if (encoder.numVars <= 100 && encoder.numVars >= 10) {
764
+ clauseSet = addSymmetryBreaking(clauseSet, encoder.numVars);
765
+ }
766
+ const result = cdclSolve(clauseSet, encoder.numVars, encoder.atomNames, timeoutMs);
767
+ if (result.satisfiable && result.model) {
768
+ const filtered = {};
769
+ for (const [key, value] of Object.entries(result.model)) {
770
+ if (!key.startsWith('_t')) {
771
+ filtered[key] = value;
772
+ }
773
+ }
774
+ result.model = filtered;
775
+ }
776
+ return result;
777
+ }
778
+ /**
779
+ * Versión asíncrona del solver CDCL con soporte de paralelismo.
780
+ * Cuando la fórmula es suficientemente grande (≥ PARALLEL_THRESHOLD vars),
781
+ * lanza workers en portfolio racing. Para fórmulas pequeñas, ejecuta
782
+ * el solver secuencial como wrapper de Promise.
783
+ *
784
+ * Compatible con Node.js (worker_threads) y Browser (Web Workers).
785
+ */
786
+ async function cdclAsync(formula, timeoutMs = 30000) {
787
+ const encoder = new TseitinEncoder();
788
+ encoder.encode(formula);
789
+ // Detección rápida de patrones
790
+ const pattern = detectPatterns(encoder.clauses, encoder.numVars);
791
+ if (pattern?.unsatisfiable) {
792
+ return {
793
+ satisfiable: false,
794
+ stats: {
795
+ decisions: 0,
796
+ propagations: 0,
797
+ conflicts: 0,
798
+ learnedClauses: 0,
799
+ restarts: 0,
800
+ preprocessEliminated: 0,
801
+ solveTimeMs: 0,
802
+ },
803
+ };
804
+ }
805
+ let clauseSet = encoder.clauses;
806
+ if (encoder.numVars <= 100 && encoder.numVars >= 10) {
807
+ clauseSet = addSymmetryBreaking(clauseSet, encoder.numVars);
808
+ }
809
+ // Intentar resolución paralela si el problema es grande
810
+ if (encoder.numVars >= parallel_sat_1.PARALLEL_THRESHOLD) {
811
+ const parallel = (0, parallel_sat_1.tryParallelSolve)(clauseSet, encoder.numVars, encoder.atomNames, timeoutMs);
812
+ if (parallel) {
813
+ const pResult = await parallel;
814
+ // Si el paralelo encontró resultado, filtramos y retornamos
815
+ if (pResult.stats) {
816
+ if (pResult.satisfiable && pResult.model) {
817
+ const filtered = {};
818
+ for (const [key, value] of Object.entries(pResult.model)) {
819
+ if (!key.startsWith('_t')) {
820
+ filtered[key] = value;
821
+ }
822
+ }
823
+ pResult.model = filtered;
824
+ }
825
+ return pResult;
826
+ }
827
+ // Si paralelo no pudo (workers no disponibles), caer a secuencial
828
+ }
829
+ }
830
+ // Fallback secuencial
831
+ const result = cdclSolve(clauseSet, encoder.numVars, encoder.atomNames, timeoutMs);
832
+ if (result.satisfiable && result.model) {
833
+ const filtered = {};
834
+ for (const [key, value] of Object.entries(result.model)) {
835
+ if (!key.startsWith('_t')) {
836
+ filtered[key] = value;
837
+ }
838
+ }
839
+ result.model = filtered;
840
+ }
841
+ return result;
842
+ }
843
+ //# sourceMappingURL=cdcl.js.map