mathai 0.4.8__py3-none-any.whl → 0.7.2__py3-none-any.whl
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.
- mathai/__init__.py +9 -8
- mathai/base.py +132 -34
- mathai/bivariate_inequality.py +317 -0
- mathai/diff.py +3 -3
- mathai/expand.py +159 -80
- mathai/factor.py +45 -21
- mathai/fraction.py +2 -2
- mathai/integrate.py +38 -19
- mathai/inverse.py +4 -4
- mathai/limit.py +94 -70
- mathai/linear.py +90 -81
- mathai/logic.py +7 -1
- mathai/matrix.py +228 -0
- mathai/parser.py +13 -7
- mathai/parsetab.py +61 -0
- mathai/printeq.py +12 -9
- mathai/simplify.py +511 -369
- mathai/structure.py +2 -2
- mathai/tool.py +2 -2
- mathai/trig.py +42 -25
- mathai/univariate_inequality.py +78 -30
- mathai-0.7.2.dist-info/METADATA +293 -0
- mathai-0.7.2.dist-info/RECORD +28 -0
- {mathai-0.4.8.dist-info → mathai-0.7.2.dist-info}/WHEEL +1 -1
- mathai-0.4.8.dist-info/METADATA +0 -234
- mathai-0.4.8.dist-info/RECORD +0 -25
- {mathai-0.4.8.dist-info → mathai-0.7.2.dist-info}/top_level.txt +0 -0
mathai/simplify.py
CHANGED
|
@@ -1,398 +1,540 @@
|
|
|
1
1
|
import math
|
|
2
2
|
from .base import *
|
|
3
3
|
from fractions import Fraction
|
|
4
|
-
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for key in sorted(dic.keys(), key=lambda x: str_form(x)):
|
|
38
|
-
n = dic[key]
|
|
39
|
-
if n == 0:
|
|
40
|
-
continue
|
|
41
|
-
if key == "const":
|
|
42
|
-
summation.children.append(tree_form("d_"+str(n)))
|
|
4
|
+
from collections import Counter
|
|
5
|
+
def convert_to_basic(node):
|
|
6
|
+
if not node.name.startswith("f_"):
|
|
7
|
+
return node
|
|
8
|
+
node.children = [convert_to_basic(c) for c in node.children]
|
|
9
|
+
if node.name == "f_sub":
|
|
10
|
+
node = node.children[0]-node.children[1]
|
|
11
|
+
if node.name == "f_div":
|
|
12
|
+
node = node.children[0]/node.children[1]
|
|
13
|
+
if node.name == "f_sqrt":
|
|
14
|
+
node = node.children[0]**(tree_form("d_2")**tree_form("d_-1"))
|
|
15
|
+
return node
|
|
16
|
+
|
|
17
|
+
def clear_div(eq, denom):
|
|
18
|
+
|
|
19
|
+
lst = factor_generation(eq)
|
|
20
|
+
|
|
21
|
+
if tree_form("d_0") in lst:
|
|
22
|
+
return tree_form("d_0"), True
|
|
23
|
+
|
|
24
|
+
lst3 = []
|
|
25
|
+
for item in lst:
|
|
26
|
+
if "v_" not in str_form(item) and compute(item) < 0:
|
|
27
|
+
lst3.append(item)
|
|
28
|
+
sign = denom
|
|
29
|
+
if len(lst3) % 2 == 1:
|
|
30
|
+
sign = False
|
|
31
|
+
if denom:
|
|
32
|
+
eq2 = []
|
|
33
|
+
eq3 = []
|
|
34
|
+
for item in lst:
|
|
35
|
+
if frac(item) is not None:#"v_" not in str_form(item):
|
|
36
|
+
eq2.append(item)
|
|
43
37
|
else:
|
|
44
|
-
|
|
45
|
-
summation.children.append(key)
|
|
46
|
-
else:
|
|
47
|
-
if key.name == "f_mul":
|
|
48
|
-
key.children.append(tree_form("d_"+str(n)))
|
|
49
|
-
else:
|
|
50
|
-
key = tree_form("d_"+str(n))*key
|
|
51
|
-
summation.children.append(key)
|
|
38
|
+
eq3.append(item)
|
|
52
39
|
|
|
53
|
-
if
|
|
54
|
-
|
|
40
|
+
if eq3 == []:
|
|
41
|
+
return product(eq2), True
|
|
42
|
+
return product(eq3), sign
|
|
43
|
+
lst4 = []
|
|
44
|
+
|
|
45
|
+
for item in lst:
|
|
46
|
+
if item.name == "f_pow":
|
|
47
|
+
tmp = frac(item.children[1])
|
|
48
|
+
if tmp is None or tmp != -1:
|
|
49
|
+
lst4.append(item)
|
|
50
|
+
else:
|
|
51
|
+
lst4.append(item)
|
|
52
|
+
|
|
53
|
+
lst2 = []
|
|
54
|
+
for item in lst4:
|
|
55
|
+
if frac(item) is None:#"v_" in str_form(item):
|
|
56
|
+
lst2.append(item)
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
if lst2 == []:
|
|
59
|
+
return product(lst4), sign
|
|
60
|
+
return product(lst2), sign
|
|
61
|
+
'''
|
|
62
|
+
def multiply_node(eq):
|
|
63
|
+
if not eq.name.startswith("f_"):
|
|
64
|
+
return eq
|
|
65
|
+
if eq.name == "f_mul":
|
|
66
|
+
con = 1
|
|
67
|
+
eq2 = TreeNode("f_mul", [])
|
|
68
|
+
for i in range(len(eq.children)-1,-1,-1):
|
|
69
|
+
if frac(eq.children[i]) is not None:
|
|
70
|
+
con = con * frac(eq.children[i])
|
|
66
71
|
else:
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
dic = {"const":1}
|
|
72
|
+
eq2.children.append(eq.children[i])
|
|
73
|
+
if con == 0:
|
|
74
|
+
return tree_form("d_0")
|
|
75
|
+
eq2.name = eq.name
|
|
76
|
+
eq = eq2
|
|
77
|
+
|
|
78
|
+
lst = {}
|
|
76
79
|
for child in eq.children:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
dic[b] += a
|
|
80
|
+
power = tree_form("d_1")
|
|
81
|
+
con2 = ""
|
|
82
|
+
if child.name == "f_pow":
|
|
83
|
+
con2 = child.children[0]
|
|
84
|
+
power = child.children[1]
|
|
83
85
|
else:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
con2 = child
|
|
87
|
+
if con2 in lst.keys():
|
|
88
|
+
lst[con2] = lst[con2] + power
|
|
89
|
+
else:
|
|
90
|
+
lst[con2] = power
|
|
91
|
+
eq3 = TreeNode("f_mul", [])
|
|
92
|
+
|
|
93
|
+
for kv in lst.keys():
|
|
94
|
+
tmp3 = lst[kv]
|
|
95
|
+
if tmp3 == tree_form("d_1"):
|
|
96
|
+
eq3.children.append(kv)
|
|
97
|
+
elif tmp3 == tree_form("d_0"):
|
|
93
98
|
continue
|
|
94
|
-
if key == "const":
|
|
95
|
-
if n != 1:
|
|
96
|
-
summation.children.append(tree_form("d_"+str(n)))
|
|
97
99
|
else:
|
|
98
|
-
|
|
99
|
-
summation.children.append(key)
|
|
100
|
-
else:
|
|
101
|
-
summation.children.append(key**tree_form("d_"+str(n)))
|
|
100
|
+
eq3.children.append(kv ** tmp3)
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return summation
|
|
110
|
-
def solve_u(eq):
|
|
111
|
-
if eq.name in ["f_pow", "f_mul"]:
|
|
112
|
-
return solve_mul(eq)
|
|
113
|
-
return solve_add(eq)
|
|
114
|
-
def recur_solve(eq):
|
|
115
|
-
eq = solve_u(eq)
|
|
102
|
+
tmp3 = frac_to_tree(con)
|
|
103
|
+
if tmp3 != tree_form("d_1"):
|
|
104
|
+
eq3.children.append(tmp3)
|
|
105
|
+
eq = eq3
|
|
106
|
+
eq4 = TreeNode(eq.name, [])
|
|
116
107
|
if eq.children == []:
|
|
117
|
-
|
|
108
|
+
return tree_form("d_1")
|
|
109
|
+
if len(eq.children) == 1:
|
|
110
|
+
eq4 = eq.children[0]
|
|
111
|
+
eq = eq4
|
|
112
|
+
return TreeNode(eq.name, [multiply_node(child) for child in eq.children])
|
|
113
|
+
'''
|
|
114
|
+
def multiply_node(equation):
|
|
115
|
+
"""
|
|
116
|
+
Iterative version of multiply_node without using TreeNode as dict key.
|
|
117
|
+
"""
|
|
118
|
+
# Stack: (node, child_index, partially_processed_children)
|
|
119
|
+
if equation is None:
|
|
120
|
+
return None
|
|
121
|
+
stack = [(equation, 0, [])]
|
|
122
|
+
|
|
123
|
+
while stack:
|
|
124
|
+
node, child_index, processed_children = stack.pop()
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
# If all children processed
|
|
127
|
+
if child_index >= len(node.children):
|
|
128
|
+
node.children = processed_children
|
|
129
|
+
|
|
130
|
+
# Only process multiplication nodes
|
|
131
|
+
if node.name == "f_mul":
|
|
132
|
+
# Step 1: combine numeric constants
|
|
133
|
+
con = 1
|
|
134
|
+
new_children = []
|
|
135
|
+
for child in reversed(node.children):
|
|
136
|
+
val = frac(child)
|
|
137
|
+
if val is not None:
|
|
138
|
+
con *= val
|
|
139
|
+
else:
|
|
140
|
+
new_children.append(child)
|
|
141
|
+
|
|
142
|
+
if con == 0:
|
|
143
|
+
node = tree_form("d_0")
|
|
144
|
+
# Return to parent
|
|
145
|
+
if stack:
|
|
146
|
+
parent, idx, parent_children = stack.pop()
|
|
147
|
+
parent_children.append(node)
|
|
148
|
+
stack.append((parent, idx + 1, parent_children))
|
|
149
|
+
continue
|
|
150
|
+
else:
|
|
151
|
+
return node
|
|
152
|
+
|
|
153
|
+
node.children = new_children
|
|
154
|
+
|
|
155
|
+
# Step 2: combine powers of same base iteratively
|
|
156
|
+
# Instead of using dict, we collect (base, exponent) in a list
|
|
157
|
+
base_powers = []
|
|
158
|
+
for child in node.children:
|
|
159
|
+
if child.name == "f_pow":
|
|
160
|
+
base, power = child.children
|
|
161
|
+
else:
|
|
162
|
+
base = child
|
|
163
|
+
power = tree_form("d_1")
|
|
164
|
+
|
|
165
|
+
# Look for existing base in base_powers (by structural equality)
|
|
166
|
+
found = False
|
|
167
|
+
for i, (b, p) in enumerate(base_powers):
|
|
168
|
+
if b == base: # structural equality check
|
|
169
|
+
base_powers[i] = (b, p + power)
|
|
170
|
+
found = True
|
|
171
|
+
break
|
|
172
|
+
if not found:
|
|
173
|
+
base_powers.append((base, power))
|
|
174
|
+
|
|
175
|
+
# Step 3: rebuild multiplication node
|
|
176
|
+
new_mul = TreeNode("f_mul", [])
|
|
177
|
+
for base, power in base_powers:
|
|
178
|
+
if power == tree_form("d_1"):
|
|
179
|
+
new_mul.children.append(base)
|
|
180
|
+
elif power == tree_form("d_0"):
|
|
181
|
+
continue
|
|
182
|
+
else:
|
|
183
|
+
new_mul.children.append(TreeNode("f_pow", [base, power]))
|
|
184
|
+
|
|
185
|
+
# Step 4: add numeric constant
|
|
186
|
+
con_tree = frac_to_tree(con)
|
|
187
|
+
if con_tree != tree_form("d_1"):
|
|
188
|
+
new_mul.children.append(con_tree)
|
|
189
|
+
|
|
190
|
+
# Step 5: simplify trivial cases
|
|
191
|
+
if not new_mul.children:
|
|
192
|
+
node = tree_form("d_1")
|
|
193
|
+
elif len(new_mul.children) == 1:
|
|
194
|
+
node = new_mul.children[0]
|
|
124
195
|
else:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
eq = TreeNode(eq.name, merged_children)
|
|
128
|
-
return TreeNode(eq.name, [recur_solve(child) for child in eq.children])
|
|
129
|
-
|
|
130
|
-
return recur_solve(eq)
|
|
131
|
-
def _convert_sub2neg(eq):
|
|
132
|
-
if eq.name == "f_neg":
|
|
133
|
-
return -_convert_sub2neg(eq.children[0])
|
|
134
|
-
elif eq.name == "f_sub":
|
|
135
|
-
return _convert_sub2neg(eq.children[0]) - _convert_sub2neg(eq.children[1])
|
|
136
|
-
elif eq.name == "f_sqrt":
|
|
137
|
-
return _convert_sub2neg(eq.children[0])**(tree_form("d_2")**-1)
|
|
138
|
-
elif eq.name == "f_div":
|
|
139
|
-
if eq.children[0] == 0:
|
|
140
|
-
return tree_form("d_0")
|
|
141
|
-
return _convert_sub2neg(eq.children[0])*_convert_sub2neg(eq.children[1])**-1
|
|
142
|
-
return TreeNode(eq.name, [_convert_sub2neg(child) for child in eq.children])
|
|
143
|
-
def solve(eq, specialfx=False):
|
|
144
|
-
if specialfx:
|
|
145
|
-
eq = _convert_sub2neg(eq)
|
|
146
|
-
|
|
147
|
-
eq = flatten_tree(eq)
|
|
148
|
-
|
|
149
|
-
return dowhile(eq, _solve)
|
|
150
|
-
def solve2(eq):
|
|
151
|
-
return solve(eq, True)
|
|
152
|
-
def clear_div(eq, denom=False):
|
|
153
|
-
lst = factor_generation(eq)
|
|
154
|
-
if tree_form("d_0") in lst:
|
|
155
|
-
return tree_form("d_0"), True
|
|
156
|
-
lst3 = [item for item in lst if "v_" not in str_form(item) and compute(item) < 0]
|
|
157
|
-
|
|
158
|
-
sign = True
|
|
159
|
-
if len(lst3) % 2 == 1:
|
|
160
|
-
sign = False
|
|
161
|
-
if denom:
|
|
162
|
-
return eq if sign else -eq, sign
|
|
163
|
-
lst = [item for item in lst if not(item.name == "f_pow" and frac(item.children[1]) is not None and frac(item.children[1]) == -1)]
|
|
196
|
+
node = new_mul
|
|
164
197
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def simplify(eq):
|
|
171
|
-
if "v_" not in str_form(eq):
|
|
172
|
-
n = frac(eq)
|
|
173
|
-
if n is not None:
|
|
174
|
-
if n.numerator == 0:
|
|
175
|
-
return tree_form("d_0")
|
|
176
|
-
if n.denominator != 1:
|
|
177
|
-
return tree_form("d_"+str(n.numerator))/tree_form("d_"+str(n.denominator))
|
|
198
|
+
# Return node to parent
|
|
199
|
+
if stack:
|
|
200
|
+
parent, idx, parent_children = stack.pop()
|
|
201
|
+
parent_children.append(node)
|
|
202
|
+
stack.append((parent, idx + 1, parent_children))
|
|
178
203
|
else:
|
|
179
|
-
return
|
|
180
|
-
error = False
|
|
181
|
-
eq = flatten_tree(eq)
|
|
182
|
-
if eq.name in ["f_and", "f_or", "f_not"]:
|
|
183
|
-
return TreeNode(eq.name, [simplify(child) for child in eq.children])
|
|
184
|
-
|
|
185
|
-
if eq.name in ["f_lt", "f_gt", "f_le", "f_ge", "f_eq"]:
|
|
186
|
-
tmp, sign = clear_div(simplify(eq.children[0]-eq.children[1]), eq.name != "f_eq")
|
|
187
|
-
name2 = eq.name
|
|
188
|
-
if not sign:
|
|
189
|
-
name2 = {"f_lt":"f_gt", "f_gt":"f_lt", "f_eq":"f_eq", "f_le":"f_ge", "f_ge":"f_le"}[name2]
|
|
204
|
+
return node # fully processed root
|
|
190
205
|
|
|
191
|
-
|
|
206
|
+
else:
|
|
207
|
+
# Push current node back to continue with next child
|
|
208
|
+
stack.append((node, child_index, processed_children))
|
|
209
|
+
# Push next child to stack
|
|
210
|
+
child = node.children[child_index]
|
|
211
|
+
stack.append((child, 0, []))
|
|
212
|
+
'''
|
|
213
|
+
def addition_node(eq):
|
|
214
|
+
if not eq.name.startswith("f_"):
|
|
215
|
+
return eq
|
|
216
|
+
if eq.name == "f_add":
|
|
217
|
+
con = 0
|
|
218
|
+
eq2 = TreeNode("f_add", [])
|
|
219
|
+
for i in range(len(eq.children)-1,-1,-1):
|
|
220
|
+
n = frac(eq.children[i])
|
|
221
|
+
if n is not None:
|
|
222
|
+
con = con + n
|
|
223
|
+
else:
|
|
224
|
+
eq2.children.append(eq.children[i])
|
|
225
|
+
eq2.name = eq.name
|
|
226
|
+
eq = eq2
|
|
192
227
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
228
|
+
lst = {}
|
|
229
|
+
for child in eq.children:
|
|
230
|
+
power = TreeNode("f_mul", [])
|
|
231
|
+
con2 = None
|
|
232
|
+
con3 = TreeNode("f_mul", [])
|
|
233
|
+
power2 = None
|
|
234
|
+
|
|
235
|
+
if child.name == "f_mul":
|
|
236
|
+
for i in range(len(child.children)):
|
|
237
|
+
if "v_" not in str_form(child.children[i]):
|
|
238
|
+
if child.children[i] != tree_form("d_0"):
|
|
239
|
+
power.children.append(child.children[i])
|
|
240
|
+
else:
|
|
241
|
+
if child.children[i] != tree_form("d_1"):
|
|
242
|
+
con3.children.append(child.children[i])
|
|
243
|
+
if len(con3.children) == 0:
|
|
244
|
+
con2 = tree_form("d_1")
|
|
245
|
+
elif len(con3.children) == 1:
|
|
246
|
+
con2 = con3.children[0]
|
|
247
|
+
else:
|
|
248
|
+
con2 = con3
|
|
208
249
|
else:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def even2(eq):
|
|
216
|
-
return any(even(item) for item in factor_generation(eq))
|
|
217
|
-
if eq.name == "f_pow" and eq.children[0].name == "f_pow":
|
|
218
|
-
if even2(eq.children[0].children[1]) or even2(eq.children[1]) and not even2(solve(eq.children[0].children[1] * eq.children[1])):
|
|
219
|
-
return eq.children[0].children[0].fx("abs") ** solve(eq.children[0].children[1] * eq.children[1])
|
|
250
|
+
con2 = child
|
|
251
|
+
|
|
252
|
+
if power.children == []:
|
|
253
|
+
power2 = tree_form("d_1")
|
|
254
|
+
elif len(power.children) == 1:
|
|
255
|
+
power2 = power.children[0]
|
|
220
256
|
else:
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if eq.name == "f_mul":
|
|
226
|
-
n = Fraction(1)
|
|
227
|
-
for i in range(len(eq.children)-1,-1,-1):
|
|
228
|
-
child= eq.children[i]
|
|
229
|
-
if child.name == "f_pow" and child.children[0].name[:2] == "d_" and child.children[1] == -1:
|
|
230
|
-
if int(child.children[0].name[2:]) == 0:
|
|
231
|
-
error = True
|
|
232
|
-
return eq
|
|
233
|
-
n = n*Fraction(1,int(child.children[0].name[2:]))
|
|
234
|
-
eq.children.pop(i)
|
|
235
|
-
elif child.name[:2] == "d_":
|
|
236
|
-
n = n*int(child.name[2:])
|
|
237
|
-
eq.children.pop(i)
|
|
238
|
-
if n.denominator == 1:
|
|
239
|
-
eq.children.append(tree_form("d_"+str(n.numerator)))
|
|
257
|
+
power2 = power
|
|
258
|
+
|
|
259
|
+
if con2 in lst.keys():
|
|
260
|
+
lst[con2] = lst[con2] + power2
|
|
240
261
|
else:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if len(eq.children) == 1:
|
|
244
|
-
eq = eq.children[0]
|
|
245
|
-
return TreeNode(eq.name, [helper3(child) for child in eq.children])
|
|
246
|
-
def helper4(eq):
|
|
247
|
-
nonlocal error
|
|
248
|
-
if eq == tree_form("d_-1")**tree_form("d_-1"):
|
|
249
|
-
return tree_form("d_-1")
|
|
250
|
-
def perfect_nth_root_value(x, n):
|
|
251
|
-
"""Return integer y if x is a perfect n-th power (y**n == x), else None."""
|
|
252
|
-
if x < 0 and n % 2 == 0:
|
|
253
|
-
return None # even root of negative number not real
|
|
254
|
-
|
|
255
|
-
sign = -1 if x < 0 else 1
|
|
256
|
-
x = abs(x)
|
|
262
|
+
lst[con2] = power2
|
|
263
|
+
eq3 = TreeNode("f_add", [])
|
|
257
264
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
265
|
+
for kv in lst.keys():
|
|
266
|
+
tmp3 = lst[kv]
|
|
267
|
+
if tmp3 == tree_form("d_1"):
|
|
268
|
+
eq3.children.append(kv)
|
|
269
|
+
elif tmp3 == tree_form("d_0"):
|
|
270
|
+
continue
|
|
271
|
+
else:
|
|
272
|
+
eq3.children.append(kv * tmp3)
|
|
273
|
+
|
|
274
|
+
eq = eq3
|
|
275
|
+
eq4 = None
|
|
276
|
+
tmp3 = frac_to_tree(con)
|
|
277
|
+
if tmp3 != tree_form("d_0"):
|
|
278
|
+
eq.children.append(tmp3)
|
|
279
|
+
if eq.children == []:
|
|
280
|
+
return tree_form("d_0")
|
|
281
|
+
if len(eq.children) == 1:
|
|
282
|
+
eq4 = eq.children[0]
|
|
283
|
+
eq = eq4
|
|
284
|
+
return TreeNode(eq.name, [addition_node(child) for child in eq.children])
|
|
285
|
+
'''
|
|
286
|
+
def addition_node(equation):
|
|
287
|
+
"""
|
|
288
|
+
Iterative version of addition_node.
|
|
289
|
+
Combines constants and like terms in addition nodes.
|
|
290
|
+
"""
|
|
291
|
+
# Stack: (node, child_index, partially_processed_children)
|
|
292
|
+
if equation is None:
|
|
293
|
+
return None
|
|
294
|
+
stack = [(equation, 0, [])]
|
|
295
|
+
|
|
296
|
+
while stack:
|
|
297
|
+
node, child_index, processed_children = stack.pop()
|
|
298
|
+
|
|
299
|
+
# If all children are processed
|
|
300
|
+
if child_index >= len(node.children):
|
|
301
|
+
node.children = processed_children
|
|
302
|
+
|
|
303
|
+
# Only process addition nodes
|
|
304
|
+
if node.name == "f_add":
|
|
305
|
+
# Step 1: combine numeric constants
|
|
306
|
+
con = 0
|
|
307
|
+
new_children = []
|
|
308
|
+
for child in reversed(node.children):
|
|
309
|
+
val = frac(child)
|
|
310
|
+
if val is not None:
|
|
311
|
+
con += val
|
|
312
|
+
else:
|
|
313
|
+
new_children.append(child)
|
|
314
|
+
|
|
315
|
+
node.children = new_children
|
|
316
|
+
|
|
317
|
+
# Step 2: combine like terms iteratively
|
|
318
|
+
# We store (base, power) pairs in a list (avoid dict/hash)
|
|
319
|
+
base_terms = []
|
|
320
|
+
for child in node.children:
|
|
321
|
+
# Decompose child into base and multiplier
|
|
322
|
+
power_node = TreeNode("f_mul", [])
|
|
323
|
+
base_node = None
|
|
324
|
+
mul_node = TreeNode("f_mul", [])
|
|
325
|
+
multiplier_node = None
|
|
326
|
+
|
|
327
|
+
if child.name == "f_mul":
|
|
328
|
+
for c in child.children:
|
|
329
|
+
if frac(c) is not None: # constant part
|
|
330
|
+
if c != tree_form("d_0"):
|
|
331
|
+
power_node.children.append(c)
|
|
332
|
+
else: # variable part
|
|
333
|
+
if c != tree_form("d_1"):
|
|
334
|
+
mul_node.children.append(c)
|
|
335
|
+
if len(mul_node.children) == 0:
|
|
336
|
+
base_node = tree_form("d_1")
|
|
337
|
+
elif len(mul_node.children) == 1:
|
|
338
|
+
base_node = mul_node.children[0]
|
|
339
|
+
else:
|
|
340
|
+
base_node = mul_node
|
|
341
|
+
else:
|
|
342
|
+
base_node = child
|
|
343
|
+
|
|
344
|
+
if not power_node.children:
|
|
345
|
+
multiplier_node = tree_form("d_1")
|
|
346
|
+
elif len(power_node.children) == 1:
|
|
347
|
+
multiplier_node = power_node.children[0]
|
|
348
|
+
else:
|
|
349
|
+
multiplier_node = power_node
|
|
350
|
+
|
|
351
|
+
# Combine like terms structurally
|
|
352
|
+
found = False
|
|
353
|
+
for i, (b, m) in enumerate(base_terms):
|
|
354
|
+
if b == base_node:
|
|
355
|
+
base_terms[i] = (b, m + multiplier_node)
|
|
356
|
+
found = True
|
|
357
|
+
break
|
|
358
|
+
if not found:
|
|
359
|
+
base_terms.append((base_node, multiplier_node))
|
|
360
|
+
|
|
361
|
+
# Step 3: rebuild addition node
|
|
362
|
+
new_add = TreeNode("f_add", [])
|
|
363
|
+
for base, multiplier in base_terms:
|
|
364
|
+
if multiplier == tree_form("d_1"):
|
|
365
|
+
new_add.children.append(base)
|
|
366
|
+
elif multiplier == tree_form("d_0"):
|
|
367
|
+
continue
|
|
278
368
|
else:
|
|
279
|
-
|
|
369
|
+
new_add.children.append(base * multiplier)
|
|
370
|
+
|
|
371
|
+
# Step 4: add numeric constant
|
|
372
|
+
con_tree = frac_to_tree(con)
|
|
373
|
+
if con_tree != tree_form("d_0"):
|
|
374
|
+
new_add.children.append(con_tree)
|
|
375
|
+
|
|
376
|
+
# Step 5: simplify trivial cases
|
|
377
|
+
if not new_add.children:
|
|
378
|
+
node = tree_form("d_0")
|
|
379
|
+
elif len(new_add.children) == 1:
|
|
380
|
+
node = new_add.children[0]
|
|
280
381
|
else:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return (eq.children[1-i].children[0]**eq.children[i]).fx("log")
|
|
289
|
-
if eq.name == "f_pow" and eq.children[0] == tree_form("s_e") and eq.children[1].name == "f_log":
|
|
290
|
-
return eq.children[1].children[0]
|
|
291
|
-
|
|
292
|
-
if eq.name == "f_pow" and eq.children[0] == tree_form("d_1"):
|
|
293
|
-
eq = tree_form("d_1")
|
|
294
|
-
if eq.name == "f_pow" and eq.children[0] == tree_form("d_0"):
|
|
295
|
-
if frac(eq.children[1]) is not None and frac(eq.children[1]) <= 0:
|
|
296
|
-
error = True
|
|
382
|
+
node = new_add
|
|
383
|
+
|
|
384
|
+
# Return node to parent
|
|
385
|
+
if stack:
|
|
386
|
+
parent, idx, parent_children = stack.pop()
|
|
387
|
+
parent_children.append(node)
|
|
388
|
+
stack.append((parent, idx + 1, parent_children))
|
|
297
389
|
else:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if
|
|
323
|
-
|
|
324
|
-
if
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
eq
|
|
328
|
-
if
|
|
329
|
-
|
|
330
|
-
if eq.name == "f_abs" and eq.children[
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if compute(eq.children[0]) > 0.00001:
|
|
334
|
-
eq = eq.children[0]
|
|
335
|
-
elif compute(eq.children[0]) < 0.00001:
|
|
336
|
-
eq = -eq.children[0]
|
|
337
|
-
if eq.name == "f_pow" and eq.children[0].name[:2] == "d_" and frac(eq.children[1]) is not None:
|
|
338
|
-
f = frac(eq.children[1])
|
|
339
|
-
if f.denominator != 1:
|
|
340
|
-
return helper(eq.children[0]**tree_form("d_"+str(f.numerator)))**(tree_form("d_"+str(f.denominator))**-1)
|
|
390
|
+
# Root node fully processed
|
|
391
|
+
return node
|
|
392
|
+
|
|
393
|
+
else:
|
|
394
|
+
# Push current node back for next child
|
|
395
|
+
stack.append((node, child_index, processed_children))
|
|
396
|
+
# Push next child to stack
|
|
397
|
+
child = node.children[child_index]
|
|
398
|
+
stack.append((child, 0, []))
|
|
399
|
+
|
|
400
|
+
def other_node(eq):
|
|
401
|
+
if eq is None:
|
|
402
|
+
return None
|
|
403
|
+
if eq.name == "f_log":
|
|
404
|
+
if len(eq.children) == 1:
|
|
405
|
+
if eq.children[0].name == "d_1":
|
|
406
|
+
return tree_form("d_0")
|
|
407
|
+
if eq.children[0].name == "s_e":
|
|
408
|
+
return tree_form("d_1")
|
|
409
|
+
if eq.name == "f_mul":
|
|
410
|
+
if tree_form("d_1") in eq.children:
|
|
411
|
+
return product([remove_extra(child) for child in eq.children if child != tree_form("d_1")])
|
|
412
|
+
if eq.name == "f_pow" and len(eq.children) == 2:
|
|
413
|
+
a, b = frac(eq.children[0]), frac(eq.children[1])
|
|
414
|
+
if a is not None and b is not None and a == 0 and b < 0:
|
|
415
|
+
return None
|
|
416
|
+
if eq.children[1].name == "d_0":
|
|
417
|
+
return tree_form("d_1")
|
|
418
|
+
if eq.children[1].name == "d_1":
|
|
419
|
+
return eq.children[0]
|
|
420
|
+
if eq.children[0].name == "d_1":
|
|
421
|
+
return tree_form("d_1")
|
|
422
|
+
if eq.children[0].name == "f_abs" and eq.children[1].name.startswith("d_")\
|
|
423
|
+
and int(eq.children[1].name[2:]) % 2 == 0:
|
|
424
|
+
return eq.children[0].children[0] ** eq.children[1]
|
|
341
425
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
426
|
+
if eq.children[0].name == "f_mul":
|
|
427
|
+
n = frac(eq.children[1])
|
|
428
|
+
if n is not None and n < 0 and n.numerator % 2 == 1 and n.denominator == 1:
|
|
429
|
+
n2 = frac_to_tree(-n)
|
|
430
|
+
if n2 == tree_form("d_1"):
|
|
431
|
+
return product([child**-1 for child in eq.children[0].children])
|
|
432
|
+
return product([child**-1 for child in eq.children[0].children]) ** n2
|
|
433
|
+
if frac(eq.children[1]) == Fraction(1,2):
|
|
434
|
+
d = frac(eq.children[0])
|
|
435
|
+
if d is not None and d < 0:
|
|
436
|
+
return tree_form("s_i")*(frac_to_tree(-d)**eq.children[1])
|
|
437
|
+
if eq.children[0].name == "f_pow":
|
|
438
|
+
b = eq.children[0].children[1]
|
|
439
|
+
c = eq.children[1]
|
|
440
|
+
out = frac(b*c)
|
|
441
|
+
if out is not None:
|
|
442
|
+
out2 = frac(b)
|
|
443
|
+
if out.numerator % 2 == 0 or (out2 is not None and out2.numerator % 2 != 0):
|
|
444
|
+
return eq.children[0].children[0] ** (b*c)
|
|
445
|
+
else:
|
|
446
|
+
return eq.children[0].children[0].fx("abs") ** (b*c)
|
|
447
|
+
else:
|
|
448
|
+
tmp = compute(eq.children[0].children[0])
|
|
449
|
+
if (tmp is not None and tmp > 0) or eq.children[0].children[0].name == "f_abs":
|
|
450
|
+
return eq.children[0].children[0] ** (b*c)
|
|
451
|
+
c = frac(eq)
|
|
452
|
+
if c is not None:
|
|
453
|
+
c = frac_to_tree(c)
|
|
454
|
+
if c != eq:
|
|
455
|
+
return c
|
|
456
|
+
if eq.name == "f_pow" and eq.children[0] == tree_form("s_i") and eq.children[1].name.startswith("d_"):
|
|
457
|
+
n = int(eq.children[1].name[2:])
|
|
458
|
+
if n % 4 == 0:
|
|
459
|
+
return tree_form("d_1")
|
|
460
|
+
if n % 4 == 1:
|
|
461
|
+
return tree_form("s_i")
|
|
462
|
+
if n % 4 == 2:
|
|
463
|
+
return tree_form("d_-1")
|
|
464
|
+
if n % 4 == 3:
|
|
465
|
+
return -tree_form("s_i")
|
|
466
|
+
if eq.name == "f_pow" and eq.children[0].name == "s_e":
|
|
467
|
+
if eq.children[1].name == "f_log":
|
|
468
|
+
return eq.children[1].children[0]
|
|
469
|
+
if eq.children[1].name == "f_mul":
|
|
470
|
+
lst = factor_generation(eq.children[1])
|
|
471
|
+
log = None
|
|
472
|
+
for i in range(len(lst)-1,-1,-1):
|
|
473
|
+
if lst[i].name == "f_log":
|
|
474
|
+
log = lst[i]
|
|
475
|
+
lst.pop(i)
|
|
476
|
+
break
|
|
477
|
+
if log is not None:
|
|
478
|
+
return log.children[0] ** product(lst)
|
|
479
|
+
for index, child in enumerate(eq.children):
|
|
480
|
+
out = other_node(child)
|
|
481
|
+
if out is None:
|
|
482
|
+
return None
|
|
483
|
+
eq.children[index] = out
|
|
484
|
+
return TreeNode(eq.name, eq.children)
|
|
485
|
+
def cancel(eq):
|
|
486
|
+
n, d = num_dem(eq)
|
|
487
|
+
d = simplify(d)
|
|
488
|
+
if d != tree_form("d_1"):
|
|
489
|
+
n = simplify(n)
|
|
490
|
+
a = Counter(factor_generation(n))
|
|
491
|
+
b = Counter(factor_generation(d))
|
|
492
|
+
c = a & b
|
|
493
|
+
a = simplify(product(list(a-c)))
|
|
494
|
+
b = simplify(product(list(b-c)))
|
|
495
|
+
if b == tree_form("d_1"):
|
|
496
|
+
return a
|
|
497
|
+
if a == tree_form("d_1"):
|
|
498
|
+
return b ** -1
|
|
499
|
+
return a/b
|
|
500
|
+
return TreeNode(eq.name, [cancel(child) for child in eq.children])
|
|
501
|
+
def solve3(eq):
|
|
502
|
+
a = lambda x: multiply_node(x)
|
|
503
|
+
b = lambda x: addition_node(x)
|
|
504
|
+
c = lambda x: other_node(x)
|
|
505
|
+
return dowhile(eq, lambda x: flatten_tree(c(b(a(x)))))
|
|
506
|
+
|
|
507
|
+
def simplify(eq, basic=True):
|
|
508
|
+
if eq is None:
|
|
509
|
+
return None
|
|
510
|
+
orig = TreeNode.matmul
|
|
511
|
+
if TreeNode.matmul == True:
|
|
512
|
+
TreeNode.matmul = False
|
|
513
|
+
if TreeNode.matmul == False:
|
|
514
|
+
eq = use(tree_form(str_form(eq).replace("f_w","f_")))
|
|
345
515
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
for j in range(len(lst)):
|
|
358
|
-
if i ==j or j in rm:
|
|
359
|
-
continue
|
|
360
|
-
if solve(helper2(lst[i]**-1)) == lst[j]:
|
|
361
|
-
rm += [i,j]
|
|
362
|
-
break
|
|
363
|
-
if rm != []:
|
|
364
|
-
for item in sorted(rm)[::-1]:
|
|
365
|
-
lst.pop(item)
|
|
366
|
-
return product(lst)
|
|
367
|
-
return TreeNode(eq.name, [helper6(child) for child in eq.children])
|
|
368
|
-
def helper5(eq):
|
|
516
|
+
if eq.name == "f_and" or eq.name == "f_not" or eq.name == "f_or":
|
|
517
|
+
new_children = []
|
|
518
|
+
for child in eq.children:
|
|
519
|
+
new_children.append(simplify(child))
|
|
520
|
+
TreeNode.matmul = orig
|
|
521
|
+
return TreeNode(eq.name, new_children)
|
|
522
|
+
if eq.name[2:] in "gt ge lt le eq".split(" "):
|
|
523
|
+
denom = eq.name != "f_eq"
|
|
524
|
+
tmp2 = simplify(eq.children[0] - eq.children[1])
|
|
525
|
+
tmp, denom = clear_div(tmp2, denom)
|
|
526
|
+
tmp = simplify(tmp)
|
|
369
527
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
eq = dowhile(eq, item)
|
|
384
|
-
return eq
|
|
385
|
-
def fx2(eq):
|
|
386
|
-
for item in [helper, helper3, helper4, helper6,helper7,helper2,helper5]:
|
|
387
|
-
eq = dowhile(eq, item)
|
|
388
|
-
return eq
|
|
389
|
-
def fx3(eq):
|
|
390
|
-
for item in [fx1, fx2]:
|
|
391
|
-
eq = dowhile(eq, item)
|
|
392
|
-
return eq
|
|
393
|
-
eq = dowhile(eq, fx3)
|
|
394
|
-
eq = dowhile(eq, helper8)
|
|
395
|
-
if error:
|
|
396
|
-
return None
|
|
397
|
-
return solve(eq)
|
|
398
|
-
|
|
528
|
+
value2 = eq.name[2:]
|
|
529
|
+
if denom is False:
|
|
530
|
+
value2 = {"ge":"le", "le":"ge", "gt":"lt", "lt":"gt", "eq":"eq"}[value2]
|
|
531
|
+
value2 = "f_"+value2
|
|
532
|
+
out = TreeNode(value2, [tmp, tree_form("d_0")])
|
|
533
|
+
TreeNode.matmul = orig
|
|
534
|
+
return out
|
|
535
|
+
eq = flatten_tree(eq)
|
|
536
|
+
if basic:
|
|
537
|
+
eq = convert_to_basic(eq)
|
|
538
|
+
eq = solve3(eq)
|
|
539
|
+
TreeNode.matmul = orig
|
|
540
|
+
return eq
|