Spaces:
Sleeping
Sleeping
| from .accumulationbounds import AccumBounds, AccumulationBounds # noqa: F401 | |
| from .singularities import singularities | |
| from sympy.core import Pow, S | |
| from sympy.core.function import diff, expand_mul, Function | |
| from sympy.core.kind import NumberKind | |
| from sympy.core.mod import Mod | |
| from sympy.core.numbers import equal_valued | |
| from sympy.core.relational import Relational | |
| from sympy.core.symbol import Symbol, Dummy | |
| from sympy.core.sympify import _sympify | |
| from sympy.functions.elementary.complexes import Abs, im, re | |
| from sympy.functions.elementary.exponential import exp, log | |
| from sympy.functions.elementary.integers import frac | |
| from sympy.functions.elementary.piecewise import Piecewise | |
| from sympy.functions.elementary.trigonometric import ( | |
| TrigonometricFunction, sin, cos, tan, cot, csc, sec, | |
| asin, acos, acot, atan, asec, acsc) | |
| from sympy.functions.elementary.hyperbolic import (sinh, cosh, tanh, coth, | |
| sech, csch, asinh, acosh, atanh, acoth, asech, acsch) | |
| from sympy.polys.polytools import degree, lcm_list | |
| from sympy.sets.sets import (Interval, Intersection, FiniteSet, Union, | |
| Complement) | |
| from sympy.sets.fancysets import ImageSet | |
| from sympy.sets.conditionset import ConditionSet | |
| from sympy.utilities import filldedent | |
| from sympy.utilities.iterables import iterable | |
| from sympy.matrices.dense import hessian | |
| def continuous_domain(f, symbol, domain): | |
| """ | |
| Returns the domain on which the function expression f is continuous. | |
| This function is limited by the ability to determine the various | |
| singularities and discontinuities of the given function. | |
| The result is either given as a union of intervals or constructed using | |
| other set operations. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| symbol : :py:class:`~.Symbol` | |
| The variable for which the intervals are to be determined. | |
| domain : :py:class:`~.Interval` | |
| The domain over which the continuity of the symbol has to be checked. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, Symbol, S, tan, log, pi, sqrt | |
| >>> from sympy.calculus.util import continuous_domain | |
| >>> x = Symbol('x') | |
| >>> continuous_domain(1/x, x, S.Reals) | |
| Union(Interval.open(-oo, 0), Interval.open(0, oo)) | |
| >>> continuous_domain(tan(x), x, Interval(0, pi)) | |
| Union(Interval.Ropen(0, pi/2), Interval.Lopen(pi/2, pi)) | |
| >>> continuous_domain(sqrt(x - 2), x, Interval(-5, 5)) | |
| Interval(2, 5) | |
| >>> continuous_domain(log(2*x - 1), x, S.Reals) | |
| Interval.open(1/2, oo) | |
| Returns | |
| ======= | |
| :py:class:`~.Interval` | |
| Union of all intervals where the function is continuous. | |
| Raises | |
| ====== | |
| NotImplementedError | |
| If the method to determine continuity of such a function | |
| has not yet been developed. | |
| """ | |
| from sympy.solvers.inequalities import solve_univariate_inequality | |
| if not domain.is_subset(S.Reals): | |
| raise NotImplementedError(filldedent(''' | |
| Domain must be a subset of S.Reals. | |
| ''')) | |
| implemented = [Pow, exp, log, Abs, frac, | |
| sin, cos, tan, cot, sec, csc, | |
| asin, acos, atan, acot, asec, acsc, | |
| sinh, cosh, tanh, coth, sech, csch, | |
| asinh, acosh, atanh, acoth, asech, acsch] | |
| used = [fct.func for fct in f.atoms(Function) if fct.has(symbol)] | |
| if any(func not in implemented for func in used): | |
| raise NotImplementedError(filldedent(''' | |
| Unable to determine the domain of the given function. | |
| ''')) | |
| x = Symbol('x') | |
| constraints = { | |
| log: (x > 0,), | |
| asin: (x >= -1, x <= 1), | |
| acos: (x >= -1, x <= 1), | |
| acosh: (x >= 1,), | |
| atanh: (x > -1, x < 1), | |
| asech: (x > 0, x <= 1) | |
| } | |
| constraints_union = { | |
| asec: (x <= -1, x >= 1), | |
| acsc: (x <= -1, x >= 1), | |
| acoth: (x < -1, x > 1) | |
| } | |
| cont_domain = domain | |
| for atom in f.atoms(Pow): | |
| den = atom.exp.as_numer_denom()[1] | |
| if atom.exp.is_rational and den.is_odd: | |
| pass # 0**negative handled by singularities() | |
| else: | |
| constraint = solve_univariate_inequality(atom.base >= 0, | |
| symbol).as_set() | |
| cont_domain = Intersection(constraint, cont_domain) | |
| for atom in f.atoms(Function): | |
| if atom.func in constraints: | |
| for c in constraints[atom.func]: | |
| constraint_relational = c.subs(x, atom.args[0]) | |
| constraint_set = solve_univariate_inequality( | |
| constraint_relational, symbol).as_set() | |
| cont_domain = Intersection(constraint_set, cont_domain) | |
| elif atom.func in constraints_union: | |
| constraint_set = S.EmptySet | |
| for c in constraints_union[atom.func]: | |
| constraint_relational = c.subs(x, atom.args[0]) | |
| constraint_set += solve_univariate_inequality( | |
| constraint_relational, symbol).as_set() | |
| cont_domain = Intersection(constraint_set, cont_domain) | |
| # XXX: the discontinuities below could be factored out in | |
| # a new "discontinuities()". | |
| elif atom.func == acot: | |
| from sympy.solvers.solveset import solveset_real | |
| # Sympy's acot() has a step discontinuity at 0. Since it's | |
| # neither an essential singularity nor a pole, singularities() | |
| # will not report it. But it's still relevant for determining | |
| # the continuity of the function f. | |
| cont_domain -= solveset_real(atom.args[0], symbol) | |
| # Note that the above may introduce spurious discontinuities, e.g. | |
| # for abs(acot(x)) at 0. | |
| elif atom.func == frac: | |
| from sympy.solvers.solveset import solveset_real | |
| r = function_range(atom.args[0], symbol, domain) | |
| r = Intersection(r, S.Integers) | |
| if r.is_finite_set: | |
| discont = S.EmptySet | |
| for n in r: | |
| discont += solveset_real(atom.args[0]-n, symbol) | |
| else: | |
| discont = ConditionSet( | |
| symbol, S.Integers.contains(atom.args[0]), cont_domain) | |
| cont_domain -= discont | |
| return cont_domain - singularities(f, symbol, domain) | |
| def function_range(f, symbol, domain): | |
| """ | |
| Finds the range of a function in a given domain. | |
| This method is limited by the ability to determine the singularities and | |
| determine limits. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| symbol : :py:class:`~.Symbol` | |
| The variable for which the range of function is to be determined. | |
| domain : :py:class:`~.Interval` | |
| The domain under which the range of the function has to be found. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, Symbol, S, exp, log, pi, sqrt, sin, tan | |
| >>> from sympy.calculus.util import function_range | |
| >>> x = Symbol('x') | |
| >>> function_range(sin(x), x, Interval(0, 2*pi)) | |
| Interval(-1, 1) | |
| >>> function_range(tan(x), x, Interval(-pi/2, pi/2)) | |
| Interval(-oo, oo) | |
| >>> function_range(1/x, x, S.Reals) | |
| Union(Interval.open(-oo, 0), Interval.open(0, oo)) | |
| >>> function_range(exp(x), x, S.Reals) | |
| Interval.open(0, oo) | |
| >>> function_range(log(x), x, S.Reals) | |
| Interval(-oo, oo) | |
| >>> function_range(sqrt(x), x, Interval(-5, 9)) | |
| Interval(0, 3) | |
| Returns | |
| ======= | |
| :py:class:`~.Interval` | |
| Union of all ranges for all intervals under domain where function is | |
| continuous. | |
| Raises | |
| ====== | |
| NotImplementedError | |
| If any of the intervals, in the given domain, for which function | |
| is continuous are not finite or real, | |
| OR if the critical points of the function on the domain cannot be found. | |
| """ | |
| if domain is S.EmptySet: | |
| return S.EmptySet | |
| period = periodicity(f, symbol) | |
| if period == S.Zero: | |
| # the expression is constant wrt symbol | |
| return FiniteSet(f.expand()) | |
| from sympy.series.limits import limit | |
| from sympy.solvers.solveset import solveset | |
| if period is not None: | |
| if isinstance(domain, Interval): | |
| if (domain.inf - domain.sup).is_infinite: | |
| domain = Interval(0, period) | |
| elif isinstance(domain, Union): | |
| for sub_dom in domain.args: | |
| if isinstance(sub_dom, Interval) and \ | |
| ((sub_dom.inf - sub_dom.sup).is_infinite): | |
| domain = Interval(0, period) | |
| intervals = continuous_domain(f, symbol, domain) | |
| range_int = S.EmptySet | |
| if isinstance(intervals,(Interval, FiniteSet)): | |
| interval_iter = (intervals,) | |
| elif isinstance(intervals, Union): | |
| interval_iter = intervals.args | |
| else: | |
| raise NotImplementedError(filldedent(''' | |
| Unable to find range for the given domain. | |
| ''')) | |
| for interval in interval_iter: | |
| if isinstance(interval, FiniteSet): | |
| for singleton in interval: | |
| if singleton in domain: | |
| range_int += FiniteSet(f.subs(symbol, singleton)) | |
| elif isinstance(interval, Interval): | |
| vals = S.EmptySet | |
| critical_points = S.EmptySet | |
| critical_values = S.EmptySet | |
| bounds = ((interval.left_open, interval.inf, '+'), | |
| (interval.right_open, interval.sup, '-')) | |
| for is_open, limit_point, direction in bounds: | |
| if is_open: | |
| critical_values += FiniteSet(limit(f, symbol, limit_point, direction)) | |
| vals += critical_values | |
| else: | |
| vals += FiniteSet(f.subs(symbol, limit_point)) | |
| solution = solveset(f.diff(symbol), symbol, interval) | |
| if not iterable(solution): | |
| raise NotImplementedError( | |
| 'Unable to find critical points for {}'.format(f)) | |
| if isinstance(solution, ImageSet): | |
| raise NotImplementedError( | |
| 'Infinite number of critical points for {}'.format(f)) | |
| critical_points += solution | |
| for critical_point in critical_points: | |
| vals += FiniteSet(f.subs(symbol, critical_point)) | |
| left_open, right_open = False, False | |
| if critical_values is not S.EmptySet: | |
| if critical_values.inf == vals.inf: | |
| left_open = True | |
| if critical_values.sup == vals.sup: | |
| right_open = True | |
| range_int += Interval(vals.inf, vals.sup, left_open, right_open) | |
| else: | |
| raise NotImplementedError(filldedent(''' | |
| Unable to find range for the given domain. | |
| ''')) | |
| return range_int | |
| def not_empty_in(finset_intersection, *syms): | |
| """ | |
| Finds the domain of the functions in ``finset_intersection`` in which the | |
| ``finite_set`` is not-empty. | |
| Parameters | |
| ========== | |
| finset_intersection : Intersection of FiniteSet | |
| The unevaluated intersection of FiniteSet containing | |
| real-valued functions with Union of Sets | |
| syms : Tuple of symbols | |
| Symbol for which domain is to be found | |
| Raises | |
| ====== | |
| NotImplementedError | |
| The algorithms to find the non-emptiness of the given FiniteSet are | |
| not yet implemented. | |
| ValueError | |
| The input is not valid. | |
| RuntimeError | |
| It is a bug, please report it to the github issue tracker | |
| (https://github.com/sympy/sympy/issues). | |
| Examples | |
| ======== | |
| >>> from sympy import FiniteSet, Interval, not_empty_in, oo | |
| >>> from sympy.abc import x | |
| >>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x) | |
| Interval(0, 2) | |
| >>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x) | |
| Union(Interval(1, 2), Interval(-sqrt(2), -1)) | |
| >>> not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x) | |
| Union(Interval.Lopen(-2, -1), Interval(2, oo)) | |
| """ | |
| # TODO: handle piecewise defined functions | |
| # TODO: handle transcendental functions | |
| # TODO: handle multivariate functions | |
| if len(syms) == 0: | |
| raise ValueError("One or more symbols must be given in syms.") | |
| if finset_intersection is S.EmptySet: | |
| return S.EmptySet | |
| if isinstance(finset_intersection, Union): | |
| elm_in_sets = finset_intersection.args[0] | |
| return Union(not_empty_in(finset_intersection.args[1], *syms), | |
| elm_in_sets) | |
| if isinstance(finset_intersection, FiniteSet): | |
| finite_set = finset_intersection | |
| _sets = S.Reals | |
| else: | |
| finite_set = finset_intersection.args[1] | |
| _sets = finset_intersection.args[0] | |
| if not isinstance(finite_set, FiniteSet): | |
| raise ValueError('A FiniteSet must be given, not %s: %s' % | |
| (type(finite_set), finite_set)) | |
| if len(syms) == 1: | |
| symb = syms[0] | |
| else: | |
| raise NotImplementedError('more than one variables %s not handled' % | |
| (syms,)) | |
| def elm_domain(expr, intrvl): | |
| """ Finds the domain of an expression in any given interval """ | |
| from sympy.solvers.solveset import solveset | |
| _start = intrvl.start | |
| _end = intrvl.end | |
| _singularities = solveset(expr.as_numer_denom()[1], symb, | |
| domain=S.Reals) | |
| if intrvl.right_open: | |
| if _end is S.Infinity: | |
| _domain1 = S.Reals | |
| else: | |
| _domain1 = solveset(expr < _end, symb, domain=S.Reals) | |
| else: | |
| _domain1 = solveset(expr <= _end, symb, domain=S.Reals) | |
| if intrvl.left_open: | |
| if _start is S.NegativeInfinity: | |
| _domain2 = S.Reals | |
| else: | |
| _domain2 = solveset(expr > _start, symb, domain=S.Reals) | |
| else: | |
| _domain2 = solveset(expr >= _start, symb, domain=S.Reals) | |
| # domain in the interval | |
| expr_with_sing = Intersection(_domain1, _domain2) | |
| expr_domain = Complement(expr_with_sing, _singularities) | |
| return expr_domain | |
| if isinstance(_sets, Interval): | |
| return Union(*[elm_domain(element, _sets) for element in finite_set]) | |
| if isinstance(_sets, Union): | |
| _domain = S.EmptySet | |
| for intrvl in _sets.args: | |
| _domain_element = Union(*[elm_domain(element, intrvl) | |
| for element in finite_set]) | |
| _domain = Union(_domain, _domain_element) | |
| return _domain | |
| def periodicity(f, symbol, check=False): | |
| """ | |
| Tests the given function for periodicity in the given symbol. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| symbol : :py:class:`~.Symbol` | |
| The variable for which the period is to be determined. | |
| check : bool, optional | |
| The flag to verify whether the value being returned is a period or not. | |
| Returns | |
| ======= | |
| period | |
| The period of the function is returned. | |
| ``None`` is returned when the function is aperiodic or has a complex period. | |
| The value of $0$ is returned as the period of a constant function. | |
| Raises | |
| ====== | |
| NotImplementedError | |
| The value of the period computed cannot be verified. | |
| Notes | |
| ===== | |
| Currently, we do not support functions with a complex period. | |
| The period of functions having complex periodic values such | |
| as ``exp``, ``sinh`` is evaluated to ``None``. | |
| The value returned might not be the "fundamental" period of the given | |
| function i.e. it may not be the smallest periodic value of the function. | |
| The verification of the period through the ``check`` flag is not reliable | |
| due to internal simplification of the given expression. Hence, it is set | |
| to ``False`` by default. | |
| Examples | |
| ======== | |
| >>> from sympy import periodicity, Symbol, sin, cos, tan, exp | |
| >>> x = Symbol('x') | |
| >>> f = sin(x) + sin(2*x) + sin(3*x) | |
| >>> periodicity(f, x) | |
| 2*pi | |
| >>> periodicity(sin(x)*cos(x), x) | |
| pi | |
| >>> periodicity(exp(tan(2*x) - 1), x) | |
| pi/2 | |
| >>> periodicity(sin(4*x)**cos(2*x), x) | |
| pi | |
| >>> periodicity(exp(x), x) | |
| """ | |
| if symbol.kind is not NumberKind: | |
| raise NotImplementedError("Cannot use symbol of kind %s" % symbol.kind) | |
| temp = Dummy('x', real=True) | |
| f = f.subs(symbol, temp) | |
| symbol = temp | |
| def _check(orig_f, period): | |
| '''Return the checked period or raise an error.''' | |
| new_f = orig_f.subs(symbol, symbol + period) | |
| if new_f.equals(orig_f): | |
| return period | |
| else: | |
| raise NotImplementedError(filldedent(''' | |
| The period of the given function cannot be verified. | |
| When `%s` was replaced with `%s + %s` in `%s`, the result | |
| was `%s` which was not recognized as being the same as | |
| the original function. | |
| So either the period was wrong or the two forms were | |
| not recognized as being equal. | |
| Set check=False to obtain the value.''' % | |
| (symbol, symbol, period, orig_f, new_f))) | |
| orig_f = f | |
| period = None | |
| if isinstance(f, Relational): | |
| f = f.lhs - f.rhs | |
| f = f.simplify() | |
| if symbol not in f.free_symbols: | |
| return S.Zero | |
| if isinstance(f, TrigonometricFunction): | |
| try: | |
| period = f.period(symbol) | |
| except NotImplementedError: | |
| pass | |
| if isinstance(f, Abs): | |
| arg = f.args[0] | |
| if isinstance(arg, (sec, csc, cos)): | |
| # all but tan and cot might have a | |
| # a period that is half as large | |
| # so recast as sin | |
| arg = sin(arg.args[0]) | |
| period = periodicity(arg, symbol) | |
| if period is not None and isinstance(arg, sin): | |
| # the argument of Abs was a trigonometric other than | |
| # cot or tan; test to see if the half-period | |
| # is valid. Abs(arg) has behaviour equivalent to | |
| # orig_f, so use that for test: | |
| orig_f = Abs(arg) | |
| try: | |
| return _check(orig_f, period/2) | |
| except NotImplementedError as err: | |
| if check: | |
| raise NotImplementedError(err) | |
| # else let new orig_f and period be | |
| # checked below | |
| if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1): | |
| f = Pow(S.Exp1, expand_mul(f.exp)) | |
| if im(f) != 0: | |
| period_real = periodicity(re(f), symbol) | |
| period_imag = periodicity(im(f), symbol) | |
| if period_real is not None and period_imag is not None: | |
| period = lcim([period_real, period_imag]) | |
| if f.is_Pow and f.base != S.Exp1: | |
| base, expo = f.args | |
| base_has_sym = base.has(symbol) | |
| expo_has_sym = expo.has(symbol) | |
| if base_has_sym and not expo_has_sym: | |
| period = periodicity(base, symbol) | |
| elif expo_has_sym and not base_has_sym: | |
| period = periodicity(expo, symbol) | |
| else: | |
| period = _periodicity(f.args, symbol) | |
| elif f.is_Mul: | |
| coeff, g = f.as_independent(symbol, as_Add=False) | |
| if isinstance(g, TrigonometricFunction) or not equal_valued(coeff, 1): | |
| period = periodicity(g, symbol) | |
| else: | |
| period = _periodicity(g.args, symbol) | |
| elif f.is_Add: | |
| k, g = f.as_independent(symbol) | |
| if k is not S.Zero: | |
| return periodicity(g, symbol) | |
| period = _periodicity(g.args, symbol) | |
| elif isinstance(f, Mod): | |
| a, n = f.args | |
| if a == symbol: | |
| period = n | |
| elif isinstance(a, TrigonometricFunction): | |
| period = periodicity(a, symbol) | |
| #check if 'f' is linear in 'symbol' | |
| elif (a.is_polynomial(symbol) and degree(a, symbol) == 1 and | |
| symbol not in n.free_symbols): | |
| period = Abs(n / a.diff(symbol)) | |
| elif isinstance(f, Piecewise): | |
| pass # not handling Piecewise yet as the return type is not favorable | |
| elif period is None: | |
| from sympy.solvers.decompogen import compogen, decompogen | |
| g_s = decompogen(f, symbol) | |
| num_of_gs = len(g_s) | |
| if num_of_gs > 1: | |
| for index, g in enumerate(reversed(g_s)): | |
| start_index = num_of_gs - 1 - index | |
| g = compogen(g_s[start_index:], symbol) | |
| if g not in (orig_f, f): # Fix for issue 12620 | |
| period = periodicity(g, symbol) | |
| if period is not None: | |
| break | |
| if period is not None: | |
| if check: | |
| return _check(orig_f, period) | |
| return period | |
| return None | |
| def _periodicity(args, symbol): | |
| """ | |
| Helper for `periodicity` to find the period of a list of simpler | |
| functions. | |
| It uses the `lcim` method to find the least common period of | |
| all the functions. | |
| Parameters | |
| ========== | |
| args : Tuple of :py:class:`~.Symbol` | |
| All the symbols present in a function. | |
| symbol : :py:class:`~.Symbol` | |
| The symbol over which the function is to be evaluated. | |
| Returns | |
| ======= | |
| period | |
| The least common period of the function for all the symbols | |
| of the function. | |
| ``None`` if for at least one of the symbols the function is aperiodic. | |
| """ | |
| periods = [] | |
| for f in args: | |
| period = periodicity(f, symbol) | |
| if period is None: | |
| return None | |
| if period is not S.Zero: | |
| periods.append(period) | |
| if len(periods) > 1: | |
| return lcim(periods) | |
| if periods: | |
| return periods[0] | |
| def lcim(numbers): | |
| """Returns the least common integral multiple of a list of numbers. | |
| The numbers can be rational or irrational or a mixture of both. | |
| `None` is returned for incommensurable numbers. | |
| Parameters | |
| ========== | |
| numbers : list | |
| Numbers (rational and/or irrational) for which lcim is to be found. | |
| Returns | |
| ======= | |
| number | |
| lcim if it exists, otherwise ``None`` for incommensurable numbers. | |
| Examples | |
| ======== | |
| >>> from sympy.calculus.util import lcim | |
| >>> from sympy import S, pi | |
| >>> lcim([S(1)/2, S(3)/4, S(5)/6]) | |
| 15/2 | |
| >>> lcim([2*pi, 3*pi, pi, pi/2]) | |
| 6*pi | |
| >>> lcim([S(1), 2*pi]) | |
| """ | |
| result = None | |
| if all(num.is_irrational for num in numbers): | |
| factorized_nums = [num.factor() for num in numbers] | |
| factors_num = [num.as_coeff_Mul() for num in factorized_nums] | |
| term = factors_num[0][1] | |
| if all(factor == term for coeff, factor in factors_num): | |
| common_term = term | |
| coeffs = [coeff for coeff, factor in factors_num] | |
| result = lcm_list(coeffs) * common_term | |
| elif all(num.is_rational for num in numbers): | |
| result = lcm_list(numbers) | |
| else: | |
| pass | |
| return result | |
| def is_convex(f, *syms, domain=S.Reals): | |
| r"""Determines the convexity of the function passed in the argument. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| syms : Tuple of :py:class:`~.Symbol` | |
| The variables with respect to which the convexity is to be determined. | |
| domain : :py:class:`~.Interval`, optional | |
| The domain over which the convexity of the function has to be checked. | |
| If unspecified, S.Reals will be the default domain. | |
| Returns | |
| ======= | |
| bool | |
| The method returns ``True`` if the function is convex otherwise it | |
| returns ``False``. | |
| Raises | |
| ====== | |
| NotImplementedError | |
| The check for the convexity of multivariate functions is not implemented yet. | |
| Notes | |
| ===== | |
| To determine concavity of a function pass `-f` as the concerned function. | |
| To determine logarithmic convexity of a function pass `\log(f)` as | |
| concerned function. | |
| To determine logarithmic concavity of a function pass `-\log(f)` as | |
| concerned function. | |
| Currently, convexity check of multivariate functions is not handled. | |
| Examples | |
| ======== | |
| >>> from sympy import is_convex, symbols, exp, oo, Interval | |
| >>> x = symbols('x') | |
| >>> is_convex(exp(x), x) | |
| True | |
| >>> is_convex(x**3, x, domain = Interval(-1, oo)) | |
| False | |
| >>> is_convex(1/x**2, x, domain=Interval.open(0, oo)) | |
| True | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Convex_function | |
| .. [2] http://www.ifp.illinois.edu/~angelia/L3_convfunc.pdf | |
| .. [3] https://en.wikipedia.org/wiki/Logarithmically_convex_function | |
| .. [4] https://en.wikipedia.org/wiki/Logarithmically_concave_function | |
| .. [5] https://en.wikipedia.org/wiki/Concave_function | |
| """ | |
| if len(syms) > 1 : | |
| return hessian(f, syms).is_positive_semidefinite | |
| from sympy.solvers.inequalities import solve_univariate_inequality | |
| f = _sympify(f) | |
| var = syms[0] | |
| if any(s in domain for s in singularities(f, var)): | |
| return False | |
| condition = f.diff(var, 2) < 0 | |
| if solve_univariate_inequality(condition, var, False, domain): | |
| return False | |
| return True | |
| def stationary_points(f, symbol, domain=S.Reals): | |
| """ | |
| Returns the stationary points of a function (where derivative of the | |
| function is 0) in the given domain. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| symbol : :py:class:`~.Symbol` | |
| The variable for which the stationary points are to be determined. | |
| domain : :py:class:`~.Interval` | |
| The domain over which the stationary points have to be checked. | |
| If unspecified, ``S.Reals`` will be the default domain. | |
| Returns | |
| ======= | |
| Set | |
| A set of stationary points for the function. If there are no | |
| stationary point, an :py:class:`~.EmptySet` is returned. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, Symbol, S, sin, pi, pprint, stationary_points | |
| >>> x = Symbol('x') | |
| >>> stationary_points(1/x, x, S.Reals) | |
| EmptySet | |
| >>> pprint(stationary_points(sin(x), x), use_unicode=False) | |
| pi 3*pi | |
| {2*n*pi + -- | n in Integers} U {2*n*pi + ---- | n in Integers} | |
| 2 2 | |
| >>> stationary_points(sin(x),x, Interval(0, 4*pi)) | |
| {pi/2, 3*pi/2, 5*pi/2, 7*pi/2} | |
| """ | |
| from sympy.solvers.solveset import solveset | |
| if domain is S.EmptySet: | |
| return S.EmptySet | |
| domain = continuous_domain(f, symbol, domain) | |
| set = solveset(diff(f, symbol), symbol, domain) | |
| return set | |
| def maximum(f, symbol, domain=S.Reals): | |
| """ | |
| Returns the maximum value of a function in the given domain. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| symbol : :py:class:`~.Symbol` | |
| The variable for maximum value needs to be determined. | |
| domain : :py:class:`~.Interval` | |
| The domain over which the maximum have to be checked. | |
| If unspecified, then the global maximum is returned. | |
| Returns | |
| ======= | |
| number | |
| Maximum value of the function in given domain. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, Symbol, S, sin, cos, pi, maximum | |
| >>> x = Symbol('x') | |
| >>> f = -x**2 + 2*x + 5 | |
| >>> maximum(f, x, S.Reals) | |
| 6 | |
| >>> maximum(sin(x), x, Interval(-pi, pi/4)) | |
| sqrt(2)/2 | |
| >>> maximum(sin(x)*cos(x), x) | |
| 1/2 | |
| """ | |
| if isinstance(symbol, Symbol): | |
| if domain is S.EmptySet: | |
| raise ValueError("Maximum value not defined for empty domain.") | |
| return function_range(f, symbol, domain).sup | |
| else: | |
| raise ValueError("%s is not a valid symbol." % symbol) | |
| def minimum(f, symbol, domain=S.Reals): | |
| """ | |
| Returns the minimum value of a function in the given domain. | |
| Parameters | |
| ========== | |
| f : :py:class:`~.Expr` | |
| The concerned function. | |
| symbol : :py:class:`~.Symbol` | |
| The variable for minimum value needs to be determined. | |
| domain : :py:class:`~.Interval` | |
| The domain over which the minimum have to be checked. | |
| If unspecified, then the global minimum is returned. | |
| Returns | |
| ======= | |
| number | |
| Minimum value of the function in the given domain. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, Symbol, S, sin, cos, minimum | |
| >>> x = Symbol('x') | |
| >>> f = x**2 + 2*x + 5 | |
| >>> minimum(f, x, S.Reals) | |
| 4 | |
| >>> minimum(sin(x), x, Interval(2, 3)) | |
| sin(3) | |
| >>> minimum(sin(x)*cos(x), x) | |
| -1/2 | |
| """ | |
| if isinstance(symbol, Symbol): | |
| if domain is S.EmptySet: | |
| raise ValueError("Minimum value not defined for empty domain.") | |
| return function_range(f, symbol, domain).inf | |
| else: | |
| raise ValueError("%s is not a valid symbol." % symbol) | |