fourier.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  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. def fourier_cos_seq(func, limits, n):
  16. """Returns the cos sequence in a Fourier series"""
  17. from sympy.integrals import integrate
  18. x, L = limits[0], limits[2] - limits[1]
  19. cos_term = cos(2*n*pi*x / L)
  20. formula = 2 * cos_term * integrate(func * cos_term, limits) / L
  21. a0 = formula.subs(n, S.Zero) / 2
  22. return a0, SeqFormula(2 * cos_term * integrate(func * cos_term, limits)
  23. / L, (n, 1, oo))
  24. def fourier_sin_seq(func, limits, n):
  25. """Returns the sin sequence in a Fourier series"""
  26. from sympy.integrals import integrate
  27. x, L = limits[0], limits[2] - limits[1]
  28. sin_term = sin(2*n*pi*x / L)
  29. return SeqFormula(2 * sin_term * integrate(func * sin_term, limits)
  30. / L, (n, 1, oo))
  31. def _process_limits(func, limits):
  32. """
  33. Limits should be of the form (x, start, stop).
  34. x should be a symbol. Both start and stop should be bounded.
  35. Explanation
  36. ===========
  37. * If x is not given, x is determined from func.
  38. * If limits is None. Limit of the form (x, -pi, pi) is returned.
  39. Examples
  40. ========
  41. >>> from sympy.series.fourier import _process_limits as pari
  42. >>> from sympy.abc import x
  43. >>> pari(x**2, (x, -2, 2))
  44. (x, -2, 2)
  45. >>> pari(x**2, (-2, 2))
  46. (x, -2, 2)
  47. >>> pari(x**2, None)
  48. (x, -pi, pi)
  49. """
  50. def _find_x(func):
  51. free = func.free_symbols
  52. if len(free) == 1:
  53. return free.pop()
  54. elif not free:
  55. return Dummy('k')
  56. else:
  57. raise ValueError(
  58. " specify dummy variables for %s. If the function contains"
  59. " more than one free symbol, a dummy variable should be"
  60. " supplied explicitly e.g. FourierSeries(m*n**2, (n, -pi, pi))"
  61. % func)
  62. x, start, stop = None, None, None
  63. if limits is None:
  64. x, start, stop = _find_x(func), -pi, pi
  65. if is_sequence(limits, Tuple):
  66. if len(limits) == 3:
  67. x, start, stop = limits
  68. elif len(limits) == 2:
  69. x = _find_x(func)
  70. start, stop = limits
  71. if not isinstance(x, Symbol) or start is None or stop is None:
  72. raise ValueError('Invalid limits given: %s' % str(limits))
  73. unbounded = [S.NegativeInfinity, S.Infinity]
  74. if start in unbounded or stop in unbounded:
  75. raise ValueError("Both the start and end value should be bounded")
  76. return sympify((x, start, stop))
  77. def finite_check(f, x, L):
  78. def check_fx(exprs, x):
  79. return x not in exprs.free_symbols
  80. def check_sincos(_expr, x, L):
  81. if isinstance(_expr, (sin, cos)):
  82. sincos_args = _expr.args[0]
  83. if sincos_args.match(a*(pi/L)*x + b) is not None:
  84. return True
  85. else:
  86. return False
  87. from sympy.simplify.fu import TR2, TR1, sincos_to_sum
  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. from sympy.simplify.fu import TR10
  388. rexpr = c + Add(*[TR10(i) for i in e])
  389. a0, exp_ls = rexpr.expand(trig=False, power_base=False, power_exp=False, log=False).as_coeff_add()
  390. x = limits[0]
  391. L = abs(limits[2] - limits[1]) / 2
  392. a = Wild('a', properties=[lambda k: k.is_Integer, lambda k: k is not S.Zero, ])
  393. b = Wild('b', properties=[lambda k: x not in k.free_symbols, ])
  394. an = {}
  395. bn = {}
  396. # separates the coefficients of sin and cos terms in dictionaries an, and bn
  397. for p in exp_ls:
  398. t = p.match(b * cos(a * (pi / L) * x))
  399. q = p.match(b * sin(a * (pi / L) * x))
  400. if t:
  401. an[t[a]] = t[b] + an.get(t[a], S.Zero)
  402. elif q:
  403. bn[q[a]] = q[b] + bn.get(q[a], S.Zero)
  404. else:
  405. a0 += p
  406. exprs = Tuple(a0, an, bn)
  407. return Expr.__new__(cls, f, limits, exprs)
  408. @property
  409. def interval(self):
  410. _length = 1 if self.a0 else 0
  411. _length += max(set(self.an.keys()).union(set(self.bn.keys()))) + 1
  412. return Interval(0, _length)
  413. @property
  414. def length(self):
  415. return self.stop - self.start
  416. def shiftx(self, s):
  417. s, x = sympify(s), self.x
  418. if x in s.free_symbols:
  419. raise ValueError("'%s' should be independent of %s" % (s, x))
  420. _expr = self.truncate().subs(x, x + s)
  421. sfunc = self.function.subs(x, x + s)
  422. return self.func(sfunc, self.args[1], _expr)
  423. def scale(self, s):
  424. s, x = sympify(s), self.x
  425. if x in s.free_symbols:
  426. raise ValueError("'%s' should be independent of %s" % (s, x))
  427. _expr = self.truncate() * s
  428. sfunc = self.function * s
  429. return self.func(sfunc, self.args[1], _expr)
  430. def scalex(self, s):
  431. s, x = sympify(s), self.x
  432. if x in s.free_symbols:
  433. raise ValueError("'%s' should be independent of %s" % (s, x))
  434. _expr = self.truncate().subs(x, x * s)
  435. sfunc = self.function.subs(x, x * s)
  436. return self.func(sfunc, self.args[1], _expr)
  437. def _eval_term(self, pt):
  438. if pt == 0:
  439. return self.a0
  440. _term = self.an.get(pt, S.Zero) * cos(pt * (pi / self.L) * self.x) \
  441. + self.bn.get(pt, S.Zero) * sin(pt * (pi / self.L) * self.x)
  442. return _term
  443. def __add__(self, other):
  444. if isinstance(other, FourierSeries):
  445. return other.__add__(fourier_series(self.function, self.args[1],\
  446. finite=False))
  447. elif isinstance(other, FiniteFourierSeries):
  448. if self.period != other.period:
  449. raise ValueError("Both the series should have same periods")
  450. x, y = self.x, other.x
  451. function = self.function + other.function.subs(y, x)
  452. if self.x not in function.free_symbols:
  453. return function
  454. return fourier_series(function, limits=self.args[1])
  455. def fourier_series(f, limits=None, finite=True):
  456. r"""Computes the Fourier trigonometric series expansion.
  457. Explanation
  458. ===========
  459. Fourier trigonometric series of $f(x)$ over the interval $(a, b)$
  460. is defined as:
  461. .. math::
  462. \frac{a_0}{2} + \sum_{n=1}^{\infty}
  463. (a_n \cos(\frac{2n \pi x}{L}) + b_n \sin(\frac{2n \pi x}{L}))
  464. where the coefficients are:
  465. .. math::
  466. L = b - a
  467. .. math::
  468. a_0 = \frac{2}{L} \int_{a}^{b}{f(x) dx}
  469. .. math::
  470. a_n = \frac{2}{L} \int_{a}^{b}{f(x) \cos(\frac{2n \pi x}{L}) dx}
  471. .. math::
  472. b_n = \frac{2}{L} \int_{a}^{b}{f(x) \sin(\frac{2n \pi x}{L}) dx}
  473. The condition whether the function $f(x)$ given should be periodic
  474. or not is more than necessary, because it is sufficient to consider
  475. the series to be converging to $f(x)$ only in the given interval,
  476. not throughout the whole real line.
  477. This also brings a lot of ease for the computation because
  478. you do not have to make $f(x)$ artificially periodic by
  479. wrapping it with piecewise, modulo operations,
  480. but you can shape the function to look like the desired periodic
  481. function only in the interval $(a, b)$, and the computed series will
  482. automatically become the series of the periodic version of $f(x)$.
  483. This property is illustrated in the examples section below.
  484. Parameters
  485. ==========
  486. limits : (sym, start, end), optional
  487. *sym* denotes the symbol the series is computed with respect to.
  488. *start* and *end* denotes the start and the end of the interval
  489. where the fourier series converges to the given function.
  490. Default range is specified as $-\pi$ and $\pi$.
  491. Returns
  492. =======
  493. FourierSeries
  494. A symbolic object representing the Fourier trigonometric series.
  495. Examples
  496. ========
  497. Computing the Fourier series of $f(x) = x^2$:
  498. >>> from sympy import fourier_series, pi
  499. >>> from sympy.abc import x
  500. >>> f = x**2
  501. >>> s = fourier_series(f, (x, -pi, pi))
  502. >>> s1 = s.truncate(n=3)
  503. >>> s1
  504. -4*cos(x) + cos(2*x) + pi**2/3
  505. Shifting of the Fourier series:
  506. >>> s.shift(1).truncate()
  507. -4*cos(x) + cos(2*x) + 1 + pi**2/3
  508. >>> s.shiftx(1).truncate()
  509. -4*cos(x + 1) + cos(2*x + 2) + pi**2/3
  510. Scaling of the Fourier series:
  511. >>> s.scale(2).truncate()
  512. -8*cos(x) + 2*cos(2*x) + 2*pi**2/3
  513. >>> s.scalex(2).truncate()
  514. -4*cos(2*x) + cos(4*x) + pi**2/3
  515. Computing the Fourier series of $f(x) = x$:
  516. This illustrates how truncating to the higher order gives better
  517. convergence.
  518. .. plot::
  519. :context: reset
  520. :format: doctest
  521. :include-source: True
  522. >>> from sympy import fourier_series, pi, plot
  523. >>> from sympy.abc import x
  524. >>> f = x
  525. >>> s = fourier_series(f, (x, -pi, pi))
  526. >>> s1 = s.truncate(n = 3)
  527. >>> s2 = s.truncate(n = 5)
  528. >>> s3 = s.truncate(n = 7)
  529. >>> p = plot(f, s1, s2, s3, (x, -pi, pi), show=False, legend=True)
  530. >>> p[0].line_color = (0, 0, 0)
  531. >>> p[0].label = 'x'
  532. >>> p[1].line_color = (0.7, 0.7, 0.7)
  533. >>> p[1].label = 'n=3'
  534. >>> p[2].line_color = (0.5, 0.5, 0.5)
  535. >>> p[2].label = 'n=5'
  536. >>> p[3].line_color = (0.3, 0.3, 0.3)
  537. >>> p[3].label = 'n=7'
  538. >>> p.show()
  539. This illustrates how the series converges to different sawtooth
  540. waves if the different ranges are specified.
  541. .. plot::
  542. :context: close-figs
  543. :format: doctest
  544. :include-source: True
  545. >>> s1 = fourier_series(x, (x, -1, 1)).truncate(10)
  546. >>> s2 = fourier_series(x, (x, -pi, pi)).truncate(10)
  547. >>> s3 = fourier_series(x, (x, 0, 1)).truncate(10)
  548. >>> p = plot(x, s1, s2, s3, (x, -5, 5), show=False, legend=True)
  549. >>> p[0].line_color = (0, 0, 0)
  550. >>> p[0].label = 'x'
  551. >>> p[1].line_color = (0.7, 0.7, 0.7)
  552. >>> p[1].label = '[-1, 1]'
  553. >>> p[2].line_color = (0.5, 0.5, 0.5)
  554. >>> p[2].label = '[-pi, pi]'
  555. >>> p[3].line_color = (0.3, 0.3, 0.3)
  556. >>> p[3].label = '[0, 1]'
  557. >>> p.show()
  558. Notes
  559. =====
  560. Computing Fourier series can be slow
  561. due to the integration required in computing
  562. an, bn.
  563. It is faster to compute Fourier series of a function
  564. by using shifting and scaling on an already
  565. computed Fourier series rather than computing
  566. again.
  567. e.g. If the Fourier series of ``x**2`` is known
  568. the Fourier series of ``x**2 - 1`` can be found by shifting by ``-1``.
  569. See Also
  570. ========
  571. sympy.series.fourier.FourierSeries
  572. References
  573. ==========
  574. .. [1] https://mathworld.wolfram.com/FourierSeries.html
  575. """
  576. f = sympify(f)
  577. limits = _process_limits(f, limits)
  578. x = limits[0]
  579. if x not in f.free_symbols:
  580. return f
  581. if finite:
  582. L = abs(limits[2] - limits[1]) / 2
  583. is_finite, res_f = finite_check(f, x, L)
  584. if is_finite:
  585. return FiniteFourierSeries(f, limits, res_f)
  586. n = Dummy('n')
  587. center = (limits[1] + limits[2]) / 2
  588. if center.is_zero:
  589. neg_f = f.subs(x, -x)
  590. if f == neg_f:
  591. a0, an = fourier_cos_seq(f, limits, n)
  592. bn = SeqFormula(0, (1, oo))
  593. return FourierSeries(f, limits, (a0, an, bn))
  594. elif f == -neg_f:
  595. a0 = S.Zero
  596. an = SeqFormula(0, (1, oo))
  597. bn = fourier_sin_seq(f, limits, n)
  598. return FourierSeries(f, limits, (a0, an, bn))
  599. a0, an = fourier_cos_seq(f, limits, n)
  600. bn = fourier_sin_seq(f, limits, n)
  601. return FourierSeries(f, limits, (a0, an, bn))