Coverage for /usr/lib/python3/dist-packages/sympy/calculus/util.py: 11%
276 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from .accumulationbounds import AccumBounds, AccumulationBounds # noqa: F401
2from .singularities import singularities
3from sympy.core import Pow, S
4from sympy.core.function import diff, expand_mul
5from sympy.core.kind import NumberKind
6from sympy.core.mod import Mod
7from sympy.core.numbers import equal_valued
8from sympy.core.relational import Relational
9from sympy.core.symbol import Symbol, Dummy
10from sympy.core.sympify import _sympify
11from sympy.functions.elementary.complexes import Abs, im, re
12from sympy.functions.elementary.exponential import exp, log
13from sympy.functions.elementary.piecewise import Piecewise
14from sympy.functions.elementary.trigonometric import (
15 TrigonometricFunction, sin, cos, csc, sec)
16from sympy.polys.polytools import degree, lcm_list
17from sympy.sets.sets import (Interval, Intersection, FiniteSet, Union,
18 Complement)
19from sympy.sets.fancysets import ImageSet
20from sympy.utilities import filldedent
21from sympy.utilities.iterables import iterable
24def continuous_domain(f, symbol, domain):
25 """
26 Returns the intervals in the given domain for which the function
27 is continuous.
28 This method is limited by the ability to determine the various
29 singularities and discontinuities of the given function.
31 Parameters
32 ==========
34 f : :py:class:`~.Expr`
35 The concerned function.
36 symbol : :py:class:`~.Symbol`
37 The variable for which the intervals are to be determined.
38 domain : :py:class:`~.Interval`
39 The domain over which the continuity of the symbol has to be checked.
41 Examples
42 ========
44 >>> from sympy import Interval, Symbol, S, tan, log, pi, sqrt
45 >>> from sympy.calculus.util import continuous_domain
46 >>> x = Symbol('x')
47 >>> continuous_domain(1/x, x, S.Reals)
48 Union(Interval.open(-oo, 0), Interval.open(0, oo))
49 >>> continuous_domain(tan(x), x, Interval(0, pi))
50 Union(Interval.Ropen(0, pi/2), Interval.Lopen(pi/2, pi))
51 >>> continuous_domain(sqrt(x - 2), x, Interval(-5, 5))
52 Interval(2, 5)
53 >>> continuous_domain(log(2*x - 1), x, S.Reals)
54 Interval.open(1/2, oo)
56 Returns
57 =======
59 :py:class:`~.Interval`
60 Union of all intervals where the function is continuous.
62 Raises
63 ======
65 NotImplementedError
66 If the method to determine continuity of such a function
67 has not yet been developed.
69 """
70 from sympy.solvers.inequalities import solve_univariate_inequality
72 if domain.is_subset(S.Reals):
73 constrained_interval = domain
74 for atom in f.atoms(Pow):
75 den = atom.exp.as_numer_denom()[1]
76 if den.is_even and den.is_nonzero:
77 constraint = solve_univariate_inequality(atom.base >= 0,
78 symbol).as_set()
79 constrained_interval = Intersection(constraint,
80 constrained_interval)
82 for atom in f.atoms(log):
83 constraint = solve_univariate_inequality(atom.args[0] > 0,
84 symbol).as_set()
85 constrained_interval = Intersection(constraint,
86 constrained_interval)
89 return constrained_interval - singularities(f, symbol, domain)
92def function_range(f, symbol, domain):
93 """
94 Finds the range of a function in a given domain.
95 This method is limited by the ability to determine the singularities and
96 determine limits.
98 Parameters
99 ==========
101 f : :py:class:`~.Expr`
102 The concerned function.
103 symbol : :py:class:`~.Symbol`
104 The variable for which the range of function is to be determined.
105 domain : :py:class:`~.Interval`
106 The domain under which the range of the function has to be found.
108 Examples
109 ========
111 >>> from sympy import Interval, Symbol, S, exp, log, pi, sqrt, sin, tan
112 >>> from sympy.calculus.util import function_range
113 >>> x = Symbol('x')
114 >>> function_range(sin(x), x, Interval(0, 2*pi))
115 Interval(-1, 1)
116 >>> function_range(tan(x), x, Interval(-pi/2, pi/2))
117 Interval(-oo, oo)
118 >>> function_range(1/x, x, S.Reals)
119 Union(Interval.open(-oo, 0), Interval.open(0, oo))
120 >>> function_range(exp(x), x, S.Reals)
121 Interval.open(0, oo)
122 >>> function_range(log(x), x, S.Reals)
123 Interval(-oo, oo)
124 >>> function_range(sqrt(x), x, Interval(-5, 9))
125 Interval(0, 3)
127 Returns
128 =======
130 :py:class:`~.Interval`
131 Union of all ranges for all intervals under domain where function is
132 continuous.
134 Raises
135 ======
137 NotImplementedError
138 If any of the intervals, in the given domain, for which function
139 is continuous are not finite or real,
140 OR if the critical points of the function on the domain cannot be found.
141 """
143 if domain is S.EmptySet:
144 return S.EmptySet
146 period = periodicity(f, symbol)
147 if period == S.Zero:
148 # the expression is constant wrt symbol
149 return FiniteSet(f.expand())
151 from sympy.series.limits import limit
152 from sympy.solvers.solveset import solveset
154 if period is not None:
155 if isinstance(domain, Interval):
156 if (domain.inf - domain.sup).is_infinite:
157 domain = Interval(0, period)
158 elif isinstance(domain, Union):
159 for sub_dom in domain.args:
160 if isinstance(sub_dom, Interval) and \
161 ((sub_dom.inf - sub_dom.sup).is_infinite):
162 domain = Interval(0, period)
164 intervals = continuous_domain(f, symbol, domain)
165 range_int = S.EmptySet
166 if isinstance(intervals,(Interval, FiniteSet)):
167 interval_iter = (intervals,)
169 elif isinstance(intervals, Union):
170 interval_iter = intervals.args
172 else:
173 raise NotImplementedError(filldedent('''
174 Unable to find range for the given domain.
175 '''))
177 for interval in interval_iter:
178 if isinstance(interval, FiniteSet):
179 for singleton in interval:
180 if singleton in domain:
181 range_int += FiniteSet(f.subs(symbol, singleton))
182 elif isinstance(interval, Interval):
183 vals = S.EmptySet
184 critical_points = S.EmptySet
185 critical_values = S.EmptySet
186 bounds = ((interval.left_open, interval.inf, '+'),
187 (interval.right_open, interval.sup, '-'))
189 for is_open, limit_point, direction in bounds:
190 if is_open:
191 critical_values += FiniteSet(limit(f, symbol, limit_point, direction))
192 vals += critical_values
194 else:
195 vals += FiniteSet(f.subs(symbol, limit_point))
197 solution = solveset(f.diff(symbol), symbol, interval)
199 if not iterable(solution):
200 raise NotImplementedError(
201 'Unable to find critical points for {}'.format(f))
202 if isinstance(solution, ImageSet):
203 raise NotImplementedError(
204 'Infinite number of critical points for {}'.format(f))
206 critical_points += solution
208 for critical_point in critical_points:
209 vals += FiniteSet(f.subs(symbol, critical_point))
211 left_open, right_open = False, False
213 if critical_values is not S.EmptySet:
214 if critical_values.inf == vals.inf:
215 left_open = True
217 if critical_values.sup == vals.sup:
218 right_open = True
220 range_int += Interval(vals.inf, vals.sup, left_open, right_open)
221 else:
222 raise NotImplementedError(filldedent('''
223 Unable to find range for the given domain.
224 '''))
226 return range_int
229def not_empty_in(finset_intersection, *syms):
230 """
231 Finds the domain of the functions in ``finset_intersection`` in which the
232 ``finite_set`` is not-empty.
234 Parameters
235 ==========
237 finset_intersection : Intersection of FiniteSet
238 The unevaluated intersection of FiniteSet containing
239 real-valued functions with Union of Sets
240 syms : Tuple of symbols
241 Symbol for which domain is to be found
243 Raises
244 ======
246 NotImplementedError
247 The algorithms to find the non-emptiness of the given FiniteSet are
248 not yet implemented.
249 ValueError
250 The input is not valid.
251 RuntimeError
252 It is a bug, please report it to the github issue tracker
253 (https://github.com/sympy/sympy/issues).
255 Examples
256 ========
258 >>> from sympy import FiniteSet, Interval, not_empty_in, oo
259 >>> from sympy.abc import x
260 >>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x)
261 Interval(0, 2)
262 >>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x)
263 Union(Interval(1, 2), Interval(-sqrt(2), -1))
264 >>> not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x)
265 Union(Interval.Lopen(-2, -1), Interval(2, oo))
266 """
268 # TODO: handle piecewise defined functions
269 # TODO: handle transcendental functions
270 # TODO: handle multivariate functions
271 if len(syms) == 0:
272 raise ValueError("One or more symbols must be given in syms.")
274 if finset_intersection is S.EmptySet:
275 return S.EmptySet
277 if isinstance(finset_intersection, Union):
278 elm_in_sets = finset_intersection.args[0]
279 return Union(not_empty_in(finset_intersection.args[1], *syms),
280 elm_in_sets)
282 if isinstance(finset_intersection, FiniteSet):
283 finite_set = finset_intersection
284 _sets = S.Reals
285 else:
286 finite_set = finset_intersection.args[1]
287 _sets = finset_intersection.args[0]
289 if not isinstance(finite_set, FiniteSet):
290 raise ValueError('A FiniteSet must be given, not %s: %s' %
291 (type(finite_set), finite_set))
293 if len(syms) == 1:
294 symb = syms[0]
295 else:
296 raise NotImplementedError('more than one variables %s not handled' %
297 (syms,))
299 def elm_domain(expr, intrvl):
300 """ Finds the domain of an expression in any given interval """
301 from sympy.solvers.solveset import solveset
303 _start = intrvl.start
304 _end = intrvl.end
305 _singularities = solveset(expr.as_numer_denom()[1], symb,
306 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)
316 if intrvl.left_open:
317 if _start is S.NegativeInfinity:
318 _domain2 = S.Reals
319 else:
320 _domain2 = solveset(expr > _start, symb, domain=S.Reals)
321 else:
322 _domain2 = solveset(expr >= _start, symb, domain=S.Reals)
324 # domain in the interval
325 expr_with_sing = Intersection(_domain1, _domain2)
326 expr_domain = Complement(expr_with_sing, _singularities)
327 return expr_domain
329 if isinstance(_sets, Interval):
330 return Union(*[elm_domain(element, _sets) for element in finite_set])
332 if isinstance(_sets, Union):
333 _domain = S.EmptySet
334 for intrvl in _sets.args:
335 _domain_element = Union(*[elm_domain(element, intrvl)
336 for element in finite_set])
337 _domain = Union(_domain, _domain_element)
338 return _domain
341def periodicity(f, symbol, check=False):
342 """
343 Tests the given function for periodicity in the given symbol.
345 Parameters
346 ==========
348 f : :py:class:`~.Expr`
349 The concerned function.
350 symbol : :py:class:`~.Symbol`
351 The variable for which the period is to be determined.
352 check : bool, optional
353 The flag to verify whether the value being returned is a period or not.
355 Returns
356 =======
358 period
359 The period of the function is returned.
360 ``None`` is returned when the function is aperiodic or has a complex period.
361 The value of $0$ is returned as the period of a constant function.
363 Raises
364 ======
366 NotImplementedError
367 The value of the period computed cannot be verified.
370 Notes
371 =====
373 Currently, we do not support functions with a complex period.
374 The period of functions having complex periodic values such
375 as ``exp``, ``sinh`` is evaluated to ``None``.
377 The value returned might not be the "fundamental" period of the given
378 function i.e. it may not be the smallest periodic value of the function.
380 The verification of the period through the ``check`` flag is not reliable
381 due to internal simplification of the given expression. Hence, it is set
382 to ``False`` by default.
384 Examples
385 ========
386 >>> from sympy import periodicity, Symbol, sin, cos, tan, exp
387 >>> x = Symbol('x')
388 >>> f = sin(x) + sin(2*x) + sin(3*x)
389 >>> periodicity(f, x)
390 2*pi
391 >>> periodicity(sin(x)*cos(x), x)
392 pi
393 >>> periodicity(exp(tan(2*x) - 1), x)
394 pi/2
395 >>> periodicity(sin(4*x)**cos(2*x), x)
396 pi
397 >>> periodicity(exp(x), x)
398 """
399 if symbol.kind is not NumberKind:
400 raise NotImplementedError("Cannot use symbol of kind %s" % symbol.kind)
401 temp = Dummy('x', real=True)
402 f = f.subs(symbol, temp)
403 symbol = temp
405 def _check(orig_f, period):
406 '''Return the checked period or raise an error.'''
407 new_f = orig_f.subs(symbol, symbol + period)
408 if new_f.equals(orig_f):
409 return period
410 else:
411 raise NotImplementedError(filldedent('''
412 The period of the given function cannot be verified.
413 When `%s` was replaced with `%s + %s` in `%s`, the result
414 was `%s` which was not recognized as being the same as
415 the original function.
416 So either the period was wrong or the two forms were
417 not recognized as being equal.
418 Set check=False to obtain the value.''' %
419 (symbol, symbol, period, orig_f, new_f)))
421 orig_f = f
422 period = None
424 if isinstance(f, Relational):
425 f = f.lhs - f.rhs
427 f = f.simplify()
429 if symbol not in f.free_symbols:
430 return S.Zero
432 if isinstance(f, TrigonometricFunction):
433 try:
434 period = f.period(symbol)
435 except NotImplementedError:
436 pass
438 if isinstance(f, Abs):
439 arg = f.args[0]
440 if isinstance(arg, (sec, csc, cos)):
441 # all but tan and cot might have a
442 # a period that is half as large
443 # so recast as sin
444 arg = sin(arg.args[0])
445 period = periodicity(arg, symbol)
446 if period is not None and isinstance(arg, sin):
447 # the argument of Abs was a trigonometric other than
448 # cot or tan; test to see if the half-period
449 # is valid. Abs(arg) has behaviour equivalent to
450 # orig_f, so use that for test:
451 orig_f = Abs(arg)
452 try:
453 return _check(orig_f, period/2)
454 except NotImplementedError as err:
455 if check:
456 raise NotImplementedError(err)
457 # else let new orig_f and period be
458 # checked below
460 if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1):
461 f = Pow(S.Exp1, expand_mul(f.exp))
462 if im(f) != 0:
463 period_real = periodicity(re(f), symbol)
464 period_imag = periodicity(im(f), symbol)
465 if period_real is not None and period_imag is not None:
466 period = lcim([period_real, period_imag])
468 if f.is_Pow and f.base != S.Exp1:
469 base, expo = f.args
470 base_has_sym = base.has(symbol)
471 expo_has_sym = expo.has(symbol)
473 if base_has_sym and not expo_has_sym:
474 period = periodicity(base, symbol)
476 elif expo_has_sym and not base_has_sym:
477 period = periodicity(expo, symbol)
479 else:
480 period = _periodicity(f.args, symbol)
482 elif f.is_Mul:
483 coeff, g = f.as_independent(symbol, as_Add=False)
484 if isinstance(g, TrigonometricFunction) or not equal_valued(coeff, 1):
485 period = periodicity(g, symbol)
486 else:
487 period = _periodicity(g.args, symbol)
489 elif f.is_Add:
490 k, g = f.as_independent(symbol)
491 if k is not S.Zero:
492 return periodicity(g, symbol)
494 period = _periodicity(g.args, symbol)
496 elif isinstance(f, Mod):
497 a, n = f.args
499 if a == symbol:
500 period = n
501 elif isinstance(a, TrigonometricFunction):
502 period = periodicity(a, symbol)
503 #check if 'f' is linear in 'symbol'
504 elif (a.is_polynomial(symbol) and degree(a, symbol) == 1 and
505 symbol not in n.free_symbols):
506 period = Abs(n / a.diff(symbol))
508 elif isinstance(f, Piecewise):
509 pass # not handling Piecewise yet as the return type is not favorable
511 elif period is None:
512 from sympy.solvers.decompogen import compogen, decompogen
513 g_s = decompogen(f, symbol)
514 num_of_gs = len(g_s)
515 if num_of_gs > 1:
516 for index, g in enumerate(reversed(g_s)):
517 start_index = num_of_gs - 1 - index
518 g = compogen(g_s[start_index:], symbol)
519 if g not in (orig_f, f): # Fix for issue 12620
520 period = periodicity(g, symbol)
521 if period is not None:
522 break
524 if period is not None:
525 if check:
526 return _check(orig_f, period)
527 return period
529 return None
532def _periodicity(args, symbol):
533 """
534 Helper for `periodicity` to find the period of a list of simpler
535 functions.
536 It uses the `lcim` method to find the least common period of
537 all the functions.
539 Parameters
540 ==========
542 args : Tuple of :py:class:`~.Symbol`
543 All the symbols present in a function.
545 symbol : :py:class:`~.Symbol`
546 The symbol over which the function is to be evaluated.
548 Returns
549 =======
551 period
552 The least common period of the function for all the symbols
553 of the function.
554 ``None`` if for at least one of the symbols the function is aperiodic.
556 """
557 periods = []
558 for f in args:
559 period = periodicity(f, symbol)
560 if period is None:
561 return None
563 if period is not S.Zero:
564 periods.append(period)
566 if len(periods) > 1:
567 return lcim(periods)
569 if periods:
570 return periods[0]
573def lcim(numbers):
574 """Returns the least common integral multiple of a list of numbers.
576 The numbers can be rational or irrational or a mixture of both.
577 `None` is returned for incommensurable numbers.
579 Parameters
580 ==========
582 numbers : list
583 Numbers (rational and/or irrational) for which lcim is to be found.
585 Returns
586 =======
588 number
589 lcim if it exists, otherwise ``None`` for incommensurable numbers.
591 Examples
592 ========
594 >>> from sympy.calculus.util import lcim
595 >>> from sympy import S, pi
596 >>> lcim([S(1)/2, S(3)/4, S(5)/6])
597 15/2
598 >>> lcim([2*pi, 3*pi, pi, pi/2])
599 6*pi
600 >>> lcim([S(1), 2*pi])
601 """
602 result = None
603 if all(num.is_irrational for num in numbers):
604 factorized_nums = [num.factor() for num in numbers]
605 factors_num = [num.as_coeff_Mul() for num in factorized_nums]
606 term = factors_num[0][1]
607 if all(factor == term for coeff, factor in factors_num):
608 common_term = term
609 coeffs = [coeff for coeff, factor in factors_num]
610 result = lcm_list(coeffs) * common_term
612 elif all(num.is_rational for num in numbers):
613 result = lcm_list(numbers)
615 else:
616 pass
618 return result
620def is_convex(f, *syms, domain=S.Reals):
621 r"""Determines the convexity of the function passed in the argument.
623 Parameters
624 ==========
626 f : :py:class:`~.Expr`
627 The concerned function.
628 syms : Tuple of :py:class:`~.Symbol`
629 The variables with respect to which the convexity is to be determined.
630 domain : :py:class:`~.Interval`, optional
631 The domain over which the convexity of the function has to be checked.
632 If unspecified, S.Reals will be the default domain.
634 Returns
635 =======
637 bool
638 The method returns ``True`` if the function is convex otherwise it
639 returns ``False``.
641 Raises
642 ======
644 NotImplementedError
645 The check for the convexity of multivariate functions is not implemented yet.
647 Notes
648 =====
650 To determine concavity of a function pass `-f` as the concerned function.
651 To determine logarithmic convexity of a function pass `\log(f)` as
652 concerned function.
653 To determine logarithmic concavity of a function pass `-\log(f)` as
654 concerned function.
656 Currently, convexity check of multivariate functions is not handled.
658 Examples
659 ========
661 >>> from sympy import is_convex, symbols, exp, oo, Interval
662 >>> x = symbols('x')
663 >>> is_convex(exp(x), x)
664 True
665 >>> is_convex(x**3, x, domain = Interval(-1, oo))
666 False
667 >>> is_convex(1/x**2, x, domain=Interval.open(0, oo))
668 True
670 References
671 ==========
673 .. [1] https://en.wikipedia.org/wiki/Convex_function
674 .. [2] http://www.ifp.illinois.edu/~angelia/L3_convfunc.pdf
675 .. [3] https://en.wikipedia.org/wiki/Logarithmically_convex_function
676 .. [4] https://en.wikipedia.org/wiki/Logarithmically_concave_function
677 .. [5] https://en.wikipedia.org/wiki/Concave_function
679 """
681 if len(syms) > 1:
682 raise NotImplementedError(
683 "The check for the convexity of multivariate functions is not implemented yet.")
685 from sympy.solvers.inequalities import solve_univariate_inequality
687 f = _sympify(f)
688 var = syms[0]
689 if any(s in domain for s in singularities(f, var)):
690 return False
692 condition = f.diff(var, 2) < 0
693 if solve_univariate_inequality(condition, var, False, domain):
694 return False
695 return True
698def stationary_points(f, symbol, domain=S.Reals):
699 """
700 Returns the stationary points of a function (where derivative of the
701 function is 0) in the given domain.
703 Parameters
704 ==========
706 f : :py:class:`~.Expr`
707 The concerned function.
708 symbol : :py:class:`~.Symbol`
709 The variable for which the stationary points are to be determined.
710 domain : :py:class:`~.Interval`
711 The domain over which the stationary points have to be checked.
712 If unspecified, ``S.Reals`` will be the default domain.
714 Returns
715 =======
717 Set
718 A set of stationary points for the function. If there are no
719 stationary point, an :py:class:`~.EmptySet` is returned.
721 Examples
722 ========
724 >>> from sympy import Interval, Symbol, S, sin, pi, pprint, stationary_points
725 >>> x = Symbol('x')
727 >>> stationary_points(1/x, x, S.Reals)
728 EmptySet
730 >>> pprint(stationary_points(sin(x), x), use_unicode=False)
731 pi 3*pi
732 {2*n*pi + -- | n in Integers} U {2*n*pi + ---- | n in Integers}
733 2 2
735 >>> stationary_points(sin(x),x, Interval(0, 4*pi))
736 {pi/2, 3*pi/2, 5*pi/2, 7*pi/2}
738 """
739 from sympy.solvers.solveset import solveset
741 if domain is S.EmptySet:
742 return S.EmptySet
744 domain = continuous_domain(f, symbol, domain)
745 set = solveset(diff(f, symbol), symbol, domain)
747 return set
750def maximum(f, symbol, domain=S.Reals):
751 """
752 Returns the maximum value of a function in the given domain.
754 Parameters
755 ==========
757 f : :py:class:`~.Expr`
758 The concerned function.
759 symbol : :py:class:`~.Symbol`
760 The variable for maximum value needs to be determined.
761 domain : :py:class:`~.Interval`
762 The domain over which the maximum have to be checked.
763 If unspecified, then the global maximum is returned.
765 Returns
766 =======
768 number
769 Maximum value of the function in given domain.
771 Examples
772 ========
774 >>> from sympy import Interval, Symbol, S, sin, cos, pi, maximum
775 >>> x = Symbol('x')
777 >>> f = -x**2 + 2*x + 5
778 >>> maximum(f, x, S.Reals)
779 6
781 >>> maximum(sin(x), x, Interval(-pi, pi/4))
782 sqrt(2)/2
784 >>> maximum(sin(x)*cos(x), x)
785 1/2
787 """
788 if isinstance(symbol, Symbol):
789 if domain is S.EmptySet:
790 raise ValueError("Maximum value not defined for empty domain.")
792 return function_range(f, symbol, domain).sup
793 else:
794 raise ValueError("%s is not a valid symbol." % symbol)
797def minimum(f, symbol, domain=S.Reals):
798 """
799 Returns the minimum value of a function in the given domain.
801 Parameters
802 ==========
804 f : :py:class:`~.Expr`
805 The concerned function.
806 symbol : :py:class:`~.Symbol`
807 The variable for minimum value needs to be determined.
808 domain : :py:class:`~.Interval`
809 The domain over which the minimum have to be checked.
810 If unspecified, then the global minimum is returned.
812 Returns
813 =======
815 number
816 Minimum value of the function in the given domain.
818 Examples
819 ========
821 >>> from sympy import Interval, Symbol, S, sin, cos, minimum
822 >>> x = Symbol('x')
824 >>> f = x**2 + 2*x + 5
825 >>> minimum(f, x, S.Reals)
826 4
828 >>> minimum(sin(x), x, Interval(2, 3))
829 sin(3)
831 >>> minimum(sin(x)*cos(x), x)
832 -1/2
834 """
835 if isinstance(symbol, Symbol):
836 if domain is S.EmptySet:
837 raise ValueError("Minimum value not defined for empty domain.")
839 return function_range(f, symbol, domain).inf
840 else:
841 raise ValueError("%s is not a valid symbol." % symbol)