mathai 2.5.0__tar.gz → 2.5.2__tar.gz

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.
Files changed (46) hide show
  1. {mathai-2.5.0 → mathai-2.5.2}/PKG-INFO +1 -1
  2. {mathai-2.5.0 → mathai-2.5.2}/mathai/__init__.py +1 -5
  3. {mathai-2.5.0 → mathai-2.5.2}/mathai/benchmark.py +1 -1
  4. {mathai-2.5.0 → mathai-2.5.2}/mathai/decision.py +1 -1
  5. mathai-2.5.2/mathai/factor.py +66 -0
  6. {mathai-2.5.0 → mathai-2.5.2}/mathai/integrate.py +3 -5
  7. {mathai-2.5.0 → mathai-2.5.2}/mathai/linear.py +1 -1
  8. {mathai-2.5.0 → mathai-2.5.2}/mathai/logic.py +1 -1
  9. mathai-2.5.2/mathai/ode.py +302 -0
  10. {mathai-2.5.0 → mathai-2.5.2}/mathai/pde.py +1 -0
  11. {mathai-2.5.0 → mathai-2.5.2}/mathai/printeq.py +0 -1
  12. {mathai-2.5.0 → mathai-2.5.2}/mathai/trig.py +0 -1
  13. {mathai-2.5.0 → mathai-2.5.2}/mathai.egg-info/PKG-INFO +1 -1
  14. {mathai-2.5.0 → mathai-2.5.2}/setup.py +1 -1
  15. mathai-2.5.0/mathai/factor.py +0 -187
  16. mathai-2.5.0/mathai/ode.py +0 -296
  17. {mathai-2.5.0 → mathai-2.5.2}/README.md +0 -0
  18. {mathai-2.5.0 → mathai-2.5.2}/mathai/apart.py +0 -0
  19. {mathai-2.5.0 → mathai-2.5.2}/mathai/base.py +0 -0
  20. {mathai-2.5.0 → mathai-2.5.2}/mathai/diff.py +0 -0
  21. {mathai-2.5.0 → mathai-2.5.2}/mathai/electronic.py +0 -0
  22. {mathai-2.5.0 → mathai-2.5.2}/mathai/expand.py +0 -0
  23. {mathai-2.5.0 → mathai-2.5.2}/mathai/formula_applier.py +0 -0
  24. {mathai-2.5.0 → mathai-2.5.2}/mathai/formula_compiler.py +0 -0
  25. {mathai-2.5.0 → mathai-2.5.2}/mathai/fraction.py +0 -0
  26. {mathai-2.5.0 → mathai-2.5.2}/mathai/graphics.py +0 -0
  27. {mathai-2.5.0 → mathai-2.5.2}/mathai/inverse.py +0 -0
  28. {mathai-2.5.0 → mathai-2.5.2}/mathai/lambda_calculus.py +0 -0
  29. {mathai-2.5.0 → mathai-2.5.2}/mathai/limit.py +0 -0
  30. {mathai-2.5.0 → mathai-2.5.2}/mathai/matrix.py +0 -0
  31. {mathai-2.5.0 → mathai-2.5.2}/mathai/ml.py +0 -0
  32. {mathai-2.5.0 → mathai-2.5.2}/mathai/mlscalar.py +0 -0
  33. {mathai-2.5.0 → mathai-2.5.2}/mathai/ncert_data.txt +0 -0
  34. {mathai-2.5.0 → mathai-2.5.2}/mathai/ncert_data_optimized.txt +0 -0
  35. {mathai-2.5.0 → mathai-2.5.2}/mathai/parser.py +0 -0
  36. {mathai-2.5.0 → mathai-2.5.2}/mathai/qm.py +0 -0
  37. {mathai-2.5.0 → mathai-2.5.2}/mathai/simplify.py +0 -0
  38. {mathai-2.5.0 → mathai-2.5.2}/mathai/square_root.py +0 -0
  39. {mathai-2.5.0 → mathai-2.5.2}/mathai/statistics.py +0 -0
  40. {mathai-2.5.0 → mathai-2.5.2}/mathai/test.py +0 -0
  41. {mathai-2.5.0 → mathai-2.5.2}/mathai/tool.py +0 -0
  42. {mathai-2.5.0 → mathai-2.5.2}/mathai/univariate_inequality.py +0 -0
  43. {mathai-2.5.0 → mathai-2.5.2}/mathai.egg-info/SOURCES.txt +0 -0
  44. {mathai-2.5.0 → mathai-2.5.2}/mathai.egg-info/dependency_links.txt +0 -0
  45. {mathai-2.5.0 → mathai-2.5.2}/mathai.egg-info/top_level.txt +0 -0
  46. {mathai-2.5.0 → mathai-2.5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mathai
3
- Version: 2.5.0
3
+ Version: 2.5.2
4
4
  Summary: Mathematics solving Ai tailored to NCERT
5
5
  Home-page: https://github.com/infinity390/mathai
6
6
  Requires-Python: >=3.7
@@ -44,11 +44,7 @@ from .integrate import normalize as integration_basic
44
44
 
45
45
  from .diff import diff, diff2
46
46
 
47
- from .factor import factor as factor1
48
- from .factor import factor2, take_common
49
- from .factor import rationalize_sqrt as rationalize
50
- from .factor import merge_sqrt
51
- from .factor import factorconst as factor0
47
+ from .factor import factor0, factor1, factor2
52
48
 
53
49
  from .fraction import fraction, fraction0
54
50
 
@@ -200,7 +200,7 @@ def execute_parallel(tree, equation, terminal_condition=None):
200
200
 
201
201
 
202
202
  def god2(string, parallel=False):
203
- tree = strategy_tree(load_sequences("ncert_data"))
203
+ tree = strategy_tree(load_sequences("ncert_data_optimized"))
204
204
  if parallel:
205
205
  out = execute_parallel(tree, simplify(parse(string)), _no_integrate_left)
206
206
  else:
@@ -11,7 +11,7 @@ from .univariate_inequality import simple_wavycurvy, wavycurvy, prepare, absolut
11
11
  from .fraction import fraction
12
12
  from .expand import expand
13
13
  from .logic import logic0, set_sub, truth_gen, solve_logically
14
- from .factor import factor2, factor
14
+ from .factor import factor2, factor1, factor0
15
15
  from .limit import limit1, limit5
16
16
  from .linear import linear_solve
17
17
  from .matrix import matrix_solve, matrix_formula
@@ -0,0 +1,66 @@
1
+
2
+ import math
3
+
4
+ from .base import * # tree_form, TreeNode, str_form, factor_generation, product, summation, partitions ...
5
+ from .simplify import simplify
6
+ from .expand import expand # noqa: F401
7
+ from .formula_compiler import formula_list_compiler
8
+
9
+ # Kept so that `from .factor import *` in other modules keeps working.
10
+ from .parser import parse # noqa: F401
11
+ from .simplify import convert_to_basic # noqa: F401
12
+ from .tool import poly # noqa: F401
13
+ from .fraction import fraction
14
+
15
+
16
+ factor2 = formula_list_compiler("poly(x,a,b,c) a*(x-(-b+sqrt(b^2-4*a*c))/(2*a))*(x-(-b-sqrt(b^2-4*a*c))/(2*a)) [] _ _ b^2-4*a*c _")
17
+ factor1_s = """x^2-y^2 (x+y)*(x-y) [] _ _ _ _
18
+ x^3-y^3 (x-y)*(x^2+x*y+y^2) [] _ _ _ _
19
+ x^3+y^3 (x+y)*(x^2-x*y+y^2) [] _ _ _ _
20
+ x*y+a*y+x+a (x+a)*(y+1) [[a,1]] _ _ _ _
21
+ a*b+a*c a*(b+c) [[b,1]] _ _ _ _
22
+ a*b*c+a*b*d a*b*(c+d) [] _ _ _ _
23
+ a*x+b*x (a+b)*x [] _ _ _ _
24
+ a*x+a*y a*(x+y) [] _ _ _ _
25
+ a*c+a*d+b*c+b*d (a+b)*(c+d) [] _ _ _ _
26
+ x^2+2*a*x+a^2 (x+a)^2 [] _ _ _ _
27
+ x^2-2*a*x+a^2 (x-a)^2 [] _ _ _ _
28
+ a*b*c+a*d a*(b*c+d) [] _ _ _ _"""
29
+ factor1 = formula_list_compiler(factor1_s)
30
+
31
+ def _coeff(term):
32
+ """Integer coefficient of a term (1 if none)."""
33
+ for item in factor_generation(term):
34
+ if item.name.startswith("d_"):
35
+ try:
36
+ return int(item.name[2:])
37
+ except ValueError:
38
+ return 1
39
+ return 1
40
+
41
+
42
+ def _hcf(numbers):
43
+ """gcd of the coefficients; negative only if every coefficient is negative."""
44
+ g = 0
45
+ for x in numbers:
46
+ g = math.gcd(g, x)
47
+ if g == 0:
48
+ return 1
49
+ if numbers and all(x < 0 for x in numbers):
50
+ return -g
51
+ return g
52
+
53
+
54
+ def _factorconst(eq):
55
+ if not eq.children:
56
+ return eq
57
+ if eq.name == "f_add":
58
+ n = _hcf([_coeff(child) for child in eq.children])
59
+ if n != 1:
60
+ const = tree_form("d_" + str(n))
61
+ return const * TreeNode(eq.name, [child / const for child in eq.children])
62
+ return TreeNode(eq.name, [_factorconst(child) for child in eq.children])
63
+
64
+
65
+ def factor0(eq):
66
+ return simplify(_factorconst(eq))
@@ -1,6 +1,4 @@
1
- from .factor import factor2
2
- from .factor import factorconst as factor0
3
- from .factor import factor as factor1
1
+ from .factor import factor2, factor1, factor0
4
2
  from .parser import parse
5
3
  import itertools
6
4
  from .diff import diff
@@ -289,14 +287,14 @@ def byparts(eq):
289
287
  return TreeNode("f_try", output)
290
288
  eq = eq2
291
289
  return TreeNode(eq.name, [byparts(child) for child in eq.children])
292
-
293
290
  integrate_fraction_s = """integrate(1/sqrt(poly(x,a,b,c)),x) 1/sqrt(a)*log(abs(sqrt(a)*x+b/(2*sqrt(a))+sqrt((sqrt(a)*x+b/(2*sqrt(a)))^2+(4*a*c-b^2)/(4*a)))) [] x [a,b,c] [a,4*a*c-b^2] _ [[a,0],[b^2-4*a*c,0]]
294
291
  integrate(1/sqrt(poly(x,a,b,c)),x) 1/sqrt(a)*log(abs(sqrt(a)*x+b/(2*sqrt(a))+sqrt((sqrt(a)*x+b/(2*sqrt(a)))^2-(b^2-4*a*c)/(4*a)))) [] x [a,b,c] [a,b^2-4*a*c] _ [[a,0],[4*a*c-b^2,0]]
295
292
  integrate(1/sqrt(poly(x,a,b,c)),x) 1/sqrt(-a)*arcsin((sqrt(-a)*x-b/(2*sqrt(-a)))/sqrt((4*a*c-b^2)/(4*a))) [] x [a,b,c] [4*a*c-b^2] a [[a,0],[b^2-4*a*c,0]]
296
293
  integrate(1/poly(x,a,b,c),x) 1/(sqrt(a)*sqrt((4*a*c-b^2)/(4*a)))*arctan((sqrt(a)*x+b/(2*sqrt(a)))/sqrt((4*a*c-b^2)/(4*a))) [] x [a,b,c] [a,4*a*c-b^2] _ [[a,0],[b^2-4*a*c,0]]
297
- integrate(1/poly(x,a,b,c),x) -1/(sqrt(-a)*sqrt((b^2-4*a*c)/(-4*a)))*arctan((sqrt(-a)*x-b/(2*sqrt(-a)))/sqrt((b^2-4*a*c)/(-4*a))) [] x [a,b,c] _ [a,b^2-4*a*c] [[a,0],[4*a*c-b^2,0]]
294
+ integrate(1/poly(x,a,b,c),x) -1/(sqrt(-a)*sqrt((4*a*c-b^2)/(-4*a)))*arctan((sqrt(-a)*x-b/(2*sqrt(-a)))/sqrt((4*a*c-b^2)/(-4*a))) [] x [a,b,c] [4*a*c-b^2] a [[a,0],[b^2-4*a*c,0]]
298
295
  integrate(poly(x,p,q)/sqrt(poly(x,a,b,c)),x) (p/a)*sqrt(a*x^2+b*x+c)+(q-p*b/(2*a))*integrate(1/sqrt(a*x^2+b*x+c),x) [] x [p,q,a,b,c] _ _ _ [[a,0]]
299
296
  integrate(poly(x,p,q)/poly(x,a,b,c),x) (p/(2*a))*log(abs(a*x^2+b*x+c))+(q-p*b/(2*a))*integrate(1/(a*x^2+b*x+c),x) [] x [p,q,a,b,c] _ _ _ [[a,0]]"""
297
+
300
298
  integrate_fraction = formula_list_compiler(integrate_fraction_s)
301
299
 
302
300
  def_integrate_formula_s = """integrate(abs(poly(x,a,b)),x,m,n) ((a*n+b)*abs(a*n+b)-(a*m+b)*abs(a*m+b))/(2*a) [] x [a,b] _ _
@@ -6,7 +6,7 @@ from .simplify import simplify
6
6
  from .fraction import fraction
7
7
  from .expand import expand
8
8
  from .base import *
9
- from .factor import factorconst
9
+
10
10
  from .tool import poly
11
11
 
12
12
  def ss(eq):
@@ -4,7 +4,7 @@ from .base import *
4
4
  from .fraction import fraction
5
5
  from .simplify import simplify
6
6
  from .expand import expand
7
- from .factor import factorconst
7
+
8
8
  def set_sub(eq):
9
9
  if eq.name == "f_sub":
10
10
  return eq.children[0] & eq.children[1].fx("not")
@@ -0,0 +1,302 @@
1
+ from collections import Counter
2
+
3
+ from .base import * # tree_form, TreeNode, contain, replace, str_form, factor_generation, product, summation ...
4
+ from .factor import factor1, factor2 # factor1 = formula-list compiler (takes ONE expression, not a list)
5
+ from .fraction import fraction
6
+ from .simplify import simplify
7
+ from .inverse import inverse
8
+ from .parser import parse
9
+
10
+ # --------------------------------------------------------------------------- #
11
+ # Constants (built once)
12
+ # --------------------------------------------------------------------------- #
13
+ ZERO = tree_form("d_0")
14
+ ONE = tree_form("d_1")
15
+ TWO = tree_form("d_2")
16
+ NEG_ONE = tree_form("d_-1")
17
+ E = tree_form("s_e")
18
+ I = tree_form("s_i")
19
+ V0 = tree_form("v_0")
20
+ V1 = tree_form("v_1")
21
+ V2 = tree_form("v_2")
22
+ V = (V0, V1)
23
+ DIF0 = V0.fx("dif")
24
+ DIF1 = V1.fx("dif")
25
+ INV_DIF0 = DIF0 ** -1
26
+ INV_DIF1 = DIF1 ** -1
27
+
28
+ _MAX_DEPTH = 8 # bound on the shift/factor rewriting loop in inversediff
29
+
30
+
31
+ # --------------------------------------------------------------------------- #
32
+ # Small helpers
33
+ # --------------------------------------------------------------------------- #
34
+ def _has_name(node, name):
35
+ return node.name == name or any(_has_name(c, name) for c in node.children)
36
+
37
+
38
+ def _terms(eq):
39
+ return list(eq.children) if eq.name == "f_add" else [eq]
40
+
41
+
42
+ def _expr(eq):
43
+ """The 'expression = 0' side of an equation (or the expression itself)."""
44
+ if eq.name == "f_eq":
45
+ lhs, rhs = eq.children
46
+ return lhs if rhs == ZERO else lhs - rhs
47
+ return eq
48
+
49
+
50
+ def allocvar():
51
+ return tree_form("v_101")
52
+
53
+
54
+ def clist(x):
55
+ return list(x.elements())
56
+
57
+
58
+ def e0(eq):
59
+ return TreeNode("f_eq", [eq, ZERO])
60
+
61
+
62
+ def e1(eq):
63
+ return eq.children[0] if eq.name == "f_eq" else eq
64
+
65
+
66
+ # --------------------------------------------------------------------------- #
67
+ # Separation of variables
68
+ # --------------------------------------------------------------------------- #
69
+ def rev(eq):
70
+ factors = factor_generation(eq)
71
+ if INV_DIF0 in factors or INV_DIF1 in factors:
72
+ return False
73
+ return all(rev(child) for child in eq.children)
74
+
75
+
76
+ def check(eq):
77
+ """False if some single factor mixes x and y."""
78
+ return not any(contain(f, V0) and contain(f, V1) for f in factor_generation(eq))
79
+
80
+
81
+ def shift(lhs, rhs):
82
+ """Rearrange lhs + rhs = 0 into A(x) = B(y)-style sides; returns [new_lhs, new_rhs]."""
83
+ if isinstance(lhs, list):
84
+ lhs = simplify(summation(lhs))
85
+ if isinstance(rhs, list):
86
+ rhs = simplify(summation(rhs))
87
+ rhs = simplify(-rhs)
88
+ sides = [factor_generation(lhs), factor_generation(rhs)]
89
+ moved = [[], []] # moved[k] = factors that must be divided out of side k
90
+ for i in (0, 1):
91
+ stay = []
92
+ for f in sides[i]:
93
+ if contain(f, V[1 - i]) and not contain(f, V[i]):
94
+ moved[1 - i].append(f)
95
+ else:
96
+ stay.append(f)
97
+ sides[i] = stay
98
+ output = [simplify(product(sides[i]) * product(moved[i]) ** NEG_ONE) for i in (0, 1)]
99
+ if any(_has_name(item, "f_dif") for item in output) and DIF0 not in factor_generation(output[0]):
100
+ output = [ONE / item for item in output]
101
+ return output
102
+
103
+
104
+ def inversediff(lhs, rhs, old=None):
105
+ if lhs is None or rhs is None:
106
+ return None
107
+ old = [] if old is None else old
108
+ if len(old) > _MAX_DEPTH:
109
+ return None
110
+
111
+ eq = factor1(simplify(lhs - rhs))
112
+ if eq in old: # already seen -> rewriting is looping
113
+ return None
114
+
115
+ const, xonly, yonly, mixed = [], [], [], []
116
+ for item in _terms(eq):
117
+ cx, cy = contain(item, V0), contain(item, V1)
118
+ if cx and cy:
119
+ mixed.append(item)
120
+ elif cx:
121
+ xonly.append(item)
122
+ elif cy:
123
+ yonly.append(item)
124
+ else:
125
+ const.append(item)
126
+ (yonly if yonly else xonly).extend(const)
127
+
128
+ if not mixed:
129
+ return simplify(lhs - rhs) # already separated (unfactored form, so terms stay iterable)
130
+
131
+ m = factor1(summation(mixed)) # factor1 takes an expression, not a list
132
+ mixed = _terms(m)
133
+ nxt = old + [eq]
134
+
135
+ if xonly and yonly:
136
+ out = inversediff(*shift(mixed + xonly, yonly), nxt)
137
+ if out is None:
138
+ out = inversediff(*shift(xonly, factor1(summation(mixed + yonly))), nxt)
139
+ return out
140
+ if yonly:
141
+ if m.name == "f_add":
142
+ return None
143
+ return inversediff(*shift(m, yonly), nxt)
144
+ if xonly:
145
+ if m.name == "f_add":
146
+ return None
147
+ return inversediff(*shift(xonly, m), nxt)
148
+ if len(mixed) == 2:
149
+ return inversediff(*shift(*mixed), nxt)
150
+ return None
151
+
152
+
153
+ def epowersplit(eq):
154
+ if not eq.children:
155
+ return eq
156
+ if eq.name == "f_pow" and eq.children[1].name == "f_add":
157
+ return product([eq.children[0] ** child for child in eq.children[1].children])
158
+ return TreeNode(eq.name, [epowersplit(child) for child in eq.children])
159
+
160
+
161
+ def esolve(s):
162
+ if not s.children:
163
+ return s
164
+ if s.name == "f_add" and _has_name(s, "f_log"):
165
+ return product([E ** child for child in s.children]) - ONE
166
+ return TreeNode(s.name, [esolve(child) for child in s.children])
167
+
168
+
169
+ def groupe(eq):
170
+ eq = simplify(esolve(eq))
171
+ eq = simplify(fraction(eq))
172
+ return epowersplit(eq)
173
+
174
+
175
+ def diffsolve_sep(eq):
176
+ expr = e1(epowersplit(eq))
177
+ return inversediff(ZERO, expr.copy_tree())
178
+
179
+
180
+ def diffsolve_sep2(eq):
181
+ """Integrate a separated form f(x)dx + g(y)dy term by term."""
182
+ if eq is None:
183
+ return None
184
+ parts = [allocvar()]
185
+ for item in _terms(eq):
186
+ factors = factor_generation(simplify(item))
187
+ rest = product([k for k in factors if k.name != "f_dif"])
188
+ if DIF0 in factors:
189
+ parts.append(TreeNode("f_integrate", [rest, V0]))
190
+ elif DIF1 in factors:
191
+ parts.append(TreeNode("f_integrate", [rest, V1]))
192
+ else:
193
+ return None # a term with no differential cannot be integrated (was silently dropped)
194
+ return TreeNode("f_eq", [summation(parts), ZERO])
195
+
196
+
197
+ # --------------------------------------------------------------------------- #
198
+ # Derivative bookkeeping
199
+ # --------------------------------------------------------------------------- #
200
+ def order(eq, m=0):
201
+ if eq.name in ("f_pdif", "f_dif"):
202
+ return order(eq.children[0], m + 1)
203
+ return max([m] + [order(child, m) for child in eq.children])
204
+
205
+
206
+ def subs2(eq, orde):
207
+ if not eq.children:
208
+ return eq
209
+ if eq.name in ("f_dif", "f_pdif") and len(eq.children) == 2:
210
+ if orde == 1:
211
+ return eq.children[0].fx("dif") / eq.children[1].fx("dif")
212
+ return eq
213
+ return TreeNode(eq.name, [subs2(child, orde) for child in eq.children])
214
+
215
+
216
+ def subs3(eq):
217
+ """Push dif through sums and products (sum / product rule)."""
218
+ if not eq.children:
219
+ return eq
220
+ if eq.name == "f_dif":
221
+ inner = eq.children[0]
222
+ if inner.name == "f_add":
223
+ return summation([subs3(c.fx("dif")) for c in inner.children])
224
+ if inner.name == "f_mul":
225
+ kids = inner.children
226
+ return summation([
227
+ product([subs3(c.fx("dif")) if i == j else c for j, c in enumerate(kids)])
228
+ for i in range(len(kids))
229
+ ])
230
+ return TreeNode(eq.name, [subs3(child) for child in eq.children])
231
+
232
+
233
+ # --------------------------------------------------------------------------- #
234
+ # Coefficient collection
235
+ # --------------------------------------------------------------------------- #
236
+ def collect_term(eq, term_lst):
237
+ """Split eq into sum(coef[t] * t for t in term_lst) + other. Returns [coef_dict, other]."""
238
+ ordered = sorted(term_lst, key=lambda x: -len(factor_generation(x))) # most specific first
239
+ counters = [(t, Counter(factor_generation(t))) for t in ordered] # built once, not per term
240
+ parts = {t: [] for t, _ in counters}
241
+ other = []
242
+ for term in _terms(eq):
243
+ tc = Counter(factor_generation(term))
244
+ for t, c in counters:
245
+ if (tc & c) == c:
246
+ parts[t].append(product(clist(tc - c)))
247
+ break
248
+ else:
249
+ other.append(term)
250
+ coef = {t: (simplify(summation(p)) if p else ZERO) for t, p in parts.items()}
251
+ return [coef, simplify(summation(other)) if other else ZERO]
252
+
253
+ def linear_dif(eq, a, b):
254
+ """db + p*b*da - q*da = 0 -> integrating-factor solution."""
255
+ eq = simplify(eq)
256
+ db, da = b.fx("dif"), a.fx("dif")
257
+ bda = b * da
258
+ coef, other = collect_term(_expr(eq), [db, bda, da])
259
+ if other != ZERO:
260
+ return None
261
+ lead = coef[db]
262
+ if lead == ZERO:
263
+ return None
264
+ p = simplify(coef[bda] / lead)
265
+ q = simplify(-(coef[da] / lead))
266
+ if contain(p, b) or contain(q, b):
267
+ return None
268
+ f = E ** TreeNode("f_integrate", [p, a])
269
+ return simplify(TreeNode("f_eq", [b * f, TreeNode("f_integrate", [q * f, a]) + allocvar()]))
270
+
271
+
272
+ def diffsolve(eq):
273
+ orig = eq.copy_tree()
274
+ eq = subs2(eq, order(eq))
275
+ eq = fraction(simplify(fraction(eq)))
276
+ n = order(eq)
277
+
278
+ if n != 1: # only first order are handled
279
+ return orig
280
+
281
+ # 1) directly separable
282
+ sol = diffsolve_sep2(diffsolve_sep(eq))
283
+ if sol is not None:
284
+ return sol
285
+
286
+ # 2) homogeneous: substitute b = b*a, separate, substitute back
287
+ first = subs2(orig, 1) # same for both loop iterations
288
+ for i in (0, 1):
289
+ a, b = V[i], V[1 - i]
290
+ eq2 = subs3(replace(first, b, b * a))
291
+ eq2 = simplify(fraction(simplify(eq2)))
292
+ eq2 = diffsolve_sep2(diffsolve_sep(eq2))
293
+ if eq2 is not None:
294
+ return e0(TreeNode("f_subs", [replace(eq2.children[0], b, V2), V2, b / a]).fx("try"))
295
+
296
+ # 3) linear first order
297
+ eq = fraction(subs2(simplify(orig), 1))
298
+ for i in (0, 1):
299
+ out = linear_dif(eq, V[i], V[1 - i])
300
+ if out is not None:
301
+ return out
302
+ return orig
@@ -4,6 +4,7 @@ from .ode import diffsolve as ode_solve
4
4
  from .integrate import integrate_formula
5
5
  from .simplify import simplify
6
6
  from .formula_applier import formula_applier
7
+ from .formula_compiler import formula_list_compiler
7
8
 
8
9
  pde_character_s = """a*pdif(u,x)+b*pdif(u,y)+c=0 list(dif(x,y)=a/b,dif(u,x)=-c/a,dif(u,y)=-c/b,u) [[c,0],[a,1],[b,1]] [u,x,y] _ _ _"""
9
10
  pde_character = lambda x: formula_applier(x, pde_character_s)
@@ -1,5 +1,4 @@
1
1
  from .base import *
2
- from .factor import merge_sqrt
3
2
  from .simplify import simplify
4
3
  import copy
5
4
  from fractions import Fraction
@@ -7,7 +7,6 @@ from .base import *
7
7
  from .expand import expand
8
8
  from .parser import parse
9
9
  from .fraction import fraction
10
- from .factor import rationalize_sqrt
11
10
  from .inverse import inverse
12
11
  from .formula_compiler import formula_list_compiler
13
12
  from .univariate_inequality import simple_wavycurvy, eq2range, Range, range2eq2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mathai
3
- Version: 2.5.0
3
+ Version: 2.5.2
4
4
  Summary: Mathematics solving Ai tailored to NCERT
5
5
  Home-page: https://github.com/infinity390/mathai
6
6
  Requires-Python: >=3.7
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
3
3
 
4
4
  setup(
5
5
  name="mathai",
6
- version="2.5.0",
6
+ version="2.5.2",
7
7
  description="Mathematics solving Ai tailored to NCERT",
8
8
  long_description=Path("README.md").read_text(encoding="utf-8"),
9
9
  long_description_content_type="text/markdown",
@@ -1,187 +0,0 @@
1
- import itertools
2
- from .parser import parse
3
- from .base import *
4
- from .simplify import simplify, convert_to_basic
5
- from .expand import expand
6
- import math
7
- from .tool import poly
8
- from .fraction import fraction
9
- from collections import Counter
10
- from .formula_compiler import formula_list_compiler
11
-
12
- factor2 = formula_list_compiler("poly(x,a,b,c) a*(x-(-b+sqrt(b^2-4*a*c))/(2*a))*(x-(-b-sqrt(b^2-4*a*c))/(2*a)) [] _ _ b^2-4*a*c _")
13
- def multiset_intersection(*lists):
14
- counters = list(map(Counter, lists))
15
- common = counters[0]
16
- for c in counters[1:]:
17
- common = common & c
18
- return list(common.elements())
19
- def subtract_sublist(full_list, sublist):
20
- c_full = Counter(full_list)
21
- c_sub = Counter(sublist)
22
- result = c_full - c_sub
23
- tmp = list(result.elements())
24
- if tmp == []:
25
- return [tree_form("d_1")]
26
- return tmp
27
- def term_common2(lst, take_neg_common=False):
28
- s = []
29
- arr = [factor_generation(child,True) for child in lst]
30
- if take_neg_common and not all(tree_form("d_-1") not in item for item in arr):
31
- arr = [item+[tree_form("d_-1"), tree_form("d_-1")] if tree_form("d_-1") not in item else item for item in arr]
32
- s = multiset_intersection(*arr)
33
- if s == []:
34
- return simplify(summation(lst))
35
- arr = [subtract_sublist(item, s) for item in arr]
36
- return simplify(product(s)*summation([product(item) for item in arr]))
37
- def take_common(eq):
38
- score = []
39
- output = []
40
- def helper(eq, depth=0):
41
- nonlocal score
42
- nonlocal output
43
- key = tuple(sorted(eq,key=lambda x: str_form(x)))
44
- if key in output:
45
- return
46
- else:
47
- score.append(depth)
48
- output.append(key)
49
- for item3 in partitions(eq):
50
- for option in [False, True]:
51
- item = [term_common2(item2, option) for item2 in item3]
52
- helper(item, depth+1)
53
- if eq.name == "f_add":
54
- helper(eq.children)
55
- else:
56
- return [eq]
57
- sc = {}
58
- for i in range(len(output)):
59
- output[i] = simplify(summation(list(output[i])))
60
- sc[output[i]] = score[i]
61
- output = list(set(output))
62
- return list(sorted(output, key=lambda x: -sc[x]))
63
- def _factorconst(eq):
64
- def hcf_list(numbers):
65
- if not numbers:
66
- return None
67
- n = 1
68
- if math.prod(numbers) < 0:
69
- n = -1
70
- hcf = numbers[0]
71
- for num in numbers[1:]:
72
- hcf = math.gcd(hcf, abs(num))
73
- return hcf*n
74
- def extractnum(eq):
75
- lst = factor_generation(eq)
76
- for item in lst:
77
- if item.name[:2] == "d_":
78
- return int(item.name[2:])
79
- return 1
80
- n = 1
81
- if eq.name == "f_add":
82
- n = hcf_list([extractnum(child) for child in eq.children])
83
- eq = TreeNode(eq.name, [child/tree_form("d_"+str(n)) for child in eq.children])
84
- if n != 1:
85
- return tree_form("d_"+str(n))*eq
86
- return TreeNode(eq.name, [factorconst(child) for child in eq.children])
87
- def _merge_sqrt(eq):
88
- lst= []
89
- eq2 = []
90
- for child in factor_generation(eq):
91
- if frac(child) is not None and frac(child).denominator==1:
92
- if frac(child)>0:
93
- eq2.append(child**2)
94
- elif frac(child)!=-1:
95
- eq2.append((-child)**2)
96
- lst.append(tree_form("d_-1"))
97
- else:
98
- lst.append(tree_form("d_-1"))
99
- elif child.name == "f_pow" and frac(child.children[1]) == Fraction(1,2):
100
- eq2.append(child.children[0])
101
- else:
102
- lst.append(child)
103
- if len(eq2)>1:
104
- if lst == []:
105
- lst= [tree_form("d_1")]
106
- return simplify(product(eq2)**(tree_form("d_2")**-1)*product(lst))
107
- return TreeNode(eq.name, [_merge_sqrt(child) for child in eq.children])
108
- def sqrt_to_a_sqrt_b(n):
109
- if n == 0:
110
- return 0, 0
111
- sign = 1
112
- if n < 0:
113
- sign = -1
114
- m = -n
115
- else:
116
- m = n
117
- a = 1
118
- b = 1
119
- p = 2
120
- while p * p <= m:
121
- exp = 0
122
- while m % p == 0:
123
- m //= p
124
- exp += 1
125
- if exp:
126
- a *= p ** (exp // 2)
127
- if exp % 2 == 1:
128
- b *= p
129
- p += 1 if p == 2 else 2
130
- if m > 1:
131
- b *= m
132
- return sign * a, b
133
- def merge_sqrt(eq):
134
- def helper(eq):
135
- if eq.name == "f_pow" and frac(eq.children[1]) == Fraction(1,2):
136
- if eq.children[0].name[:2] == "d_":
137
- n = int(eq.children[0].name[2:])
138
- a, b =sqrt_to_a_sqrt_b(n)
139
- return tree_form("d_"+str(b))**(tree_form("d_2")**-1)*tree_form("d_"+str(a))
140
- if eq.name == "f_pow" and frac(eq.children[1]) == Fraction(-1,2):
141
- if frac(eq.children[0]) is not None:
142
- out = frac(eq.children[0])
143
- b, a = [tree_form("d_"+str(x)) for x in [out.numerator, out.denominator]]
144
- return a**(tree_form("d_2")**-1)/b**(tree_form("d_2")**-1)
145
- return TreeNode(eq.name, [helper(child) for child in eq.children])
146
- return helper(_merge_sqrt(eq))
147
- def rationalize_sqrt(eq):
148
- if eq.name== "f_pow" and frac(eq.children[1]) == Fraction(-1,2):
149
- eq = eq.children[0]**(tree_form("d_2")**-1)/eq.children[0]
150
- def term(eq):
151
- if eq.name == "f_add":
152
- output = []
153
- for child in eq.children:
154
- if any(child2.name == "f_pow" and frac(child2.children[1]) == Fraction(1,2) for child2 in factor_generation(child)):
155
- output.append(simplify(-child))
156
- else:
157
- output.append(child)
158
- return summation(output)
159
- return None
160
- n, d=num_dem(eq)
161
- n,d=simplify(n), simplify(d)
162
- if d != 1:
163
- t = term(d)
164
- if t is not None and t!=1:
165
- n,d=simplify(expand(simplify(n*t))),simplify(expand(simplify(d*t)))
166
- tmp= simplify(n/d)
167
- tmp = _merge_sqrt(tmp)
168
- return tmp
169
- return TreeNode(eq.name, [rationalize_sqrt(child) for child in eq.children])
170
- def factorconst(eq):
171
- return simplify(_factorconst(eq))
172
- def max_depth(node):
173
- if node is None:
174
- return 0
175
- if not node.children:
176
- return 1
177
- return 1 + max(max_depth(child) for child in node.children)
178
- def factor(equation):
179
- if equation.name == "f_add":
180
- lst = take_common(equation)
181
- lst3 = lst
182
- mx = min([len(item.children) for item in lst3])
183
- lst3 = [item for item in lst3 if len(item.children)==mx]
184
- tmp = max(lst3, key=lambda x: -max_depth(x))
185
- if equation != tmp:
186
- return tmp
187
- return TreeNode(equation.name, [factor(child) for child in equation.children])
@@ -1,296 +0,0 @@
1
- import itertools
2
- from collections import Counter
3
- from .diff import diff
4
- from .factor import factor, factor2, take_common, term_common2
5
- from .expand import expand
6
- from .base import *
7
- from .fraction import fraction
8
- from .simplify import simplify
9
- import copy
10
- from .inverse import inverse
11
- from .parser import parse
12
- def rev(eq):
13
- tmp = factor_generation(eq)
14
- if tree_form("v_0").fx("dif")**-1 in tmp or tree_form("v_1").fx("dif")**-1 in tmp:
15
- return False
16
- for child in eq.children:
17
- if not rev(child):
18
- return False
19
- return True
20
- def clr(eq):
21
- return simplify(product([item for item in factor_generation(eq) if "f_add" in str_form(item)]))
22
- def tt(eq):
23
- if eq.name == "f_add":
24
- return term_common2(eq.children)
25
- return eq
26
- def shift(lhs, rhs):
27
- if isinstance(lhs, list):
28
- lhs = simplify(summation(lhs))
29
- if isinstance(rhs, list):
30
- rhs = simplify(summation(rhs))
31
- rhs = simplify(-rhs)
32
- out2 = [factor_generation(lhs), factor_generation(rhs)]
33
- out = [[], []]
34
- for i in range(2):
35
- for j in range(len(out2[i])-1,-1,-1):
36
- if contain(out2[i][j], tree_form(f"v_{1-i}")) and not contain(out2[i][j], tree_form(f"v_{i}")):
37
- out[1-i].append(out2[i].pop(j))
38
- output = [simplify(product(out2[i])*product(out[i])**tree_form("d_-1")) for i in range(2)]
39
- if ("f_dif" in str_form(output[0]) or "f_dif" in str_form(output[1])) and tree_form("v_0").fx("dif") not in factor_generation(output[0]):
40
- output = [tree_form("d_1")/item for item in output]
41
- return output
42
- def check(eq):
43
- for item in factor_generation(eq):
44
- if contain(item, tree_form("v_0")) and contain(item, tree_form("v_1")) :
45
- return False
46
- return True
47
- def t(eq):
48
- eq2 = simplify(summation(eq))
49
- lst = take_common(eq2)
50
- for item in lst:
51
- if item.name == "f_add" and len(item.children) == 2:
52
- if all(check(child) for child in item.children):
53
- return item
54
- elif check(item):
55
- return item
56
- return eq2
57
- def inversediff(lhs, rhs, old=None):
58
- if old is None:
59
- old = []
60
- if lhs is None or rhs is None:
61
- return None
62
- eq = simplify(lhs - rhs)
63
- eq = tt(eq)
64
- eq = clr(eq)
65
- eq = t(eq.children)
66
- xonly = []
67
- yonly = []
68
- mixed = []
69
- const = []
70
- lst = []
71
- if eq.name != "f_add":
72
- lst.append(eq)
73
- else:
74
- lst = eq.children
75
- for item in lst:
76
- con_x = contain(item, tree_form("v_0"))
77
- con_y = contain(item, tree_form("v_1"))
78
- if not con_x and not con_y:
79
- const.append(item)
80
- elif con_x and con_y:
81
- mixed.append(item)
82
- elif con_x:
83
- xonly.append(item)
84
- elif con_y:
85
- yonly.append(item)
86
- if len(const) > 0:
87
- if len(yonly) > 0:
88
- yonly += const
89
- else:
90
- xonly += const
91
- if eq in old:
92
- return None
93
- if len(mixed) == 0:
94
- tmp = shift(lhs, -rhs)
95
- return simplify(lhs-rhs)
96
- elif len(mixed) > 0:
97
- m = t(mixed)
98
- if m.name == "f_add":
99
- mixed = m.children
100
- else:
101
- mixed = [m]
102
- if len(yonly) > 0 and len(xonly) > 0:
103
- tmp = inversediff(*(shift(t(mixed + xonly), yonly)+[old+[eq]]))
104
- if tmp is None:
105
- tmp = inversediff(*(shift(xonly, t(mixed + yonly))+[old+[eq]]))
106
- if tmp is None:
107
- return None
108
- return tmp
109
- elif len(yonly) > 0:
110
- if m.name == "f_add":
111
- return None
112
- return inversediff(*(shift(m, yonly)+[old+[eq]]))
113
- elif len(xonly) > 0:
114
- if m.name == "f_add":
115
- return None
116
- return inversediff(*(shift(xonly, m)+[old+[eq]]))
117
- else:
118
- if len(mixed) == 2:
119
- return inversediff(*(shift(*mixed)+[old+[eq]]))
120
- return None
121
- def allocvar():
122
- return tree_form("v_101")
123
- def epowersplit(eq):
124
- if eq.name == "f_pow" and eq.children[1].name == "f_add":
125
- return product([eq.children[0]**child for child in eq.children[1].children])
126
- return TreeNode(eq.name, [epowersplit(child) for child in eq.children])
127
- def esolve(s):
128
- if s.name == "f_add" and "f_log" in str_form(s):
129
- return product([tree_form("s_e")**child for child in s.children]) - tree_form("d_1")
130
- return TreeNode(s.name, [esolve(child) for child in s.children])
131
- def diffsolve_sep2(eq):
132
- lst = None
133
- if eq is None:
134
- return None
135
- if eq.name == "f_add":
136
- lst = list(eq.children)
137
- else:
138
- lst = [eq]
139
- s = [allocvar()]
140
- for item in lst:
141
- item = simplify(item)
142
- tmp = factor_generation(item)
143
- tmp2 = product([k for k in tmp if k.name != "f_dif"])
144
- if tree_form("v_0").fx("dif") in tmp:
145
- s.append(TreeNode("f_integrate", [tmp2, tree_form("v_0")]))
146
- elif tree_form("v_1").fx("dif") in tmp:
147
- s.append(TreeNode("f_integrate", [tmp2, tree_form("v_1")]))
148
- return TreeNode("f_eq", [summation(s), tree_form("d_0")])
149
- def e0(eq):
150
- return TreeNode("f_eq", [eq, tree_form("d_0")])
151
- def e1(eq):
152
- if eq.name == "f_eq":
153
- eq = eq.children[0]
154
- return eq
155
- def groupe(eq):
156
- eq = esolve(eq)
157
- eq = simplify(eq)
158
- eq = fraction(eq)
159
- eq = simplify(eq)
160
- eq = epowersplit(eq)
161
- return eq
162
- def diffsolve_sep(eq):
163
- eq = epowersplit(eq)
164
- eq = inversediff(tree_form("d_0"), eq.children[0].copy_tree())
165
- return eq
166
- def diffsolve(eq):
167
- orig = eq.copy_tree()
168
- # eq = diff2(eq)
169
- eq = subs2(eq, order(eq))
170
- eq = fraction(simplify(fraction(eq)))
171
- if order(eq) == 2:
172
- for i in range(2):
173
- out = second_order_dif(eq, tree_form(f"v_{i}"), tree_form(f"v_{1-i}"))
174
- if out is not None:
175
- return out
176
- return orig
177
- eq = diffsolve_sep2(diffsolve_sep(eq))
178
- if eq is None:
179
- for i in range(2):
180
- a = tree_form(f"v_{i}")
181
- b = tree_form(f"v_{1-i}")
182
- c = tree_form("v_2")
183
- eq2 = orig
184
- eq2 = subs2(eq2, 1)
185
- eq2 = replace(eq2, b, b*a)
186
- eq2 = subs3(eq2)
187
- eq2 = simplify(fraction(simplify(eq2)))
188
- eq2 = diffsolve_sep(eq2)
189
- eq2 = diffsolve_sep2(eq2)
190
- if eq2 is not None:
191
- return e0(TreeNode("f_subs", [replace(eq2.children[0],b,c), c,b/a]).fx("try"))
192
- eq = orig
193
- eq = simplify(eq)
194
- eq = subs2(eq, 1)
195
- eq = fraction(eq)
196
- for i in range(2):
197
- out = linear_dif(eq, tree_form(f"v_{i}"), tree_form(f"v_{1-i}"))
198
- if out is not None:
199
- return out
200
- return orig
201
- else:
202
- return eq
203
- def clist(x):
204
- return list(x.elements())
205
- def collect_term(eq, term_lst):
206
- lst = None
207
- if eq.name == "f_add":
208
- lst = copy.deepcopy(eq.children)
209
- else:
210
- lst = [eq]
211
- other = []
212
- dic = {}
213
- term_lst = list(sorted(term_lst, key=lambda x: -len(factor_generation(x))))
214
- for item in term_lst:
215
- dic[item] = tree_form("d_0")
216
- for item2 in lst:
217
- done = True
218
- tmp2 = Counter(factor_generation(item2))
219
- for index, item in enumerate(term_lst):
220
- tmp = Counter(factor_generation(item))
221
- if (tmp2&tmp) == tmp:
222
- if item in dic.keys():
223
- dic[item] += product(clist(tmp2-tmp))
224
- else:
225
- dic[item] = product(clist(tmp2-tmp))
226
- done = False
227
- break
228
- if done:
229
- other.append(item2)
230
- other = summation(other)
231
- for key in dic.keys():
232
- dic[key] = simplify(dic[key])
233
- return [dic, simplify(other)]
234
- def order(eq,m=0):
235
- best = m
236
- if eq.name in ["f_pdif", "f_dif"]:
237
- out = order(eq.children[0], m+1)
238
- best = max(out, best)
239
- else:
240
- for child in eq.children:
241
- out = order(child, m)
242
- best = max(out, best)
243
- return best
244
- def subs2(eq, orde):
245
- if eq.name in ["f_dif", "f_pdif"] and len(eq.children) == 2:
246
- if orde == 1:
247
- return eq.children[0].fx("dif")/eq.children[1].fx("dif")
248
- else:
249
- return eq
250
- return TreeNode(eq.name, [subs2(child, orde) for child in eq.children])
251
- def subs3(eq):
252
- if eq.name == "f_dif" and eq.children[0].name == "f_add":
253
- return summation([subs3(child.fx("dif")) for child in eq.children[0].children])
254
- if eq.name == "f_dif" and eq.children[0].name == "f_mul":
255
- return summation([product([subs3(child.fx("dif")) if index==index2 else child for index2, child in enumerate(eq.children[0].children)]) for index in range(len(eq.children[0].children))])
256
- return TreeNode(eq.name, [subs3(child) for child in eq.children])
257
- def second_order_dif(eq, a, b):
258
- eq = simplify(eq)
259
- nn = [TreeNode("f_dif", [TreeNode("f_dif", [b,a]),a]), TreeNode("f_dif", [b,a]), b]
260
- out = collect_term(eq.children[0], nn)
261
- if out[1] == tree_form("d_0"):
262
- tmp = out[0][nn[0]]
263
- if tmp != tree_form("d_0"):
264
- for key in out[0].keys():
265
- out[0][key] = simplify(out[0][key]/tmp)
266
- B = out[0][nn[1]]
267
- C = out[0][nn[2]]
268
- if all(all(not contain(item, item2) for item2 in [a,b]) for item in [B, C]):
269
- r = parse("r")
270
- s = simplify(factor2(simplify(TreeNode("f_eq", [r**2 + B*r + C, tree_form("d_0")])), True))
271
- r1, r2 = [inverse(item, r.name) for item in s.children[0].children]
272
- out = None
273
- if contain(r1, tree_form("s_i")):
274
- real = simplify(fraction((r1+r2)/tree_form("d_2")))
275
- imagine = simplify((r1-real)/tree_form("s_i"))
276
- out = tree_form("s_e")**(real*a)*(tree_form("v_101")*(imagine*a).fx("cos")+tree_form("v_102")*(imagine*a).fx("sin"))
277
- elif fraction(simplify(r1-r2)) == tree_form("d_0"):
278
- out =(tree_form("v_101")+tree_form("v_102")*a)*tree_form("s_e")**(r1*a)
279
- else:
280
- out = tree_form("v_101")*tree_form("s_e")**(r1*a) + tree_form("v_102")*tree_form("s_e")**(r2*a)
281
- return TreeNode("f_eq", [b, out])
282
- return None
283
- def linear_dif(eq, a, b):
284
- eq = simplify(eq)
285
- out = collect_term(eq.children[0], [b.fx("dif"), b*a.fx("dif"), a.fx("dif")])
286
- if out[1] == tree_form("d_0"):
287
- tmp = out[0][b.fx("dif")]
288
- if tmp != tree_form("d_0"):
289
- for key in out[0].keys():
290
- out[0][key] = simplify(out[0][key]/tmp)
291
- p, q = out[0][b*a.fx("dif")], -out[0][a.fx("dif")]
292
- if contain(p, b) or contain(q, b):
293
- return None
294
- f = tree_form("s_e") ** TreeNode("f_integrate", [p, a])
295
- return simplify(TreeNode("f_eq", [b*f, TreeNode("f_integrate", [q*f, a])+allocvar()]))
296
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes