1+ # -*- coding: utf-8 -*-
2+
13from mathics .version import __version__ # noqa used in loading to check consistency.
4+
25from mathics .core .expression import (
36 Expression ,
47 Symbol ,
1417# TODO: Extend these rules?
1518
1619
20+ def debug_logical_expr (pref , expr , evaluation ):
21+ pass
22+ # return
23+ # print(pref , expr) #expr.format(evaluation,"OutputForm").boxes_to_text(evaluation=evaluation))
24+
25+
26+ logical_algebraic_rules_spec = {
27+ # Inequality rules
28+ "Unequal[a_, b_]" : "Not[Equal[a, b]]" ,
29+ "Greater[a_, b_]" : "Less[b, a]" ,
30+ "GreaterEqual[a_, b_]" : "Not[Less[a, b]]" ,
31+ "LessEqual[a_, b_]" : "Not[Less[b, a]]" ,
32+ "PositiveQ[a_]" : "Less[0, a]" ,
33+ "NegativeQ[a_]" : "Less[a, 0]" ,
34+ # Logical basic reductions
35+ "Or[q_, Not[q_]]" : "True" ,
36+ "Or[q_,]" : "q" ,
37+ "Or[q_, q_]" : "q" ,
38+ "Or[pred1___, q_, pred2___, q_, pred3___]" : "Or[pred1, q, pred2, pred3]" ,
39+ # TODO: Logical operations should sort the leaves...
40+ "Or[Not[q_], q_]" : "True" ,
41+ "Or[pred1___, q_, pred2___, Not[q_], pred3___]" : "Or[pred1, pred2, pred3]" ,
42+ "Or[pred1___, Not[q_], pred2___, q_, pred3___]" : "Or[pred1, pred2, pred3]" ,
43+ "And[q_,q_]" : "q" ,
44+ "And[q_, Not[q_]]" : "False" ,
45+ "And[Not[q_],q_]" : "False" ,
46+ "And[pred1___, q_, pred2___, Not[q_], pred3___]" : "False" ,
47+ "And[pred1___, Not[q_], pred2___, q_, pred3___]" : "False" ,
48+ # Logical reductions involving equalities
49+ "Or[pred1___, a_==b_, pred2___ , b_==a_, pred3___]" : "Or[pred1, a==b, pred2, pred3]" ,
50+ "And[pred1___, a_==b_, pred2___ , b_==a_, pred3___]" : "And[pred1, a==b, pred2, pred3]" ,
51+ "Or[pred1___, a_==b_, pred2___ , Not[b_==a_], pred3___]" : "Or[pred1, pred2, pred3]" ,
52+ "And[pred1___, a_==b_, pred2___ , Not[b_==a_], pred3___]" : "False" ,
53+ "Xor[q_, Not[q_]]" : "True" ,
54+ "Xor[a_==b_, Not[b_==a_]]" : "True" ,
55+ # Logical reductions involving inequalities
56+ "Or[a_<b_, b_<a_]" : "! (a==b)" ,
57+ "And[a_<b_, b_<a_]" : "False" ,
58+ "Or[a_<b_, b_==a_]" : "! (a>b)" ,
59+ "Or[b_==a_, a_<b_]" : "! (a>b)" ,
60+ "And[a_<b_, b_==a_]" : "False" ,
61+ "And[b_==a_, a_<b_]" : "False" ,
62+ "And[pred1___, a_<b_, pred2___, Not[b_<a_], pred3___ ]" : "And[pred1, a==b, pred2, pred3]" ,
63+ "And[pred1___, a_<b_, pred2___, b_<a_, pred3___ ]" : "False" ,
64+ "And[pred1___, a_<b_, pred2___, b_==a_, pred3___ ]" : "False" ,
65+ "Or[pred1___, a_<b_, pred2___, b_<a_, pred3___ ]" : "And[pred1,Not[a==b],pred2, pred3]" ,
66+ "Or[pred1___, a_<b_, pred2___, b_==a_, pred3___ ]" : "And[pred1,Not[b<a],pred2, pred3]" ,
67+ "Or[pred1___, q_, pred2___, Not[q_], pred3___ ]" : "Or[pred1, pred2, pred3]" ,
68+ # Let's assume that variables are finite
69+ "-Infinity< Infinity" : "True" ,
70+ "Infinity< -Infinity " : "False" ,
71+ "Infinity == -Infinity " : "False" ,
72+ "_Symbol < Infinity " : "True" ,
73+ "-Infinity <_Symbol" : "True" ,
74+ }
75+
76+ remove_not_rules_spec = {
77+ "Not[a_<b_]" : "a>=b" ,
78+ "Not[a_==b_]" : "a!=b" ,
79+ }
80+
1781
18- def get_assumptions_list (evaluation ):
82+ logical_algebraic_rules = None
83+ remove_not_rules = None
84+
85+
86+ def ensure_logical_algebraic_rules ():
87+ global logical_algebraic_rules
88+ global remove_not_rules
89+ if logical_algebraic_rules is None :
90+ logical_algebraic_rules = []
91+ for pattern , replace in logical_algebraic_rules_spec .items ():
92+ pattern = parse_builtin_rule (pattern , SystemDefinitions ())
93+ logical_algebraic_rules .append (
94+ Rule (pattern , parse_builtin_rule (replace ), system = True )
95+ )
96+ remove_not_rules = []
97+ for pattern , replace in remove_not_rules_spec .items ():
98+ pattern = parse_builtin_rule (pattern , SystemDefinitions ())
99+ remove_not_rules .append (
100+ Rule (pattern , parse_builtin_rule (replace ), system = True )
101+ )
102+ return
103+
104+
105+ def remove_nots_when_unnecesary (pred , evaluation ):
106+ global remove_not_rules
107+ cc = True
108+ while cc :
109+ pred , cc = pred .apply_rules (remove_not_rules , evaluation )
110+ debug_logical_expr ("-> " , pred , evaluation )
111+ if pred .is_true () or pred == SymbolFalse :
112+ return pred
113+ return pred
114+
115+
116+ def get_assumptions_list (evaluation ):
19117 assumptions = None
20118 assumptions_def = evaluation .definitions .get_definition (
21119 "System`$Assumptions" , only_if_exists = True
@@ -35,23 +133,254 @@ def get_assumptions_list(evaluation):
35133 return assumptions
36134
37135
136+ def remove_duplicated_assumptions (assumptions_list , evaluation ):
137+ if len (assumptions_list ) == 0 :
138+ return assumptions_list
139+ assumptions_list = sorted (assumptions_list )
140+ unique_assumptions = [assumptions_list [0 ]]
141+ for i , assumption in enumerate (assumptions_list ):
142+ if not (assumption == unique_assumptions [- 1 ]):
143+ unique_assumptions .append (assumption )
144+ return unique_assumptions
38145
39- def evaluate_predicate (pred , evaluation ):
40- if pred .has_form (("List" , "Sequence" ), None ):
41- return Expression (pred ._head , * [evaluate_predicate (subp , evaluation ) for subp in pred ._leaves ] )
42- assumptions = get_assumptions_list (evaluation )
43146
147+ def logical_expand_assumptions (assumptions_list , evaluation ):
148+ new_assumptions_list = []
149+ changed = False
150+ for assumption in assumptions_list :
151+ if assumption .is_symbol ():
152+ if assumption .is_true ():
153+ changed = True
154+ continue
155+ if assumption == SymbolFalse :
156+ evaluation .message ("Assumption" , "faas" )
157+ changed = True
158+ continue
159+ if assumption .is_numeric ():
160+ evaluation .message ("Assumption" , "baas" )
161+ changed = True
162+ continue
163+ new_assumptions_list .append (assumption )
164+ continue
165+ if assumption .has_form ("And" , None ):
166+ changed = True
167+ for leaf in assumption .leaves :
168+ new_assumptions_list .append (leaf )
169+ continue
170+ if assumption .has_form ("Not" , 1 ):
171+ sentence = assumption ._leaves [0 ]
172+ if sentence .has_form ("Or" , None ):
173+ changed = True
174+ for leaf in sentence ._leaves :
175+ new_assumptions_list .append (Expression ("Not" , leaf ))
176+ continue
177+ if sentence .has_form ("And" , None ):
178+ leaves = (Expression ("Not" , leaf ) for leaf in sentence ._leaves )
179+ new_assumptions_list .append (Expression ("Or" , * leaves ))
180+ continue
181+ if sentence .has_form ("Implies" , 2 ):
182+ changed = True
183+ new_assumptions_list .append (sentence ._leaves [0 ])
184+ new_assumptions_list .append (Expression ("Not" , sentence ._leaves [1 ]))
185+ if assumption .has_form ("Nor" , None ):
186+ changed = True
187+ for leaf in assumption .leaves :
188+ new_assumptions_list .append (Expression ("Not" , leaf ))
189+ continue
190+ else :
191+ new_assumptions_list .append (assumption )
192+
193+ if changed :
194+ new_assumptions_list = remove_duplicated_assumptions (
195+ new_assumptions_list , evaluation
196+ )
197+
198+ return new_assumptions_list , changed
199+
200+
201+ def algebraic_expand_assumptions (assumptions_list , evaluation ):
202+ global logical_algebraic_rules
203+ ensure_logical_algebraic_rules ()
204+ new_assumptions_list = []
205+ changed = False
206+ # First apply standard rules of reduction.
207+ # These rules are generated the first time that are used.
208+ for assumption in assumptions_list :
209+ assumption , applied = assumption .apply_rules (
210+ logical_algebraic_rules , evaluation
211+ )
212+ changed = changed or applied
213+ new_assumptions_list .append (assumption )
214+ if changed :
215+ return new_assumptions_list , changed
216+ # If not changed, let's try with the next set of rules
217+ for assumption in assumptions_list :
218+ if assumption .has_form ("Not" , 1 ):
219+ nas , local_change = algebraic_expand_assumptions (
220+ [assumption ._leaves [0 ]], evaluation
221+ )
222+ if local_change :
223+ changed = local_change
224+ for na in nas :
225+ if na .has_form ("Not" , 1 ):
226+ new_assumptions_list .append (na ._leaves [0 ])
227+ else :
228+ new_assumptions_list .append (Expression ("Not" , na ))
229+ else :
230+ new_assumptions_list .append (assumption )
231+ elif assumption .has_form (("Equal" , "Unequal" , "Equivalent" ), (3 , None )):
232+ leaves = assumption .leaves ()
233+ head = assumption .get_head ()
234+ changed = True
235+ for i in range (len (leaves )):
236+ for j in range (i ):
237+ new_assumptions_list .append (Expression (head , leaves [i ], leaves [j ]))
238+ new_assumptions_list .append (Expression (head , leaves [j ], leaves [i ]))
239+ elif assumption .has_form (
240+ ("Less" , "Greater" , "LessEqual" , "GreaterEqual" ), (3 , None )
241+ ):
242+ leaves = assumption .leaves ()
243+ head = assumption .get_head ()
244+ changed = True
245+ for i in range (len (leaves )):
246+ for j in range (i ):
247+ new_assumptions_list .append (Expression (head , leaves [i ], leaves [j ]))
248+ else :
249+ new_assumptions_list .append (assumption )
250+
251+ if changed :
252+ assumptions_list = remove_duplicated_assumptions (
253+ new_assumptions_list , evaluation
254+ )
255+ new_assumptions_list = []
256+ for assumption in assumptions_list :
257+ assumption , applied = assumption .apply_rules (
258+ logical_algebraic_rules , evaluation
259+ )
260+ new_assumptions_list .append (assumption )
261+ return new_assumptions_list , changed
262+
263+
264+ def get_assumption_rules_dispatch (evaluation ):
265+ # TODO: cache the generated rules...
266+ assumptions_list = get_assumptions_list (evaluation )
267+ if assumptions_list is None :
268+ return None
269+
270+ # check for consistency:
271+ consistent_assumptions = Expression ("And" , * assumptions_list )
272+ val_consistent_assumptions = consistent_assumptions .evaluate (evaluation )
273+ if val_consistent_assumptions == SymbolFalse :
274+ evaluation .message ("Inconsistent assumptions" )
275+
276+ if assumptions_list is None :
277+ return remove_nots_when_unnecesary (pred , evaluation ).evaluate (evaluation )
278+
279+ # Expands Logically
280+ assumptions_list , cont = logical_expand_assumptions (assumptions_list , evaluation )
281+ while cont :
282+ assumptions_list , cont = logical_expand_assumptions (
283+ assumptions_list , evaluation
284+ )
285+
286+ # Expands algebraically
287+ assumptions_list , cont = algebraic_expand_assumptions (assumptions_list , evaluation )
288+ while cont :
289+ assumptions_list , cont = algebraic_expand_assumptions (
290+ assumptions_list , evaluation
291+ )
44292 assumption_rules = []
45- for assumption in assumptions :
46- true_state = True
47- while assumption .has_form ("Not" ,1 ):
48- true_state = False
49- assumption = assumption ._leaves [0 ]
50- if true_state :
51- assumption_rules .append (Rule (assumption , SymbolTrue ))
293+ for pat in assumptions_list :
294+ value = True
295+ while pat .has_form ("Not" , 1 ):
296+ value = not value
297+ pat = pat ._leaves [0 ]
298+
299+ if value :
300+ symbol_value = SymbolTrue
301+ symbol_negate_value = SymbolFalse
302+ else :
303+ symbol_value = SymbolFalse
304+ symbol_negate_value = SymbolTrue
305+
306+ if pat .has_form ("Equal" , 2 ):
307+ if value :
308+ lhs , rhs = pat ._leaves
309+ if lhs .is_numeric ():
310+ assumption_rules .append (Rule (rhs , lhs ))
311+ else :
312+ assumption_rules .append (Rule (lhs , rhs ))
313+ else :
314+ assumption_rules .append (Rule (pat , SymbolFalse ))
315+ symm_pat = Expression (pat ._head , pat ._leaves [1 ], pat ._leaves [0 ])
316+ assumption_rules .append (Rule (symm_pat , SymbolFalse ))
317+ elif pat .has_form ("Equivalent" , 2 ):
318+ assumption_rules .append (Rule (pat , symbol_value ))
319+ symm_pat = Expression (pat ._head , pat ._leaves [1 ], pat ._leaves [0 ])
320+ assumption_rules .append (Rule (symm_pat , symbol_value ))
321+ elif pat .has_form ("Less" , 2 ):
322+ if value :
323+ assumption_rules .append (Rule (pat , SymbolTrue ))
324+ assumption_rules .append (
325+ Rule (
326+ Expression (pat ._head , pat ._leaves [1 ], pat ._leaves [0 ]),
327+ SymbolFalse ,
328+ )
329+ )
330+ for head in ("Equal" , "Equivalent" ):
331+ assumption_rules .append (
332+ Rule (
333+ Expression (head , pat ._leaves [0 ], pat ._leaves [1 ]),
334+ SymbolFalse ,
335+ )
336+ )
337+ assumption_rules .append (
338+ Rule (
339+ Expression (head , pat ._leaves [1 ], pat ._leaves [0 ]),
340+ SymbolFalse ,
341+ )
342+ )
343+ else :
344+ assumption_rules .append (Rule (pat , SymbolFalse ))
52345 else :
53- assumption_rules .append (Rule (assumption , SymbolFalse ))
54-
55- pred , changed = pred .apply_rules (assumption_rules , evaluation )
346+ assumption_rules .append (Rule (pat , symbol_value ))
347+ # TODO: expand the pred and assumptions into an standard,
348+ # atomized form, and then apply the rules...
349+ if len (assumption_rules ) == 0 :
350+ return None
351+ return assumption_rules
352+
353+
354+ def evaluate_predicate (pred , evaluation ):
355+ global logical_algebraic_rules
356+ global remove_not_rules
357+
358+ if pred .has_form (("List" , "Sequence" ), None ):
359+ return Expression (
360+ pred ._head , * [evaluate_predicate (subp , evaluation ) for subp in pred ._leaves ]
361+ )
362+
363+ debug_logical_expr ("reducing " , pred , evaluation )
364+ ensure_logical_algebraic_rules ()
56365 pred = pred .evaluate (evaluation )
366+ debug_logical_expr ("-> " , pred , evaluation )
367+ cc = True
368+ while cc :
369+ pred , cc = pred .apply_rules (logical_algebraic_rules , evaluation )
370+ debug_logical_expr ("-> " , pred , evaluation )
371+ if pred .is_true () or pred == SymbolFalse :
372+ return pred
373+
374+ assumption_rules = get_assumption_rules_dispatch (evaluation )
375+ if assumption_rules is None :
376+ return remove_nots_when_unnecesary (pred , evaluation ).evaluate (evaluation )
377+
378+ if assumption_rules is not None :
379+ debug_logical_expr (" Now, using the assumptions over " , pred , evaluation )
380+ changed = True
381+ while changed :
382+ pred , changed = pred .apply_rules (assumption_rules , evaluation )
383+ debug_logical_expr (" -> " , pred , evaluation )
384+
385+ pred = remove_nots_when_unnecesary (pred , evaluation ).evaluate (evaluation )
57386 return pred
0 commit comments