@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.
- package/dist/ast/nodes.d.ts +35 -2
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +4 -0
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/lexer/tokens.d.ts +8 -0
- package/dist/lexer/tokens.d.ts.map +1 -1
- package/dist/lexer/tokens.js +23 -0
- package/dist/lexer/tokens.js.map +1 -1
- package/dist/parser/parser.d.ts +6 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +171 -6
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/classical/cdcl.d.ts +34 -0
- package/dist/profiles/classical/cdcl.d.ts.map +1 -0
- package/dist/profiles/classical/cdcl.js +843 -0
- package/dist/profiles/classical/cdcl.js.map +1 -0
- package/dist/profiles/classical/dpll.d.ts +11 -1
- package/dist/profiles/classical/dpll.d.ts.map +1 -1
- package/dist/profiles/classical/dpll.js +54 -17
- package/dist/profiles/classical/dpll.js.map +1 -1
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +20 -10
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/parallel-sat.d.ts +62 -0
- package/dist/profiles/classical/parallel-sat.d.ts.map +1 -0
- package/dist/profiles/classical/parallel-sat.js +630 -0
- package/dist/profiles/classical/parallel-sat.js.map +1 -0
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +35 -19
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/classical/sat-preprocess.d.ts +17 -0
- package/dist/profiles/classical/sat-preprocess.d.ts.map +1 -0
- package/dist/profiles/classical/sat-preprocess.js +372 -0
- package/dist/profiles/classical/sat-preprocess.js.map +1 -0
- package/dist/profiles/classical/undecidability-detector.d.ts +13 -0
- package/dist/profiles/classical/undecidability-detector.d.ts.map +1 -0
- package/dist/profiles/classical/undecidability-detector.js +277 -0
- package/dist/profiles/classical/undecidability-detector.js.map +1 -0
- package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
- package/dist/profiles/paraconsistent/belnap.js +4 -2
- package/dist/profiles/paraconsistent/belnap.js.map +1 -1
- package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
- package/dist/profiles/shared/tableau-engine.js +3 -1
- package/dist/profiles/shared/tableau-engine.js.map +1 -1
- package/dist/protocol/handler.d.ts.map +1 -1
- package/dist/protocol/handler.js +327 -88
- package/dist/protocol/handler.js.map +1 -1
- package/dist/runtime/formula-factory.d.ts.map +1 -1
- package/dist/runtime/formula-factory.js +3 -2
- package/dist/runtime/formula-factory.js.map +1 -1
- package/dist/runtime/interpreter.d.ts +33 -0
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +516 -5
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/benchmark-cdcl.test.d.ts +2 -0
- package/dist/tests/benchmark-cdcl.test.d.ts.map +1 -0
- package/dist/tests/benchmark-cdcl.test.js +172 -0
- package/dist/tests/benchmark-cdcl.test.js.map +1 -0
- package/dist/tests/limits.test.js +11 -4
- package/dist/tests/limits.test.js.map +1 -1
- package/dist/tests/parallel-sat.test.d.ts +2 -0
- package/dist/tests/parallel-sat.test.d.ts.map +1 -0
- package/dist/tests/parallel-sat.test.js +351 -0
- package/dist/tests/parallel-sat.test.js.map +1 -0
- package/dist/tests/stress-cdcl.test.d.ts +2 -0
- package/dist/tests/stress-cdcl.test.d.ts.map +1 -0
- package/dist/tests/stress-cdcl.test.js +792 -0
- package/dist/tests/stress-cdcl.test.js.map +1 -0
- package/dist/tests/stress-extreme.test.d.ts +2 -0
- package/dist/tests/stress-extreme.test.d.ts.map +1 -0
- package/dist/tests/stress-extreme.test.js +1005 -0
- package/dist/tests/stress-extreme.test.js.map +1 -0
- package/dist/tests/v3-features.test.d.ts +2 -0
- package/dist/tests/v3-features.test.d.ts.map +1 -0
- package/dist/tests/v3-features.test.js +529 -0
- package/dist/tests/v3-features.test.js.map +1 -0
- package/dist/tests/v3-stress.test.d.ts +2 -0
- package/dist/tests/v3-stress.test.d.ts.map +1 -0
- package/dist/tests/v3-stress.test.js +755 -0
- package/dist/tests/v3-stress.test.js.map +1 -0
- package/dist/text-layer/compiler.d.ts +4 -1
- package/dist/text-layer/compiler.d.ts.map +1 -1
- package/dist/text-layer/compiler.js +35 -0
- package/dist/text-layer/compiler.js.map +1 -1
- package/dist/types/index.d.ts +27 -1
- package/dist/types/index.d.ts.map +1 -1
- 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
|