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

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 

22 

23 

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. 

30 

31 Parameters 

32 ========== 

33 

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. 

40 

41 Examples 

42 ======== 

43 

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) 

55 

56 Returns 

57 ======= 

58 

59 :py:class:`~.Interval` 

60 Union of all intervals where the function is continuous. 

61 

62 Raises 

63 ====== 

64 

65 NotImplementedError 

66 If the method to determine continuity of such a function 

67 has not yet been developed. 

68 

69 """ 

70 from sympy.solvers.inequalities import solve_univariate_inequality 

71 

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) 

81 

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) 

87 

88 

89 return constrained_interval - singularities(f, symbol, domain) 

90 

91 

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. 

97 

98 Parameters 

99 ========== 

100 

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. 

107 

108 Examples 

109 ======== 

110 

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) 

126 

127 Returns 

128 ======= 

129 

130 :py:class:`~.Interval` 

131 Union of all ranges for all intervals under domain where function is 

132 continuous. 

133 

134 Raises 

135 ====== 

136 

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 """ 

142 

143 if domain is S.EmptySet: 

144 return S.EmptySet 

145 

146 period = periodicity(f, symbol) 

147 if period == S.Zero: 

148 # the expression is constant wrt symbol 

149 return FiniteSet(f.expand()) 

150 

151 from sympy.series.limits import limit 

152 from sympy.solvers.solveset import solveset 

153 

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) 

163 

164 intervals = continuous_domain(f, symbol, domain) 

165 range_int = S.EmptySet 

166 if isinstance(intervals,(Interval, FiniteSet)): 

167 interval_iter = (intervals,) 

168 

169 elif isinstance(intervals, Union): 

170 interval_iter = intervals.args 

171 

172 else: 

173 raise NotImplementedError(filldedent(''' 

174 Unable to find range for the given domain. 

175 ''')) 

176 

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, '-')) 

188 

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 

193 

194 else: 

195 vals += FiniteSet(f.subs(symbol, limit_point)) 

196 

197 solution = solveset(f.diff(symbol), symbol, interval) 

198 

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)) 

205 

206 critical_points += solution 

207 

208 for critical_point in critical_points: 

209 vals += FiniteSet(f.subs(symbol, critical_point)) 

210 

211 left_open, right_open = False, False 

212 

213 if critical_values is not S.EmptySet: 

214 if critical_values.inf == vals.inf: 

215 left_open = True 

216 

217 if critical_values.sup == vals.sup: 

218 right_open = True 

219 

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 ''')) 

225 

226 return range_int 

227 

228 

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. 

233 

234 Parameters 

235 ========== 

236 

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 

242 

243 Raises 

244 ====== 

245 

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). 

254 

255 Examples 

256 ======== 

257 

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 """ 

267 

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.") 

273 

274 if finset_intersection is S.EmptySet: 

275 return S.EmptySet 

276 

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) 

281 

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] 

288 

289 if not isinstance(finite_set, FiniteSet): 

290 raise ValueError('A FiniteSet must be given, not %s: %s' % 

291 (type(finite_set), finite_set)) 

292 

293 if len(syms) == 1: 

294 symb = syms[0] 

295 else: 

296 raise NotImplementedError('more than one variables %s not handled' % 

297 (syms,)) 

298 

299 def elm_domain(expr, intrvl): 

300 """ Finds the domain of an expression in any given interval """ 

301 from sympy.solvers.solveset import solveset 

302 

303 _start = intrvl.start 

304 _end = intrvl.end 

305 _singularities = solveset(expr.as_numer_denom()[1], symb, 

306 domain=S.Reals) 

307 

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 

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) 

323 

324 # domain in the interval 

325 expr_with_sing = Intersection(_domain1, _domain2) 

326 expr_domain = Complement(expr_with_sing, _singularities) 

327 return expr_domain 

328 

329 if isinstance(_sets, Interval): 

330 return Union(*[elm_domain(element, _sets) for element in finite_set]) 

331 

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 

339 

340 

341def periodicity(f, symbol, check=False): 

342 """ 

343 Tests the given function for periodicity in the given symbol. 

344 

345 Parameters 

346 ========== 

347 

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. 

354 

355 Returns 

356 ======= 

357 

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. 

362 

363 Raises 

364 ====== 

365 

366 NotImplementedError 

367 The value of the period computed cannot be verified. 

368 

369 

370 Notes 

371 ===== 

372 

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``. 

376 

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. 

379 

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. 

383 

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 

404 

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))) 

420 

421 orig_f = f 

422 period = None 

423 

424 if isinstance(f, Relational): 

425 f = f.lhs - f.rhs 

426 

427 f = f.simplify() 

428 

429 if symbol not in f.free_symbols: 

430 return S.Zero 

431 

432 if isinstance(f, TrigonometricFunction): 

433 try: 

434 period = f.period(symbol) 

435 except NotImplementedError: 

436 pass 

437 

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 

459 

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]) 

467 

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) 

472 

473 if base_has_sym and not expo_has_sym: 

474 period = periodicity(base, symbol) 

475 

476 elif expo_has_sym and not base_has_sym: 

477 period = periodicity(expo, symbol) 

478 

479 else: 

480 period = _periodicity(f.args, symbol) 

481 

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) 

488 

489 elif f.is_Add: 

490 k, g = f.as_independent(symbol) 

491 if k is not S.Zero: 

492 return periodicity(g, symbol) 

493 

494 period = _periodicity(g.args, symbol) 

495 

496 elif isinstance(f, Mod): 

497 a, n = f.args 

498 

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)) 

507 

508 elif isinstance(f, Piecewise): 

509 pass # not handling Piecewise yet as the return type is not favorable 

510 

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 

523 

524 if period is not None: 

525 if check: 

526 return _check(orig_f, period) 

527 return period 

528 

529 return None 

530 

531 

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. 

538 

539 Parameters 

540 ========== 

541 

542 args : Tuple of :py:class:`~.Symbol` 

543 All the symbols present in a function. 

544 

545 symbol : :py:class:`~.Symbol` 

546 The symbol over which the function is to be evaluated. 

547 

548 Returns 

549 ======= 

550 

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. 

555 

556 """ 

557 periods = [] 

558 for f in args: 

559 period = periodicity(f, symbol) 

560 if period is None: 

561 return None 

562 

563 if period is not S.Zero: 

564 periods.append(period) 

565 

566 if len(periods) > 1: 

567 return lcim(periods) 

568 

569 if periods: 

570 return periods[0] 

571 

572 

573def lcim(numbers): 

574 """Returns the least common integral multiple of a list of numbers. 

575 

576 The numbers can be rational or irrational or a mixture of both. 

577 `None` is returned for incommensurable numbers. 

578 

579 Parameters 

580 ========== 

581 

582 numbers : list 

583 Numbers (rational and/or irrational) for which lcim is to be found. 

584 

585 Returns 

586 ======= 

587 

588 number 

589 lcim if it exists, otherwise ``None`` for incommensurable numbers. 

590 

591 Examples 

592 ======== 

593 

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 

611 

612 elif all(num.is_rational for num in numbers): 

613 result = lcm_list(numbers) 

614 

615 else: 

616 pass 

617 

618 return result 

619 

620def is_convex(f, *syms, domain=S.Reals): 

621 r"""Determines the convexity of the function passed in the argument. 

622 

623 Parameters 

624 ========== 

625 

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. 

633 

634 Returns 

635 ======= 

636 

637 bool 

638 The method returns ``True`` if the function is convex otherwise it 

639 returns ``False``. 

640 

641 Raises 

642 ====== 

643 

644 NotImplementedError 

645 The check for the convexity of multivariate functions is not implemented yet. 

646 

647 Notes 

648 ===== 

649 

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. 

655 

656 Currently, convexity check of multivariate functions is not handled. 

657 

658 Examples 

659 ======== 

660 

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 

669 

670 References 

671 ========== 

672 

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 

678 

679 """ 

680 

681 if len(syms) > 1: 

682 raise NotImplementedError( 

683 "The check for the convexity of multivariate functions is not implemented yet.") 

684 

685 from sympy.solvers.inequalities import solve_univariate_inequality 

686 

687 f = _sympify(f) 

688 var = syms[0] 

689 if any(s in domain for s in singularities(f, var)): 

690 return False 

691 

692 condition = f.diff(var, 2) < 0 

693 if solve_univariate_inequality(condition, var, False, domain): 

694 return False 

695 return True 

696 

697 

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. 

702 

703 Parameters 

704 ========== 

705 

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. 

713 

714 Returns 

715 ======= 

716 

717 Set 

718 A set of stationary points for the function. If there are no 

719 stationary point, an :py:class:`~.EmptySet` is returned. 

720 

721 Examples 

722 ======== 

723 

724 >>> from sympy import Interval, Symbol, S, sin, pi, pprint, stationary_points 

725 >>> x = Symbol('x') 

726 

727 >>> stationary_points(1/x, x, S.Reals) 

728 EmptySet 

729 

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 

734 

735 >>> stationary_points(sin(x),x, Interval(0, 4*pi)) 

736 {pi/2, 3*pi/2, 5*pi/2, 7*pi/2} 

737 

738 """ 

739 from sympy.solvers.solveset import solveset 

740 

741 if domain is S.EmptySet: 

742 return S.EmptySet 

743 

744 domain = continuous_domain(f, symbol, domain) 

745 set = solveset(diff(f, symbol), symbol, domain) 

746 

747 return set 

748 

749 

750def maximum(f, symbol, domain=S.Reals): 

751 """ 

752 Returns the maximum value of a function in the given domain. 

753 

754 Parameters 

755 ========== 

756 

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. 

764 

765 Returns 

766 ======= 

767 

768 number 

769 Maximum value of the function in given domain. 

770 

771 Examples 

772 ======== 

773 

774 >>> from sympy import Interval, Symbol, S, sin, cos, pi, maximum 

775 >>> x = Symbol('x') 

776 

777 >>> f = -x**2 + 2*x + 5 

778 >>> maximum(f, x, S.Reals) 

779 6 

780 

781 >>> maximum(sin(x), x, Interval(-pi, pi/4)) 

782 sqrt(2)/2 

783 

784 >>> maximum(sin(x)*cos(x), x) 

785 1/2 

786 

787 """ 

788 if isinstance(symbol, Symbol): 

789 if domain is S.EmptySet: 

790 raise ValueError("Maximum value not defined for empty domain.") 

791 

792 return function_range(f, symbol, domain).sup 

793 else: 

794 raise ValueError("%s is not a valid symbol." % symbol) 

795 

796 

797def minimum(f, symbol, domain=S.Reals): 

798 """ 

799 Returns the minimum value of a function in the given domain. 

800 

801 Parameters 

802 ========== 

803 

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. 

811 

812 Returns 

813 ======= 

814 

815 number 

816 Minimum value of the function in the given domain. 

817 

818 Examples 

819 ======== 

820 

821 >>> from sympy import Interval, Symbol, S, sin, cos, minimum 

822 >>> x = Symbol('x') 

823 

824 >>> f = x**2 + 2*x + 5 

825 >>> minimum(f, x, S.Reals) 

826 4 

827 

828 >>> minimum(sin(x), x, Interval(2, 3)) 

829 sin(3) 

830 

831 >>> minimum(sin(x)*cos(x), x) 

832 -1/2 

833 

834 """ 

835 if isinstance(symbol, Symbol): 

836 if domain is S.EmptySet: 

837 raise ValueError("Minimum value not defined for empty domain.") 

838 

839 return function_range(f, symbol, domain).inf 

840 else: 

841 raise ValueError("%s is not a valid symbol." % symbol)