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/tool.py ADDED
@@ -0,0 +1,163 @@
1
+ from .diff import diff
2
+ from .expand import expand
3
+ from .simplify import simplify
4
+ from .base import *
5
+ import math
6
+
7
+ def poly_div(dividend_coeffs, divisor_coeffs):
8
+ """
9
+ Perform polynomial division using coefficients with symbolic simplification.
10
+ """
11
+ # Deep copy inputs using copy_tree()
12
+ dividend = [item.copy_tree() for item in dividend_coeffs]
13
+ divisor = [item.copy_tree() for item in divisor_coeffs]
14
+
15
+ # Remove leading zeros
16
+ while len(dividend) > 1 and simplify(dividend[0]) == 0:
17
+ dividend.pop(0)
18
+ while len(divisor) > 1 and simplify(divisor[0]) == 0:
19
+ divisor.pop(0)
20
+
21
+ # Validate divisor
22
+ if len(divisor) == 0 or simplify(divisor[0]) == 0:
23
+ raise ValueError("Invalid divisor")
24
+
25
+ if len(dividend) < len(divisor):
26
+ return [tree_form("d_0")], [item.copy_tree() for item in dividend]
27
+
28
+ # Calculate degrees
29
+ deg_p = len(dividend) - 1
30
+ deg_q = len(divisor) - 1
31
+ deg_quot = deg_p - deg_q
32
+
33
+ # Initialize quotient (highest degree first)
34
+ quotient = [tree_form("d_0")] * (deg_quot + 1)
35
+
36
+ # Working dividend - keep original structure
37
+ working_dividend = [item.copy_tree() for item in dividend]
38
+
39
+ # Long division - align by current leading terms
40
+ for k in range(deg_quot, -1, -1):
41
+ # Remove leading zeros from working dividend
42
+ while len(working_dividend) > 1 and simplify(working_dividend[0]) == 0:
43
+ working_dividend.pop(0)
44
+
45
+ if len(working_dividend) == 0 or simplify(working_dividend[0]) == 0:
46
+ continue
47
+
48
+ # Calculate quotient term for degree k
49
+ leading_ratio = simplify(working_dividend[0] / divisor[0])
50
+ quotient[k] = leading_ratio
51
+
52
+ # Subtract leading_ratio * divisor (aligned at leading terms)
53
+ new_dividend = []
54
+ for i in range(max(len(working_dividend), len(divisor))):
55
+ dividend_term = working_dividend[i] if i < len(working_dividend) else tree_form("d_0")
56
+ divisor_term = simplify(leading_ratio * divisor[i]) if i < len(divisor) else tree_form("d_0")
57
+ result = simplify(dividend_term - divisor_term)
58
+ new_dividend.append(result)
59
+
60
+ working_dividend = new_dividend
61
+
62
+ # Remainder is terms with degree < deg_q (last deg_q terms of final dividend)
63
+ remainder = working_dividend[-(deg_q):] if len(working_dividend) > deg_q else working_dividend
64
+ while len(remainder) > 1 and simplify(remainder[0]) == 0:
65
+ remainder.pop(0)
66
+ if not remainder:
67
+ remainder = [tree_form("d_0")]
68
+
69
+ # Clean quotient trailing zeros
70
+ while len(quotient) > 1 and simplify(quotient[-1]) == 0:
71
+ quotient.pop()
72
+
73
+ return quotient, remainder
74
+
75
+ def unpoly(eq, var):
76
+ eq = eq[::-1]
77
+ eq = [simplify(item) for item in eq]
78
+ eq2 = copy.deepcopy([eq[i]*tree_form(var)**tree_form("d_"+str(i)) if i != 0 else eq[i] for i in range(len(eq))])
79
+ return summation(eq2)
80
+
81
+ def longdiv(p, q, p_min=0, q_min=0):
82
+ p, q = simplify(p), simplify(q)
83
+
84
+ var = set(vlist(p)) & set(vlist(q))
85
+ if len(var) > 0:
86
+ var = list(var)[0]
87
+ p = poly(p, var)
88
+ q = poly(q, var)
89
+ if p is not None and q is not None and len(p)-1>=p_min and len(q)-1>=q_min and len(p)<=len(q):
90
+ a, b = poly_div(p, q)
91
+ return unpoly(a, var), unpoly(b, var)
92
+ return None
93
+ def poly_simplify(eq):
94
+ a, b = num_dem(eq)
95
+ b = simplify(b)
96
+ if b != 1:
97
+ return simplify(poly_simplify(a)/poly_simplify(b))
98
+ for var in vlist(eq):
99
+ n = poly(eq, var, 20)
100
+ if n is not None:
101
+ return simplify(unpoly(n, var))
102
+ return TreeNode(eq.name, [poly_simplify(child) for child in eq.children])
103
+ def enclose_const(eq):
104
+ def req(eq, dic):
105
+ for key in dic.keys():
106
+ eq = replace(eq, dic[key], key)
107
+ return eq
108
+ alloclst = []
109
+ for i in range(0,26):
110
+ if "v_"+str(i) not in vlist(eq):
111
+ alloclst.append(tree_form("v_"+str(i)))
112
+ dic = {}
113
+ def helper(eq):
114
+ nonlocal alloclst, dic
115
+ if frac(eq) is not None:
116
+ return eq
117
+
118
+ if "v_" not in str_form(eq):
119
+ if eq not in dic.keys():
120
+ n = alloclst.pop(0)
121
+ dic[eq] = n
122
+ return dic[eq]
123
+ else:
124
+ if eq.name == "f_pow":
125
+ return TreeNode(eq.name, [helper(eq.children[0]), eq.children[1]])
126
+ return TreeNode(eq.name, [helper(child) for child in eq.children])
127
+ eq= helper(eq)
128
+ return eq, lambda x: req(x, dic)
129
+
130
+ def poly(eq, to_compute, m=10):
131
+ def substitute_val(eq, val, var="v_0"):
132
+ eq = replace(eq, tree_form(var), tree_form("d_"+str(val)))
133
+ return eq
134
+ def inv(eq):
135
+ if eq.name[:2] == "f_" and eq.name[2:] in "ref try integrate subs".split(" "):
136
+ return False
137
+ if eq.name[2:] in ["sin", "cos", "log"] and contain(eq.children[0], tree_form(to_compute)):
138
+ return False
139
+ if eq.name == "f_pow" and contain(eq.children[0], tree_form(to_compute)) and\
140
+ (frac(eq.children[1]) is None or frac(eq.children[1]) < 0 or frac(eq.children[1]).denominator != 1):
141
+ return False
142
+ if eq.name == "f_abs":
143
+ return False
144
+ if any(not inv(child) for child in eq.children):
145
+ return False
146
+ return True
147
+ if not inv(eq):
148
+ return None
149
+ out = []
150
+ eq2 = eq
151
+ for i in range(m):
152
+ out.append(expand(simplify(eq2)))
153
+ eq2 = diff(eq2, to_compute)
154
+ for i in range(len(out)-1,-1,-1):
155
+ if out[i] == tree_form("d_0"):
156
+ out.pop(i)
157
+ else:
158
+ break
159
+ final = []
160
+ for index, item in enumerate(out):
161
+ final.append(substitute_val(item, 0, to_compute)/tree_form("d_"+str(math.factorial(index))))
162
+
163
+ return [expand(simplify(item)) for item in final][::-1]
mathai/trig.py ADDED
@@ -0,0 +1,276 @@
1
+ import itertools
2
+ from .simplify import simplify
3
+ from .base import *
4
+ from .expand import expand
5
+ from .structure import transform_formula
6
+ from .parser import parse
7
+ trig_sin_table = {
8
+ (0,1): parse("0"),
9
+ (1,6): parse("1/2"),
10
+ (1,4): parse("2^(1/2)/2"), # π/4
11
+ (1,3): parse("3^(1/2)/2"), # π/3
12
+ (1,2): parse("1"), # π/2
13
+ (2,3): parse("3^(1/2)/2"), # 2π/3
14
+ (3,4): parse("2^(1/2)/2"), # 3π/4
15
+ (5,6): parse("1/2"), # 5π/6
16
+ (1,1): parse("0") # π
17
+ }
18
+ trig_cos_table = {
19
+ (0,1): parse("1"), # 0
20
+ (1,6): parse("3^(1/2)/2"), # π/6
21
+ (1,4): parse("2^(1/2)/2"), # π/4
22
+ (1,3): parse("1/2"), # π/3
23
+ (1,2): parse("0"), # π/2
24
+ (2,3): parse("-1/2"), # 2π/3
25
+ (3,4): parse("-2^(1/2)/2"), # 3π/4
26
+ (5,6): parse("-1/2"), # 5π/6
27
+ (1,1): parse("-1") # π
28
+ }
29
+
30
+ for key in trig_cos_table.keys():
31
+ trig_cos_table[key] = simplify(trig_cos_table[key])
32
+ for key in trig_sin_table.keys():
33
+ trig_sin_table[key] = simplify(trig_sin_table[key])
34
+ def trig0(eq):
35
+ if eq is None:
36
+ return None
37
+ def isneg(eq):
38
+ if eq.name[:2] != "d_":
39
+ return False
40
+ if int(eq.name[2:]) >= 0:
41
+ return False
42
+ return True
43
+ def single_pi(lst):
44
+ if tree_form("d_0") in lst:
45
+ return 0, 1
46
+ count = 0
47
+ for item in lst:
48
+ if item == tree_form("s_pi"):
49
+ count += 1
50
+ if count != 1:
51
+ return None
52
+ eq = simplify(product(lst)/tree_form("s_pi"))
53
+ out = frac(eq)
54
+ if out is None or out < 0:
55
+ return None
56
+ a,b= out.numerator, out.denominator
57
+ a %= 2*b
58
+ if a > b:
59
+ a = 2*b - a
60
+ return a, b
61
+ if eq.name == "f_arccosec":
62
+ return (1/eq.children[0]).fx("arcsin")
63
+
64
+ if eq.name == "f_arctan":
65
+ if eq.children[0].name == "d_0":
66
+ return tree_form("d_0")
67
+ if eq.name == "f_log":
68
+ if eq.children[0].name == "d_1":
69
+ return tree_form("d_0")
70
+ if eq.name=="f_tan":
71
+ if eq.children[0].name == "f_arctan":
72
+ return eq.children[0].children[0]
73
+ return eq.children[0].fx("sin")/eq.children[0].fx("cos")
74
+ if eq.name == "f_sec":
75
+ return eq.children[0].fx("cos")**-1
76
+ if eq.name == "f_cosec":
77
+ return eq.children[0].fx("sin")**-1
78
+ if eq.name == "f_cot":
79
+ return eq.children[0].fx("cos")/eq.children[0].fx("sin")
80
+
81
+ if eq.name == "f_sin":
82
+ if eq.children[0].name == "f_arcsin":
83
+ return eq.children[0].children[0]
84
+ lst = factor_generation(eq.children[0])
85
+ if any(isneg(item) for item in lst):
86
+ return -(eq.children[0]*-1).fx("sin")
87
+ out=single_pi(lst)
88
+ if out is not None and tuple(out) in trig_sin_table.keys():
89
+ return trig_sin_table[tuple(out)]
90
+
91
+ if eq.name == "f_cos":
92
+ if eq.children[0].name == "f_arccos":
93
+ return eq.children[0].children[0]
94
+ lst = factor_generation(eq.children[0])
95
+ if any(isneg(item) for item in lst):
96
+ return (eq.children[0]*-1).fx("cos")
97
+ out=single_pi(lst)
98
+ if out is not None:
99
+ if tuple(out) in trig_cos_table.keys():
100
+ return trig_cos_table[tuple(out)]
101
+ return TreeNode(eq.name, [trig0(child) for child in eq.children])
102
+ def cog(expr):
103
+ expr = TreeNode(expr.name, [product_to_sum(child) for child in expr.children])
104
+ expr = trig0(simplify(expr))
105
+ expr = expand(simplify(expr))
106
+ return expr
107
+ def product_to_sum(expr):
108
+ factors = factor_generation(expr)
109
+ other = []
110
+ lst = []
111
+ for item in factors:
112
+ if item.name in ["f_cos", "f_sin"]:
113
+ lst.append(item)
114
+ else:
115
+ other.append(item)
116
+ if len(lst) <= 1:
117
+
118
+ return dowhile(expr, cog)
119
+ if len(lst) == 2:
120
+ a, b = lst
121
+ out = None
122
+ if a.name < b.name:
123
+ a, b = b, a
124
+ A, B = a.children[0], b.children[0]
125
+ if a.name == "f_sin" and b.name == "f_sin":
126
+ out =((A - B).fx("cos") - (A + B).fx("cos")) / tree_form("d_2")
127
+ elif a.name == "f_cos" and b.name == "f_cos":
128
+ out =((A - B).fx("cos") + (A + B).fx("cos")) / tree_form("d_2")
129
+ elif a.name == "f_sin" and b.name == "f_cos":
130
+ out =((A + B).fx("sin") + (A - B).fx("sin")) / tree_form("d_2")
131
+
132
+ return out * product(other)
133
+
134
+ rest = tree_form("d_1")
135
+ if len(lst) % 2 == 1:
136
+ rest = lst.pop(0)
137
+ out = []
138
+ for i in range(0, len(lst), 2):
139
+ out.append(product_to_sum(product(lst[i:i+2])))
140
+ expr = product(out)*rest*product(other)
141
+
142
+ return dowhile(expr, cog)
143
+
144
+ def trig_formula_init():
145
+ var = ""
146
+ formula_list = [(f"A*sin(B)+C*sin(B)", f"(A^2+C^2)^(1/2)*sin(B+arctan(C/A))"),\
147
+ (f"sin(B+D)", f"sin(B)*cos(D)+cos(B)*sin(D)"),\
148
+ (f"cos(B+D)", f"cos(B)*cos(D)-sin(B)*sin(D)"),\
149
+ (f"cos(B)^2", f"1-sin(B)^2"),\
150
+ (f"1/cos(B)^2", f"1/(1-sin(B)^2)"),\
151
+ (f"cos(arcsin(B))", f"sqrt(1-B^2)"),\
152
+ (f"sin(arccos(B))", f"sqrt(1-B^2)"),\
153
+ (f"arccos(B)", f"pi/2-arcsin(B)"),\
154
+ (f"sin(arctan(B))", f"x/sqrt(1+x^2)"),\
155
+ (f"cos(arctan(B))", f"1/sqrt(1+x^2)")]
156
+ formula_list = [[simplify(parse(y)) for y in x] for x in formula_list]
157
+ expr = [[parse("A"), parse("1")], [parse("B")], [parse("C"), parse("1")], [parse("D")]]
158
+ return [formula_list, var, expr]
159
+ #formula_gen4 = trig_formula_init()
160
+ def trig3(eq):
161
+ def iseven(eq):
162
+ if eq.name[:2] != "d_":
163
+ return False
164
+ if int(eq.name[2:]) < 2 or int(eq.name[2:]) % 2 != 0:
165
+ return False
166
+ return True
167
+
168
+ if eq.name == "f_sin":
169
+ lst = factor_generation(eq.children[0])
170
+ if any(iseven(item) for item in lst):
171
+ eq= 2*(eq.children[0]/2).fx("sin")*(eq.children[0]/2).fx("cos")
172
+ if eq.name == "f_cos":
173
+ lst = factor_generation(eq.children[0])
174
+ if any(iseven(item) for item in lst):
175
+ eq = (eq.children[0]/2).fx("cos")**2-(eq.children[0]/2).fx("sin")**2
176
+ eq = expand(simplify(eq))
177
+ return TreeNode(eq.name, [trig3(child) for child in eq.children])
178
+ def noneg_pow(eq):
179
+ if eq.name == "f_pow" and frac(eq.children[1]) is not None and frac(eq.children[1])<0:
180
+ return (eq.children[0]**(simplify(-eq.children[1])))**-1
181
+ return TreeNode(eq.name, [noneg_pow(child) for child in eq.children])
182
+
183
+ def trig1(eq):
184
+ eq = noneg_pow(eq)
185
+ return product_to_sum(eq)
186
+
187
+ def trig4(eq):
188
+ done = False
189
+ def _trig4(eq, numer=True, chance="sin"):
190
+ nonlocal done
191
+ if eq.name == "f_sin":
192
+ if eq.children[0].name == "f_add" and len(eq.children[0].children)>=2:
193
+ r = len(eq.children[0].children)%2
194
+ a, b = TreeNode("f_add", eq.children[0].children[:round((len(eq.children[0].children)-r)/2)]),\
195
+ TreeNode("f_add", eq.children[0].children[round((len(eq.children[0].children)-r)/2):])
196
+ if len(a.children)==1:
197
+ a=a.children[0]
198
+ if len(b.children)==1:
199
+ b=b.children[0]
200
+ return a.fx("sin")*b.fx("cos") + a.fx("cos")*b.fx("sin")
201
+ if eq.children[0].name == "f_arccos":
202
+ a = eq.children[0].children[0]
203
+ return (1-a**2)**(tree_form("d_2")**-1)
204
+ if eq.children[0].name == "f_arctan":
205
+ a = eq.children[0].children[0]
206
+ return a/(1+a**2)**(tree_form("d_2")**-1)
207
+ if eq.name == "f_pow" and numer:
208
+ if eq.children[0].name == "f_cos" and chance == "cos":
209
+ a = eq.children[0].children[0]
210
+ if frac(eq.children[1]) == 2:
211
+ done = True
212
+ return 1 - a.fx("sin")**2
213
+ if eq.children[0].name == "f_sin" and chance == "cos":
214
+ a = eq.children[0].children[0]
215
+ if frac(eq.children[1]) == 2:
216
+ done = True
217
+ return 1 - a.fx("cos")**2
218
+ if eq.name == "f_cos":
219
+ if eq.children[0].name == "f_add" and len(eq.children[0].children)>=2:
220
+ r = len(eq.children[0].children)%2
221
+ a, b = TreeNode("f_add", eq.children[0].children[:round((len(eq.children[0].children)-r)/2)]),\
222
+ TreeNode("f_add", eq.children[0].children[round((len(eq.children[0].children)-r)/2):])
223
+ if len(a.children)==1:
224
+ a=a.children[0]
225
+ if len(b.children)==1:
226
+ b=b.children[0]
227
+ return a.fx("cos")*b.fx("cos") - a.fx("sin")*b.fx("sin")
228
+ if eq.children[0].name == "f_arcsin":
229
+ a = eq.children[0].children[0]
230
+ return (1-a**2)**(tree_form("d_2")**-1)
231
+ if eq.children[0].name == "f_arctan":
232
+ a = eq.children[0].children[0]
233
+ return tree_form("d_1")/(1+a**2)**(tree_form("d_2")**-1)
234
+
235
+ return TreeNode(eq.name, [_trig4(child, False, chance) if eq.name != "f_add" and\
236
+ (not numer or (eq.name == "f_pow" and frac(eq.children[1]) is not None and frac(eq.children[1]) < 0))\
237
+ else _trig4(child, True, chance) for child in eq.children])
238
+ eq= _trig4(eq)
239
+ if not done:
240
+ eq = _trig4(eq,"cos")
241
+ return eq
242
+ def trig2(eq):
243
+ # Base case: if not an addition, recurse into children
244
+ if eq.name != "f_add":
245
+ return TreeNode(eq.name, [trig2(child) for child in eq.children])
246
+
247
+ # Try all pairs in the addition
248
+ for i, j in itertools.combinations(range(len(eq.children)), 2):
249
+ c1, c2 = eq.children[i], eq.children[j]
250
+
251
+ # Combine only sin/sin or cos/cos
252
+ if c1.name in ["f_sin", "f_cos"] and c2.name in ["f_sin", "f_cos"]:
253
+ A, B = c1.children[0], c2.children[0]
254
+ rest = [eq.children[k] for k in range(len(eq.children)) if k not in (i, j)]
255
+ rest_tree = summation(rest) if rest else tree_form("d_0")
256
+
257
+ two = tree_form("d_2")
258
+
259
+ # sinA + sinB
260
+ if c1.name == "f_sin" and c2.name == "f_sin":
261
+ combined = two * ((A + B) / two).fx("sin") * ((A - B) / two).fx("cos")
262
+
263
+ # cosA + cosB
264
+ elif c1.name == "f_cos" and c2.name == "f_cos":
265
+ combined = two * ((A + B) / two).fx("cos") * ((A - B) / two).fx("cos")
266
+
267
+ # sinA + cosB (leave unchanged)
268
+ else:
269
+ continue
270
+
271
+ new_expr = rest_tree + combined
272
+ # Re-run trig2 in case there are more sin/cos sums to simplify
273
+ return trig2(new_expr)
274
+
275
+ # If no sin/cos pairs found, just recurse on children
276
+ return TreeNode(eq.name, [trig2(child) for child in eq.children])