util.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. from .accumulationbounds import AccumBounds, AccumulationBounds # noqa: F401
  2. from .singularities import singularities
  3. from sympy.core import Pow, S
  4. from sympy.core.function import diff, expand_mul
  5. from sympy.core.kind import NumberKind
  6. from sympy.core.mod import Mod
  7. from sympy.core.relational import Relational
  8. from sympy.core.symbol import Symbol, Dummy
  9. from sympy.core.sympify import _sympify
  10. from sympy.functions.elementary.complexes import Abs, im, re
  11. from sympy.functions.elementary.exponential import exp, log
  12. from sympy.functions.elementary.piecewise import Piecewise
  13. from sympy.functions.elementary.trigonometric import (
  14. TrigonometricFunction, sin, cos, csc, sec)
  15. from sympy.polys.polytools import degree, lcm_list
  16. from sympy.sets.sets import (Interval, Intersection, FiniteSet, Union,
  17. Complement)
  18. from sympy.sets.fancysets import ImageSet
  19. from sympy.simplify.simplify import simplify
  20. from sympy.utilities import filldedent
  21. from sympy.utilities.iterables import iterable
  22. def continuous_domain(f, symbol, domain):
  23. """
  24. Returns the intervals in the given domain for which the function
  25. is continuous.
  26. This method is limited by the ability to determine the various
  27. singularities and discontinuities of the given function.
  28. Parameters
  29. ==========
  30. f : :py:class:`~.Expr`
  31. The concerned function.
  32. symbol : :py:class:`~.Symbol`
  33. The variable for which the intervals are to be determined.
  34. domain : :py:class:`~.Interval`
  35. The domain over which the continuity of the symbol has to be checked.
  36. Examples
  37. ========
  38. >>> from sympy import Interval, Symbol, S, tan, log, pi, sqrt
  39. >>> from sympy.calculus.util import continuous_domain
  40. >>> x = Symbol('x')
  41. >>> continuous_domain(1/x, x, S.Reals)
  42. Union(Interval.open(-oo, 0), Interval.open(0, oo))
  43. >>> continuous_domain(tan(x), x, Interval(0, pi))
  44. Union(Interval.Ropen(0, pi/2), Interval.Lopen(pi/2, pi))
  45. >>> continuous_domain(sqrt(x - 2), x, Interval(-5, 5))
  46. Interval(2, 5)
  47. >>> continuous_domain(log(2*x - 1), x, S.Reals)
  48. Interval.open(1/2, oo)
  49. Returns
  50. =======
  51. :py:class:`~.Interval`
  52. Union of all intervals where the function is continuous.
  53. Raises
  54. ======
  55. NotImplementedError
  56. If the method to determine continuity of such a function
  57. has not yet been developed.
  58. """
  59. from sympy.solvers.inequalities import solve_univariate_inequality
  60. if domain.is_subset(S.Reals):
  61. constrained_interval = domain
  62. for atom in f.atoms(Pow):
  63. den = atom.exp.as_numer_denom()[1]
  64. if den.is_even and den.is_nonzero:
  65. constraint = solve_univariate_inequality(atom.base >= 0,
  66. symbol).as_set()
  67. constrained_interval = Intersection(constraint,
  68. constrained_interval)
  69. for atom in f.atoms(log):
  70. constraint = solve_univariate_inequality(atom.args[0] > 0,
  71. symbol).as_set()
  72. constrained_interval = Intersection(constraint,
  73. constrained_interval)
  74. return constrained_interval - singularities(f, symbol, domain)
  75. def function_range(f, symbol, domain):
  76. """
  77. Finds the range of a function in a given domain.
  78. This method is limited by the ability to determine the singularities and
  79. determine limits.
  80. Parameters
  81. ==========
  82. f : :py:class:`~.Expr`
  83. The concerned function.
  84. symbol : :py:class:`~.Symbol`
  85. The variable for which the range of function is to be determined.
  86. domain : :py:class:`~.Interval`
  87. The domain under which the range of the function has to be found.
  88. Examples
  89. ========
  90. >>> from sympy import Interval, Symbol, S, exp, log, pi, sqrt, sin, tan
  91. >>> from sympy.calculus.util import function_range
  92. >>> x = Symbol('x')
  93. >>> function_range(sin(x), x, Interval(0, 2*pi))
  94. Interval(-1, 1)
  95. >>> function_range(tan(x), x, Interval(-pi/2, pi/2))
  96. Interval(-oo, oo)
  97. >>> function_range(1/x, x, S.Reals)
  98. Union(Interval.open(-oo, 0), Interval.open(0, oo))
  99. >>> function_range(exp(x), x, S.Reals)
  100. Interval.open(0, oo)
  101. >>> function_range(log(x), x, S.Reals)
  102. Interval(-oo, oo)
  103. >>> function_range(sqrt(x), x, Interval(-5, 9))
  104. Interval(0, 3)
  105. Returns
  106. =======
  107. :py:class:`~.Interval`
  108. Union of all ranges for all intervals under domain where function is
  109. continuous.
  110. Raises
  111. ======
  112. NotImplementedError
  113. If any of the intervals, in the given domain, for which function
  114. is continuous are not finite or real,
  115. OR if the critical points of the function on the domain cannot be found.
  116. """
  117. if domain is S.EmptySet:
  118. return S.EmptySet
  119. period = periodicity(f, symbol)
  120. if period == S.Zero:
  121. # the expression is constant wrt symbol
  122. return FiniteSet(f.expand())
  123. from sympy.series.limits import limit
  124. from sympy.solvers.solveset import solveset
  125. if period is not None:
  126. if isinstance(domain, Interval):
  127. if (domain.inf - domain.sup).is_infinite:
  128. domain = Interval(0, period)
  129. elif isinstance(domain, Union):
  130. for sub_dom in domain.args:
  131. if isinstance(sub_dom, Interval) and \
  132. ((sub_dom.inf - sub_dom.sup).is_infinite):
  133. domain = Interval(0, period)
  134. intervals = continuous_domain(f, symbol, domain)
  135. range_int = S.EmptySet
  136. if isinstance(intervals,(Interval, FiniteSet)):
  137. interval_iter = (intervals,)
  138. elif isinstance(intervals, Union):
  139. interval_iter = intervals.args
  140. else:
  141. raise NotImplementedError(filldedent('''
  142. Unable to find range for the given domain.
  143. '''))
  144. for interval in interval_iter:
  145. if isinstance(interval, FiniteSet):
  146. for singleton in interval:
  147. if singleton in domain:
  148. range_int += FiniteSet(f.subs(symbol, singleton))
  149. elif isinstance(interval, Interval):
  150. vals = S.EmptySet
  151. critical_points = S.EmptySet
  152. critical_values = S.EmptySet
  153. bounds = ((interval.left_open, interval.inf, '+'),
  154. (interval.right_open, interval.sup, '-'))
  155. for is_open, limit_point, direction in bounds:
  156. if is_open:
  157. critical_values += FiniteSet(limit(f, symbol, limit_point, direction))
  158. vals += critical_values
  159. else:
  160. vals += FiniteSet(f.subs(symbol, limit_point))
  161. solution = solveset(f.diff(symbol), symbol, interval)
  162. if not iterable(solution):
  163. raise NotImplementedError(
  164. 'Unable to find critical points for {}'.format(f))
  165. if isinstance(solution, ImageSet):
  166. raise NotImplementedError(
  167. 'Infinite number of critical points for {}'.format(f))
  168. critical_points += solution
  169. for critical_point in critical_points:
  170. vals += FiniteSet(f.subs(symbol, critical_point))
  171. left_open, right_open = False, False
  172. if critical_values is not S.EmptySet:
  173. if critical_values.inf == vals.inf:
  174. left_open = True
  175. if critical_values.sup == vals.sup:
  176. right_open = True
  177. range_int += Interval(vals.inf, vals.sup, left_open, right_open)
  178. else:
  179. raise NotImplementedError(filldedent('''
  180. Unable to find range for the given domain.
  181. '''))
  182. return range_int
  183. def not_empty_in(finset_intersection, *syms):
  184. """
  185. Finds the domain of the functions in ``finset_intersection`` in which the
  186. ``finite_set`` is not-empty
  187. Parameters
  188. ==========
  189. finset_intersection : Intersection of FiniteSet
  190. The unevaluated intersection of FiniteSet containing
  191. real-valued functions with Union of Sets
  192. syms : Tuple of symbols
  193. Symbol for which domain is to be found
  194. Raises
  195. ======
  196. NotImplementedError
  197. The algorithms to find the non-emptiness of the given FiniteSet are
  198. not yet implemented.
  199. ValueError
  200. The input is not valid.
  201. RuntimeError
  202. It is a bug, please report it to the github issue tracker
  203. (https://github.com/sympy/sympy/issues).
  204. Examples
  205. ========
  206. >>> from sympy import FiniteSet, Interval, not_empty_in, oo
  207. >>> from sympy.abc import x
  208. >>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x)
  209. Interval(0, 2)
  210. >>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x)
  211. Union(Interval(1, 2), Interval(-sqrt(2), -1))
  212. >>> not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x)
  213. Union(Interval.Lopen(-2, -1), Interval(2, oo))
  214. """
  215. # TODO: handle piecewise defined functions
  216. # TODO: handle transcendental functions
  217. # TODO: handle multivariate functions
  218. if len(syms) == 0:
  219. raise ValueError("One or more symbols must be given in syms.")
  220. if finset_intersection is S.EmptySet:
  221. return S.EmptySet
  222. if isinstance(finset_intersection, Union):
  223. elm_in_sets = finset_intersection.args[0]
  224. return Union(not_empty_in(finset_intersection.args[1], *syms),
  225. elm_in_sets)
  226. if isinstance(finset_intersection, FiniteSet):
  227. finite_set = finset_intersection
  228. _sets = S.Reals
  229. else:
  230. finite_set = finset_intersection.args[1]
  231. _sets = finset_intersection.args[0]
  232. if not isinstance(finite_set, FiniteSet):
  233. raise ValueError('A FiniteSet must be given, not %s: %s' %
  234. (type(finite_set), finite_set))
  235. if len(syms) == 1:
  236. symb = syms[0]
  237. else:
  238. raise NotImplementedError('more than one variables %s not handled' %
  239. (syms,))
  240. def elm_domain(expr, intrvl):
  241. """ Finds the domain of an expression in any given interval """
  242. from sympy.solvers.solveset import solveset
  243. _start = intrvl.start
  244. _end = intrvl.end
  245. _singularities = solveset(expr.as_numer_denom()[1], symb,
  246. domain=S.Reals)
  247. if intrvl.right_open:
  248. if _end is S.Infinity:
  249. _domain1 = S.Reals
  250. else:
  251. _domain1 = solveset(expr < _end, symb, domain=S.Reals)
  252. else:
  253. _domain1 = solveset(expr <= _end, symb, domain=S.Reals)
  254. if intrvl.left_open:
  255. if _start is S.NegativeInfinity:
  256. _domain2 = S.Reals
  257. else:
  258. _domain2 = solveset(expr > _start, symb, domain=S.Reals)
  259. else:
  260. _domain2 = solveset(expr >= _start, symb, domain=S.Reals)
  261. # domain in the interval
  262. expr_with_sing = Intersection(_domain1, _domain2)
  263. expr_domain = Complement(expr_with_sing, _singularities)
  264. return expr_domain
  265. if isinstance(_sets, Interval):
  266. return Union(*[elm_domain(element, _sets) for element in finite_set])
  267. if isinstance(_sets, Union):
  268. _domain = S.EmptySet
  269. for intrvl in _sets.args:
  270. _domain_element = Union(*[elm_domain(element, intrvl)
  271. for element in finite_set])
  272. _domain = Union(_domain, _domain_element)
  273. return _domain
  274. def periodicity(f, symbol, check=False):
  275. """
  276. Tests the given function for periodicity in the given symbol.
  277. Parameters
  278. ==========
  279. f : :py:class:`~.Expr`.
  280. The concerned function.
  281. symbol : :py:class:`~.Symbol`
  282. The variable for which the period is to be determined.
  283. check : bool, optional
  284. The flag to verify whether the value being returned is a period or not.
  285. Returns
  286. =======
  287. period
  288. The period of the function is returned.
  289. ``None`` is returned when the function is aperiodic or has a complex period.
  290. The value of $0$ is returned as the period of a constant function.
  291. Raises
  292. ======
  293. NotImplementedError
  294. The value of the period computed cannot be verified.
  295. Notes
  296. =====
  297. Currently, we do not support functions with a complex period.
  298. The period of functions having complex periodic values such
  299. as ``exp``, ``sinh`` is evaluated to ``None``.
  300. The value returned might not be the "fundamental" period of the given
  301. function i.e. it may not be the smallest periodic value of the function.
  302. The verification of the period through the ``check`` flag is not reliable
  303. due to internal simplification of the given expression. Hence, it is set
  304. to ``False`` by default.
  305. Examples
  306. ========
  307. >>> from sympy import periodicity, Symbol, sin, cos, tan, exp
  308. >>> x = Symbol('x')
  309. >>> f = sin(x) + sin(2*x) + sin(3*x)
  310. >>> periodicity(f, x)
  311. 2*pi
  312. >>> periodicity(sin(x)*cos(x), x)
  313. pi
  314. >>> periodicity(exp(tan(2*x) - 1), x)
  315. pi/2
  316. >>> periodicity(sin(4*x)**cos(2*x), x)
  317. pi
  318. >>> periodicity(exp(x), x)
  319. """
  320. if symbol.kind is not NumberKind:
  321. raise NotImplementedError("Cannot use symbol of kind %s" % symbol.kind)
  322. temp = Dummy('x', real=True)
  323. f = f.subs(symbol, temp)
  324. symbol = temp
  325. def _check(orig_f, period):
  326. '''Return the checked period or raise an error.'''
  327. new_f = orig_f.subs(symbol, symbol + period)
  328. if new_f.equals(orig_f):
  329. return period
  330. else:
  331. raise NotImplementedError(filldedent('''
  332. The period of the given function cannot be verified.
  333. When `%s` was replaced with `%s + %s` in `%s`, the result
  334. was `%s` which was not recognized as being the same as
  335. the original function.
  336. So either the period was wrong or the two forms were
  337. not recognized as being equal.
  338. Set check=False to obtain the value.''' %
  339. (symbol, symbol, period, orig_f, new_f)))
  340. orig_f = f
  341. period = None
  342. if isinstance(f, Relational):
  343. f = f.lhs - f.rhs
  344. f = simplify(f)
  345. if symbol not in f.free_symbols:
  346. return S.Zero
  347. if isinstance(f, TrigonometricFunction):
  348. try:
  349. period = f.period(symbol)
  350. except NotImplementedError:
  351. pass
  352. if isinstance(f, Abs):
  353. arg = f.args[0]
  354. if isinstance(arg, (sec, csc, cos)):
  355. # all but tan and cot might have a
  356. # a period that is half as large
  357. # so recast as sin
  358. arg = sin(arg.args[0])
  359. period = periodicity(arg, symbol)
  360. if period is not None and isinstance(arg, sin):
  361. # the argument of Abs was a trigonometric other than
  362. # cot or tan; test to see if the half-period
  363. # is valid. Abs(arg) has behaviour equivalent to
  364. # orig_f, so use that for test:
  365. orig_f = Abs(arg)
  366. try:
  367. return _check(orig_f, period/2)
  368. except NotImplementedError as err:
  369. if check:
  370. raise NotImplementedError(err)
  371. # else let new orig_f and period be
  372. # checked below
  373. if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1):
  374. f = Pow(S.Exp1, expand_mul(f.exp))
  375. if im(f) != 0:
  376. period_real = periodicity(re(f), symbol)
  377. period_imag = periodicity(im(f), symbol)
  378. if period_real is not None and period_imag is not None:
  379. period = lcim([period_real, period_imag])
  380. if f.is_Pow and f.base != S.Exp1:
  381. base, expo = f.args
  382. base_has_sym = base.has(symbol)
  383. expo_has_sym = expo.has(symbol)
  384. if base_has_sym and not expo_has_sym:
  385. period = periodicity(base, symbol)
  386. elif expo_has_sym and not base_has_sym:
  387. period = periodicity(expo, symbol)
  388. else:
  389. period = _periodicity(f.args, symbol)
  390. elif f.is_Mul:
  391. coeff, g = f.as_independent(symbol, as_Add=False)
  392. if isinstance(g, TrigonometricFunction) or coeff is not S.One:
  393. period = periodicity(g, symbol)
  394. else:
  395. period = _periodicity(g.args, symbol)
  396. elif f.is_Add:
  397. k, g = f.as_independent(symbol)
  398. if k is not S.Zero:
  399. return periodicity(g, symbol)
  400. period = _periodicity(g.args, symbol)
  401. elif isinstance(f, Mod):
  402. a, n = f.args
  403. if a == symbol:
  404. period = n
  405. elif isinstance(a, TrigonometricFunction):
  406. period = periodicity(a, symbol)
  407. #check if 'f' is linear in 'symbol'
  408. elif (a.is_polynomial(symbol) and degree(a, symbol) == 1 and
  409. symbol not in n.free_symbols):
  410. period = Abs(n / a.diff(symbol))
  411. elif isinstance(f, Piecewise):
  412. pass # not handling Piecewise yet as the return type is not favorable
  413. elif period is None:
  414. from sympy.solvers.decompogen import compogen, decompogen
  415. g_s = decompogen(f, symbol)
  416. num_of_gs = len(g_s)
  417. if num_of_gs > 1:
  418. for index, g in enumerate(reversed(g_s)):
  419. start_index = num_of_gs - 1 - index
  420. g = compogen(g_s[start_index:], symbol)
  421. if g not in (orig_f, f): # Fix for issue 12620
  422. period = periodicity(g, symbol)
  423. if period is not None:
  424. break
  425. if period is not None:
  426. if check:
  427. return _check(orig_f, period)
  428. return period
  429. return None
  430. def _periodicity(args, symbol):
  431. """
  432. Helper for `periodicity` to find the period of a list of simpler
  433. functions.
  434. It uses the `lcim` method to find the least common period of
  435. all the functions.
  436. Parameters
  437. ==========
  438. args : Tuple of :py:class:`~.Symbol`
  439. All the symbols present in a function.
  440. symbol : :py:class:`~.Symbol`
  441. The symbol over which the function is to be evaluated.
  442. Returns
  443. =======
  444. period
  445. The least common period of the function for all the symbols
  446. of the function.
  447. ``None`` if for at least one of the symbols the function is aperiodic.
  448. """
  449. periods = []
  450. for f in args:
  451. period = periodicity(f, symbol)
  452. if period is None:
  453. return None
  454. if period is not S.Zero:
  455. periods.append(period)
  456. if len(periods) > 1:
  457. return lcim(periods)
  458. if periods:
  459. return periods[0]
  460. def lcim(numbers):
  461. """Returns the least common integral multiple of a list of numbers.
  462. The numbers can be rational or irrational or a mixture of both.
  463. `None` is returned for incommensurable numbers.
  464. Parameters
  465. ==========
  466. numbers : list
  467. Numbers (rational and/or irrational) for which lcim is to be found.
  468. Returns
  469. =======
  470. number
  471. lcim if it exists, otherwise ``None`` for incommensurable numbers.
  472. Examples
  473. ========
  474. >>> from sympy.calculus.util import lcim
  475. >>> from sympy import S, pi
  476. >>> lcim([S(1)/2, S(3)/4, S(5)/6])
  477. 15/2
  478. >>> lcim([2*pi, 3*pi, pi, pi/2])
  479. 6*pi
  480. >>> lcim([S(1), 2*pi])
  481. """
  482. result = None
  483. if all(num.is_irrational for num in numbers):
  484. factorized_nums = list(map(lambda num: num.factor(), numbers))
  485. factors_num = list(
  486. map(lambda num: num.as_coeff_Mul(),
  487. factorized_nums))
  488. term = factors_num[0][1]
  489. if all(factor == term for coeff, factor in factors_num):
  490. common_term = term
  491. coeffs = [coeff for coeff, factor in factors_num]
  492. result = lcm_list(coeffs) * common_term
  493. elif all(num.is_rational for num in numbers):
  494. result = lcm_list(numbers)
  495. else:
  496. pass
  497. return result
  498. def is_convex(f, *syms, domain=S.Reals):
  499. r"""Determines the convexity of the function passed in the argument.
  500. Parameters
  501. ==========
  502. f : :py:class:`~.Expr`
  503. The concerned function.
  504. syms : Tuple of :py:class:`~.Symbol`
  505. The variables with respect to which the convexity is to be determined.
  506. domain : :py:class:`~.Interval`, optional
  507. The domain over which the convexity of the function has to be checked.
  508. If unspecified, S.Reals will be the default domain.
  509. Returns
  510. =======
  511. bool
  512. The method returns ``True`` if the function is convex otherwise it
  513. returns ``False``.
  514. Raises
  515. ======
  516. NotImplementedError
  517. The check for the convexity of multivariate functions is not implemented yet.
  518. Notes
  519. =====
  520. To determine concavity of a function pass `-f` as the concerned function.
  521. To determine logarithmic convexity of a function pass `\log(f)` as
  522. concerned function.
  523. To determine logartihmic concavity of a function pass `-\log(f)` as
  524. concerned function.
  525. Currently, convexity check of multivariate functions is not handled.
  526. Examples
  527. ========
  528. >>> from sympy import is_convex, symbols, exp, oo, Interval
  529. >>> x = symbols('x')
  530. >>> is_convex(exp(x), x)
  531. True
  532. >>> is_convex(x**3, x, domain = Interval(-1, oo))
  533. False
  534. References
  535. ==========
  536. .. [1] https://en.wikipedia.org/wiki/Convex_function
  537. .. [2] http://www.ifp.illinois.edu/~angelia/L3_convfunc.pdf
  538. .. [3] https://en.wikipedia.org/wiki/Logarithmically_convex_function
  539. .. [4] https://en.wikipedia.org/wiki/Logarithmically_concave_function
  540. .. [5] https://en.wikipedia.org/wiki/Concave_function
  541. """
  542. if len(syms) > 1:
  543. raise NotImplementedError(
  544. "The check for the convexity of multivariate functions is not implemented yet.")
  545. from sympy.solvers.inequalities import solve_univariate_inequality
  546. f = _sympify(f)
  547. var = syms[0]
  548. condition = f.diff(var, 2) < 0
  549. if solve_univariate_inequality(condition, var, False, domain):
  550. return False
  551. return True
  552. def stationary_points(f, symbol, domain=S.Reals):
  553. """
  554. Returns the stationary points of a function (where derivative of the
  555. function is 0) in the given domain.
  556. Parameters
  557. ==========
  558. f : :py:class:`~.Expr`
  559. The concerned function.
  560. symbol : :py:class:`~.Symbol`
  561. The variable for which the stationary points are to be determined.
  562. domain : :py:class:`~.Interval`
  563. The domain over which the stationary points have to be checked.
  564. If unspecified, ``S.Reals`` will be the default domain.
  565. Returns
  566. =======
  567. Set
  568. A set of stationary points for the function. If there are no
  569. stationary point, an :py:class:`~.EmptySet` is returned.
  570. Examples
  571. ========
  572. >>> from sympy import Interval, Symbol, S, sin, pi, pprint, stationary_points
  573. >>> x = Symbol('x')
  574. >>> stationary_points(1/x, x, S.Reals)
  575. EmptySet
  576. >>> pprint(stationary_points(sin(x), x), use_unicode=False)
  577. pi 3*pi
  578. {2*n*pi + -- | n in Integers} U {2*n*pi + ---- | n in Integers}
  579. 2 2
  580. >>> stationary_points(sin(x),x, Interval(0, 4*pi))
  581. {pi/2, 3*pi/2, 5*pi/2, 7*pi/2}
  582. """
  583. from sympy.solvers.solveset import solveset
  584. if domain is S.EmptySet:
  585. return S.EmptySet
  586. domain = continuous_domain(f, symbol, domain)
  587. set = solveset(diff(f, symbol), symbol, domain)
  588. return set
  589. def maximum(f, symbol, domain=S.Reals):
  590. """
  591. Returns the maximum value of a function in the given domain.
  592. Parameters
  593. ==========
  594. f : :py:class:`~.Expr`
  595. The concerned function.
  596. symbol : :py:class:`~.Symbol`
  597. The variable for maximum value needs to be determined.
  598. domain : :py:class:`~.Interval`
  599. The domain over which the maximum have to be checked.
  600. If unspecified, then the global maximum is returned.
  601. Returns
  602. =======
  603. number
  604. Maximum value of the function in given domain.
  605. Examples
  606. ========
  607. >>> from sympy import Interval, Symbol, S, sin, cos, pi, maximum
  608. >>> x = Symbol('x')
  609. >>> f = -x**2 + 2*x + 5
  610. >>> maximum(f, x, S.Reals)
  611. 6
  612. >>> maximum(sin(x), x, Interval(-pi, pi/4))
  613. sqrt(2)/2
  614. >>> maximum(sin(x)*cos(x), x)
  615. 1/2
  616. """
  617. if isinstance(symbol, Symbol):
  618. if domain is S.EmptySet:
  619. raise ValueError("Maximum value not defined for empty domain.")
  620. return function_range(f, symbol, domain).sup
  621. else:
  622. raise ValueError("%s is not a valid symbol." % symbol)
  623. def minimum(f, symbol, domain=S.Reals):
  624. """
  625. Returns the minimum value of a function in the given domain.
  626. Parameters
  627. ==========
  628. f : :py:class:`~.Expr`
  629. The concerned function.
  630. symbol : :py:class:`~.Symbol`
  631. The variable for minimum value needs to be determined.
  632. domain : :py:class:`~.Interval`
  633. The domain over which the minimum have to be checked.
  634. If unspecified, then the global minimum is returned.
  635. Returns
  636. =======
  637. number
  638. Minimum value of the function in the given domain.
  639. Examples
  640. ========
  641. >>> from sympy import Interval, Symbol, S, sin, cos, minimum
  642. >>> x = Symbol('x')
  643. >>> f = x**2 + 2*x + 5
  644. >>> minimum(f, x, S.Reals)
  645. 4
  646. >>> minimum(sin(x), x, Interval(2, 3))
  647. sin(3)
  648. >>> minimum(sin(x)*cos(x), x)
  649. -1/2
  650. """
  651. if isinstance(symbol, Symbol):
  652. if domain is S.EmptySet:
  653. raise ValueError("Minimum value not defined for empty domain.")
  654. return function_range(f, symbol, domain).inf
  655. else:
  656. raise ValueError("%s is not a valid symbol." % symbol)