expr_with_limits.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. from sympy.core.add import Add
  2. from sympy.core.containers import Tuple
  3. from sympy.core.expr import Expr
  4. from sympy.core.function import AppliedUndef, UndefinedFunction
  5. from sympy.core.mul import Mul
  6. from sympy.core.relational import Equality, Relational
  7. from sympy.core.singleton import S
  8. from sympy.core.symbol import Symbol, Dummy
  9. from sympy.core.sympify import sympify
  10. from sympy.functions.elementary.piecewise import (piecewise_fold,
  11. Piecewise)
  12. from sympy.logic.boolalg import BooleanFunction
  13. from sympy.matrices.matrices import MatrixBase
  14. from sympy.sets.sets import Interval, Set
  15. from sympy.sets.fancysets import Range
  16. from sympy.tensor.indexed import Idx
  17. from sympy.utilities import flatten
  18. from sympy.utilities.iterables import sift, is_sequence
  19. from sympy.utilities.exceptions import sympy_deprecation_warning
  20. def _common_new(cls, function, *symbols, discrete, **assumptions):
  21. """Return either a special return value or the tuple,
  22. (function, limits, orientation). This code is common to
  23. both ExprWithLimits and AddWithLimits."""
  24. function = sympify(function)
  25. if isinstance(function, Equality):
  26. # This transforms e.g. Integral(Eq(x, y)) to Eq(Integral(x), Integral(y))
  27. # but that is only valid for definite integrals.
  28. limits, orientation = _process_limits(*symbols, discrete=discrete)
  29. if not (limits and all(len(limit) == 3 for limit in limits)):
  30. sympy_deprecation_warning(
  31. """
  32. Creating a indefinite integral with an Eq() argument is
  33. deprecated.
  34. This is because indefinite integrals do not preserve equality
  35. due to the arbitrary constants. If you want an equality of
  36. indefinite integrals, use Eq(Integral(a, x), Integral(b, x))
  37. explicitly.
  38. """,
  39. deprecated_since_version="1.6",
  40. active_deprecations_target="deprecated-indefinite-integral-eq",
  41. stacklevel=5,
  42. )
  43. lhs = function.lhs
  44. rhs = function.rhs
  45. return Equality(cls(lhs, *symbols, **assumptions), \
  46. cls(rhs, *symbols, **assumptions))
  47. if function is S.NaN:
  48. return S.NaN
  49. if symbols:
  50. limits, orientation = _process_limits(*symbols, discrete=discrete)
  51. for i, li in enumerate(limits):
  52. if len(li) == 4:
  53. function = function.subs(li[0], li[-1])
  54. limits[i] = Tuple(*li[:-1])
  55. else:
  56. # symbol not provided -- we can still try to compute a general form
  57. free = function.free_symbols
  58. if len(free) != 1:
  59. raise ValueError(
  60. "specify dummy variables for %s" % function)
  61. limits, orientation = [Tuple(s) for s in free], 1
  62. # denest any nested calls
  63. while cls == type(function):
  64. limits = list(function.limits) + limits
  65. function = function.function
  66. # Any embedded piecewise functions need to be brought out to the
  67. # top level. We only fold Piecewise that contain the integration
  68. # variable.
  69. reps = {}
  70. symbols_of_integration = {i[0] for i in limits}
  71. for p in function.atoms(Piecewise):
  72. if not p.has(*symbols_of_integration):
  73. reps[p] = Dummy()
  74. # mask off those that don't
  75. function = function.xreplace(reps)
  76. # do the fold
  77. function = piecewise_fold(function)
  78. # remove the masking
  79. function = function.xreplace({v: k for k, v in reps.items()})
  80. return function, limits, orientation
  81. def _process_limits(*symbols, discrete=None):
  82. """Process the list of symbols and convert them to canonical limits,
  83. storing them as Tuple(symbol, lower, upper). The orientation of
  84. the function is also returned when the upper limit is missing
  85. so (x, 1, None) becomes (x, None, 1) and the orientation is changed.
  86. In the case that a limit is specified as (symbol, Range), a list of
  87. length 4 may be returned if a change of variables is needed; the
  88. expression that should replace the symbol in the expression is
  89. the fourth element in the list.
  90. """
  91. limits = []
  92. orientation = 1
  93. if discrete is None:
  94. err_msg = 'discrete must be True or False'
  95. elif discrete:
  96. err_msg = 'use Range, not Interval or Relational'
  97. else:
  98. err_msg = 'use Interval or Relational, not Range'
  99. for V in symbols:
  100. if isinstance(V, (Relational, BooleanFunction)):
  101. if discrete:
  102. raise TypeError(err_msg)
  103. variable = V.atoms(Symbol).pop()
  104. V = (variable, V.as_set())
  105. elif isinstance(V, Symbol) or getattr(V, '_diff_wrt', False):
  106. if isinstance(V, Idx):
  107. if V.lower is None or V.upper is None:
  108. limits.append(Tuple(V))
  109. else:
  110. limits.append(Tuple(V, V.lower, V.upper))
  111. else:
  112. limits.append(Tuple(V))
  113. continue
  114. if is_sequence(V) and not isinstance(V, Set):
  115. if len(V) == 2 and isinstance(V[1], Set):
  116. V = list(V)
  117. if isinstance(V[1], Interval): # includes Reals
  118. if discrete:
  119. raise TypeError(err_msg)
  120. V[1:] = V[1].inf, V[1].sup
  121. elif isinstance(V[1], Range):
  122. if not discrete:
  123. raise TypeError(err_msg)
  124. lo = V[1].inf
  125. hi = V[1].sup
  126. dx = abs(V[1].step) # direction doesn't matter
  127. if dx == 1:
  128. V[1:] = [lo, hi]
  129. else:
  130. if lo is not S.NegativeInfinity:
  131. V = [V[0]] + [0, (hi - lo)//dx, dx*V[0] + lo]
  132. else:
  133. V = [V[0]] + [0, S.Infinity, -dx*V[0] + hi]
  134. else:
  135. # more complicated sets would require splitting, e.g.
  136. # Union(Interval(1, 3), interval(6,10))
  137. raise NotImplementedError(
  138. 'expecting Range' if discrete else
  139. 'Relational or single Interval' )
  140. V = sympify(flatten(V)) # list of sympified elements/None
  141. if isinstance(V[0], (Symbol, Idx)) or getattr(V[0], '_diff_wrt', False):
  142. newsymbol = V[0]
  143. if len(V) == 3:
  144. # general case
  145. if V[2] is None and V[1] is not None:
  146. orientation *= -1
  147. V = [newsymbol] + [i for i in V[1:] if i is not None]
  148. lenV = len(V)
  149. if not isinstance(newsymbol, Idx) or lenV == 3:
  150. if lenV == 4:
  151. limits.append(Tuple(*V))
  152. continue
  153. if lenV == 3:
  154. if isinstance(newsymbol, Idx):
  155. # Idx represents an integer which may have
  156. # specified values it can take on; if it is
  157. # given such a value, an error is raised here
  158. # if the summation would try to give it a larger
  159. # or smaller value than permitted. None and Symbolic
  160. # values will not raise an error.
  161. lo, hi = newsymbol.lower, newsymbol.upper
  162. try:
  163. if lo is not None and not bool(V[1] >= lo):
  164. raise ValueError("Summation will set Idx value too low.")
  165. except TypeError:
  166. pass
  167. try:
  168. if hi is not None and not bool(V[2] <= hi):
  169. raise ValueError("Summation will set Idx value too high.")
  170. except TypeError:
  171. pass
  172. limits.append(Tuple(*V))
  173. continue
  174. if lenV == 1 or (lenV == 2 and V[1] is None):
  175. limits.append(Tuple(newsymbol))
  176. continue
  177. elif lenV == 2:
  178. limits.append(Tuple(newsymbol, V[1]))
  179. continue
  180. raise ValueError('Invalid limits given: %s' % str(symbols))
  181. return limits, orientation
  182. class ExprWithLimits(Expr):
  183. __slots__ = ('is_commutative',)
  184. def __new__(cls, function, *symbols, **assumptions):
  185. from sympy.concrete.products import Product
  186. pre = _common_new(cls, function, *symbols,
  187. discrete=issubclass(cls, Product), **assumptions)
  188. if isinstance(pre, tuple):
  189. function, limits, _ = pre
  190. else:
  191. return pre
  192. # limits must have upper and lower bounds; the indefinite form
  193. # is not supported. This restriction does not apply to AddWithLimits
  194. if any(len(l) != 3 or None in l for l in limits):
  195. raise ValueError('ExprWithLimits requires values for lower and upper bounds.')
  196. obj = Expr.__new__(cls, **assumptions)
  197. arglist = [function]
  198. arglist.extend(limits)
  199. obj._args = tuple(arglist)
  200. obj.is_commutative = function.is_commutative # limits already checked
  201. return obj
  202. @property
  203. def function(self):
  204. """Return the function applied across limits.
  205. Examples
  206. ========
  207. >>> from sympy import Integral
  208. >>> from sympy.abc import x
  209. >>> Integral(x**2, (x,)).function
  210. x**2
  211. See Also
  212. ========
  213. limits, variables, free_symbols
  214. """
  215. return self._args[0]
  216. @property
  217. def kind(self):
  218. return self.function.kind
  219. @property
  220. def limits(self):
  221. """Return the limits of expression.
  222. Examples
  223. ========
  224. >>> from sympy import Integral
  225. >>> from sympy.abc import x, i
  226. >>> Integral(x**i, (i, 1, 3)).limits
  227. ((i, 1, 3),)
  228. See Also
  229. ========
  230. function, variables, free_symbols
  231. """
  232. return self._args[1:]
  233. @property
  234. def variables(self):
  235. """Return a list of the limit variables.
  236. >>> from sympy import Sum
  237. >>> from sympy.abc import x, i
  238. >>> Sum(x**i, (i, 1, 3)).variables
  239. [i]
  240. See Also
  241. ========
  242. function, limits, free_symbols
  243. as_dummy : Rename dummy variables
  244. sympy.integrals.integrals.Integral.transform : Perform mapping on the dummy variable
  245. """
  246. return [l[0] for l in self.limits]
  247. @property
  248. def bound_symbols(self):
  249. """Return only variables that are dummy variables.
  250. Examples
  251. ========
  252. >>> from sympy import Integral
  253. >>> from sympy.abc import x, i, j, k
  254. >>> Integral(x**i, (i, 1, 3), (j, 2), k).bound_symbols
  255. [i, j]
  256. See Also
  257. ========
  258. function, limits, free_symbols
  259. as_dummy : Rename dummy variables
  260. sympy.integrals.integrals.Integral.transform : Perform mapping on the dummy variable
  261. """
  262. return [l[0] for l in self.limits if len(l) != 1]
  263. @property
  264. def free_symbols(self):
  265. """
  266. This method returns the symbols in the object, excluding those
  267. that take on a specific value (i.e. the dummy symbols).
  268. Examples
  269. ========
  270. >>> from sympy import Sum
  271. >>> from sympy.abc import x, y
  272. >>> Sum(x, (x, y, 1)).free_symbols
  273. {y}
  274. """
  275. # don't test for any special values -- nominal free symbols
  276. # should be returned, e.g. don't return set() if the
  277. # function is zero -- treat it like an unevaluated expression.
  278. function, limits = self.function, self.limits
  279. # mask off non-symbol integration variables that have
  280. # more than themself as a free symbol
  281. reps = {i[0]: i[0] if i[0].free_symbols == {i[0]} else Dummy()
  282. for i in self.limits}
  283. function = function.xreplace(reps)
  284. isyms = function.free_symbols
  285. for xab in limits:
  286. v = reps[xab[0]]
  287. if len(xab) == 1:
  288. isyms.add(v)
  289. continue
  290. # take out the target symbol
  291. if v in isyms:
  292. isyms.remove(v)
  293. # add in the new symbols
  294. for i in xab[1:]:
  295. isyms.update(i.free_symbols)
  296. reps = {v: k for k, v in reps.items()}
  297. return {reps.get(_, _) for _ in isyms}
  298. @property
  299. def is_number(self):
  300. """Return True if the Sum has no free symbols, else False."""
  301. return not self.free_symbols
  302. def _eval_interval(self, x, a, b):
  303. limits = [(i if i[0] != x else (x, a, b)) for i in self.limits]
  304. integrand = self.function
  305. return self.func(integrand, *limits)
  306. def _eval_subs(self, old, new):
  307. """
  308. Perform substitutions over non-dummy variables
  309. of an expression with limits. Also, can be used
  310. to specify point-evaluation of an abstract antiderivative.
  311. Examples
  312. ========
  313. >>> from sympy import Sum, oo
  314. >>> from sympy.abc import s, n
  315. >>> Sum(1/n**s, (n, 1, oo)).subs(s, 2)
  316. Sum(n**(-2), (n, 1, oo))
  317. >>> from sympy import Integral
  318. >>> from sympy.abc import x, a
  319. >>> Integral(a*x**2, x).subs(x, 4)
  320. Integral(a*x**2, (x, 4))
  321. See Also
  322. ========
  323. variables : Lists the integration variables
  324. transform : Perform mapping on the dummy variable for integrals
  325. change_index : Perform mapping on the sum and product dummy variables
  326. """
  327. func, limits = self.function, list(self.limits)
  328. # If one of the expressions we are replacing is used as a func index
  329. # one of two things happens.
  330. # - the old variable first appears as a free variable
  331. # so we perform all free substitutions before it becomes
  332. # a func index.
  333. # - the old variable first appears as a func index, in
  334. # which case we ignore. See change_index.
  335. # Reorder limits to match standard mathematical practice for scoping
  336. limits.reverse()
  337. if not isinstance(old, Symbol) or \
  338. old.free_symbols.intersection(self.free_symbols):
  339. sub_into_func = True
  340. for i, xab in enumerate(limits):
  341. if 1 == len(xab) and old == xab[0]:
  342. if new._diff_wrt:
  343. xab = (new,)
  344. else:
  345. xab = (old, old)
  346. limits[i] = Tuple(xab[0], *[l._subs(old, new) for l in xab[1:]])
  347. if len(xab[0].free_symbols.intersection(old.free_symbols)) != 0:
  348. sub_into_func = False
  349. break
  350. if isinstance(old, (AppliedUndef, UndefinedFunction)):
  351. sy2 = set(self.variables).intersection(set(new.atoms(Symbol)))
  352. sy1 = set(self.variables).intersection(set(old.args))
  353. if not sy2.issubset(sy1):
  354. raise ValueError(
  355. "substitution cannot create dummy dependencies")
  356. sub_into_func = True
  357. if sub_into_func:
  358. func = func.subs(old, new)
  359. else:
  360. # old is a Symbol and a dummy variable of some limit
  361. for i, xab in enumerate(limits):
  362. if len(xab) == 3:
  363. limits[i] = Tuple(xab[0], *[l._subs(old, new) for l in xab[1:]])
  364. if old == xab[0]:
  365. break
  366. # simplify redundant limits (x, x) to (x, )
  367. for i, xab in enumerate(limits):
  368. if len(xab) == 2 and (xab[0] - xab[1]).is_zero:
  369. limits[i] = Tuple(xab[0], )
  370. # Reorder limits back to representation-form
  371. limits.reverse()
  372. return self.func(func, *limits)
  373. @property
  374. def has_finite_limits(self):
  375. """
  376. Returns True if the limits are known to be finite, either by the
  377. explicit bounds, assumptions on the bounds, or assumptions on the
  378. variables. False if known to be infinite, based on the bounds.
  379. None if not enough information is available to determine.
  380. Examples
  381. ========
  382. >>> from sympy import Sum, Integral, Product, oo, Symbol
  383. >>> x = Symbol('x')
  384. >>> Sum(x, (x, 1, 8)).has_finite_limits
  385. True
  386. >>> Integral(x, (x, 1, oo)).has_finite_limits
  387. False
  388. >>> M = Symbol('M')
  389. >>> Sum(x, (x, 1, M)).has_finite_limits
  390. >>> N = Symbol('N', integer=True)
  391. >>> Product(x, (x, 1, N)).has_finite_limits
  392. True
  393. See Also
  394. ========
  395. has_reversed_limits
  396. """
  397. ret_None = False
  398. for lim in self.limits:
  399. if len(lim) == 3:
  400. if any(l.is_infinite for l in lim[1:]):
  401. # Any of the bounds are +/-oo
  402. return False
  403. elif any(l.is_infinite is None for l in lim[1:]):
  404. # Maybe there are assumptions on the variable?
  405. if lim[0].is_infinite is None:
  406. ret_None = True
  407. else:
  408. if lim[0].is_infinite is None:
  409. ret_None = True
  410. if ret_None:
  411. return None
  412. return True
  413. @property
  414. def has_reversed_limits(self):
  415. """
  416. Returns True if the limits are known to be in reversed order, either
  417. by the explicit bounds, assumptions on the bounds, or assumptions on the
  418. variables. False if known to be in normal order, based on the bounds.
  419. None if not enough information is available to determine.
  420. Examples
  421. ========
  422. >>> from sympy import Sum, Integral, Product, oo, Symbol
  423. >>> x = Symbol('x')
  424. >>> Sum(x, (x, 8, 1)).has_reversed_limits
  425. True
  426. >>> Sum(x, (x, 1, oo)).has_reversed_limits
  427. False
  428. >>> M = Symbol('M')
  429. >>> Integral(x, (x, 1, M)).has_reversed_limits
  430. >>> N = Symbol('N', integer=True, positive=True)
  431. >>> Sum(x, (x, 1, N)).has_reversed_limits
  432. False
  433. >>> Product(x, (x, 2, N)).has_reversed_limits
  434. >>> Product(x, (x, 2, N)).subs(N, N + 2).has_reversed_limits
  435. False
  436. See Also
  437. ========
  438. sympy.concrete.expr_with_intlimits.ExprWithIntLimits.has_empty_sequence
  439. """
  440. ret_None = False
  441. for lim in self.limits:
  442. if len(lim) == 3:
  443. var, a, b = lim
  444. dif = b - a
  445. if dif.is_extended_negative:
  446. return True
  447. elif dif.is_extended_nonnegative:
  448. continue
  449. else:
  450. ret_None = True
  451. else:
  452. return None
  453. if ret_None:
  454. return None
  455. return False
  456. class AddWithLimits(ExprWithLimits):
  457. r"""Represents unevaluated oriented additions.
  458. Parent class for Integral and Sum.
  459. """
  460. __slots__ = ()
  461. def __new__(cls, function, *symbols, **assumptions):
  462. from sympy.concrete.summations import Sum
  463. pre = _common_new(cls, function, *symbols,
  464. discrete=issubclass(cls, Sum), **assumptions)
  465. if isinstance(pre, tuple):
  466. function, limits, orientation = pre
  467. else:
  468. return pre
  469. obj = Expr.__new__(cls, **assumptions)
  470. arglist = [orientation*function] # orientation not used in ExprWithLimits
  471. arglist.extend(limits)
  472. obj._args = tuple(arglist)
  473. obj.is_commutative = function.is_commutative # limits already checked
  474. return obj
  475. def _eval_adjoint(self):
  476. if all(x.is_real for x in flatten(self.limits)):
  477. return self.func(self.function.adjoint(), *self.limits)
  478. return None
  479. def _eval_conjugate(self):
  480. if all(x.is_real for x in flatten(self.limits)):
  481. return self.func(self.function.conjugate(), *self.limits)
  482. return None
  483. def _eval_transpose(self):
  484. if all(x.is_real for x in flatten(self.limits)):
  485. return self.func(self.function.transpose(), *self.limits)
  486. return None
  487. def _eval_factor(self, **hints):
  488. if 1 == len(self.limits):
  489. summand = self.function.factor(**hints)
  490. if summand.is_Mul:
  491. out = sift(summand.args, lambda w: w.is_commutative \
  492. and not set(self.variables) & w.free_symbols)
  493. return Mul(*out[True])*self.func(Mul(*out[False]), \
  494. *self.limits)
  495. else:
  496. summand = self.func(self.function, *self.limits[0:-1]).factor()
  497. if not summand.has(self.variables[-1]):
  498. return self.func(1, [self.limits[-1]]).doit()*summand
  499. elif isinstance(summand, Mul):
  500. return self.func(summand, self.limits[-1]).factor()
  501. return self
  502. def _eval_expand_basic(self, **hints):
  503. summand = self.function.expand(**hints)
  504. force = hints.get('force', False)
  505. if (summand.is_Add and (force or summand.is_commutative and
  506. self.has_finite_limits is not False)):
  507. return Add(*[self.func(i, *self.limits) for i in summand.args])
  508. elif isinstance(summand, MatrixBase):
  509. return summand.applyfunc(lambda x: self.func(x, *self.limits))
  510. elif summand != self.function:
  511. return self.func(summand, *self.limits)
  512. return self