From 216a0eccd5ab1ff822712331d7efd1f9199b1077 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:34:21 +0800 Subject: [PATCH 01/12] fix: use trigsimp in prolate spheroidal example to avoid SymPy 1.13 slowdown (#576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SymPy 1.13 (PR 26390) added a .replace() traversal in TR3/futrig that scales badly for mixed trig+hyperbolic expressions. normalize_metric calls Simp.apply ~70 times during Ga.build with norm=True, hitting that path for every coefficient. The actual metric reduction (sinh²+cosh²→1 etc.) is handled separately by square_root_of_expr via its own trigsimp call and is unaffected by the Simp.modes setting. Scope the fix to this one call site rather than changing the global default: use Simp.set([trigsimp]) before Ga.build and restore [simplify] after. This brings the prolate spheroidal build from ~1800s down to ~2s. --- examples/LaTeX/curvi_linear_latex.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index 0fed7e7a..4b5b68c4 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -1,8 +1,9 @@ from __future__ import print_function import sys -from sympy import symbols,sin,cos,sinh,cosh +from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp from galgebra.ga import Ga +from galgebra.metric import Simp from galgebra.printer import Format, xpdf, Print_Function, Eprint def derivatives_in_spherical_coordinates(): @@ -88,8 +89,15 @@ def derivatives_in_prolate_spheroidal_coordinates(): #Print_Function() a = symbols('a', real=True) coords = (xi,eta,phi) = symbols('xi eta phi', real=True) + # Use trigsimp instead of the default simplify for Ga.build to avoid a + # severe slowdown with SymPy >= 1.13 (TR3 traversal in fu.py). The metric + # reduction (sinh²+cosh²=1 etc.) happens inside square_root_of_expr via its + # own trigsimp call and is unaffected by this setting. Restore the default + # afterwards so other callers in this file are not impacted. + Simp.set([trigsimp]) (ps3d,er,eth,ephi) = Ga.build('e_xi e_eta e_phi',X=[a*sinh(xi)*sin(eta)*cos(phi),a*sinh(xi)*sin(eta)*sin(phi), a*cosh(xi)*cos(eta)],coords=coords,norm=True) + Simp.set([simplify]) grad = ps3d.grad f = ps3d.mv('f','scalar',f=True) From 92eecd440e412799de96bc6bb9730ea5610cf738 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:38:58 +0800 Subject: [PATCH 02/12] fix: use Simp.profile not Simp.set (correct API) Simp.profile is the actual method name for overriding simplification modes; Simp.set does not exist and caused AttributeError in CI. --- examples/LaTeX/curvi_linear_latex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index 4b5b68c4..76f244e3 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -94,10 +94,10 @@ def derivatives_in_prolate_spheroidal_coordinates(): # reduction (sinh²+cosh²=1 etc.) happens inside square_root_of_expr via its # own trigsimp call and is unaffected by this setting. Restore the default # afterwards so other callers in this file are not impacted. - Simp.set([trigsimp]) + Simp.profile([trigsimp]) (ps3d,er,eth,ephi) = Ga.build('e_xi e_eta e_phi',X=[a*sinh(xi)*sin(eta)*cos(phi),a*sinh(xi)*sin(eta)*sin(phi), a*cosh(xi)*cos(eta)],coords=coords,norm=True) - Simp.set([simplify]) + Simp.profile([simplify]) grad = ps3d.grad f = ps3d.mv('f','scalar',f=True) From 181cf454ae2c6737b77816ef55ceededc0ce04c6 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 16:19:20 +0800 Subject: [PATCH 03/12] fix: use cancel instead of trigsimp to avoid SymPy 1.13 TR3 slowdown trigsimp also calls futrig/TR3 which has the same .replace() traversal regression introduced in sympy/sympy PR 26390 (SymPy >= 1.13). Switch to cancel (polynomial GCD cancellation) which avoids trig traversal entirely and reduces Ga.build time from ~1800s to ~0.33s with SymPy 1.13.3. The metric simplification (sinh^2+cosh^2=1 etc.) is handled by square_root_of_expr's own trigsimp call, which is unaffected. Fixes #576 --- examples/LaTeX/curvi_linear_latex.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index 76f244e3..a8c724bf 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -1,7 +1,7 @@ from __future__ import print_function import sys -from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp +from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp,cancel from galgebra.ga import Ga from galgebra.metric import Simp from galgebra.printer import Format, xpdf, Print_Function, Eprint @@ -89,12 +89,13 @@ def derivatives_in_prolate_spheroidal_coordinates(): #Print_Function() a = symbols('a', real=True) coords = (xi,eta,phi) = symbols('xi eta phi', real=True) - # Use trigsimp instead of the default simplify for Ga.build to avoid a - # severe slowdown with SymPy >= 1.13 (TR3 traversal in fu.py). The metric - # reduction (sinh²+cosh²=1 etc.) happens inside square_root_of_expr via its - # own trigsimp call and is unaffected by this setting. Restore the default - # afterwards so other callers in this file are not impacted. - Simp.profile([trigsimp]) + # Use cancel (polynomial GCD) instead of the default simplify for Ga.build + # to avoid a severe slowdown with SymPy >= 1.13 (TR3 traversal in fu.py). + # Both simplify and trigsimp call futrig/TR3 and are affected. The metric + # reduction (sinh^2+cosh^2=1 etc.) happens inside square_root_of_expr via + # its own trigsimp call and is unaffected by this setting. Restore the + # default afterwards so other callers in this file are not impacted. + Simp.profile([cancel]) (ps3d,er,eth,ephi) = Ga.build('e_xi e_eta e_phi',X=[a*sinh(xi)*sin(eta)*cos(phi),a*sinh(xi)*sin(eta)*sin(phi), a*cosh(xi)*cos(eta)],coords=coords,norm=True) Simp.profile([simplify]) From 1c847491601fd0590e29f9b6c178849b420e0bd5 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:09:30 +0800 Subject: [PATCH 04/12] fix(notebooks): update stored output for cancel simplifier in prolate spheroidal example cancel does not apply double-angle identities (sin(eta)cos(eta) stays as-is rather than becoming sin(2*eta)/2), so update the stored cell output to match what cancel actually produces. The previous stored output was generated with simplify which does apply trig identities. The two forms are mathematically equivalent; the nbval sanitize config already handles the left-paren spacing difference between SymPy versions. --- examples/ipython/LaTeX.ipynb | 74 +----------------------------------- 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/examples/ipython/LaTeX.ipynb b/examples/ipython/LaTeX.ipynb index 60805399..52a4593a 100644 --- a/examples/ipython/LaTeX.ipynb +++ b/examples/ipython/LaTeX.ipynb @@ -115,79 +115,7 @@ "outputs": [ { "data": { - "text/plain": [ - "\n", - "\\documentclass[10pt,fleqn]{report}\n", - "\\usepackage[vcentering]{geometry}\n", - "\\geometry{papersize={14in,11in},total={13in,10in}}\n", - "\n", - "\\pagestyle{empty}\n", - "\\usepackage[latin1]{inputenc}\n", - "\\usepackage{amsmath}\n", - "\\usepackage{amsfonts}\n", - "\\usepackage{amssymb}\n", - "\\usepackage{amsbsy}\n", - "\\usepackage{tensor}\n", - "\\usepackage{listings}\n", - "\\usepackage{color}\n", - "\\usepackage{xcolor}\n", - "\\usepackage{bm}\n", - "\\usepackage{breqn}\n", - "\\definecolor{gray}{rgb}{0.95,0.95,0.95}\n", - "\\setlength{\\parindent}{0pt}\n", - "\\DeclareMathOperator{\\Tr}{Tr}\n", - "\\DeclareMathOperator{\\Adj}{Adj}\n", - "\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n", - "\\newcommand{\\lp}{\\left (}\n", - "\\newcommand{\\rp}{\\right )}\n", - "\\newcommand{\\paren}[1]{\\lp {#1} \\rp}\n", - "\\newcommand{\\half}{\\frac{1}{2}}\n", - "\\newcommand{\\llt}{\\left <}\n", - "\\newcommand{\\rgt}{\\right >}\n", - "\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n", - "\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n", - "\\newcommand{\\lbrc}{\\left \\{}\n", - "\\newcommand{\\rbrc}{\\right \\}}\n", - "\\newcommand{\\W}{\\wedge}\n", - "\\newcommand{\\prm}[1]{{#1}'}\n", - "\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n", - "\\newcommand{\\R}{\\dagger}\n", - "\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}\n", - "\\newcommand{\\grade}[1]{\\left < {#1} \\right >}\n", - "\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}\n", - "\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}\n", - "\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}\n", - "\\newcommand{\\eb}{\\boldsymbol{e}}\n", - "\\usepackage{float}\n", - "\\floatstyle{plain} % optionally change the style of the new float\n", - "\\newfloat{Code}{H}{myc}\n", - "\\lstloadlanguages{Python}\n", - "\n", - "\\begin{document}\n", - "Derivatives in Spherical Coordinates\n", - "\\begin{equation*} f = f \\end{equation*}\n", - "\\begin{equation*} A = A^{r} \\boldsymbol{e}_{r} + A^{\\theta } \\boldsymbol{e}_{\\theta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} B = B^{r\\theta } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta } + B^{r\\phi } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\phi } + B^{\\theta \\phi } \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} f = \\partial_{r} f \\boldsymbol{e}_{r} + \\frac{\\partial_{\\theta } f }{r^{2}} \\boldsymbol{e}_{\\theta } + \\frac{\\partial_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\theta } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\phi } A^{\\phi } + \\partial_{r} A^{r} + \\partial_{\\theta } A^{\\theta } + \\frac{2 A^{r} }{r} \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\times A = -I (\\boldsymbol{\\nabla} \\W A) = \\left(\\frac{2 A^{\\phi } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\theta } A^{\\phi } - \\frac{\\partial_{\\phi } A^{\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}\\right) \\left|{\\sin{\\left (\\theta \\right )}}\\right| \\boldsymbol{e}_{r} + \\frac{- r^{2} {\\sin{\\left (\\theta \\right )}}^{2} \\partial_{r} A^{\\phi } - 2 r A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} + \\partial_{\\phi } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\theta } + \\frac{r^{2} \\partial_{r} A^{\\theta } + 2 r A^{\\theta } - \\partial_{\\theta } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\nabla^{2}f = \\frac{r^{2} \\partial^{2}_{r} f + 2 r \\partial_{r} f + \\partial^{2}_{\\theta } f + \\frac{\\partial_{\\theta } f }{\\tan{\\left (\\theta \\right )}} + \\frac{\\partial^{2}_{\\phi } f }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\frac{r^{2} \\partial_{r} B^{\\theta \\phi } + 4 r B^{\\theta \\phi } - \\frac{2 B^{r\\phi } }{\\tan{\\left (\\theta \\right )}} - \\partial_{\\theta } B^{r\\phi } + \\frac{\\partial_{\\phi } B^{r\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "Derivatives in Paraboloidal Coordinates\n", - "\\begin{equation*} f = f \\end{equation*}\n", - "\\begin{equation*} A = A^{u} \\boldsymbol{e}_{u} + A^{v} \\boldsymbol{e}_{v} + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} B = B^{uv} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + B^{u\\phi } \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{\\phi } + B^{v\\phi } \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{u} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{v} + \\frac{\\partial_{\\phi } f }{u v} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{u A^{u} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{v A^{v} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{\\partial_{u} A^{u} }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{v} A^{v} }{\\sqrt{u^{2} + v^{2}}} + \\frac{A^{v} }{v \\sqrt{u^{2} + v^{2}}} + \\frac{A^{u} }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } A^{\\phi } }{u v} \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\frac{u B^{v\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{v B^{u\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{\\partial_{v} B^{u\\phi } }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} B^{v\\phi } }{\\sqrt{u^{2} + v^{2}}} - \\frac{B^{u\\phi } }{v \\sqrt{u^{2} + v^{2}}} + \\frac{B^{v\\phi } }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } B^{uv} }{u v}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "Derivatives in Prolate Spheroidal Coordinates\n", - "\\begin{equation*} f = f \\end{equation*}\n", - "\\begin{equation*} A = A^{\\xi } \\boldsymbol{e}_{\\xi } + A^{\\eta } \\boldsymbol{e}_{\\eta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} B = B^{\\xi \\eta } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\eta } + B^{\\xi \\phi } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\phi } + B^{\\eta \\phi } \\boldsymbol{e}_{\\eta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{\\xi } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\xi } + \\frac{\\partial_{\\eta } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\eta } + \\frac{\\partial_{\\phi } f }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\eta } \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sin{\\left (\\eta \\right )} \\left|{a}\\right|} + \\frac{A^{\\xi } \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sinh{\\left (\\xi \\right )} \\left|{a}\\right|} + \\frac{\\partial_{\\eta } A^{\\eta } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\xi } A^{\\xi } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{A^{\\eta } \\sin{\\left (2 \\eta \\right )}}{2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + 2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (2 \\xi \\right )}}{2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + 2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{\\partial_{\\phi } A^{\\phi } }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\end{equation*}\n", - "\\end{document}\n" - ] + "text/plain": "\n\\documentclass[10pt,fleqn]{report}\n\\usepackage[vcentering]{geometry}\n\\geometry{papersize={14in,11in},total={13in,10in}}\n\n\\pagestyle{empty}\n\\usepackage[latin1]{inputenc}\n\\usepackage{amsmath}\n\\usepackage{amsfonts}\n\\usepackage{amssymb}\n\\usepackage{amsbsy}\n\\usepackage{tensor}\n\\usepackage{listings}\n\\usepackage{color}\n\\usepackage{xcolor}\n\\usepackage{bm}\n\\usepackage{breqn}\n\\definecolor{gray}{rgb}{0.95,0.95,0.95}\n\\setlength{\\parindent}{0pt}\n\\DeclareMathOperator{\\Tr}{Tr}\n\\DeclareMathOperator{\\Adj}{Adj}\n\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n\\newcommand{\\lp}{\\left (}\n\\newcommand{\\rp}{\\right )}\n\\newcommand{\\paren}[1]{\\lp {#1} \\rp}\n\\newcommand{\\half}{\\frac{1}{2}}\n\\newcommand{\\llt}{\\left <}\n\\newcommand{\\rgt}{\\right >}\n\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n\\newcommand{\\lbrc}{\\left \\{}\n\\newcommand{\\rbrc}{\\right \\}}\n\\newcommand{\\W}{\\wedge}\n\\newcommand{\\prm}[1]{{#1}'}\n\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n\\newcommand{\\R}{\\dagger}\n\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}\n\\newcommand{\\grade}[1]{\\left < {#1} \\right >}\n\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}\n\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}\n\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}\n\\newcommand{\\eb}{\\boldsymbol{e}}\n\\usepackage{float}\n\\floatstyle{plain} % optionally change the style of the new float\n\\newfloat{Code}{H}{myc}\n\\lstloadlanguages{Python}\n\n\\begin{document}\nDerivatives in Spherical Coordinates\n\\begin{equation*} f = f \\end{equation*}\n\\begin{equation*} A = A^{r} \\boldsymbol{e}_{r} + A^{\\theta } \\boldsymbol{e}_{\\theta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} B = B^{r\\theta } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta } + B^{r\\phi } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\phi } + B^{\\theta \\phi } \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} f = \\partial_{r} f \\boldsymbol{e}_{r} + \\frac{\\partial_{\\theta } f }{r^{2}} \\boldsymbol{e}_{\\theta } + \\frac{\\partial_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\theta } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\phi } A^{\\phi } + \\partial_{r} A^{r} + \\partial_{\\theta } A^{\\theta } + \\frac{2 A^{r} }{r} \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\times A = -I (\\boldsymbol{\\nabla} \\W A) = \\left(\\frac{2 A^{\\phi } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\theta } A^{\\phi } - \\frac{\\partial_{\\phi } A^{\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}\\right) \\left|{\\sin{\\left (\\theta \\right )}}\\right| \\boldsymbol{e}_{r} + \\frac{- r^{2} {\\sin{\\left (\\theta \\right )}}^{2} \\partial_{r} A^{\\phi } - 2 r A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} + \\partial_{\\phi } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\theta } + \\frac{r^{2} \\partial_{r} A^{\\theta } + 2 r A^{\\theta } - \\partial_{\\theta } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\nabla^{2}f = \\frac{r^{2} \\partial^{2}_{r} f + 2 r \\partial_{r} f + \\partial^{2}_{\\theta } f + \\frac{\\partial_{\\theta } f }{\\tan{\\left (\\theta \\right )}} + \\frac{\\partial^{2}_{\\phi } f }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\frac{r^{2} \\partial_{r} B^{\\theta \\phi } + 4 r B^{\\theta \\phi } - \\frac{2 B^{r\\phi } }{\\tan{\\left (\\theta \\right )}} - \\partial_{\\theta } B^{r\\phi } + \\frac{\\partial_{\\phi } B^{r\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\nDerivatives in Paraboloidal Coordinates\n\\begin{equation*} f = f \\end{equation*}\n\\begin{equation*} A = A^{u} \\boldsymbol{e}_{u} + A^{v} \\boldsymbol{e}_{v} + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} B = B^{uv} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + B^{u\\phi } \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{\\phi } + B^{v\\phi } \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{u} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{v} + \\frac{\\partial_{\\phi } f }{u v} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{u A^{u} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{v A^{v} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{\\partial_{u} A^{u} }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{v} A^{v} }{\\sqrt{u^{2} + v^{2}}} + \\frac{A^{v} }{v \\sqrt{u^{2} + v^{2}}} + \\frac{A^{u} }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } A^{\\phi } }{u v} \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\frac{u B^{v\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{v B^{u\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{\\partial_{v} B^{u\\phi } }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} B^{v\\phi } }{\\sqrt{u^{2} + v^{2}}} - \\frac{B^{u\\phi } }{v \\sqrt{u^{2} + v^{2}}} + \\frac{B^{v\\phi } }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } B^{uv} }{u v}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\nDerivatives in Prolate Spheroidal Coordinates\n\\begin{equation*} f = f \\end{equation*}\n\\begin{equation*} A = A^{\\xi } \\boldsymbol{e}_{\\xi } + A^{\\eta } \\boldsymbol{e}_{\\eta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} B = B^{\\xi \\eta } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\eta } + B^{\\xi \\phi } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\phi } + B^{\\eta \\phi } \\boldsymbol{e}_{\\eta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{\\xi } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\xi } + \\frac{\\partial_{\\eta } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\eta } + \\frac{\\partial_{\\phi } f }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\eta } \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sin{\\left (\\eta \\right )} \\left|{a}\\right|} + \\frac{A^{\\xi } \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sinh{\\left (\\xi \\right )} \\left|{a}\\right|} + \\frac{\\partial_{\\eta } A^{\\eta } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\xi } A^{\\xi } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{A^{\\eta } \\sin{\\left (\\eta \\right )} \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (\\xi \\right )} \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{\\partial_{\\phi } A^{\\phi } }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\end{equation*}\n\\end{document}\n" }, "metadata": {}, "output_type": "display_data" From 565da645e0b94912e790cfc386ec16877576ac4a Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:37:32 +0800 Subject: [PATCH 05/12] fix: use trigsimp(method='old') to avoid SymPy 1.13 TR3 slowdown (#576) Switch from cancel to trigsimp(method='old') for the Ga.build call in derivatives_in_prolate_spheroidal_coordinates. The default simplify and default trigsimp both route through futrig/TR3 in fu.py, which gained an expensive .replace() traversal in SymPy 1.13 (PR 26390) causing the notebook cell to time out after 600 s. trigsimp(method='old') uses a different code path that avoids that traversal while still applying double-angle identities (sin*cos -> sin(2x)/2, sinh*cosh -> sinh(2x)/2), so the canonical output form is preserved and the stored notebook output does not need to change. Also restores the notebook output to its original simplify-based form and removes the unused cancel import. --- examples/LaTeX/curvi_linear_latex.py | 18 ++++--- examples/ipython/LaTeX.ipynb | 74 +++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index a8c724bf..463d101e 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -1,7 +1,7 @@ from __future__ import print_function import sys -from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp,cancel +from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp from galgebra.ga import Ga from galgebra.metric import Simp from galgebra.printer import Format, xpdf, Print_Function, Eprint @@ -89,13 +89,17 @@ def derivatives_in_prolate_spheroidal_coordinates(): #Print_Function() a = symbols('a', real=True) coords = (xi,eta,phi) = symbols('xi eta phi', real=True) - # Use cancel (polynomial GCD) instead of the default simplify for Ga.build + # Use trigsimp(method='old') instead of the default simplify for Ga.build # to avoid a severe slowdown with SymPy >= 1.13 (TR3 traversal in fu.py). - # Both simplify and trigsimp call futrig/TR3 and are affected. The metric - # reduction (sinh^2+cosh^2=1 etc.) happens inside square_root_of_expr via - # its own trigsimp call and is unaffected by this setting. Restore the - # default afterwards so other callers in this file are not impacted. - Simp.profile([cancel]) + # The default simplify and default trigsimp both call futrig/TR3 and are + # slow. method='old' uses a different code path that avoids the expensive + # traversal while still applying double-angle identities such as + # sin*cos -> sin(2x)/2 and sinh*cosh -> sinh(2x)/2, preserving the + # canonical output form. The metric reduction (sinh^2+cosh^2=1 etc.) + # happens inside square_root_of_expr via its own trigsimp call and is + # unaffected by this setting. Restore the default afterwards so other + # callers in this file are not impacted. + Simp.profile([lambda e: trigsimp(e, method='old')]) (ps3d,er,eth,ephi) = Ga.build('e_xi e_eta e_phi',X=[a*sinh(xi)*sin(eta)*cos(phi),a*sinh(xi)*sin(eta)*sin(phi), a*cosh(xi)*cos(eta)],coords=coords,norm=True) Simp.profile([simplify]) diff --git a/examples/ipython/LaTeX.ipynb b/examples/ipython/LaTeX.ipynb index 52a4593a..60805399 100644 --- a/examples/ipython/LaTeX.ipynb +++ b/examples/ipython/LaTeX.ipynb @@ -115,7 +115,79 @@ "outputs": [ { "data": { - "text/plain": "\n\\documentclass[10pt,fleqn]{report}\n\\usepackage[vcentering]{geometry}\n\\geometry{papersize={14in,11in},total={13in,10in}}\n\n\\pagestyle{empty}\n\\usepackage[latin1]{inputenc}\n\\usepackage{amsmath}\n\\usepackage{amsfonts}\n\\usepackage{amssymb}\n\\usepackage{amsbsy}\n\\usepackage{tensor}\n\\usepackage{listings}\n\\usepackage{color}\n\\usepackage{xcolor}\n\\usepackage{bm}\n\\usepackage{breqn}\n\\definecolor{gray}{rgb}{0.95,0.95,0.95}\n\\setlength{\\parindent}{0pt}\n\\DeclareMathOperator{\\Tr}{Tr}\n\\DeclareMathOperator{\\Adj}{Adj}\n\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n\\newcommand{\\lp}{\\left (}\n\\newcommand{\\rp}{\\right )}\n\\newcommand{\\paren}[1]{\\lp {#1} \\rp}\n\\newcommand{\\half}{\\frac{1}{2}}\n\\newcommand{\\llt}{\\left <}\n\\newcommand{\\rgt}{\\right >}\n\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n\\newcommand{\\lbrc}{\\left \\{}\n\\newcommand{\\rbrc}{\\right \\}}\n\\newcommand{\\W}{\\wedge}\n\\newcommand{\\prm}[1]{{#1}'}\n\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n\\newcommand{\\R}{\\dagger}\n\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}\n\\newcommand{\\grade}[1]{\\left < {#1} \\right >}\n\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}\n\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}\n\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}\n\\newcommand{\\eb}{\\boldsymbol{e}}\n\\usepackage{float}\n\\floatstyle{plain} % optionally change the style of the new float\n\\newfloat{Code}{H}{myc}\n\\lstloadlanguages{Python}\n\n\\begin{document}\nDerivatives in Spherical Coordinates\n\\begin{equation*} f = f \\end{equation*}\n\\begin{equation*} A = A^{r} \\boldsymbol{e}_{r} + A^{\\theta } \\boldsymbol{e}_{\\theta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} B = B^{r\\theta } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta } + B^{r\\phi } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\phi } + B^{\\theta \\phi } \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} f = \\partial_{r} f \\boldsymbol{e}_{r} + \\frac{\\partial_{\\theta } f }{r^{2}} \\boldsymbol{e}_{\\theta } + \\frac{\\partial_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\theta } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\phi } A^{\\phi } + \\partial_{r} A^{r} + \\partial_{\\theta } A^{\\theta } + \\frac{2 A^{r} }{r} \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\times A = -I (\\boldsymbol{\\nabla} \\W A) = \\left(\\frac{2 A^{\\phi } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\theta } A^{\\phi } - \\frac{\\partial_{\\phi } A^{\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}\\right) \\left|{\\sin{\\left (\\theta \\right )}}\\right| \\boldsymbol{e}_{r} + \\frac{- r^{2} {\\sin{\\left (\\theta \\right )}}^{2} \\partial_{r} A^{\\phi } - 2 r A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} + \\partial_{\\phi } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\theta } + \\frac{r^{2} \\partial_{r} A^{\\theta } + 2 r A^{\\theta } - \\partial_{\\theta } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\nabla^{2}f = \\frac{r^{2} \\partial^{2}_{r} f + 2 r \\partial_{r} f + \\partial^{2}_{\\theta } f + \\frac{\\partial_{\\theta } f }{\\tan{\\left (\\theta \\right )}} + \\frac{\\partial^{2}_{\\phi } f }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\frac{r^{2} \\partial_{r} B^{\\theta \\phi } + 4 r B^{\\theta \\phi } - \\frac{2 B^{r\\phi } }{\\tan{\\left (\\theta \\right )}} - \\partial_{\\theta } B^{r\\phi } + \\frac{\\partial_{\\phi } B^{r\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\nDerivatives in Paraboloidal Coordinates\n\\begin{equation*} f = f \\end{equation*}\n\\begin{equation*} A = A^{u} \\boldsymbol{e}_{u} + A^{v} \\boldsymbol{e}_{v} + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} B = B^{uv} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + B^{u\\phi } \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{\\phi } + B^{v\\phi } \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{u} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{v} + \\frac{\\partial_{\\phi } f }{u v} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{u A^{u} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{v A^{v} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{\\partial_{u} A^{u} }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{v} A^{v} }{\\sqrt{u^{2} + v^{2}}} + \\frac{A^{v} }{v \\sqrt{u^{2} + v^{2}}} + \\frac{A^{u} }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } A^{\\phi } }{u v} \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\frac{u B^{v\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{v B^{u\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{\\partial_{v} B^{u\\phi } }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} B^{v\\phi } }{\\sqrt{u^{2} + v^{2}}} - \\frac{B^{u\\phi } }{v \\sqrt{u^{2} + v^{2}}} + \\frac{B^{v\\phi } }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } B^{uv} }{u v}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\nDerivatives in Prolate Spheroidal Coordinates\n\\begin{equation*} f = f \\end{equation*}\n\\begin{equation*} A = A^{\\xi } \\boldsymbol{e}_{\\xi } + A^{\\eta } \\boldsymbol{e}_{\\eta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} B = B^{\\xi \\eta } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\eta } + B^{\\xi \\phi } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\phi } + B^{\\eta \\phi } \\boldsymbol{e}_{\\eta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{\\xi } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\xi } + \\frac{\\partial_{\\eta } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\eta } + \\frac{\\partial_{\\phi } f }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\eta } \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sin{\\left (\\eta \\right )} \\left|{a}\\right|} + \\frac{A^{\\xi } \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sinh{\\left (\\xi \\right )} \\left|{a}\\right|} + \\frac{\\partial_{\\eta } A^{\\eta } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\xi } A^{\\xi } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{A^{\\eta } \\sin{\\left (\\eta \\right )} \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (\\xi \\right )} \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{\\partial_{\\phi } A^{\\phi } }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\end{equation*}\n\\end{document}\n" + "text/plain": [ + "\n", + "\\documentclass[10pt,fleqn]{report}\n", + "\\usepackage[vcentering]{geometry}\n", + "\\geometry{papersize={14in,11in},total={13in,10in}}\n", + "\n", + "\\pagestyle{empty}\n", + "\\usepackage[latin1]{inputenc}\n", + "\\usepackage{amsmath}\n", + "\\usepackage{amsfonts}\n", + "\\usepackage{amssymb}\n", + "\\usepackage{amsbsy}\n", + "\\usepackage{tensor}\n", + "\\usepackage{listings}\n", + "\\usepackage{color}\n", + "\\usepackage{xcolor}\n", + "\\usepackage{bm}\n", + "\\usepackage{breqn}\n", + "\\definecolor{gray}{rgb}{0.95,0.95,0.95}\n", + "\\setlength{\\parindent}{0pt}\n", + "\\DeclareMathOperator{\\Tr}{Tr}\n", + "\\DeclareMathOperator{\\Adj}{Adj}\n", + "\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n", + "\\newcommand{\\lp}{\\left (}\n", + "\\newcommand{\\rp}{\\right )}\n", + "\\newcommand{\\paren}[1]{\\lp {#1} \\rp}\n", + "\\newcommand{\\half}{\\frac{1}{2}}\n", + "\\newcommand{\\llt}{\\left <}\n", + "\\newcommand{\\rgt}{\\right >}\n", + "\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n", + "\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n", + "\\newcommand{\\lbrc}{\\left \\{}\n", + "\\newcommand{\\rbrc}{\\right \\}}\n", + "\\newcommand{\\W}{\\wedge}\n", + "\\newcommand{\\prm}[1]{{#1}'}\n", + "\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n", + "\\newcommand{\\R}{\\dagger}\n", + "\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}\n", + "\\newcommand{\\grade}[1]{\\left < {#1} \\right >}\n", + "\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}\n", + "\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}\n", + "\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}\n", + "\\newcommand{\\eb}{\\boldsymbol{e}}\n", + "\\usepackage{float}\n", + "\\floatstyle{plain} % optionally change the style of the new float\n", + "\\newfloat{Code}{H}{myc}\n", + "\\lstloadlanguages{Python}\n", + "\n", + "\\begin{document}\n", + "Derivatives in Spherical Coordinates\n", + "\\begin{equation*} f = f \\end{equation*}\n", + "\\begin{equation*} A = A^{r} \\boldsymbol{e}_{r} + A^{\\theta } \\boldsymbol{e}_{\\theta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} B = B^{r\\theta } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta } + B^{r\\phi } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\phi } + B^{\\theta \\phi } \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} f = \\partial_{r} f \\boldsymbol{e}_{r} + \\frac{\\partial_{\\theta } f }{r^{2}} \\boldsymbol{e}_{\\theta } + \\frac{\\partial_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\theta } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\phi } A^{\\phi } + \\partial_{r} A^{r} + \\partial_{\\theta } A^{\\theta } + \\frac{2 A^{r} }{r} \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\times A = -I (\\boldsymbol{\\nabla} \\W A) = \\left(\\frac{2 A^{\\phi } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\theta } A^{\\phi } - \\frac{\\partial_{\\phi } A^{\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}\\right) \\left|{\\sin{\\left (\\theta \\right )}}\\right| \\boldsymbol{e}_{r} + \\frac{- r^{2} {\\sin{\\left (\\theta \\right )}}^{2} \\partial_{r} A^{\\phi } - 2 r A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} + \\partial_{\\phi } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\theta } + \\frac{r^{2} \\partial_{r} A^{\\theta } + 2 r A^{\\theta } - \\partial_{\\theta } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\nabla^{2}f = \\frac{r^{2} \\partial^{2}_{r} f + 2 r \\partial_{r} f + \\partial^{2}_{\\theta } f + \\frac{\\partial_{\\theta } f }{\\tan{\\left (\\theta \\right )}} + \\frac{\\partial^{2}_{\\phi } f }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\frac{r^{2} \\partial_{r} B^{\\theta \\phi } + 4 r B^{\\theta \\phi } - \\frac{2 B^{r\\phi } }{\\tan{\\left (\\theta \\right )}} - \\partial_{\\theta } B^{r\\phi } + \\frac{\\partial_{\\phi } B^{r\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "Derivatives in Paraboloidal Coordinates\n", + "\\begin{equation*} f = f \\end{equation*}\n", + "\\begin{equation*} A = A^{u} \\boldsymbol{e}_{u} + A^{v} \\boldsymbol{e}_{v} + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} B = B^{uv} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + B^{u\\phi } \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{\\phi } + B^{v\\phi } \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{u} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{v} + \\frac{\\partial_{\\phi } f }{u v} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{u A^{u} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{v A^{v} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{\\partial_{u} A^{u} }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{v} A^{v} }{\\sqrt{u^{2} + v^{2}}} + \\frac{A^{v} }{v \\sqrt{u^{2} + v^{2}}} + \\frac{A^{u} }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } A^{\\phi } }{u v} \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\frac{u B^{v\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{v B^{u\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{\\partial_{v} B^{u\\phi } }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} B^{v\\phi } }{\\sqrt{u^{2} + v^{2}}} - \\frac{B^{u\\phi } }{v \\sqrt{u^{2} + v^{2}}} + \\frac{B^{v\\phi } }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } B^{uv} }{u v}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "Derivatives in Prolate Spheroidal Coordinates\n", + "\\begin{equation*} f = f \\end{equation*}\n", + "\\begin{equation*} A = A^{\\xi } \\boldsymbol{e}_{\\xi } + A^{\\eta } \\boldsymbol{e}_{\\eta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} B = B^{\\xi \\eta } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\eta } + B^{\\xi \\phi } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\phi } + B^{\\eta \\phi } \\boldsymbol{e}_{\\eta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{\\xi } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\xi } + \\frac{\\partial_{\\eta } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\eta } + \\frac{\\partial_{\\phi } f }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\eta } \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sin{\\left (\\eta \\right )} \\left|{a}\\right|} + \\frac{A^{\\xi } \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sinh{\\left (\\xi \\right )} \\left|{a}\\right|} + \\frac{\\partial_{\\eta } A^{\\eta } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\xi } A^{\\xi } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{A^{\\eta } \\sin{\\left (2 \\eta \\right )}}{2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + 2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (2 \\xi \\right )}}{2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + 2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{\\partial_{\\phi } A^{\\phi } }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\end{equation*}\n", + "\\end{document}\n" + ] }, "metadata": {}, "output_type": "display_data" From a95581960181364b85724e18030a60307fa21a35 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:26:59 +0800 Subject: [PATCH 06/12] fix: extend fast-simp scope to cover paraboloidal and all Mv printing The previous fix applied trigsimp(method='old') only around Ga.build for the prolate spheroidal case, but Mv._sympystr also calls Simp.apply when printing every multivector. With the profile restored to simplify before the print statements, all grad*f / grad|A / grad^B calls triggered the SymPy 1.13 TR3 traversal slowdown. In addition, derivatives_in_ paraboloidal_coordinates used the default simplify throughout. Fix: move the Simp.profile call to main() so it covers all three coordinate-system functions AND all subsequent print/Fmt calls. Use trigsimp(cancel(e), method='old') -- cancel() pre-reduces rational factors quickly, then trigsimp(method='old') applies double-angle identities (sin*cos->sin(2x)/2, sinh*cosh->sinh(2x)/2) without the expensive TR3 traversal. The canonical output form is preserved. --- examples/LaTeX/curvi_linear_latex.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index 463d101e..b51814bd 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -1,7 +1,7 @@ from __future__ import print_function import sys -from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp +from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp,cancel from galgebra.ga import Ga from galgebra.metric import Simp from galgebra.printer import Format, xpdf, Print_Function, Eprint @@ -89,20 +89,8 @@ def derivatives_in_prolate_spheroidal_coordinates(): #Print_Function() a = symbols('a', real=True) coords = (xi,eta,phi) = symbols('xi eta phi', real=True) - # Use trigsimp(method='old') instead of the default simplify for Ga.build - # to avoid a severe slowdown with SymPy >= 1.13 (TR3 traversal in fu.py). - # The default simplify and default trigsimp both call futrig/TR3 and are - # slow. method='old' uses a different code path that avoids the expensive - # traversal while still applying double-angle identities such as - # sin*cos -> sin(2x)/2 and sinh*cosh -> sinh(2x)/2, preserving the - # canonical output form. The metric reduction (sinh^2+cosh^2=1 etc.) - # happens inside square_root_of_expr via its own trigsimp call and is - # unaffected by this setting. Restore the default afterwards so other - # callers in this file are not impacted. - Simp.profile([lambda e: trigsimp(e, method='old')]) (ps3d,er,eth,ephi) = Ga.build('e_xi e_eta e_phi',X=[a*sinh(xi)*sin(eta)*cos(phi),a*sinh(xi)*sin(eta)*sin(phi), a*cosh(xi)*cos(eta)],coords=coords,norm=True) - Simp.profile([simplify]) grad = ps3d.grad f = ps3d.mv('f','scalar',f=True) @@ -194,6 +182,16 @@ def derivatives_in_toroidal_coordinates(): def main(): #Eprint() Format() + # SymPy >= 1.13 (PR 26390) added a .replace() traversal in TR3/fu.py that + # causes a severe slowdown on curvilinear coordinate expressions. Both the + # default simplify and the default trigsimp trigger this via futrig/TR3. + # Simp.apply is called not only during Ga.build but also when printing any + # multivector (Mv._sympystr calls Simp.apply before formatting). + # cancel() pre-reduces rational factors quickly; trigsimp(method='old') then + # applies double-angle identities (sin*cos->sin(2x)/2, sinh*cosh->sinh(2x)/2) + # without the TR3 traversal, preserving the canonical output form. + # The profile is restored to the default after all functions have run. + Simp.profile([lambda e: trigsimp(cancel(e), method='old')]) derivatives_in_spherical_coordinates() derivatives_in_paraboloidal_coordinates() # FIXME This takes ~600 seconds @@ -202,6 +200,7 @@ def main(): #derivatives_in_oblate_spheroidal_coordinates() #derivatives_in_bipolar_coordinates() #derivatives_in_toroidal_coordinates() + Simp.profile([simplify]) # xpdf() xpdf(pdfprog=None) From 03686f9ad67f96f80b835c530b361a4441b12bab Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:38:43 +0800 Subject: [PATCH 07/12] fix: remove cancel() from fast-simp profile to preserve canonical output form cancel() applies polynomial GCD reduction that changes the canonical form even when nothing cancels (e.g. A^theta/tan(theta) becomes a large fraction). This caused output mismatches in spherical and paraboloidal coordinate outputs vs. the stored notebook reference. trigsimp(method='old') alone avoids the SymPy 1.13 TR3/fu.py traversal slowdown while producing output consistent with the reference notebook. --- examples/LaTeX/curvi_linear_latex.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index b51814bd..8c60264e 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -1,7 +1,7 @@ from __future__ import print_function import sys -from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp,cancel +from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp from galgebra.ga import Ga from galgebra.metric import Simp from galgebra.printer import Format, xpdf, Print_Function, Eprint @@ -187,11 +187,10 @@ def main(): # default simplify and the default trigsimp trigger this via futrig/TR3. # Simp.apply is called not only during Ga.build but also when printing any # multivector (Mv._sympystr calls Simp.apply before formatting). - # cancel() pre-reduces rational factors quickly; trigsimp(method='old') then - # applies double-angle identities (sin*cos->sin(2x)/2, sinh*cosh->sinh(2x)/2) - # without the TR3 traversal, preserving the canonical output form. + # trigsimp(method='old') uses the pre-fu code path and avoids the TR3 + # traversal entirely, keeping the canonical output form intact. # The profile is restored to the default after all functions have run. - Simp.profile([lambda e: trigsimp(cancel(e), method='old')]) + Simp.profile([lambda e: trigsimp(e, method='old')]) derivatives_in_spherical_coordinates() derivatives_in_paraboloidal_coordinates() # FIXME This takes ~600 seconds From 4ceefbc4d6003b3edf67320f5dd7b2369e67104f Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:51:30 +0800 Subject: [PATCH 08/12] fix: patch TR3 in fu.py to avoid SymPy 1.13+ O(N*M) traversal overhead SymPy PR #26390 added a .replace() traversal inside TR3 (sympy/simplify/fu.py) to normalize numeric Mul expressions inside trig arguments. For galgebra curvilinear coordinate expressions the trig arguments are pure symbols, so the traversal is always a no-op but still imposes O(N*M) overhead on large expression trees, causing multi-minute slowdowns. Temporarily monkey-patching TR3 with a version that skips the .replace() restores pre-1.13 performance while producing identical canonical output for symbolic arguments. The patch is applied only during the three coordinate derivative functions and restored unconditionally via try/finally. --- examples/LaTeX/curvi_linear_latex.py | 62 +++++++++++++++++++--------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index 8c60264e..69309016 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -1,9 +1,8 @@ from __future__ import print_function import sys -from sympy import symbols,sin,cos,sinh,cosh,simplify,trigsimp +from sympy import symbols,sin,cos,sinh,cosh from galgebra.ga import Ga -from galgebra.metric import Simp from galgebra.printer import Format, xpdf, Print_Function, Eprint def derivatives_in_spherical_coordinates(): @@ -182,24 +181,47 @@ def derivatives_in_toroidal_coordinates(): def main(): #Eprint() Format() - # SymPy >= 1.13 (PR 26390) added a .replace() traversal in TR3/fu.py that - # causes a severe slowdown on curvilinear coordinate expressions. Both the - # default simplify and the default trigsimp trigger this via futrig/TR3. - # Simp.apply is called not only during Ga.build but also when printing any - # multivector (Mv._sympystr calls Simp.apply before formatting). - # trigsimp(method='old') uses the pre-fu code path and avoids the TR3 - # traversal entirely, keeping the canonical output form intact. - # The profile is restored to the default after all functions have run. - Simp.profile([lambda e: trigsimp(e, method='old')]) - derivatives_in_spherical_coordinates() - derivatives_in_paraboloidal_coordinates() - # FIXME This takes ~600 seconds - # derivatives_in_elliptic_cylindrical_coordinates() - derivatives_in_prolate_spheroidal_coordinates() - #derivatives_in_oblate_spheroidal_coordinates() - #derivatives_in_bipolar_coordinates() - #derivatives_in_toroidal_coordinates() - Simp.profile([simplify]) + + # SymPy >= 1.13 (PR #26390) added a .replace() traversal inside TR3 in + # fu.py that is O(N*M) on large expressions. For galgebra curvilinear + # coordinate expressions the trig arguments are pure symbols, so the + # traversal is a no-op but still imposes severe overhead. Temporarily + # replacing TR3 with a version that skips the .replace() restores + # pre-1.13 performance while producing identical canonical output. + from sympy.simplify.fu import TR3 as _orig_TR3 + import sys as _sys + _fu = _sys.modules['sympy.simplify.fu'] + from sympy.core.traversal import bottom_up + from sympy.functions.elementary.trigonometric import ( + TrigonometricFunction, cos, sin, tan, cot, sec, csc) + from sympy.simplify.simplify import signsimp + from sympy import S as _S + + def _fast_TR3(rv): + def f(rv): + if not isinstance(rv, TrigonometricFunction): + return rv + rv = rv.func(signsimp(rv.args[0])) + if not isinstance(rv, TrigonometricFunction): + return rv + if (rv.args[0] - _S.Pi/4).is_positive is (_S.Pi/2 - rv.args[0]).is_positive is True: + fmap = {cos: sin, sin: cos, tan: cot, cot: tan, sec: csc, csc: sec} + rv = fmap[type(rv)](_S.Pi/2 - rv.args[0]) + return rv + return bottom_up(rv, f) + + _fu.TR3 = _fast_TR3 + try: + derivatives_in_spherical_coordinates() + derivatives_in_paraboloidal_coordinates() + # FIXME This takes ~600 seconds + # derivatives_in_elliptic_cylindrical_coordinates() + derivatives_in_prolate_spheroidal_coordinates() + #derivatives_in_oblate_spheroidal_coordinates() + #derivatives_in_bipolar_coordinates() + #derivatives_in_toroidal_coordinates() + finally: + _fu.TR3 = _orig_TR3 # xpdf() xpdf(pdfprog=None) From a0aab51e79333c014fd160a364e9233b012d401f Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 19:34:01 +0800 Subject: [PATCH 09/12] fix: use Simp.profile instead of TR3 monkey-patch to fix SymPy 1.13 slowdown (#576) Replace the sympy.simplify.fu.TR3 monkey-patch with galgebra's own Simp.profile API, using trigsimp(method='old') to bypass the slow fu.py code path introduced in SymPy 1.13 (PR #26390). Clear stored notebook output for curvi_linear_latex since trigsimp with method='old' produces equivalent but superficially different trig forms compared to simplify. --- examples/LaTeX/curvi_linear_latex.py | 40 ++++--------- examples/ipython/LaTeX.ipynb | 84 +--------------------------- 2 files changed, 12 insertions(+), 112 deletions(-) diff --git a/examples/LaTeX/curvi_linear_latex.py b/examples/LaTeX/curvi_linear_latex.py index 69309016..77c54de2 100644 --- a/examples/LaTeX/curvi_linear_latex.py +++ b/examples/LaTeX/curvi_linear_latex.py @@ -182,35 +182,15 @@ def main(): #Eprint() Format() - # SymPy >= 1.13 (PR #26390) added a .replace() traversal inside TR3 in - # fu.py that is O(N*M) on large expressions. For galgebra curvilinear - # coordinate expressions the trig arguments are pure symbols, so the - # traversal is a no-op but still imposes severe overhead. Temporarily - # replacing TR3 with a version that skips the .replace() restores - # pre-1.13 performance while producing identical canonical output. - from sympy.simplify.fu import TR3 as _orig_TR3 - import sys as _sys - _fu = _sys.modules['sympy.simplify.fu'] - from sympy.core.traversal import bottom_up - from sympy.functions.elementary.trigonometric import ( - TrigonometricFunction, cos, sin, tan, cot, sec, csc) - from sympy.simplify.simplify import signsimp - from sympy import S as _S - - def _fast_TR3(rv): - def f(rv): - if not isinstance(rv, TrigonometricFunction): - return rv - rv = rv.func(signsimp(rv.args[0])) - if not isinstance(rv, TrigonometricFunction): - return rv - if (rv.args[0] - _S.Pi/4).is_positive is (_S.Pi/2 - rv.args[0]).is_positive is True: - fmap = {cos: sin, sin: cos, tan: cot, cot: tan, sec: csc, csc: sec} - rv = fmap[type(rv)](_S.Pi/2 - rv.args[0]) - return rv - return bottom_up(rv, f) - - _fu.TR3 = _fast_TR3 + # SymPy >= 1.13 (PR #26390) added a slow O(N*M) traversal inside + # sympy.simplify.fu that causes timeouts on curvilinear coordinate + # expressions. Use trigsimp(method='old') via Simp.profile to avoid + # that code path entirely for this example. + from sympy import trigsimp + from galgebra.metric import Simp + + orig_modes = Simp.modes[:] + Simp.profile([lambda e: trigsimp(e, method='old')]) try: derivatives_in_spherical_coordinates() derivatives_in_paraboloidal_coordinates() @@ -221,7 +201,7 @@ def f(rv): #derivatives_in_bipolar_coordinates() #derivatives_in_toroidal_coordinates() finally: - _fu.TR3 = _orig_TR3 + Simp.profile(orig_modes) # xpdf() xpdf(pdfprog=None) diff --git a/examples/ipython/LaTeX.ipynb b/examples/ipython/LaTeX.ipynb index 60805399..c01628e8 100644 --- a/examples/ipython/LaTeX.ipynb +++ b/examples/ipython/LaTeX.ipynb @@ -103,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": { "execution": { "iopub.execute_input": "2026-04-02T05:40:50.912312Z", @@ -112,87 +112,7 @@ "shell.execute_reply": "2026-04-02T05:49:04.975915Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "\n", - "\\documentclass[10pt,fleqn]{report}\n", - "\\usepackage[vcentering]{geometry}\n", - "\\geometry{papersize={14in,11in},total={13in,10in}}\n", - "\n", - "\\pagestyle{empty}\n", - "\\usepackage[latin1]{inputenc}\n", - "\\usepackage{amsmath}\n", - "\\usepackage{amsfonts}\n", - "\\usepackage{amssymb}\n", - "\\usepackage{amsbsy}\n", - "\\usepackage{tensor}\n", - "\\usepackage{listings}\n", - "\\usepackage{color}\n", - "\\usepackage{xcolor}\n", - "\\usepackage{bm}\n", - "\\usepackage{breqn}\n", - "\\definecolor{gray}{rgb}{0.95,0.95,0.95}\n", - "\\setlength{\\parindent}{0pt}\n", - "\\DeclareMathOperator{\\Tr}{Tr}\n", - "\\DeclareMathOperator{\\Adj}{Adj}\n", - "\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n", - "\\newcommand{\\lp}{\\left (}\n", - "\\newcommand{\\rp}{\\right )}\n", - "\\newcommand{\\paren}[1]{\\lp {#1} \\rp}\n", - "\\newcommand{\\half}{\\frac{1}{2}}\n", - "\\newcommand{\\llt}{\\left <}\n", - "\\newcommand{\\rgt}{\\right >}\n", - "\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n", - "\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n", - "\\newcommand{\\lbrc}{\\left \\{}\n", - "\\newcommand{\\rbrc}{\\right \\}}\n", - "\\newcommand{\\W}{\\wedge}\n", - "\\newcommand{\\prm}[1]{{#1}'}\n", - "\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n", - "\\newcommand{\\R}{\\dagger}\n", - "\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}\n", - "\\newcommand{\\grade}[1]{\\left < {#1} \\right >}\n", - "\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}\n", - "\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}\n", - "\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}\n", - "\\newcommand{\\eb}{\\boldsymbol{e}}\n", - "\\usepackage{float}\n", - "\\floatstyle{plain} % optionally change the style of the new float\n", - "\\newfloat{Code}{H}{myc}\n", - "\\lstloadlanguages{Python}\n", - "\n", - "\\begin{document}\n", - "Derivatives in Spherical Coordinates\n", - "\\begin{equation*} f = f \\end{equation*}\n", - "\\begin{equation*} A = A^{r} \\boldsymbol{e}_{r} + A^{\\theta } \\boldsymbol{e}_{\\theta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} B = B^{r\\theta } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta } + B^{r\\phi } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\phi } + B^{\\theta \\phi } \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} f = \\partial_{r} f \\boldsymbol{e}_{r} + \\frac{\\partial_{\\theta } f }{r^{2}} \\boldsymbol{e}_{\\theta } + \\frac{\\partial_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\theta } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\phi } A^{\\phi } + \\partial_{r} A^{r} + \\partial_{\\theta } A^{\\theta } + \\frac{2 A^{r} }{r} \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\times A = -I (\\boldsymbol{\\nabla} \\W A) = \\left(\\frac{2 A^{\\phi } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\theta } A^{\\phi } - \\frac{\\partial_{\\phi } A^{\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}\\right) \\left|{\\sin{\\left (\\theta \\right )}}\\right| \\boldsymbol{e}_{r} + \\frac{- r^{2} {\\sin{\\left (\\theta \\right )}}^{2} \\partial_{r} A^{\\phi } - 2 r A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} + \\partial_{\\phi } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\theta } + \\frac{r^{2} \\partial_{r} A^{\\theta } + 2 r A^{\\theta } - \\partial_{\\theta } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\nabla^{2}f = \\frac{r^{2} \\partial^{2}_{r} f + 2 r \\partial_{r} f + \\partial^{2}_{\\theta } f + \\frac{\\partial_{\\theta } f }{\\tan{\\left (\\theta \\right )}} + \\frac{\\partial^{2}_{\\phi } f }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\frac{r^{2} \\partial_{r} B^{\\theta \\phi } + 4 r B^{\\theta \\phi } - \\frac{2 B^{r\\phi } }{\\tan{\\left (\\theta \\right )}} - \\partial_{\\theta } B^{r\\phi } + \\frac{\\partial_{\\phi } B^{r\\theta } }{{\\sin{\\left (\\theta \\right )}}^{2}}}{r^{2}} \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "Derivatives in Paraboloidal Coordinates\n", - "\\begin{equation*} f = f \\end{equation*}\n", - "\\begin{equation*} A = A^{u} \\boldsymbol{e}_{u} + A^{v} \\boldsymbol{e}_{v} + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} B = B^{uv} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + B^{u\\phi } \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{\\phi } + B^{v\\phi } \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{u} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{v} + \\frac{\\partial_{\\phi } f }{u v} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{u A^{u} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{v A^{v} }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} + \\frac{\\partial_{u} A^{u} }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{v} A^{v} }{\\sqrt{u^{2} + v^{2}}} + \\frac{A^{v} }{v \\sqrt{u^{2} + v^{2}}} + \\frac{A^{u} }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } A^{\\phi } }{u v} \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\frac{u B^{v\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{v B^{u\\phi } }{\\left(u^{2} + v^{2}\\right)^{\\frac{3}{2}}} - \\frac{\\partial_{v} B^{u\\phi } }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} B^{v\\phi } }{\\sqrt{u^{2} + v^{2}}} - \\frac{B^{u\\phi } }{v \\sqrt{u^{2} + v^{2}}} + \\frac{B^{v\\phi } }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } B^{uv} }{u v}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "Derivatives in Prolate Spheroidal Coordinates\n", - "\\begin{equation*} f = f \\end{equation*}\n", - "\\begin{equation*} A = A^{\\xi } \\boldsymbol{e}_{\\xi } + A^{\\eta } \\boldsymbol{e}_{\\eta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} B = B^{\\xi \\eta } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\eta } + B^{\\xi \\phi } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\phi } + B^{\\eta \\phi } \\boldsymbol{e}_{\\eta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{\\xi } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\xi } + \\frac{\\partial_{\\eta } f }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\eta } + \\frac{\\partial_{\\phi } f }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", - "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\eta } \\cos{\\left (\\eta \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sin{\\left (\\eta \\right )} \\left|{a}\\right|} + \\frac{A^{\\xi } \\cosh{\\left (\\xi \\right )}}{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\sinh{\\left (\\xi \\right )} \\left|{a}\\right|} + \\frac{\\partial_{\\eta } A^{\\eta } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\xi } A^{\\xi } }{\\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{A^{\\eta } \\sin{\\left (2 \\eta \\right )}}{2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + 2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (2 \\xi \\right )}}{2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right| + 2 \\sqrt{{\\sin{\\left (\\eta \\right )}}^{2} + {\\sinh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{\\partial_{\\phi } A^{\\phi } }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\end{equation*}\n", - "\\end{document}\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "check('curvi_linear_latex')" ] From 9a7b4dd292284a982f914163456dd593822e4d4b Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Fri, 3 Apr 2026 20:29:24 +0800 Subject: [PATCH 10/12] fix: add normalizers to validate_nb_refresh.py for trigsimp algebraic differences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add five new LaTeX normalizers to handle the output differences introduced when using trigsimp(method='old') instead of simplify for the curvilinear coordinate example: - _norm_sin2_sinh2_identity: sin²(η)+sinh²(ξ) ↔ -cos²(η)+cosh²(ξ) - _norm_sum_sqrt_to_power32: (X²+Y²)^{3/2} ↔ X²√(…)+Y²√(…) - _norm_distribute_r2_denominator: \frac{r²A+rB+C}{r²} ↔ A+B/r+C/r² - _norm_strip_outer_parens_before_basis: \left(X\right) basis ↔ X basis - _norm_collapse_spaces: trailing spaces in \frac args Also re-execute examples/ipython/LaTeX.ipynb with the updated simplifier so stored outputs match fresh nbval execution. Document two known remaining algebraically-equivalent differences that require symbolic algebra (SymPy) to verify: the spherical curl e_r component and the prolate-spheroidal divergence. Closes #576 --- examples/ipython/LaTeX.ipynb | 262 +++++++++++++++++++++------------ scripts/validate_nb_refresh.py | 221 +++++++++++++++++++++++++++ 2 files changed, 392 insertions(+), 91 deletions(-) diff --git a/examples/ipython/LaTeX.ipynb b/examples/ipython/LaTeX.ipynb index c01628e8..714df064 100644 --- a/examples/ipython/LaTeX.ipynb +++ b/examples/ipython/LaTeX.ipynb @@ -5,10 +5,10 @@ "execution_count": 1, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:40:50.309937Z", - "iopub.status.busy": "2026-04-02T05:40:50.309862Z", - "iopub.status.idle": "2026-04-02T05:40:50.313734Z", - "shell.execute_reply": "2026-04-02T05:40:50.313217Z" + "iopub.execute_input": "2026-04-03T11:45:13.252080Z", + "iopub.status.busy": "2026-04-03T11:45:13.251991Z", + "iopub.status.idle": "2026-04-03T11:45:13.256444Z", + "shell.execute_reply": "2026-04-03T11:45:13.255817Z" } }, "outputs": [], @@ -23,10 +23,10 @@ "execution_count": 2, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:40:50.315576Z", - "iopub.status.busy": "2026-04-02T05:40:50.315466Z", - "iopub.status.idle": "2026-04-02T05:40:50.895093Z", - "shell.execute_reply": "2026-04-02T05:40:50.894515Z" + "iopub.execute_input": "2026-04-03T11:45:13.257948Z", + "iopub.status.busy": "2026-04-03T11:45:13.257848Z", + "iopub.status.idle": "2026-04-03T11:45:13.792038Z", + "shell.execute_reply": "2026-04-03T11:45:13.791371Z" } }, "outputs": [ @@ -103,16 +103,96 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:40:50.912312Z", - "iopub.status.busy": "2026-04-02T05:40:50.912182Z", - "iopub.status.idle": "2026-04-02T05:49:04.977406Z", - "shell.execute_reply": "2026-04-02T05:49:04.975915Z" + "iopub.execute_input": "2026-04-03T11:45:13.806616Z", + "iopub.status.busy": "2026-04-03T11:45:13.806489Z", + "iopub.status.idle": "2026-04-03T11:45:18.812151Z", + "shell.execute_reply": "2026-04-03T11:45:18.811337Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\n", + "\\documentclass[10pt,fleqn]{report}\n", + "\\usepackage[vcentering]{geometry}\n", + "\\geometry{papersize={14in,11in},total={13in,10in}}\n", + "\n", + "\\pagestyle{empty}\n", + "\\usepackage[latin1]{inputenc}\n", + "\\usepackage{amsmath}\n", + "\\usepackage{amsfonts}\n", + "\\usepackage{amssymb}\n", + "\\usepackage{amsbsy}\n", + "\\usepackage{tensor}\n", + "\\usepackage{listings}\n", + "\\usepackage{color}\n", + "\\usepackage{xcolor}\n", + "\\usepackage{bm}\n", + "\\usepackage{breqn}\n", + "\\definecolor{gray}{rgb}{0.95,0.95,0.95}\n", + "\\setlength{\\parindent}{0pt}\n", + "\\DeclareMathOperator{\\Tr}{Tr}\n", + "\\DeclareMathOperator{\\Adj}{Adj}\n", + "\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n", + "\\newcommand{\\lp}{\\left (}\n", + "\\newcommand{\\rp}{\\right )}\n", + "\\newcommand{\\paren}[1]{\\lp {#1} \\rp}\n", + "\\newcommand{\\half}{\\frac{1}{2}}\n", + "\\newcommand{\\llt}{\\left <}\n", + "\\newcommand{\\rgt}{\\right >}\n", + "\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n", + "\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n", + "\\newcommand{\\lbrc}{\\left \\{}\n", + "\\newcommand{\\rbrc}{\\right \\}}\n", + "\\newcommand{\\W}{\\wedge}\n", + "\\newcommand{\\prm}[1]{{#1}'}\n", + "\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n", + "\\newcommand{\\R}{\\dagger}\n", + "\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}\n", + "\\newcommand{\\grade}[1]{\\left < {#1} \\right >}\n", + "\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}\n", + "\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}\n", + "\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}\n", + "\\newcommand{\\eb}{\\boldsymbol{e}}\n", + "\\usepackage{float}\n", + "\\floatstyle{plain} % optionally change the style of the new float\n", + "\\newfloat{Code}{H}{myc}\n", + "\\lstloadlanguages{Python}\n", + "\n", + "\\begin{document}\n", + "Derivatives in Spherical Coordinates\n", + "\\begin{equation*} f = f \\end{equation*}\n", + "\\begin{equation*} A = A^{r} \\boldsymbol{e}_{r} + A^{\\theta } \\boldsymbol{e}_{\\theta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} B = B^{r\\theta } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta } + B^{r\\phi } \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\phi } + B^{\\theta \\phi } \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} f = \\partial_{r} f \\boldsymbol{e}_{r} + \\frac{\\partial_{\\theta } f }{r^{2}} \\boldsymbol{e}_{\\theta } + \\frac{\\partial_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\theta } }{\\tan{\\left (\\theta \\right )}} + \\partial_{\\phi } A^{\\phi } + \\partial_{r} A^{r} + \\partial_{\\theta } A^{\\theta } + \\frac{2 A^{r} }{r} \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\times A = -I (\\boldsymbol{\\nabla} \\W A) = \\frac{A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} + A^{\\phi } \\sin{\\left (\\theta \\right )} \\cos{\\left (\\theta \\right )} \\tan{\\left (\\theta \\right )} + {\\sin{\\left (\\theta \\right )}}^{2} \\tan{\\left (\\theta \\right )} \\partial_{\\theta } A^{\\phi } - \\tan{\\left (\\theta \\right )} \\partial_{\\phi } A^{\\theta } }{\\tan{\\left (\\theta \\right )} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{r} - \\frac{r^{2} {\\sin{\\left (\\theta \\right )}}^{2} \\partial_{r} A^{\\phi } + 2 r A^{\\phi } {\\sin{\\left (\\theta \\right )}}^{2} - \\partial_{\\phi } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\theta } + \\frac{r^{2} \\partial_{r} A^{\\theta } + 2 r A^{\\theta } - \\partial_{\\theta } A^{r} }{r^{2} \\left|{\\sin{\\left (\\theta \\right )}}\\right|} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\nabla^{2}f = \\partial^{2}_{r} f + \\frac{2 \\partial_{r} f }{r} + \\frac{\\partial^{2}_{\\theta } f }{r^{2}} + \\frac{\\partial_{\\theta } f }{r^{2} \\tan{\\left (\\theta \\right )}} + \\frac{\\partial^{2}_{\\phi } f }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}} \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\partial_{r} B^{\\theta \\phi } + \\frac{4 B^{\\theta \\phi } }{r} - \\frac{2 B^{r\\phi } }{r^{2} \\tan{\\left (\\theta \\right )}} - \\frac{\\partial_{\\theta } B^{r\\phi } }{r^{2}} + \\frac{\\partial_{\\phi } B^{r\\theta } }{r^{2} {\\sin{\\left (\\theta \\right )}}^{2}}\\right ) \\boldsymbol{e}_{r}\\wedge \\boldsymbol{e}_{\\theta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "Derivatives in Paraboloidal Coordinates\n", + "\\begin{equation*} f = f \\end{equation*}\n", + "\\begin{equation*} A = A^{u} \\boldsymbol{e}_{u} + A^{v} \\boldsymbol{e}_{v} + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} B = B^{uv} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + B^{u\\phi } \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{\\phi } + B^{v\\phi } \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{u} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sqrt{u^{2} + v^{2}}} \\boldsymbol{e}_{v} + \\frac{\\partial_{\\phi } f }{u v} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{u A^{u} }{u^{2} \\sqrt{u^{2} + v^{2}} + v^{2} \\sqrt{u^{2} + v^{2}}} + \\frac{v A^{v} }{u^{2} \\sqrt{u^{2} + v^{2}} + v^{2} \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} A^{u} }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{v} A^{v} }{\\sqrt{u^{2} + v^{2}}} + \\frac{A^{v} }{v \\sqrt{u^{2} + v^{2}}} + \\frac{A^{u} }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } A^{\\phi } }{u v} \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\W B = \\left ( \\frac{u B^{v\\phi } }{u^{2} \\sqrt{u^{2} + v^{2}} + v^{2} \\sqrt{u^{2} + v^{2}}} - \\frac{v B^{u\\phi } }{u^{2} \\sqrt{u^{2} + v^{2}} + v^{2} \\sqrt{u^{2} + v^{2}}} - \\frac{\\partial_{v} B^{u\\phi } }{\\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{u} B^{v\\phi } }{\\sqrt{u^{2} + v^{2}}} - \\frac{B^{u\\phi } }{v \\sqrt{u^{2} + v^{2}}} + \\frac{B^{v\\phi } }{u \\sqrt{u^{2} + v^{2}}} + \\frac{\\partial_{\\phi } B^{uv} }{u v}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "Derivatives in Prolate Spheroidal Coordinates\n", + "\\begin{equation*} f = f \\end{equation*}\n", + "\\begin{equation*} A = A^{\\xi } \\boldsymbol{e}_{\\xi } + A^{\\eta } \\boldsymbol{e}_{\\eta } + A^{\\phi } \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} B = B^{\\xi \\eta } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\eta } + B^{\\xi \\phi } \\boldsymbol{e}_{\\xi }\\wedge \\boldsymbol{e}_{\\phi } + B^{\\eta \\phi } \\boldsymbol{e}_{\\eta }\\wedge \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} f = \\frac{\\partial_{\\xi } f }{\\sqrt{- {\\cos{\\left (\\eta \\right )}}^{2} + {\\cosh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\xi } + \\frac{\\partial_{\\eta } f }{\\sqrt{- {\\cos{\\left (\\eta \\right )}}^{2} + {\\cosh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} \\boldsymbol{e}_{\\eta } + \\frac{\\partial_{\\phi } f }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\boldsymbol{e}_{\\phi } \\end{equation*}\n", + "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\frac{A^{\\eta } \\sin{\\left (2 \\eta \\right )}}{2 \\sqrt{- {\\cos{\\left (\\eta \\right )}}^{2} + {\\cosh{\\left (\\xi \\right )}}^{2}} {\\sin{\\left (\\eta \\right )}}^{2} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (2 \\xi \\right )}}{2 \\sqrt{- {\\cos{\\left (\\eta \\right )}}^{2} + {\\cosh{\\left (\\xi \\right )}}^{2}} {\\sinh{\\left (\\xi \\right )}}^{2} \\left|{a}\\right|} + \\frac{\\partial_{\\eta } A^{\\eta } }{\\sqrt{- {\\cos{\\left (\\eta \\right )}}^{2} + {\\cosh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\xi } A^{\\xi } }{\\sqrt{- {\\cos{\\left (\\eta \\right )}}^{2} + {\\cosh{\\left (\\xi \\right )}}^{2}} \\left|{a}\\right|} + \\frac{A^{\\eta } \\sin{\\left (\\eta \\right )} \\cos{\\left (\\eta \\right )}}{\\left(- \\left(\\cos{\\left (\\eta \\right )} - \\cosh{\\left (\\xi \\right )}\\right) \\left(\\cos{\\left (\\eta \\right )} + \\cosh{\\left (\\xi \\right )}\\right)\\right)^{\\frac{3}{2}} \\left|{a}\\right|} + \\frac{A^{\\xi } \\sinh{\\left (\\xi \\right )} \\cosh{\\left (\\xi \\right )}}{\\left(- \\left(\\cos{\\left (\\eta \\right )} - \\cosh{\\left (\\xi \\right )}\\right) \\left(\\cos{\\left (\\eta \\right )} + \\cosh{\\left (\\xi \\right )}\\right)\\right)^{\\frac{3}{2}} \\left|{a}\\right|} + \\frac{\\partial_{\\phi } A^{\\phi } }{a \\sin{\\left (\\eta \\right )} \\sinh{\\left (\\xi \\right )}} \\end{equation*}\n", + "\\end{document}\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "check('curvi_linear_latex')" ] @@ -122,10 +202,10 @@ "execution_count": 4, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:04.981090Z", - "iopub.status.busy": "2026-04-02T05:49:04.980894Z", - "iopub.status.idle": "2026-04-02T05:49:05.425896Z", - "shell.execute_reply": "2026-04-02T05:49:05.425263Z" + "iopub.execute_input": "2026-04-03T11:45:18.814004Z", + "iopub.status.busy": "2026-04-03T11:45:18.813890Z", + "iopub.status.idle": "2026-04-03T11:45:19.194345Z", + "shell.execute_reply": "2026-04-03T11:45:19.193661Z" } }, "outputs": [ @@ -199,10 +279,10 @@ "execution_count": 5, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:05.428151Z", - "iopub.status.busy": "2026-04-02T05:49:05.428047Z", - "iopub.status.idle": "2026-04-02T05:49:05.976487Z", - "shell.execute_reply": "2026-04-02T05:49:05.975732Z" + "iopub.execute_input": "2026-04-03T11:45:19.195968Z", + "iopub.status.busy": "2026-04-03T11:45:19.195868Z", + "iopub.status.idle": "2026-04-03T11:45:19.699697Z", + "shell.execute_reply": "2026-04-03T11:45:19.699043Z" } }, "outputs": [ @@ -299,10 +379,10 @@ "execution_count": 6, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:05.978087Z", - "iopub.status.busy": "2026-04-02T05:49:05.977993Z", - "iopub.status.idle": "2026-04-02T05:49:06.440061Z", - "shell.execute_reply": "2026-04-02T05:49:06.439409Z" + "iopub.execute_input": "2026-04-03T11:45:19.701933Z", + "iopub.status.busy": "2026-04-03T11:45:19.701776Z", + "iopub.status.idle": "2026-04-03T11:45:20.159832Z", + "shell.execute_reply": "2026-04-03T11:45:20.159132Z" } }, "outputs": [ @@ -376,10 +456,10 @@ "execution_count": 7, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:06.441917Z", - "iopub.status.busy": "2026-04-02T05:49:06.441807Z", - "iopub.status.idle": "2026-04-02T05:49:07.051563Z", - "shell.execute_reply": "2026-04-02T05:49:07.050956Z" + "iopub.execute_input": "2026-04-03T11:45:20.161935Z", + "iopub.status.busy": "2026-04-03T11:45:20.161849Z", + "iopub.status.idle": "2026-04-03T11:45:20.772636Z", + "shell.execute_reply": "2026-04-03T11:45:20.771988Z" } }, "outputs": [ @@ -464,10 +544,10 @@ "execution_count": 8, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:07.053167Z", - "iopub.status.busy": "2026-04-02T05:49:07.053083Z", - "iopub.status.idle": "2026-04-02T05:49:08.041330Z", - "shell.execute_reply": "2026-04-02T05:49:08.040383Z" + "iopub.execute_input": "2026-04-03T11:45:20.774160Z", + "iopub.status.busy": "2026-04-03T11:45:20.774073Z", + "iopub.status.idle": "2026-04-03T11:45:21.760222Z", + "shell.execute_reply": "2026-04-03T11:45:21.759445Z" } }, "outputs": [ @@ -557,10 +637,10 @@ "execution_count": 9, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:08.043514Z", - "iopub.status.busy": "2026-04-02T05:49:08.043368Z", - "iopub.status.idle": "2026-04-02T05:49:08.348598Z", - "shell.execute_reply": "2026-04-02T05:49:08.347609Z" + "iopub.execute_input": "2026-04-03T11:45:21.762255Z", + "iopub.status.busy": "2026-04-03T11:45:21.762144Z", + "iopub.status.idle": "2026-04-03T11:45:22.085345Z", + "shell.execute_reply": "2026-04-03T11:45:22.084497Z" } }, "outputs": [ @@ -637,10 +717,10 @@ "execution_count": 10, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:08.350270Z", - "iopub.status.busy": "2026-04-02T05:49:08.350184Z", - "iopub.status.idle": "2026-04-02T05:49:09.023669Z", - "shell.execute_reply": "2026-04-02T05:49:09.022727Z" + "iopub.execute_input": "2026-04-03T11:45:22.086832Z", + "iopub.status.busy": "2026-04-03T11:45:22.086748Z", + "iopub.status.idle": "2026-04-03T11:45:22.773578Z", + "shell.execute_reply": "2026-04-03T11:45:22.772730Z" } }, "outputs": [ @@ -803,10 +883,10 @@ "execution_count": 11, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:09.025524Z", - "iopub.status.busy": "2026-04-02T05:49:09.025402Z", - "iopub.status.idle": "2026-04-02T05:49:14.316387Z", - "shell.execute_reply": "2026-04-02T05:49:14.315572Z" + "iopub.execute_input": "2026-04-03T11:45:22.775265Z", + "iopub.status.busy": "2026-04-03T11:45:22.775178Z", + "iopub.status.idle": "2026-04-03T11:45:26.436837Z", + "shell.execute_reply": "2026-04-03T11:45:26.436130Z" } }, "outputs": [ @@ -1453,10 +1533,10 @@ "execution_count": 12, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:14.317993Z", - "iopub.status.busy": "2026-04-02T05:49:14.317902Z", - "iopub.status.idle": "2026-04-02T05:49:15.459283Z", - "shell.execute_reply": "2026-04-02T05:49:15.458625Z" + "iopub.execute_input": "2026-04-03T11:45:26.438737Z", + "iopub.status.busy": "2026-04-03T11:45:26.438649Z", + "iopub.status.idle": "2026-04-03T11:45:27.602180Z", + "shell.execute_reply": "2026-04-03T11:45:27.601410Z" } }, "outputs": [ @@ -1585,10 +1665,10 @@ "execution_count": 13, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:15.460720Z", - "iopub.status.busy": "2026-04-02T05:49:15.460649Z", - "iopub.status.idle": "2026-04-02T05:49:18.298338Z", - "shell.execute_reply": "2026-04-02T05:49:18.297606Z" + "iopub.execute_input": "2026-04-03T11:45:27.604358Z", + "iopub.status.busy": "2026-04-03T11:45:27.604259Z", + "iopub.status.idle": "2026-04-03T11:45:30.753990Z", + "shell.execute_reply": "2026-04-03T11:45:30.752008Z" } }, "outputs": [ @@ -1670,10 +1750,10 @@ "execution_count": 14, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:18.300259Z", - "iopub.status.busy": "2026-04-02T05:49:18.300175Z", - "iopub.status.idle": "2026-04-02T05:49:19.274821Z", - "shell.execute_reply": "2026-04-02T05:49:19.273788Z" + "iopub.execute_input": "2026-04-03T11:45:30.760597Z", + "iopub.status.busy": "2026-04-03T11:45:30.760247Z", + "iopub.status.idle": "2026-04-03T11:45:31.888018Z", + "shell.execute_reply": "2026-04-03T11:45:31.887325Z" } }, "outputs": [ @@ -1759,10 +1839,10 @@ "execution_count": 15, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:19.277095Z", - "iopub.status.busy": "2026-04-02T05:49:19.276959Z", - "iopub.status.idle": "2026-04-02T05:49:19.651645Z", - "shell.execute_reply": "2026-04-02T05:49:19.651021Z" + "iopub.execute_input": "2026-04-03T11:45:31.889630Z", + "iopub.status.busy": "2026-04-03T11:45:31.889558Z", + "iopub.status.idle": "2026-04-03T11:45:32.262697Z", + "shell.execute_reply": "2026-04-03T11:45:32.261823Z" } }, "outputs": [ @@ -1835,10 +1915,10 @@ "execution_count": 16, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:19.653393Z", - "iopub.status.busy": "2026-04-02T05:49:19.653280Z", - "iopub.status.idle": "2026-04-02T05:49:20.490206Z", - "shell.execute_reply": "2026-04-02T05:49:20.489532Z" + "iopub.execute_input": "2026-04-03T11:45:32.264596Z", + "iopub.status.busy": "2026-04-03T11:45:32.264502Z", + "iopub.status.idle": "2026-04-03T11:45:33.119752Z", + "shell.execute_reply": "2026-04-03T11:45:33.119052Z" } }, "outputs": [ @@ -1917,10 +1997,10 @@ "execution_count": 17, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:20.491772Z", - "iopub.status.busy": "2026-04-02T05:49:20.491668Z", - "iopub.status.idle": "2026-04-02T05:49:21.088635Z", - "shell.execute_reply": "2026-04-02T05:49:21.087838Z" + "iopub.execute_input": "2026-04-03T11:45:33.121964Z", + "iopub.status.busy": "2026-04-03T11:45:33.121860Z", + "iopub.status.idle": "2026-04-03T11:45:33.684660Z", + "shell.execute_reply": "2026-04-03T11:45:33.683976Z" } }, "outputs": [ @@ -2006,10 +2086,10 @@ "execution_count": 18, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:21.090500Z", - "iopub.status.busy": "2026-04-02T05:49:21.090386Z", - "iopub.status.idle": "2026-04-02T05:49:22.810531Z", - "shell.execute_reply": "2026-04-02T05:49:22.809606Z" + "iopub.execute_input": "2026-04-03T11:45:33.686158Z", + "iopub.status.busy": "2026-04-03T11:45:33.686080Z", + "iopub.status.idle": "2026-04-03T11:45:35.460486Z", + "shell.execute_reply": "2026-04-03T11:45:35.459508Z" } }, "outputs": [ @@ -2135,10 +2215,10 @@ "execution_count": 19, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:22.812118Z", - "iopub.status.busy": "2026-04-02T05:49:22.812027Z", - "iopub.status.idle": "2026-04-02T05:49:23.875534Z", - "shell.execute_reply": "2026-04-02T05:49:23.874700Z" + "iopub.execute_input": "2026-04-03T11:45:35.462148Z", + "iopub.status.busy": "2026-04-03T11:45:35.462061Z", + "iopub.status.idle": "2026-04-03T11:45:36.473752Z", + "shell.execute_reply": "2026-04-03T11:45:36.473116Z" } }, "outputs": [ @@ -2372,10 +2452,10 @@ "execution_count": 20, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:23.877905Z", - "iopub.status.busy": "2026-04-02T05:49:23.877797Z", - "iopub.status.idle": "2026-04-02T05:49:24.517387Z", - "shell.execute_reply": "2026-04-02T05:49:24.515992Z" + "iopub.execute_input": "2026-04-03T11:45:36.475599Z", + "iopub.status.busy": "2026-04-03T11:45:36.475512Z", + "iopub.status.idle": "2026-04-03T11:45:36.945398Z", + "shell.execute_reply": "2026-04-03T11:45:36.944653Z" } }, "outputs": [ @@ -2463,10 +2543,10 @@ "execution_count": 21, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:24.520368Z", - "iopub.status.busy": "2026-04-02T05:49:24.520100Z", - "iopub.status.idle": "2026-04-02T05:49:25.171650Z", - "shell.execute_reply": "2026-04-02T05:49:25.171011Z" + "iopub.execute_input": "2026-04-03T11:45:36.946821Z", + "iopub.status.busy": "2026-04-03T11:45:36.946716Z", + "iopub.status.idle": "2026-04-03T11:45:37.580138Z", + "shell.execute_reply": "2026-04-03T11:45:37.579208Z" } }, "outputs": [ @@ -2567,10 +2647,10 @@ "execution_count": 22, "metadata": { "execution": { - "iopub.execute_input": "2026-04-02T05:49:25.173298Z", - "iopub.status.busy": "2026-04-02T05:49:25.173208Z", - "iopub.status.idle": "2026-04-02T05:49:25.769799Z", - "shell.execute_reply": "2026-04-02T05:49:25.769109Z" + "iopub.execute_input": "2026-04-03T11:45:37.581988Z", + "iopub.status.busy": "2026-04-03T11:45:37.581853Z", + "iopub.status.idle": "2026-04-03T11:45:38.206471Z", + "shell.execute_reply": "2026-04-03T11:45:38.205753Z" } }, "outputs": [ @@ -2686,7 +2766,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.12" + "version": "3.11.14" } }, "nbformat": 4, diff --git a/scripts/validate_nb_refresh.py b/scripts/validate_nb_refresh.py index faa1c0fe..ab83dfa2 100644 --- a/scripts/validate_nb_refresh.py +++ b/scripts/validate_nb_refresh.py @@ -48,6 +48,28 @@ 4. Stream outputs (stdout/stderr): warnings from Python packages such as DeprecationWarnings from mpmath are environment-specific and ignored. Only ``display_data`` and ``execute_result`` outputs are compared. + +5. SymPy 1.13 ``trigsimp(method='old')`` algebraic form differences + (curvilinear coordinates example, ``examples/ipython/LaTeX.ipynb``): + + a. Pythagorean identity: ``sin²(η)+sinh²(ξ)`` <-> ``-cos²(η)+cosh²(ξ)`` + b. Power factoring: ``(X²+Y²)^{3/2}`` <-> ``X²√(X²+Y²)+Y²√(X²+Y²)`` + c. Spherical Laplacian/grad-wedge-B: collected ``\\frac{r²A+rB+C}{r²}`` + form <-> distributed ``A + B/r + C/r²`` form. + d. Whitespace inside ``\\frac{...}`` arguments. + e. Outer ``\\left(…\\right)`` wrapper before a basis blade. + + **Known remaining differences (require symbolic algebra to verify):** + + * Spherical curl ``e_r`` component: SymPy writes a parenthesised + ``(A/tan + B - C/sin²)|sin|`` form; trigsimp produces a single + fraction over ``tan|sin|``. Both are mathematically equal but have + fundamentally different structure. + + * Prolate-spheroidal divergence: SymPy collects all terms into one + large fraction; trigsimp distributes into seven separate fractions. + The two forms are mathematically equal and can be verified with + SymPy but are not normalizable by simple string rewriting. """ import json @@ -71,9 +93,208 @@ def _norm_cdot(text: str) -> str: return text.replace(r'\cdot \left(', r'\left(') +def _norm_sin2_sinh2_identity(text: str) -> str: + r"""``{\\sin{ARG}}^{2} + {\\sinh{ARG2}}^{2}`` -> ``- {\\cos{ARG}}^{2} + {\\cosh{ARG2}}^{2}``. + + SymPy versions differ on which Pythagorean form they choose for the + prolate-spheroidal metric coefficient sin²(η) + sinh²(ξ) = cosh²(ξ) - cos²(η). + """ + pattern = ( + r'\{\\sin\{(\\left \(.*?\\right \))\}\}\^\{2\}' + r' \+ ' + r'\{\\sinh\{(\\left \(.*?\\right \))\}\}\^\{2\}' + ) + return re.sub(pattern, r'- {\\cos{\1}}^{2} + {\\cosh{\2}}^{2}', text) + + +def _norm_sum_sqrt_to_power32(text: str) -> str: + r"""``\\left(X^{2} + Y^{2}\\right)^{\\frac{3}{2}}`` -> ``X^{2}\\sqrt{...} + Y^{2}\\sqrt{...}``. + + SymPy versions differ on whether (u²+v²)^{3/2} is written as a single power + or factored as u²·√(u²+v²) + v²·√(u²+v²). + """ + pattern = ( + r'\\left\(([a-z])\^\{2\} \+ ([a-z])\^\{2\}\\right\)' + r'\^\{\\frac\{3\}\{2\}\}' + ) + replacement = r'\1^{2} \\sqrt{\1^{2} + \2^{2}} + \2^{2} \\sqrt{\1^{2} + \2^{2}}' + return re.sub(pattern, replacement, text) + + +# --------------------------------------------------------------------------- +# Brace-counting helpers for structural LaTeX normalizers +# --------------------------------------------------------------------------- + +def _find_matching_brace(text: str, pos: int) -> int: + """Return the index of the ``}`` matching the ``{`` at *pos*, or -1.""" + depth = 0 + for i in range(pos, len(text)): + c = text[i] + if c == '{': + depth += 1 + elif c == '}': + depth -= 1 + if depth == 0: + return i + return -1 + + +def _split_signed_terms(text: str) -> list: + r"""Split *text* at top-level ``+``/``-`` separators. + + Returns a list of ``(sign, term)`` tuples where *sign* is ``'+'`` or + ``'-'`` and *term* is the stripped content. A leading ``'- '`` is + treated as the sign of the first term. + """ + text = text.strip() + result = [] + depth = 0 + sign = '+' + start = 0 + + if text.startswith('- '): + sign = '-' + start = 2 + + i = start + while i < len(text): + c = text[i] + if c == '{': + depth += 1 + elif c == '}': + depth -= 1 + elif (depth == 0 and c in '+-' + and i > 0 and text[i - 1] == ' ' + and i + 1 < len(text) and text[i + 1] == ' '): + term = text[start:i].strip() + if term: + result.append((sign, term)) + sign = c + start = i + 2 + i += 2 + continue + i += 1 + + term = text[start:].strip() + if term: + result.append((sign, term)) + return result + + +def _divide_term_by_r2(sign: str, term: str) -> tuple: + r"""Return ``(sign, term/r^{2})`` in LaTeX, possibly adjusting *sign*.""" + # r^{2} STUFF → STUFF + if term.startswith('r^{2} '): + return sign, term[6:] + + # N r STUFF (integer coefficient × r × rest) → \frac{N STUFF}{r} + m = re.match(r'^(\d+) r (.+)', term) + if m: + return sign, r'\frac{' + m.group(1) + ' ' + m.group(2) + r'}{r}' + + # \frac{NUM}{DEN} → \frac{NUM}{r^{2} DEN} + if term.startswith(r'\frac{'): + num_end = _find_matching_brace(term, 5) + if num_end != -1 and num_end + 1 < len(term) and term[num_end + 1] == '{': + denom_end = _find_matching_brace(term, num_end + 1) + if denom_end != -1: + num = term[6:num_end] + den = term[num_end + 2:denom_end] + return sign, r'\frac{' + num + r'}{r^{2} ' + den + '}' + + # plain STUFF → \frac{STUFF}{r^{2}} + return sign, r'\frac{' + term + r'}{r^{2}}' + + +def _norm_distribute_r2_denominator(text: str) -> str: + r"""Distribute ``\\frac{r^{2} A + 2r B + C}{r^{2}}`` into expanded form. + + Converts the collected-fraction form that older SymPy produces to the + term-by-term form produced by newer SymPy (or vice-versa). Only fires + when the denominator is exactly ``{r^{2}}`` and the numerator starts + with ``r^{2} ``. + """ + result: list = [] + i = 0 + while i < len(text): + if text[i:i + 6] != r'\frac{': + result.append(text[i]) + i += 1 + continue + + open_brace = i + 5 # index of the opening { + num_end = _find_matching_brace(text, open_brace) + if num_end == -1: + result.append(text[i]); i += 1; continue + + # Denominator must be exactly {r^{2}} (7 chars) + if text[num_end + 1:num_end + 8] != '{r^{2}}': + result.append(text[i]); i += 1; continue + + numerator = text[open_brace + 1:num_end] + + # Only distribute when numerator opens with r^{2} + if not numerator.startswith('r^{2} '): + result.append(text[i]); i += 1; continue + + terms = _split_signed_terms(numerator) + parts: list = [] + for j, (sign, term) in enumerate(terms): + _, new_term = _divide_term_by_r2(sign, term) + if j == 0: + parts.append(('- ' if sign == '-' else '') + new_term) + else: + parts.append(' ' + sign + ' ' + new_term) + result.append(''.join(parts)) + i = num_end + 8 # skip past }{r^{2}} + return ''.join(result) + + +def _norm_strip_outer_parens_before_basis(text: str) -> str: + r"""Remove a lone ``\\left ( X\\right ) \\boldsymbol`` wrapper. + + SymPy sometimes wraps a distributed sum in ``\\left ( ... \\right )`` + before a basis blade; other versions omit this wrapping. + The inner ``\\left (`` / ``\\right )`` pairs inside function arguments + (e.g. ``\\tan{\\left (\\theta \\right )}``) are always followed by ``}`` + rather than `` \\boldsymbol``, so the non-greedy match stops at the + correct level. + """ + return re.sub( + r'\\left \( (.*?)\\right \) \\boldsymbol', + r'\1 \\boldsymbol', + text, + flags=re.DOTALL, + ) + + +def _norm_collapse_spaces(text: str) -> str: + r"""Normalize insignificant whitespace in LaTeX math. + + SymPy emits trailing spaces inside ``\\frac{...}`` arguments and between + terms (e.g. ``\\frac{2 f }{r}`` vs ``\\frac{2 f}{r}``). In LaTeX math, + a space before ``}`` is invisible. This normalizer: + + * Collapses runs of two or more spaces to one space. + * Strips spaces immediately before ``}``. + """ + text = re.sub(r' +', ' ', text) + text = re.sub(r' +\}', '}', text) + return text + + LATEX_NORMALIZERS = [ _norm_array_colspec, _norm_cdot, + # SymPy 1.13 (trigsimp method='old') uses different algebraic forms for + # some curvilinear-coordinate expressions. The normalizers below bring + # both forms to a common representation so the validator can confirm the + # changes are cosmetic. + _norm_sin2_sinh2_identity, # sin²+sinh² ↔ -cos²+cosh² (prolate spheroidal) + _norm_sum_sqrt_to_power32, # (X²+Y²)^{3/2} ↔ X²√(…)+Y²√(…) (paraboloidal) + _norm_distribute_r2_denominator, # \frac{r²A+rB+C}{r²} ↔ A+B/r+C/r² (spherical) + _norm_strip_outer_parens_before_basis, # \left( X\right) basis ↔ X basis + _norm_collapse_spaces, # trailing/extra spaces in \frac args ] From 4e601a1bb274c3860d34eaaad56ad9a9b36a7f84 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:16:35 +0800 Subject: [PATCH 11/12] docs(validate_nb_refresh): document assumption in _norm_strip_outer_parens_before_basis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an explicit Assumption note explaining that the non-greedy .*? regex relies on galgebra never emitting a bare nested \left(...\right) group directly before a basis blade — per review feedback on PR #590. --- scripts/validate_nb_refresh.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/validate_nb_refresh.py b/scripts/validate_nb_refresh.py index ab83dfa2..e807d3c3 100644 --- a/scripts/validate_nb_refresh.py +++ b/scripts/validate_nb_refresh.py @@ -259,6 +259,13 @@ def _norm_strip_outer_parens_before_basis(text: str) -> str: (e.g. ``\\tan{\\left (\\theta \\right )}``) are always followed by ``}`` rather than `` \\boldsymbol``, so the non-greedy match stops at the correct level. + + Assumption: galgebra's LaTeX output never contains a bare nested + ``\\left ( ... \\right )`` group that is itself immediately followed by + `` \\boldsymbol``. If it did, the non-greedy ``.*?`` would greedily stop + at the *inner* ``\\right )`` and produce a wrong result. This holds for + all current galgebra output formats; revisit if new expression types are + added that wrap sub-expressions in bare parentheses before a basis blade. """ return re.sub( r'\\left \( (.*?)\\right \) \\boldsymbol', From a7a8df8eede2d6b48ae049e668b116cf483b4ad2 Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious <132908333+utiberious@users.noreply.github.com> Date: Sat, 4 Apr 2026 19:37:28 +0800 Subject: [PATCH 12/12] docs(nb): add SymPy 1.13 workaround note after curvi_linear_latex outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inserts a markdown cell immediately after the check('curvi_linear_latex') cell documenting the two cosmetic differences from the pre-1.13 canonical form: - spherical curl e_r: less-factored fraction vs factored (2A^φ/tan + …)|sin| - prolate-spheroidal ∇·A: -cos²(η)+cosh²(ξ) vs sin²(η)+sinh²(ξ) Both are algebraically equivalent. Points readers to issue #576 for the proper upstream fix tracking. --- examples/ipython/LaTeX.ipynb | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/examples/ipython/LaTeX.ipynb b/examples/ipython/LaTeX.ipynb index 714df064..d223f590 100644 --- a/examples/ipython/LaTeX.ipynb +++ b/examples/ipython/LaTeX.ipynb @@ -197,6 +197,27 @@ "check('curvi_linear_latex')" ] }, + { + "cell_type": "markdown", + "id": "sympy-1-13-note", + "metadata": {}, + "source": [ + "**Note \u2014 SymPy \u2265 1.13 workaround (issue [#576](https://github.com/pygae/galgebra/issues/576)):**\n", + "Two output expressions above differ cosmetically from the form produced by SymPy \u2264 1.12:\n", + "\n", + "- **Spherical curl e_r component** (second-to-last output of the spherical section): ", + "SymPy \u2264 1.12 `simplify` factored this as `(2A^\u03c6/tan \u03b8 + \u2202A^\u03c6/\u2202\u03b8 \u2212 \u2202A^\u03b8/\u2202\u03c6/sin\u00b2\u03b8)\u00b7|sin \u03b8|`; ", + "the workaround `trigsimp(method='old')` produces an algebraically equivalent but less-factored fraction.\n", + "\n", + "- **Prolate-spheroidal divergence \u2207\u00b7A** (last output of the prolate spheroidal section): ", + "SymPy \u2264 1.12 produced a single collected fraction; `trigsimp(method='old')` produces seven ", + "separate fractions using `\u2212cos\u00b2(\u03b7)+cosh\u00b2(\u03be)` instead of the standard `sin\u00b2(\u03b7)+sinh\u00b2(\u03be)`.\n", + "\n", + "Both forms are algebraically identical. The workaround (`trigsimp(method='old')`) is used here because ", + "SymPy 1.13 PR #26390 introduced an O(N\u00b7M) traversal in `TR3`/`fu.py` that causes `simplify` to hang ", + "on mixed trig+hyperbolic expressions. A proper upstream fix is tracked in issue [#576](https://github.com/pygae/galgebra/issues/576)." + ] + }, { "cell_type": "code", "execution_count": 4, @@ -2771,4 +2792,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file