mathai 0.6.0__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 +53 -0
- mathai/apart.py +142 -0
- mathai/base.py +419 -0
- mathai/bivariate_inequality.py +317 -0
- mathai/console.py +84 -0
- mathai/diff.py +68 -0
- mathai/expand.py +124 -0
- mathai/factor.py +304 -0
- mathai/fraction.py +103 -0
- mathai/integrate.py +459 -0
- mathai/inverse.py +65 -0
- mathai/limit.py +156 -0
- mathai/linear.py +165 -0
- mathai/logic.py +230 -0
- mathai/matrix.py +22 -0
- mathai/ode.py +124 -0
- mathai/parser.py +158 -0
- mathai/printeq.py +34 -0
- mathai/simplify.py +521 -0
- mathai/structure.py +103 -0
- mathai/tool.py +163 -0
- mathai/trig.py +276 -0
- mathai/univariate_inequality.py +458 -0
- mathai-0.6.0.dist-info/METADATA +234 -0
- mathai-0.6.0.dist-info/RECORD +27 -0
- mathai-0.6.0.dist-info/WHEEL +5 -0
- mathai-0.6.0.dist-info/top_level.txt +1 -0
mathai/simplify.py
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from .base import *
|
|
3
|
+
from fractions import Fraction
|
|
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)
|
|
34
|
+
else:
|
|
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])
|
|
64
|
+
else:
|
|
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 = {}
|
|
72
|
+
for child in eq.children:
|
|
73
|
+
power = tree_form("d_1")
|
|
74
|
+
con2 = ""
|
|
75
|
+
if child.name == "f_pow":
|
|
76
|
+
con2 = child.children[0]
|
|
77
|
+
power = child.children[1]
|
|
78
|
+
else:
|
|
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"):
|
|
91
|
+
continue
|
|
92
|
+
else:
|
|
93
|
+
eq3.children.append(kv ** tmp3)
|
|
94
|
+
|
|
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, [])
|
|
100
|
+
if eq.children == []:
|
|
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")
|
|
157
|
+
|
|
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]
|
|
188
|
+
else:
|
|
189
|
+
node = new_mul
|
|
190
|
+
|
|
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))
|
|
196
|
+
else:
|
|
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_"):
|
|
208
|
+
return eq
|
|
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
|
|
220
|
+
|
|
221
|
+
lst = {}
|
|
222
|
+
for child in eq.children:
|
|
223
|
+
power = TreeNode("f_mul", [])
|
|
224
|
+
con2 = None
|
|
225
|
+
con3 = TreeNode("f_mul", [])
|
|
226
|
+
power2 = None
|
|
227
|
+
|
|
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
|
|
242
|
+
else:
|
|
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]
|
|
249
|
+
else:
|
|
250
|
+
power2 = power
|
|
251
|
+
|
|
252
|
+
if con2 in lst.keys():
|
|
253
|
+
lst[con2] = lst[con2] + power2
|
|
254
|
+
else:
|
|
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]
|
|
341
|
+
else:
|
|
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]
|
|
374
|
+
else:
|
|
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))
|
|
382
|
+
else:
|
|
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:
|
|
395
|
+
return None
|
|
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 is not None and 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
|
+
for index, child in enumerate(eq.children):
|
|
473
|
+
out = other_node(child)
|
|
474
|
+
if out is None:
|
|
475
|
+
return None
|
|
476
|
+
eq.children[index] = out
|
|
477
|
+
return TreeNode(eq.name, eq.children)
|
|
478
|
+
def cancel(eq):
|
|
479
|
+
n, d = num_dem(eq)
|
|
480
|
+
d = simplify(d)
|
|
481
|
+
if d != tree_form("d_1"):
|
|
482
|
+
n = simplify(n)
|
|
483
|
+
a = Counter(factor_generation(n))
|
|
484
|
+
b = Counter(factor_generation(d))
|
|
485
|
+
c = a & b
|
|
486
|
+
a = simplify(product(list(a-c)))
|
|
487
|
+
b = simplify(product(list(b-c)))
|
|
488
|
+
if b == tree_form("d_1"):
|
|
489
|
+
return a
|
|
490
|
+
if a == tree_form("d_1"):
|
|
491
|
+
return b ** -1
|
|
492
|
+
return a/b
|
|
493
|
+
return TreeNode(eq.name, [cancel(child) for child in eq.children])
|
|
494
|
+
def solve3(eq):
|
|
495
|
+
a = lambda x: multiply_node(x)
|
|
496
|
+
b = lambda x: addition_node(x)
|
|
497
|
+
c = lambda x: other_node(x)
|
|
498
|
+
return dowhile(eq, lambda x: flatten_tree(c(b(a(x)))))
|
|
499
|
+
|
|
500
|
+
def simplify(eq, basic=True):
|
|
501
|
+
if eq is None:
|
|
502
|
+
return None
|
|
503
|
+
if eq.name == "f_and" or eq.name == "f_not" or eq.name == "f_or":
|
|
504
|
+
new_children = []
|
|
505
|
+
for child in eq.children:
|
|
506
|
+
new_children.append(simplify(child))
|
|
507
|
+
return TreeNode(eq.name, new_children)
|
|
508
|
+
if eq.name[2:] in "gt ge lt le eq".split(" "):
|
|
509
|
+
denom = eq.name != "f_eq"
|
|
510
|
+
tmp2 = simplify(eq.children[0] - eq.children[1])
|
|
511
|
+
tmp, denom = clear_div(tmp2, denom)
|
|
512
|
+
tmp = simplify(tmp)
|
|
513
|
+
value2 = eq.name[2:]
|
|
514
|
+
if denom is False:
|
|
515
|
+
value2 = {"ge":"le", "le":"ge", "gt":"lt", "lt":"gt", "eq":"eq"}[value2]
|
|
516
|
+
value2 = "f_"+value2
|
|
517
|
+
return TreeNode(value2, [tmp, tree_form("d_0")])
|
|
518
|
+
eq = flatten_tree(eq)
|
|
519
|
+
if basic:
|
|
520
|
+
eq = convert_to_basic(eq)
|
|
521
|
+
return solve3(eq)
|
mathai/structure.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
from .simplify import simplify
|
|
3
|
+
from .base import *
|
|
4
|
+
|
|
5
|
+
def structure(equation, formula, formula_out=None, only_const=False):
|
|
6
|
+
varlist = {}
|
|
7
|
+
def helper(equation, formula):
|
|
8
|
+
nonlocal varlist
|
|
9
|
+
if formula.name[:2] == "v_" and int(formula.name[2:])< 0:
|
|
10
|
+
if formula.name in varlist.keys():
|
|
11
|
+
return varlist[formula.name] == equation
|
|
12
|
+
else:
|
|
13
|
+
varlist[formula.name] = equation
|
|
14
|
+
return True
|
|
15
|
+
if equation.name != formula.name:
|
|
16
|
+
return False
|
|
17
|
+
if len(equation.children) != len(formula.children):
|
|
18
|
+
return False
|
|
19
|
+
return all(helper(equation.children[i], formula.children[i]) for i in range(len(equation.children)))
|
|
20
|
+
def lst(formula):
|
|
21
|
+
out = set()
|
|
22
|
+
|
|
23
|
+
formula = conversion(formula)
|
|
24
|
+
def helper(node):
|
|
25
|
+
if not node.children:
|
|
26
|
+
return [node]
|
|
27
|
+
child_groups = [tuple(node.children)]
|
|
28
|
+
if node.name in ["f_addw", "f_mulw"]:
|
|
29
|
+
child_groups = list(itertools.permutations(node.children))
|
|
30
|
+
results = []
|
|
31
|
+
for children in child_groups:
|
|
32
|
+
child_perms = [helper(child) for child in children]
|
|
33
|
+
for combo in itertools.product(*child_perms):
|
|
34
|
+
results.append(TreeNode(node.name, list(combo)))
|
|
35
|
+
return results
|
|
36
|
+
def conversionrev(node):
|
|
37
|
+
if node.name == "f_addw":
|
|
38
|
+
node.name = "f_add"
|
|
39
|
+
elif node.name == "f_mulw":
|
|
40
|
+
node.name = "f_mul"
|
|
41
|
+
return TreeNode(node.name, [conversionrev(child) for child in node.children])
|
|
42
|
+
for tree in helper(formula):
|
|
43
|
+
out.add(tree)
|
|
44
|
+
return list(out)
|
|
45
|
+
def conversion(node):
|
|
46
|
+
if node.name == "f_add":
|
|
47
|
+
node.name = "f_addw"
|
|
48
|
+
elif node.name == "f_mul":
|
|
49
|
+
node.name = "f_mulw"
|
|
50
|
+
return TreeNode(node.name, [conversion(child) for child in node.children])
|
|
51
|
+
def conversionrev(node):
|
|
52
|
+
if node.name == "f_addw":
|
|
53
|
+
node.name = "f_add"
|
|
54
|
+
elif node.name == "f_mulw":
|
|
55
|
+
node.name = "f_mul"
|
|
56
|
+
return TreeNode(node.name, [conversionrev(child) for child in node.children])
|
|
57
|
+
equation = conversion(equation)
|
|
58
|
+
if formula_out is not None:
|
|
59
|
+
formula_out = conversion(formula_out)
|
|
60
|
+
for item in lst(formula):
|
|
61
|
+
varlist = {}
|
|
62
|
+
if helper(equation, item):
|
|
63
|
+
if only_const and any("v_" in str_form(varlist[key]) for key in varlist.keys()):
|
|
64
|
+
continue
|
|
65
|
+
if formula_out is None:
|
|
66
|
+
return varlist
|
|
67
|
+
for key in varlist.keys():
|
|
68
|
+
formula_out = replace(formula_out, tree_form(key), varlist[key])
|
|
69
|
+
|
|
70
|
+
return conversionrev(formula_out)
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
def transform_formula(equation, wrt, formula_list, var, expr):
|
|
74
|
+
|
|
75
|
+
var2 = str(tree_form(wrt))
|
|
76
|
+
if var != var2:
|
|
77
|
+
formula_list = [[replace(y, tree_form("v_0"), tree_form(wrt)) for y in x] for x in formula_list]
|
|
78
|
+
expr = [[replace(item, tree_form("v_0"), tree_form(wrt)) for item in item2] for item2 in expr]
|
|
79
|
+
for item in formula_list:
|
|
80
|
+
item = list(item)
|
|
81
|
+
orig = copy.deepcopy(item)
|
|
82
|
+
for item2 in itertools.product(*expr):
|
|
83
|
+
for i in range(2):
|
|
84
|
+
for j in range(len(expr)):
|
|
85
|
+
item[i] = replace(item[i], expr[j][0], item2[j])
|
|
86
|
+
for i in range(2):
|
|
87
|
+
item[i] = simplify(item[i])
|
|
88
|
+
out = None
|
|
89
|
+
p = False
|
|
90
|
+
if var != "":
|
|
91
|
+
p = True
|
|
92
|
+
try:
|
|
93
|
+
out = structure(equation.copy_tree(), copy.deepcopy(item[0]), copy.deepcopy(item[1]), p)
|
|
94
|
+
if out is not None:
|
|
95
|
+
out = simplify(out)
|
|
96
|
+
|
|
97
|
+
except:
|
|
98
|
+
out = None
|
|
99
|
+
|
|
100
|
+
if out is not None:
|
|
101
|
+
return out
|
|
102
|
+
item = copy.deepcopy(orig)
|
|
103
|
+
return None
|