fourier.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. """Fourier Series"""
  2. from sympy.core.numbers import (oo, pi)
  3. from sympy.core.symbol import Wild
  4. from sympy.core.expr import Expr
  5. from sympy.core.add import Add
  6. from sympy.core.containers import Tuple
  7. from sympy.core.singleton import S
  8. from sympy.core.symbol import Dummy, Symbol
  9. from sympy.core.sympify import sympify
  10. from sympy.functions.elementary.trigonometric import sin, cos, sinc
  11. from sympy.series.series_class import SeriesBase
  12. from sympy.series.sequences import SeqFormula
  13. from sympy.sets.sets import Interval
  14. from sympy.utilities.iterables import is_sequence
  15. __doctest_requires__ = {('fourier_series',): ['matplotlib']}
  16. def fourier_cos_seq(func, limits, n):
  17. """Returns the cos sequence in a Fourier series"""
  18. from sympy.integrals import integrate
  19. x, L = limits[0], limits[2] - limits[1]
  20. cos_term = cos(2*n*pi*x / L)
  21. formula = 2 * cos_term * integrate(func * cos_term, limits) / L
  22. a0 = formula.subs(n, S.Zero) / 2
  23. return a0, SeqFormula(2 * cos_term * integrate(func * cos_term, limits)
  24. / L, (n, 1, oo))
  25. def fourier_sin_seq(func, limits, n):
  26. """Returns the sin sequence in a Fourier series"""
  27. from sympy.integrals import integrate
  28. x, L = limits[0], limits[2] - limits[1]
  29. sin_term = sin(2*n*pi*x / L)
  30. return SeqFormula(2 * sin_term * integrate(func * sin_term, limits)
  31. / L, (n, 1, oo))
  32. def _process_limits(func, limits):
  33. """
  34. Limits should be of the form (x, start, stop).
  35. x should be a symbol. Both start and stop should be bounded.
  36. Explanation
  37. ===========
  38. * If x is not given, x is determined from func.
  39. * If limits is None. Limit of the form (x, -pi, pi) is returned.
  40. Examples
  41. ========
  42. >>> from sympy.series.fourier import _process_limits as pari
  43. >>> from sympy.abc import x
  44. >>> pari(x**2, (x, -2, 2))
  45. (x, -2, 2)
  46. >>> pari(x**2, (-2, 2))
  47. (x, -2, 2)
  48. >>> pari(x**2, None)
  49. (x, -pi, pi)
  50. """
  51. def _find_x(func):
  52. free = func.free_symbols
  53. if len(free) == 1:
  54. return free.pop()
  55. elif not free:
  56. return Dummy('k')
  57. else:
  58. raise ValueError(
  59. " specify dummy variables for %s. If the function contains"
  60. " more than one free symbol, a dummy variable should be"
  61. " supplied explicitly e.g. FourierSeries(m*n**2, (n, -pi, pi))"
  62. % func)
  63. x, start, stop = None, None, None
  64. if limits is None:
  65. x, start, stop = _find_x(func), -pi, pi
  66. if is_sequence(limits, Tuple):
  67. if len(limits) == 3:
  68. x, start, stop = limits
  69. elif len(limits) == 2:
  70. x = _find_x(func)
  71. start, stop = limits
  72. if not isinstance(x, Symbol) or start is None or stop is None:
  73. raise ValueError('Invalid limits given: %s' % str(limits))
  74. unbounded = [S.NegativeInfinity, S.Infinity]
  75. if start in unbounded or stop in unbounded:
  76. raise ValueError("Both the start and end value should be bounded")
  77. return sympify((x, start, stop))
  78. def finite_check(f, x, L):
  79. def check_fx(exprs, x):
  80. return x not in exprs.free_symbols
  81. def check_sincos(_expr, x, L):
  82. if isinstance(_expr, (sin, cos)):
  83. sincos_args = _expr.args[0]
  84. if sincos_args.match(a*(pi/L)*x + b) is not None:
  85. return True
  86. else:
  87. return False
  88. from sympy.simplify.fu import TR2, TR1, sincos_to_sum
  89. _expr = sincos_to_sum(TR2(TR1(f)))
  90. add_coeff = _expr.as_coeff_add()
  91. a = Wild('a', properties=[lambda k: k.is_Integer, lambda k: k != S.Zero, ])
  92. b = Wild('b', properties=[lambda k: x not in k.free_symbols, ])
  93. for s in add_coeff[1]:
  94. mul_coeffs = s.as_coeff_mul()[1]
  95. for t in mul_coeffs:
  96. if not (check_fx(t, x) or check_sincos(t, x, L)):
  97. return False, f
  98. return True, _expr
  99. class FourierSeries(SeriesBase):
  100. r"""Represents Fourier sine/cosine series.
  101. Explanation
  102. ===========
  103. This class only represents a fourier series.
  104. No computation is performed.
  105. For how to compute Fourier series, see the :func:`fourier_series`
  106. docstring.
  107. See Also
  108. ========
  109. sympy.series.fourier.fourier_series
  110. """
  111. def __new__(cls, *args):
  112. args = map(sympify, args)
  113. return Expr.__new__(cls, *args)
  114. @property
  115. def function(self):
  116. return self.args[0]
  117. @property
  118. def x(self):
  119. return self.args[1][0]
  120. @property
  121. def period(self):
  122. return (self.args[1][1], self.args[1][2])
  123. @property
  124. def a0(self):
  125. return self.args[2][0]
  126. @property
  127. def an(self):
  128. return self.args[2][1]
  129. @property
  130. def bn(self):
  131. return self.args[2][2]
  132. @property
  133. def interval(self):
  134. return Interval(0, oo)
  135. @property
  136. def start(self):
  137. return self.interval.inf
  138. @property
  139. def stop(self):
  140. return self.interval.sup
  141. @property
  142. def length(self):
  143. return oo
  144. @property
  145. def L(self):
  146. return abs(self.period[1] - self.period[0]) / 2
  147. def _eval_subs(self, old, new):
  148. x = self.x
  149. if old.has(x):
  150. return self
  151. def truncate(self, n=3):
  152. """
  153. Return the first n nonzero terms of the series.
  154. If ``n`` is None return an iterator.
  155. Parameters
  156. ==========
  157. n : int or None
  158. Amount of non-zero terms in approximation or None.
  159. Returns
  160. =======
  161. Expr or iterator :
  162. Approximation of function expanded into Fourier series.
  163. Examples
  164. ========
  165. >>> from sympy import fourier_series, pi
  166. >>> from sympy.abc import x
  167. >>> s = fourier_series(x, (x, -pi, pi))
  168. >>> s.truncate(4)
  169. 2*sin(x) - sin(2*x) + 2*sin(3*x)/3 - sin(4*x)/2
  170. See Also
  171. ========
  172. sympy.series.fourier.FourierSeries.sigma_approximation
  173. """
  174. if n is None:
  175. return iter(self)
  176. terms = []
  177. for t in self:
  178. if len(terms) == n:
  179. break
  180. if t is not S.Zero:
  181. terms.append(t)
  182. return Add(*terms)
  183. def sigma_approximation(self, n=3):
  184. r"""
  185. Return :math:`\sigma`-approximation of Fourier series with respect
  186. to order n.
  187. Explanation
  188. ===========
  189. Sigma approximation adjusts a Fourier summation to eliminate the Gibbs
  190. phenomenon which would otherwise occur at discontinuities.
  191. A sigma-approximated summation for a Fourier series of a T-periodical
  192. function can be written as
  193. .. math::
  194. s(\theta) = \frac{1}{2} a_0 + \sum _{k=1}^{m-1}
  195. \operatorname{sinc} \Bigl( \frac{k}{m} \Bigr) \cdot
  196. \left[ a_k \cos \Bigl( \frac{2\pi k}{T} \theta \Bigr)
  197. + b_k \sin \Bigl( \frac{2\pi k}{T} \theta \Bigr) \right],
  198. where :math:`a_0, a_k, b_k, k=1,\ldots,{m-1}` are standard Fourier
  199. series coefficients and
  200. :math:`\operatorname{sinc} \Bigl( \frac{k}{m} \Bigr)` is a Lanczos
  201. :math:`\sigma` factor (expressed in terms of normalized
  202. :math:`\operatorname{sinc}` function).
  203. Parameters
  204. ==========
  205. n : int
  206. Highest order of the terms taken into account in approximation.
  207. Returns
  208. =======
  209. Expr :
  210. Sigma approximation of function expanded into Fourier series.
  211. Examples
  212. ========
  213. >>> from sympy import fourier_series, pi
  214. >>> from sympy.abc import x
  215. >>> s = fourier_series(x, (x, -pi, pi))
  216. >>> s.sigma_approximation(4)
  217. 2*sin(x)*sinc(pi/4) - 2*sin(2*x)/pi + 2*sin(3*x)*sinc(3*pi/4)/3
  218. See Also
  219. ========
  220. sympy.series.fourier.FourierSeries.truncate
  221. Notes
  222. =====
  223. The behaviour of
  224. :meth:`~sympy.series.fourier.FourierSeries.sigma_approximation`
  225. is different from :meth:`~sympy.series.fourier.FourierSeries.truncate`
  226. - it takes all nonzero terms of degree smaller than n, rather than
  227. first n nonzero ones.
  228. References
  229. ==========
  230. .. [1] https://en.wikipedia.org/wiki/Gibbs_phenomenon
  231. .. [2] https://en.wikipedia.org/wiki/Sigma_approximation
  232. """
  233. terms = [sinc(pi * i / n) * t for i, t in enumerate(self[:n])
  234. if t is not S.Zero]
  235. return Add(*terms)
  236. def shift(self, s):
  237. """
  238. Shift the function by a term independent of x.
  239. Explanation
  240. ===========
  241. f(x) -> f(x) + s
  242. This is fast, if Fourier series of f(x) is already
  243. computed.
  244. Examples
  245. ========
  246. >>> from sympy import fourier_series, pi
  247. >>> from sympy.abc import x
  248. >>> s = fourier_series(x**2, (x, -pi, pi))
  249. >>> s.shift(1).truncate()
  250. -4*cos(x) + cos(2*x) + 1 + pi**2/3
  251. """
  252. s, x = sympify(s), self.x
  253. if x in s.free_symbols:
  254. raise ValueError("'%s' should be independent of %s" % (s, x))
  255. a0 = self.a0 + s
  256. sfunc = self.function + s
  257. return self.func(sfunc, self.args[1], (a0, self.an, self.bn))
  258. def shiftx(self, s):
  259. """
  260. Shift x by a term independent of x.
  261. Explanation
  262. ===========
  263. f(x) -> f(x + s)
  264. This is fast, if Fourier series of f(x) is already
  265. computed.
  266. Examples
  267. ========
  268. >>> from sympy import fourier_series, pi
  269. >>> from sympy.abc import x
  270. >>> s = fourier_series(x**2, (x, -pi, pi))
  271. >>> s.shiftx(1).truncate()
  272. -4*cos(x + 1) + cos(2*x + 2) + pi**2/3
  273. """
  274. s, x = sympify(s), self.x
  275. if x in s.free_symbols:
  276. raise ValueError("'%s' should be independent of %s" % (s, x))
  277. an = self.an.subs(x, x + s)
  278. bn = self.bn.subs(x, x + s)
  279. sfunc = self.function.subs(x, x + s)
  280. return self.func(sfunc, self.args[1], (self.a0, an, bn))
  281. def scale(self, s):
  282. """
  283. Scale the function by a term independent of x.
  284. Explanation
  285. ===========
  286. f(x) -> s * f(x)
  287. This is fast, if Fourier series of f(x) is already
  288. computed.
  289. Examples
  290. ========
  291. >>> from sympy import fourier_series, pi
  292. >>> from sympy.abc import x
  293. >>> s = fourier_series(x**2, (x, -pi, pi))
  294. >>> s.scale(2).truncate()
  295. -8*cos(x) + 2*cos(2*x) + 2*pi**2/3
  296. """
  297. s, x = sympify(s), self.x
  298. if x in s.free_symbols:
  299. raise ValueError("'%s' should be independent of %s" % (s, x))
  300. an = self.an.coeff_mul(s)
  301. bn = self.bn.coeff_mul(s)
  302. a0 = self.a0 * s
  303. sfunc = self.args[0] * s
  304. return self.func(sfunc, self.args[1], (a0, an, bn))
  305. def scalex(self, s):
  306. """
  307. Scale x by a term independent of x.
  308. Explanation
  309. ===========
  310. f(x) -> f(s*x)
  311. This is fast, if Fourier series of f(x) is already
  312. computed.
  313. Examples
  314. ========
  315. >>> from sympy import fourier_series, pi
  316. >>> from sympy.abc import x
  317. >>> s = fourier_series(x**2, (x, -pi, pi))
  318. >>> s.scalex(2).truncate()
  319. -4*cos(2*x) + cos(4*x) + pi**2/3
  320. """
  321. s, x = sympify(s), self.x
  322. if x in s.free_symbols:
  323. raise ValueError("'%s' should be independent of %s" % (s, x))
  324. an = self.an.subs(x, x * s)
  325. bn = self.bn.subs(x, x * s)
  326. sfunc = self.function.subs(x, x * s)
  327. return self.func(sfunc, self.args[1], (self.a0, an, bn))
  328. def _eval_as_leading_term(self, x, logx, cdir):
  329. for t in self:
  330. if t is not S.Zero:
  331. return t
  332. def _eval_term(self, pt):
  333. if pt == 0:
  334. return self.a0
  335. return self.an.coeff(pt) + self.bn.coeff(pt)
  336. def __neg__(self):
  337. return self.scale(-1)
  338. def __add__(self, other):
  339. if isinstance(other, FourierSeries):
  340. if self.period != other.period:
  341. raise ValueError("Both the series should have same periods")
  342. x, y = self.x, other.x
  343. function = self.function + other.function.subs(y, x)
  344. if self.x not in function.free_symbols:
  345. return function
  346. an = self.an + other.an
  347. bn = self.bn + other.bn
  348. a0 = self.a0 + other.a0
  349. return self.func(function, self.args[1], (a0, an, bn))
  350. return Add(self, other)
  351. def __sub__(self, other):
  352. return self.__add__(-other)
  353. class FiniteFourierSeries(FourierSeries):
  354. r"""Represents Finite Fourier sine/cosine series.
  355. For how to compute Fourier series, see the :func:`fourier_series`
  356. docstring.
  357. Parameters
  358. ==========
  359. f : Expr
  360. Expression for finding fourier_series
  361. limits : ( x, start, stop)
  362. x is the independent variable for the expression f
  363. (start, stop) is the period of the fourier series
  364. exprs: (a0, an, bn) or Expr
  365. a0 is the constant term a0 of the fourier series
  366. an is a dictionary of coefficients of cos terms
  367. an[k] = coefficient of cos(pi*(k/L)*x)
  368. bn is a dictionary of coefficients of sin terms
  369. bn[k] = coefficient of sin(pi*(k/L)*x)
  370. or exprs can be an expression to be converted to fourier form
  371. Methods
  372. =======
  373. This class is an extension of FourierSeries class.
  374. Please refer to sympy.series.fourier.FourierSeries for
  375. further information.
  376. See Also
  377. ========
  378. sympy.series.fourier.FourierSeries
  379. sympy.series.fourier.fourier_series
  380. """
  381. def __new__(cls, f, limits, exprs):
  382. f = sympify(f)
  383. limits = sympify(limits)
  384. exprs = sympify(exprs)
  385. if not (isinstance(exprs, Tuple) and len(exprs) == 3): # exprs is not of form (a0, an, bn)
  386. # Converts the expression to fourier form
  387. c, e = exprs.as_coeff_add()
  388. from sympy.simplify.fu import TR10
  389. rexpr = c + Add(*[TR10(i) for i in e])
  390. a0, exp_ls = rexpr.expand(trig=False, power_base=False, power_exp=False, log=False).as_coeff_add()
  391. x = limits[0]
  392. L = abs(limits[2] - limits[1]) / 2
  393. a = Wild('a', properties=[lambda k: k.is_Integer, lambda k: k is not S.Zero, ])
  394. b = Wild('b', properties=[lambda k: x not in k.free_symbols, ])
  395. an = {}
  396. bn = {}
  397. # separates the coefficients of sin and cos terms in dictionaries an, and bn
  398. for p in exp_ls:
  399. t = p.match(b * cos(a * (pi / L) * x))
  400. q = p.match(b * sin(a * (pi / L) * x))
  401. if t:
  402. an[t[a]] = t[b] + an.get(t[a], S.Zero)
  403. elif q:
  404. bn[q[a]] = q[b] + bn.get(q[a], S.Zero)
  405. else:
  406. a0 += p
  407. exprs = Tuple(a0, an, bn)
  408. return Expr.__new__(cls, f, limits, exprs)
  409. @property
  410. def interval(self):
  411. _length = 1 if self.a0 else 0
  412. _length += max(set(self.an.keys()).union(set(self.bn.keys()))) + 1
  413. return Interval(0, _length)
  414. @property
  415. def length(self):
  416. return self.stop - self.start
  417. def shiftx(self, s):
  418. s, x = sympify(s), self.x
  419. if x in s.free_symbols:
  420. raise ValueError("'%s' should be independent of %s" % (s, x))
  421. _expr = self.truncate().subs(x, x + s)
  422. sfunc = self.function.subs(x, x + s)
  423. return self.func(sfunc, self.args[1], _expr)
  424. def scale(self, s):
  425. s, x = sympify(s), self.x
  426. if x in s.free_symbols:
  427. raise ValueError("'%s' should be independent of %s" % (s, x))
  428. _expr = self.truncate() * s
  429. sfunc = self.function * s
  430. return self.func(sfunc, self.args[1], _expr)
  431. def scalex(self, s):
  432. s, x = sympify(s), self.x
  433. if x in s.free_symbols:
  434. raise ValueError("'%s' should be independent of %s" % (s, x))
  435. _expr = self.truncate().subs(x, x * s)
  436. sfunc = self.function.subs(x, x * s)
  437. return self.func(sfunc, self.args[1], _expr)
  438. def _eval_term(self, pt):
  439. if pt == 0:
  440. return self.a0
  441. _term = self.an.get(pt, S.Zero) * cos(pt * (pi / self.L) * self.x) \
  442. + self.bn.get(pt, S.Zero) * sin(pt * (pi / self.L) * self.x)
  443. return _term
  444. def __add__(self, other):
  445. if isinstance(other, FourierSeries):
  446. return other.__add__(fourier_series(self.function, self.args[1],\
  447. finite=False))
  448. elif isinstance(other, FiniteFourierSeries):
  449. if self.period != other.period:
  450. raise ValueError("Both the series should have same periods")
  451. x, y = self.x, other.x
  452. function = self.function + other.function.subs(y, x)
  453. if self.x not in function.free_symbols:
  454. return function
  455. return fourier_series(function, limits=self.args[1])
  456. def fourier_series(f, limits=None, finite=True):
  457. r"""Computes the Fourier trigonometric series expansion.
  458. Explanation
  459. ===========
  460. Fourier trigonometric series of $f(x)$ over the interval $(a, b)$
  461. is defined as:
  462. .. math::
  463. \frac{a_0}{2} + \sum_{n=1}^{\infty}
  464. (a_n \cos(\frac{2n \pi x}{L}) + b_n \sin(\frac{2n \pi x}{L}))
  465. where the coefficients are:
  466. .. math::
  467. L = b - a
  468. .. math::
  469. a_0 = \frac{2}{L} \int_{a}^{b}{f(x) dx}
  470. .. math::
  471. a_n = \frac{2}{L} \int_{a}^{b}{f(x) \cos(\frac{2n \pi x}{L}) dx}
  472. .. math::
  473. b_n = \frac{2}{L} \int_{a}^{b}{f(x) \sin(\frac{2n \pi x}{L}) dx}
  474. The condition whether the function $f(x)$ given should be periodic
  475. or not is more than necessary, because it is sufficient to consider
  476. the series to be converging to $f(x)$ only in the given interval,
  477. not throughout the whole real line.
  478. This also brings a lot of ease for the computation because
  479. you do not have to make $f(x)$ artificially periodic by
  480. wrapping it with piecewise, modulo operations,
  481. but you can shape the function to look like the desired periodic
  482. function only in the interval $(a, b)$, and the computed series will
  483. automatically become the series of the periodic version of $f(x)$.
  484. This property is illustrated in the examples section below.
  485. Parameters
  486. ==========
  487. limits : (sym, start, end), optional
  488. *sym* denotes the symbol the series is computed with respect to.
  489. *start* and *end* denotes the start and the end of the interval
  490. where the fourier series converges to the given function.
  491. Default range is specified as $-\pi$ and $\pi$.
  492. Returns
  493. =======
  494. FourierSeries
  495. A symbolic object representing the Fourier trigonometric series.
  496. Examples
  497. ========
  498. Computing the Fourier series of $f(x) = x^2$:
  499. >>> from sympy import fourier_series, pi
  500. >>> from sympy.abc import x
  501. >>> f = x**2
  502. >>> s = fourier_series(f, (x, -pi, pi))
  503. >>> s1 = s.truncate(n=3)
  504. >>> s1
  505. -4*cos(x) + cos(2*x) + pi**2/3
  506. Shifting of the Fourier series:
  507. >>> s.shift(1).truncate()
  508. -4*cos(x) + cos(2*x) + 1 + pi**2/3
  509. >>> s.shiftx(1).truncate()
  510. -4*cos(x + 1) + cos(2*x + 2) + pi**2/3
  511. Scaling of the Fourier series:
  512. >>> s.scale(2).truncate()
  513. -8*cos(x) + 2*cos(2*x) + 2*pi**2/3
  514. >>> s.scalex(2).truncate()
  515. -4*cos(2*x) + cos(4*x) + pi**2/3
  516. Computing the Fourier series of $f(x) = x$:
  517. This illustrates how truncating to the higher order gives better
  518. convergence.
  519. .. plot::
  520. :context: reset
  521. :format: doctest
  522. :include-source: True
  523. >>> from sympy import fourier_series, pi, plot
  524. >>> from sympy.abc import x
  525. >>> f = x
  526. >>> s = fourier_series(f, (x, -pi, pi))
  527. >>> s1 = s.truncate(n = 3)
  528. >>> s2 = s.truncate(n = 5)
  529. >>> s3 = s.truncate(n = 7)
  530. >>> p = plot(f, s1, s2, s3, (x, -pi, pi), show=False, legend=True)
  531. >>> p[0].line_color = (0, 0, 0)
  532. >>> p[0].label = 'x'
  533. >>> p[1].line_color = (0.7, 0.7, 0.7)
  534. >>> p[1].label = 'n=3'
  535. >>> p[2].line_color = (0.5, 0.5, 0.5)
  536. >>> p[2].label = 'n=5'
  537. >>> p[3].line_color = (0.3, 0.3, 0.3)
  538. >>> p[3].label = 'n=7'
  539. >>> p.show()
  540. This illustrates how the series converges to different sawtooth
  541. waves if the different ranges are specified.
  542. .. plot::
  543. :context: close-figs
  544. :format: doctest
  545. :include-source: True
  546. >>> s1 = fourier_series(x, (x, -1, 1)).truncate(10)
  547. >>> s2 = fourier_series(x, (x, -pi, pi)).truncate(10)
  548. >>> s3 = fourier_series(x, (x, 0, 1)).truncate(10)
  549. >>> p = plot(x, s1, s2, s3, (x, -5, 5), show=False, legend=True)
  550. >>> p[0].line_color = (0, 0, 0)
  551. >>> p[0].label = 'x'
  552. >>> p[1].line_color = (0.7, 0.7, 0.7)
  553. >>> p[1].label = '[-1, 1]'
  554. >>> p[2].line_color = (0.5, 0.5, 0.5)
  555. >>> p[2].label = '[-pi, pi]'
  556. >>> p[3].line_color = (0.3, 0.3, 0.3)
  557. >>> p[3].label = '[0, 1]'
  558. >>> p.show()
  559. Notes
  560. =====
  561. Computing Fourier series can be slow
  562. due to the integration required in computing
  563. an, bn.
  564. It is faster to compute Fourier series of a function
  565. by using shifting and scaling on an already
  566. computed Fourier series rather than computing
  567. again.
  568. e.g. If the Fourier series of ``x**2`` is known
  569. the Fourier series of ``x**2 - 1`` can be found by shifting by ``-1``.
  570. See Also
  571. ========
  572. sympy.series.fourier.FourierSeries
  573. References
  574. ==========
  575. .. [1] https://mathworld.wolfram.com/FourierSeries.html
  576. """
  577. f = sympify(f)
  578. limits = _process_limits(f, limits)
  579. x = limits[0]
  580. if x not in f.free_symbols:
  581. return f
  582. if finite:
  583. L = abs(limits[2] - limits[1]) / 2
  584. is_finite, res_f = finite_check(f, x, L)
  585. if is_finite:
  586. return FiniteFourierSeries(f, limits, res_f)
  587. n = Dummy('n')
  588. center = (limits[1] + limits[2]) / 2
  589. if center.is_zero:
  590. neg_f = f.subs(x, -x)
  591. if f == neg_f:
  592. a0, an = fourier_cos_seq(f, limits, n)
  593. bn = SeqFormula(0, (1, oo))
  594. return FourierSeries(f, limits, (a0, an, bn))
  595. elif f == -neg_f:
  596. a0 = S.Zero
  597. an = SeqFormula(0, (1, oo))
  598. bn = fourier_sin_seq(f, limits, n)
  599. return FourierSeries(f, limits, (a0, an, bn))
  600. a0, an = fourier_cos_seq(f, limits, n)
  601. bn = fourier_sin_seq(f, limits, n)
  602. return FourierSeries(f, limits, (a0, an, bn))