mathai 2.3.5__tar.gz → 2.3.7__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.
- {mathai-2.3.5 → mathai-2.3.7}/PKG-INFO +1 -1
- {mathai-2.3.5 → mathai-2.3.7}/mathai/__init__.py +6 -10
- mathai-2.3.7/mathai/decision.py +134 -0
- mathai-2.3.7/mathai/diff.py +179 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/formula_applier.py +98 -53
- {mathai-2.3.5 → mathai-2.3.7}/mathai/integrate.py +19 -274
- {mathai-2.3.5 → mathai-2.3.7}/mathai/linear.py +36 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/matrix.py +202 -124
- {mathai-2.3.5 → mathai-2.3.7}/mathai/qm.py +2 -3
- {mathai-2.3.5 → mathai-2.3.7}/mathai/simplify.py +8 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai.egg-info/PKG-INFO +1 -1
- {mathai-2.3.5 → mathai-2.3.7}/setup.py +1 -1
- mathai-2.3.5/mathai/decision.py +0 -355
- mathai-2.3.5/mathai/diff.py +0 -24
- {mathai-2.3.5 → mathai-2.3.7}/README.md +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/apart.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/base.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/electronic.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/expand.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/factor.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/fraction.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/graphics.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/inverse.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/lambda_calculus.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/limit.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/logic.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/ml.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/mlscalar.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/ode.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/parser.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/pde.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/printeq.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/statistics.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/test.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/tool.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/trig.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai/univariate_inequality.py +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai.egg-info/SOURCES.txt +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai.egg-info/dependency_links.txt +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/mathai.egg-info/top_level.txt +0 -0
- {mathai-2.3.5 → mathai-2.3.7}/setup.cfg +0 -0
|
@@ -5,7 +5,7 @@ from .pde import solve_pde
|
|
|
5
5
|
|
|
6
6
|
from .qm import hydrogen_gse, helium_gse, power_rule
|
|
7
7
|
|
|
8
|
-
from .graphics import draw_graphics_from_solution
|
|
8
|
+
from .graphics import draw_graphics_from_solution, solve_graphics, solve_graphics_string
|
|
9
9
|
|
|
10
10
|
from .lambda_calculus import lambda_expand, lambda_compress, lambda_reduce
|
|
11
11
|
|
|
@@ -15,9 +15,7 @@ from .linear import linear_solve
|
|
|
15
15
|
|
|
16
16
|
from .test import test
|
|
17
17
|
|
|
18
|
-
from .decision import god
|
|
19
|
-
|
|
20
|
-
from .graphics import solve_graphics, solve_graphics_string
|
|
18
|
+
from .decision import god
|
|
21
19
|
|
|
22
20
|
from .expand import expand
|
|
23
21
|
|
|
@@ -32,12 +30,10 @@ from .formula_applier import formula_applier, formula_list_applier
|
|
|
32
30
|
|
|
33
31
|
from .integrate import integrate_subs_main as integrate_subs
|
|
34
32
|
from .integrate import byparts as integrate_byparts
|
|
35
|
-
from .integrate import
|
|
36
|
-
from .integrate import conv_int
|
|
33
|
+
from .integrate import integrate_fraction
|
|
37
34
|
from .integrate import solve_integrate as integrate_clean
|
|
38
|
-
from .integrate import integrate_formula, integrate_trig_formula,
|
|
35
|
+
from .integrate import integrate_formula, integrate_trig_formula, integrate_full, def_integrate_formula
|
|
39
36
|
from .integrate import normalize as integration_basic
|
|
40
|
-
from .integrate import normalize_qm as integration_qm_basic
|
|
41
37
|
|
|
42
38
|
from .diff import diff, diff2
|
|
43
39
|
|
|
@@ -54,11 +50,11 @@ from .inverse import inverse, inverse2
|
|
|
54
50
|
from .logic import logic0, set_sub, truth_gen, solve_logically
|
|
55
51
|
from .apart import apart, apart2
|
|
56
52
|
|
|
57
|
-
from .limit import limit1, limit2, limit0, limit3, limit4, solve_inf
|
|
53
|
+
from .limit import limit1, limit2, limit0, limit3, limit4, solve_inf, limit5
|
|
58
54
|
|
|
59
55
|
from .univariate_inequality import wavycurvy, absolute, domain, handle_sqrt, prepare, eq2range, range2eq2, simple_wavycurvy
|
|
60
56
|
|
|
61
|
-
from .matrix import matrix_solve
|
|
57
|
+
from .matrix import matrix_solve, matrix_formula
|
|
62
58
|
|
|
63
59
|
from .base import *
|
|
64
60
|
from .printeq import printeq_obj, print_raw
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from .inverse import inverse, inverse2
|
|
3
|
+
from .integrate import integrate_full, def_integrate_formula
|
|
4
|
+
from .ode import diffsolve as ode_solve
|
|
5
|
+
from .parser import parse
|
|
6
|
+
from .simplify import simplify, log0
|
|
7
|
+
from .base import *
|
|
8
|
+
from .diff import diff, diff2
|
|
9
|
+
from .trig import trig0, trig1, zu_simplify
|
|
10
|
+
from .univariate_inequality import simple_wavycurvy, wavycurvy, prepare, absolute, handle_sqrt, eq2range, domain, Range
|
|
11
|
+
from .fraction import fraction
|
|
12
|
+
from .expand import expand
|
|
13
|
+
from .logic import logic0, set_sub, truth_gen, solve_logically
|
|
14
|
+
from .factor import factor2, factor
|
|
15
|
+
from .limit import limit1, limit5
|
|
16
|
+
from .linear import linear_solve
|
|
17
|
+
from .matrix import matrix_solve, matrix_formula
|
|
18
|
+
|
|
19
|
+
def vlist_2(eq):
|
|
20
|
+
out = []
|
|
21
|
+
if eq.name[2:] in "det adj dim".split(" ") and eq.children[0].name[:2] == "v_":
|
|
22
|
+
return [eq]
|
|
23
|
+
if eq.name[:2] == "v_":
|
|
24
|
+
return [eq]
|
|
25
|
+
for child in eq.children:
|
|
26
|
+
out += vlist_2(child)
|
|
27
|
+
return list(set(out))
|
|
28
|
+
def convert_single_solution(eq):
|
|
29
|
+
if eq.name == "f_range":
|
|
30
|
+
out = eq2range(eq)
|
|
31
|
+
if out.r == [False] and len(out.p) == 1:
|
|
32
|
+
return TreeNode("f_eq", [out.variable - out.p[0], TreeNode("d_0")])
|
|
33
|
+
return eq
|
|
34
|
+
def solve_subs(q):
|
|
35
|
+
solution = {}
|
|
36
|
+
orig = copy.deepcopy(q)
|
|
37
|
+
if q.name != "f_and":
|
|
38
|
+
q = TreeNode("f_and", [q])
|
|
39
|
+
lst = q.children
|
|
40
|
+
if not all(item.name == "f_eq" for item in lst):
|
|
41
|
+
return orig
|
|
42
|
+
q = simplify(q)
|
|
43
|
+
done = True
|
|
44
|
+
while done:
|
|
45
|
+
done = False
|
|
46
|
+
for i in range(len(q.children)):
|
|
47
|
+
item = q.children[i]
|
|
48
|
+
for key, item2 in solution.items():
|
|
49
|
+
item = replace(item, tree_form(key), item2)
|
|
50
|
+
item = fraction(expand(trig0(simplify(item))))
|
|
51
|
+
q.children[i] = item
|
|
52
|
+
vars_in_item = vlist_2(item)
|
|
53
|
+
if len(vars_in_item) == 1:
|
|
54
|
+
target_var = vars_in_item[0]
|
|
55
|
+
tmp = inverse(item.children[0], str_form(target_var))
|
|
56
|
+
if tmp is not None:
|
|
57
|
+
solution[str_form(target_var)] = tmp
|
|
58
|
+
done = True
|
|
59
|
+
break
|
|
60
|
+
if len(solution.keys()) == 0:
|
|
61
|
+
return orig
|
|
62
|
+
return operation("f_and", [item for item in lst if all(not contain(item, tree_form(key)) for key in solution.keys())] + [TreeNode("f_eq", [tree_form(key), item]) for key, item in solution.items()])
|
|
63
|
+
def god(string):
|
|
64
|
+
print(f"? {string}")
|
|
65
|
+
print("thinking...")
|
|
66
|
+
eq = None
|
|
67
|
+
eq = parse(string)
|
|
68
|
+
log = [eq]
|
|
69
|
+
if "f_pdif" in str_form(eq):
|
|
70
|
+
eq = diff2(eq)
|
|
71
|
+
if "f_limit" in str_form(eq):
|
|
72
|
+
eq = limit1(limit5(eq))
|
|
73
|
+
if any(contain2(eq, "f_"+item) for item in "adj det list transpose".split(" ")):
|
|
74
|
+
eq = matrix_formula(eq)
|
|
75
|
+
print(eq)
|
|
76
|
+
eq = matrix_solve(eq)
|
|
77
|
+
print(eq)
|
|
78
|
+
eq = solve_subs(eq)
|
|
79
|
+
print(f"=> {eq}")
|
|
80
|
+
print()
|
|
81
|
+
return eq
|
|
82
|
+
elif all(not contain2(eq, "f_"+item) for item in "dif add mul abs pow dif integrate arcsin sin cos log limit eq lt le ge gt".split(" ")) and\
|
|
83
|
+
any(contain2(eq, "f_"+item) for item in "and or not imply equiv".split(" ")):
|
|
84
|
+
eq = solve_logically(truth_gen(simplify(set_sub(eq))))
|
|
85
|
+
print(f"=> {eq}")
|
|
86
|
+
print()
|
|
87
|
+
return eq
|
|
88
|
+
elif not (eq.name == "f_and" and len(vlist(eq)) > 1) and any(contain2(eq, "f_"+item) for item in "eq lt le ge gt".split(" ")) and all(not contain2(eq, "f_"+item) for item in "limit dif integrate".split(" ")):
|
|
89
|
+
lst = [simplify, log0, simplify, lambda x: dowhile(x, absolute), lambda x: dowhile(x, lambda y: simplify(expand(y))), lambda x: dowhile(x, lambda y: simplify(fraction(y))), logic0, simple_wavycurvy, simple_wavycurvy]
|
|
90
|
+
fx2 = lambda x: dowhile(x, lambda y: simplify(fraction(y)))
|
|
91
|
+
fx3 = lambda x: dowhile(x, trig1)
|
|
92
|
+
lst2 = [simplify, trig0, lambda x: dowhile(x, lambda y: fx2(fx3(y))), solve_subs, logic0]
|
|
93
|
+
sel = lst.copy()
|
|
94
|
+
if any(contain2(eq, "f_"+item) for item in "sin cos tan cosec sec cot".split(" ")) or\
|
|
95
|
+
len(vlist(eq)) > 1:
|
|
96
|
+
sel = lst2
|
|
97
|
+
for item in sel:
|
|
98
|
+
eq = item(eq)
|
|
99
|
+
if eq not in log:
|
|
100
|
+
log.append(eq)
|
|
101
|
+
print(eq)
|
|
102
|
+
# eq = trig7(eq)
|
|
103
|
+
print(f"=> {eq}")
|
|
104
|
+
print()
|
|
105
|
+
return eq
|
|
106
|
+
elif "f_dif" in str_form(eq) or "f_integrate" in str_form(eq):
|
|
107
|
+
if "f_dif" in str_form(eq) and "f_integrate" not in str_form(eq):
|
|
108
|
+
eq = simplify(ode_solve(simplify(eq)))
|
|
109
|
+
log.append(eq)
|
|
110
|
+
if "f_integrate" in str_form(eq):
|
|
111
|
+
eq = def_integrate_formula(eq)
|
|
112
|
+
eq = integrate_full(eq)
|
|
113
|
+
eq = limit5(eq)
|
|
114
|
+
eq = limit1(eq)
|
|
115
|
+
eq = simplify(expand(simplify(eq)))
|
|
116
|
+
print(f"=> {eq}")
|
|
117
|
+
print()
|
|
118
|
+
return eq
|
|
119
|
+
if any("f_"+item in str_form(eq) for item in "eq lt le ge gt".split(" ")):
|
|
120
|
+
eq = simple_wavycurvy(eq)
|
|
121
|
+
eq = transform_dfs(eq, convert_single_solution)
|
|
122
|
+
out = linear_solve(eq)
|
|
123
|
+
if out is not None:
|
|
124
|
+
eq = out
|
|
125
|
+
print(eq)
|
|
126
|
+
eq = solve_subs(eq)
|
|
127
|
+
out = linear_solve(eq)
|
|
128
|
+
if out is not None:
|
|
129
|
+
eq = out
|
|
130
|
+
eq = simple_wavycurvy(eq)
|
|
131
|
+
eq = simplify(expand(simplify(fraction(simplify(eq)))))
|
|
132
|
+
print(f"=> {eq}")
|
|
133
|
+
print()
|
|
134
|
+
return eq
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from .simplify import simplify
|
|
2
|
+
from .base import *
|
|
3
|
+
from .parser import parse
|
|
4
|
+
from .formula_applier import formula_list_applier
|
|
5
|
+
|
|
6
|
+
diff2_s = """pdif(e^a,x) e^a*pdif(a,x) [] x _ _ _
|
|
7
|
+
pdif(a^b,x) b*(a^(b-1))*pdif(a,x)+(a^b)*log(a)*pdif(b,x) [] x _ _ _
|
|
8
|
+
pdif(sin(a),x) cos(a)*pdif(a,x) [] x _ _ _
|
|
9
|
+
pdif(cos(a),x) -sin(a)*pdif(a,x) [] x _ _ _
|
|
10
|
+
pdif(arcsin(a),x) (1/sqrt(1-a^2))*pdif(a,x) [] x _ _ _
|
|
11
|
+
pdif(arccos(a),x) (-1/sqrt(1-a^2))*pdif(a,x) [] x _ _ _
|
|
12
|
+
pdif(arctan(a),x) (1/(1+a^2))*pdif(a,x) [] x _ _ _
|
|
13
|
+
pdif(k,x) 0 [] x k _ _
|
|
14
|
+
pdif(x,x) 1 [] x _ _ _
|
|
15
|
+
pdif(product(a,b),x) pdif(a,x)*b+a*pdif(b,x) [] x _ _ _
|
|
16
|
+
pdif(sum(a,b),x) pdif(a,x)+pdif(b,x) [] x _ _ _
|
|
17
|
+
pdif(product(k,a),x) k*pdif(a,x) [] x k _ _"""
|
|
18
|
+
|
|
19
|
+
diff2 = lambda x: formula_list_applier(x, diff2_s)
|
|
20
|
+
'''
|
|
21
|
+
def diff(equation, var=None):
|
|
22
|
+
var = var if var else "v_0"
|
|
23
|
+
out = diff2(simplify(TreeNode("f_pdif", [equation, tree_form(var)])))
|
|
24
|
+
return out
|
|
25
|
+
'''
|
|
26
|
+
def diff(equation, var="v_0"):
|
|
27
|
+
def diffeq_iterative(root_eq):
|
|
28
|
+
stack = [(root_eq, False)]
|
|
29
|
+
results = {}
|
|
30
|
+
while stack:
|
|
31
|
+
eq, processed = stack.pop()
|
|
32
|
+
if eq in results:
|
|
33
|
+
continue
|
|
34
|
+
if not processed:
|
|
35
|
+
eq_simplified = simplify(eq)
|
|
36
|
+
if "v_" not in str_form(eq_simplified):
|
|
37
|
+
results[eq] = tree_form("d_0")
|
|
38
|
+
continue
|
|
39
|
+
stack.append((eq, True))
|
|
40
|
+
if eq.name in ("f_add", "f_mul"):
|
|
41
|
+
for child in eq.children:
|
|
42
|
+
if child not in results:
|
|
43
|
+
stack.append((child, False))
|
|
44
|
+
elif eq.name in ("f_abs", "f_tan", "f_log", "f_arcsin", "f_arccos", "f_arctan", "f_sin", "f_cos"):
|
|
45
|
+
child = eq.children[0]
|
|
46
|
+
if child not in results:
|
|
47
|
+
stack.append((child, False))
|
|
48
|
+
elif eq.name == "f_pow":
|
|
49
|
+
for child in eq.children:
|
|
50
|
+
if child not in results:
|
|
51
|
+
stack.append((child, False))
|
|
52
|
+
else:
|
|
53
|
+
if "v_" not in str_form(eq):
|
|
54
|
+
results[eq] = tree_form("d_0")
|
|
55
|
+
continue
|
|
56
|
+
if eq.name == "f_add":
|
|
57
|
+
add_res = tree_form("d_0")
|
|
58
|
+
for child in eq.children:
|
|
59
|
+
add_res = add_res + results.get(child, tree_form("d_0"))
|
|
60
|
+
results[eq] = add_res
|
|
61
|
+
elif eq.name == "f_abs":
|
|
62
|
+
u = eq.children[0]
|
|
63
|
+
du = results.get(u, tree_form("d_0"))
|
|
64
|
+
results[eq] = du * u / eq
|
|
65
|
+
elif eq.name == "f_pow" and eq.children[0].name == "s_e":
|
|
66
|
+
u = eq.children[1]
|
|
67
|
+
du = results.get(u, tree_form("d_0"))
|
|
68
|
+
results[eq] = du * eq
|
|
69
|
+
elif eq.name == "f_tan":
|
|
70
|
+
u = eq.children[0]
|
|
71
|
+
du = results.get(u, tree_form("d_0"))
|
|
72
|
+
cos_u = u.fx("cos")
|
|
73
|
+
results[eq] = du / (cos_u * cos_u)
|
|
74
|
+
elif eq.name == "f_log":
|
|
75
|
+
u = eq.children[0]
|
|
76
|
+
du = results.get(u, tree_form("d_0"))
|
|
77
|
+
results[eq] = du * (tree_form("d_1") / u)
|
|
78
|
+
elif eq.name == "f_arcsin":
|
|
79
|
+
u = eq.children[0]
|
|
80
|
+
du = results.get(u, tree_form("d_0"))
|
|
81
|
+
one = tree_form("d_1")
|
|
82
|
+
half = tree_form("d_2") ** tree_form("d_-1")
|
|
83
|
+
results[eq] = du / ((one - u * u) ** half)
|
|
84
|
+
elif eq.name == "f_arccos":
|
|
85
|
+
u = eq.children[0]
|
|
86
|
+
du = results.get(u, tree_form("d_0"))
|
|
87
|
+
one = tree_form("d_1")
|
|
88
|
+
half = tree_form("d_2") ** tree_form("d_-1")
|
|
89
|
+
results[eq] = tree_form("d_-1") * du / ((one - u * u) ** half)
|
|
90
|
+
elif eq.name == "f_arctan":
|
|
91
|
+
u = eq.children[0]
|
|
92
|
+
du = results.get(u, tree_form("d_0"))
|
|
93
|
+
results[eq] = du / (tree_form("d_1") + u * u)
|
|
94
|
+
elif eq.name == "f_pow" and "v_" in str_form(eq.children[1]):
|
|
95
|
+
u, v = eq.children
|
|
96
|
+
du = results.get(u, tree_form("d_0"))
|
|
97
|
+
dv = results.get(v, tree_form("d_0"))
|
|
98
|
+
term1 = (v / u) * du
|
|
99
|
+
term2 = u.fx("log") * dv
|
|
100
|
+
results[eq] = (u ** v) * (term1 + term2)
|
|
101
|
+
elif eq.name == "f_mul":
|
|
102
|
+
add_res = tree_form("d_0")
|
|
103
|
+
n_children = len(eq.children)
|
|
104
|
+
for i in range(n_children):
|
|
105
|
+
target_child = eq.children[i]
|
|
106
|
+
d_target = results.get(target_child, tree_form("d_0"))
|
|
107
|
+
other_terms = [eq.children[j] for j in range(n_children) if j != i]
|
|
108
|
+
if len(other_terms) == 1:
|
|
109
|
+
other_prod = other_terms[0]
|
|
110
|
+
else:
|
|
111
|
+
other_prod = TreeNode("f_mul", other_terms)
|
|
112
|
+
add_res = add_res + (d_target * other_prod)
|
|
113
|
+
results[eq] = add_res
|
|
114
|
+
elif eq.name == "f_sin":
|
|
115
|
+
u = eq.children[0]
|
|
116
|
+
du = results.get(u, tree_form("d_0"))
|
|
117
|
+
results[eq] = du * u.fx("cos")
|
|
118
|
+
elif eq.name == "f_cos":
|
|
119
|
+
u = eq.children[0]
|
|
120
|
+
du = results.get(u, tree_form("d_0"))
|
|
121
|
+
results[eq] = tree_form("d_-1") * du * u.fx("sin")
|
|
122
|
+
elif eq.name[:2] == "v_":
|
|
123
|
+
results[eq] = TreeNode("f_dif", [eq])
|
|
124
|
+
elif eq.name == "f_pow" and "v_" not in str_form(eq.children[1]):
|
|
125
|
+
base, power = eq.children
|
|
126
|
+
dbase = results.get(base, tree_form("d_0"))
|
|
127
|
+
power_minus_1 = power - tree_form("d_1")
|
|
128
|
+
results[eq] = power * (base ** power_minus_1) * dbase
|
|
129
|
+
else:
|
|
130
|
+
results[eq] = TreeNode("f_dif", [eq, tree_form(var)])
|
|
131
|
+
return results.get(root_eq, tree_form("d_0"))
|
|
132
|
+
def helper2_iterative(root_eq, target_var):
|
|
133
|
+
stack = [(root_eq, False)]
|
|
134
|
+
results = {}
|
|
135
|
+
while stack:
|
|
136
|
+
eq, processed = stack.pop()
|
|
137
|
+
if eq in results:
|
|
138
|
+
continue
|
|
139
|
+
if not processed:
|
|
140
|
+
stack.append((eq, True))
|
|
141
|
+
for child in eq.children:
|
|
142
|
+
if child not in results:
|
|
143
|
+
stack.append((child, False))
|
|
144
|
+
else:
|
|
145
|
+
if eq.name == "f_dif":
|
|
146
|
+
if eq.children[0].name == target_var:
|
|
147
|
+
results[eq] = tree_form("d_1")
|
|
148
|
+
elif not contain(eq.children[0], target_var):
|
|
149
|
+
results[eq] = tree_form("d_0")
|
|
150
|
+
else:
|
|
151
|
+
results[eq] = eq
|
|
152
|
+
else:
|
|
153
|
+
new_children = [results.get(child, child) for child in eq.children]
|
|
154
|
+
results[eq] = TreeNode(eq.name, new_children)
|
|
155
|
+
return results.get(root_eq, root_eq)
|
|
156
|
+
def calc_iterative(root_eq):
|
|
157
|
+
stack = [(root_eq, False)]
|
|
158
|
+
results = {}
|
|
159
|
+
while stack:
|
|
160
|
+
eq, processed = stack.pop()
|
|
161
|
+
if eq in results:
|
|
162
|
+
continue
|
|
163
|
+
if not processed:
|
|
164
|
+
stack.append((eq, True))
|
|
165
|
+
for child in eq.children:
|
|
166
|
+
if child not in results:
|
|
167
|
+
stack.append((child, False))
|
|
168
|
+
else:
|
|
169
|
+
if eq.name == "f_dif":
|
|
170
|
+
results[eq] = diffeq_iterative(eq.children[0])
|
|
171
|
+
else:
|
|
172
|
+
new_children = [results.get(child, child) for child in eq.children]
|
|
173
|
+
results[eq] = TreeNode(eq.name, new_children)
|
|
174
|
+
return results.get(root_eq, root_eq)
|
|
175
|
+
if var is None:
|
|
176
|
+
return simplify(calc_iterative(equation))
|
|
177
|
+
equation = diffeq_iterative(equation)
|
|
178
|
+
equation = helper2_iterative(equation, var)
|
|
179
|
+
return simplify(equation)
|
|
@@ -16,15 +16,16 @@ _W_NAMES = {
|
|
|
16
16
|
_COMMUTATIVE = frozenset(("f_addw", "f_mulw", "f_hadamardw"))
|
|
17
17
|
_ORDERED_ASSOC = frozenset(("f_wmulw",))
|
|
18
18
|
_LABEL_PREFIXES = frozenset(("f_", "s_", "d_"))
|
|
19
|
-
_SPECIAL_PATTERN = frozenset(("f_poly", "f_sum", "f_product", "f_hprod"))
|
|
19
|
+
_SPECIAL_PATTERN = frozenset(("f_poly", "f_sum", "f_product", "f_hprod", "f_mprod"))
|
|
20
20
|
_PATTERN_OPS = {"f_sum": "f_addw", "f_product": "f_mulw", "f_hprod": "f_hadamardw"}
|
|
21
|
+
_ORDERED_PATTERN_OPS = {"f_mprod": "f_wmulw"}
|
|
21
22
|
_AGG_NAMES = frozenset((
|
|
22
23
|
"f_addw", "f_add", "f_mulw", "f_mul", "f_hadamardw", "f_hadamard",
|
|
23
|
-
"f_product", "f_sum", "f_hprod", "f_poly",
|
|
24
|
+
"f_product", "f_sum", "f_hprod", "f_mprod", "f_poly", "f_wmulw", "f_wmul",
|
|
24
25
|
))
|
|
25
26
|
_EXTRACT_NAMES = frozenset((
|
|
26
27
|
"f_add", "f_wadd", "f_waddw", "f_wmul", "f_wmulw",
|
|
27
|
-
"f_mul", "f_product", "f_sum", "f_hprod", "f_poly",
|
|
28
|
+
"f_mul", "f_product", "f_sum", "f_hprod", "f_mprod", "f_poly",
|
|
28
29
|
))
|
|
29
30
|
_DE_W_NAMES = frozenset(("f_addw", "f_mulw", "f_waddw", "f_hadamardw", "f_wmulw"))
|
|
30
31
|
|
|
@@ -32,7 +33,7 @@ _DE_W_NAMES = frozenset(("f_addw", "f_mulw", "f_waddw", "f_hadamardw", "f_wmulw"
|
|
|
32
33
|
def transform_dfs(node, fn, stable=None):
|
|
33
34
|
if node is None:
|
|
34
35
|
return None
|
|
35
|
-
if stable is not None and node in stable:
|
|
36
|
+
if stable is not None and id(node) in stable:
|
|
36
37
|
return node
|
|
37
38
|
children = node.children
|
|
38
39
|
if children is None:
|
|
@@ -50,15 +51,17 @@ def transform_dfs(node, fn, stable=None):
|
|
|
50
51
|
if out is not None and out is not cur and out == cur:
|
|
51
52
|
out = cur
|
|
52
53
|
if stable is not None and unchanged and out is cur:
|
|
53
|
-
stable[cur] = cur
|
|
54
|
+
stable[id(cur)] = cur # holding the reference keeps the id valid
|
|
54
55
|
return out
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
def dowhile(x, fn):
|
|
58
59
|
while True:
|
|
59
60
|
y = fn(x)
|
|
60
|
-
if y is
|
|
61
|
-
return x
|
|
61
|
+
if y is None:
|
|
62
|
+
return x
|
|
63
|
+
if y is x or y == x:
|
|
64
|
+
return y
|
|
62
65
|
x = y
|
|
63
66
|
|
|
64
67
|
|
|
@@ -68,8 +71,10 @@ def _fixpoint(eq, fn):
|
|
|
68
71
|
stable = {}
|
|
69
72
|
while True:
|
|
70
73
|
new = transform_dfs(eq, fn, stable)
|
|
71
|
-
if new is
|
|
72
|
-
return eq
|
|
74
|
+
if new is None:
|
|
75
|
+
return eq
|
|
76
|
+
if new is eq or new == eq:
|
|
77
|
+
return new
|
|
73
78
|
eq = new
|
|
74
79
|
|
|
75
80
|
|
|
@@ -111,6 +116,8 @@ def _collect_labels(node, acc):
|
|
|
111
116
|
acc.add("f_mulw")
|
|
112
117
|
elif node.name == "f_hprod":
|
|
113
118
|
acc.add("f_hadamardw")
|
|
119
|
+
elif node.name == "f_mprod":
|
|
120
|
+
acc.add("f_wmulw")
|
|
114
121
|
elif node.name == "f_poly":
|
|
115
122
|
acc.add("f_mulw")
|
|
116
123
|
acc.add("f_addw")
|
|
@@ -190,6 +197,17 @@ def _as_exprs(x):
|
|
|
190
197
|
return [x]
|
|
191
198
|
|
|
192
199
|
|
|
200
|
+
def _as_pairs(x):
|
|
201
|
+
"""Like _as_exprs but keeps [expr, value] pairs intact (no flattening)."""
|
|
202
|
+
if x is None or not isinstance(x, list):
|
|
203
|
+
return []
|
|
204
|
+
if x and all(isinstance(p, list) for p in x):
|
|
205
|
+
return [p for p in x if len(p) == 2]
|
|
206
|
+
if len(x) == 2 and not any(isinstance(p, list) for p in x):
|
|
207
|
+
return [x] # a single bare pair
|
|
208
|
+
return []
|
|
209
|
+
|
|
210
|
+
|
|
193
211
|
def _subst(node, mapping):
|
|
194
212
|
if node is None:
|
|
195
213
|
return None
|
|
@@ -253,8 +271,10 @@ def _match_struct_bind_core(eq, f, binding, const_1_vars, ignore_set, resolved_i
|
|
|
253
271
|
for item in vlist(eq):
|
|
254
272
|
out = extract_polynomial_coefficients(eq_plain, item, degree)
|
|
255
273
|
if out is not None:
|
|
256
|
-
out = out[::-1]
|
|
257
|
-
|
|
274
|
+
out = out[::-1]
|
|
275
|
+
while len(out) != 0 and out[0] == 0:
|
|
276
|
+
out.pop(0)
|
|
277
|
+
if len(out) == degree:
|
|
258
278
|
local_binding = binding.copy()
|
|
259
279
|
for i in range(1, len(f.children)):
|
|
260
280
|
local_binding[f.children[i].name] = out[i - 1]
|
|
@@ -291,6 +311,37 @@ def _match_struct_bind_core(eq, f, binding, const_1_vars, ignore_set, resolved_i
|
|
|
291
311
|
local_binding[f.children[1 - index].name] = operation(op_name, non_const_term)
|
|
292
312
|
binding.update(local_binding)
|
|
293
313
|
return True
|
|
314
|
+
if fname in _ORDERED_PATTERN_OPS and len(f.children) >= 2:
|
|
315
|
+
op_name = _ORDERED_PATTERN_OPS[fname]
|
|
316
|
+
if eq.name == op_name and len(eq.children) >= len(f.children):
|
|
317
|
+
fc, ec = f.children, eq.children
|
|
318
|
+
n = len(fc)
|
|
319
|
+
m = len(ec)
|
|
320
|
+
if m < n:
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
def search_ordered_pattern(i, j, b):
|
|
324
|
+
if i == n:
|
|
325
|
+
return b if j == m else None
|
|
326
|
+
pat = fc[i]
|
|
327
|
+
free = (not pat.children) and pat.name in free_vars
|
|
328
|
+
remaining_pats = n - i - 1
|
|
329
|
+
max_len = (m - j - remaining_pats) if free else 1
|
|
330
|
+
for L in range(1, max_len + 1):
|
|
331
|
+
seg = ec[j] if L == 1 else TreeNode(op_name, list(ec[j:j + L]))
|
|
332
|
+
sb = b.copy()
|
|
333
|
+
if _match_struct_bind_core(seg, pat, sb, const_1_vars,
|
|
334
|
+
ignore_set, resolved_ignore, free_vars):
|
|
335
|
+
r = search_ordered_pattern(i + 1, j + L, sb)
|
|
336
|
+
if r is not None:
|
|
337
|
+
return r
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
result = search_ordered_pattern(0, 0, binding)
|
|
341
|
+
if result is None:
|
|
342
|
+
return False
|
|
343
|
+
binding.update(result)
|
|
344
|
+
return True
|
|
294
345
|
if fname.startswith("v_") and not f.children and fname in free_vars:
|
|
295
346
|
f_str = str_form(f)
|
|
296
347
|
if f_str in binding:
|
|
@@ -389,11 +440,13 @@ def _match_struct_bind(eq, f, binding, const_1_vars=None, ignore_set=None,
|
|
|
389
440
|
|
|
390
441
|
|
|
391
442
|
class _Matcher:
|
|
392
|
-
def __init__(self, formula, formula_out, positive=None, negative=None,
|
|
443
|
+
def __init__(self, formula, formula_out, positive=None, negative=None,
|
|
444
|
+
ignore_list=None, const_1=None, forbidden_list=None):
|
|
393
445
|
self.orig_formula = formula
|
|
394
446
|
self.formula_out = formula_out
|
|
395
447
|
self.formula_w = en_w_addmul(formula)
|
|
396
|
-
|
|
448
|
+
has_formula = self.formula_w is not None
|
|
449
|
+
self.root_name = self.formula_w.name if has_formula else ""
|
|
397
450
|
f_sum_vars = set()
|
|
398
451
|
_extract_f_sum_vars(self.formula_w, f_sum_vars)
|
|
399
452
|
self.free_vars = frozenset(f_sum_vars)
|
|
@@ -402,15 +455,15 @@ class _Matcher:
|
|
|
402
455
|
self.ignore_list_exprs = _as_exprs(ignore_list)
|
|
403
456
|
self.ignore_set = {get_name(item) for item in self.ignore_list_exprs}
|
|
404
457
|
self.ignore_names = {item.name for item in self.ignore_list_exprs}
|
|
405
|
-
self.
|
|
406
|
-
|
|
407
|
-
names = set(vlist(self.formula_w)) - f_sum_vars if
|
|
458
|
+
self.forbidden_pairs = _as_pairs(forbidden_list) # FIX: keep pairs intact
|
|
459
|
+
|
|
460
|
+
names = (set(vlist(self.formula_w)) - f_sum_vars) if has_formula else set()
|
|
408
461
|
self.vl = sorted(names, key=lambda n: (0 if n in self.ignore_set else 1, n))
|
|
409
462
|
self.must_be_var = {n: (n in self.ignore_set and n not in f_sum_vars) for n in self.vl}
|
|
410
463
|
self.const_1_vars = {get_name(item) for item in _as_exprs(const_1)}
|
|
411
464
|
self.must_be_const = {n: (n in self.const_1_vars) for n in self.vl}
|
|
412
465
|
self.root_is_free = (
|
|
413
|
-
not
|
|
466
|
+
not has_formula
|
|
414
467
|
or (self.root_name.startswith("v_") and not self.formula_w.children)
|
|
415
468
|
or self.root_name in _SPECIAL_PATTERN
|
|
416
469
|
)
|
|
@@ -421,19 +474,15 @@ class _Matcher:
|
|
|
421
474
|
self._same_count = [count_same_nodes(self.orig_formula, TreeNode(x)) for x in self.vl]
|
|
422
475
|
|
|
423
476
|
def _is_forbidden(self, mapping):
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
simp_expr = simplify(subbed_expr)
|
|
434
|
-
simp_val = simplify(subbed_val)
|
|
435
|
-
if simp_expr is None or simp_val is None or simp_expr == simp_val:
|
|
436
|
-
return True
|
|
477
|
+
for expr_part, forb_val in self.forbidden_pairs:
|
|
478
|
+
subbed_expr = _subst(expr_part, mapping)
|
|
479
|
+
subbed_val = _subst(forb_val, mapping)
|
|
480
|
+
if subbed_expr is None or subbed_val is None:
|
|
481
|
+
return True
|
|
482
|
+
simp_expr = simplify(subbed_expr)
|
|
483
|
+
simp_val = simplify(subbed_val)
|
|
484
|
+
if simp_expr is None or simp_val is None or simp_expr == simp_val:
|
|
485
|
+
return True
|
|
437
486
|
return False
|
|
438
487
|
|
|
439
488
|
def conditions_hold(self, mapping):
|
|
@@ -484,11 +533,11 @@ class _Matcher:
|
|
|
484
533
|
eq_cand_w = en_w_addmul(equation)
|
|
485
534
|
binding = {}
|
|
486
535
|
if not _match_struct_bind(eq_w, eq_cand_w, binding, self.const_1_vars,
|
|
487
|
-
|
|
536
|
+
self.ignore_names, assigned_mapping, self.free_vars):
|
|
488
537
|
return None
|
|
489
538
|
full_mapping = dict(assigned_mapping)
|
|
490
539
|
full_mapping.update({k: de_w_addmul(v) for k, v in binding.items()})
|
|
491
|
-
|
|
540
|
+
|
|
492
541
|
if self._is_forbidden(full_mapping):
|
|
493
542
|
return None
|
|
494
543
|
|
|
@@ -547,8 +596,10 @@ class _Matcher:
|
|
|
547
596
|
return dfs({}, 0, 0, frozenset())
|
|
548
597
|
|
|
549
598
|
|
|
550
|
-
def structure(equation, formula, formula_out, ignore_list=None, const_1=None,
|
|
551
|
-
|
|
599
|
+
def structure(equation, formula, formula_out, ignore_list=None, const_1=None,
|
|
600
|
+
positive=None, negative=None, forbidden_list=None):
|
|
601
|
+
return _Matcher(formula, formula_out, positive, negative, ignore_list,
|
|
602
|
+
const_1, forbidden_list)(equation)
|
|
552
603
|
|
|
553
604
|
|
|
554
605
|
def convert_lst(eq):
|
|
@@ -592,16 +643,15 @@ def _compile_rule(s):
|
|
|
592
643
|
if parsed_subs:
|
|
593
644
|
for key, value in parsed_subs:
|
|
594
645
|
subs.setdefault(key, [None]).append(value)
|
|
595
|
-
|
|
646
|
+
|
|
596
647
|
lst = [convert_string(x) for x in item[3:]]
|
|
597
648
|
lst += [None] * (5 - len(lst))
|
|
598
649
|
ignore_list, const_1, positive, negative, forbidden_list = lst[:5]
|
|
599
|
-
|
|
650
|
+
|
|
600
651
|
keys = tuple(subs)
|
|
601
652
|
matchers = []
|
|
602
653
|
for combination in itertools.product(*subs.values()):
|
|
603
654
|
x, y, pos, neg, forb = a, b, positive, negative, forbidden_list
|
|
604
|
-
valid_comb = True
|
|
605
655
|
for key, value in zip(keys, combination):
|
|
606
656
|
if value is not None:
|
|
607
657
|
x = replace(x, key, value)
|
|
@@ -609,22 +659,17 @@ def _compile_rule(s):
|
|
|
609
659
|
pos = _replace_in(pos, key, value)
|
|
610
660
|
neg = _replace_in(neg, key, value)
|
|
611
661
|
forb = _replace_in(forb, key, value)
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
simp_val = simplify(sub_val)
|
|
624
|
-
if simp_expr is None or simp_val is None or simp_expr == simp_val:
|
|
625
|
-
valid_comb = False
|
|
626
|
-
break
|
|
627
|
-
|
|
662
|
+
|
|
663
|
+
# FIX: check the already-substituted pairs; skip combinations that are
|
|
664
|
+
# forbidden outright (e.g. a pair that simplifies to identical sides)
|
|
665
|
+
valid_comb = True
|
|
666
|
+
for expr_part, forb_val in _as_pairs(forb):
|
|
667
|
+
se = simplify(expr_part)
|
|
668
|
+
sv = simplify(forb_val)
|
|
669
|
+
if se is None or sv is None or se == sv:
|
|
670
|
+
valid_comb = False
|
|
671
|
+
break
|
|
672
|
+
|
|
628
673
|
if valid_comb:
|
|
629
674
|
simp_x = simplify(x)
|
|
630
675
|
simp_y = simplify(y)
|