Spaces:
Sleeping
Sleeping
File size: 29,048 Bytes
6a86ad5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 |
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)
|