# domain/math_normalizer.py import re import logging logger = logging.getLogger(__name__) class MathCanonicalizer: @staticmethod 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