mathai 0.6.9__tar.gz → 0.7.1__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 (37) hide show
  1. mathai-0.7.1/PKG-INFO +239 -0
  2. mathai-0.7.1/README.md +222 -0
  3. {mathai-0.6.9 → mathai-0.7.1}/mathai/base.py +5 -3
  4. mathai-0.7.1/mathai/expand.py +175 -0
  5. {mathai-0.6.9 → mathai-0.7.1}/mathai/matrix.py +1 -1
  6. {mathai-0.6.9 → mathai-0.7.1}/mathai/parser.py +1 -1
  7. mathai-0.7.1/mathai.egg-info/PKG-INFO +239 -0
  8. {mathai-0.6.9 → mathai-0.7.1}/setup.py +1 -1
  9. mathai-0.6.9/PKG-INFO +0 -234
  10. mathai-0.6.9/README.md +0 -217
  11. mathai-0.6.9/mathai/expand.py +0 -95
  12. mathai-0.6.9/mathai.egg-info/PKG-INFO +0 -234
  13. {mathai-0.6.9 → mathai-0.7.1}/mathai/__init__.py +0 -0
  14. {mathai-0.6.9 → mathai-0.7.1}/mathai/apart.py +0 -0
  15. {mathai-0.6.9 → mathai-0.7.1}/mathai/bivariate_inequality.py +0 -0
  16. {mathai-0.6.9 → mathai-0.7.1}/mathai/console.py +0 -0
  17. {mathai-0.6.9 → mathai-0.7.1}/mathai/diff.py +0 -0
  18. {mathai-0.6.9 → mathai-0.7.1}/mathai/factor.py +0 -0
  19. {mathai-0.6.9 → mathai-0.7.1}/mathai/fraction.py +0 -0
  20. {mathai-0.6.9 → mathai-0.7.1}/mathai/integrate.py +0 -0
  21. {mathai-0.6.9 → mathai-0.7.1}/mathai/inverse.py +0 -0
  22. {mathai-0.6.9 → mathai-0.7.1}/mathai/limit.py +0 -0
  23. {mathai-0.6.9 → mathai-0.7.1}/mathai/linear.py +0 -0
  24. {mathai-0.6.9 → mathai-0.7.1}/mathai/logic.py +0 -0
  25. {mathai-0.6.9 → mathai-0.7.1}/mathai/ode.py +0 -0
  26. {mathai-0.6.9 → mathai-0.7.1}/mathai/parsetab.py +0 -0
  27. {mathai-0.6.9 → mathai-0.7.1}/mathai/printeq.py +0 -0
  28. {mathai-0.6.9 → mathai-0.7.1}/mathai/simplify.py +0 -0
  29. {mathai-0.6.9 → mathai-0.7.1}/mathai/structure.py +0 -0
  30. {mathai-0.6.9 → mathai-0.7.1}/mathai/tool.py +0 -0
  31. {mathai-0.6.9 → mathai-0.7.1}/mathai/trig.py +0 -0
  32. {mathai-0.6.9 → mathai-0.7.1}/mathai/univariate_inequality.py +0 -0
  33. {mathai-0.6.9 → mathai-0.7.1}/mathai.egg-info/SOURCES.txt +0 -0
  34. {mathai-0.6.9 → mathai-0.7.1}/mathai.egg-info/dependency_links.txt +0 -0
  35. {mathai-0.6.9 → mathai-0.7.1}/mathai.egg-info/requires.txt +0 -0
  36. {mathai-0.6.9 → mathai-0.7.1}/mathai.egg-info/top_level.txt +0 -0
  37. {mathai-0.6.9 → mathai-0.7.1}/setup.cfg +0 -0
mathai-0.7.1/PKG-INFO ADDED
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: mathai
3
+ Version: 0.7.1
4
+ Summary: Mathematics solving Ai tailored to NCERT
5
+ Home-page: https://github.com/infinity390/mathai4
6
+ Author: educated indians are having a low iq and are good for nothing
7
+ Requires-Python: >=3.7
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: lark-parser
10
+ Dynamic: author
11
+ Dynamic: description
12
+ Dynamic: description-content-type
13
+ Dynamic: home-page
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
17
+
18
+ # Math AI Documentation
19
+ ## Source
20
+ Github repository of the code
21
+ https://github.com/infinity390/mathai4
22
+
23
+ ## Philosophy
24
+ I think it is a big realization in computer science and programming to realize that computers can solve mathematics.
25
+ This understanding should be made mainstream. It can help transform education, mathematical research, and computation of mathematical equations for work.
26
+
27
+ ## Societal Implications Of Such A Computer Program And The Author's Comment On Universities Of India
28
+ I think mathematics is valued by society because of education. Schools and universities teach them.
29
+ So this kind of software, if made mainstream, could bring real change.
30
+
31
+ ## The Summary Of How Computer "Solves" Math
32
+ Math equations are a tree data structure (`TreeNode` class).
33
+ We can manipulate the math equations using various algorithms (functions provided by the `mathai` library).
34
+ We first parse the math equation strings to get the tree data structure (`parse` function in `mathai`).
35
+
36
+ ## The Library
37
+ Import the library by doing:
38
+
39
+ ```python
40
+ from mathai import *
41
+ ```
42
+
43
+ ### str_form
44
+ It is the string representation of a `TreeNode` math equation.
45
+
46
+ #### Example
47
+ ```text
48
+ (cos(x)^2)+(sin(x)^2)
49
+ ```
50
+
51
+ Is represented internally as:
52
+
53
+ ```text
54
+ f_add
55
+ f_pow
56
+ f_cos
57
+ v_0
58
+ d_2
59
+ f_pow
60
+ f_sin
61
+ v_0
62
+ d_2
63
+ ```
64
+
65
+ #### Leaf Nodes
66
+
67
+ **Variables** (start with a `v_` prefix):
68
+
69
+ - `v_0` -> x
70
+ - `v_1` -> y
71
+ - `v_2` -> z
72
+ - `v_3` -> a
73
+
74
+ **Numbers** (start with `d_` prefix; only integers):
75
+
76
+ - `d_-1` -> -1
77
+ - `d_0` -> 0
78
+ - `d_1` -> 1
79
+ - `d_2` -> 2
80
+
81
+ #### Branch Nodes
82
+ - `f_add` -> addition
83
+ - `f_mul` -> multiplication
84
+ - `f_pow` -> power
85
+
86
+ ### parse
87
+ Takes a math equation string and outputs a `TreeNode` object.
88
+
89
+ ```python
90
+ from mathai import *
91
+
92
+ equation = parse("sin(x)^2+cos(x)^2")
93
+ print(equation)
94
+ ```
95
+
96
+ #### Output
97
+ ```text
98
+ (cos(x)^2)+(sin(x)^2)
99
+ ```
100
+
101
+ ### solve, simplify
102
+ It simplifies and cleans up a given math equation.
103
+
104
+ ```python
105
+ from mathai import *
106
+
107
+ equation = simplify(parse("(x+x+x+x-1-1-1-1)*(4*x-4)*sin(sin(x+x+x)*sin(3*x))"))
108
+ printeq(equation)
109
+ ```
110
+
111
+ #### Output
112
+ ```text
113
+ ((-4+(4*x))^2)*sin((sin((3*x))^2))
114
+ ```
115
+
116
+ ### Incomplete Documentation, Will be updated and completed later on
117
+
118
+ ### Demonstrations
119
+
120
+ #### Example Demonstration 1
121
+ ```python
122
+ from mathai import *
123
+ question_list_from_lecture = [
124
+ "2*x/(2*x^2 + 5*x + 2) > 1/(x + 1)",
125
+ "(x + 2)*(x + 3)/((x - 2)*(x - 3)) <= 1",
126
+ "(5*x - 1) < (x + 1)^2 & (x + 1)^2 < 7*x - 3",
127
+ "(2*x - 1)/(2*x^3 + 3*x^2 + x) > 0",
128
+ "abs(x + 5)*x + 2*abs(x + 7) - 2 = 0",
129
+ "x*abs(x) - 5*abs(x + 2) + 6 = 0",
130
+ "x^2 - abs(x + 2) + x > 0",
131
+ "abs(abs(x - 2) - 3) <= 2",
132
+ "abs(3*x - 5) + abs(8 - x) = abs(3 + 2*x)",
133
+ "abs(x^2 + 5*x + 9) < abs(x^2 + 2*x + 2) + abs(3*x + 7)"
134
+ ]
135
+
136
+ for item in question_list_from_lecture:
137
+ eq = simplify(parse(item))
138
+ eq = dowhile(eq, absolute)
139
+ eq = simplify(factor1(fraction(eq)))
140
+ eq = prepare(eq)
141
+ eq = factor2(eq)
142
+ c = wavycurvy(eq & domain(eq)).fix()
143
+ print(c)
144
+ ```
145
+ #### Output
146
+
147
+ ```
148
+ (-2,-1)U(-(2/3),-(1/2))
149
+ (-inf,0)U(2,3)U{0}
150
+ (2,4)
151
+ (-inf,-1)U(-(1/2),0)U(1/2,+inf)
152
+ {-4,-3,-(3/2)-(sqrt(57)/2)}
153
+ {-1,(5/2)-(sqrt(89)/2),(5/2)+(sqrt(41)/2)}
154
+ (-inf,-sqrt(2))U((2*sqrt(2))/2,+inf)
155
+ (-3,1)U(3,7)U{1,-3,7,3}
156
+ (5/3,8)U{5/3,8}
157
+ (-inf,-(7/3))
158
+ ```
159
+
160
+ #### Example Demonstration 2
161
+ ```python
162
+ from mathai import *
163
+ def nested_func(eq_node):
164
+ eq_node = fraction(eq_node)
165
+ eq_node = simplify(eq_node)
166
+ eq_node = trig1(eq_node)
167
+ eq_node = trig0(eq_node)
168
+ return eq_node
169
+ for item in ["(cosec(x)-cot(x))^2=(1-cos(x))/(1+cos(x))", "cos(x)/(1+sin(x)) + (1+sin(x))/cos(x) = 2*sec(x)",\
170
+ "tan(x)/(1-cot(x)) + cot(x)/(1-tan(x)) = 1 + sec(x)*cosec(x)", "(1+sec(x))/sec(x) = sin(x)^2/(1-cos(x))",\
171
+ "(cos(x)-sin(x)+1)/(cos(x)+sin(x)-1) = cosec(x)+cot(x)"]:
172
+ eq = logic0(dowhile(parse(item), nested_func))
173
+ print(eq)
174
+ ```
175
+ #### Output
176
+
177
+ ```
178
+ true
179
+ true
180
+ true
181
+ true
182
+ true
183
+ ```
184
+
185
+ #### Example Demonstration 3
186
+ ```python
187
+ from mathai import *
188
+
189
+ eq = simplify(parse("integrate(2*x/(x^2+1),x)"))
190
+ eq = integrate_const(eq)
191
+ eq = integrate_fraction(eq)
192
+ print(simplify(fraction(simplify(eq))))
193
+
194
+ eq = simplify(parse("integrate(sin(cos(x))*sin(x),x)"))
195
+ eq = integrate_subs(eq)
196
+ eq = integrate_const(eq)
197
+ eq = integrate_formula(eq)
198
+ eq = integrate_clean(eq)
199
+ print(simplify(eq))
200
+
201
+ eq = simplify(parse("integrate(x*sqrt(x+2),x)"))
202
+ eq = integrate_subs(eq)
203
+ eq = integrate_const(eq)
204
+ eq = integrate_formula(eq)
205
+ eq = expand(eq)
206
+ eq = integrate_const(eq)
207
+ eq = integrate_summation(eq)
208
+ eq = simplify(eq)
209
+ eq = integrate_const(eq)
210
+ eq = integrate_formula(eq)
211
+ eq = integrate_clean(eq)
212
+ print(simplify(fraction(simplify(eq))))
213
+
214
+ eq = simplify(parse("integrate(x/(e^(x^2)),x)"))
215
+ eq = integrate_subs(eq)
216
+ eq = integrate_const(eq)
217
+ eq = integrate_formula(eq)
218
+ eq = simplify(eq)
219
+ eq = integrate_formula(eq)
220
+ eq = integrate_clean(eq)
221
+ print(simplify(eq))
222
+
223
+ eq = fraction(trig0(trig1(simplify(parse("integrate(sin(x)^4,x)")))))
224
+ eq = integrate_const(eq)
225
+ eq = integrate_summation(eq)
226
+ eq = integrate_formula(eq)
227
+ eq = integrate_const(eq)
228
+ eq = integrate_formula(eq)
229
+ print(factor0(simplify(fraction(simplify(eq)))))
230
+ ```
231
+ #### Output
232
+
233
+ ```
234
+ log(abs((1+(x^2))))
235
+ cos(cos(x))
236
+ ((6*((2+x)^(5/2)))-(20*((2+x)^(3/2))))/15
237
+ -((e^-(x^2))/2)
238
+ -(((8*sin((2*x)))-(12*x)-sin((4*x)))/32)
239
+ ```
mathai-0.7.1/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # Math AI Documentation
2
+ ## Source
3
+ Github repository of the code
4
+ https://github.com/infinity390/mathai4
5
+
6
+ ## Philosophy
7
+ I think it is a big realization in computer science and programming to realize that computers can solve mathematics.
8
+ This understanding should be made mainstream. It can help transform education, mathematical research, and computation of mathematical equations for work.
9
+
10
+ ## Societal Implications Of Such A Computer Program And The Author's Comment On Universities Of India
11
+ I think mathematics is valued by society because of education. Schools and universities teach them.
12
+ So this kind of software, if made mainstream, could bring real change.
13
+
14
+ ## The Summary Of How Computer "Solves" Math
15
+ Math equations are a tree data structure (`TreeNode` class).
16
+ We can manipulate the math equations using various algorithms (functions provided by the `mathai` library).
17
+ We first parse the math equation strings to get the tree data structure (`parse` function in `mathai`).
18
+
19
+ ## The Library
20
+ Import the library by doing:
21
+
22
+ ```python
23
+ from mathai import *
24
+ ```
25
+
26
+ ### str_form
27
+ It is the string representation of a `TreeNode` math equation.
28
+
29
+ #### Example
30
+ ```text
31
+ (cos(x)^2)+(sin(x)^2)
32
+ ```
33
+
34
+ Is represented internally as:
35
+
36
+ ```text
37
+ f_add
38
+ f_pow
39
+ f_cos
40
+ v_0
41
+ d_2
42
+ f_pow
43
+ f_sin
44
+ v_0
45
+ d_2
46
+ ```
47
+
48
+ #### Leaf Nodes
49
+
50
+ **Variables** (start with a `v_` prefix):
51
+
52
+ - `v_0` -> x
53
+ - `v_1` -> y
54
+ - `v_2` -> z
55
+ - `v_3` -> a
56
+
57
+ **Numbers** (start with `d_` prefix; only integers):
58
+
59
+ - `d_-1` -> -1
60
+ - `d_0` -> 0
61
+ - `d_1` -> 1
62
+ - `d_2` -> 2
63
+
64
+ #### Branch Nodes
65
+ - `f_add` -> addition
66
+ - `f_mul` -> multiplication
67
+ - `f_pow` -> power
68
+
69
+ ### parse
70
+ Takes a math equation string and outputs a `TreeNode` object.
71
+
72
+ ```python
73
+ from mathai import *
74
+
75
+ equation = parse("sin(x)^2+cos(x)^2")
76
+ print(equation)
77
+ ```
78
+
79
+ #### Output
80
+ ```text
81
+ (cos(x)^2)+(sin(x)^2)
82
+ ```
83
+
84
+ ### solve, simplify
85
+ It simplifies and cleans up a given math equation.
86
+
87
+ ```python
88
+ from mathai import *
89
+
90
+ equation = simplify(parse("(x+x+x+x-1-1-1-1)*(4*x-4)*sin(sin(x+x+x)*sin(3*x))"))
91
+ printeq(equation)
92
+ ```
93
+
94
+ #### Output
95
+ ```text
96
+ ((-4+(4*x))^2)*sin((sin((3*x))^2))
97
+ ```
98
+
99
+ ### Incomplete Documentation, Will be updated and completed later on
100
+
101
+ ### Demonstrations
102
+
103
+ #### Example Demonstration 1
104
+ ```python
105
+ from mathai import *
106
+ question_list_from_lecture = [
107
+ "2*x/(2*x^2 + 5*x + 2) > 1/(x + 1)",
108
+ "(x + 2)*(x + 3)/((x - 2)*(x - 3)) <= 1",
109
+ "(5*x - 1) < (x + 1)^2 & (x + 1)^2 < 7*x - 3",
110
+ "(2*x - 1)/(2*x^3 + 3*x^2 + x) > 0",
111
+ "abs(x + 5)*x + 2*abs(x + 7) - 2 = 0",
112
+ "x*abs(x) - 5*abs(x + 2) + 6 = 0",
113
+ "x^2 - abs(x + 2) + x > 0",
114
+ "abs(abs(x - 2) - 3) <= 2",
115
+ "abs(3*x - 5) + abs(8 - x) = abs(3 + 2*x)",
116
+ "abs(x^2 + 5*x + 9) < abs(x^2 + 2*x + 2) + abs(3*x + 7)"
117
+ ]
118
+
119
+ for item in question_list_from_lecture:
120
+ eq = simplify(parse(item))
121
+ eq = dowhile(eq, absolute)
122
+ eq = simplify(factor1(fraction(eq)))
123
+ eq = prepare(eq)
124
+ eq = factor2(eq)
125
+ c = wavycurvy(eq & domain(eq)).fix()
126
+ print(c)
127
+ ```
128
+ #### Output
129
+
130
+ ```
131
+ (-2,-1)U(-(2/3),-(1/2))
132
+ (-inf,0)U(2,3)U{0}
133
+ (2,4)
134
+ (-inf,-1)U(-(1/2),0)U(1/2,+inf)
135
+ {-4,-3,-(3/2)-(sqrt(57)/2)}
136
+ {-1,(5/2)-(sqrt(89)/2),(5/2)+(sqrt(41)/2)}
137
+ (-inf,-sqrt(2))U((2*sqrt(2))/2,+inf)
138
+ (-3,1)U(3,7)U{1,-3,7,3}
139
+ (5/3,8)U{5/3,8}
140
+ (-inf,-(7/3))
141
+ ```
142
+
143
+ #### Example Demonstration 2
144
+ ```python
145
+ from mathai import *
146
+ def nested_func(eq_node):
147
+ eq_node = fraction(eq_node)
148
+ eq_node = simplify(eq_node)
149
+ eq_node = trig1(eq_node)
150
+ eq_node = trig0(eq_node)
151
+ return eq_node
152
+ for item in ["(cosec(x)-cot(x))^2=(1-cos(x))/(1+cos(x))", "cos(x)/(1+sin(x)) + (1+sin(x))/cos(x) = 2*sec(x)",\
153
+ "tan(x)/(1-cot(x)) + cot(x)/(1-tan(x)) = 1 + sec(x)*cosec(x)", "(1+sec(x))/sec(x) = sin(x)^2/(1-cos(x))",\
154
+ "(cos(x)-sin(x)+1)/(cos(x)+sin(x)-1) = cosec(x)+cot(x)"]:
155
+ eq = logic0(dowhile(parse(item), nested_func))
156
+ print(eq)
157
+ ```
158
+ #### Output
159
+
160
+ ```
161
+ true
162
+ true
163
+ true
164
+ true
165
+ true
166
+ ```
167
+
168
+ #### Example Demonstration 3
169
+ ```python
170
+ from mathai import *
171
+
172
+ eq = simplify(parse("integrate(2*x/(x^2+1),x)"))
173
+ eq = integrate_const(eq)
174
+ eq = integrate_fraction(eq)
175
+ print(simplify(fraction(simplify(eq))))
176
+
177
+ eq = simplify(parse("integrate(sin(cos(x))*sin(x),x)"))
178
+ eq = integrate_subs(eq)
179
+ eq = integrate_const(eq)
180
+ eq = integrate_formula(eq)
181
+ eq = integrate_clean(eq)
182
+ print(simplify(eq))
183
+
184
+ eq = simplify(parse("integrate(x*sqrt(x+2),x)"))
185
+ eq = integrate_subs(eq)
186
+ eq = integrate_const(eq)
187
+ eq = integrate_formula(eq)
188
+ eq = expand(eq)
189
+ eq = integrate_const(eq)
190
+ eq = integrate_summation(eq)
191
+ eq = simplify(eq)
192
+ eq = integrate_const(eq)
193
+ eq = integrate_formula(eq)
194
+ eq = integrate_clean(eq)
195
+ print(simplify(fraction(simplify(eq))))
196
+
197
+ eq = simplify(parse("integrate(x/(e^(x^2)),x)"))
198
+ eq = integrate_subs(eq)
199
+ eq = integrate_const(eq)
200
+ eq = integrate_formula(eq)
201
+ eq = simplify(eq)
202
+ eq = integrate_formula(eq)
203
+ eq = integrate_clean(eq)
204
+ print(simplify(eq))
205
+
206
+ eq = fraction(trig0(trig1(simplify(parse("integrate(sin(x)^4,x)")))))
207
+ eq = integrate_const(eq)
208
+ eq = integrate_summation(eq)
209
+ eq = integrate_formula(eq)
210
+ eq = integrate_const(eq)
211
+ eq = integrate_formula(eq)
212
+ print(factor0(simplify(fraction(simplify(eq)))))
213
+ ```
214
+ #### Output
215
+
216
+ ```
217
+ log(abs((1+(x^2))))
218
+ cos(cos(x))
219
+ ((6*((2+x)^(5/2)))-(20*((2+x)^(3/2))))/15
220
+ -((e^-(x^2))/2)
221
+ -(((8*sin((2*x)))-(12*x)-sin((4*x)))/32)
222
+ ```
@@ -370,21 +370,23 @@ def product(lst):
370
370
  for item in lst[1:]:
371
371
  s *= item
372
372
  return s
373
- def flatten_tree(node, add=[]):
373
+ def flatten_tree(node):
374
+ if node is None:
375
+ return None
374
376
  if not node.children:
375
377
  return node
376
378
  ad = []
377
379
  if node.name in ["f_add", "f_mul", "f_and", "f_or", "f_wmul"]:
378
380
  merged_children = []
379
381
  for child in node.children:
380
- flattened_child = flatten_tree(child, add)
382
+ flattened_child = flatten_tree(child)
381
383
  if flattened_child.name == node.name:
382
384
  merged_children.extend(flattened_child.children)
383
385
  else:
384
386
  merged_children.append(flattened_child)
385
387
  return TreeNode(node.name, merged_children)
386
388
  else:
387
- node.children = [flatten_tree(child, add) for child in node.children]
389
+ node.children = [flatten_tree(child) for child in node.children]
388
390
  return node
389
391
  def dowhile(eq, fx):
390
392
  if eq is None:
@@ -0,0 +1,175 @@
1
+ from .base import *
2
+ from .simplify import simplify
3
+ import itertools
4
+
5
+ def eliminate_powers(node):
6
+ if not node.children:
7
+ return node
8
+
9
+ node.children = [eliminate_powers(c) for c in node.children]
10
+
11
+ if node.name == "f_pow":
12
+ base, exp = node.children
13
+ n = frac(exp)
14
+
15
+ # Only expand positive integer powers
16
+ if not (n and n.denominator == 1 and n.numerator > 1):
17
+ return node
18
+
19
+ n = n.numerator
20
+
21
+ # ---- Multinomial expansion ----
22
+ if base.name == "f_add":
23
+ terms = []
24
+ for combo in itertools.product(base.children, repeat=n):
25
+ prod = combo[0]
26
+ for c in combo[1:]:
27
+ prod = prod * c
28
+ terms.append(prod)
29
+ return simplify(TreeNode("f_add", terms))
30
+
31
+ # ---- Fallback: simple power ----
32
+ return TreeNode("f_mul", [base] * n)
33
+
34
+ return node
35
+
36
+
37
+
38
+ # =====================================================
39
+ # Phase 2: Single distributive rewrite (DEEPEST FIRST)
40
+ # =====================================================
41
+
42
+ def expand_once(node):
43
+ """
44
+ Performs exactly ONE distributive expansion.
45
+ Deepest-first (post-order).
46
+ """
47
+
48
+ # ---- recurse FIRST (this is the fix) ----
49
+ for i, c in enumerate(node.children):
50
+ new, changed = expand_once(c)
51
+ if changed:
52
+ node.children[i] = new
53
+ return node, True
54
+
55
+ # ---- now try expanding at this node ----
56
+ if node.name == "f_mul":
57
+ for i, child in enumerate(node.children):
58
+ if child.name == "f_add":
59
+ left = node.children[:i]
60
+ right = node.children[i+1:]
61
+
62
+ terms = []
63
+ for t in child.children:
64
+ prod = t
65
+ for r in right:
66
+ prod = prod * r
67
+ for l in reversed(left):
68
+ prod = l * prod
69
+ terms.append(prod)
70
+
71
+ return TreeNode("f_add", terms), True
72
+
73
+ return node, False
74
+
75
+ def _expand2(equation):
76
+ """Iterative version of _expand without recursion."""
77
+ # Stack: (node, child_index, partially_processed_children)
78
+ stack = [(equation, 0, [])]
79
+
80
+ while stack:
81
+ node, child_index, processed_children = stack.pop()
82
+
83
+ # If all children are processed
84
+ if child_index >= len(node.children):
85
+ # Replace children with processed versions
86
+ node.children = processed_children
87
+
88
+ # === Handle f_pow ===
89
+ if node.name == "f_pow":
90
+ n = frac(node.children[1])
91
+ if n is not None and n.denominator == 1 and n.numerator > 1:
92
+ # Convert power to repeated multiplication
93
+ power_children = [node.children[0] for _ in range(n.numerator)]
94
+ new_node = TreeNode("f_mul", power_children)
95
+ # Flatten tree
96
+ node = flatten_tree(new_node)
97
+ # Push it back for further processing
98
+ stack.append((node, 0, []))
99
+ continue
100
+
101
+ # === Handle f_mul ===
102
+ elif node.name == "f_mul":
103
+ # Separate lone children and bracket children
104
+ lone_children = tree_form("d_1")
105
+ bracket_children = []
106
+
107
+ # Iterate in reverse (like original)
108
+ for child in reversed(node.children):
109
+ if child.name == "f_add":
110
+ bracket_children.append(child)
111
+ elif child.name == "f_pow" and child.children[0].name == "f_add":
112
+ n = frac(child.children[1])
113
+ if n is not None and n.denominator == 1 and n.numerator > 1:
114
+ for _ in range(n.numerator):
115
+ bracket_children.append(child.children[0])
116
+ else:
117
+ lone_children = lone_children * child
118
+ else:
119
+ lone_children = lone_children * child
120
+
121
+ lone_children = simplify(lone_children)
122
+
123
+ # Distribute bracket children over lone children iteratively
124
+ while bracket_children:
125
+ tmp = tree_form("d_0")
126
+ bracket = bracket_children.pop(0)
127
+ for bc in bracket.children:
128
+ if lone_children.name == "f_add":
129
+ for lc in lone_children.children:
130
+ tmp = tmp + bc * lc
131
+ else:
132
+ tmp = tmp + bc * lone_children
133
+ # Simplify after each distribution
134
+ lone_children = flatten_tree(simplify(tmp))
135
+
136
+ node = lone_children
137
+
138
+ # === Return node to parent ===
139
+ if stack:
140
+ parent, idx, parent_children = stack.pop()
141
+ parent_children.append(node)
142
+ stack.append((parent, idx + 1, parent_children))
143
+ else:
144
+ # Root node fully expanded
145
+ return node
146
+
147
+ else:
148
+ # Push current node back for next child
149
+ stack.append((node, child_index, processed_children))
150
+ # Push the child to process next
151
+ child = flatten_tree(node.children[child_index])
152
+ stack.append((child, 0, []))
153
+
154
+ # =====================================================
155
+ # Phase 3: Global fixed-point driver
156
+ # =====================================================
157
+
158
+ def expand(eq):
159
+ orig = TreeNode.matmul
160
+ if TreeNode.matmul is None:
161
+ return _expand2(eq)
162
+ eq = simplify(eq)
163
+ if TreeNode.matmul is not None:
164
+ TreeNode.matmul = True
165
+ eq = tree_form(str_form(eq).replace("f_wmul", "f_mul"))
166
+ eq = flatten_tree(eq)
167
+ eq = eliminate_powers(eq)
168
+ while True:
169
+ eq = flatten_tree(eq)
170
+ eq, changed = expand_once(eq)
171
+ if not changed:
172
+ break
173
+ eq =simplify(eq)
174
+ TreeNode.matmul = orig
175
+ return eq
@@ -215,7 +215,7 @@ def fold_wmul(root):
215
215
  return newnode[root]
216
216
 
217
217
  def flat(eq):
218
- return flatten_tree(eq, ["f_wmul"])
218
+ return flatten_tree(eq)
219
219
  def use(eq):
220
220
  return TreeNode(eq.name, [use(child) for child in eq.children])
221
221
  def _matrix_solve(eq):
@@ -154,7 +154,7 @@ def parse(equation, funclist=None):
154
154
  return tree_node
155
155
 
156
156
  tree_node = rfx(tree_node)
157
- tree_node = flatten_tree(tree_node, ["f_wmul"])
157
+ tree_node = flatten_tree(tree_node)
158
158
  if TreeNode.matmul == True:
159
159
  TreeNode.matmul = False
160
160
  tree_node = use(tree_form(str_form(tree_node).replace("f_w","f_")))