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