Repository: sympy/sympy

Issue:
Add Lanczos sigma approximation functionality
I would like to add new function performing [Lanczos sigma approximation](http://mathworld.wolfram.com/LanczosSigmaFactor.html) task (I haven't seen one in sympy). 

My first idea is is to create`LanczosApproximation` (or `Lanczos Series`) class in `sympy.series.fourier` module inheriting `FourierSeries` class and than write function `lanczos_approximation` in `sympy.core.expr.Expr` using new class.

Are you OK with that? Do you have any remarks/corrections to my idea?


Discussion:
asmeurer: I don't think the printing tests are necessary. The return type is just a normal expression, isn't it? Those are already tested quite well. Just test the expressions themselves in the series tests. 
asmeurer: Regarding assigning to issues, just put "fixes #11626" in one of your commit messages, or in the top post. This will cause it to automatically close the issue when this gets merged. You can also post a link to the PR on the issue. Beyond that there is no metadata on GitHub that links pull requests to issues. 
flamyowl: Thanks for your insights - I've updated pull request according to it. Also, issue linking works :)
flamyowl: @asmeurer Can you look into this pull request one more? I think I've fixed all issues and it could be reviewed again.
flamyowl: @asmeurer May I ping you? Is that pull request ready to merge?

Patch:
diff --git a/sympy/series/fourier.py b/sympy/series/fourier.py
index 2196cea554..4a2a70a556 100644
--- a/sympy/series/fourier.py
+++ b/sympy/series/fourier.py
@@ -10,7 +10,7 @@
 from sympy.core.symbol import Dummy, Symbol
 from sympy.core.compatibility import is_sequence
 from sympy.core.containers import Tuple
-from sympy.functions.elementary.trigonometric import sin, cos
+from sympy.functions.elementary.trigonometric import sin, cos, sinc
 from sympy.sets.sets import Interval
 from sympy.series.series_class import SeriesBase
 from sympy.series.sequences import SeqFormula
@@ -154,9 +154,34 @@ def _eval_subs(self, old, new):
             return self
 
     def truncate(self, n=3):
-        """Returns the first n (non-zero)terms of the series.
+        """
+        Return the first n nonzero terms of the series.
+
+        If n is None return an iterator.
+
+        Parameters
+        ==========
+        n : int or None
+            Amount of non-zero terms in approximation or None.
+
+        Returns
+        =======
+        Expr or iterator
+            Approximation of function expanded into Fourier series.
 
-        If n is ``None`` returns an iterator.
+        Examples
+        ========
+
+        >>> from sympy import fourier_series, pi
+        >>> from sympy.abc import x
+        >>> s = fourier_series(x, (x, -pi, pi))
+        >>> s.truncate(4)
+        2*sin(x) - sin(2*x) + 2*sin(3*x)/3 - sin(4*x)/2
+
+        See Also
+        ========
+
+        sympy.series.fourier.FourierSeries.sigma_approximation
         """
         if n is None:
             return iter(self)
@@ -170,6 +195,71 @@ def truncate(self, n=3):
 
         return Add(*terms)
 
+    def sigma_approximation(self, n=3):
+        """
+        Return :math:`\sigma`-approximation of Fourier series with respect
+        to order n.
+
+        Sigma approximation adjusts a Fourier summation to eliminate the Gibbs
+        phenomenon which would otherwise occur at discontinuities.
+        A sigma-approximated summation for a Fourier series of a T-periodical
+        function can be written as
+
+        .. math::
+            s(\\theta) = \\frac{1}{2} a_0 + \\sum _{k=1}^{m-1}
+            \\operatorname{sinc} \\Bigl( \\frac{k}{m} \\Bigr) \\cdot
+            \\left[ a_k \\cos \\Bigl( \\frac{2\\pi k}{T} \\theta \\Bigr)
+            + b_k \\sin \\Bigl( \\frac{2\\pi k}{T} \\theta \\Bigr) \\right],
+
+        where :math:`a_0, a_k, b_k, k=1,\ldots,{m-1}` are standard Fourier
+        series coefficients and
+        :math:`\\operatorname{sinc} \\Bigl( \\frac{k}{m} \\Bigr)` is a Lanczos
+        :math:`\sigma` factor (expressed in terms of normalized
+        :math:`\operatorname{sinc}` function).
+
+        Parameters
+        ==========
+        n : int
+            Highest order of the terms taken into account in approximation.
+
+        Returns
+        =======
+        Expr
+            Sigma approximation of function expanded into Fourier series.
+
+        Examples
+        ========
+
+        >>> from sympy import fourier_series, pi
+        >>> from sympy.abc import x
+        >>> s = fourier_series(x, (x, -pi, pi))
+        >>> s.sigma_approximation(4)
+        2*sin(x)*sinc(pi/4) - 2*sin(2*x)/pi + 2*sin(3*x)*sinc(3*pi/4)/3
+
+        See Also
+        ========
+
+        sympy.series.fourier.FourierSeries.truncate
+
+        Notes
+        =====
+
+        The behaviour of
+        :meth:`~sympy.series.fourier.FourierSeries.sigma_approximation`
+        is different from :meth:`~sympy.series.fourier.FourierSeries.truncate`
+        - it takes all nonzero terms of degree smaller than n, rather than
+        first n nonzero ones.
+
+        References
+        ==========
+
+        .. [1] https://en.wikipedia.org/wiki/Gibbs_phenomenon
+        .. [2] https://en.wikipedia.org/wiki/Sigma_approximation
+        """
+        terms = [sinc(pi * i / n) * t for i, t in enumerate(self[:n])
+                 if t is not S.Zero]
+        return Add(*terms)
+
     def shift(self, s):
         """Shift the function by a term independent of x.
 


Test patch:
diff --git a/sympy/series/tests/test_fourier.py b/sympy/series/tests/test_fourier.py
index c1b3acb87e..6ff4722b00 100644
--- a/sympy/series/tests/test_fourier.py
+++ b/sympy/series/tests/test_fourier.py
@@ -1,4 +1,4 @@
-from sympy import (symbols, pi, Piecewise, sin, cos, Rational,
+from sympy import (symbols, pi, Piecewise, sin, cos, sinc, Rational,
                    oo, fourier_series, Add)
 from sympy.series.fourier import FourierSeries
 from sympy.utilities.pytest import raises
@@ -65,6 +65,17 @@ def test_FourierSeries_2():
                             4*cos(pi*x / 2) / pi**2 + Rational(1, 2))
 
 
+def test_fourier_series_square_wave():
+    """Test if fourier_series approximates discontinuous function correctly."""
+    square_wave = Piecewise((1, x < pi), (-1, True))
+    s = fourier_series(square_wave, (x, 0, 2*pi))
+
+    assert s.truncate(3) == 4 / pi * sin(x) + 4 / (3 * pi) * sin(3 * x) + \
+        4 / (5 * pi) * sin(5 * x)
+    assert s.sigma_approximation(4) == 4 / pi * sin(x) * sinc(pi / 4) + \
+        4 / (3 * pi) * sin(3 * x) * sinc(3 * pi / 4)
+
+
 def test_FourierSeries__operations():
     fes = fe.scale(-1).shift(pi**2)
     assert fes.truncate() == 4*cos(x) - cos(2*x) + 2*pi**2 / 3