fourier.py 22 KB

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