Coverage for /usr/lib/python3/dist-packages/sympy/concrete/summations.py: 8%

808 statements  

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

1from typing import Tuple as tTuple 

2 

3from sympy.calculus.singularities import is_decreasing 

4from sympy.calculus.accumulationbounds import AccumulationBounds 

5from .expr_with_intlimits import ExprWithIntLimits 

6from .expr_with_limits import AddWithLimits 

7from .gosper import gosper_sum 

8from sympy.core.expr import Expr 

9from sympy.core.add import Add 

10from sympy.core.containers import Tuple 

11from sympy.core.function import Derivative, expand 

12from sympy.core.mul import Mul 

13from sympy.core.numbers import Float, _illegal 

14from sympy.core.relational import Eq 

15from sympy.core.singleton import S 

16from sympy.core.sorting import ordered 

17from sympy.core.symbol import Dummy, Wild, Symbol, symbols 

18from sympy.functions.combinatorial.factorials import factorial 

19from sympy.functions.combinatorial.numbers import bernoulli, harmonic 

20from sympy.functions.elementary.exponential import exp, log 

21from sympy.functions.elementary.piecewise import Piecewise 

22from sympy.functions.elementary.trigonometric import cot, csc 

23from sympy.functions.special.hyper import hyper 

24from sympy.functions.special.tensor_functions import KroneckerDelta 

25from sympy.functions.special.zeta_functions import zeta 

26from sympy.integrals.integrals import Integral 

27from sympy.logic.boolalg import And 

28from sympy.polys.partfrac import apart 

29from sympy.polys.polyerrors import PolynomialError, PolificationFailed 

30from sympy.polys.polytools import parallel_poly_from_expr, Poly, factor 

31from sympy.polys.rationaltools import together 

32from sympy.series.limitseq import limit_seq 

33from sympy.series.order import O 

34from sympy.series.residues import residue 

35from sympy.sets.sets import FiniteSet, Interval 

36from sympy.utilities.iterables import sift 

37import itertools 

38 

39 

40class Sum(AddWithLimits, ExprWithIntLimits): 

41 r""" 

42 Represents unevaluated summation. 

43 

44 Explanation 

45 =========== 

46 

47 ``Sum`` represents a finite or infinite series, with the first argument 

48 being the general form of terms in the series, and the second argument 

49 being ``(dummy_variable, start, end)``, with ``dummy_variable`` taking 

50 all integer values from ``start`` through ``end``. In accordance with 

51 long-standing mathematical convention, the end term is included in the 

52 summation. 

53 

54 Finite sums 

55 =========== 

56 

57 For finite sums (and sums with symbolic limits assumed to be finite) we 

58 follow the summation convention described by Karr [1], especially 

59 definition 3 of section 1.4. The sum: 

60 

61 .. math:: 

62 

63 \sum_{m \leq i < n} f(i) 

64 

65 has *the obvious meaning* for `m < n`, namely: 

66 

67 .. math:: 

68 

69 \sum_{m \leq i < n} f(i) = f(m) + f(m+1) + \ldots + f(n-2) + f(n-1) 

70 

71 with the upper limit value `f(n)` excluded. The sum over an empty set is 

72 zero if and only if `m = n`: 

73 

74 .. math:: 

75 

76 \sum_{m \leq i < n} f(i) = 0 \quad \mathrm{for} \quad m = n 

77 

78 Finally, for all other sums over empty sets we assume the following 

79 definition: 

80 

81 .. math:: 

82 

83 \sum_{m \leq i < n} f(i) = - \sum_{n \leq i < m} f(i) \quad \mathrm{for} \quad m > n 

84 

85 It is important to note that Karr defines all sums with the upper 

86 limit being exclusive. This is in contrast to the usual mathematical notation, 

87 but does not affect the summation convention. Indeed we have: 

88 

89 .. math:: 

90 

91 \sum_{m \leq i < n} f(i) = \sum_{i = m}^{n - 1} f(i) 

92 

93 where the difference in notation is intentional to emphasize the meaning, 

94 with limits typeset on the top being inclusive. 

95 

96 Examples 

97 ======== 

98 

99 >>> from sympy.abc import i, k, m, n, x 

100 >>> from sympy import Sum, factorial, oo, IndexedBase, Function 

101 >>> Sum(k, (k, 1, m)) 

102 Sum(k, (k, 1, m)) 

103 >>> Sum(k, (k, 1, m)).doit() 

104 m**2/2 + m/2 

105 >>> Sum(k**2, (k, 1, m)) 

106 Sum(k**2, (k, 1, m)) 

107 >>> Sum(k**2, (k, 1, m)).doit() 

108 m**3/3 + m**2/2 + m/6 

109 >>> Sum(x**k, (k, 0, oo)) 

110 Sum(x**k, (k, 0, oo)) 

111 >>> Sum(x**k, (k, 0, oo)).doit() 

112 Piecewise((1/(1 - x), Abs(x) < 1), (Sum(x**k, (k, 0, oo)), True)) 

113 >>> Sum(x**k/factorial(k), (k, 0, oo)).doit() 

114 exp(x) 

115 

116 Here are examples to do summation with symbolic indices. You 

117 can use either Function of IndexedBase classes: 

118 

119 >>> f = Function('f') 

120 >>> Sum(f(n), (n, 0, 3)).doit() 

121 f(0) + f(1) + f(2) + f(3) 

122 >>> Sum(f(n), (n, 0, oo)).doit() 

123 Sum(f(n), (n, 0, oo)) 

124 >>> f = IndexedBase('f') 

125 >>> Sum(f[n]**2, (n, 0, 3)).doit() 

126 f[0]**2 + f[1]**2 + f[2]**2 + f[3]**2 

127 

128 An example showing that the symbolic result of a summation is still 

129 valid for seemingly nonsensical values of the limits. Then the Karr 

130 convention allows us to give a perfectly valid interpretation to 

131 those sums by interchanging the limits according to the above rules: 

132 

133 >>> S = Sum(i, (i, 1, n)).doit() 

134 >>> S 

135 n**2/2 + n/2 

136 >>> S.subs(n, -4) 

137 6 

138 >>> Sum(i, (i, 1, -4)).doit() 

139 6 

140 >>> Sum(-i, (i, -3, 0)).doit() 

141 6 

142 

143 An explicit example of the Karr summation convention: 

144 

145 >>> S1 = Sum(i**2, (i, m, m+n-1)).doit() 

146 >>> S1 

147 m**2*n + m*n**2 - m*n + n**3/3 - n**2/2 + n/6 

148 >>> S2 = Sum(i**2, (i, m+n, m-1)).doit() 

149 >>> S2 

150 -m**2*n - m*n**2 + m*n - n**3/3 + n**2/2 - n/6 

151 >>> S1 + S2 

152 0 

153 >>> S3 = Sum(i, (i, m, m-1)).doit() 

154 >>> S3 

155 0 

156 

157 See Also 

158 ======== 

159 

160 summation 

161 Product, sympy.concrete.products.product 

162 

163 References 

164 ========== 

165 

166 .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, 

167 Volume 28 Issue 2, April 1981, Pages 305-350 

168 https://dl.acm.org/doi/10.1145/322248.322255 

169 .. [2] https://en.wikipedia.org/wiki/Summation#Capital-sigma_notation 

170 .. [3] https://en.wikipedia.org/wiki/Empty_sum 

171 """ 

172 

173 __slots__ = () 

174 

175 limits: tTuple[tTuple[Symbol, Expr, Expr]] 

176 

177 def __new__(cls, function, *symbols, **assumptions): 

178 obj = AddWithLimits.__new__(cls, function, *symbols, **assumptions) 

179 if not hasattr(obj, 'limits'): 

180 return obj 

181 if any(len(l) != 3 or None in l for l in obj.limits): 

182 raise ValueError('Sum requires values for lower and upper bounds.') 

183 

184 return obj 

185 

186 def _eval_is_zero(self): 

187 # a Sum is only zero if its function is zero or if all terms 

188 # cancel out. This only answers whether the summand is zero; if 

189 # not then None is returned since we don't analyze whether all 

190 # terms cancel out. 

191 if self.function.is_zero or self.has_empty_sequence: 

192 return True 

193 

194 def _eval_is_extended_real(self): 

195 if self.has_empty_sequence: 

196 return True 

197 return self.function.is_extended_real 

198 

199 def _eval_is_positive(self): 

200 if self.has_finite_limits and self.has_reversed_limits is False: 

201 return self.function.is_positive 

202 

203 def _eval_is_negative(self): 

204 if self.has_finite_limits and self.has_reversed_limits is False: 

205 return self.function.is_negative 

206 

207 def _eval_is_finite(self): 

208 if self.has_finite_limits and self.function.is_finite: 

209 return True 

210 

211 def doit(self, **hints): 

212 if hints.get('deep', True): 

213 f = self.function.doit(**hints) 

214 else: 

215 f = self.function 

216 

217 # first make sure any definite limits have summation 

218 # variables with matching assumptions 

219 reps = {} 

220 for xab in self.limits: 

221 d = _dummy_with_inherited_properties_concrete(xab) 

222 if d: 

223 reps[xab[0]] = d 

224 if reps: 

225 undo = {v: k for k, v in reps.items()} 

226 did = self.xreplace(reps).doit(**hints) 

227 if isinstance(did, tuple): # when separate=True 

228 did = tuple([i.xreplace(undo) for i in did]) 

229 elif did is not None: 

230 did = did.xreplace(undo) 

231 else: 

232 did = self 

233 return did 

234 

235 

236 if self.function.is_Matrix: 

237 expanded = self.expand() 

238 if self != expanded: 

239 return expanded.doit() 

240 return _eval_matrix_sum(self) 

241 

242 for n, limit in enumerate(self.limits): 

243 i, a, b = limit 

244 dif = b - a 

245 if dif == -1: 

246 # Any summation over an empty set is zero 

247 return S.Zero 

248 if dif.is_integer and dif.is_negative: 

249 a, b = b + 1, a - 1 

250 f = -f 

251 

252 newf = eval_sum(f, (i, a, b)) 

253 if newf is None: 

254 if f == self.function: 

255 zeta_function = self.eval_zeta_function(f, (i, a, b)) 

256 if zeta_function is not None: 

257 return zeta_function 

258 return self 

259 else: 

260 return self.func(f, *self.limits[n:]) 

261 f = newf 

262 

263 if hints.get('deep', True): 

264 # eval_sum could return partially unevaluated 

265 # result with Piecewise. In this case we won't 

266 # doit() recursively. 

267 if not isinstance(f, Piecewise): 

268 return f.doit(**hints) 

269 

270 return f 

271 

272 def eval_zeta_function(self, f, limits): 

273 """ 

274 Check whether the function matches with the zeta function. 

275 

276 If it matches, then return a `Piecewise` expression because 

277 zeta function does not converge unless `s > 1` and `q > 0` 

278 """ 

279 i, a, b = limits 

280 w, y, z = Wild('w', exclude=[i]), Wild('y', exclude=[i]), Wild('z', exclude=[i]) 

281 result = f.match((w * i + y) ** (-z)) 

282 if result is not None and b is S.Infinity: 

283 coeff = 1 / result[w] ** result[z] 

284 s = result[z] 

285 q = result[y] / result[w] + a 

286 return Piecewise((coeff * zeta(s, q), And(q > 0, s > 1)), (self, True)) 

287 

288 def _eval_derivative(self, x): 

289 """ 

290 Differentiate wrt x as long as x is not in the free symbols of any of 

291 the upper or lower limits. 

292 

293 Explanation 

294 =========== 

295 

296 Sum(a*b*x, (x, 1, a)) can be differentiated wrt x or b but not `a` 

297 since the value of the sum is discontinuous in `a`. In a case 

298 involving a limit variable, the unevaluated derivative is returned. 

299 """ 

300 

301 # diff already confirmed that x is in the free symbols of self, but we 

302 # don't want to differentiate wrt any free symbol in the upper or lower 

303 # limits 

304 # XXX remove this test for free_symbols when the default _eval_derivative is in 

305 if isinstance(x, Symbol) and x not in self.free_symbols: 

306 return S.Zero 

307 

308 # get limits and the function 

309 f, limits = self.function, list(self.limits) 

310 

311 limit = limits.pop(-1) 

312 

313 if limits: # f is the argument to a Sum 

314 f = self.func(f, *limits) 

315 

316 _, a, b = limit 

317 if x in a.free_symbols or x in b.free_symbols: 

318 return None 

319 df = Derivative(f, x, evaluate=True) 

320 rv = self.func(df, limit) 

321 return rv 

322 

323 def _eval_difference_delta(self, n, step): 

324 k, _, upper = self.args[-1] 

325 new_upper = upper.subs(n, n + step) 

326 

327 if len(self.args) == 2: 

328 f = self.args[0] 

329 else: 

330 f = self.func(*self.args[:-1]) 

331 

332 return Sum(f, (k, upper + 1, new_upper)).doit() 

333 

334 def _eval_simplify(self, **kwargs): 

335 

336 function = self.function 

337 

338 if kwargs.get('deep', True): 

339 function = function.simplify(**kwargs) 

340 

341 # split the function into adds 

342 terms = Add.make_args(expand(function)) 

343 s_t = [] # Sum Terms 

344 o_t = [] # Other Terms 

345 

346 for term in terms: 

347 if term.has(Sum): 

348 # if there is an embedded sum here 

349 # it is of the form x * (Sum(whatever)) 

350 # hence we make a Mul out of it, and simplify all interior sum terms 

351 subterms = Mul.make_args(expand(term)) 

352 out_terms = [] 

353 for subterm in subterms: 

354 # go through each term 

355 if isinstance(subterm, Sum): 

356 # if it's a sum, simplify it 

357 out_terms.append(subterm._eval_simplify(**kwargs)) 

358 else: 

359 # otherwise, add it as is 

360 out_terms.append(subterm) 

361 

362 # turn it back into a Mul 

363 s_t.append(Mul(*out_terms)) 

364 else: 

365 o_t.append(term) 

366 

367 # next try to combine any interior sums for further simplification 

368 from sympy.simplify.simplify import factor_sum, sum_combine 

369 result = Add(sum_combine(s_t), *o_t) 

370 

371 return factor_sum(result, limits=self.limits) 

372 

373 def is_convergent(self): 

374 r""" 

375 Checks for the convergence of a Sum. 

376 

377 Explanation 

378 =========== 

379 

380 We divide the study of convergence of infinite sums and products in 

381 two parts. 

382 

383 First Part: 

384 One part is the question whether all the terms are well defined, i.e., 

385 they are finite in a sum and also non-zero in a product. Zero 

386 is the analogy of (minus) infinity in products as 

387 :math:`e^{-\infty} = 0`. 

388 

389 Second Part: 

390 The second part is the question of convergence after infinities, 

391 and zeros in products, have been omitted assuming that their number 

392 is finite. This means that we only consider the tail of the sum or 

393 product, starting from some point after which all terms are well 

394 defined. 

395 

396 For example, in a sum of the form: 

397 

398 .. math:: 

399 

400 \sum_{1 \leq i < \infty} \frac{1}{n^2 + an + b} 

401 

402 where a and b are numbers. The routine will return true, even if there 

403 are infinities in the term sequence (at most two). An analogous 

404 product would be: 

405 

406 .. math:: 

407 

408 \prod_{1 \leq i < \infty} e^{\frac{1}{n^2 + an + b}} 

409 

410 This is how convergence is interpreted. It is concerned with what 

411 happens at the limit. Finding the bad terms is another independent 

412 matter. 

413 

414 Note: It is responsibility of user to see that the sum or product 

415 is well defined. 

416 

417 There are various tests employed to check the convergence like 

418 divergence test, root test, integral test, alternating series test, 

419 comparison tests, Dirichlet tests. It returns true if Sum is convergent 

420 and false if divergent and NotImplementedError if it cannot be checked. 

421 

422 References 

423 ========== 

424 

425 .. [1] https://en.wikipedia.org/wiki/Convergence_tests 

426 

427 Examples 

428 ======== 

429 

430 >>> from sympy import factorial, S, Sum, Symbol, oo 

431 >>> n = Symbol('n', integer=True) 

432 >>> Sum(n/(n - 1), (n, 4, 7)).is_convergent() 

433 True 

434 >>> Sum(n/(2*n + 1), (n, 1, oo)).is_convergent() 

435 False 

436 >>> Sum(factorial(n)/5**n, (n, 1, oo)).is_convergent() 

437 False 

438 >>> Sum(1/n**(S(6)/5), (n, 1, oo)).is_convergent() 

439 True 

440 

441 See Also 

442 ======== 

443 

444 Sum.is_absolutely_convergent 

445 sympy.concrete.products.Product.is_convergent 

446 """ 

447 p, q, r = symbols('p q r', cls=Wild) 

448 

449 sym = self.limits[0][0] 

450 lower_limit = self.limits[0][1] 

451 upper_limit = self.limits[0][2] 

452 sequence_term = self.function.simplify() 

453 

454 if len(sequence_term.free_symbols) > 1: 

455 raise NotImplementedError("convergence checking for more than one symbol " 

456 "containing series is not handled") 

457 

458 if lower_limit.is_finite and upper_limit.is_finite: 

459 return S.true 

460 

461 # transform sym -> -sym and swap the upper_limit = S.Infinity 

462 # and lower_limit = - upper_limit 

463 if lower_limit is S.NegativeInfinity: 

464 if upper_limit is S.Infinity: 

465 return Sum(sequence_term, (sym, 0, S.Infinity)).is_convergent() and \ 

466 Sum(sequence_term, (sym, S.NegativeInfinity, 0)).is_convergent() 

467 from sympy.simplify.simplify import simplify 

468 sequence_term = simplify(sequence_term.xreplace({sym: -sym})) 

469 lower_limit = -upper_limit 

470 upper_limit = S.Infinity 

471 

472 sym_ = Dummy(sym.name, integer=True, positive=True) 

473 sequence_term = sequence_term.xreplace({sym: sym_}) 

474 sym = sym_ 

475 

476 interval = Interval(lower_limit, upper_limit) 

477 

478 # Piecewise function handle 

479 if sequence_term.is_Piecewise: 

480 for func, cond in sequence_term.args: 

481 # see if it represents something going to oo 

482 if cond == True or cond.as_set().sup is S.Infinity: 

483 s = Sum(func, (sym, lower_limit, upper_limit)) 

484 return s.is_convergent() 

485 return S.true 

486 

487 ### -------- Divergence test ----------- ### 

488 try: 

489 lim_val = limit_seq(sequence_term, sym) 

490 if lim_val is not None and lim_val.is_zero is False: 

491 return S.false 

492 except NotImplementedError: 

493 pass 

494 

495 try: 

496 lim_val_abs = limit_seq(abs(sequence_term), sym) 

497 if lim_val_abs is not None and lim_val_abs.is_zero is False: 

498 return S.false 

499 except NotImplementedError: 

500 pass 

501 

502 order = O(sequence_term, (sym, S.Infinity)) 

503 

504 ### --------- p-series test (1/n**p) ---------- ### 

505 p_series_test = order.expr.match(sym**p) 

506 if p_series_test is not None: 

507 if p_series_test[p] < -1: 

508 return S.true 

509 if p_series_test[p] >= -1: 

510 return S.false 

511 

512 ### ------------- comparison test ------------- ### 

513 # 1/(n**p*log(n)**q*log(log(n))**r) comparison 

514 n_log_test = (order.expr.match(1/(sym**p*log(1/sym)**q*log(-log(1/sym))**r)) or 

515 order.expr.match(1/(sym**p*(-log(1/sym))**q*log(-log(1/sym))**r))) 

516 if n_log_test is not None: 

517 if (n_log_test[p] > 1 or 

518 (n_log_test[p] == 1 and n_log_test[q] > 1) or 

519 (n_log_test[p] == n_log_test[q] == 1 and n_log_test[r] > 1)): 

520 return S.true 

521 return S.false 

522 

523 ### ------------- Limit comparison test -----------### 

524 # (1/n) comparison 

525 try: 

526 lim_comp = limit_seq(sym*sequence_term, sym) 

527 if lim_comp is not None and lim_comp.is_number and lim_comp > 0: 

528 return S.false 

529 except NotImplementedError: 

530 pass 

531 

532 ### ----------- ratio test ---------------- ### 

533 next_sequence_term = sequence_term.xreplace({sym: sym + 1}) 

534 from sympy.simplify.combsimp import combsimp 

535 from sympy.simplify.powsimp import powsimp 

536 ratio = combsimp(powsimp(next_sequence_term/sequence_term)) 

537 try: 

538 lim_ratio = limit_seq(ratio, sym) 

539 if lim_ratio is not None and lim_ratio.is_number: 

540 if abs(lim_ratio) > 1: 

541 return S.false 

542 if abs(lim_ratio) < 1: 

543 return S.true 

544 except NotImplementedError: 

545 lim_ratio = None 

546 

547 ### ---------- Raabe's test -------------- ### 

548 if lim_ratio == 1: # ratio test inconclusive 

549 test_val = sym*(sequence_term/ 

550 sequence_term.subs(sym, sym + 1) - 1) 

551 test_val = test_val.gammasimp() 

552 try: 

553 lim_val = limit_seq(test_val, sym) 

554 if lim_val is not None and lim_val.is_number: 

555 if lim_val > 1: 

556 return S.true 

557 if lim_val < 1: 

558 return S.false 

559 except NotImplementedError: 

560 pass 

561 

562 ### ----------- root test ---------------- ### 

563 # lim = Limit(abs(sequence_term)**(1/sym), sym, S.Infinity) 

564 try: 

565 lim_evaluated = limit_seq(abs(sequence_term)**(1/sym), sym) 

566 if lim_evaluated is not None and lim_evaluated.is_number: 

567 if lim_evaluated < 1: 

568 return S.true 

569 if lim_evaluated > 1: 

570 return S.false 

571 except NotImplementedError: 

572 pass 

573 

574 ### ------------- alternating series test ----------- ### 

575 dict_val = sequence_term.match(S.NegativeOne**(sym + p)*q) 

576 if not dict_val[p].has(sym) and is_decreasing(dict_val[q], interval): 

577 return S.true 

578 

579 ### ------------- integral test -------------- ### 

580 check_interval = None 

581 from sympy.solvers.solveset import solveset 

582 maxima = solveset(sequence_term.diff(sym), sym, interval) 

583 if not maxima: 

584 check_interval = interval 

585 elif isinstance(maxima, FiniteSet) and maxima.sup.is_number: 

586 check_interval = Interval(maxima.sup, interval.sup) 

587 if (check_interval is not None and 

588 (is_decreasing(sequence_term, check_interval) or 

589 is_decreasing(-sequence_term, check_interval))): 

590 integral_val = Integral( 

591 sequence_term, (sym, lower_limit, upper_limit)) 

592 try: 

593 integral_val_evaluated = integral_val.doit() 

594 if integral_val_evaluated.is_number: 

595 return S(integral_val_evaluated.is_finite) 

596 except NotImplementedError: 

597 pass 

598 

599 ### ----- Dirichlet and bounded times convergent tests ----- ### 

600 # TODO 

601 # 

602 # Dirichlet_test 

603 # https://en.wikipedia.org/wiki/Dirichlet%27s_test 

604 # 

605 # Bounded times convergent test 

606 # It is based on comparison theorems for series. 

607 # In particular, if the general term of a series can 

608 # be written as a product of two terms a_n and b_n 

609 # and if a_n is bounded and if Sum(b_n) is absolutely 

610 # convergent, then the original series Sum(a_n * b_n) 

611 # is absolutely convergent and so convergent. 

612 # 

613 # The following code can grows like 2**n where n is the 

614 # number of args in order.expr 

615 # Possibly combined with the potentially slow checks 

616 # inside the loop, could make this test extremely slow 

617 # for larger summation expressions. 

618 

619 if order.expr.is_Mul: 

620 args = order.expr.args 

621 argset = set(args) 

622 

623 ### -------------- Dirichlet tests -------------- ### 

624 m = Dummy('m', integer=True) 

625 def _dirichlet_test(g_n): 

626 try: 

627 ing_val = limit_seq(Sum(g_n, (sym, interval.inf, m)).doit(), m) 

628 if ing_val is not None and ing_val.is_finite: 

629 return S.true 

630 except NotImplementedError: 

631 pass 

632 

633 ### -------- bounded times convergent test ---------### 

634 def _bounded_convergent_test(g1_n, g2_n): 

635 try: 

636 lim_val = limit_seq(g1_n, sym) 

637 if lim_val is not None and (lim_val.is_finite or ( 

638 isinstance(lim_val, AccumulationBounds) 

639 and (lim_val.max - lim_val.min).is_finite)): 

640 if Sum(g2_n, (sym, lower_limit, upper_limit)).is_absolutely_convergent(): 

641 return S.true 

642 except NotImplementedError: 

643 pass 

644 

645 for n in range(1, len(argset)): 

646 for a_tuple in itertools.combinations(args, n): 

647 b_set = argset - set(a_tuple) 

648 a_n = Mul(*a_tuple) 

649 b_n = Mul(*b_set) 

650 

651 if is_decreasing(a_n, interval): 

652 dirich = _dirichlet_test(b_n) 

653 if dirich is not None: 

654 return dirich 

655 

656 bc_test = _bounded_convergent_test(a_n, b_n) 

657 if bc_test is not None: 

658 return bc_test 

659 

660 _sym = self.limits[0][0] 

661 sequence_term = sequence_term.xreplace({sym: _sym}) 

662 raise NotImplementedError("The algorithm to find the Sum convergence of %s " 

663 "is not yet implemented" % (sequence_term)) 

664 

665 def is_absolutely_convergent(self): 

666 """ 

667 Checks for the absolute convergence of an infinite series. 

668 

669 Same as checking convergence of absolute value of sequence_term of 

670 an infinite series. 

671 

672 References 

673 ========== 

674 

675 .. [1] https://en.wikipedia.org/wiki/Absolute_convergence 

676 

677 Examples 

678 ======== 

679 

680 >>> from sympy import Sum, Symbol, oo 

681 >>> n = Symbol('n', integer=True) 

682 >>> Sum((-1)**n, (n, 1, oo)).is_absolutely_convergent() 

683 False 

684 >>> Sum((-1)**n/n**2, (n, 1, oo)).is_absolutely_convergent() 

685 True 

686 

687 See Also 

688 ======== 

689 

690 Sum.is_convergent 

691 """ 

692 return Sum(abs(self.function), self.limits).is_convergent() 

693 

694 def euler_maclaurin(self, m=0, n=0, eps=0, eval_integral=True): 

695 """ 

696 Return an Euler-Maclaurin approximation of self, where m is the 

697 number of leading terms to sum directly and n is the number of 

698 terms in the tail. 

699 

700 With m = n = 0, this is simply the corresponding integral 

701 plus a first-order endpoint correction. 

702 

703 Returns (s, e) where s is the Euler-Maclaurin approximation 

704 and e is the estimated error (taken to be the magnitude of 

705 the first omitted term in the tail): 

706 

707 >>> from sympy.abc import k, a, b 

708 >>> from sympy import Sum 

709 >>> Sum(1/k, (k, 2, 5)).doit().evalf() 

710 1.28333333333333 

711 >>> s, e = Sum(1/k, (k, 2, 5)).euler_maclaurin() 

712 >>> s 

713 -log(2) + 7/20 + log(5) 

714 >>> from sympy import sstr 

715 >>> print(sstr((s.evalf(), e.evalf()), full_prec=True)) 

716 (1.26629073187415, 0.0175000000000000) 

717 

718 The endpoints may be symbolic: 

719 

720 >>> s, e = Sum(1/k, (k, a, b)).euler_maclaurin() 

721 >>> s 

722 -log(a) + log(b) + 1/(2*b) + 1/(2*a) 

723 >>> e 

724 Abs(1/(12*b**2) - 1/(12*a**2)) 

725 

726 If the function is a polynomial of degree at most 2n+1, the 

727 Euler-Maclaurin formula becomes exact (and e = 0 is returned): 

728 

729 >>> Sum(k, (k, 2, b)).euler_maclaurin() 

730 (b**2/2 + b/2 - 1, 0) 

731 >>> Sum(k, (k, 2, b)).doit() 

732 b**2/2 + b/2 - 1 

733 

734 With a nonzero eps specified, the summation is ended 

735 as soon as the remainder term is less than the epsilon. 

736 """ 

737 m = int(m) 

738 n = int(n) 

739 f = self.function 

740 if len(self.limits) != 1: 

741 raise ValueError("More than 1 limit") 

742 i, a, b = self.limits[0] 

743 if (a > b) == True: 

744 if a - b == 1: 

745 return S.Zero, S.Zero 

746 a, b = b + 1, a - 1 

747 f = -f 

748 s = S.Zero 

749 if m: 

750 if b.is_Integer and a.is_Integer: 

751 m = min(m, b - a + 1) 

752 if not eps or f.is_polynomial(i): 

753 s = Add(*[f.subs(i, a + k) for k in range(m)]) 

754 else: 

755 term = f.subs(i, a) 

756 if term: 

757 test = abs(term.evalf(3)) < eps 

758 if test == True: 

759 return s, abs(term) 

760 elif not (test == False): 

761 # a symbolic Relational class, can't go further 

762 return term, S.Zero 

763 s = term 

764 for k in range(1, m): 

765 term = f.subs(i, a + k) 

766 if abs(term.evalf(3)) < eps and term != 0: 

767 return s, abs(term) 

768 s += term 

769 if b - a + 1 == m: 

770 return s, S.Zero 

771 a += m 

772 x = Dummy('x') 

773 I = Integral(f.subs(i, x), (x, a, b)) 

774 if eval_integral: 

775 I = I.doit() 

776 s += I 

777 

778 def fpoint(expr): 

779 if b is S.Infinity: 

780 return expr.subs(i, a), 0 

781 return expr.subs(i, a), expr.subs(i, b) 

782 fa, fb = fpoint(f) 

783 iterm = (fa + fb)/2 

784 g = f.diff(i) 

785 for k in range(1, n + 2): 

786 ga, gb = fpoint(g) 

787 term = bernoulli(2*k)/factorial(2*k)*(gb - ga) 

788 if k > n: 

789 break 

790 if eps and term: 

791 term_evalf = term.evalf(3) 

792 if term_evalf is S.NaN: 

793 return S.NaN, S.NaN 

794 if abs(term_evalf) < eps: 

795 break 

796 s += term 

797 g = g.diff(i, 2, simplify=False) 

798 return s + iterm, abs(term) 

799 

800 

801 def reverse_order(self, *indices): 

802 """ 

803 Reverse the order of a limit in a Sum. 

804 

805 Explanation 

806 =========== 

807 

808 ``reverse_order(self, *indices)`` reverses some limits in the expression 

809 ``self`` which can be either a ``Sum`` or a ``Product``. The selectors in 

810 the argument ``indices`` specify some indices whose limits get reversed. 

811 These selectors are either variable names or numerical indices counted 

812 starting from the inner-most limit tuple. 

813 

814 Examples 

815 ======== 

816 

817 >>> from sympy import Sum 

818 >>> from sympy.abc import x, y, a, b, c, d 

819 

820 >>> Sum(x, (x, 0, 3)).reverse_order(x) 

821 Sum(-x, (x, 4, -1)) 

822 >>> Sum(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(x, y) 

823 Sum(x*y, (x, 6, 0), (y, 7, -1)) 

824 >>> Sum(x, (x, a, b)).reverse_order(x) 

825 Sum(-x, (x, b + 1, a - 1)) 

826 >>> Sum(x, (x, a, b)).reverse_order(0) 

827 Sum(-x, (x, b + 1, a - 1)) 

828 

829 While one should prefer variable names when specifying which limits 

830 to reverse, the index counting notation comes in handy in case there 

831 are several symbols with the same name. 

832 

833 >>> S = Sum(x**2, (x, a, b), (x, c, d)) 

834 >>> S 

835 Sum(x**2, (x, a, b), (x, c, d)) 

836 >>> S0 = S.reverse_order(0) 

837 >>> S0 

838 Sum(-x**2, (x, b + 1, a - 1), (x, c, d)) 

839 >>> S1 = S0.reverse_order(1) 

840 >>> S1 

841 Sum(x**2, (x, b + 1, a - 1), (x, d + 1, c - 1)) 

842 

843 Of course we can mix both notations: 

844 

845 >>> Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) 

846 Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) 

847 >>> Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) 

848 Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) 

849 

850 See Also 

851 ======== 

852 

853 sympy.concrete.expr_with_intlimits.ExprWithIntLimits.index, reorder_limit, 

854 sympy.concrete.expr_with_intlimits.ExprWithIntLimits.reorder 

855 

856 References 

857 ========== 

858 

859 .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, 

860 Volume 28 Issue 2, April 1981, Pages 305-350 

861 https://dl.acm.org/doi/10.1145/322248.322255 

862 """ 

863 l_indices = list(indices) 

864 

865 for i, indx in enumerate(l_indices): 

866 if not isinstance(indx, int): 

867 l_indices[i] = self.index(indx) 

868 

869 e = 1 

870 limits = [] 

871 for i, limit in enumerate(self.limits): 

872 l = limit 

873 if i in l_indices: 

874 e = -e 

875 l = (limit[0], limit[2] + 1, limit[1] - 1) 

876 limits.append(l) 

877 

878 return Sum(e * self.function, *limits) 

879 

880 def _eval_rewrite_as_Product(self, *args, **kwargs): 

881 from sympy.concrete.products import Product 

882 if self.function.is_extended_real: 

883 return log(Product(exp(self.function), *self.limits)) 

884 

885 

886def summation(f, *symbols, **kwargs): 

887 r""" 

888 Compute the summation of f with respect to symbols. 

889 

890 Explanation 

891 =========== 

892 

893 The notation for symbols is similar to the notation used in Integral. 

894 summation(f, (i, a, b)) computes the sum of f with respect to i from a to b, 

895 i.e., 

896 

897 :: 

898 

899 b 

900 ____ 

901 \ ` 

902 summation(f, (i, a, b)) = ) f 

903 /___, 

904 i = a 

905 

906 If it cannot compute the sum, it returns an unevaluated Sum object. 

907 Repeated sums can be computed by introducing additional symbols tuples:: 

908 

909 Examples 

910 ======== 

911 

912 >>> from sympy import summation, oo, symbols, log 

913 >>> i, n, m = symbols('i n m', integer=True) 

914 

915 >>> summation(2*i - 1, (i, 1, n)) 

916 n**2 

917 >>> summation(1/2**i, (i, 0, oo)) 

918 2 

919 >>> summation(1/log(n)**n, (n, 2, oo)) 

920 Sum(log(n)**(-n), (n, 2, oo)) 

921 >>> summation(i, (i, 0, n), (n, 0, m)) 

922 m**3/6 + m**2/2 + m/3 

923 

924 >>> from sympy.abc import x 

925 >>> from sympy import factorial 

926 >>> summation(x**n/factorial(n), (n, 0, oo)) 

927 exp(x) 

928 

929 See Also 

930 ======== 

931 

932 Sum 

933 Product, sympy.concrete.products.product 

934 

935 """ 

936 return Sum(f, *symbols, **kwargs).doit(deep=False) 

937 

938 

939def telescopic_direct(L, R, n, limits): 

940 """ 

941 Returns the direct summation of the terms of a telescopic sum 

942 

943 Explanation 

944 =========== 

945 

946 L is the term with lower index 

947 R is the term with higher index 

948 n difference between the indexes of L and R 

949 

950 Examples 

951 ======== 

952 

953 >>> from sympy.concrete.summations import telescopic_direct 

954 >>> from sympy.abc import k, a, b 

955 >>> telescopic_direct(1/k, -1/(k+2), 2, (k, a, b)) 

956 -1/(b + 2) - 1/(b + 1) + 1/(a + 1) + 1/a 

957 

958 """ 

959 (i, a, b) = limits 

960 return Add(*[L.subs(i, a + m) + R.subs(i, b - m) for m in range(n)]) 

961 

962 

963def telescopic(L, R, limits): 

964 ''' 

965 Tries to perform the summation using the telescopic property. 

966 

967 Return None if not possible. 

968 ''' 

969 (i, a, b) = limits 

970 if L.is_Add or R.is_Add: 

971 return None 

972 

973 # We want to solve(L.subs(i, i + m) + R, m) 

974 # First we try a simple match since this does things that 

975 # solve doesn't do, e.g. solve(cos(k+m)-cos(k), m) gives 

976 # a more complicated solution than m == 0. 

977 

978 k = Wild("k") 

979 sol = (-R).match(L.subs(i, i + k)) 

980 s = None 

981 if sol and k in sol: 

982 s = sol[k] 

983 if not (s.is_Integer and L.subs(i, i + s) + R == 0): 

984 # invalid match or match didn't work 

985 s = None 

986 

987 # But there are things that match doesn't do that solve 

988 # can do, e.g. determine that 1/(x + m) = 1/(1 - x) when m = 1 

989 

990 if s is None: 

991 m = Dummy('m') 

992 try: 

993 from sympy.solvers.solvers import solve 

994 sol = solve(L.subs(i, i + m) + R, m) or [] 

995 except NotImplementedError: 

996 return None 

997 sol = [si for si in sol if si.is_Integer and 

998 (L.subs(i, i + si) + R).expand().is_zero] 

999 if len(sol) != 1: 

1000 return None 

1001 s = sol[0] 

1002 

1003 if s < 0: 

1004 return telescopic_direct(R, L, abs(s), (i, a, b)) 

1005 elif s > 0: 

1006 return telescopic_direct(L, R, s, (i, a, b)) 

1007 

1008 

1009def eval_sum(f, limits): 

1010 (i, a, b) = limits 

1011 if f.is_zero: 

1012 return S.Zero 

1013 if i not in f.free_symbols: 

1014 return f*(b - a + 1) 

1015 if a == b: 

1016 return f.subs(i, a) 

1017 if isinstance(f, Piecewise): 

1018 if not any(i in arg.args[1].free_symbols for arg in f.args): 

1019 # Piecewise conditions do not depend on the dummy summation variable, 

1020 # therefore we can fold: Sum(Piecewise((e, c), ...), limits) 

1021 # --> Piecewise((Sum(e, limits), c), ...) 

1022 newargs = [] 

1023 for arg in f.args: 

1024 newexpr = eval_sum(arg.expr, limits) 

1025 if newexpr is None: 

1026 return None 

1027 newargs.append((newexpr, arg.cond)) 

1028 return f.func(*newargs) 

1029 

1030 if f.has(KroneckerDelta): 

1031 from .delta import deltasummation, _has_simple_delta 

1032 f = f.replace( 

1033 lambda x: isinstance(x, Sum), 

1034 lambda x: x.factor() 

1035 ) 

1036 if _has_simple_delta(f, limits[0]): 

1037 return deltasummation(f, limits) 

1038 

1039 dif = b - a 

1040 definite = dif.is_Integer 

1041 # Doing it directly may be faster if there are very few terms. 

1042 if definite and (dif < 100): 

1043 return eval_sum_direct(f, (i, a, b)) 

1044 if isinstance(f, Piecewise): 

1045 return None 

1046 # Try to do it symbolically. Even when the number of terms is 

1047 # known, this can save time when b-a is big. 

1048 value = eval_sum_symbolic(f.expand(), (i, a, b)) 

1049 if value is not None: 

1050 return value 

1051 # Do it directly 

1052 if definite: 

1053 return eval_sum_direct(f, (i, a, b)) 

1054 

1055 

1056def eval_sum_direct(expr, limits): 

1057 """ 

1058 Evaluate expression directly, but perform some simple checks first 

1059 to possibly result in a smaller expression and faster execution. 

1060 """ 

1061 (i, a, b) = limits 

1062 

1063 dif = b - a 

1064 # Linearity 

1065 if expr.is_Mul: 

1066 # Try factor out everything not including i 

1067 without_i, with_i = expr.as_independent(i) 

1068 if without_i != 1: 

1069 s = eval_sum_direct(with_i, (i, a, b)) 

1070 if s: 

1071 r = without_i*s 

1072 if r is not S.NaN: 

1073 return r 

1074 else: 

1075 # Try term by term 

1076 L, R = expr.as_two_terms() 

1077 

1078 if not L.has(i): 

1079 sR = eval_sum_direct(R, (i, a, b)) 

1080 if sR: 

1081 return L*sR 

1082 

1083 if not R.has(i): 

1084 sL = eval_sum_direct(L, (i, a, b)) 

1085 if sL: 

1086 return sL*R 

1087 

1088 # do this whether its an Add or Mul 

1089 # e.g. apart(1/(25*i**2 + 45*i + 14)) and 

1090 # apart(1/((5*i + 2)*(5*i + 7))) -> 

1091 # -1/(5*(5*i + 7)) + 1/(5*(5*i + 2)) 

1092 try: 

1093 expr = apart(expr, i) # see if it becomes an Add 

1094 except PolynomialError: 

1095 pass 

1096 

1097 if expr.is_Add: 

1098 # Try factor out everything not including i 

1099 without_i, with_i = expr.as_independent(i) 

1100 if without_i != 0: 

1101 s = eval_sum_direct(with_i, (i, a, b)) 

1102 if s: 

1103 r = without_i*(dif + 1) + s 

1104 if r is not S.NaN: 

1105 return r 

1106 else: 

1107 # Try term by term 

1108 L, R = expr.as_two_terms() 

1109 lsum = eval_sum_direct(L, (i, a, b)) 

1110 rsum = eval_sum_direct(R, (i, a, b)) 

1111 

1112 if None not in (lsum, rsum): 

1113 r = lsum + rsum 

1114 if r is not S.NaN: 

1115 return r 

1116 

1117 return Add(*[expr.subs(i, a + j) for j in range(dif + 1)]) 

1118 

1119 

1120def eval_sum_symbolic(f, limits): 

1121 f_orig = f 

1122 (i, a, b) = limits 

1123 if not f.has(i): 

1124 return f*(b - a + 1) 

1125 

1126 # Linearity 

1127 if f.is_Mul: 

1128 # Try factor out everything not including i 

1129 without_i, with_i = f.as_independent(i) 

1130 if without_i != 1: 

1131 s = eval_sum_symbolic(with_i, (i, a, b)) 

1132 if s: 

1133 r = without_i*s 

1134 if r is not S.NaN: 

1135 return r 

1136 else: 

1137 # Try term by term 

1138 L, R = f.as_two_terms() 

1139 

1140 if not L.has(i): 

1141 sR = eval_sum_symbolic(R, (i, a, b)) 

1142 if sR: 

1143 return L*sR 

1144 

1145 if not R.has(i): 

1146 sL = eval_sum_symbolic(L, (i, a, b)) 

1147 if sL: 

1148 return sL*R 

1149 

1150 # do this whether its an Add or Mul 

1151 # e.g. apart(1/(25*i**2 + 45*i + 14)) and 

1152 # apart(1/((5*i + 2)*(5*i + 7))) -> 

1153 # -1/(5*(5*i + 7)) + 1/(5*(5*i + 2)) 

1154 try: 

1155 f = apart(f, i) 

1156 except PolynomialError: 

1157 pass 

1158 

1159 if f.is_Add: 

1160 L, R = f.as_two_terms() 

1161 lrsum = telescopic(L, R, (i, a, b)) 

1162 

1163 if lrsum: 

1164 return lrsum 

1165 

1166 # Try factor out everything not including i 

1167 without_i, with_i = f.as_independent(i) 

1168 if without_i != 0: 

1169 s = eval_sum_symbolic(with_i, (i, a, b)) 

1170 if s: 

1171 r = without_i*(b - a + 1) + s 

1172 if r is not S.NaN: 

1173 return r 

1174 else: 

1175 # Try term by term 

1176 lsum = eval_sum_symbolic(L, (i, a, b)) 

1177 rsum = eval_sum_symbolic(R, (i, a, b)) 

1178 

1179 if None not in (lsum, rsum): 

1180 r = lsum + rsum 

1181 if r is not S.NaN: 

1182 return r 

1183 

1184 

1185 # Polynomial terms with Faulhaber's formula 

1186 n = Wild('n') 

1187 result = f.match(i**n) 

1188 

1189 if result is not None: 

1190 n = result[n] 

1191 

1192 if n.is_Integer: 

1193 if n >= 0: 

1194 if (b is S.Infinity and a is not S.NegativeInfinity) or \ 

1195 (a is S.NegativeInfinity and b is not S.Infinity): 

1196 return S.Infinity 

1197 return ((bernoulli(n + 1, b + 1) - bernoulli(n + 1, a))/(n + 1)).expand() 

1198 elif a.is_Integer and a >= 1: 

1199 if n == -1: 

1200 return harmonic(b) - harmonic(a - 1) 

1201 else: 

1202 return harmonic(b, abs(n)) - harmonic(a - 1, abs(n)) 

1203 

1204 if not (a.has(S.Infinity, S.NegativeInfinity) or 

1205 b.has(S.Infinity, S.NegativeInfinity)): 

1206 # Geometric terms 

1207 c1 = Wild('c1', exclude=[i]) 

1208 c2 = Wild('c2', exclude=[i]) 

1209 c3 = Wild('c3', exclude=[i]) 

1210 wexp = Wild('wexp') 

1211 

1212 # Here we first attempt powsimp on f for easier matching with the 

1213 # exponential pattern, and attempt expansion on the exponent for easier 

1214 # matching with the linear pattern. 

1215 e = f.powsimp().match(c1 ** wexp) 

1216 if e is not None: 

1217 e_exp = e.pop(wexp).expand().match(c2*i + c3) 

1218 if e_exp is not None: 

1219 e.update(e_exp) 

1220 

1221 p = (c1**c3).subs(e) 

1222 q = (c1**c2).subs(e) 

1223 r = p*(q**a - q**(b + 1))/(1 - q) 

1224 l = p*(b - a + 1) 

1225 return Piecewise((l, Eq(q, S.One)), (r, True)) 

1226 

1227 r = gosper_sum(f, (i, a, b)) 

1228 

1229 if isinstance(r, (Mul,Add)): 

1230 from sympy.simplify.radsimp import denom 

1231 from sympy.solvers.solvers import solve 

1232 non_limit = r.free_symbols - Tuple(*limits[1:]).free_symbols 

1233 den = denom(together(r)) 

1234 den_sym = non_limit & den.free_symbols 

1235 args = [] 

1236 for v in ordered(den_sym): 

1237 try: 

1238 s = solve(den, v) 

1239 m = Eq(v, s[0]) if s else S.false 

1240 if m != False: 

1241 args.append((Sum(f_orig.subs(*m.args), limits).doit(), m)) 

1242 break 

1243 except NotImplementedError: 

1244 continue 

1245 

1246 args.append((r, True)) 

1247 return Piecewise(*args) 

1248 

1249 if r not in (None, S.NaN): 

1250 return r 

1251 

1252 h = eval_sum_hyper(f_orig, (i, a, b)) 

1253 if h is not None: 

1254 return h 

1255 

1256 r = eval_sum_residue(f_orig, (i, a, b)) 

1257 if r is not None: 

1258 return r 

1259 

1260 factored = f_orig.factor() 

1261 if factored != f_orig: 

1262 return eval_sum_symbolic(factored, (i, a, b)) 

1263 

1264 

1265def _eval_sum_hyper(f, i, a): 

1266 """ Returns (res, cond). Sums from a to oo. """ 

1267 if a != 0: 

1268 return _eval_sum_hyper(f.subs(i, i + a), i, 0) 

1269 

1270 if f.subs(i, 0) == 0: 

1271 from sympy.simplify.simplify import simplify 

1272 if simplify(f.subs(i, Dummy('i', integer=True, positive=True))) == 0: 

1273 return S.Zero, True 

1274 return _eval_sum_hyper(f.subs(i, i + 1), i, 0) 

1275 

1276 from sympy.simplify.simplify import hypersimp 

1277 hs = hypersimp(f, i) 

1278 if hs is None: 

1279 return None 

1280 

1281 if isinstance(hs, Float): 

1282 from sympy.simplify.simplify import nsimplify 

1283 hs = nsimplify(hs) 

1284 

1285 from sympy.simplify.combsimp import combsimp 

1286 from sympy.simplify.hyperexpand import hyperexpand 

1287 from sympy.simplify.radsimp import fraction 

1288 numer, denom = fraction(factor(hs)) 

1289 top, topl = numer.as_coeff_mul(i) 

1290 bot, botl = denom.as_coeff_mul(i) 

1291 ab = [top, bot] 

1292 factors = [topl, botl] 

1293 params = [[], []] 

1294 for k in range(2): 

1295 for fac in factors[k]: 

1296 mul = 1 

1297 if fac.is_Pow: 

1298 mul = fac.exp 

1299 fac = fac.base 

1300 if not mul.is_Integer: 

1301 return None 

1302 p = Poly(fac, i) 

1303 if p.degree() != 1: 

1304 return None 

1305 m, n = p.all_coeffs() 

1306 ab[k] *= m**mul 

1307 params[k] += [n/m]*mul 

1308 

1309 # Add "1" to numerator parameters, to account for implicit n! in 

1310 # hypergeometric series. 

1311 ap = params[0] + [1] 

1312 bq = params[1] 

1313 x = ab[0]/ab[1] 

1314 h = hyper(ap, bq, x) 

1315 f = combsimp(f) 

1316 return f.subs(i, 0)*hyperexpand(h), h.convergence_statement 

1317 

1318 

1319def eval_sum_hyper(f, i_a_b): 

1320 i, a, b = i_a_b 

1321 

1322 if f.is_hypergeometric(i) is False: 

1323 return 

1324 

1325 if (b - a).is_Integer: 

1326 # We are never going to do better than doing the sum in the obvious way 

1327 return None 

1328 

1329 old_sum = Sum(f, (i, a, b)) 

1330 

1331 if b != S.Infinity: 

1332 if a is S.NegativeInfinity: 

1333 res = _eval_sum_hyper(f.subs(i, -i), i, -b) 

1334 if res is not None: 

1335 return Piecewise(res, (old_sum, True)) 

1336 else: 

1337 n_illegal = lambda x: sum(x.count(_) for _ in _illegal) 

1338 had = n_illegal(f) 

1339 # check that no extra illegals are introduced 

1340 res1 = _eval_sum_hyper(f, i, a) 

1341 if res1 is None or n_illegal(res1) > had: 

1342 return 

1343 res2 = _eval_sum_hyper(f, i, b + 1) 

1344 if res2 is None or n_illegal(res2) > had: 

1345 return 

1346 (res1, cond1), (res2, cond2) = res1, res2 

1347 cond = And(cond1, cond2) 

1348 if cond == False: 

1349 return None 

1350 return Piecewise((res1 - res2, cond), (old_sum, True)) 

1351 

1352 if a is S.NegativeInfinity: 

1353 res1 = _eval_sum_hyper(f.subs(i, -i), i, 1) 

1354 res2 = _eval_sum_hyper(f, i, 0) 

1355 if res1 is None or res2 is None: 

1356 return None 

1357 res1, cond1 = res1 

1358 res2, cond2 = res2 

1359 cond = And(cond1, cond2) 

1360 if cond == False or cond.as_set() == S.EmptySet: 

1361 return None 

1362 return Piecewise((res1 + res2, cond), (old_sum, True)) 

1363 

1364 # Now b == oo, a != -oo 

1365 res = _eval_sum_hyper(f, i, a) 

1366 if res is not None: 

1367 r, c = res 

1368 if c == False: 

1369 if r.is_number: 

1370 f = f.subs(i, Dummy('i', integer=True, positive=True) + a) 

1371 if f.is_positive or f.is_zero: 

1372 return S.Infinity 

1373 elif f.is_negative: 

1374 return S.NegativeInfinity 

1375 return None 

1376 return Piecewise(res, (old_sum, True)) 

1377 

1378 

1379def eval_sum_residue(f, i_a_b): 

1380 r"""Compute the infinite summation with residues 

1381 

1382 Notes 

1383 ===== 

1384 

1385 If $f(n), g(n)$ are polynomials with $\deg(g(n)) - \deg(f(n)) \ge 2$, 

1386 some infinite summations can be computed by the following residue 

1387 evaluations. 

1388 

1389 .. math:: 

1390 \sum_{n=-\infty, g(n) \ne 0}^{\infty} \frac{f(n)}{g(n)} = 

1391 -\pi \sum_{\alpha|g(\alpha)=0} 

1392 \text{Res}(\cot(\pi x) \frac{f(x)}{g(x)}, \alpha) 

1393 

1394 .. math:: 

1395 \sum_{n=-\infty, g(n) \ne 0}^{\infty} (-1)^n \frac{f(n)}{g(n)} = 

1396 -\pi \sum_{\alpha|g(\alpha)=0} 

1397 \text{Res}(\csc(\pi x) \frac{f(x)}{g(x)}, \alpha) 

1398 

1399 Examples 

1400 ======== 

1401 

1402 >>> from sympy import Sum, oo, Symbol 

1403 >>> x = Symbol('x') 

1404 

1405 Doubly infinite series of rational functions. 

1406 

1407 >>> Sum(1 / (x**2 + 1), (x, -oo, oo)).doit() 

1408 pi/tanh(pi) 

1409 

1410 Doubly infinite alternating series of rational functions. 

1411 

1412 >>> Sum((-1)**x / (x**2 + 1), (x, -oo, oo)).doit() 

1413 pi/sinh(pi) 

1414 

1415 Infinite series of even rational functions. 

1416 

1417 >>> Sum(1 / (x**2 + 1), (x, 0, oo)).doit() 

1418 1/2 + pi/(2*tanh(pi)) 

1419 

1420 Infinite series of alternating even rational functions. 

1421 

1422 >>> Sum((-1)**x / (x**2 + 1), (x, 0, oo)).doit() 

1423 pi/(2*sinh(pi)) + 1/2 

1424 

1425 This also have heuristics to transform arbitrarily shifted summand or 

1426 arbitrarily shifted summation range to the canonical problem the 

1427 formula can handle. 

1428 

1429 >>> Sum(1 / (x**2 + 2*x + 2), (x, -1, oo)).doit() 

1430 1/2 + pi/(2*tanh(pi)) 

1431 >>> Sum(1 / (x**2 + 4*x + 5), (x, -2, oo)).doit() 

1432 1/2 + pi/(2*tanh(pi)) 

1433 >>> Sum(1 / (x**2 + 1), (x, 1, oo)).doit() 

1434 -1/2 + pi/(2*tanh(pi)) 

1435 >>> Sum(1 / (x**2 + 1), (x, 2, oo)).doit() 

1436 -1 + pi/(2*tanh(pi)) 

1437 

1438 References 

1439 ========== 

1440 

1441 .. [#] http://www.supermath.info/InfiniteSeriesandtheResidueTheorem.pdf 

1442 

1443 .. [#] Asmar N.H., Grafakos L. (2018) Residue Theory. 

1444 In: Complex Analysis with Applications. 

1445 Undergraduate Texts in Mathematics. Springer, Cham. 

1446 https://doi.org/10.1007/978-3-319-94063-2_5 

1447 """ 

1448 i, a, b = i_a_b 

1449 

1450 def is_even_function(numer, denom): 

1451 """Test if the rational function is an even function""" 

1452 numer_even = all(i % 2 == 0 for (i,) in numer.monoms()) 

1453 denom_even = all(i % 2 == 0 for (i,) in denom.monoms()) 

1454 numer_odd = all(i % 2 == 1 for (i,) in numer.monoms()) 

1455 denom_odd = all(i % 2 == 1 for (i,) in denom.monoms()) 

1456 return (numer_even and denom_even) or (numer_odd and denom_odd) 

1457 

1458 def match_rational(f, i): 

1459 numer, denom = f.as_numer_denom() 

1460 try: 

1461 (numer, denom), opt = parallel_poly_from_expr((numer, denom), i) 

1462 except (PolificationFailed, PolynomialError): 

1463 return None 

1464 return numer, denom 

1465 

1466 def get_poles(denom): 

1467 roots = denom.sqf_part().all_roots() 

1468 roots = sift(roots, lambda x: x.is_integer) 

1469 if None in roots: 

1470 return None 

1471 int_roots, nonint_roots = roots[True], roots[False] 

1472 return int_roots, nonint_roots 

1473 

1474 def get_shift(denom): 

1475 n = denom.degree(i) 

1476 a = denom.coeff_monomial(i**n) 

1477 b = denom.coeff_monomial(i**(n-1)) 

1478 shift = - b / a / n 

1479 return shift 

1480 

1481 #Need a dummy symbol with no assumptions set for get_residue_factor 

1482 z = Dummy('z') 

1483 

1484 def get_residue_factor(numer, denom, alternating): 

1485 residue_factor = (numer.as_expr() / denom.as_expr()).subs(i, z) 

1486 if not alternating: 

1487 residue_factor *= cot(S.Pi * z) 

1488 else: 

1489 residue_factor *= csc(S.Pi * z) 

1490 return residue_factor 

1491 

1492 # We don't know how to deal with symbolic constants in summand 

1493 if f.free_symbols - {i}: 

1494 return None 

1495 

1496 if not (a.is_Integer or a in (S.Infinity, S.NegativeInfinity)): 

1497 return None 

1498 if not (b.is_Integer or b in (S.Infinity, S.NegativeInfinity)): 

1499 return None 

1500 

1501 # Quick exit heuristic for the sums which doesn't have infinite range 

1502 if a != S.NegativeInfinity and b != S.Infinity: 

1503 return None 

1504 

1505 match = match_rational(f, i) 

1506 if match: 

1507 alternating = False 

1508 numer, denom = match 

1509 else: 

1510 match = match_rational(f / S.NegativeOne**i, i) 

1511 if match: 

1512 alternating = True 

1513 numer, denom = match 

1514 else: 

1515 return None 

1516 

1517 if denom.degree(i) - numer.degree(i) < 2: 

1518 return None 

1519 

1520 if (a, b) == (S.NegativeInfinity, S.Infinity): 

1521 poles = get_poles(denom) 

1522 if poles is None: 

1523 return None 

1524 int_roots, nonint_roots = poles 

1525 

1526 if int_roots: 

1527 return None 

1528 

1529 residue_factor = get_residue_factor(numer, denom, alternating) 

1530 residues = [residue(residue_factor, z, root) for root in nonint_roots] 

1531 return -S.Pi * sum(residues) 

1532 

1533 if not (a.is_finite and b is S.Infinity): 

1534 return None 

1535 

1536 if not is_even_function(numer, denom): 

1537 # Try shifting summation and check if the summand can be made 

1538 # and even function from the origin. 

1539 # Sum(f(n), (n, a, b)) => Sum(f(n + s), (n, a - s, b - s)) 

1540 shift = get_shift(denom) 

1541 

1542 if not shift.is_Integer: 

1543 return None 

1544 if shift == 0: 

1545 return None 

1546 

1547 numer = numer.shift(shift) 

1548 denom = denom.shift(shift) 

1549 

1550 if not is_even_function(numer, denom): 

1551 return None 

1552 

1553 if alternating: 

1554 f = S.NegativeOne**i * (S.NegativeOne**shift * numer.as_expr() / denom.as_expr()) 

1555 else: 

1556 f = numer.as_expr() / denom.as_expr() 

1557 return eval_sum_residue(f, (i, a-shift, b-shift)) 

1558 

1559 poles = get_poles(denom) 

1560 if poles is None: 

1561 return None 

1562 int_roots, nonint_roots = poles 

1563 

1564 if int_roots: 

1565 int_roots = [int(root) for root in int_roots] 

1566 int_roots_max = max(int_roots) 

1567 int_roots_min = min(int_roots) 

1568 # Integer valued poles must be next to each other 

1569 # and also symmetric from origin (Because the function is even) 

1570 if not len(int_roots) == int_roots_max - int_roots_min + 1: 

1571 return None 

1572 

1573 # Check whether the summation indices contain poles 

1574 if a <= max(int_roots): 

1575 return None 

1576 

1577 residue_factor = get_residue_factor(numer, denom, alternating) 

1578 residues = [residue(residue_factor, z, root) for root in int_roots + nonint_roots] 

1579 full_sum = -S.Pi * sum(residues) 

1580 

1581 if not int_roots: 

1582 # Compute Sum(f, (i, 0, oo)) by adding a extraneous evaluation 

1583 # at the origin. 

1584 half_sum = (full_sum + f.xreplace({i: 0})) / 2 

1585 

1586 # Add and subtract extraneous evaluations 

1587 extraneous_neg = [f.xreplace({i: i0}) for i0 in range(int(a), 0)] 

1588 extraneous_pos = [f.xreplace({i: i0}) for i0 in range(0, int(a))] 

1589 result = half_sum + sum(extraneous_neg) - sum(extraneous_pos) 

1590 

1591 return result 

1592 

1593 # Compute Sum(f, (i, min(poles) + 1, oo)) 

1594 half_sum = full_sum / 2 

1595 

1596 # Subtract extraneous evaluations 

1597 extraneous = [f.xreplace({i: i0}) for i0 in range(max(int_roots) + 1, int(a))] 

1598 result = half_sum - sum(extraneous) 

1599 

1600 return result 

1601 

1602 

1603def _eval_matrix_sum(expression): 

1604 f = expression.function 

1605 for n, limit in enumerate(expression.limits): 

1606 i, a, b = limit 

1607 dif = b - a 

1608 if dif.is_Integer: 

1609 if (dif < 0) == True: 

1610 a, b = b + 1, a - 1 

1611 f = -f 

1612 

1613 newf = eval_sum_direct(f, (i, a, b)) 

1614 if newf is not None: 

1615 return newf.doit() 

1616 

1617 

1618def _dummy_with_inherited_properties_concrete(limits): 

1619 """ 

1620 Return a Dummy symbol that inherits as many assumptions as possible 

1621 from the provided symbol and limits. 

1622 

1623 If the symbol already has all True assumption shared by the limits 

1624 then return None. 

1625 """ 

1626 x, a, b = limits 

1627 l = [a, b] 

1628 

1629 assumptions_to_consider = ['extended_nonnegative', 'nonnegative', 

1630 'extended_nonpositive', 'nonpositive', 

1631 'extended_positive', 'positive', 

1632 'extended_negative', 'negative', 

1633 'integer', 'rational', 'finite', 

1634 'zero', 'real', 'extended_real'] 

1635 

1636 assumptions_to_keep = {} 

1637 assumptions_to_add = {} 

1638 for assum in assumptions_to_consider: 

1639 assum_true = x._assumptions.get(assum, None) 

1640 if assum_true: 

1641 assumptions_to_keep[assum] = True 

1642 elif all(getattr(i, 'is_' + assum) for i in l): 

1643 assumptions_to_add[assum] = True 

1644 if assumptions_to_add: 

1645 assumptions_to_keep.update(assumptions_to_add) 

1646 return Dummy('d', **assumptions_to_keep)