util.py 28 KB

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