-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnormalization.py
More file actions
82 lines (65 loc) · 2.9 KB
/
normalization.py
File metadata and controls
82 lines (65 loc) · 2.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import re
import sympy as sp
from sympy import (
symbols, sin, cos, tan, cot, asin, acos, atan, acot,
sinh, cosh, tanh, coth, asinh, acosh, atanh, acoth,
log, exp, sqrt
)
x = symbols('x')
FUNC_MAP = {
'sin': sin, 'cos': cos, 'tan': tan, 'cot': cot,
'sinh': sinh, 'cosh': cosh, 'tanh': tanh, 'coth': coth,
'asin': asin, 'acos': acos, 'atan': atan, 'acot': acot,
'asinh': asinh, 'acosh': acosh, 'atanh': atanh, 'acoth': acoth,
'log': log, 'exp': exp, 'sqrt': sqrt,
}
ALIASES = {
'ln': 'log', 'ctan': 'cot', 'ctg': 'cot', 'tg': 'tan',
'sh': 'sinh', 'ch': 'cosh', 'th': 'tanh', 'cth': 'coth',
'arccos': 'acos', 'arcsin': 'asin', 'arctan': 'atan', 'arccot': 'acot',
'arctg': 'atan', 'arccotg': 'acot',
'arcsinh': 'asinh', 'arccosh': 'acosh', 'arctanh': 'atanh',
'arccoth': 'acoth', 'arcth': 'atanh', 'arccth': 'acoth',
'log2': lambda x: log(x)/log(2),
}
def normalize_exp(expr: str) -> str:
x = sp.Symbol('x')
e = sp.E
expr = sp.sympify(expr, locals={'e': e, 'exp': sp.exp}, evaluate=True)
expr = sp.expand(expr)
expr = sp.powsimp(expr, force=True)
return expr
# Замена в поддерживаемые функции
def _apply_aliases(expr: str) -> str:
expr = expr.lower().replace('^', '**')
expr = re.sub(r'\blog2\(([^)]+)\)', r'(log(\1)/log(2))', expr)
for k, v in ALIASES.items():
expr = re.sub(rf'\b{k}(?=[a-z0-9(])', v, expr)
return expr
# Приведение к стандартному виду
def _add_brackets(expr: str) -> str:
for f in FUNC_MAP:
expr = re.sub(rf'\b{f}(\d+)\*?x\b', rf'{f}(\1*x)', expr)
expr = re.sub(rf'\b{f}\*?x\b', rf'{f}(x)', expr)
expr = re.sub(rf'\b{f}\*?x\*\*(\d+)', rf'{f}(x)**\1', expr)
return expr
# Вставка * между числами и переменными
def _insert_sign(expr: str) -> str:
expr = re.sub(r'(\d)([a-zA-Z(])', r'\1*\2', expr) # 2x → 2*x
expr = re.sub(r'([a-zA-Z\)])(\d)', r'\1*\2', expr) # x2 → x*2
expr = re.sub(r'([x\)])(?=[a-z])', r'\1*', expr) # xsin → x*sin
expr = re.sub(r'([x\)])\(', r'\1*(', expr) # x( → x*(
return expr
# Вставка * между числами и переменными
def normalize(expr: str) -> str:
expr = _apply_aliases(expr)
expr = expr.replace('^', '**')
# Защитить exp(...) от искажения exp(2*x) → __EXP__[2*x]
expr = re.sub(r'\bexp\(([^()]*)\)', r'__EXP__[\1]', expr)
expr = _add_brackets(expr) # 4. sinx → sin(x), sin2x → sin(2*x)
expr = _insert_sign(expr) # 5. Вставить * (2x → 2*x, xsin → x*sin)
expr = _add_brackets(expr) # 6. Повторное применение: sin2*x → sin(2*x)
# Вернуть exp(...) на место
expr = re.sub(r'__EXP__\[(.*?)\]', r'exp(\1)', expr)
expr = normalize_exp(expr) # 8. Приведение e^x к exp(x)
return str(expr)