mathai 2.3.6__tar.gz → 2.3.8__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.6 → mathai-2.3.8}/PKG-INFO +1 -1
- {mathai-2.3.6 → mathai-2.3.8}/mathai/__init__.py +2 -2
- mathai-2.3.8/mathai/decision.py +134 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/formula_applier.py +96 -53
- {mathai-2.3.6 → mathai-2.3.8}/mathai/integrate.py +18 -8
- {mathai-2.3.6 → mathai-2.3.8}/mathai/linear.py +36 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/matrix.py +202 -124
- {mathai-2.3.6 → mathai-2.3.8}/mathai.egg-info/PKG-INFO +1 -1
- {mathai-2.3.6 → mathai-2.3.8}/setup.py +1 -1
- mathai-2.3.6/mathai/decision.py +0 -358
- {mathai-2.3.6 → mathai-2.3.8}/README.md +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/apart.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/base.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/diff.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/electronic.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/expand.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/factor.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/fraction.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/graphics.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/inverse.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/lambda_calculus.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/limit.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/logic.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/ml.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/mlscalar.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/ode.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/parser.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/pde.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/printeq.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/qm.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/simplify.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/statistics.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/test.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/tool.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/trig.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai/univariate_inequality.py +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai.egg-info/SOURCES.txt +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai.egg-info/dependency_links.txt +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/mathai.egg-info/top_level.txt +0 -0
- {mathai-2.3.6 → mathai-2.3.8}/setup.cfg +0 -0
|
@@ -15,7 +15,7 @@ from .linear import linear_solve
|
|
|
15
15
|
|
|
16
16
|
from .test import test
|
|
17
17
|
|
|
18
|
-
from .decision import god
|
|
18
|
+
from .decision import god
|
|
19
19
|
|
|
20
20
|
from .expand import expand
|
|
21
21
|
|
|
@@ -54,7 +54,7 @@ from .limit import limit1, limit2, limit0, limit3, limit4, solve_inf, limit5
|
|
|
54
54
|
|
|
55
55
|
from .univariate_inequality import wavycurvy, absolute, domain, handle_sqrt, prepare, eq2range, range2eq2, simple_wavycurvy
|
|
56
56
|
|
|
57
|
-
from .matrix import matrix_solve
|
|
57
|
+
from .matrix import matrix_solve, matrix_formula
|
|
58
58
|
|
|
59
59
|
from .base import *
|
|
60
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
|
|
@@ -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
|
|
@@ -254,9 +272,9 @@ def _match_struct_bind_core(eq, f, binding, const_1_vars, ignore_set, resolved_i
|
|
|
254
272
|
out = extract_polynomial_coefficients(eq_plain, item, degree)
|
|
255
273
|
if out is not None:
|
|
256
274
|
out = out[::-1]
|
|
257
|
-
while out[0] == 0:
|
|
275
|
+
while len(out) != 0 and out[0] == 0:
|
|
258
276
|
out.pop(0)
|
|
259
|
-
if len(out) == degree:
|
|
277
|
+
if len(out) == degree:
|
|
260
278
|
local_binding = binding.copy()
|
|
261
279
|
for i in range(1, len(f.children)):
|
|
262
280
|
local_binding[f.children[i].name] = out[i - 1]
|
|
@@ -293,6 +311,37 @@ def _match_struct_bind_core(eq, f, binding, const_1_vars, ignore_set, resolved_i
|
|
|
293
311
|
local_binding[f.children[1 - index].name] = operation(op_name, non_const_term)
|
|
294
312
|
binding.update(local_binding)
|
|
295
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
|
|
296
345
|
if fname.startswith("v_") and not f.children and fname in free_vars:
|
|
297
346
|
f_str = str_form(f)
|
|
298
347
|
if f_str in binding:
|
|
@@ -391,11 +440,13 @@ def _match_struct_bind(eq, f, binding, const_1_vars=None, ignore_set=None,
|
|
|
391
440
|
|
|
392
441
|
|
|
393
442
|
class _Matcher:
|
|
394
|
-
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):
|
|
395
445
|
self.orig_formula = formula
|
|
396
446
|
self.formula_out = formula_out
|
|
397
447
|
self.formula_w = en_w_addmul(formula)
|
|
398
|
-
|
|
448
|
+
has_formula = self.formula_w is not None
|
|
449
|
+
self.root_name = self.formula_w.name if has_formula else ""
|
|
399
450
|
f_sum_vars = set()
|
|
400
451
|
_extract_f_sum_vars(self.formula_w, f_sum_vars)
|
|
401
452
|
self.free_vars = frozenset(f_sum_vars)
|
|
@@ -404,15 +455,15 @@ class _Matcher:
|
|
|
404
455
|
self.ignore_list_exprs = _as_exprs(ignore_list)
|
|
405
456
|
self.ignore_set = {get_name(item) for item in self.ignore_list_exprs}
|
|
406
457
|
self.ignore_names = {item.name for item in self.ignore_list_exprs}
|
|
407
|
-
self.
|
|
408
|
-
|
|
409
|
-
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()
|
|
410
461
|
self.vl = sorted(names, key=lambda n: (0 if n in self.ignore_set else 1, n))
|
|
411
462
|
self.must_be_var = {n: (n in self.ignore_set and n not in f_sum_vars) for n in self.vl}
|
|
412
463
|
self.const_1_vars = {get_name(item) for item in _as_exprs(const_1)}
|
|
413
464
|
self.must_be_const = {n: (n in self.const_1_vars) for n in self.vl}
|
|
414
465
|
self.root_is_free = (
|
|
415
|
-
not
|
|
466
|
+
not has_formula
|
|
416
467
|
or (self.root_name.startswith("v_") and not self.formula_w.children)
|
|
417
468
|
or self.root_name in _SPECIAL_PATTERN
|
|
418
469
|
)
|
|
@@ -423,19 +474,15 @@ class _Matcher:
|
|
|
423
474
|
self._same_count = [count_same_nodes(self.orig_formula, TreeNode(x)) for x in self.vl]
|
|
424
475
|
|
|
425
476
|
def _is_forbidden(self, mapping):
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
simp_expr = simplify(subbed_expr)
|
|
436
|
-
simp_val = simplify(subbed_val)
|
|
437
|
-
if simp_expr is None or simp_val is None or simp_expr == simp_val:
|
|
438
|
-
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
|
|
439
486
|
return False
|
|
440
487
|
|
|
441
488
|
def conditions_hold(self, mapping):
|
|
@@ -486,11 +533,11 @@ class _Matcher:
|
|
|
486
533
|
eq_cand_w = en_w_addmul(equation)
|
|
487
534
|
binding = {}
|
|
488
535
|
if not _match_struct_bind(eq_w, eq_cand_w, binding, self.const_1_vars,
|
|
489
|
-
|
|
536
|
+
self.ignore_names, assigned_mapping, self.free_vars):
|
|
490
537
|
return None
|
|
491
538
|
full_mapping = dict(assigned_mapping)
|
|
492
539
|
full_mapping.update({k: de_w_addmul(v) for k, v in binding.items()})
|
|
493
|
-
|
|
540
|
+
|
|
494
541
|
if self._is_forbidden(full_mapping):
|
|
495
542
|
return None
|
|
496
543
|
|
|
@@ -549,8 +596,10 @@ class _Matcher:
|
|
|
549
596
|
return dfs({}, 0, 0, frozenset())
|
|
550
597
|
|
|
551
598
|
|
|
552
|
-
def structure(equation, formula, formula_out, ignore_list=None, const_1=None,
|
|
553
|
-
|
|
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)
|
|
554
603
|
|
|
555
604
|
|
|
556
605
|
def convert_lst(eq):
|
|
@@ -594,16 +643,15 @@ def _compile_rule(s):
|
|
|
594
643
|
if parsed_subs:
|
|
595
644
|
for key, value in parsed_subs:
|
|
596
645
|
subs.setdefault(key, [None]).append(value)
|
|
597
|
-
|
|
646
|
+
|
|
598
647
|
lst = [convert_string(x) for x in item[3:]]
|
|
599
648
|
lst += [None] * (5 - len(lst))
|
|
600
649
|
ignore_list, const_1, positive, negative, forbidden_list = lst[:5]
|
|
601
|
-
|
|
650
|
+
|
|
602
651
|
keys = tuple(subs)
|
|
603
652
|
matchers = []
|
|
604
653
|
for combination in itertools.product(*subs.values()):
|
|
605
654
|
x, y, pos, neg, forb = a, b, positive, negative, forbidden_list
|
|
606
|
-
valid_comb = True
|
|
607
655
|
for key, value in zip(keys, combination):
|
|
608
656
|
if value is not None:
|
|
609
657
|
x = replace(x, key, value)
|
|
@@ -611,22 +659,17 @@ def _compile_rule(s):
|
|
|
611
659
|
pos = _replace_in(pos, key, value)
|
|
612
660
|
neg = _replace_in(neg, key, value)
|
|
613
661
|
forb = _replace_in(forb, key, value)
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
simp_val = simplify(sub_val)
|
|
626
|
-
if simp_expr is None or simp_val is None or simp_expr == simp_val:
|
|
627
|
-
valid_comb = False
|
|
628
|
-
break
|
|
629
|
-
|
|
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
|
+
|
|
630
673
|
if valid_comb:
|
|
631
674
|
simp_x = simplify(x)
|
|
632
675
|
simp_y = simplify(y)
|
|
@@ -8,7 +8,7 @@ from .fraction import fraction
|
|
|
8
8
|
from .simplify import simplify
|
|
9
9
|
from .expand import expand
|
|
10
10
|
from .base import *
|
|
11
|
-
from .formula_applier import formula_list_applier
|
|
11
|
+
from .formula_applier import formula_list_applier, formula_applier
|
|
12
12
|
from .inverse import inverse
|
|
13
13
|
from .tool import poly
|
|
14
14
|
from fractions import Fraction
|
|
@@ -36,6 +36,8 @@ def subs_heuristic(eq, var):
|
|
|
36
36
|
if eq.name == "f_pow" and frac(eq.children[1]) is not None and frac(eq.children[1]) == Fraction(1,2):
|
|
37
37
|
if eq.children[0] == var:
|
|
38
38
|
output.append(str_form(eq))
|
|
39
|
+
if eq.name == "f_log":
|
|
40
|
+
output.append(str_form(eq))
|
|
39
41
|
if eq.name in ["f_pow"] and var.name in str_form(eq):
|
|
40
42
|
if eq.children[1].name[:2] != "v_":
|
|
41
43
|
output.append(str_form(eq))
|
|
@@ -66,20 +68,22 @@ def subs_heuristic(eq, var):
|
|
|
66
68
|
tmp = list(set([simplify(tree_form(x)) for x in output]))
|
|
67
69
|
tmp = sorted(tmp, key=lambda x: len(str_form(x)))
|
|
68
70
|
poly_term = None
|
|
69
|
-
|
|
71
|
+
poly_size = -1
|
|
70
72
|
fx = is_integrate_subs
|
|
71
73
|
output = []
|
|
72
74
|
if set(all_branch(eq)) <= set("f_add f_mul f_pow".split(" ")):
|
|
73
75
|
for item in tmp:
|
|
74
76
|
if fx(item).name == "s_true":
|
|
75
|
-
|
|
77
|
+
if len(item) > poly_size:
|
|
78
|
+
poly_term = item
|
|
79
|
+
poly_size = len(item)
|
|
76
80
|
else:
|
|
77
81
|
output.append(item)
|
|
78
82
|
p = None
|
|
79
83
|
if poly_term is None:
|
|
80
84
|
p = tmp
|
|
81
85
|
else:
|
|
82
|
-
p =
|
|
86
|
+
p = [poly_term]+output
|
|
83
87
|
last = list(set(last))
|
|
84
88
|
if len(p)>3:
|
|
85
89
|
p = p[:3]
|
|
@@ -176,12 +180,14 @@ def integrate_subs(equation, term, v1, v2, extra):
|
|
|
176
180
|
equation = simplify(equation)
|
|
177
181
|
eq = equation
|
|
178
182
|
termeq = term
|
|
179
|
-
t = inverse(
|
|
180
|
-
g = inverse(
|
|
183
|
+
t = inverse(termeq, v1)
|
|
184
|
+
g = inverse(termeq, v2)
|
|
181
185
|
if g is None:
|
|
182
186
|
return none
|
|
183
187
|
if t is None:
|
|
184
|
-
|
|
188
|
+
equation = simplify(factor2(factor0(simplify(replace(eq, g, tree_form(v2))/diff(g, v1)))))
|
|
189
|
+
if v1 in str_form(equation):
|
|
190
|
+
return none
|
|
185
191
|
else:
|
|
186
192
|
t = expand(t)
|
|
187
193
|
eq3 = diff(g, v1)
|
|
@@ -193,6 +199,9 @@ def integrate_subs(equation, term, v1, v2, extra):
|
|
|
193
199
|
eq = replace(eq, tree_form(v1), t)
|
|
194
200
|
equation = eq/eq3
|
|
195
201
|
equation = simplify(equation)
|
|
202
|
+
if v1 in str_form(equation):
|
|
203
|
+
equation = replace(equation, tree_form(v1), t)
|
|
204
|
+
equation = formula_applier(equation, "abs(x) x [] _ _ _ _")
|
|
196
205
|
if v1 in str_form(equation):
|
|
197
206
|
return none
|
|
198
207
|
return dowhile(TreeNode("f_subs", [TreeNode("f_integrate", [simplify(equation), tree_form(origv2)]),tree_form(origv2) ,g]+extra), lambda x: simplify(trig4(trig0(x))))
|
|
@@ -321,13 +330,14 @@ def integrate_full(root):
|
|
|
321
330
|
return "f_integrate" not in str_form(x)
|
|
322
331
|
result = None
|
|
323
332
|
root = normalize(root)
|
|
333
|
+
root = formula_applier(root, "(a^b)^c a^(b*c) [] b c _ _")
|
|
324
334
|
normalize2 = lambda x: normalize(x, False)
|
|
325
335
|
log = []
|
|
326
336
|
orig = copy.deepcopy(root)
|
|
327
337
|
eq = root
|
|
328
338
|
for item in [[lambda x: x, simplify, expand, normalize], [factor2, apart, normalize2, normalize], [trig1, normalize2],\
|
|
329
339
|
[factor1, normalize, trig6, normalize, expand, normalize, integrate_subs_main, normalize, factor2, simplify, integrate_trig_formula, trig1, apart, normalize2],\
|
|
330
|
-
[
|
|
340
|
+
[normalize2, integrate_subs_main, expand, normalize, byparts, normalize], [simplify, integrate_trig_formula, trig1, integrate_formula]]:
|
|
331
341
|
for item2 in item:
|
|
332
342
|
eq = item2(eq)
|
|
333
343
|
if eq not in log:
|
|
@@ -100,6 +100,42 @@ def linear_solve(eq, lst=None):
|
|
|
100
100
|
else:
|
|
101
101
|
return eq
|
|
102
102
|
|
|
103
|
+
fxconst = (lambda x: False) if lst is None else (lambda x: any(contain(x, item) for item in lst))
|
|
104
|
+
|
|
105
|
+
# 1. Collect all non-constant variables (vl) across all equations
|
|
106
|
+
vl = []
|
|
107
|
+
def varlist(node, fxconst):
|
|
108
|
+
nonlocal vl
|
|
109
|
+
if node.name[:2] == "v_" and not fxconst(node):
|
|
110
|
+
vl.append(node.name)
|
|
111
|
+
for child in node.children:
|
|
112
|
+
varlist(child, fxconst)
|
|
113
|
+
|
|
114
|
+
for item in eqlist:
|
|
115
|
+
varlist(item, fxconst)
|
|
116
|
+
vl = sorted(list(set(vl)))
|
|
117
|
+
|
|
118
|
+
# 2. Check linearity by iterating through all equations and variables:
|
|
119
|
+
# If diff w.r.t a variable still contains any variable from vl, it's non-linear.
|
|
120
|
+
for eq_item in eqlist:
|
|
121
|
+
for v in vl:
|
|
122
|
+
d = simplify(diff(eq_item, v))
|
|
123
|
+
|
|
124
|
+
# Helper to check if derivative 'd' contains any variable from vl
|
|
125
|
+
contains_var = False
|
|
126
|
+
def check_vars(node):
|
|
127
|
+
nonlocal contains_var
|
|
128
|
+
if node.name[:2] == "v_" and node.name in vl:
|
|
129
|
+
contains_var = True
|
|
130
|
+
return
|
|
131
|
+
for child in node.children:
|
|
132
|
+
check_vars(child)
|
|
133
|
+
|
|
134
|
+
check_vars(d)
|
|
135
|
+
if contains_var:
|
|
136
|
+
return None # Non-linear expression detected
|
|
137
|
+
|
|
138
|
+
# 3. Proceed with linear solver and rref if check passes
|
|
103
139
|
out = None
|
|
104
140
|
if lst is None:
|
|
105
141
|
out = linear(copy.deepcopy(eqlist), lambda x: False)
|