def test_sequence(): form = SeqFormula(n**2, (n, 0, 5)) per = SeqPer((1, 2, 3), (n, 0, 5)) inter = SeqFormula(n**2) assert sequence(n**2, (n, 0, 5)) == form assert sequence((1, 2, 3), (n, 0, 5)) == per assert sequence(n**2) == inter
def test_add(): per = SeqPer((1, 2), (n, 0, oo)) form = SeqFormula(n**2) assert per + (SeqPer((2, 3))) == SeqPer((3, 5), (n, 0, oo)) assert form + SeqFormula(n**3) == SeqFormula(n**2 + n**3) assert per + form == SeqAdd(per, form) raises(TypeError, lambda: per + n) raises(TypeError, lambda: n + per)
def test_sub(): per = SeqPer((1, 2), (n, 0, oo)) form = SeqFormula(n**2) assert per - (SeqPer((2, 3))) == SeqPer((-1, -1), (n, 0, oo)) assert form - (SeqFormula(n**3)) == SeqFormula(n**2 - n**3) assert per - form == SeqAdd(per, -form) raises(TypeError, lambda: per - n) raises(TypeError, lambda: n - per)
def fourier_sin_seq(func, limits, n): """Returns the sin sequence in a Fourier series""" from sympy.integrals import integrate x, L = limits[0], limits[2] - limits[1] sin_term = sin(2 * n * pi * x / L) return SeqFormula(2 * sin_term * integrate(func * sin_term, limits) / L, (n, 1, oo))
def fourier_cos_seq(func, limits, n): """Returns the cos sequence in a Fourier series""" from sympy.integrals import integrate x, L = limits[0], limits[2] - limits[1] cos_term = cos(2 * n * pi * x / L) formula = 2 * cos_term * integrate(func * cos_term, limits) / L a0 = formula.subs(n, S.Zero) / 2 return a0, SeqFormula( 2 * cos_term * integrate(func * cos_term, limits) / L, (n, 1, oo))
def test_SeqExprOp(): form = SeqFormula(n**2, (n, 0, 10)) per = SeqPer((1, 2, 3), (m, 5, 10)) s = SeqExprOp(form, per) assert s.gen == (n**2, (1, 2, 3)) assert s.interval == Interval(5, 10) assert s.start == 5 assert s.stop == 10 assert s.length == 6 assert s.variables == (n, m)
def test_mul__coeff_mul(): assert SeqPer((1, 2), (n, 0, oo)).coeff_mul(2) == SeqPer((2, 4), (n, 0, oo)) assert SeqFormula(n**2).coeff_mul(2) == SeqFormula(2*n**2) assert S.EmptySequence.coeff_mul(100) == S.EmptySequence assert SeqPer((1, 2), (n, 0, oo)) * (SeqPer((2, 3))) == \ SeqPer((2, 6), (n, 0, oo)) assert SeqFormula(n**2) * SeqFormula(n**3) == SeqFormula(n**5) assert S.EmptySequence * SeqFormula(n**2) == S.EmptySequence assert SeqFormula(n**2) * S.EmptySequence == S.EmptySequence raises(TypeError, lambda: sequence(n**2) * n) raises(TypeError, lambda: n * sequence(n**2))
def test_operations(): per = SeqPer((1, 2), (n, 0, oo)) per2 = SeqPer((2, 4), (n, 0, oo)) form = SeqFormula(n**2) form2 = SeqFormula(n**3) assert per + form + form2 == SeqAdd(per, form, form2) assert per + form - form2 == SeqAdd(per, form, -form2) assert per + form - S.EmptySequence == SeqAdd(per, form) assert per + per2 + form == SeqAdd(SeqPer((3, 6), (n, 0, oo)), form) assert S.EmptySequence - per == -per assert form + form == SeqFormula(2*n**2) assert per * form * form2 == SeqMul(per, form, form2) assert form * form == SeqFormula(n**4) assert form * -form == SeqFormula(-n**4) assert form * (per + form2) == SeqMul(form, SeqAdd(per, form2)) assert form * (per + per) == SeqMul(form, per2) assert form.coeff_mul(m) == SeqFormula(m*n**2, (n, 0, oo)) assert per.coeff_mul(m) == SeqPer((m, 2*m), (n, 0, oo))
def test_SeqAdd(): per = SeqPer((1, 2, 3), (n, 0, oo)) form = SeqFormula(n**2) per_bou = SeqPer((1, 2), (n, 1, 5)) form_bou = SeqFormula(n**2, (6, 10)) form_bou2 = SeqFormula(n**2, (1, 5)) assert SeqAdd() == S.EmptySequence assert SeqAdd(S.EmptySequence) == S.EmptySequence assert SeqAdd(per) == per assert SeqAdd(per, S.EmptySequence) == per assert SeqAdd(per_bou, form_bou) == S.EmptySequence s = SeqAdd(per_bou, form_bou2, evaluate=False) assert s.args == (form_bou2, per_bou) assert s[:] == [2, 6, 10, 18, 26] assert list(s) == [2, 6, 10, 18, 26] assert isinstance(SeqAdd(per, per_bou, evaluate=False), SeqAdd) s1 = SeqAdd(per, per_bou) assert isinstance(s1, SeqPer) assert s1 == SeqPer((2, 4, 4, 3, 3, 5), (n, 1, 5)) s2 = SeqAdd(form, form_bou) assert isinstance(s2, SeqFormula) assert s2 == SeqFormula(2*n**2, (6, 10)) assert SeqAdd(form, form_bou, per) == \ SeqAdd(per, SeqFormula(2*n**2, (6, 10))) assert SeqAdd(form, SeqAdd(form_bou, per)) == \ SeqAdd(per, SeqFormula(2*n**2, (6, 10))) assert SeqAdd(per, SeqAdd(form, form_bou), evaluate=False) == \ SeqAdd(per, SeqFormula(2*n**2, (6, 10))) assert SeqAdd(SeqPer((1, 2), (n, 0, oo)), SeqPer((1, 2), (m, 0, oo))) == \ SeqPer((2, 4), (n, 0, oo))
def test_SeqMul(): per = SeqPer((1, 2, 3), (n, 0, oo)) form = SeqFormula(n**2) per_bou = SeqPer((1, 2), (n, 1, 5)) form_bou = SeqFormula(n**2, (n, 6, 10)) form_bou2 = SeqFormula(n**2, (1, 5)) assert SeqMul() == S.EmptySequence assert SeqMul(S.EmptySequence) == S.EmptySequence assert SeqMul(per) == per assert SeqMul(per, S.EmptySequence) == S.EmptySequence assert SeqMul(per_bou, form_bou) == S.EmptySequence s = SeqMul(per_bou, form_bou2, evaluate=False) assert s.args == (form_bou2, per_bou) assert s[:] == [1, 8, 9, 32, 25] assert list(s) == [1, 8, 9, 32, 25] assert isinstance(SeqMul(per, per_bou, evaluate=False), SeqMul) s1 = SeqMul(per, per_bou) assert isinstance(s1, SeqPer) assert s1 == SeqPer((1, 4, 3, 2, 2, 6), (n, 1, 5)) s2 = SeqMul(form, form_bou) assert isinstance(s2, SeqFormula) assert s2 == SeqFormula(n**4, (6, 10)) assert SeqMul(form, form_bou, per) == \ SeqMul(per, SeqFormula(n**4, (6, 10))) assert SeqMul(form, SeqMul(form_bou, per)) == \ SeqMul(per, SeqFormula(n**4, (6, 10))) assert SeqMul(per, SeqMul(form, form_bou2, evaluate=False), evaluate=False) == \ SeqMul(form, per, form_bou2, evaluate=False) assert SeqMul(SeqPer((1, 2), (n, 0, oo)), SeqPer((1, 2), (n, 0, oo))) == \ SeqPer((1, 4), (n, 0, oo))
def fourier_series(f, limits=None, finite=True): r"""Computes the Fourier trigonometric series expansion. Explanation =========== Fourier trigonometric series of $f(x)$ over the interval $(a, b)$ is defined as: .. math:: \frac{a_0}{2} + \sum_{n=1}^{\infty} (a_n \cos(\frac{2n \pi x}{L}) + b_n \sin(\frac{2n \pi x}{L})) where the coefficients are: .. math:: L = b - a .. math:: a_0 = \frac{2}{L} \int_{a}^{b}{f(x) dx} .. math:: a_n = \frac{2}{L} \int_{a}^{b}{f(x) \cos(\frac{2n \pi x}{L}) dx} .. math:: b_n = \frac{2}{L} \int_{a}^{b}{f(x) \sin(\frac{2n \pi x}{L}) dx} The condition whether the function $f(x)$ given should be periodic or not is more than necessary, because it is sufficient to consider the series to be converging to $f(x)$ only in the given interval, not throughout the whole real line. This also brings a lot of ease for the computation because you don't have to make $f(x)$ artificially periodic by wrapping it with piecewise, modulo operations, but you can shape the function to look like the desired periodic function only in the interval $(a, b)$, and the computed series will automatically become the series of the periodic version of $f(x)$. This property is illustrated in the examples section below. Parameters ========== limits : (sym, start, end), optional *sym* denotes the symbol the series is computed with respect to. *start* and *end* denotes the start and the end of the interval where the fourier series converges to the given function. Default range is specified as $-\pi$ and $\pi$. Returns ======= FourierSeries A symbolic object representing the Fourier trigonometric series. Examples ======== Computing the Fourier series of $f(x) = x^2$: >>> from sympy import fourier_series, pi >>> from sympy.abc import x >>> f = x**2 >>> s = fourier_series(f, (x, -pi, pi)) >>> s1 = s.truncate(n=3) >>> s1 -4*cos(x) + cos(2*x) + pi**2/3 Shifting of the Fourier series: >>> s.shift(1).truncate() -4*cos(x) + cos(2*x) + 1 + pi**2/3 >>> s.shiftx(1).truncate() -4*cos(x + 1) + cos(2*x + 2) + pi**2/3 Scaling of the Fourier series: >>> s.scale(2).truncate() -8*cos(x) + 2*cos(2*x) + 2*pi**2/3 >>> s.scalex(2).truncate() -4*cos(2*x) + cos(4*x) + pi**2/3 Computing the Fourier series of $f(x) = x$: This illustrates how truncating to the higher order gives better convergence. .. plot:: :context: reset :format: doctest :include-source: True >>> from sympy import fourier_series, pi, plot >>> from sympy.abc import x >>> f = x >>> s = fourier_series(f, (x, -pi, pi)) >>> s1 = s.truncate(n = 3) >>> s2 = s.truncate(n = 5) >>> s3 = s.truncate(n = 7) >>> p = plot(f, s1, s2, s3, (x, -pi, pi), show=False, legend=True) >>> p[0].line_color = (0, 0, 0) >>> p[0].label = 'x' >>> p[1].line_color = (0.7, 0.7, 0.7) >>> p[1].label = 'n=3' >>> p[2].line_color = (0.5, 0.5, 0.5) >>> p[2].label = 'n=5' >>> p[3].line_color = (0.3, 0.3, 0.3) >>> p[3].label = 'n=7' >>> p.show() This illustrates how the series converges to different sawtooth waves if the different ranges are specified. .. plot:: :context: close-figs :format: doctest :include-source: True >>> s1 = fourier_series(x, (x, -1, 1)).truncate(10) >>> s2 = fourier_series(x, (x, -pi, pi)).truncate(10) >>> s3 = fourier_series(x, (x, 0, 1)).truncate(10) >>> p = plot(x, s1, s2, s3, (x, -5, 5), show=False, legend=True) >>> p[0].line_color = (0, 0, 0) >>> p[0].label = 'x' >>> p[1].line_color = (0.7, 0.7, 0.7) >>> p[1].label = '[-1, 1]' >>> p[2].line_color = (0.5, 0.5, 0.5) >>> p[2].label = '[-pi, pi]' >>> p[3].line_color = (0.3, 0.3, 0.3) >>> p[3].label = '[0, 1]' >>> p.show() Notes ===== Computing Fourier series can be slow due to the integration required in computing an, bn. It is faster to compute Fourier series of a function by using shifting and scaling on an already computed Fourier series rather than computing again. e.g. If the Fourier series of ``x**2`` is known the Fourier series of ``x**2 - 1`` can be found by shifting by ``-1``. See Also ======== sympy.series.fourier.FourierSeries References ========== .. [1] https://mathworld.wolfram.com/FourierSeries.html """ f = sympify(f) limits = _process_limits(f, limits) x = limits[0] if x not in f.free_symbols: return f if finite: L = abs(limits[2] - limits[1]) / 2 is_finite, res_f = finite_check(f, x, L) if is_finite: return FiniteFourierSeries(f, limits, res_f) n = Dummy('n') center = (limits[1] + limits[2]) / 2 if center.is_zero: neg_f = f.subs(x, -x) if f == neg_f: a0, an = fourier_cos_seq(f, limits, n) bn = SeqFormula(0, (1, oo)) return FourierSeries(f, limits, (a0, an, bn)) elif f == -neg_f: a0 = S.Zero an = SeqFormula(0, (1, oo)) bn = fourier_sin_seq(f, limits, n) return FourierSeries(f, limits, (a0, an, bn)) a0, an = fourier_cos_seq(f, limits, n) bn = fourier_sin_seq(f, limits, n) return FourierSeries(f, limits, (a0, an, bn))
def test_SeqFormula(): s = SeqFormula(n**2, (n, 0, 5)) assert isinstance(s, SeqFormula) assert s.formula == n**2 assert s.coeff(3) == 9 assert list(s) == [i**2 for i in range(6)] assert s[:] == [i**2 for i in range(6)] assert SeqFormula(n**2, (n, -oo, 0))[0:6] == [i**2 for i in range(6)] assert SeqFormula(n**2, (0, oo)) == SeqFormula(n**2, (n, 0, oo)) assert SeqFormula(n**2, (0, m)).subs(m, x) == SeqFormula(n**2, (0, x)) assert SeqFormula(m*n**2, (n, 0, oo)).subs(m, x) == \ SeqFormula(x*n**2, (n, 0, oo)) raises(ValueError, lambda: SeqFormula(n**2, (0, 1, 2))) raises(ValueError, lambda: SeqFormula(n**2, (n, -oo, oo))) raises(ValueError, lambda: SeqFormula(m*n**2, (0, oo))) seq = SeqFormula(x*(y**2 + z), (z, 1, 100)) assert seq.expand() == SeqFormula(x*y**2 + x*z, (z, 1, 100)) seq = SeqFormula(sin(x*(y**2 + z)),(z, 1, 100)) assert seq.expand(trig=True) == SeqFormula(sin(x*y**2)*cos(x*z) + sin(x*z)*cos(x*y**2), (z, 1, 100)) assert seq.expand() == SeqFormula(sin(x*y**2 + x*z), (z, 1, 100)) assert seq.expand(trig=False) == SeqFormula(sin(x*y**2 + x*z), (z, 1, 100)) seq = SeqFormula(exp(x*(y**2 + z)), (z, 1, 100)) assert seq.expand() == SeqFormula(exp(x*y**2)*exp(x*z), (z, 1, 100)) assert seq.expand(power_exp=False) == SeqFormula(exp(x*y**2 + x*z), (z, 1, 100)) assert seq.expand(mul=False, power_exp=False) == SeqFormula(exp(x*(y**2 + z)), (z, 1, 100))
def test_Idx_limits(): i = symbols('i', cls=Idx) r = Indexed('r', i) assert SeqFormula(r, (i, 0, 5))[:] == [r.subs(i, j) for j in range(6)] assert SeqPer((1, 2), (i, 0, 5))[:] == [1, 2, 1, 2, 1, 2]
def test_neg(): assert -SeqPer((1, -2), (n, 0, oo)) == SeqPer((-1, 2), (n, 0, oo)) assert -SeqFormula(n**2) == SeqFormula(-n**2)
def fourier_series(f, limits=None, finite=True): """Computes Fourier sine/cosine series expansion. Returns a :class:`FourierSeries` object. Examples ======== >>> from sympy import fourier_series, pi, cos >>> from sympy.abc import x >>> s = fourier_series(x**2, (x, -pi, pi)) >>> s.truncate(n=3) -4*cos(x) + cos(2*x) + pi**2/3 Shifting >>> s.shift(1).truncate() -4*cos(x) + cos(2*x) + 1 + pi**2/3 >>> s.shiftx(1).truncate() -4*cos(x + 1) + cos(2*x + 2) + pi**2/3 Scaling >>> s.scale(2).truncate() -8*cos(x) + 2*cos(2*x) + 2*pi**2/3 >>> s.scalex(2).truncate() -4*cos(2*x) + cos(4*x) + pi**2/3 Notes ===== Computing Fourier series can be slow due to the integration required in computing an, bn. It is faster to compute Fourier series of a function by using shifting and scaling on an already computed Fourier series rather than computing again. e.g. If the Fourier series of ``x**2`` is known the Fourier series of ``x**2 - 1`` can be found by shifting by ``-1``. See Also ======== sympy.series.fourier.FourierSeries References ========== .. [1] mathworld.wolfram.com/FourierSeries.html """ f = sympify(f) limits = _process_limits(f, limits) x = limits[0] if x not in f.free_symbols: return f if finite: L = abs(limits[2] - limits[1]) / 2 is_finite, res_f = finite_check(f, x, L) if is_finite: return FiniteFourierSeries(f, limits, res_f) n = Dummy('n') neg_f = f.subs(x, -x) if f == neg_f: a0, an = fourier_cos_seq(f, limits, n) bn = SeqFormula(0, (1, oo)) elif f == -neg_f: a0 = S.Zero an = SeqFormula(0, (1, oo)) bn = fourier_sin_seq(f, limits, n) else: a0, an = fourier_cos_seq(f, limits, n) bn = fourier_sin_seq(f, limits, n) return FourierSeries(f, limits, (a0, an, bn))
def fourier_series(f, limits=None): """Computes fourier sine/cosine series expansion returns a ``FourierSeries`` object Examples ======== >>> from sympy import fourier_series, pi, cos >>> from sympy.abc import x >>> s = fourier_series(x**2, (x, -pi, pi)) >>> s.truncate(n=3) -4*cos(x) + cos(2*x) + pi**2/3 Shifting >>> s.shift(1).truncate() -4*cos(x) + cos(2*x) + 1 + pi**2/3 >>> s.shiftx(1).truncate() -4*cos(x + 1) + cos(2*x + 2) + pi**2/3 Scaling >>> s.scale(2).truncate() -8*cos(x) + 2*cos(2*x) + 2*pi**2/3 >>> s.scalex(2).truncate() -4*cos(2*x) + cos(4*x) + pi**2/3 Notes ===== Computing a fourier series can be slow due to the integration required in computing an, bn. It is faster to compute fourier series of a function by using shifting and scaling on an already computed fourier series rather than computing again. eg. If Fourier series of x**2 is known fourier series of x**2 - 1 can be found by shifting by -1 See Also ======== sympy.series.fourier.FourierSeries References ========== .. [1] mathworld.wolfram.com/FourierSeries.html """ f = sympify(f) limits = _process_limits(f, limits) x = limits[0] if x not in f.free_symbols: return f n = Dummy('n') neg_f = f.subs(x, -x) if f == neg_f: a0, an = fourier_cos_seq(f, limits, n) bn = SeqFormula(0, (1, oo)) elif f == -neg_f: a0 = S.Zero an = SeqFormula(0, (1, oo)) bn = fourier_sin_seq(f, limits, n) else: a0, an = fourier_cos_seq(f, limits, n) bn = fourier_sin_seq(f, limits, n) return FourierSeries(f, limits, (a0, an, bn))