Coverage for /usr/lib/python3/dist-packages/sympy/functions/elementary/miscellaneous.py: 31%

317 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from sympy.core import Function, S, sympify, NumberKind 

2from sympy.utilities.iterables import sift 

3from sympy.core.add import Add 

4from sympy.core.containers import Tuple 

5from sympy.core.operations import LatticeOp, ShortCircuit 

6from sympy.core.function import (Application, Lambda, 

7 ArgumentIndexError) 

8from sympy.core.expr import Expr 

9from sympy.core.exprtools import factor_terms 

10from sympy.core.mod import Mod 

11from sympy.core.mul import Mul 

12from sympy.core.numbers import Rational 

13from sympy.core.power import Pow 

14from sympy.core.relational import Eq, Relational 

15from sympy.core.singleton import Singleton 

16from sympy.core.sorting import ordered 

17from sympy.core.symbol import Dummy 

18from sympy.core.rules import Transform 

19from sympy.core.logic import fuzzy_and, fuzzy_or, _torf 

20from sympy.core.traversal import walk 

21from sympy.core.numbers import Integer 

22from sympy.logic.boolalg import And, Or 

23 

24 

25def _minmax_as_Piecewise(op, *args): 

26 # helper for Min/Max rewrite as Piecewise 

27 from sympy.functions.elementary.piecewise import Piecewise 

28 ec = [] 

29 for i, a in enumerate(args): 

30 c = [Relational(a, args[j], op) for j in range(i + 1, len(args))] 

31 ec.append((a, And(*c))) 

32 return Piecewise(*ec) 

33 

34 

35class IdentityFunction(Lambda, metaclass=Singleton): 

36 """ 

37 The identity function 

38 

39 Examples 

40 ======== 

41 

42 >>> from sympy import Id, Symbol 

43 >>> x = Symbol('x') 

44 >>> Id(x) 

45 x 

46 

47 """ 

48 

49 _symbol = Dummy('x') 

50 

51 @property 

52 def signature(self): 

53 return Tuple(self._symbol) 

54 

55 @property 

56 def expr(self): 

57 return self._symbol 

58 

59 

60Id = S.IdentityFunction 

61 

62############################################################################### 

63############################# ROOT and SQUARE ROOT FUNCTION ################### 

64############################################################################### 

65 

66 

67def sqrt(arg, evaluate=None): 

68 """Returns the principal square root. 

69 

70 Parameters 

71 ========== 

72 

73 evaluate : bool, optional 

74 The parameter determines if the expression should be evaluated. 

75 If ``None``, its value is taken from 

76 ``global_parameters.evaluate``. 

77 

78 Examples 

79 ======== 

80 

81 >>> from sympy import sqrt, Symbol, S 

82 >>> x = Symbol('x') 

83 

84 >>> sqrt(x) 

85 sqrt(x) 

86 

87 >>> sqrt(x)**2 

88 x 

89 

90 Note that sqrt(x**2) does not simplify to x. 

91 

92 >>> sqrt(x**2) 

93 sqrt(x**2) 

94 

95 This is because the two are not equal to each other in general. 

96 For example, consider x == -1: 

97 

98 >>> from sympy import Eq 

99 >>> Eq(sqrt(x**2), x).subs(x, -1) 

100 False 

101 

102 This is because sqrt computes the principal square root, so the square may 

103 put the argument in a different branch. This identity does hold if x is 

104 positive: 

105 

106 >>> y = Symbol('y', positive=True) 

107 >>> sqrt(y**2) 

108 y 

109 

110 You can force this simplification by using the powdenest() function with 

111 the force option set to True: 

112 

113 >>> from sympy import powdenest 

114 >>> sqrt(x**2) 

115 sqrt(x**2) 

116 >>> powdenest(sqrt(x**2), force=True) 

117 x 

118 

119 To get both branches of the square root you can use the rootof function: 

120 

121 >>> from sympy import rootof 

122 

123 >>> [rootof(x**2-3,i) for i in (0,1)] 

124 [-sqrt(3), sqrt(3)] 

125 

126 Although ``sqrt`` is printed, there is no ``sqrt`` function so looking for 

127 ``sqrt`` in an expression will fail: 

128 

129 >>> from sympy.utilities.misc import func_name 

130 >>> func_name(sqrt(x)) 

131 'Pow' 

132 >>> sqrt(x).has(sqrt) 

133 False 

134 

135 To find ``sqrt`` look for ``Pow`` with an exponent of ``1/2``: 

136 

137 >>> (x + 1/sqrt(x)).find(lambda i: i.is_Pow and abs(i.exp) is S.Half) 

138 {1/sqrt(x)} 

139 

140 See Also 

141 ======== 

142 

143 sympy.polys.rootoftools.rootof, root, real_root 

144 

145 References 

146 ========== 

147 

148 .. [1] https://en.wikipedia.org/wiki/Square_root 

149 .. [2] https://en.wikipedia.org/wiki/Principal_value 

150 """ 

151 # arg = sympify(arg) is handled by Pow 

152 return Pow(arg, S.Half, evaluate=evaluate) 

153 

154 

155def cbrt(arg, evaluate=None): 

156 """Returns the principal cube root. 

157 

158 Parameters 

159 ========== 

160 

161 evaluate : bool, optional 

162 The parameter determines if the expression should be evaluated. 

163 If ``None``, its value is taken from 

164 ``global_parameters.evaluate``. 

165 

166 Examples 

167 ======== 

168 

169 >>> from sympy import cbrt, Symbol 

170 >>> x = Symbol('x') 

171 

172 >>> cbrt(x) 

173 x**(1/3) 

174 

175 >>> cbrt(x)**3 

176 x 

177 

178 Note that cbrt(x**3) does not simplify to x. 

179 

180 >>> cbrt(x**3) 

181 (x**3)**(1/3) 

182 

183 This is because the two are not equal to each other in general. 

184 For example, consider `x == -1`: 

185 

186 >>> from sympy import Eq 

187 >>> Eq(cbrt(x**3), x).subs(x, -1) 

188 False 

189 

190 This is because cbrt computes the principal cube root, this 

191 identity does hold if `x` is positive: 

192 

193 >>> y = Symbol('y', positive=True) 

194 >>> cbrt(y**3) 

195 y 

196 

197 See Also 

198 ======== 

199 

200 sympy.polys.rootoftools.rootof, root, real_root 

201 

202 References 

203 ========== 

204 

205 .. [1] https://en.wikipedia.org/wiki/Cube_root 

206 .. [2] https://en.wikipedia.org/wiki/Principal_value 

207 

208 """ 

209 return Pow(arg, Rational(1, 3), evaluate=evaluate) 

210 

211 

212def root(arg, n, k=0, evaluate=None): 

213 r"""Returns the *k*-th *n*-th root of ``arg``. 

214 

215 Parameters 

216 ========== 

217 

218 k : int, optional 

219 Should be an integer in $\{0, 1, ..., n-1\}$. 

220 Defaults to the principal root if $0$. 

221 

222 evaluate : bool, optional 

223 The parameter determines if the expression should be evaluated. 

224 If ``None``, its value is taken from 

225 ``global_parameters.evaluate``. 

226 

227 Examples 

228 ======== 

229 

230 >>> from sympy import root, Rational 

231 >>> from sympy.abc import x, n 

232 

233 >>> root(x, 2) 

234 sqrt(x) 

235 

236 >>> root(x, 3) 

237 x**(1/3) 

238 

239 >>> root(x, n) 

240 x**(1/n) 

241 

242 >>> root(x, -Rational(2, 3)) 

243 x**(-3/2) 

244 

245 To get the k-th n-th root, specify k: 

246 

247 >>> root(-2, 3, 2) 

248 -(-1)**(2/3)*2**(1/3) 

249 

250 To get all n n-th roots you can use the rootof function. 

251 The following examples show the roots of unity for n 

252 equal 2, 3 and 4: 

253 

254 >>> from sympy import rootof 

255 

256 >>> [rootof(x**2 - 1, i) for i in range(2)] 

257 [-1, 1] 

258 

259 >>> [rootof(x**3 - 1,i) for i in range(3)] 

260 [1, -1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2] 

261 

262 >>> [rootof(x**4 - 1,i) for i in range(4)] 

263 [-1, 1, -I, I] 

264 

265 SymPy, like other symbolic algebra systems, returns the 

266 complex root of negative numbers. This is the principal 

267 root and differs from the text-book result that one might 

268 be expecting. For example, the cube root of -8 does not 

269 come back as -2: 

270 

271 >>> root(-8, 3) 

272 2*(-1)**(1/3) 

273 

274 The real_root function can be used to either make the principal 

275 result real (or simply to return the real root directly): 

276 

277 >>> from sympy import real_root 

278 >>> real_root(_) 

279 -2 

280 >>> real_root(-32, 5) 

281 -2 

282 

283 Alternatively, the n//2-th n-th root of a negative number can be 

284 computed with root: 

285 

286 >>> root(-32, 5, 5//2) 

287 -2 

288 

289 See Also 

290 ======== 

291 

292 sympy.polys.rootoftools.rootof 

293 sympy.core.power.integer_nthroot 

294 sqrt, real_root 

295 

296 References 

297 ========== 

298 

299 .. [1] https://en.wikipedia.org/wiki/Square_root 

300 .. [2] https://en.wikipedia.org/wiki/Real_root 

301 .. [3] https://en.wikipedia.org/wiki/Root_of_unity 

302 .. [4] https://en.wikipedia.org/wiki/Principal_value 

303 .. [5] https://mathworld.wolfram.com/CubeRoot.html 

304 

305 """ 

306 n = sympify(n) 

307 if k: 

308 return Mul(Pow(arg, S.One/n, evaluate=evaluate), S.NegativeOne**(2*k/n), evaluate=evaluate) 

309 return Pow(arg, 1/n, evaluate=evaluate) 

310 

311 

312def real_root(arg, n=None, evaluate=None): 

313 r"""Return the real *n*'th-root of *arg* if possible. 

314 

315 Parameters 

316 ========== 

317 

318 n : int or None, optional 

319 If *n* is ``None``, then all instances of 

320 $(-n)^{1/\text{odd}}$ will be changed to $-n^{1/\text{odd}}$. 

321 This will only create a real root of a principal root. 

322 The presence of other factors may cause the result to not be 

323 real. 

324 

325 evaluate : bool, optional 

326 The parameter determines if the expression should be evaluated. 

327 If ``None``, its value is taken from 

328 ``global_parameters.evaluate``. 

329 

330 Examples 

331 ======== 

332 

333 >>> from sympy import root, real_root 

334 

335 >>> real_root(-8, 3) 

336 -2 

337 >>> root(-8, 3) 

338 2*(-1)**(1/3) 

339 >>> real_root(_) 

340 -2 

341 

342 If one creates a non-principal root and applies real_root, the 

343 result will not be real (so use with caution): 

344 

345 >>> root(-8, 3, 2) 

346 -2*(-1)**(2/3) 

347 >>> real_root(_) 

348 -2*(-1)**(2/3) 

349 

350 See Also 

351 ======== 

352 

353 sympy.polys.rootoftools.rootof 

354 sympy.core.power.integer_nthroot 

355 root, sqrt 

356 """ 

357 from sympy.functions.elementary.complexes import Abs, im, sign 

358 from sympy.functions.elementary.piecewise import Piecewise 

359 if n is not None: 

360 return Piecewise( 

361 (root(arg, n, evaluate=evaluate), Or(Eq(n, S.One), Eq(n, S.NegativeOne))), 

362 (Mul(sign(arg), root(Abs(arg), n, evaluate=evaluate), evaluate=evaluate), 

363 And(Eq(im(arg), S.Zero), Eq(Mod(n, 2), S.One))), 

364 (root(arg, n, evaluate=evaluate), True)) 

365 rv = sympify(arg) 

366 n1pow = Transform(lambda x: -(-x.base)**x.exp, 

367 lambda x: 

368 x.is_Pow and 

369 x.base.is_negative and 

370 x.exp.is_Rational and 

371 x.exp.p == 1 and x.exp.q % 2) 

372 return rv.xreplace(n1pow) 

373 

374############################################################################### 

375############################# MINIMUM and MAXIMUM ############################# 

376############################################################################### 

377 

378 

379class MinMaxBase(Expr, LatticeOp): 

380 def __new__(cls, *args, **assumptions): 

381 from sympy.core.parameters import global_parameters 

382 evaluate = assumptions.pop('evaluate', global_parameters.evaluate) 

383 args = (sympify(arg) for arg in args) 

384 

385 # first standard filter, for cls.zero and cls.identity 

386 # also reshape Max(a, Max(b, c)) to Max(a, b, c) 

387 

388 if evaluate: 

389 try: 

390 args = frozenset(cls._new_args_filter(args)) 

391 except ShortCircuit: 

392 return cls.zero 

393 # remove redundant args that are easily identified 

394 args = cls._collapse_arguments(args, **assumptions) 

395 # find local zeros 

396 args = cls._find_localzeros(args, **assumptions) 

397 args = frozenset(args) 

398 

399 if not args: 

400 return cls.identity 

401 

402 if len(args) == 1: 

403 return list(args).pop() 

404 

405 # base creation 

406 obj = Expr.__new__(cls, *ordered(args), **assumptions) 

407 obj._argset = args 

408 return obj 

409 

410 @classmethod 

411 def _collapse_arguments(cls, args, **assumptions): 

412 """Remove redundant args. 

413 

414 Examples 

415 ======== 

416 

417 >>> from sympy import Min, Max 

418 >>> from sympy.abc import a, b, c, d, e 

419 

420 Any arg in parent that appears in any 

421 parent-like function in any of the flat args 

422 of parent can be removed from that sub-arg: 

423 

424 >>> Min(a, Max(b, Min(a, c, d))) 

425 Min(a, Max(b, Min(c, d))) 

426 

427 If the arg of parent appears in an opposite-than parent 

428 function in any of the flat args of parent that function 

429 can be replaced with the arg: 

430 

431 >>> Min(a, Max(b, Min(c, d, Max(a, e)))) 

432 Min(a, Max(b, Min(a, c, d))) 

433 """ 

434 if not args: 

435 return args 

436 args = list(ordered(args)) 

437 if cls == Min: 

438 other = Max 

439 else: 

440 other = Min 

441 

442 # find global comparable max of Max and min of Min if a new 

443 # value is being introduced in these args at position 0 of 

444 # the ordered args 

445 if args[0].is_number: 

446 sifted = mins, maxs = [], [] 

447 for i in args: 

448 for v in walk(i, Min, Max): 

449 if v.args[0].is_comparable: 

450 sifted[isinstance(v, Max)].append(v) 

451 small = Min.identity 

452 for i in mins: 

453 v = i.args[0] 

454 if v.is_number and (v < small) == True: 

455 small = v 

456 big = Max.identity 

457 for i in maxs: 

458 v = i.args[0] 

459 if v.is_number and (v > big) == True: 

460 big = v 

461 # at the point when this function is called from __new__, 

462 # there may be more than one numeric arg present since 

463 # local zeros have not been handled yet, so look through 

464 # more than the first arg 

465 if cls == Min: 

466 for arg in args: 

467 if not arg.is_number: 

468 break 

469 if (arg < small) == True: 

470 small = arg 

471 elif cls == Max: 

472 for arg in args: 

473 if not arg.is_number: 

474 break 

475 if (arg > big) == True: 

476 big = arg 

477 T = None 

478 if cls == Min: 

479 if small != Min.identity: 

480 other = Max 

481 T = small 

482 elif big != Max.identity: 

483 other = Min 

484 T = big 

485 if T is not None: 

486 # remove numerical redundancy 

487 for i in range(len(args)): 

488 a = args[i] 

489 if isinstance(a, other): 

490 a0 = a.args[0] 

491 if ((a0 > T) if other == Max else (a0 < T)) == True: 

492 args[i] = cls.identity 

493 

494 # remove redundant symbolic args 

495 def do(ai, a): 

496 if not isinstance(ai, (Min, Max)): 

497 return ai 

498 cond = a in ai.args 

499 if not cond: 

500 return ai.func(*[do(i, a) for i in ai.args], 

501 evaluate=False) 

502 if isinstance(ai, cls): 

503 return ai.func(*[do(i, a) for i in ai.args if i != a], 

504 evaluate=False) 

505 return a 

506 for i, a in enumerate(args): 

507 args[i + 1:] = [do(ai, a) for ai in args[i + 1:]] 

508 

509 # factor out common elements as for 

510 # Min(Max(x, y), Max(x, z)) -> Max(x, Min(y, z)) 

511 # and vice versa when swapping Min/Max -- do this only for the 

512 # easy case where all functions contain something in common; 

513 # trying to find some optimal subset of args to modify takes 

514 # too long 

515 

516 def factor_minmax(args): 

517 is_other = lambda arg: isinstance(arg, other) 

518 other_args, remaining_args = sift(args, is_other, binary=True) 

519 if not other_args: 

520 return args 

521 

522 # Min(Max(x, y, z), Max(x, y, u, v)) -> {x,y}, ({z}, {u,v}) 

523 arg_sets = [set(arg.args) for arg in other_args] 

524 common = set.intersection(*arg_sets) 

525 if not common: 

526 return args 

527 

528 new_other_args = list(common) 

529 arg_sets_diff = [arg_set - common for arg_set in arg_sets] 

530 

531 # If any set is empty after removing common then all can be 

532 # discarded e.g. Min(Max(a, b, c), Max(a, b)) -> Max(a, b) 

533 if all(arg_sets_diff): 

534 other_args_diff = [other(*s, evaluate=False) for s in arg_sets_diff] 

535 new_other_args.append(cls(*other_args_diff, evaluate=False)) 

536 

537 other_args_factored = other(*new_other_args, evaluate=False) 

538 return remaining_args + [other_args_factored] 

539 

540 if len(args) > 1: 

541 args = factor_minmax(args) 

542 

543 return args 

544 

545 @classmethod 

546 def _new_args_filter(cls, arg_sequence): 

547 """ 

548 Generator filtering args. 

549 

550 first standard filter, for cls.zero and cls.identity. 

551 Also reshape ``Max(a, Max(b, c))`` to ``Max(a, b, c)``, 

552 and check arguments for comparability 

553 """ 

554 for arg in arg_sequence: 

555 # pre-filter, checking comparability of arguments 

556 if not isinstance(arg, Expr) or arg.is_extended_real is False or ( 

557 arg.is_number and 

558 not arg.is_comparable): 

559 raise ValueError("The argument '%s' is not comparable." % arg) 

560 

561 if arg == cls.zero: 

562 raise ShortCircuit(arg) 

563 elif arg == cls.identity: 

564 continue 

565 elif arg.func == cls: 

566 yield from arg.args 

567 else: 

568 yield arg 

569 

570 @classmethod 

571 def _find_localzeros(cls, values, **options): 

572 """ 

573 Sequentially allocate values to localzeros. 

574 

575 When a value is identified as being more extreme than another member it 

576 replaces that member; if this is never true, then the value is simply 

577 appended to the localzeros. 

578 """ 

579 localzeros = set() 

580 for v in values: 

581 is_newzero = True 

582 localzeros_ = list(localzeros) 

583 for z in localzeros_: 

584 if id(v) == id(z): 

585 is_newzero = False 

586 else: 

587 con = cls._is_connected(v, z) 

588 if con: 

589 is_newzero = False 

590 if con is True or con == cls: 

591 localzeros.remove(z) 

592 localzeros.update([v]) 

593 if is_newzero: 

594 localzeros.update([v]) 

595 return localzeros 

596 

597 @classmethod 

598 def _is_connected(cls, x, y): 

599 """ 

600 Check if x and y are connected somehow. 

601 """ 

602 for i in range(2): 

603 if x == y: 

604 return True 

605 t, f = Max, Min 

606 for op in "><": 

607 for j in range(2): 

608 try: 

609 if op == ">": 

610 v = x >= y 

611 else: 

612 v = x <= y 

613 except TypeError: 

614 return False # non-real arg 

615 if not v.is_Relational: 

616 return t if v else f 

617 t, f = f, t 

618 x, y = y, x 

619 x, y = y, x # run next pass with reversed order relative to start 

620 # simplification can be expensive, so be conservative 

621 # in what is attempted 

622 x = factor_terms(x - y) 

623 y = S.Zero 

624 

625 return False 

626 

627 def _eval_derivative(self, s): 

628 # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s) 

629 i = 0 

630 l = [] 

631 for a in self.args: 

632 i += 1 

633 da = a.diff(s) 

634 if da.is_zero: 

635 continue 

636 try: 

637 df = self.fdiff(i) 

638 except ArgumentIndexError: 

639 df = Function.fdiff(self, i) 

640 l.append(df * da) 

641 return Add(*l) 

642 

643 def _eval_rewrite_as_Abs(self, *args, **kwargs): 

644 from sympy.functions.elementary.complexes import Abs 

645 s = (args[0] + self.func(*args[1:]))/2 

646 d = abs(args[0] - self.func(*args[1:]))/2 

647 return (s + d if isinstance(self, Max) else s - d).rewrite(Abs) 

648 

649 def evalf(self, n=15, **options): 

650 return self.func(*[a.evalf(n, **options) for a in self.args]) 

651 

652 def n(self, *args, **kwargs): 

653 return self.evalf(*args, **kwargs) 

654 

655 _eval_is_algebraic = lambda s: _torf(i.is_algebraic for i in s.args) 

656 _eval_is_antihermitian = lambda s: _torf(i.is_antihermitian for i in s.args) 

657 _eval_is_commutative = lambda s: _torf(i.is_commutative for i in s.args) 

658 _eval_is_complex = lambda s: _torf(i.is_complex for i in s.args) 

659 _eval_is_composite = lambda s: _torf(i.is_composite for i in s.args) 

660 _eval_is_even = lambda s: _torf(i.is_even for i in s.args) 

661 _eval_is_finite = lambda s: _torf(i.is_finite for i in s.args) 

662 _eval_is_hermitian = lambda s: _torf(i.is_hermitian for i in s.args) 

663 _eval_is_imaginary = lambda s: _torf(i.is_imaginary for i in s.args) 

664 _eval_is_infinite = lambda s: _torf(i.is_infinite for i in s.args) 

665 _eval_is_integer = lambda s: _torf(i.is_integer for i in s.args) 

666 _eval_is_irrational = lambda s: _torf(i.is_irrational for i in s.args) 

667 _eval_is_negative = lambda s: _torf(i.is_negative for i in s.args) 

668 _eval_is_noninteger = lambda s: _torf(i.is_noninteger for i in s.args) 

669 _eval_is_nonnegative = lambda s: _torf(i.is_nonnegative for i in s.args) 

670 _eval_is_nonpositive = lambda s: _torf(i.is_nonpositive for i in s.args) 

671 _eval_is_nonzero = lambda s: _torf(i.is_nonzero for i in s.args) 

672 _eval_is_odd = lambda s: _torf(i.is_odd for i in s.args) 

673 _eval_is_polar = lambda s: _torf(i.is_polar for i in s.args) 

674 _eval_is_positive = lambda s: _torf(i.is_positive for i in s.args) 

675 _eval_is_prime = lambda s: _torf(i.is_prime for i in s.args) 

676 _eval_is_rational = lambda s: _torf(i.is_rational for i in s.args) 

677 _eval_is_real = lambda s: _torf(i.is_real for i in s.args) 

678 _eval_is_extended_real = lambda s: _torf(i.is_extended_real for i in s.args) 

679 _eval_is_transcendental = lambda s: _torf(i.is_transcendental for i in s.args) 

680 _eval_is_zero = lambda s: _torf(i.is_zero for i in s.args) 

681 

682 

683class Max(MinMaxBase, Application): 

684 r""" 

685 Return, if possible, the maximum value of the list. 

686 

687 When number of arguments is equal one, then 

688 return this argument. 

689 

690 When number of arguments is equal two, then 

691 return, if possible, the value from (a, b) that is $\ge$ the other. 

692 

693 In common case, when the length of list greater than 2, the task 

694 is more complicated. Return only the arguments, which are greater 

695 than others, if it is possible to determine directional relation. 

696 

697 If is not possible to determine such a relation, return a partially 

698 evaluated result. 

699 

700 Assumptions are used to make the decision too. 

701 

702 Also, only comparable arguments are permitted. 

703 

704 It is named ``Max`` and not ``max`` to avoid conflicts 

705 with the built-in function ``max``. 

706 

707 

708 Examples 

709 ======== 

710 

711 >>> from sympy import Max, Symbol, oo 

712 >>> from sympy.abc import x, y, z 

713 >>> p = Symbol('p', positive=True) 

714 >>> n = Symbol('n', negative=True) 

715 

716 >>> Max(x, -2) 

717 Max(-2, x) 

718 >>> Max(x, -2).subs(x, 3) 

719 3 

720 >>> Max(p, -2) 

721 p 

722 >>> Max(x, y) 

723 Max(x, y) 

724 >>> Max(x, y) == Max(y, x) 

725 True 

726 >>> Max(x, Max(y, z)) 

727 Max(x, y, z) 

728 >>> Max(n, 8, p, 7, -oo) 

729 Max(8, p) 

730 >>> Max (1, x, oo) 

731 oo 

732 

733 * Algorithm 

734 

735 The task can be considered as searching of supremums in the 

736 directed complete partial orders [1]_. 

737 

738 The source values are sequentially allocated by the isolated subsets 

739 in which supremums are searched and result as Max arguments. 

740 

741 If the resulted supremum is single, then it is returned. 

742 

743 The isolated subsets are the sets of values which are only the comparable 

744 with each other in the current set. E.g. natural numbers are comparable with 

745 each other, but not comparable with the `x` symbol. Another example: the 

746 symbol `x` with negative assumption is comparable with a natural number. 

747 

748 Also there are "least" elements, which are comparable with all others, 

749 and have a zero property (maximum or minimum for all elements). 

750 For example, in case of $\infty$, the allocation operation is terminated 

751 and only this value is returned. 

752 

753 Assumption: 

754 - if $A > B > C$ then $A > C$ 

755 - if $A = B$ then $B$ can be removed 

756 

757 References 

758 ========== 

759 

760 .. [1] https://en.wikipedia.org/wiki/Directed_complete_partial_order 

761 .. [2] https://en.wikipedia.org/wiki/Lattice_%28order%29 

762 

763 See Also 

764 ======== 

765 

766 Min : find minimum values 

767 """ 

768 zero = S.Infinity 

769 identity = S.NegativeInfinity 

770 

771 def fdiff( self, argindex ): 

772 from sympy.functions.special.delta_functions import Heaviside 

773 n = len(self.args) 

774 if 0 < argindex and argindex <= n: 

775 argindex -= 1 

776 if n == 2: 

777 return Heaviside(self.args[argindex] - self.args[1 - argindex]) 

778 newargs = tuple([self.args[i] for i in range(n) if i != argindex]) 

779 return Heaviside(self.args[argindex] - Max(*newargs)) 

780 else: 

781 raise ArgumentIndexError(self, argindex) 

782 

783 def _eval_rewrite_as_Heaviside(self, *args, **kwargs): 

784 from sympy.functions.special.delta_functions import Heaviside 

785 return Add(*[j*Mul(*[Heaviside(j - i) for i in args if i!=j]) \ 

786 for j in args]) 

787 

788 def _eval_rewrite_as_Piecewise(self, *args, **kwargs): 

789 return _minmax_as_Piecewise('>=', *args) 

790 

791 def _eval_is_positive(self): 

792 return fuzzy_or(a.is_positive for a in self.args) 

793 

794 def _eval_is_nonnegative(self): 

795 return fuzzy_or(a.is_nonnegative for a in self.args) 

796 

797 def _eval_is_negative(self): 

798 return fuzzy_and(a.is_negative for a in self.args) 

799 

800 

801class Min(MinMaxBase, Application): 

802 """ 

803 Return, if possible, the minimum value of the list. 

804 It is named ``Min`` and not ``min`` to avoid conflicts 

805 with the built-in function ``min``. 

806 

807 Examples 

808 ======== 

809 

810 >>> from sympy import Min, Symbol, oo 

811 >>> from sympy.abc import x, y 

812 >>> p = Symbol('p', positive=True) 

813 >>> n = Symbol('n', negative=True) 

814 

815 >>> Min(x, -2) 

816 Min(-2, x) 

817 >>> Min(x, -2).subs(x, 3) 

818 -2 

819 >>> Min(p, -3) 

820 -3 

821 >>> Min(x, y) 

822 Min(x, y) 

823 >>> Min(n, 8, p, -7, p, oo) 

824 Min(-7, n) 

825 

826 See Also 

827 ======== 

828 

829 Max : find maximum values 

830 """ 

831 zero = S.NegativeInfinity 

832 identity = S.Infinity 

833 

834 def fdiff( self, argindex ): 

835 from sympy.functions.special.delta_functions import Heaviside 

836 n = len(self.args) 

837 if 0 < argindex and argindex <= n: 

838 argindex -= 1 

839 if n == 2: 

840 return Heaviside( self.args[1-argindex] - self.args[argindex] ) 

841 newargs = tuple([ self.args[i] for i in range(n) if i != argindex]) 

842 return Heaviside( Min(*newargs) - self.args[argindex] ) 

843 else: 

844 raise ArgumentIndexError(self, argindex) 

845 

846 def _eval_rewrite_as_Heaviside(self, *args, **kwargs): 

847 from sympy.functions.special.delta_functions import Heaviside 

848 return Add(*[j*Mul(*[Heaviside(i-j) for i in args if i!=j]) \ 

849 for j in args]) 

850 

851 def _eval_rewrite_as_Piecewise(self, *args, **kwargs): 

852 return _minmax_as_Piecewise('<=', *args) 

853 

854 def _eval_is_positive(self): 

855 return fuzzy_and(a.is_positive for a in self.args) 

856 

857 def _eval_is_nonnegative(self): 

858 return fuzzy_and(a.is_nonnegative for a in self.args) 

859 

860 def _eval_is_negative(self): 

861 return fuzzy_or(a.is_negative for a in self.args) 

862 

863 

864class Rem(Function): 

865 """Returns the remainder when ``p`` is divided by ``q`` where ``p`` is finite 

866 and ``q`` is not equal to zero. The result, ``p - int(p/q)*q``, has the same sign 

867 as the divisor. 

868 

869 Parameters 

870 ========== 

871 

872 p : Expr 

873 Dividend. 

874 

875 q : Expr 

876 Divisor. 

877 

878 Notes 

879 ===== 

880 

881 ``Rem`` corresponds to the ``%`` operator in C. 

882 

883 Examples 

884 ======== 

885 

886 >>> from sympy.abc import x, y 

887 >>> from sympy import Rem 

888 >>> Rem(x**3, y) 

889 Rem(x**3, y) 

890 >>> Rem(x**3, y).subs({x: -5, y: 3}) 

891 -2 

892 

893 See Also 

894 ======== 

895 

896 Mod 

897 """ 

898 kind = NumberKind 

899 

900 @classmethod 

901 def eval(cls, p, q): 

902 """Return the function remainder if both p, q are numbers and q is not 

903 zero. 

904 """ 

905 

906 if q.is_zero: 

907 raise ZeroDivisionError("Division by zero") 

908 if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False: 

909 return S.NaN 

910 if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1): 

911 return S.Zero 

912 

913 if q.is_Number: 

914 if p.is_Number: 

915 return p - Integer(p/q)*q