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/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