@stevenvo780/st-lang 2.6.1 → 2.7.1
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 +18 -1
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/lexer/lexer.d.ts +2 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +4 -2
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/lexer/tokens.d.ts +17 -0
- package/dist/lexer/tokens.d.ts.map +1 -1
- package/dist/lexer/tokens.js +58 -21
- package/dist/lexer/tokens.js.map +1 -1
- package/dist/parser/parser.d.ts +4 -1
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +86 -2
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/classical/dpll.d.ts +10 -0
- package/dist/profiles/classical/dpll.d.ts.map +1 -0
- package/dist/profiles/classical/dpll.js +446 -0
- package/dist/profiles/classical/dpll.js.map +1 -0
- package/dist/profiles/classical/first-order.d.ts +1 -0
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +35 -0
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts +6 -1
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +434 -38
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
- package/dist/profiles/paraconsistent/belnap.js +151 -3
- 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 +49 -22
- package/dist/profiles/shared/tableau-engine.js.map +1 -1
- package/dist/runtime/educational-notes.d.ts +27 -0
- package/dist/runtime/educational-notes.d.ts.map +1 -0
- package/dist/runtime/educational-notes.js +100 -0
- package/dist/runtime/educational-notes.js.map +1 -0
- package/dist/runtime/format.d.ts.map +1 -1
- package/dist/runtime/format.js +4 -0
- package/dist/runtime/format.js.map +1 -1
- package/dist/runtime/formula-factory.d.ts +26 -0
- package/dist/runtime/formula-factory.d.ts.map +1 -0
- package/dist/runtime/formula-factory.js +67 -0
- package/dist/runtime/formula-factory.js.map +1 -0
- package/dist/runtime/interpreter.d.ts +41 -0
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +1039 -246
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/arithmetic.test.js +2 -1
- package/dist/tests/arithmetic.test.js.map +1 -1
- package/dist/tests/examples.test.js +12 -1
- package/dist/tests/examples.test.js.map +1 -1
- package/dist/tests/exhaustive-matrix.test.js +1 -1
- package/dist/tests/exhaustive-matrix.test.js.map +1 -1
- package/dist/tests/limits.test.js +17 -4
- package/dist/tests/limits.test.js.map +1 -1
- package/dist/tests/result-bindings.test.d.ts +2 -0
- package/dist/tests/result-bindings.test.d.ts.map +1 -0
- package/dist/tests/result-bindings.test.js +59 -0
- package/dist/tests/result-bindings.test.js.map +1 -0
- package/dist/tests/stress-hardware.test.d.ts +2 -0
- package/dist/tests/stress-hardware.test.d.ts.map +1 -0
- package/dist/tests/stress-hardware.test.js +673 -0
- package/dist/tests/stress-hardware.test.js.map +1 -0
- package/dist/tests/v1-features.test.js +1 -1
- package/dist/tests/v1-features.test.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/memo.d.ts +3 -0
- package/dist/utils/memo.d.ts.map +1 -1
- package/dist/utils/memo.js +30 -0
- package/dist/utils/memo.js.map +1 -1
- package/package.json +2 -1
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ClassicalPropositional = void 0;
|
|
7
7
|
exports.collectAtoms = collectAtoms;
|
|
8
|
+
exports.evaluateClassical = evaluateClassical;
|
|
9
|
+
exports.generateValuationsLazy = generateValuationsLazy;
|
|
8
10
|
exports.formulaToString = formulaToString;
|
|
9
11
|
exports.toNNF = toNNF;
|
|
10
12
|
exports.toCNF = toCNF;
|
|
@@ -12,7 +14,9 @@ exports.toDNF = toDNF;
|
|
|
12
14
|
exports.extractClauses = extractClauses;
|
|
13
15
|
const formula_classifier_1 = require("../../runtime/formula-classifier");
|
|
14
16
|
const format_1 = require("../../runtime/format");
|
|
17
|
+
const educational_notes_1 = require("../../runtime/educational-notes");
|
|
15
18
|
const memo_1 = require("../../utils/memo");
|
|
19
|
+
const dpll_1 = require("./dpll");
|
|
16
20
|
// --- Utilidades de fórmulas ---
|
|
17
21
|
function collectAtoms(f) {
|
|
18
22
|
return (0, memo_1.memoizeAtoms)(f, computeCollectAtoms);
|
|
@@ -32,39 +36,39 @@ function computeCollectAtoms(f) {
|
|
|
32
36
|
walk(f);
|
|
33
37
|
return atoms;
|
|
34
38
|
}
|
|
35
|
-
function
|
|
39
|
+
function evaluateClassical(f, v) {
|
|
36
40
|
switch (f.kind) {
|
|
37
41
|
case 'atom':
|
|
38
42
|
return f.name ? (v[f.name] ?? false) : false;
|
|
39
43
|
case 'not':
|
|
40
|
-
return f.args && f.args[0] ? !
|
|
44
|
+
return f.args && f.args[0] ? !evaluateClassical(f.args[0], v) : false;
|
|
41
45
|
case 'and':
|
|
42
46
|
return f.args && f.args[0] && f.args[1]
|
|
43
|
-
?
|
|
47
|
+
? evaluateClassical(f.args[0], v) && evaluateClassical(f.args[1], v)
|
|
44
48
|
: false;
|
|
45
49
|
case 'or':
|
|
46
50
|
return f.args && f.args[0] && f.args[1]
|
|
47
|
-
?
|
|
51
|
+
? evaluateClassical(f.args[0], v) || evaluateClassical(f.args[1], v)
|
|
48
52
|
: false;
|
|
49
53
|
case 'implies':
|
|
50
54
|
return f.args && f.args[0] && f.args[1]
|
|
51
|
-
? !
|
|
55
|
+
? !evaluateClassical(f.args[0], v) || evaluateClassical(f.args[1], v)
|
|
52
56
|
: false;
|
|
53
57
|
case 'biconditional':
|
|
54
58
|
return f.args && f.args[0] && f.args[1]
|
|
55
|
-
?
|
|
59
|
+
? evaluateClassical(f.args[0], v) === evaluateClassical(f.args[1], v)
|
|
56
60
|
: false;
|
|
57
61
|
case 'nand':
|
|
58
62
|
return f.args && f.args[0] && f.args[1]
|
|
59
|
-
? !(
|
|
63
|
+
? !(evaluateClassical(f.args[0], v) && evaluateClassical(f.args[1], v))
|
|
60
64
|
: false;
|
|
61
65
|
case 'nor':
|
|
62
66
|
return f.args && f.args[0] && f.args[1]
|
|
63
|
-
? !(
|
|
67
|
+
? !(evaluateClassical(f.args[0], v) || evaluateClassical(f.args[1], v))
|
|
64
68
|
: false;
|
|
65
69
|
case 'xor':
|
|
66
70
|
return f.args && f.args[0] && f.args[1]
|
|
67
|
-
?
|
|
71
|
+
? evaluateClassical(f.args[0], v) !== evaluateClassical(f.args[1], v)
|
|
68
72
|
: false;
|
|
69
73
|
default:
|
|
70
74
|
throw new Error(`Operador lógico no soportado en evaluación clásica: ${f.kind}`);
|
|
@@ -79,8 +83,8 @@ function generateValuations(atoms) {
|
|
|
79
83
|
const n = atoms.length;
|
|
80
84
|
if (n === 0)
|
|
81
85
|
return [{}];
|
|
82
|
-
if (n >
|
|
83
|
-
throw new Error('Demasiadas variables para tabla de verdad (>
|
|
86
|
+
if (n > 23)
|
|
87
|
+
throw new Error('Demasiadas variables para tabla de verdad (>23)');
|
|
84
88
|
const total = 1 << n;
|
|
85
89
|
const valuations = new Array(total);
|
|
86
90
|
for (let i = 0; i < total; i++) {
|
|
@@ -93,6 +97,184 @@ function generateValuations(atoms) {
|
|
|
93
97
|
}
|
|
94
98
|
return valuations;
|
|
95
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Generador lazy de valuaciones para streaming (usado por el intérprete para truth_table masivas).
|
|
102
|
+
*/
|
|
103
|
+
function* generateValuationsLazy(atoms) {
|
|
104
|
+
const n = atoms.length;
|
|
105
|
+
if (n === 0) {
|
|
106
|
+
yield {};
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const total = 1 << n;
|
|
110
|
+
for (let i = 0; i < total; i++) {
|
|
111
|
+
const v = {};
|
|
112
|
+
for (let j = 0; j < n; j++) {
|
|
113
|
+
v[atoms[j]] = Boolean((i >> (n - 1 - j)) & 1);
|
|
114
|
+
}
|
|
115
|
+
yield v;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function bvCreate(total) {
|
|
119
|
+
return new Uint32Array((total + 31) >>> 5);
|
|
120
|
+
}
|
|
121
|
+
function bvOnes(total) {
|
|
122
|
+
const words = (total + 31) >>> 5;
|
|
123
|
+
const v = new Uint32Array(words);
|
|
124
|
+
v.fill(0xffffffff);
|
|
125
|
+
// Clear trailing bits in last word
|
|
126
|
+
const tail = total & 31;
|
|
127
|
+
if (tail)
|
|
128
|
+
v[words - 1] = (1 << tail) - 1;
|
|
129
|
+
return v;
|
|
130
|
+
}
|
|
131
|
+
function bvAnd(a, b) {
|
|
132
|
+
const r = new Uint32Array(a.length);
|
|
133
|
+
for (let i = 0; i < a.length; i++)
|
|
134
|
+
r[i] = a[i] & b[i];
|
|
135
|
+
return r;
|
|
136
|
+
}
|
|
137
|
+
function bvOr(a, b) {
|
|
138
|
+
const r = new Uint32Array(a.length);
|
|
139
|
+
for (let i = 0; i < a.length; i++)
|
|
140
|
+
r[i] = a[i] | b[i];
|
|
141
|
+
return r;
|
|
142
|
+
}
|
|
143
|
+
function bvXor(a, b) {
|
|
144
|
+
const r = new Uint32Array(a.length);
|
|
145
|
+
for (let i = 0; i < a.length; i++)
|
|
146
|
+
r[i] = a[i] ^ b[i];
|
|
147
|
+
return r;
|
|
148
|
+
}
|
|
149
|
+
function bvNot(a, ones) {
|
|
150
|
+
const r = new Uint32Array(a.length);
|
|
151
|
+
for (let i = 0; i < a.length; i++)
|
|
152
|
+
r[i] = ~a[i] & ones[i];
|
|
153
|
+
return r;
|
|
154
|
+
}
|
|
155
|
+
function bvIsZero(a) {
|
|
156
|
+
for (let i = 0; i < a.length; i++)
|
|
157
|
+
if (a[i] !== 0)
|
|
158
|
+
return false;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
function bvEquals(a, b) {
|
|
162
|
+
for (let i = 0; i < a.length; i++)
|
|
163
|
+
if (a[i] !== b[i])
|
|
164
|
+
return false;
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
function bvPopcount(a) {
|
|
168
|
+
let count = 0;
|
|
169
|
+
for (let i = 0; i < a.length; i++) {
|
|
170
|
+
let v = a[i];
|
|
171
|
+
v = v - ((v >>> 1) & 0x55555555);
|
|
172
|
+
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
|
|
173
|
+
count += (((v + (v >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
|
174
|
+
}
|
|
175
|
+
return count;
|
|
176
|
+
}
|
|
177
|
+
function bvTestBit(a, i) {
|
|
178
|
+
return (a[i >>> 5] & (1 << (i & 31))) !== 0;
|
|
179
|
+
}
|
|
180
|
+
// Find first set bit, or -1
|
|
181
|
+
function bvFirstSet(a) {
|
|
182
|
+
for (let w = 0; w < a.length; w++) {
|
|
183
|
+
if (a[w] !== 0)
|
|
184
|
+
return ((w << 5) + Math.clz32(a[w] & (-a[w] | 0))) ^ 31;
|
|
185
|
+
}
|
|
186
|
+
return -1;
|
|
187
|
+
}
|
|
188
|
+
function evaluateBitset(formula, atoms) {
|
|
189
|
+
const n = atoms.length;
|
|
190
|
+
if (n > 26)
|
|
191
|
+
throw new Error('Demasiadas variables para evaluación bitset (>26)');
|
|
192
|
+
const total = 1 << n;
|
|
193
|
+
const allOnes = bvOnes(total);
|
|
194
|
+
const words = allOnes.length;
|
|
195
|
+
// Build atom masks: atom j is true when bit j of the row index is 1.
|
|
196
|
+
// Row index i has bit j set when (i >>> (n-1-j)) & 1.
|
|
197
|
+
// Equivalent: word w, bit b (i = w*32+b), atom j true iff ((w*32+b) >>> (n-1-j)) & 1.
|
|
198
|
+
const atomMasks = new Map();
|
|
199
|
+
for (let j = 0; j < n; j++) {
|
|
200
|
+
const shift = n - 1 - j;
|
|
201
|
+
const mask = bvCreate(total);
|
|
202
|
+
// The pattern for atom j repeats with period 2^(shift+1).
|
|
203
|
+
// Within each period, the first 2^shift bits are 0, next 2^shift are 1.
|
|
204
|
+
// For shift < 5, the pattern fits within single words and we can use word-level fill.
|
|
205
|
+
if (shift < 5) {
|
|
206
|
+
// Pattern period in bits
|
|
207
|
+
const period = 1 << (shift + 1);
|
|
208
|
+
const halfPeriod = 1 << shift;
|
|
209
|
+
// Build a 32-bit pattern
|
|
210
|
+
let pattern = 0;
|
|
211
|
+
for (let b = 0; b < 32; b++) {
|
|
212
|
+
if (b % period >= halfPeriod)
|
|
213
|
+
pattern |= 1 << b;
|
|
214
|
+
}
|
|
215
|
+
mask.fill(pattern);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// shift >= 5: consecutive words are all-0 or all-1
|
|
219
|
+
const wordPeriod = 1 << (shift - 5 + 1); // period in words
|
|
220
|
+
const halfWordPeriod = wordPeriod >>> 1;
|
|
221
|
+
for (let w = 0; w < words; w++) {
|
|
222
|
+
const posInPeriod = w % wordPeriod;
|
|
223
|
+
mask[w] = posInPeriod >= halfWordPeriod ? 0xffffffff : 0;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Clear trailing bits
|
|
227
|
+
const tail = total & 31;
|
|
228
|
+
if (tail && words > 0)
|
|
229
|
+
mask[words - 1] &= (1 << tail) - 1;
|
|
230
|
+
atomMasks.set(atoms[j], mask);
|
|
231
|
+
}
|
|
232
|
+
function evalBits(f) {
|
|
233
|
+
switch (f.kind) {
|
|
234
|
+
case 'atom':
|
|
235
|
+
return atomMasks.get(f.name) ?? bvCreate(total);
|
|
236
|
+
case 'not':
|
|
237
|
+
return bvNot(evalBits(f.args[0]), allOnes);
|
|
238
|
+
case 'and':
|
|
239
|
+
return bvAnd(evalBits(f.args[0]), evalBits(f.args[1]));
|
|
240
|
+
case 'or':
|
|
241
|
+
return bvOr(evalBits(f.args[0]), evalBits(f.args[1]));
|
|
242
|
+
case 'implies':
|
|
243
|
+
return bvOr(bvNot(evalBits(f.args[0]), allOnes), evalBits(f.args[1]));
|
|
244
|
+
case 'biconditional':
|
|
245
|
+
return bvNot(bvXor(evalBits(f.args[0]), evalBits(f.args[1])), allOnes);
|
|
246
|
+
case 'xor':
|
|
247
|
+
return bvXor(evalBits(f.args[0]), evalBits(f.args[1]));
|
|
248
|
+
case 'nand':
|
|
249
|
+
return bvNot(bvAnd(evalBits(f.args[0]), evalBits(f.args[1])), allOnes);
|
|
250
|
+
case 'nor':
|
|
251
|
+
return bvNot(bvOr(evalBits(f.args[0]), evalBits(f.args[1])), allOnes);
|
|
252
|
+
default:
|
|
253
|
+
throw new Error(`Operador no soportado en evaluación bitset: ${f.kind}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return { result: evalBits(formula), atomMasks, total, allOnes };
|
|
257
|
+
}
|
|
258
|
+
function bitsetPopcount(a) {
|
|
259
|
+
return bvPopcount(a);
|
|
260
|
+
}
|
|
261
|
+
function isPurePropositional(f) {
|
|
262
|
+
switch (f.kind) {
|
|
263
|
+
case 'atom':
|
|
264
|
+
return true;
|
|
265
|
+
case 'not':
|
|
266
|
+
case 'and':
|
|
267
|
+
case 'or':
|
|
268
|
+
case 'implies':
|
|
269
|
+
case 'biconditional':
|
|
270
|
+
case 'xor':
|
|
271
|
+
case 'nand':
|
|
272
|
+
case 'nor':
|
|
273
|
+
return (f.args || []).every(isPurePropositional);
|
|
274
|
+
default:
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
96
278
|
/**
|
|
97
279
|
* Aplana recursivamente nodos binarios del mismo kind asociativo.
|
|
98
280
|
* Ej: or(or(P,Q), R) → [P, Q, R]
|
|
@@ -219,6 +401,9 @@ function computeFormulaToString(f) {
|
|
|
219
401
|
}
|
|
220
402
|
}
|
|
221
403
|
function toNNF(f) {
|
|
404
|
+
return (0, memo_1.memoizeNNF)(f, computeNNF);
|
|
405
|
+
}
|
|
406
|
+
function computeNNF(f) {
|
|
222
407
|
const simplify = (node, negated) => {
|
|
223
408
|
const k = node.kind;
|
|
224
409
|
const args = node.args || [];
|
|
@@ -351,7 +536,7 @@ function distributeOrOverAnd(f) {
|
|
|
351
536
|
return f;
|
|
352
537
|
}
|
|
353
538
|
function toCNF(f) {
|
|
354
|
-
return distributeOrOverAnd(toNNF(
|
|
539
|
+
return (0, memo_1.memoizeCNF)(f, (formula) => distributeOrOverAnd(toNNF(formula)));
|
|
355
540
|
}
|
|
356
541
|
function distributeAndOverOr(f) {
|
|
357
542
|
if (f.kind === 'and' && f.args?.[0] && f.args?.[1]) {
|
|
@@ -382,7 +567,7 @@ function distributeAndOverOr(f) {
|
|
|
382
567
|
return f;
|
|
383
568
|
}
|
|
384
569
|
function toDNF(f) {
|
|
385
|
-
return distributeAndOverOr(toNNF(
|
|
570
|
+
return (0, memo_1.memoizeDNF)(f, (formula) => distributeAndOverOr(toNNF(formula)));
|
|
386
571
|
}
|
|
387
572
|
/**
|
|
388
573
|
* Extracts clauses from a CNF formula for resolution analysis (#28)
|
|
@@ -943,23 +1128,67 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
943
1128
|
for (const f of allAxiomFormulas)
|
|
944
1129
|
collectAtoms(f).forEach((a) => atoms.add(a));
|
|
945
1130
|
collectAtoms(goal).forEach((a) => atoms.add(a));
|
|
946
|
-
const atomList = Array.from(atoms);
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1131
|
+
const atomList = Array.from(atoms).sort();
|
|
1132
|
+
// Fast path: bitset semantic check
|
|
1133
|
+
const allPure = allAxiomFormulas.every(isPurePropositional) && isPurePropositional(goal);
|
|
1134
|
+
if (allPure && atomList.length <= 26) {
|
|
1135
|
+
const premiseBits = allAxiomFormulas.map((f) => evaluateBitset(f, atomList).result);
|
|
1136
|
+
const goalBits = evaluateBitset(goal, atomList).result;
|
|
1137
|
+
const allOnes = bvOnes(1 << atomList.length);
|
|
1138
|
+
// Conjunction of all premises
|
|
1139
|
+
let premisesConj = allOnes;
|
|
1140
|
+
for (const pb of premiseBits)
|
|
1141
|
+
premisesConj = bvAnd(premisesConj, pb);
|
|
1142
|
+
// Valid if: wherever premises are true, goal is also true
|
|
1143
|
+
// i.e., premisesConj & ~goalBits === 0
|
|
1144
|
+
if (bvIsZero(bvAnd(premisesConj, bvNot(goalBits, allOnes)))) {
|
|
1145
|
+
return {
|
|
1146
|
+
goal,
|
|
1147
|
+
steps: state.steps,
|
|
1148
|
+
status: 'complete',
|
|
1149
|
+
derivedFrom: premiseNames,
|
|
1150
|
+
};
|
|
954
1151
|
}
|
|
955
1152
|
}
|
|
956
|
-
if (
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1153
|
+
else if (allPure && atomList.length > 26) {
|
|
1154
|
+
// DPLL fallback for >26 atoms
|
|
1155
|
+
// Build: (premise1 & premise2 & ... & premiseN) -> goal
|
|
1156
|
+
// Valid iff NOT satisfiable: (premises & !goal)
|
|
1157
|
+
let conjunction = allAxiomFormulas[0];
|
|
1158
|
+
for (let i = 1; i < allAxiomFormulas.length; i++) {
|
|
1159
|
+
conjunction = { kind: 'and', args: [conjunction, allAxiomFormulas[i]] };
|
|
1160
|
+
}
|
|
1161
|
+
const negGoal = { kind: 'not', args: [goal] };
|
|
1162
|
+
const check = { kind: 'and', args: [conjunction, negGoal] };
|
|
1163
|
+
const result = (0, dpll_1.dpll)(check);
|
|
1164
|
+
if (!result.satisfiable) {
|
|
1165
|
+
return {
|
|
1166
|
+
goal,
|
|
1167
|
+
steps: state.steps,
|
|
1168
|
+
status: 'complete',
|
|
1169
|
+
derivedFrom: premiseNames,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
// Classic fallback
|
|
1175
|
+
const valuations = generateValuations(atomList);
|
|
1176
|
+
let semanticallyValid = true;
|
|
1177
|
+
for (const v of valuations) {
|
|
1178
|
+
const premisesTrue = allAxiomFormulas.every((f) => evaluateClassical(f, v));
|
|
1179
|
+
if (premisesTrue && !evaluateClassical(goal, v)) {
|
|
1180
|
+
semanticallyValid = false;
|
|
1181
|
+
break;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
if (semanticallyValid) {
|
|
1185
|
+
return {
|
|
1186
|
+
goal,
|
|
1187
|
+
steps: state.steps,
|
|
1188
|
+
status: 'complete',
|
|
1189
|
+
derivedFrom: premiseNames,
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
963
1192
|
}
|
|
964
1193
|
}
|
|
965
1194
|
return null;
|
|
@@ -1054,24 +1283,56 @@ class ClassicalPropositional {
|
|
|
1054
1283
|
if (wf.length > 0) {
|
|
1055
1284
|
return { status: 'error', diagnostics: wf, formula };
|
|
1056
1285
|
}
|
|
1286
|
+
// Fast path: bitset validity check (no row materialization)
|
|
1287
|
+
const atoms = Array.from(collectAtoms(formula)).sort();
|
|
1288
|
+
if (isPurePropositional(formula) && atoms.length <= 26) {
|
|
1289
|
+
const { result, allOnes } = evaluateBitset(formula, atoms);
|
|
1290
|
+
const isValid = bvEquals(result, allOnes);
|
|
1291
|
+
return {
|
|
1292
|
+
status: isValid ? 'valid' : 'invalid',
|
|
1293
|
+
output: isValid
|
|
1294
|
+
? `${formulaToString(formula)} es VALIDA (tautologia)`
|
|
1295
|
+
: `${formulaToString(formula)} NO es valida`,
|
|
1296
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'valid', valid: isValid }),
|
|
1297
|
+
diagnostics: [],
|
|
1298
|
+
formula,
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
// DPLL path: for formulas with >26 atoms, use SAT solver
|
|
1302
|
+
if (isPurePropositional(formula) && atoms.length > 26) {
|
|
1303
|
+
const negated = { kind: 'not', args: [formula] };
|
|
1304
|
+
const result = (0, dpll_1.dpll)(negated);
|
|
1305
|
+
const isValid = !result.satisfiable;
|
|
1306
|
+
return {
|
|
1307
|
+
status: isValid ? 'valid' : 'invalid',
|
|
1308
|
+
output: isValid
|
|
1309
|
+
? `${formulaToString(formula)} es VALIDA (tautologia)`
|
|
1310
|
+
: `${formulaToString(formula)} NO es valida`,
|
|
1311
|
+
model: !isValid && result.model ? { type: 'propositional', valuation: result.model } : undefined,
|
|
1312
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'valid', valid: isValid }),
|
|
1313
|
+
diagnostics: [],
|
|
1314
|
+
formula,
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1057
1317
|
const tt = this.truthTable(formula);
|
|
1058
1318
|
if (tt.isTautology) {
|
|
1059
1319
|
return {
|
|
1060
1320
|
status: 'valid',
|
|
1061
1321
|
output: `${formulaToString(formula)} es VALIDA (tautologia)`,
|
|
1062
1322
|
truthTable: tt,
|
|
1323
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'valid', valid: true }),
|
|
1063
1324
|
diagnostics: [],
|
|
1064
1325
|
formula,
|
|
1065
1326
|
};
|
|
1066
1327
|
}
|
|
1067
1328
|
else {
|
|
1068
|
-
// Encontrar contramodelo
|
|
1069
1329
|
const cm = tt.rows.find((r) => !r.result);
|
|
1070
1330
|
return {
|
|
1071
1331
|
status: 'invalid',
|
|
1072
1332
|
output: `${formulaToString(formula)} NO es valida`,
|
|
1073
1333
|
truthTable: tt,
|
|
1074
1334
|
model: cm ? { type: 'propositional', valuation: cm.valuation } : undefined,
|
|
1335
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'valid', valid: false }),
|
|
1075
1336
|
diagnostics: [],
|
|
1076
1337
|
formula,
|
|
1077
1338
|
};
|
|
@@ -1082,6 +1343,37 @@ class ClassicalPropositional {
|
|
|
1082
1343
|
if (wf.length > 0) {
|
|
1083
1344
|
return { status: 'error', diagnostics: wf, formula };
|
|
1084
1345
|
}
|
|
1346
|
+
// Fast path: bitset satisfiability check (no row materialization)
|
|
1347
|
+
const atoms = Array.from(collectAtoms(formula)).sort();
|
|
1348
|
+
if (isPurePropositional(formula) && atoms.length <= 26) {
|
|
1349
|
+
const { result } = evaluateBitset(formula, atoms);
|
|
1350
|
+
const isSat = !bvIsZero(result);
|
|
1351
|
+
return {
|
|
1352
|
+
status: isSat ? 'satisfiable' : 'unsatisfiable',
|
|
1353
|
+
output: isSat
|
|
1354
|
+
? `${formulaToString(formula)} es SATISFACIBLE`
|
|
1355
|
+
: `${formulaToString(formula)} es INSATISFACIBLE (contradiccion)`,
|
|
1356
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'satisfiable', sat: isSat }),
|
|
1357
|
+
diagnostics: [],
|
|
1358
|
+
formula,
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
// DPLL path: for formulas with >26 atoms, use SAT solver
|
|
1362
|
+
if (isPurePropositional(formula) && atoms.length > 26) {
|
|
1363
|
+
const result = (0, dpll_1.dpll)(formula);
|
|
1364
|
+
return {
|
|
1365
|
+
status: result.satisfiable ? 'satisfiable' : 'unsatisfiable',
|
|
1366
|
+
output: result.satisfiable
|
|
1367
|
+
? `${formulaToString(formula)} es SATISFACIBLE`
|
|
1368
|
+
: `${formulaToString(formula)} es INSATISFACIBLE (contradiccion)`,
|
|
1369
|
+
model: result.satisfiable && result.model
|
|
1370
|
+
? { type: 'propositional', valuation: result.model }
|
|
1371
|
+
: undefined,
|
|
1372
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'satisfiable', sat: result.satisfiable }),
|
|
1373
|
+
diagnostics: [],
|
|
1374
|
+
formula,
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1085
1377
|
const tt = this.truthTable(formula);
|
|
1086
1378
|
if (tt.isSatisfiable) {
|
|
1087
1379
|
const sat = tt.rows.find((r) => r.result);
|
|
@@ -1090,6 +1382,7 @@ class ClassicalPropositional {
|
|
|
1090
1382
|
output: `${formulaToString(formula)} es SATISFACIBLE`,
|
|
1091
1383
|
model: sat ? { type: 'propositional', valuation: sat.valuation } : undefined,
|
|
1092
1384
|
truthTable: tt,
|
|
1385
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'satisfiable', sat: true }),
|
|
1093
1386
|
diagnostics: [],
|
|
1094
1387
|
formula,
|
|
1095
1388
|
};
|
|
@@ -1099,6 +1392,7 @@ class ClassicalPropositional {
|
|
|
1099
1392
|
status: 'unsatisfiable',
|
|
1100
1393
|
output: `${formulaToString(formula)} es INSATISFACIBLE (contradiccion)`,
|
|
1101
1394
|
truthTable: tt,
|
|
1395
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'satisfiable', sat: false }),
|
|
1102
1396
|
diagnostics: [],
|
|
1103
1397
|
formula,
|
|
1104
1398
|
};
|
|
@@ -1116,6 +1410,7 @@ class ClassicalPropositional {
|
|
|
1116
1410
|
status: 'provable',
|
|
1117
1411
|
output: `${formulaToString(goal)} es DEMOSTRABLE desde la teoria`,
|
|
1118
1412
|
proof,
|
|
1413
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'prove', ok: true }),
|
|
1119
1414
|
diagnostics: [],
|
|
1120
1415
|
formula: goal,
|
|
1121
1416
|
};
|
|
@@ -1123,6 +1418,7 @@ class ClassicalPropositional {
|
|
|
1123
1418
|
return {
|
|
1124
1419
|
status: 'refutable',
|
|
1125
1420
|
output: `${formulaToString(goal)} NO es demostrable desde la teoria dada`,
|
|
1421
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'prove', ok: false }),
|
|
1126
1422
|
diagnostics: [],
|
|
1127
1423
|
formula: goal,
|
|
1128
1424
|
};
|
|
@@ -1154,7 +1450,12 @@ class ClassicalPropositional {
|
|
|
1154
1450
|
: rulesUsed.has('Silogismo Hipotetico')
|
|
1155
1451
|
? 'φ → ψ, ψ → χ ⊢ φ → χ'
|
|
1156
1452
|
: undefined,
|
|
1157
|
-
educationalNote:
|
|
1453
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({
|
|
1454
|
+
op: 'derive',
|
|
1455
|
+
ok: true,
|
|
1456
|
+
steps: proof.steps.length,
|
|
1457
|
+
rules: Array.from(rulesUsed),
|
|
1458
|
+
}),
|
|
1158
1459
|
diagnostics: [],
|
|
1159
1460
|
formula: goal,
|
|
1160
1461
|
};
|
|
@@ -1162,6 +1463,7 @@ class ClassicalPropositional {
|
|
|
1162
1463
|
return {
|
|
1163
1464
|
status: 'refutable',
|
|
1164
1465
|
output: `No se puede derivar ${formulaToString(goal)} desde las premisas dadas`,
|
|
1466
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'derive', ok: false }),
|
|
1165
1467
|
diagnostics: [],
|
|
1166
1468
|
formula: goal,
|
|
1167
1469
|
};
|
|
@@ -1171,16 +1473,70 @@ class ClassicalPropositional {
|
|
|
1171
1473
|
if (wf.length > 0) {
|
|
1172
1474
|
return { status: 'error', diagnostics: wf, formula };
|
|
1173
1475
|
}
|
|
1174
|
-
const atoms = Array.from(collectAtoms(formula));
|
|
1476
|
+
const atoms = Array.from(collectAtoms(formula)).sort();
|
|
1477
|
+
const n = atoms.length;
|
|
1478
|
+
// Fast path: bitset finds countermodel in one pass
|
|
1479
|
+
if (isPurePropositional(formula) && n <= 26) {
|
|
1480
|
+
const { result, allOnes } = evaluateBitset(formula, atoms);
|
|
1481
|
+
if (bvEquals(result, allOnes)) {
|
|
1482
|
+
return {
|
|
1483
|
+
status: 'valid',
|
|
1484
|
+
output: `${formulaToString(formula)} es tautologia, no hay contramodelo`,
|
|
1485
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'countermodel', found: false }),
|
|
1486
|
+
diagnostics: [],
|
|
1487
|
+
formula,
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
// Find first 0 bit (first falsifying row)
|
|
1491
|
+
const inverted = bvNot(result, allOnes);
|
|
1492
|
+
const idx = bvFirstSet(inverted);
|
|
1493
|
+
const v = {};
|
|
1494
|
+
for (let j = 0; j < n; j++) {
|
|
1495
|
+
v[atoms[j]] = Boolean((idx >> (n - 1 - j)) & 1);
|
|
1496
|
+
}
|
|
1497
|
+
const valStr = atoms.map((a) => `${a}=${v[a] ? 'V' : 'F'}`).join(', ');
|
|
1498
|
+
return {
|
|
1499
|
+
status: 'invalid',
|
|
1500
|
+
output: `Contramodelo encontrado para ${formulaToString(formula)}\n ← ${valStr}`,
|
|
1501
|
+
model: { type: 'propositional', valuation: v },
|
|
1502
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'countermodel', found: true }),
|
|
1503
|
+
diagnostics: [],
|
|
1504
|
+
formula,
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
// DPLL path: for formulas with >26 atoms, use SAT solver to find countermodel
|
|
1508
|
+
if (isPurePropositional(formula) && n > 26) {
|
|
1509
|
+
const negated = { kind: 'not', args: [formula] };
|
|
1510
|
+
const result = (0, dpll_1.dpll)(negated);
|
|
1511
|
+
if (result.satisfiable && result.model) {
|
|
1512
|
+
const valStr = atoms.map((a) => `${a}=${result.model[a] ? 'V' : 'F'}`).join(', ');
|
|
1513
|
+
return {
|
|
1514
|
+
status: 'invalid',
|
|
1515
|
+
output: `Contramodelo encontrado para ${formulaToString(formula)}\n ← ${valStr}`,
|
|
1516
|
+
model: { type: 'propositional', valuation: result.model },
|
|
1517
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'countermodel', found: true }),
|
|
1518
|
+
diagnostics: [],
|
|
1519
|
+
formula,
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
return {
|
|
1523
|
+
status: 'valid',
|
|
1524
|
+
output: `${formulaToString(formula)} es tautologia, no hay contramodelo`,
|
|
1525
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'countermodel', found: false }),
|
|
1526
|
+
diagnostics: [],
|
|
1527
|
+
formula,
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
// Fallback: classic evaluation
|
|
1175
1531
|
const valuations = generateValuations(atoms);
|
|
1176
1532
|
for (const v of valuations) {
|
|
1177
|
-
if (!
|
|
1178
|
-
// #25: mark the countermodel valuation with ←
|
|
1533
|
+
if (!evaluateClassical(formula, v)) {
|
|
1179
1534
|
const valStr = atoms.map((a) => `${a}=${v[a] ? 'V' : 'F'}`).join(', ');
|
|
1180
1535
|
return {
|
|
1181
1536
|
status: 'invalid',
|
|
1182
1537
|
output: `Contramodelo encontrado para ${formulaToString(formula)}\n ← ${valStr}`,
|
|
1183
1538
|
model: { type: 'propositional', valuation: v },
|
|
1539
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'countermodel', found: true }),
|
|
1184
1540
|
diagnostics: [],
|
|
1185
1541
|
formula,
|
|
1186
1542
|
};
|
|
@@ -1189,6 +1545,7 @@ class ClassicalPropositional {
|
|
|
1189
1545
|
return {
|
|
1190
1546
|
status: 'valid',
|
|
1191
1547
|
output: `${formulaToString(formula)} es tautologia, no hay contramodelo`,
|
|
1548
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'countermodel', found: false }),
|
|
1192
1549
|
diagnostics: [],
|
|
1193
1550
|
formula,
|
|
1194
1551
|
};
|
|
@@ -1296,17 +1653,54 @@ class ClassicalPropositional {
|
|
|
1296
1653
|
}
|
|
1297
1654
|
truthTable(formula) {
|
|
1298
1655
|
const atoms = Array.from(collectAtoms(formula)).sort();
|
|
1299
|
-
const
|
|
1656
|
+
const n = atoms.length;
|
|
1300
1657
|
const subForms = getSubFormulas(formula);
|
|
1658
|
+
const subFormulasInfo = subForms.map((sf) => ({ formula: sf, label: formulaToString(sf) }));
|
|
1659
|
+
// Fast path: bitset evaluation for pure propositional formulas
|
|
1660
|
+
// Limit to n<=20 for row materialization (2^20 = ~1M rows)
|
|
1661
|
+
if (isPurePropositional(formula) && n <= 20) {
|
|
1662
|
+
const { result, atomMasks, total, allOnes } = evaluateBitset(formula, atoms);
|
|
1663
|
+
const satisfyingCount = bitsetPopcount(result);
|
|
1664
|
+
// Evaluate subformulas with bitsets too
|
|
1665
|
+
const subBitsets = subForms.map((sf) => isPurePropositional(sf) ? evaluateBitset(sf, atoms).result : bvCreate(total));
|
|
1666
|
+
// Materialize rows from bitset results
|
|
1667
|
+
const rows = new Array(total);
|
|
1668
|
+
for (let i = 0; i < total; i++) {
|
|
1669
|
+
const v = {};
|
|
1670
|
+
for (let j = 0; j < n; j++) {
|
|
1671
|
+
v[atoms[j]] = bvTestBit(atomMasks.get(atoms[j]), i);
|
|
1672
|
+
}
|
|
1673
|
+
rows[i] = { valuation: v, result: bvTestBit(result, i) };
|
|
1674
|
+
}
|
|
1675
|
+
const subFormulaValues = rows.map((_, i) => {
|
|
1676
|
+
const vals = {};
|
|
1677
|
+
subForms.forEach((sf, si) => {
|
|
1678
|
+
vals[formulaToString(sf)] = bvTestBit(subBitsets[si], i);
|
|
1679
|
+
});
|
|
1680
|
+
return vals;
|
|
1681
|
+
});
|
|
1682
|
+
return {
|
|
1683
|
+
variables: atoms,
|
|
1684
|
+
rows,
|
|
1685
|
+
isTautology: bvEquals(result, allOnes),
|
|
1686
|
+
isContradiction: bvIsZero(result),
|
|
1687
|
+
isSatisfiable: !bvIsZero(result),
|
|
1688
|
+
subFormulas: subFormulasInfo,
|
|
1689
|
+
subFormulaValues,
|
|
1690
|
+
satisfyingCount,
|
|
1691
|
+
totalCount: total,
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
// Fallback: classic evaluation
|
|
1695
|
+
const valuations = generateValuations(atoms);
|
|
1301
1696
|
const rows = valuations.map((v) => ({
|
|
1302
1697
|
valuation: v,
|
|
1303
|
-
result:
|
|
1698
|
+
result: evaluateClassical(formula, v),
|
|
1304
1699
|
}));
|
|
1305
|
-
const subFormulasInfo = subForms.map((sf) => ({ formula: sf, label: formulaToString(sf) }));
|
|
1306
1700
|
const subFormulaValues = valuations.map((v) => {
|
|
1307
1701
|
const vals = {};
|
|
1308
1702
|
subForms.forEach((sf) => {
|
|
1309
|
-
vals[formulaToString(sf)] =
|
|
1703
|
+
vals[formulaToString(sf)] = evaluateClassical(sf, v);
|
|
1310
1704
|
});
|
|
1311
1705
|
return vals;
|
|
1312
1706
|
});
|
|
@@ -1317,7 +1711,7 @@ class ClassicalPropositional {
|
|
|
1317
1711
|
isContradiction: rows.every((r) => !r.result),
|
|
1318
1712
|
isSatisfiable: rows.some((r) => r.result),
|
|
1319
1713
|
subFormulas: subFormulasInfo,
|
|
1320
|
-
subFormulaValues
|
|
1714
|
+
subFormulaValues,
|
|
1321
1715
|
satisfyingCount: rows.filter((r) => r.result).length,
|
|
1322
1716
|
totalCount: rows.length,
|
|
1323
1717
|
};
|
|
@@ -1335,6 +1729,7 @@ class ClassicalPropositional {
|
|
|
1335
1729
|
status: 'valid',
|
|
1336
1730
|
output: `${formulaToString(a)} y ${formulaToString(b)} son EQUIVALENTES`,
|
|
1337
1731
|
truthTable: tt,
|
|
1732
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'equivalent', equiv: true }),
|
|
1338
1733
|
diagnostics: [],
|
|
1339
1734
|
};
|
|
1340
1735
|
}
|
|
@@ -1343,6 +1738,7 @@ class ClassicalPropositional {
|
|
|
1343
1738
|
status: 'invalid',
|
|
1344
1739
|
output: `${formulaToString(a)} y ${formulaToString(b)} NO son equivalentes`,
|
|
1345
1740
|
model: cm ? { type: 'propositional', valuation: cm.valuation } : undefined,
|
|
1741
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'equivalent', equiv: false }),
|
|
1346
1742
|
diagnostics: [],
|
|
1347
1743
|
};
|
|
1348
1744
|
}
|