Repository: sympy/sympy
Pull request: Patched the latex printing error in _print_SingularityFunction()
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->
Fixes #21127 

#### Brief description of what is fixed or changed
Latex printer for singularity function threw TypeError because of an additional functional parameter for exponent (exp) which was lacking in the function signature. Moreover, there was no provision for printing angle brackets with an exponent which was also fixed.

#### Other comments


#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
* printing
  * Fixed a bug which led to latex printing error in singularity function expressions.
<!-- END RELEASE NOTES -->


Problem statement:
_print_SingularityFunction() got an unexpected keyword argument 'exp'
On a Jupyter Notebook cell, type the following:

```python
from sympy import *
from sympy.physics.continuum_mechanics import Beam
# Young's modulus
E = symbols("E")
# length of the beam
L = symbols("L")
# concentrated load at the end tip of the beam
F = symbols("F")
# square cross section
B, H = symbols("B, H")
I = B * H**3 / 12
# numerical values (material: steel)
d = {B: 1e-02, H: 1e-02, E: 210e09, L: 0.2, F: 100}

b2 = Beam(L, E, I)
b2.apply_load(-F, L / 2, -1)
b2.apply_support(0, "fixed")
R0, M0 = symbols("R_0, M_0")
b2.solve_for_reaction_loads(R0, M0)
```

Then:

```
b2.shear_force()
```

The following error appears:
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/lib/python3.8/dist-packages/IPython/core/formatters.py in __call__(self, obj)
    343             method = get_real_method(obj, self.print_method)
    344             if method is not None:
--> 345                 return method()
    346             return None
    347         else:

/usr/local/lib/python3.8/dist-packages/sympy/interactive/printing.py in _print_latex_png(o)
    184         """
    185         if _can_print(o):
--> 186             s = latex(o, mode=latex_mode, **settings)
    187             if latex_mode == 'plain':
    188                 s = '$\\displaystyle %s$' % s

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in __call__(self, *args, **kwargs)
    371 
    372     def __call__(self, *args, **kwargs):
--> 373         return self.__wrapped__(*args, **kwargs)
    374 
    375     @property

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in latex(expr, **settings)
   2913 
   2914     """
-> 2915     return LatexPrinter(settings).doprint(expr)
   2916 
   2917 

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in doprint(self, expr)
    252 
    253     def doprint(self, expr):
--> 254         tex = Printer.doprint(self, expr)
    255 
    256         if self._settings['mode'] == 'plain':

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in doprint(self, expr)
    289     def doprint(self, expr):
    290         """Returns printer's representation for expr (as a string)"""
--> 291         return self._str(self._print(expr))
    292 
    293     def _print(self, expr, **kwargs):

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in _print_Add(self, expr, order)
    381             else:
    382                 tex += " + "
--> 383             term_tex = self._print(term)
    384             if self._needs_add_brackets(term):
    385                 term_tex = r"\left(%s\right)" % term_tex

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in _print_Mul(self, expr)
    565             # use the original expression here, since fraction() may have
    566             # altered it when producing numer and denom
--> 567             tex += convert(expr)
    568 
    569         else:

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in convert(expr)
    517                                isinstance(x.base, Quantity)))
    518 
--> 519                 return convert_args(args)
    520 
    521         def convert_args(args):

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in convert_args(args)
    523 
    524                 for i, term in enumerate(args):
--> 525                     term_tex = self._print(term)
    526 
    527                     if self._needs_mul_brackets(term, first=(i == 0),

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in _print_Add(self, expr, order)
    381             else:
    382                 tex += " + "
--> 383             term_tex = self._print(term)
    384             if self._needs_add_brackets(term):
    385                 term_tex = r"\left(%s\right)" % term_tex

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in _print_Mul(self, expr)
    569         else:
    570             snumer = convert(numer)
--> 571             sdenom = convert(denom)
    572             ldenom = len(sdenom.split())
    573             ratio = self._settings['long_frac_ratio']

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in convert(expr)
    505         def convert(expr):
    506             if not expr.is_Mul:
--> 507                 return str(self._print(expr))
    508             else:
    509                 if self.order not in ('old', 'none'):

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in _print_Add(self, expr, order)
    381             else:
    382                 tex += " + "
--> 383             term_tex = self._print(term)
    384             if self._needs_add_brackets(term):
    385                 term_tex = r"\left(%s\right)" % term_tex

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

/usr/local/lib/python3.8/dist-packages/sympy/printing/latex.py in _print_Pow(self, expr)
    649         else:
    650             if expr.base.is_Function:
--> 651                 return self._print(expr.base, exp=self._print(expr.exp))
    652             else:
    653                 tex = r"%s^{%s}"

/usr/local/lib/python3.8/dist-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    327                 printmethod = '_print_' + cls.__name__
    328                 if hasattr(self, printmethod):
--> 329                     return getattr(self, printmethod)(expr, **kwargs)
    330             # Unknown object, fall back to the emptyPrinter.
    331             return self.emptyPrinter(expr)

TypeError: _print_SingularityFunction() got an unexpected keyword argument 'exp'
```

Hints from issue discussion:
Could you provide a fully working example? Copying and pasting your code leaves a number of non-defined variables. Thanks for the report.
@moorepants Sorry for that, I've just updated the code in the original post.
This is the string printed version from `b2..shear_force()`:

```
Out[5]: -F*SingularityFunction(x, L/2, 0) + (F*SingularityFunction(L, 0, -1)*SingularityFunction(L, L/2, 1)/(SingularityFunction(L, 0, -1)*SingularityFunction(L, 0, 1) - SingularityFunction(L, 0, 0)**2) - F*SingularityFunction(L, 0, 0)*SingularityFunction(L, L/2, 0)/(SingularityFunction(L, 0, -1)*SingularityFunction(L, 0, 1) - SingularityFunction(L, 0, 0)**2))*SingularityFunction(x, 0, 0) + (-F*SingularityFunction(L, 0, 0)*SingularityFunction(L, L/2, 1)/(SingularityFunction(L, 0, -1)*SingularityFunction(L, 0, 1) - SingularityFunction(L, 0, 0)**2) + F*SingularityFunction(L, 0, 1)*SingularityFunction(L, L/2, 0)/(SingularityFunction(L, 0, -1)*SingularityFunction(L, 0, 1) - SingularityFunction(L, 0, 0)**2))*SingularityFunction(x, 0, -1)
```
Yes works correctly if you print the string. It throws the error when you display the expression on a jupyter notebook with latex
It errors on this term: `SingularityFunction(L, 0, 0)**2`. For some reasons the latex printer fails on printing a singularity function raised to a power.

Commit message: remove trailing whitespace

Patch to review:
diff --git a/sympy/printing/latex.py b/sympy/printing/latex.py
index 43f9e6ad8d93..6a713e8098ec 100644
--- a/sympy/printing/latex.py
+++ b/sympy/printing/latex.py
@@ -1968,10 +1968,12 @@ def _print_DiracDelta(self, expr, exp=None):
             tex = r"\left(%s\right)^{%s}" % (tex, exp)
         return tex
 
-    def _print_SingularityFunction(self, expr):
+    def _print_SingularityFunction(self, expr, exp=None):
         shift = self._print(expr.args[0] - expr.args[1])
         power = self._print(expr.args[2])
         tex = r"{\left\langle %s \right\rangle}^{%s}" % (shift, power)
+        if exp:
+            tex = r"{\left({\langle %s \rangle}^{%s}\right)}^{%s}" % (shift, power, exp)
         return tex
 
     def _print_Heaviside(self, expr, exp=None):
diff --git a/sympy/printing/tests/test_latex.py b/sympy/printing/tests/test_latex.py
index 84227a1785f6..f04d04c404a9 100644
--- a/sympy/printing/tests/test_latex.py
+++ b/sympy/printing/tests/test_latex.py
@@ -214,6 +214,19 @@ def test_latex_SingularityFunction():
     assert latex(SingularityFunction(x, 4, -1)) == \
         r"{\left\langle x - 4 \right\rangle}^{-1}"
 
+    assert latex(SingularityFunction(x, 4, 5)**3) == \
+        r"{\left({\langle x - 4 \rangle}^{5}\right)}^{3}"
+    assert latex(SingularityFunction(x, -3, 4)**3) == \
+        r"{\left({\langle x + 3 \rangle}^{4}\right)}^{3}"
+    assert latex(SingularityFunction(x, 0, 4)**3) == \
+        r"{\left({\langle x \rangle}^{4}\right)}^{3}"
+    assert latex(SingularityFunction(x, a, n)**3) == \
+        r"{\left({\langle - a + x \rangle}^{n}\right)}^{3}"
+    assert latex(SingularityFunction(x, 4, -2)**3) == \
+        r"{\left({\langle x - 4 \rangle}^{-2}\right)}^{3}"
+    assert latex((SingularityFunction(x, 4, -1)**3)**3) == \
+        r"{\left({\langle x - 4 \rangle}^{-1}\right)}^{9}"
+
 
 def test_latex_cycle():
     assert latex(Cycle(1, 2, 4)) == r"\left( 1\; 2\; 4\right)"


Code review comments:
diff_hunk: @@ -1968,10 +1968,12 @@ def _print_DiracDelta(self, expr, exp=None):
             tex = r"\left(%s\right)^{%s}" % (tex, exp)
         return tex
 
-    def _print_SingularityFunction(self, expr):
+    def _print_SingularityFunction(self, expr, exp=None):
         shift = self._print(expr.args[0] - expr.args[1])
         power = self._print(expr.args[2])
         tex = r"{\left\langle %s \right\rangle}^{%s}" % (shift, power)
+        if exp:
path: sympy/printing/latex.py
text: @user1:
```suggestion
        if exp is not None:
```