1
1
import itertools
2
- from collections import defaultdict
2
+ from collections import OrderedDict
3
3
4
4
from lark import Tree , Token
5
5
from itertools import permutations
@@ -36,14 +36,19 @@ def safe_paren(node: object):
36
36
37
37
def idempotence (tree : Tree ):
38
38
assert tree .data == "term" or tree .data == "expr"
39
- unique_exprs = set ()
40
- new_children = []
41
- for i , c in enumerate (tree .children ):
42
- if c not in unique_exprs :
43
- unique_exprs .add (c )
44
- new_children .append (c )
45
- tree .children = new_children
46
- return tree
39
+ return Tree (tree .data , list ({k : None for k in tree .children }.keys ())) # dicts remember insertion order now!
40
+
41
+
42
+ def reverse_idempotence (node ):
43
+ if is_tree (node , "term" ) or is_tree (node , "expr" ):
44
+ new_trees = [
45
+ Tree (node .data , node .children [:i ] + [c ] + node .children [i :]) for i , c in enumerate (node .children )
46
+ ]
47
+ else :
48
+ new_trees = [
49
+ Tree ("expr" , [node , node ]), Tree ("term" , [node , node ])
50
+ ]
51
+ return new_trees
47
52
48
53
49
54
def simplify_multiple_negation (tree : Tree ):
@@ -101,16 +106,19 @@ def commutativity(tree: Tree): # p^q^r == q^p^r == ... r^p^q, returns list of p
101
106
def associativity_LR (tree : Tree ): # only for (a^b)^c or with V, i.e. 2-node 3-var ops. Somewhat Hacky. Expand later
102
107
assert tree .data == "expr" or tree .data == "term"
103
108
ch = tree .children
104
- if len (ch ) == 2 and type (ch [0 ]) == Tree and ch [0 ].data == "paren_expr" :
105
- par = ch [0 ].children [0 ]
106
- if is_tree (par , tree .data ) and len (par .children ) == 2 :
107
- tree .children = [
108
- par .children [0 ],
109
- Tree ("paren_expr" , [
110
- Tree (tree .data , [par .children [1 ], ch [1 ]])
111
- ])
112
- ]
113
- return tree
109
+ new_trees = []
110
+ for i , c in enumerate (ch ):
111
+ if is_tree (c , "paren_expr" ) and is_tree (c .children [0 ], tree .data ):
112
+ swap_ch = c .children [0 ].children
113
+ if i > 0 :
114
+ for j in range (len (swap_ch )- 1 ):
115
+ new_paren = parenthesize (Tree (tree .data , ch [:i ] + swap_ch [:j + 1 ]))
116
+ new_trees .append (Tree (tree .data , [new_paren ] + swap_ch [j + 1 :] + ch [i + 1 :]))
117
+ if i < len (ch )- 1 :
118
+ for j in range (1 , len (swap_ch )):
119
+ new_paren = parenthesize (Tree (tree .data , swap_ch [j :] + ch [i + 1 :]))
120
+ new_trees .append (Tree (tree .data , ch [:i ] + swap_ch [:j ] + [new_paren ]))
121
+ return new_trees
114
122
115
123
116
124
def associativity_expand (tree : Tree ): # remove all parenthesized expressions
@@ -125,6 +133,16 @@ def associativity_expand(tree: Tree): # remove all parenthesized expressions
125
133
return tree
126
134
127
135
136
+ def reverse_associativity_expand (tree : Tree ): # add parentheses around arbitrary sequences of expressions
137
+ assert tree .data == "expr" or tree .data == "term"
138
+ ch = tree .children
139
+ new_trees = []
140
+ for i in range (len (ch )- 1 ):
141
+ for j in range (i + 2 , len (ch )+ 1 ):
142
+ new_trees .append (Tree (tree .data , ch [:i ] + [parenthesize (Tree (tree .data , ch [i :j ]))] + ch [j :]))
143
+ return new_trees
144
+
145
+
128
146
def impl_to_disj (tree : Tree ): # p->q == ~pVq
129
147
assert tree .data == "dbl_expr"
130
148
if len (tree .children ) > 1 :
@@ -178,8 +196,9 @@ def impl_to_dblimpl(tree: Tree): # (p->q)^(q->p) == p<=>q, any adjacent pair ca
178
196
if ch0 == c2 .children [0 ].children [1 ] and ch1 == c2 .children [0 ].children [0 ]:
179
197
pre_exclude = tree .children [:i ]
180
198
post_exclude = tree .children [i + 2 :]
181
- tr = Tree ("eqn" , [ch0 , ch1 ])
182
- new_trees .append (Tree ("term" , pre_exclude + [tr ] + post_exclude ))
199
+ tr_fwd , tr_bkwd = Tree ("eqn" , [ch0 , ch1 ]), Tree ("eqn" , [ch1 , ch0 ])
200
+ new_trees .append (Tree ("term" , pre_exclude + [tr_fwd ] + post_exclude ))
201
+ new_trees .append (Tree ("term" , pre_exclude + [tr_bkwd ] + post_exclude ))
183
202
return new_trees if len (new_trees ) > 0 else [tree ]
184
203
185
204
@@ -195,6 +214,18 @@ def negation(tree: Tree): # pv~p=T, p^~p=F
195
214
return tree
196
215
197
216
217
+ def reverse_negation (token : Token , additional_ids = ('p' , 'q' , 'r' , 's' )):
218
+ assert is_token (token , "TRUE" ) or is_token (token , "FALSE" )
219
+ new_trees = []
220
+ for i in additional_ids :
221
+ tok = Token ("ID" , i )
222
+ if is_token (token , "TRUE" ):
223
+ new_trees .append (parenthesize (Tree ("expr" , [tok , negate (tok )])))
224
+ else :
225
+ new_trees .append (parenthesize (Tree ("term" , [tok , negate (tok )])))
226
+ return new_trees
227
+
228
+
198
229
def demorgan (tree : Tree ): # ~(pvq) == ~p^~q, ~(p^q) == ~pV~q
199
230
assert tree .data == "literal"
200
231
if is_tree (tree .children [1 ], "paren_expr" ):
@@ -233,19 +264,17 @@ def absorption(tree: Tree): # pV(p^q) == p, p^(pVq) == p
233
264
return tree
234
265
235
266
236
- def reverse_absorption (tree : Tree ): # pvq == pv(p^q), p^q == p^(pvq), consecutive pairs in order
237
- assert tree .data == "expr" or tree .data == "term"
238
- if len (tree .children ) == 1 :
239
- return [tree ]
240
- dual = "expr" if tree .data == "term" else "term"
267
+ def reverse_absorption (node , additional_ids = ('p' , 'q' , 'r' , 's' )): # p == pv(p^ID), p == p^(pvID)
241
268
new_trees = []
242
- for i , c in enumerate (tree .children [:- 1 ]):
243
- tr = parenthesize (Tree (dual , [c , tree .children [i + 1 ]]))
244
- new_trees .append (Tree (tree .data , tree .children [:i + 1 ] + [tr ] + tree .children [i + 2 :])) # :i+1 keeps p in pv(p^q)
269
+ if is_token (node , "ID" ):
270
+ additional_ids = [v for v in additional_ids if node .value != v ]
271
+ for v in additional_ids :
272
+ new_trees .append (Tree ("expr" , [node , parenthesize (Tree ("term" , [node , v ]))]))
273
+ new_trees .append (Tree ("term" , [node , parenthesize (Tree ("expr" , [node , v ]))]))
245
274
return new_trees
246
275
247
276
248
- def TF_negation (tree : Tree ):
277
+ def TF_negation (tree : Tree ): # ~T == F, ~F == T
249
278
assert tree .data == "literal"
250
279
if len (tree .children ) == 2 :
251
280
if is_token (tree .children [1 ], "TRUE" ):
@@ -311,13 +340,13 @@ def double_negate(tree: Tree): # p == ~~p
311
340
"Double Negation" : [double_negate , simplify_multiple_negation ],
312
341
"Implication as Disjunction" : [impl_to_disj , disj_to_impl ],
313
342
"Iff as Implication" : [dblimpl_to_impl , impl_to_dblimpl ],
314
- "Idempotence" : [idempotence ],
343
+ "Idempotence" : [idempotence , reverse_idempotence ],
315
344
"Identity" : [identity , reverse_identity ],
316
345
"Domination" : [domination ],
317
346
"Commutativity" : [commutativity ],
318
- "Associativity" : [associativity_LR , associativity_expand ],
319
- "Negation" : [negation , TF_negation ],
320
- "Absorption" : [absorption ],
347
+ "Associativity" : [associativity_LR , associativity_expand , reverse_associativity_expand ],
348
+ "Negation" : [negation , TF_negation , reverse_negation ],
349
+ "Absorption" : [absorption , reverse_absorption ],
321
350
"Distributivity" : [distributivity , reverse_distributivity ],
322
351
"De Morgan's Law" : [demorgan , reverse_demorgan ]
323
352
}
@@ -339,25 +368,31 @@ def get_operation_name(op):
339
368
impl_to_disj , reverse_identity
340
369
],
341
370
'expr' : [
342
- idempotence , identity , domination , commutativity , associativity_LR , associativity_expand , negation , absorption ,
343
- distributivity , reverse_distributivity , double_negate , reverse_demorgan , disj_to_impl , reverse_identity
371
+ idempotence , identity , domination , commutativity , associativity_LR , associativity_expand ,
372
+ reverse_associativity_expand , negation , absorption , distributivity , reverse_distributivity , double_negate ,
373
+ reverse_demorgan , disj_to_impl , reverse_identity , reverse_idempotence
344
374
],
345
375
'term' : [
346
- idempotence , identity , domination , commutativity , associativity_LR , associativity_expand , negation , absorption ,
347
- distributivity , reverse_distributivity , double_negate , reverse_demorgan , impl_to_dblimpl , reverse_identity
376
+ idempotence , identity , domination , commutativity , associativity_LR , associativity_expand ,
377
+ reverse_associativity_expand , negation , absorption , distributivity , reverse_distributivity , double_negate ,
378
+ reverse_demorgan , impl_to_dblimpl , reverse_identity , reverse_idempotence
348
379
],
349
380
'literal' : [
350
- simplify_multiple_negation , TF_negation , demorgan , reverse_identity
381
+ simplify_multiple_negation , TF_negation , demorgan , reverse_identity , reverse_idempotence
351
382
],
352
383
'variable' : [],
353
384
'paren_expr' : [
354
- double_negate , reverse_identity
385
+ double_negate , reverse_identity , reverse_idempotence , reverse_absorption
355
386
],
356
387
'ID' : [
357
- reverse_identity
388
+ reverse_identity , reverse_idempotence , reverse_absorption
389
+ ],
390
+ "TRUE" : [
391
+ reverse_negation
392
+ ],
393
+ "FALSE" : [
394
+ reverse_negation
358
395
],
359
- "TRUE" : [],
360
- "FALSE" : [],
361
396
"_LPAR" : [],
362
397
"_RPAR" : [],
363
398
"NOT" : [],
@@ -378,13 +413,16 @@ def get_operation_name(op):
378
413
ep = ExpressionParser ()
379
414
tts = TreeToString ()
380
415
381
- tr1 = ep .parse ('pvqvr ' ).children [0 ]
382
- tr2 = disj_to_impl (tr1 )
416
+ tr1 = Token ( "TRUE" , "True" ) # ep.parse('p ').children[0]
417
+ tr2 = reverse_negation (tr1 )
383
418
print ([tts .transform (t ) for t in tr2 ])
384
- tr1 = ep .parse ('a->q->c ' ).children [0 ]
385
- tr2 = impl_to_disj (tr1 )
419
+ tr1 = Token ( "FALSE" , "F" ) # ep.parse('(pvq) ').children[0]
420
+ tr2 = reverse_negation (tr1 )
386
421
print ([tts .transform (t ) for t in tr2 ])
387
422
388
- tr3 = ep .parse ('(pvq)' ).children [0 ]
389
- tr4 = simplify_paren_expr (tr3 )
390
- print (tr4 if type (tr4 ) == Token else tts .transform (tr4 ), sep = "\n " )
423
+ t1 = ep .parse ('a^b^c^a^b^a' ).children [0 ]
424
+ t1 = idempotence (t1 )
425
+ print (t1 if type (t1 ) == Token else tts .transform (t1 ), sep = "\n " )
426
+ t3 = ep .parse ('(a^b^c)v(a^b^c)' ).children [0 ]
427
+ t3 = idempotence (t3 )
428
+ print (t3 if type (t3 ) == Token else tts .transform (t3 ), sep = "\n " )
0 commit comments