From 21031d8efbbba23d54b9fe52c72378befbec6d48 Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Tue, 2 Jun 2026 15:15:11 +0100 Subject: [PATCH 1/3] Added parameterized tests for summation preview rendering --- app/preview_test.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app/preview_test.py b/app/preview_test.py index f67aeb4..e6c5f2e 100644 --- a/app/preview_test.py +++ b/app/preview_test.py @@ -273,6 +273,47 @@ def test_laplace_transforms(self, response, is_latex, latex, sympy): assert result["preview"]["latex"] == latex assert result["preview"]["sympy"] == sympy + @pytest.mark.parametrize( + "response, latex, sympy", [ + # summation() with explicit bounds — rendered correctly by SymPy + ( + 'summation((4/n**2)(-1)**n cos(nx), (n, 1, oo))', + r'\sum_{n=1}^{\infty} \frac{4 \cdot \left(-1\right)^{n} \cdot \cos{\left(n \cdot x \right)}}{n^{2}}', + 'summation((4/n**2)(-1)**n cos(nx), (n, 1, oo))', + ), + # infsum as a custom prefix symbol — content must appear inside the sum, not outside + ( + 'infsum((-1)^n / n^2)', + r'\sum_{n=1}^{\infty} \frac{\left(-1\right)^{n}}{n^{2}}', + 'infsum((-1)^n / n^2)', + ), + ( + 'infsum((4/n^2)*(-1)^n*cos(nx))', + r'\sum_{n=1}^{\infty} \frac{4 \cdot \left(-1\right)^{n} \cdot \cos{\left(n \cdot x \right)}}{n^{2}}', + 'infsum((4/n^2)*(-1)^n* cos(nx))', + ), + ( + '((pi^2)/(3))+infsum((4/n^2)*(-1)^n*cos(nx))', + r'\frac{\pi^{2}}{3} + \sum_{n=1}^{\infty} \frac{4 \cdot \left(-1\right)^{n} \cdot \cos{\left(n \cdot x \right)}}{n^{2}}', + '(( pi^2)/(3))+infsum((4/n^2)*(-1)^n* cos(nx))', + ), + ] + ) + def test_sum_preview(self, response, latex, sympy): + params = { + "is_latex": False, + "strict_syntax": False, + "elementary_functions": True, + "symbols": { + "infsum": {"aliases": [], "latex": "\\sum_{n=1}^{\\infty}"}, + "pi": {"aliases": [], "latex": "\\pi"}, + }, + } + + result = preview_function(response, params) + assert result["preview"]["latex"] == latex + assert result["preview"]["sympy"] == sympy + if __name__ == "__main__": From 7e0c543a05f85127b107d40bb058a4b570ea34dc Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Tue, 2 Jun 2026 15:28:40 +0100 Subject: [PATCH 2/3] Added support for custom LaTeX handling of big operator symbols in sympy symbols --- app/utility/expression_utilities.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/app/utility/expression_utilities.py b/app/utility/expression_utilities.py index 49e9755..39903af 100644 --- a/app/utility/expression_utilities.py +++ b/app/utility/expression_utilities.py @@ -561,22 +561,33 @@ class SymbolData(TypedDict): r"(?P\\\(|\$\$|\$)(?P.*?)(?P\\\)|\$\$|\$)" ) +BIG_OPERATOR_PREFIXES = (r"\sum", r"\int", r"\prod", r"\lim", r"\bigcup", r"\bigcap") + def sympy_symbols(symbols): """Create a mapping of local variables for parsing sympy expressions. Args: symbols (SymbolDict): A dictionary of sympy symbol strings to LaTeX - symbol strings. - - Note: - Only the sympy string is used in this function. + symbol strings, or an iterable of symbol name strings. Returns: - Dict[str, Symbol]: A dictionary of sympy symbol strings to sympy - Symbol objects. + Dict[str, Symbol | type]: A dictionary mapping symbol names to SymPy + Symbol objects, or to Function subclasses for prefix operator symbols + (those whose latex begins with a big operator command like \\sum). """ - return {k: Symbol(k) for k in symbols} + result = {} + for k in symbols: + symbol_def = symbols[k] if isinstance(symbols, dict) else None + if isinstance(symbol_def, dict): + latex = extract_latex(symbol_def.get("latex", "")).strip() + if any(latex.startswith(op) for op in BIG_OPERATOR_PREFIXES): + def _latex(self, printer, _l=latex): + return _l + " " + printer.doprint(self.args[0]) + result[k] = type(k, (Function,), {"_latex": _latex}) + continue + result[k] = Symbol(k) + return result def extract_latex(symbol): @@ -684,6 +695,8 @@ def create_sympy_parsing_params(params, unsplittable_symbols=tuple(), symbol_ass } symbol_dict.update(sympy_symbols(unsplittable_symbols)) + if "symbols" in params: + symbol_dict.update(sympy_symbols(params["symbols"])) strict_syntax = params.get("strict_syntax", False) From f398d9f6597cefe57421507a57fbb9408d008fce Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Tue, 2 Jun 2026 15:39:40 +0100 Subject: [PATCH 3/3] Removed unused "symbols" parameter handling in expression_utilities --- app/utility/expression_utilities.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/utility/expression_utilities.py b/app/utility/expression_utilities.py index 39903af..d06ba95 100644 --- a/app/utility/expression_utilities.py +++ b/app/utility/expression_utilities.py @@ -695,8 +695,6 @@ def create_sympy_parsing_params(params, unsplittable_symbols=tuple(), symbol_ass } symbol_dict.update(sympy_symbols(unsplittable_symbols)) - if "symbols" in params: - symbol_dict.update(sympy_symbols(params["symbols"])) strict_syntax = params.get("strict_syntax", False)