| # domain/math_normalizer.py | |
| import re | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| class MathCanonicalizer: | |
| def preprocess_ocr_string(math_string: str) -> str: | |
| """ | |
| V6 Polish (P0): Aggressively cleans raw OCR math strings before SymPy parsing. | |
| - Adds explicit multiplication. | |
| - Wraps trigonometric functions in parentheses (e.g., sin x -> sin(x)). | |
| - Normalizes basic artifacts. | |
| """ | |
| if not math_string: | |
| return "" | |
| clean_str = str(math_string).strip() | |
| # 1. Standardize Whitespace | |
| clean_str = re.sub(r'\s+', ' ', clean_str) | |
| # 2. Add implicit multiplication between numbers and variables (e.g., 2x -> 2*x) | |
| # Note: SymPy handles some of this, but we do it explicitly to be safe | |
| clean_str = re.sub(r'(\d)([a-zA-Z])', r'\1*\2', clean_str) | |
| # 3. Trigonometric Functions Parentheses Guard | |
| # Matches sin, cos, tan, cot, arcsin, arccos, arctan, etc. followed by space and a variable/number | |
| # Example: 'sin x' -> 'sin(x)', 'cos 2x' -> 'cos(2*x)', 'tan ^2x' is trickier but we handle the basics. | |
| trig_funcs = r'(sin|cos|tan|cot|sec|csc|arcsin|arccos|arctan)' | |
| # Match 'sin x' or 'sin 2*x' but not 'sin(x)' | |
| # This regex looks for trig function, optional space, and then something that is not a parenthesis | |
| clean_str = re.sub(fr'{trig_funcs}\s+([a-zA-Z0-9_*]+)(?!\()', r'\1(\2)', clean_str) | |
| return clean_str | |