Coverage for /usr/lib/python3/dist-packages/sympy/polys/ring_series.py: 6%

967 statements  

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

1"""Power series evaluation and manipulation using sparse Polynomials 

2 

3Implementing a new function 

4--------------------------- 

5 

6There are a few things to be kept in mind when adding a new function here:: 

7 

8 - The implementation should work on all possible input domains/rings. 

9 Special cases include the ``EX`` ring and a constant term in the series 

10 to be expanded. There can be two types of constant terms in the series: 

11 

12 + A constant value or symbol. 

13 + A term of a multivariate series not involving the generator, with 

14 respect to which the series is to expanded. 

15 

16 Strictly speaking, a generator of a ring should not be considered a 

17 constant. However, for series expansion both the cases need similar 

18 treatment (as the user does not care about inner details), i.e, use an 

19 addition formula to separate the constant part and the variable part (see 

20 rs_sin for reference). 

21 

22 - All the algorithms used here are primarily designed to work for Taylor 

23 series (number of iterations in the algo equals the required order). 

24 Hence, it becomes tricky to get the series of the right order if a 

25 Puiseux series is input. Use rs_puiseux? in your function if your 

26 algorithm is not designed to handle fractional powers. 

27 

28Extending rs_series 

29------------------- 

30 

31To make a function work with rs_series you need to do two things:: 

32 

33 - Many sure it works with a constant term (as explained above). 

34 - If the series contains constant terms, you might need to extend its ring. 

35 You do so by adding the new terms to the rings as generators. 

36 ``PolyRing.compose`` and ``PolyRing.add_gens`` are two functions that do 

37 so and need to be called every time you expand a series containing a 

38 constant term. 

39 

40Look at rs_sin and rs_series for further reference. 

41 

42""" 

43 

44from sympy.polys.domains import QQ, EX 

45from sympy.polys.rings import PolyElement, ring, sring 

46from sympy.polys.polyerrors import DomainError 

47from sympy.polys.monomials import (monomial_min, monomial_mul, monomial_div, 

48 monomial_ldiv) 

49from mpmath.libmp.libintmath import ifac 

50from sympy.core import PoleError, Function, Expr 

51from sympy.core.numbers import Rational, igcd 

52from sympy.functions import sin, cos, tan, atan, exp, atanh, tanh, log, ceiling 

53from sympy.utilities.misc import as_int 

54from mpmath.libmp.libintmath import giant_steps 

55import math 

56 

57 

58def _invert_monoms(p1): 

59 """ 

60 Compute ``x**n * p1(1/x)`` for a univariate polynomial ``p1`` in ``x``. 

61 

62 Examples 

63 ======== 

64 

65 >>> from sympy.polys.domains import ZZ 

66 >>> from sympy.polys.rings import ring 

67 >>> from sympy.polys.ring_series import _invert_monoms 

68 >>> R, x = ring('x', ZZ) 

69 >>> p = x**2 + 2*x + 3 

70 >>> _invert_monoms(p) 

71 3*x**2 + 2*x + 1 

72 

73 See Also 

74 ======== 

75 

76 sympy.polys.densebasic.dup_reverse 

77 """ 

78 terms = list(p1.items()) 

79 terms.sort() 

80 deg = p1.degree() 

81 R = p1.ring 

82 p = R.zero 

83 cv = p1.listcoeffs() 

84 mv = p1.listmonoms() 

85 for mvi, cvi in zip(mv, cv): 

86 p[(deg - mvi[0],)] = cvi 

87 return p 

88 

89def _giant_steps(target): 

90 """Return a list of precision steps for the Newton's method""" 

91 res = giant_steps(2, target) 

92 if res[0] != 2: 

93 res = [2] + res 

94 return res 

95 

96def rs_trunc(p1, x, prec): 

97 """ 

98 Truncate the series in the ``x`` variable with precision ``prec``, 

99 that is, modulo ``O(x**prec)`` 

100 

101 Examples 

102 ======== 

103 

104 >>> from sympy.polys.domains import QQ 

105 >>> from sympy.polys.rings import ring 

106 >>> from sympy.polys.ring_series import rs_trunc 

107 >>> R, x = ring('x', QQ) 

108 >>> p = x**10 + x**5 + x + 1 

109 >>> rs_trunc(p, x, 12) 

110 x**10 + x**5 + x + 1 

111 >>> rs_trunc(p, x, 10) 

112 x**5 + x + 1 

113 """ 

114 R = p1.ring 

115 p = R.zero 

116 i = R.gens.index(x) 

117 for exp1 in p1: 

118 if exp1[i] >= prec: 

119 continue 

120 p[exp1] = p1[exp1] 

121 return p 

122 

123def rs_is_puiseux(p, x): 

124 """ 

125 Test if ``p`` is Puiseux series in ``x``. 

126 

127 Raise an exception if it has a negative power in ``x``. 

128 

129 Examples 

130 ======== 

131 

132 >>> from sympy.polys.domains import QQ 

133 >>> from sympy.polys.rings import ring 

134 >>> from sympy.polys.ring_series import rs_is_puiseux 

135 >>> R, x = ring('x', QQ) 

136 >>> p = x**QQ(2,5) + x**QQ(2,3) + x 

137 >>> rs_is_puiseux(p, x) 

138 True 

139 """ 

140 index = p.ring.gens.index(x) 

141 for k in p: 

142 if k[index] != int(k[index]): 

143 return True 

144 if k[index] < 0: 

145 raise ValueError('The series is not regular in %s' % x) 

146 return False 

147 

148def rs_puiseux(f, p, x, prec): 

149 """ 

150 Return the puiseux series for `f(p, x, prec)`. 

151 

152 To be used when function ``f`` is implemented only for regular series. 

153 

154 Examples 

155 ======== 

156 

157 >>> from sympy.polys.domains import QQ 

158 >>> from sympy.polys.rings import ring 

159 >>> from sympy.polys.ring_series import rs_puiseux, rs_exp 

160 >>> R, x = ring('x', QQ) 

161 >>> p = x**QQ(2,5) + x**QQ(2,3) + x 

162 >>> rs_puiseux(rs_exp,p, x, 1) 

163 1/2*x**(4/5) + x**(2/3) + x**(2/5) + 1 

164 """ 

165 index = p.ring.gens.index(x) 

166 n = 1 

167 for k in p: 

168 power = k[index] 

169 if isinstance(power, Rational): 

170 num, den = power.as_numer_denom() 

171 n = int(n*den // igcd(n, den)) 

172 elif power != int(power): 

173 den = power.denominator 

174 n = int(n*den // igcd(n, den)) 

175 if n != 1: 

176 p1 = pow_xin(p, index, n) 

177 r = f(p1, x, prec*n) 

178 n1 = QQ(1, n) 

179 if isinstance(r, tuple): 

180 r = tuple([pow_xin(rx, index, n1) for rx in r]) 

181 else: 

182 r = pow_xin(r, index, n1) 

183 else: 

184 r = f(p, x, prec) 

185 return r 

186 

187def rs_puiseux2(f, p, q, x, prec): 

188 """ 

189 Return the puiseux series for `f(p, q, x, prec)`. 

190 

191 To be used when function ``f`` is implemented only for regular series. 

192 """ 

193 index = p.ring.gens.index(x) 

194 n = 1 

195 for k in p: 

196 power = k[index] 

197 if isinstance(power, Rational): 

198 num, den = power.as_numer_denom() 

199 n = n*den // igcd(n, den) 

200 elif power != int(power): 

201 den = power.denominator 

202 n = n*den // igcd(n, den) 

203 if n != 1: 

204 p1 = pow_xin(p, index, n) 

205 r = f(p1, q, x, prec*n) 

206 n1 = QQ(1, n) 

207 r = pow_xin(r, index, n1) 

208 else: 

209 r = f(p, q, x, prec) 

210 return r 

211 

212def rs_mul(p1, p2, x, prec): 

213 """ 

214 Return the product of the given two series, modulo ``O(x**prec)``. 

215 

216 ``x`` is the series variable or its position in the generators. 

217 

218 Examples 

219 ======== 

220 

221 >>> from sympy.polys.domains import QQ 

222 >>> from sympy.polys.rings import ring 

223 >>> from sympy.polys.ring_series import rs_mul 

224 >>> R, x = ring('x', QQ) 

225 >>> p1 = x**2 + 2*x + 1 

226 >>> p2 = x + 1 

227 >>> rs_mul(p1, p2, x, 3) 

228 3*x**2 + 3*x + 1 

229 """ 

230 R = p1.ring 

231 p = R.zero 

232 if R.__class__ != p2.ring.__class__ or R != p2.ring: 

233 raise ValueError('p1 and p2 must have the same ring') 

234 iv = R.gens.index(x) 

235 if not isinstance(p2, PolyElement): 

236 raise ValueError('p2 must be a polynomial') 

237 if R == p2.ring: 

238 get = p.get 

239 items2 = list(p2.items()) 

240 items2.sort(key=lambda e: e[0][iv]) 

241 if R.ngens == 1: 

242 for exp1, v1 in p1.items(): 

243 for exp2, v2 in items2: 

244 exp = exp1[0] + exp2[0] 

245 if exp < prec: 

246 exp = (exp, ) 

247 p[exp] = get(exp, 0) + v1*v2 

248 else: 

249 break 

250 else: 

251 monomial_mul = R.monomial_mul 

252 for exp1, v1 in p1.items(): 

253 for exp2, v2 in items2: 

254 if exp1[iv] + exp2[iv] < prec: 

255 exp = monomial_mul(exp1, exp2) 

256 p[exp] = get(exp, 0) + v1*v2 

257 else: 

258 break 

259 

260 p.strip_zero() 

261 return p 

262 

263def rs_square(p1, x, prec): 

264 """ 

265 Square the series modulo ``O(x**prec)`` 

266 

267 Examples 

268 ======== 

269 

270 >>> from sympy.polys.domains import QQ 

271 >>> from sympy.polys.rings import ring 

272 >>> from sympy.polys.ring_series import rs_square 

273 >>> R, x = ring('x', QQ) 

274 >>> p = x**2 + 2*x + 1 

275 >>> rs_square(p, x, 3) 

276 6*x**2 + 4*x + 1 

277 """ 

278 R = p1.ring 

279 p = R.zero 

280 iv = R.gens.index(x) 

281 get = p.get 

282 items = list(p1.items()) 

283 items.sort(key=lambda e: e[0][iv]) 

284 monomial_mul = R.monomial_mul 

285 for i in range(len(items)): 

286 exp1, v1 = items[i] 

287 for j in range(i): 

288 exp2, v2 = items[j] 

289 if exp1[iv] + exp2[iv] < prec: 

290 exp = monomial_mul(exp1, exp2) 

291 p[exp] = get(exp, 0) + v1*v2 

292 else: 

293 break 

294 p = p.imul_num(2) 

295 get = p.get 

296 for expv, v in p1.items(): 

297 if 2*expv[iv] < prec: 

298 e2 = monomial_mul(expv, expv) 

299 p[e2] = get(e2, 0) + v**2 

300 p.strip_zero() 

301 return p 

302 

303def rs_pow(p1, n, x, prec): 

304 """ 

305 Return ``p1**n`` modulo ``O(x**prec)`` 

306 

307 Examples 

308 ======== 

309 

310 >>> from sympy.polys.domains import QQ 

311 >>> from sympy.polys.rings import ring 

312 >>> from sympy.polys.ring_series import rs_pow 

313 >>> R, x = ring('x', QQ) 

314 >>> p = x + 1 

315 >>> rs_pow(p, 4, x, 3) 

316 6*x**2 + 4*x + 1 

317 """ 

318 R = p1.ring 

319 if isinstance(n, Rational): 

320 np = int(n.p) 

321 nq = int(n.q) 

322 if nq != 1: 

323 res = rs_nth_root(p1, nq, x, prec) 

324 if np != 1: 

325 res = rs_pow(res, np, x, prec) 

326 else: 

327 res = rs_pow(p1, np, x, prec) 

328 return res 

329 

330 n = as_int(n) 

331 if n == 0: 

332 if p1: 

333 return R(1) 

334 else: 

335 raise ValueError('0**0 is undefined') 

336 if n < 0: 

337 p1 = rs_pow(p1, -n, x, prec) 

338 return rs_series_inversion(p1, x, prec) 

339 if n == 1: 

340 return rs_trunc(p1, x, prec) 

341 if n == 2: 

342 return rs_square(p1, x, prec) 

343 if n == 3: 

344 p2 = rs_square(p1, x, prec) 

345 return rs_mul(p1, p2, x, prec) 

346 p = R(1) 

347 while 1: 

348 if n & 1: 

349 p = rs_mul(p1, p, x, prec) 

350 n -= 1 

351 if not n: 

352 break 

353 p1 = rs_square(p1, x, prec) 

354 n = n // 2 

355 return p 

356 

357def rs_subs(p, rules, x, prec): 

358 """ 

359 Substitution with truncation according to the mapping in ``rules``. 

360 

361 Return a series with precision ``prec`` in the generator ``x`` 

362 

363 Note that substitutions are not done one after the other 

364 

365 >>> from sympy.polys.domains import QQ 

366 >>> from sympy.polys.rings import ring 

367 >>> from sympy.polys.ring_series import rs_subs 

368 >>> R, x, y = ring('x, y', QQ) 

369 >>> p = x**2 + y**2 

370 >>> rs_subs(p, {x: x+ y, y: x+ 2*y}, x, 3) 

371 2*x**2 + 6*x*y + 5*y**2 

372 >>> (x + y)**2 + (x + 2*y)**2 

373 2*x**2 + 6*x*y + 5*y**2 

374 

375 which differs from 

376 

377 >>> rs_subs(rs_subs(p, {x: x+ y}, x, 3), {y: x+ 2*y}, x, 3) 

378 5*x**2 + 12*x*y + 8*y**2 

379 

380 Parameters 

381 ---------- 

382 p : :class:`~.PolyElement` Input series. 

383 rules : ``dict`` with substitution mappings. 

384 x : :class:`~.PolyElement` in which the series truncation is to be done. 

385 prec : :class:`~.Integer` order of the series after truncation. 

386 

387 Examples 

388 ======== 

389 

390 >>> from sympy.polys.domains import QQ 

391 >>> from sympy.polys.rings import ring 

392 >>> from sympy.polys.ring_series import rs_subs 

393 >>> R, x, y = ring('x, y', QQ) 

394 >>> rs_subs(x**2+y**2, {y: (x+y)**2}, x, 3) 

395 6*x**2*y**2 + x**2 + 4*x*y**3 + y**4 

396 """ 

397 R = p.ring 

398 ngens = R.ngens 

399 d = R(0) 

400 for i in range(ngens): 

401 d[(i, 1)] = R.gens[i] 

402 for var in rules: 

403 d[(R.index(var), 1)] = rules[var] 

404 p1 = R(0) 

405 p_keys = sorted(p.keys()) 

406 for expv in p_keys: 

407 p2 = R(1) 

408 for i in range(ngens): 

409 power = expv[i] 

410 if power == 0: 

411 continue 

412 if (i, power) not in d: 

413 q, r = divmod(power, 2) 

414 if r == 0 and (i, q) in d: 

415 d[(i, power)] = rs_square(d[(i, q)], x, prec) 

416 elif (i, power - 1) in d: 

417 d[(i, power)] = rs_mul(d[(i, power - 1)], d[(i, 1)], 

418 x, prec) 

419 else: 

420 d[(i, power)] = rs_pow(d[(i, 1)], power, x, prec) 

421 p2 = rs_mul(p2, d[(i, power)], x, prec) 

422 p1 += p2*p[expv] 

423 return p1 

424 

425def _has_constant_term(p, x): 

426 """ 

427 Check if ``p`` has a constant term in ``x`` 

428 

429 Examples 

430 ======== 

431 

432 >>> from sympy.polys.domains import QQ 

433 >>> from sympy.polys.rings import ring 

434 >>> from sympy.polys.ring_series import _has_constant_term 

435 >>> R, x = ring('x', QQ) 

436 >>> p = x**2 + x + 1 

437 >>> _has_constant_term(p, x) 

438 True 

439 """ 

440 R = p.ring 

441 iv = R.gens.index(x) 

442 zm = R.zero_monom 

443 a = [0]*R.ngens 

444 a[iv] = 1 

445 miv = tuple(a) 

446 for expv in p: 

447 if monomial_min(expv, miv) == zm: 

448 return True 

449 return False 

450 

451def _get_constant_term(p, x): 

452 """Return constant term in p with respect to x 

453 

454 Note that it is not simply `p[R.zero_monom]` as there might be multiple 

455 generators in the ring R. We want the `x`-free term which can contain other 

456 generators. 

457 """ 

458 R = p.ring 

459 i = R.gens.index(x) 

460 zm = R.zero_monom 

461 a = [0]*R.ngens 

462 a[i] = 1 

463 miv = tuple(a) 

464 c = 0 

465 for expv in p: 

466 if monomial_min(expv, miv) == zm: 

467 c += R({expv: p[expv]}) 

468 return c 

469 

470def _check_series_var(p, x, name): 

471 index = p.ring.gens.index(x) 

472 m = min(p, key=lambda k: k[index])[index] 

473 if m < 0: 

474 raise PoleError("Asymptotic expansion of %s around [oo] not " 

475 "implemented." % name) 

476 return index, m 

477 

478def _series_inversion1(p, x, prec): 

479 """ 

480 Univariate series inversion ``1/p`` modulo ``O(x**prec)``. 

481 

482 The Newton method is used. 

483 

484 Examples 

485 ======== 

486 

487 >>> from sympy.polys.domains import QQ 

488 >>> from sympy.polys.rings import ring 

489 >>> from sympy.polys.ring_series import _series_inversion1 

490 >>> R, x = ring('x', QQ) 

491 >>> p = x + 1 

492 >>> _series_inversion1(p, x, 4) 

493 -x**3 + x**2 - x + 1 

494 """ 

495 if rs_is_puiseux(p, x): 

496 return rs_puiseux(_series_inversion1, p, x, prec) 

497 R = p.ring 

498 zm = R.zero_monom 

499 c = p[zm] 

500 

501 # giant_steps does not seem to work with PythonRational numbers with 1 as 

502 # denominator. This makes sure such a number is converted to integer. 

503 if prec == int(prec): 

504 prec = int(prec) 

505 

506 if zm not in p: 

507 raise ValueError("No constant term in series") 

508 if _has_constant_term(p - c, x): 

509 raise ValueError("p cannot contain a constant term depending on " 

510 "parameters") 

511 one = R(1) 

512 if R.domain is EX: 

513 one = 1 

514 if c != one: 

515 # TODO add check that it is a unit 

516 p1 = R(1)/c 

517 else: 

518 p1 = R(1) 

519 for precx in _giant_steps(prec): 

520 t = 1 - rs_mul(p1, p, x, precx) 

521 p1 = p1 + rs_mul(p1, t, x, precx) 

522 return p1 

523 

524def rs_series_inversion(p, x, prec): 

525 """ 

526 Multivariate series inversion ``1/p`` modulo ``O(x**prec)``. 

527 

528 Examples 

529 ======== 

530 

531 >>> from sympy.polys.domains import QQ 

532 >>> from sympy.polys.rings import ring 

533 >>> from sympy.polys.ring_series import rs_series_inversion 

534 >>> R, x, y = ring('x, y', QQ) 

535 >>> rs_series_inversion(1 + x*y**2, x, 4) 

536 -x**3*y**6 + x**2*y**4 - x*y**2 + 1 

537 >>> rs_series_inversion(1 + x*y**2, y, 4) 

538 -x*y**2 + 1 

539 >>> rs_series_inversion(x + x**2, x, 4) 

540 x**3 - x**2 + x - 1 + x**(-1) 

541 """ 

542 R = p.ring 

543 if p == R.zero: 

544 raise ZeroDivisionError 

545 zm = R.zero_monom 

546 index = R.gens.index(x) 

547 m = min(p, key=lambda k: k[index])[index] 

548 if m: 

549 p = mul_xin(p, index, -m) 

550 prec = prec + m 

551 if zm not in p: 

552 raise NotImplementedError("No constant term in series") 

553 

554 if _has_constant_term(p - p[zm], x): 

555 raise NotImplementedError("p - p[0] must not have a constant term in " 

556 "the series variables") 

557 r = _series_inversion1(p, x, prec) 

558 if m != 0: 

559 r = mul_xin(r, index, -m) 

560 return r 

561 

562def _coefficient_t(p, t): 

563 r"""Coefficient of `x_i**j` in p, where ``t`` = (i, j)""" 

564 i, j = t 

565 R = p.ring 

566 expv1 = [0]*R.ngens 

567 expv1[i] = j 

568 expv1 = tuple(expv1) 

569 p1 = R(0) 

570 for expv in p: 

571 if expv[i] == j: 

572 p1[monomial_div(expv, expv1)] = p[expv] 

573 return p1 

574 

575def rs_series_reversion(p, x, n, y): 

576 r""" 

577 Reversion of a series. 

578 

579 ``p`` is a series with ``O(x**n)`` of the form $p = ax + f(x)$ 

580 where $a$ is a number different from 0. 

581 

582 $f(x) = \sum_{k=2}^{n-1} a_kx_k$ 

583 

584 Parameters 

585 ========== 

586 

587 a_k : Can depend polynomially on other variables, not indicated. 

588 x : Variable with name x. 

589 y : Variable with name y. 

590 

591 Returns 

592 ======= 

593 

594 Solve $p = y$, that is, given $ax + f(x) - y = 0$, 

595 find the solution $x = r(y)$ up to $O(y^n)$. 

596 

597 Algorithm 

598 ========= 

599 

600 If $r_i$ is the solution at order $i$, then: 

601 $ar_i + f(r_i) - y = O\left(y^{i + 1}\right)$ 

602 

603 and if $r_{i + 1}$ is the solution at order $i + 1$, then: 

604 $ar_{i + 1} + f(r_{i + 1}) - y = O\left(y^{i + 2}\right)$ 

605 

606 We have, $r_{i + 1} = r_i + e$, such that, 

607 $ae + f(r_i) = O\left(y^{i + 2}\right)$ 

608 or $e = -f(r_i)/a$ 

609 

610 So we use the recursion relation: 

611 $r_{i + 1} = r_i - f(r_i)/a$ 

612 with the boundary condition: $r_1 = y$ 

613 

614 Examples 

615 ======== 

616 

617 >>> from sympy.polys.domains import QQ 

618 >>> from sympy.polys.rings import ring 

619 >>> from sympy.polys.ring_series import rs_series_reversion, rs_trunc 

620 >>> R, x, y, a, b = ring('x, y, a, b', QQ) 

621 >>> p = x - x**2 - 2*b*x**2 + 2*a*b*x**2 

622 >>> p1 = rs_series_reversion(p, x, 3, y); p1 

623 -2*y**2*a*b + 2*y**2*b + y**2 + y 

624 >>> rs_trunc(p.compose(x, p1), y, 3) 

625 y 

626 """ 

627 if rs_is_puiseux(p, x): 

628 raise NotImplementedError 

629 R = p.ring 

630 nx = R.gens.index(x) 

631 y = R(y) 

632 ny = R.gens.index(y) 

633 if _has_constant_term(p, x): 

634 raise ValueError("p must not contain a constant term in the series " 

635 "variable") 

636 a = _coefficient_t(p, (nx, 1)) 

637 zm = R.zero_monom 

638 assert zm in a and len(a) == 1 

639 a = a[zm] 

640 r = y/a 

641 for i in range(2, n): 

642 sp = rs_subs(p, {x: r}, y, i + 1) 

643 sp = _coefficient_t(sp, (ny, i))*y**i 

644 r -= sp/a 

645 return r 

646 

647def rs_series_from_list(p, c, x, prec, concur=1): 

648 """ 

649 Return a series `sum c[n]*p**n` modulo `O(x**prec)`. 

650 

651 It reduces the number of multiplications by summing concurrently. 

652 

653 `ax = [1, p, p**2, .., p**(J - 1)]` 

654 `s = sum(c[i]*ax[i]` for i in `range(r, (r + 1)*J))*p**((K - 1)*J)` 

655 with `K >= (n + 1)/J` 

656 

657 Examples 

658 ======== 

659 

660 >>> from sympy.polys.domains import QQ 

661 >>> from sympy.polys.rings import ring 

662 >>> from sympy.polys.ring_series import rs_series_from_list, rs_trunc 

663 >>> R, x = ring('x', QQ) 

664 >>> p = x**2 + x + 1 

665 >>> c = [1, 2, 3] 

666 >>> rs_series_from_list(p, c, x, 4) 

667 6*x**3 + 11*x**2 + 8*x + 6 

668 >>> rs_trunc(1 + 2*p + 3*p**2, x, 4) 

669 6*x**3 + 11*x**2 + 8*x + 6 

670 >>> pc = R.from_list(list(reversed(c))) 

671 >>> rs_trunc(pc.compose(x, p), x, 4) 

672 6*x**3 + 11*x**2 + 8*x + 6 

673 

674 """ 

675 

676 # TODO: Add this when it is documented in Sphinx 

677 """ 

678 See Also 

679 ======== 

680 

681 sympy.polys.rings.PolyRing.compose 

682 

683 """ 

684 R = p.ring 

685 n = len(c) 

686 if not concur: 

687 q = R(1) 

688 s = c[0]*q 

689 for i in range(1, n): 

690 q = rs_mul(q, p, x, prec) 

691 s += c[i]*q 

692 return s 

693 J = int(math.sqrt(n) + 1) 

694 K, r = divmod(n, J) 

695 if r: 

696 K += 1 

697 ax = [R(1)] 

698 q = R(1) 

699 if len(p) < 20: 

700 for i in range(1, J): 

701 q = rs_mul(q, p, x, prec) 

702 ax.append(q) 

703 else: 

704 for i in range(1, J): 

705 if i % 2 == 0: 

706 q = rs_square(ax[i//2], x, prec) 

707 else: 

708 q = rs_mul(q, p, x, prec) 

709 ax.append(q) 

710 # optimize using rs_square 

711 pj = rs_mul(ax[-1], p, x, prec) 

712 b = R(1) 

713 s = R(0) 

714 for k in range(K - 1): 

715 r = J*k 

716 s1 = c[r] 

717 for j in range(1, J): 

718 s1 += c[r + j]*ax[j] 

719 s1 = rs_mul(s1, b, x, prec) 

720 s += s1 

721 b = rs_mul(b, pj, x, prec) 

722 if not b: 

723 break 

724 k = K - 1 

725 r = J*k 

726 if r < n: 

727 s1 = c[r]*R(1) 

728 for j in range(1, J): 

729 if r + j >= n: 

730 break 

731 s1 += c[r + j]*ax[j] 

732 s1 = rs_mul(s1, b, x, prec) 

733 s += s1 

734 return s 

735 

736def rs_diff(p, x): 

737 """ 

738 Return partial derivative of ``p`` with respect to ``x``. 

739 

740 Parameters 

741 ========== 

742 

743 x : :class:`~.PolyElement` with respect to which ``p`` is differentiated. 

744 

745 Examples 

746 ======== 

747 

748 >>> from sympy.polys.domains import QQ 

749 >>> from sympy.polys.rings import ring 

750 >>> from sympy.polys.ring_series import rs_diff 

751 >>> R, x, y = ring('x, y', QQ) 

752 >>> p = x + x**2*y**3 

753 >>> rs_diff(p, x) 

754 2*x*y**3 + 1 

755 """ 

756 R = p.ring 

757 n = R.gens.index(x) 

758 p1 = R.zero 

759 mn = [0]*R.ngens 

760 mn[n] = 1 

761 mn = tuple(mn) 

762 for expv in p: 

763 if expv[n]: 

764 e = monomial_ldiv(expv, mn) 

765 p1[e] = R.domain_new(p[expv]*expv[n]) 

766 return p1 

767 

768def rs_integrate(p, x): 

769 """ 

770 Integrate ``p`` with respect to ``x``. 

771 

772 Parameters 

773 ========== 

774 

775 x : :class:`~.PolyElement` with respect to which ``p`` is integrated. 

776 

777 Examples 

778 ======== 

779 

780 >>> from sympy.polys.domains import QQ 

781 >>> from sympy.polys.rings import ring 

782 >>> from sympy.polys.ring_series import rs_integrate 

783 >>> R, x, y = ring('x, y', QQ) 

784 >>> p = x + x**2*y**3 

785 >>> rs_integrate(p, x) 

786 1/3*x**3*y**3 + 1/2*x**2 

787 """ 

788 R = p.ring 

789 p1 = R.zero 

790 n = R.gens.index(x) 

791 mn = [0]*R.ngens 

792 mn[n] = 1 

793 mn = tuple(mn) 

794 

795 for expv in p: 

796 e = monomial_mul(expv, mn) 

797 p1[e] = R.domain_new(p[expv]/(expv[n] + 1)) 

798 return p1 

799 

800def rs_fun(p, f, *args): 

801 r""" 

802 Function of a multivariate series computed by substitution. 

803 

804 The case with f method name is used to compute `rs\_tan` and `rs\_nth\_root` 

805 of a multivariate series: 

806 

807 `rs\_fun(p, tan, iv, prec)` 

808 

809 tan series is first computed for a dummy variable _x, 

810 i.e, `rs\_tan(\_x, iv, prec)`. Then we substitute _x with p to get the 

811 desired series 

812 

813 Parameters 

814 ========== 

815 

816 p : :class:`~.PolyElement` The multivariate series to be expanded. 

817 f : `ring\_series` function to be applied on `p`. 

818 args[-2] : :class:`~.PolyElement` with respect to which, the series is to be expanded. 

819 args[-1] : Required order of the expanded series. 

820 

821 Examples 

822 ======== 

823 

824 >>> from sympy.polys.domains import QQ 

825 >>> from sympy.polys.rings import ring 

826 >>> from sympy.polys.ring_series import rs_fun, _tan1 

827 >>> R, x, y = ring('x, y', QQ) 

828 >>> p = x + x*y + x**2*y + x**3*y**2 

829 >>> rs_fun(p, _tan1, x, 4) 

830 1/3*x**3*y**3 + 2*x**3*y**2 + x**3*y + 1/3*x**3 + x**2*y + x*y + x 

831 """ 

832 _R = p.ring 

833 R1, _x = ring('_x', _R.domain) 

834 h = int(args[-1]) 

835 args1 = args[:-2] + (_x, h) 

836 zm = _R.zero_monom 

837 # separate the constant term of the series 

838 # compute the univariate series f(_x, .., 'x', sum(nv)) 

839 if zm in p: 

840 x1 = _x + p[zm] 

841 p1 = p - p[zm] 

842 else: 

843 x1 = _x 

844 p1 = p 

845 if isinstance(f, str): 

846 q = getattr(x1, f)(*args1) 

847 else: 

848 q = f(x1, *args1) 

849 a = sorted(q.items()) 

850 c = [0]*h 

851 for x in a: 

852 c[x[0][0]] = x[1] 

853 p1 = rs_series_from_list(p1, c, args[-2], args[-1]) 

854 return p1 

855 

856def mul_xin(p, i, n): 

857 r""" 

858 Return `p*x_i**n`. 

859 

860 `x\_i` is the ith variable in ``p``. 

861 """ 

862 R = p.ring 

863 q = R(0) 

864 for k, v in p.items(): 

865 k1 = list(k) 

866 k1[i] += n 

867 q[tuple(k1)] = v 

868 return q 

869 

870def pow_xin(p, i, n): 

871 """ 

872 >>> from sympy.polys.domains import QQ 

873 >>> from sympy.polys.rings import ring 

874 >>> from sympy.polys.ring_series import pow_xin 

875 >>> R, x, y = ring('x, y', QQ) 

876 >>> p = x**QQ(2,5) + x + x**QQ(2,3) 

877 >>> index = p.ring.gens.index(x) 

878 >>> pow_xin(p, index, 15) 

879 x**15 + x**10 + x**6 

880 """ 

881 R = p.ring 

882 q = R(0) 

883 for k, v in p.items(): 

884 k1 = list(k) 

885 k1[i] *= n 

886 q[tuple(k1)] = v 

887 return q 

888 

889def _nth_root1(p, n, x, prec): 

890 """ 

891 Univariate series expansion of the nth root of ``p``. 

892 

893 The Newton method is used. 

894 """ 

895 if rs_is_puiseux(p, x): 

896 return rs_puiseux2(_nth_root1, p, n, x, prec) 

897 R = p.ring 

898 zm = R.zero_monom 

899 if zm not in p: 

900 raise NotImplementedError('No constant term in series') 

901 n = as_int(n) 

902 assert p[zm] == 1 

903 p1 = R(1) 

904 if p == 1: 

905 return p 

906 if n == 0: 

907 return R(1) 

908 if n == 1: 

909 return p 

910 if n < 0: 

911 n = -n 

912 sign = 1 

913 else: 

914 sign = 0 

915 for precx in _giant_steps(prec): 

916 tmp = rs_pow(p1, n + 1, x, precx) 

917 tmp = rs_mul(tmp, p, x, precx) 

918 p1 += p1/n - tmp/n 

919 if sign: 

920 return p1 

921 else: 

922 return _series_inversion1(p1, x, prec) 

923 

924def rs_nth_root(p, n, x, prec): 

925 """ 

926 Multivariate series expansion of the nth root of ``p``. 

927 

928 Parameters 

929 ========== 

930 

931 p : Expr 

932 The polynomial to computer the root of. 

933 n : integer 

934 The order of the root to be computed. 

935 x : :class:`~.PolyElement` 

936 prec : integer 

937 Order of the expanded series. 

938 

939 Notes 

940 ===== 

941 

942 The result of this function is dependent on the ring over which the 

943 polynomial has been defined. If the answer involves a root of a constant, 

944 make sure that the polynomial is over a real field. It cannot yet handle 

945 roots of symbols. 

946 

947 Examples 

948 ======== 

949 

950 >>> from sympy.polys.domains import QQ, RR 

951 >>> from sympy.polys.rings import ring 

952 >>> from sympy.polys.ring_series import rs_nth_root 

953 >>> R, x, y = ring('x, y', QQ) 

954 >>> rs_nth_root(1 + x + x*y, -3, x, 3) 

955 2/9*x**2*y**2 + 4/9*x**2*y + 2/9*x**2 - 1/3*x*y - 1/3*x + 1 

956 >>> R, x, y = ring('x, y', RR) 

957 >>> rs_nth_root(3 + x + x*y, 3, x, 2) 

958 0.160249952256379*x*y + 0.160249952256379*x + 1.44224957030741 

959 """ 

960 if n == 0: 

961 if p == 0: 

962 raise ValueError('0**0 expression') 

963 else: 

964 return p.ring(1) 

965 if n == 1: 

966 return rs_trunc(p, x, prec) 

967 R = p.ring 

968 index = R.gens.index(x) 

969 m = min(p, key=lambda k: k[index])[index] 

970 p = mul_xin(p, index, -m) 

971 prec -= m 

972 

973 if _has_constant_term(p - 1, x): 

974 zm = R.zero_monom 

975 c = p[zm] 

976 if R.domain is EX: 

977 c_expr = c.as_expr() 

978 const = c_expr**QQ(1, n) 

979 elif isinstance(c, PolyElement): 

980 try: 

981 c_expr = c.as_expr() 

982 const = R(c_expr**(QQ(1, n))) 

983 except ValueError: 

984 raise DomainError("The given series cannot be expanded in " 

985 "this domain.") 

986 else: 

987 try: # RealElement doesn't support 

988 const = R(c**Rational(1, n)) # exponentiation with mpq object 

989 except ValueError: # as exponent 

990 raise DomainError("The given series cannot be expanded in " 

991 "this domain.") 

992 res = rs_nth_root(p/c, n, x, prec)*const 

993 else: 

994 res = _nth_root1(p, n, x, prec) 

995 if m: 

996 m = QQ(m, n) 

997 res = mul_xin(res, index, m) 

998 return res 

999 

1000def rs_log(p, x, prec): 

1001 """ 

1002 The Logarithm of ``p`` modulo ``O(x**prec)``. 

1003 

1004 Notes 

1005 ===== 

1006 

1007 Truncation of ``integral dx p**-1*d p/dx`` is used. 

1008 

1009 Examples 

1010 ======== 

1011 

1012 >>> from sympy.polys.domains import QQ 

1013 >>> from sympy.polys.rings import ring 

1014 >>> from sympy.polys.ring_series import rs_log 

1015 >>> R, x = ring('x', QQ) 

1016 >>> rs_log(1 + x, x, 8) 

1017 1/7*x**7 - 1/6*x**6 + 1/5*x**5 - 1/4*x**4 + 1/3*x**3 - 1/2*x**2 + x 

1018 >>> rs_log(x**QQ(3, 2) + 1, x, 5) 

1019 1/3*x**(9/2) - 1/2*x**3 + x**(3/2) 

1020 """ 

1021 if rs_is_puiseux(p, x): 

1022 return rs_puiseux(rs_log, p, x, prec) 

1023 R = p.ring 

1024 if p == 1: 

1025 return R.zero 

1026 c = _get_constant_term(p, x) 

1027 if c: 

1028 const = 0 

1029 if c == 1: 

1030 pass 

1031 else: 

1032 c_expr = c.as_expr() 

1033 if R.domain is EX: 

1034 const = log(c_expr) 

1035 elif isinstance(c, PolyElement): 

1036 try: 

1037 const = R(log(c_expr)) 

1038 except ValueError: 

1039 R = R.add_gens([log(c_expr)]) 

1040 p = p.set_ring(R) 

1041 x = x.set_ring(R) 

1042 c = c.set_ring(R) 

1043 const = R(log(c_expr)) 

1044 else: 

1045 try: 

1046 const = R(log(c)) 

1047 except ValueError: 

1048 raise DomainError("The given series cannot be expanded in " 

1049 "this domain.") 

1050 

1051 dlog = p.diff(x) 

1052 dlog = rs_mul(dlog, _series_inversion1(p, x, prec), x, prec - 1) 

1053 return rs_integrate(dlog, x) + const 

1054 else: 

1055 raise NotImplementedError 

1056 

1057def rs_LambertW(p, x, prec): 

1058 """ 

1059 Calculate the series expansion of the principal branch of the Lambert W 

1060 function. 

1061 

1062 Examples 

1063 ======== 

1064 

1065 >>> from sympy.polys.domains import QQ 

1066 >>> from sympy.polys.rings import ring 

1067 >>> from sympy.polys.ring_series import rs_LambertW 

1068 >>> R, x, y = ring('x, y', QQ) 

1069 >>> rs_LambertW(x + x*y, x, 3) 

1070 -x**2*y**2 - 2*x**2*y - x**2 + x*y + x 

1071 

1072 See Also 

1073 ======== 

1074 

1075 LambertW 

1076 """ 

1077 if rs_is_puiseux(p, x): 

1078 return rs_puiseux(rs_LambertW, p, x, prec) 

1079 R = p.ring 

1080 p1 = R(0) 

1081 if _has_constant_term(p, x): 

1082 raise NotImplementedError("Polynomial must not have constant term in " 

1083 "the series variables") 

1084 if x in R.gens: 

1085 for precx in _giant_steps(prec): 

1086 e = rs_exp(p1, x, precx) 

1087 p2 = rs_mul(e, p1, x, precx) - p 

1088 p3 = rs_mul(e, p1 + 1, x, precx) 

1089 p3 = rs_series_inversion(p3, x, precx) 

1090 tmp = rs_mul(p2, p3, x, precx) 

1091 p1 -= tmp 

1092 return p1 

1093 else: 

1094 raise NotImplementedError 

1095 

1096def _exp1(p, x, prec): 

1097 r"""Helper function for `rs\_exp`. """ 

1098 R = p.ring 

1099 p1 = R(1) 

1100 for precx in _giant_steps(prec): 

1101 pt = p - rs_log(p1, x, precx) 

1102 tmp = rs_mul(pt, p1, x, precx) 

1103 p1 += tmp 

1104 return p1 

1105 

1106def rs_exp(p, x, prec): 

1107 """ 

1108 Exponentiation of a series modulo ``O(x**prec)`` 

1109 

1110 Examples 

1111 ======== 

1112 

1113 >>> from sympy.polys.domains import QQ 

1114 >>> from sympy.polys.rings import ring 

1115 >>> from sympy.polys.ring_series import rs_exp 

1116 >>> R, x = ring('x', QQ) 

1117 >>> rs_exp(x**2, x, 7) 

1118 1/6*x**6 + 1/2*x**4 + x**2 + 1 

1119 """ 

1120 if rs_is_puiseux(p, x): 

1121 return rs_puiseux(rs_exp, p, x, prec) 

1122 R = p.ring 

1123 c = _get_constant_term(p, x) 

1124 if c: 

1125 if R.domain is EX: 

1126 c_expr = c.as_expr() 

1127 const = exp(c_expr) 

1128 elif isinstance(c, PolyElement): 

1129 try: 

1130 c_expr = c.as_expr() 

1131 const = R(exp(c_expr)) 

1132 except ValueError: 

1133 R = R.add_gens([exp(c_expr)]) 

1134 p = p.set_ring(R) 

1135 x = x.set_ring(R) 

1136 c = c.set_ring(R) 

1137 const = R(exp(c_expr)) 

1138 else: 

1139 try: 

1140 const = R(exp(c)) 

1141 except ValueError: 

1142 raise DomainError("The given series cannot be expanded in " 

1143 "this domain.") 

1144 p1 = p - c 

1145 

1146 # Makes use of SymPy functions to evaluate the values of the cos/sin 

1147 # of the constant term. 

1148 return const*rs_exp(p1, x, prec) 

1149 

1150 if len(p) > 20: 

1151 return _exp1(p, x, prec) 

1152 one = R(1) 

1153 n = 1 

1154 c = [] 

1155 for k in range(prec): 

1156 c.append(one/n) 

1157 k += 1 

1158 n *= k 

1159 

1160 r = rs_series_from_list(p, c, x, prec) 

1161 return r 

1162 

1163def _atan(p, iv, prec): 

1164 """ 

1165 Expansion using formula. 

1166 

1167 Faster on very small and univariate series. 

1168 """ 

1169 R = p.ring 

1170 mo = R(-1) 

1171 c = [-mo] 

1172 p2 = rs_square(p, iv, prec) 

1173 for k in range(1, prec): 

1174 c.append(mo**k/(2*k + 1)) 

1175 s = rs_series_from_list(p2, c, iv, prec) 

1176 s = rs_mul(s, p, iv, prec) 

1177 return s 

1178 

1179def rs_atan(p, x, prec): 

1180 """ 

1181 The arctangent of a series 

1182 

1183 Return the series expansion of the atan of ``p``, about 0. 

1184 

1185 Examples 

1186 ======== 

1187 

1188 >>> from sympy.polys.domains import QQ 

1189 >>> from sympy.polys.rings import ring 

1190 >>> from sympy.polys.ring_series import rs_atan 

1191 >>> R, x, y = ring('x, y', QQ) 

1192 >>> rs_atan(x + x*y, x, 4) 

1193 -1/3*x**3*y**3 - x**3*y**2 - x**3*y - 1/3*x**3 + x*y + x 

1194 

1195 See Also 

1196 ======== 

1197 

1198 atan 

1199 """ 

1200 if rs_is_puiseux(p, x): 

1201 return rs_puiseux(rs_atan, p, x, prec) 

1202 R = p.ring 

1203 const = 0 

1204 if _has_constant_term(p, x): 

1205 zm = R.zero_monom 

1206 c = p[zm] 

1207 if R.domain is EX: 

1208 c_expr = c.as_expr() 

1209 const = atan(c_expr) 

1210 elif isinstance(c, PolyElement): 

1211 try: 

1212 c_expr = c.as_expr() 

1213 const = R(atan(c_expr)) 

1214 except ValueError: 

1215 raise DomainError("The given series cannot be expanded in " 

1216 "this domain.") 

1217 else: 

1218 try: 

1219 const = R(atan(c)) 

1220 except ValueError: 

1221 raise DomainError("The given series cannot be expanded in " 

1222 "this domain.") 

1223 

1224 # Instead of using a closed form formula, we differentiate atan(p) to get 

1225 # `1/(1+p**2) * dp`, whose series expansion is much easier to calculate. 

1226 # Finally we integrate to get back atan 

1227 dp = p.diff(x) 

1228 p1 = rs_square(p, x, prec) + R(1) 

1229 p1 = rs_series_inversion(p1, x, prec - 1) 

1230 p1 = rs_mul(dp, p1, x, prec - 1) 

1231 return rs_integrate(p1, x) + const 

1232 

1233def rs_asin(p, x, prec): 

1234 """ 

1235 Arcsine of a series 

1236 

1237 Return the series expansion of the asin of ``p``, about 0. 

1238 

1239 Examples 

1240 ======== 

1241 

1242 >>> from sympy.polys.domains import QQ 

1243 >>> from sympy.polys.rings import ring 

1244 >>> from sympy.polys.ring_series import rs_asin 

1245 >>> R, x, y = ring('x, y', QQ) 

1246 >>> rs_asin(x, x, 8) 

1247 5/112*x**7 + 3/40*x**5 + 1/6*x**3 + x 

1248 

1249 See Also 

1250 ======== 

1251 

1252 asin 

1253 """ 

1254 if rs_is_puiseux(p, x): 

1255 return rs_puiseux(rs_asin, p, x, prec) 

1256 if _has_constant_term(p, x): 

1257 raise NotImplementedError("Polynomial must not have constant term in " 

1258 "series variables") 

1259 R = p.ring 

1260 if x in R.gens: 

1261 # get a good value 

1262 if len(p) > 20: 

1263 dp = rs_diff(p, x) 

1264 p1 = 1 - rs_square(p, x, prec - 1) 

1265 p1 = rs_nth_root(p1, -2, x, prec - 1) 

1266 p1 = rs_mul(dp, p1, x, prec - 1) 

1267 return rs_integrate(p1, x) 

1268 one = R(1) 

1269 c = [0, one, 0] 

1270 for k in range(3, prec, 2): 

1271 c.append((k - 2)**2*c[-2]/(k*(k - 1))) 

1272 c.append(0) 

1273 return rs_series_from_list(p, c, x, prec) 

1274 

1275 else: 

1276 raise NotImplementedError 

1277 

1278def _tan1(p, x, prec): 

1279 r""" 

1280 Helper function of :func:`rs_tan`. 

1281 

1282 Return the series expansion of tan of a univariate series using Newton's 

1283 method. It takes advantage of the fact that series expansion of atan is 

1284 easier than that of tan. 

1285 

1286 Consider `f(x) = y - \arctan(x)` 

1287 Let r be a root of f(x) found using Newton's method. 

1288 Then `f(r) = 0` 

1289 Or `y = \arctan(x)` where `x = \tan(y)` as required. 

1290 """ 

1291 R = p.ring 

1292 p1 = R(0) 

1293 for precx in _giant_steps(prec): 

1294 tmp = p - rs_atan(p1, x, precx) 

1295 tmp = rs_mul(tmp, 1 + rs_square(p1, x, precx), x, precx) 

1296 p1 += tmp 

1297 return p1 

1298 

1299def rs_tan(p, x, prec): 

1300 """ 

1301 Tangent of a series. 

1302 

1303 Return the series expansion of the tan of ``p``, about 0. 

1304 

1305 Examples 

1306 ======== 

1307 

1308 >>> from sympy.polys.domains import QQ 

1309 >>> from sympy.polys.rings import ring 

1310 >>> from sympy.polys.ring_series import rs_tan 

1311 >>> R, x, y = ring('x, y', QQ) 

1312 >>> rs_tan(x + x*y, x, 4) 

1313 1/3*x**3*y**3 + x**3*y**2 + x**3*y + 1/3*x**3 + x*y + x 

1314 

1315 See Also 

1316 ======== 

1317 

1318 _tan1, tan 

1319 """ 

1320 if rs_is_puiseux(p, x): 

1321 r = rs_puiseux(rs_tan, p, x, prec) 

1322 return r 

1323 R = p.ring 

1324 const = 0 

1325 c = _get_constant_term(p, x) 

1326 if c: 

1327 if R.domain is EX: 

1328 c_expr = c.as_expr() 

1329 const = tan(c_expr) 

1330 elif isinstance(c, PolyElement): 

1331 try: 

1332 c_expr = c.as_expr() 

1333 const = R(tan(c_expr)) 

1334 except ValueError: 

1335 R = R.add_gens([tan(c_expr, )]) 

1336 p = p.set_ring(R) 

1337 x = x.set_ring(R) 

1338 c = c.set_ring(R) 

1339 const = R(tan(c_expr)) 

1340 else: 

1341 try: 

1342 const = R(tan(c)) 

1343 except ValueError: 

1344 raise DomainError("The given series cannot be expanded in " 

1345 "this domain.") 

1346 p1 = p - c 

1347 

1348 # Makes use of SymPy functions to evaluate the values of the cos/sin 

1349 # of the constant term. 

1350 t2 = rs_tan(p1, x, prec) 

1351 t = rs_series_inversion(1 - const*t2, x, prec) 

1352 return rs_mul(const + t2, t, x, prec) 

1353 

1354 if R.ngens == 1: 

1355 return _tan1(p, x, prec) 

1356 else: 

1357 return rs_fun(p, rs_tan, x, prec) 

1358 

1359def rs_cot(p, x, prec): 

1360 """ 

1361 Cotangent of a series 

1362 

1363 Return the series expansion of the cot of ``p``, about 0. 

1364 

1365 Examples 

1366 ======== 

1367 

1368 >>> from sympy.polys.domains import QQ 

1369 >>> from sympy.polys.rings import ring 

1370 >>> from sympy.polys.ring_series import rs_cot 

1371 >>> R, x, y = ring('x, y', QQ) 

1372 >>> rs_cot(x, x, 6) 

1373 -2/945*x**5 - 1/45*x**3 - 1/3*x + x**(-1) 

1374 

1375 See Also 

1376 ======== 

1377 

1378 cot 

1379 """ 

1380 # It can not handle series like `p = x + x*y` where the coefficient of the 

1381 # linear term in the series variable is symbolic. 

1382 if rs_is_puiseux(p, x): 

1383 r = rs_puiseux(rs_cot, p, x, prec) 

1384 return r 

1385 i, m = _check_series_var(p, x, 'cot') 

1386 prec1 = prec + 2*m 

1387 c, s = rs_cos_sin(p, x, prec1) 

1388 s = mul_xin(s, i, -m) 

1389 s = rs_series_inversion(s, x, prec1) 

1390 res = rs_mul(c, s, x, prec1) 

1391 res = mul_xin(res, i, -m) 

1392 res = rs_trunc(res, x, prec) 

1393 return res 

1394 

1395def rs_sin(p, x, prec): 

1396 """ 

1397 Sine of a series 

1398 

1399 Return the series expansion of the sin of ``p``, about 0. 

1400 

1401 Examples 

1402 ======== 

1403 

1404 >>> from sympy.polys.domains import QQ 

1405 >>> from sympy.polys.rings import ring 

1406 >>> from sympy.polys.ring_series import rs_sin 

1407 >>> R, x, y = ring('x, y', QQ) 

1408 >>> rs_sin(x + x*y, x, 4) 

1409 -1/6*x**3*y**3 - 1/2*x**3*y**2 - 1/2*x**3*y - 1/6*x**3 + x*y + x 

1410 >>> rs_sin(x**QQ(3, 2) + x*y**QQ(7, 5), x, 4) 

1411 -1/2*x**(7/2)*y**(14/5) - 1/6*x**3*y**(21/5) + x**(3/2) + x*y**(7/5) 

1412 

1413 See Also 

1414 ======== 

1415 

1416 sin 

1417 """ 

1418 if rs_is_puiseux(p, x): 

1419 return rs_puiseux(rs_sin, p, x, prec) 

1420 R = x.ring 

1421 if not p: 

1422 return R(0) 

1423 c = _get_constant_term(p, x) 

1424 if c: 

1425 if R.domain is EX: 

1426 c_expr = c.as_expr() 

1427 t1, t2 = sin(c_expr), cos(c_expr) 

1428 elif isinstance(c, PolyElement): 

1429 try: 

1430 c_expr = c.as_expr() 

1431 t1, t2 = R(sin(c_expr)), R(cos(c_expr)) 

1432 except ValueError: 

1433 R = R.add_gens([sin(c_expr), cos(c_expr)]) 

1434 p = p.set_ring(R) 

1435 x = x.set_ring(R) 

1436 c = c.set_ring(R) 

1437 t1, t2 = R(sin(c_expr)), R(cos(c_expr)) 

1438 else: 

1439 try: 

1440 t1, t2 = R(sin(c)), R(cos(c)) 

1441 except ValueError: 

1442 raise DomainError("The given series cannot be expanded in " 

1443 "this domain.") 

1444 p1 = p - c 

1445 

1446 # Makes use of SymPy cos, sin functions to evaluate the values of the 

1447 # cos/sin of the constant term. 

1448 return rs_sin(p1, x, prec)*t2 + rs_cos(p1, x, prec)*t1 

1449 

1450 # Series is calculated in terms of tan as its evaluation is fast. 

1451 if len(p) > 20 and R.ngens == 1: 

1452 t = rs_tan(p/2, x, prec) 

1453 t2 = rs_square(t, x, prec) 

1454 p1 = rs_series_inversion(1 + t2, x, prec) 

1455 return rs_mul(p1, 2*t, x, prec) 

1456 one = R(1) 

1457 n = 1 

1458 c = [0] 

1459 for k in range(2, prec + 2, 2): 

1460 c.append(one/n) 

1461 c.append(0) 

1462 n *= -k*(k + 1) 

1463 return rs_series_from_list(p, c, x, prec) 

1464 

1465def rs_cos(p, x, prec): 

1466 """ 

1467 Cosine of a series 

1468 

1469 Return the series expansion of the cos of ``p``, about 0. 

1470 

1471 Examples 

1472 ======== 

1473 

1474 >>> from sympy.polys.domains import QQ 

1475 >>> from sympy.polys.rings import ring 

1476 >>> from sympy.polys.ring_series import rs_cos 

1477 >>> R, x, y = ring('x, y', QQ) 

1478 >>> rs_cos(x + x*y, x, 4) 

1479 -1/2*x**2*y**2 - x**2*y - 1/2*x**2 + 1 

1480 >>> rs_cos(x + x*y, x, 4)/x**QQ(7, 5) 

1481 -1/2*x**(3/5)*y**2 - x**(3/5)*y - 1/2*x**(3/5) + x**(-7/5) 

1482 

1483 See Also 

1484 ======== 

1485 

1486 cos 

1487 """ 

1488 if rs_is_puiseux(p, x): 

1489 return rs_puiseux(rs_cos, p, x, prec) 

1490 R = p.ring 

1491 c = _get_constant_term(p, x) 

1492 if c: 

1493 if R.domain is EX: 

1494 c_expr = c.as_expr() 

1495 _, _ = sin(c_expr), cos(c_expr) 

1496 elif isinstance(c, PolyElement): 

1497 try: 

1498 c_expr = c.as_expr() 

1499 _, _ = R(sin(c_expr)), R(cos(c_expr)) 

1500 except ValueError: 

1501 R = R.add_gens([sin(c_expr), cos(c_expr)]) 

1502 p = p.set_ring(R) 

1503 x = x.set_ring(R) 

1504 c = c.set_ring(R) 

1505 else: 

1506 try: 

1507 _, _ = R(sin(c)), R(cos(c)) 

1508 except ValueError: 

1509 raise DomainError("The given series cannot be expanded in " 

1510 "this domain.") 

1511 p1 = p - c 

1512 

1513 # Makes use of SymPy cos, sin functions to evaluate the values of the 

1514 # cos/sin of the constant term. 

1515 p_cos = rs_cos(p1, x, prec) 

1516 p_sin = rs_sin(p1, x, prec) 

1517 R = R.compose(p_cos.ring).compose(p_sin.ring) 

1518 p_cos.set_ring(R) 

1519 p_sin.set_ring(R) 

1520 t1, t2 = R(sin(c_expr)), R(cos(c_expr)) 

1521 return p_cos*t2 - p_sin*t1 

1522 

1523 # Series is calculated in terms of tan as its evaluation is fast. 

1524 if len(p) > 20 and R.ngens == 1: 

1525 t = rs_tan(p/2, x, prec) 

1526 t2 = rs_square(t, x, prec) 

1527 p1 = rs_series_inversion(1+t2, x, prec) 

1528 return rs_mul(p1, 1 - t2, x, prec) 

1529 one = R(1) 

1530 n = 1 

1531 c = [] 

1532 for k in range(2, prec + 2, 2): 

1533 c.append(one/n) 

1534 c.append(0) 

1535 n *= -k*(k - 1) 

1536 return rs_series_from_list(p, c, x, prec) 

1537 

1538def rs_cos_sin(p, x, prec): 

1539 r""" 

1540 Return the tuple ``(rs_cos(p, x, prec)`, `rs_sin(p, x, prec))``. 

1541 

1542 Is faster than calling rs_cos and rs_sin separately 

1543 """ 

1544 if rs_is_puiseux(p, x): 

1545 return rs_puiseux(rs_cos_sin, p, x, prec) 

1546 t = rs_tan(p/2, x, prec) 

1547 t2 = rs_square(t, x, prec) 

1548 p1 = rs_series_inversion(1 + t2, x, prec) 

1549 return (rs_mul(p1, 1 - t2, x, prec), rs_mul(p1, 2*t, x, prec)) 

1550 

1551def _atanh(p, x, prec): 

1552 """ 

1553 Expansion using formula 

1554 

1555 Faster for very small and univariate series 

1556 """ 

1557 R = p.ring 

1558 one = R(1) 

1559 c = [one] 

1560 p2 = rs_square(p, x, prec) 

1561 for k in range(1, prec): 

1562 c.append(one/(2*k + 1)) 

1563 s = rs_series_from_list(p2, c, x, prec) 

1564 s = rs_mul(s, p, x, prec) 

1565 return s 

1566 

1567def rs_atanh(p, x, prec): 

1568 """ 

1569 Hyperbolic arctangent of a series 

1570 

1571 Return the series expansion of the atanh of ``p``, about 0. 

1572 

1573 Examples 

1574 ======== 

1575 

1576 >>> from sympy.polys.domains import QQ 

1577 >>> from sympy.polys.rings import ring 

1578 >>> from sympy.polys.ring_series import rs_atanh 

1579 >>> R, x, y = ring('x, y', QQ) 

1580 >>> rs_atanh(x + x*y, x, 4) 

1581 1/3*x**3*y**3 + x**3*y**2 + x**3*y + 1/3*x**3 + x*y + x 

1582 

1583 See Also 

1584 ======== 

1585 

1586 atanh 

1587 """ 

1588 if rs_is_puiseux(p, x): 

1589 return rs_puiseux(rs_atanh, p, x, prec) 

1590 R = p.ring 

1591 const = 0 

1592 if _has_constant_term(p, x): 

1593 zm = R.zero_monom 

1594 c = p[zm] 

1595 if R.domain is EX: 

1596 c_expr = c.as_expr() 

1597 const = atanh(c_expr) 

1598 elif isinstance(c, PolyElement): 

1599 try: 

1600 c_expr = c.as_expr() 

1601 const = R(atanh(c_expr)) 

1602 except ValueError: 

1603 raise DomainError("The given series cannot be expanded in " 

1604 "this domain.") 

1605 else: 

1606 try: 

1607 const = R(atanh(c)) 

1608 except ValueError: 

1609 raise DomainError("The given series cannot be expanded in " 

1610 "this domain.") 

1611 

1612 # Instead of using a closed form formula, we differentiate atanh(p) to get 

1613 # `1/(1-p**2) * dp`, whose series expansion is much easier to calculate. 

1614 # Finally we integrate to get back atanh 

1615 dp = rs_diff(p, x) 

1616 p1 = - rs_square(p, x, prec) + 1 

1617 p1 = rs_series_inversion(p1, x, prec - 1) 

1618 p1 = rs_mul(dp, p1, x, prec - 1) 

1619 return rs_integrate(p1, x) + const 

1620 

1621def rs_sinh(p, x, prec): 

1622 """ 

1623 Hyperbolic sine of a series 

1624 

1625 Return the series expansion of the sinh of ``p``, about 0. 

1626 

1627 Examples 

1628 ======== 

1629 

1630 >>> from sympy.polys.domains import QQ 

1631 >>> from sympy.polys.rings import ring 

1632 >>> from sympy.polys.ring_series import rs_sinh 

1633 >>> R, x, y = ring('x, y', QQ) 

1634 >>> rs_sinh(x + x*y, x, 4) 

1635 1/6*x**3*y**3 + 1/2*x**3*y**2 + 1/2*x**3*y + 1/6*x**3 + x*y + x 

1636 

1637 See Also 

1638 ======== 

1639 

1640 sinh 

1641 """ 

1642 if rs_is_puiseux(p, x): 

1643 return rs_puiseux(rs_sinh, p, x, prec) 

1644 t = rs_exp(p, x, prec) 

1645 t1 = rs_series_inversion(t, x, prec) 

1646 return (t - t1)/2 

1647 

1648def rs_cosh(p, x, prec): 

1649 """ 

1650 Hyperbolic cosine of a series 

1651 

1652 Return the series expansion of the cosh of ``p``, about 0. 

1653 

1654 Examples 

1655 ======== 

1656 

1657 >>> from sympy.polys.domains import QQ 

1658 >>> from sympy.polys.rings import ring 

1659 >>> from sympy.polys.ring_series import rs_cosh 

1660 >>> R, x, y = ring('x, y', QQ) 

1661 >>> rs_cosh(x + x*y, x, 4) 

1662 1/2*x**2*y**2 + x**2*y + 1/2*x**2 + 1 

1663 

1664 See Also 

1665 ======== 

1666 

1667 cosh 

1668 """ 

1669 if rs_is_puiseux(p, x): 

1670 return rs_puiseux(rs_cosh, p, x, prec) 

1671 t = rs_exp(p, x, prec) 

1672 t1 = rs_series_inversion(t, x, prec) 

1673 return (t + t1)/2 

1674 

1675def _tanh(p, x, prec): 

1676 r""" 

1677 Helper function of :func:`rs_tanh` 

1678 

1679 Return the series expansion of tanh of a univariate series using Newton's 

1680 method. It takes advantage of the fact that series expansion of atanh is 

1681 easier than that of tanh. 

1682 

1683 See Also 

1684 ======== 

1685 

1686 _tanh 

1687 """ 

1688 R = p.ring 

1689 p1 = R(0) 

1690 for precx in _giant_steps(prec): 

1691 tmp = p - rs_atanh(p1, x, precx) 

1692 tmp = rs_mul(tmp, 1 - rs_square(p1, x, prec), x, precx) 

1693 p1 += tmp 

1694 return p1 

1695 

1696def rs_tanh(p, x, prec): 

1697 """ 

1698 Hyperbolic tangent of a series 

1699 

1700 Return the series expansion of the tanh of ``p``, about 0. 

1701 

1702 Examples 

1703 ======== 

1704 

1705 >>> from sympy.polys.domains import QQ 

1706 >>> from sympy.polys.rings import ring 

1707 >>> from sympy.polys.ring_series import rs_tanh 

1708 >>> R, x, y = ring('x, y', QQ) 

1709 >>> rs_tanh(x + x*y, x, 4) 

1710 -1/3*x**3*y**3 - x**3*y**2 - x**3*y - 1/3*x**3 + x*y + x 

1711 

1712 See Also 

1713 ======== 

1714 

1715 tanh 

1716 """ 

1717 if rs_is_puiseux(p, x): 

1718 return rs_puiseux(rs_tanh, p, x, prec) 

1719 R = p.ring 

1720 const = 0 

1721 if _has_constant_term(p, x): 

1722 zm = R.zero_monom 

1723 c = p[zm] 

1724 if R.domain is EX: 

1725 c_expr = c.as_expr() 

1726 const = tanh(c_expr) 

1727 elif isinstance(c, PolyElement): 

1728 try: 

1729 c_expr = c.as_expr() 

1730 const = R(tanh(c_expr)) 

1731 except ValueError: 

1732 raise DomainError("The given series cannot be expanded in " 

1733 "this domain.") 

1734 else: 

1735 try: 

1736 const = R(tanh(c)) 

1737 except ValueError: 

1738 raise DomainError("The given series cannot be expanded in " 

1739 "this domain.") 

1740 p1 = p - c 

1741 t1 = rs_tanh(p1, x, prec) 

1742 t = rs_series_inversion(1 + const*t1, x, prec) 

1743 return rs_mul(const + t1, t, x, prec) 

1744 

1745 if R.ngens == 1: 

1746 return _tanh(p, x, prec) 

1747 else: 

1748 return rs_fun(p, _tanh, x, prec) 

1749 

1750def rs_newton(p, x, prec): 

1751 """ 

1752 Compute the truncated Newton sum of the polynomial ``p`` 

1753 

1754 Examples 

1755 ======== 

1756 

1757 >>> from sympy.polys.domains import QQ 

1758 >>> from sympy.polys.rings import ring 

1759 >>> from sympy.polys.ring_series import rs_newton 

1760 >>> R, x = ring('x', QQ) 

1761 >>> p = x**2 - 2 

1762 >>> rs_newton(p, x, 5) 

1763 8*x**4 + 4*x**2 + 2 

1764 """ 

1765 deg = p.degree() 

1766 p1 = _invert_monoms(p) 

1767 p2 = rs_series_inversion(p1, x, prec) 

1768 p3 = rs_mul(p1.diff(x), p2, x, prec) 

1769 res = deg - p3*x 

1770 return res 

1771 

1772def rs_hadamard_exp(p1, inverse=False): 

1773 """ 

1774 Return ``sum f_i/i!*x**i`` from ``sum f_i*x**i``, 

1775 where ``x`` is the first variable. 

1776 

1777 If ``invers=True`` return ``sum f_i*i!*x**i`` 

1778 

1779 Examples 

1780 ======== 

1781 

1782 >>> from sympy.polys.domains import QQ 

1783 >>> from sympy.polys.rings import ring 

1784 >>> from sympy.polys.ring_series import rs_hadamard_exp 

1785 >>> R, x = ring('x', QQ) 

1786 >>> p = 1 + x + x**2 + x**3 

1787 >>> rs_hadamard_exp(p) 

1788 1/6*x**3 + 1/2*x**2 + x + 1 

1789 """ 

1790 R = p1.ring 

1791 if R.domain != QQ: 

1792 raise NotImplementedError 

1793 p = R.zero 

1794 if not inverse: 

1795 for exp1, v1 in p1.items(): 

1796 p[exp1] = v1/int(ifac(exp1[0])) 

1797 else: 

1798 for exp1, v1 in p1.items(): 

1799 p[exp1] = v1*int(ifac(exp1[0])) 

1800 return p 

1801 

1802def rs_compose_add(p1, p2): 

1803 """ 

1804 compute the composed sum ``prod(p2(x - beta) for beta root of p1)`` 

1805 

1806 Examples 

1807 ======== 

1808 

1809 >>> from sympy.polys.domains import QQ 

1810 >>> from sympy.polys.rings import ring 

1811 >>> from sympy.polys.ring_series import rs_compose_add 

1812 >>> R, x = ring('x', QQ) 

1813 >>> f = x**2 - 2 

1814 >>> g = x**2 - 3 

1815 >>> rs_compose_add(f, g) 

1816 x**4 - 10*x**2 + 1 

1817 

1818 References 

1819 ========== 

1820 

1821 .. [1] A. Bostan, P. Flajolet, B. Salvy and E. Schost 

1822 "Fast Computation with Two Algebraic Numbers", 

1823 (2002) Research Report 4579, Institut 

1824 National de Recherche en Informatique et en Automatique 

1825 """ 

1826 R = p1.ring 

1827 x = R.gens[0] 

1828 prec = p1.degree()*p2.degree() + 1 

1829 np1 = rs_newton(p1, x, prec) 

1830 np1e = rs_hadamard_exp(np1) 

1831 np2 = rs_newton(p2, x, prec) 

1832 np2e = rs_hadamard_exp(np2) 

1833 np3e = rs_mul(np1e, np2e, x, prec) 

1834 np3 = rs_hadamard_exp(np3e, True) 

1835 np3a = (np3[(0,)] - np3)/x 

1836 q = rs_integrate(np3a, x) 

1837 q = rs_exp(q, x, prec) 

1838 q = _invert_monoms(q) 

1839 q = q.primitive()[1] 

1840 dp = p1.degree()*p2.degree() - q.degree() 

1841 # `dp` is the multiplicity of the zeroes of the resultant; 

1842 # these zeroes are missed in this computation so they are put here. 

1843 # if p1 and p2 are monic irreducible polynomials, 

1844 # there are zeroes in the resultant 

1845 # if and only if p1 = p2 ; in fact in that case p1 and p2 have a 

1846 # root in common, so gcd(p1, p2) != 1; being p1 and p2 irreducible 

1847 # this means p1 = p2 

1848 if dp: 

1849 q = q*x**dp 

1850 return q 

1851 

1852 

1853_convert_func = { 

1854 'sin': 'rs_sin', 

1855 'cos': 'rs_cos', 

1856 'exp': 'rs_exp', 

1857 'tan': 'rs_tan', 

1858 'log': 'rs_log' 

1859 } 

1860 

1861def rs_min_pow(expr, series_rs, a): 

1862 """Find the minimum power of `a` in the series expansion of expr""" 

1863 series = 0 

1864 n = 2 

1865 while series == 0: 

1866 series = _rs_series(expr, series_rs, a, n) 

1867 n *= 2 

1868 R = series.ring 

1869 a = R(a) 

1870 i = R.gens.index(a) 

1871 return min(series, key=lambda t: t[i])[i] 

1872 

1873 

1874def _rs_series(expr, series_rs, a, prec): 

1875 # TODO Use _parallel_dict_from_expr instead of sring as sring is 

1876 # inefficient. For details, read the todo in sring. 

1877 args = expr.args 

1878 R = series_rs.ring 

1879 

1880 # expr does not contain any function to be expanded 

1881 if not any(arg.has(Function) for arg in args) and not expr.is_Function: 

1882 return series_rs 

1883 

1884 if not expr.has(a): 

1885 return series_rs 

1886 

1887 elif expr.is_Function: 

1888 arg = args[0] 

1889 if len(args) > 1: 

1890 raise NotImplementedError 

1891 R1, series = sring(arg, domain=QQ, expand=False, series=True) 

1892 series_inner = _rs_series(arg, series, a, prec) 

1893 

1894 # Why do we need to compose these three rings? 

1895 # 

1896 # We want to use a simple domain (like ``QQ`` or ``RR``) but they don't 

1897 # support symbolic coefficients. We need a ring that for example lets 

1898 # us have `sin(1)` and `cos(1)` as coefficients if we are expanding 

1899 # `sin(x + 1)`. The ``EX`` domain allows all symbolic coefficients, but 

1900 # that makes it very complex and hence slow. 

1901 # 

1902 # To solve this problem, we add only those symbolic elements as 

1903 # generators to our ring, that we need. Here, series_inner might 

1904 # involve terms like `sin(4)`, `exp(a)`, etc, which are not there in 

1905 # R1 or R. Hence, we compose these three rings to create one that has 

1906 # the generators of all three. 

1907 R = R.compose(R1).compose(series_inner.ring) 

1908 series_inner = series_inner.set_ring(R) 

1909 series = eval(_convert_func[str(expr.func)])(series_inner, 

1910 R(a), prec) 

1911 return series 

1912 

1913 elif expr.is_Mul: 

1914 n = len(args) 

1915 for arg in args: # XXX Looks redundant 

1916 if not arg.is_Number: 

1917 R1, _ = sring(arg, expand=False, series=True) 

1918 R = R.compose(R1) 

1919 min_pows = list(map(rs_min_pow, args, [R(arg) for arg in args], 

1920 [a]*len(args))) 

1921 sum_pows = sum(min_pows) 

1922 series = R(1) 

1923 

1924 for i in range(n): 

1925 _series = _rs_series(args[i], R(args[i]), a, prec - sum_pows + 

1926 min_pows[i]) 

1927 R = R.compose(_series.ring) 

1928 _series = _series.set_ring(R) 

1929 series = series.set_ring(R) 

1930 series *= _series 

1931 series = rs_trunc(series, R(a), prec) 

1932 return series 

1933 

1934 elif expr.is_Add: 

1935 n = len(args) 

1936 series = R(0) 

1937 for i in range(n): 

1938 _series = _rs_series(args[i], R(args[i]), a, prec) 

1939 R = R.compose(_series.ring) 

1940 _series = _series.set_ring(R) 

1941 series = series.set_ring(R) 

1942 series += _series 

1943 return series 

1944 

1945 elif expr.is_Pow: 

1946 R1, _ = sring(expr.base, domain=QQ, expand=False, series=True) 

1947 R = R.compose(R1) 

1948 series_inner = _rs_series(expr.base, R(expr.base), a, prec) 

1949 return rs_pow(series_inner, expr.exp, series_inner.ring(a), prec) 

1950 

1951 # The `is_constant` method is buggy hence we check it at the end. 

1952 # See issue #9786 for details. 

1953 elif isinstance(expr, Expr) and expr.is_constant(): 

1954 return sring(expr, domain=QQ, expand=False, series=True)[1] 

1955 

1956 else: 

1957 raise NotImplementedError 

1958 

1959def rs_series(expr, a, prec): 

1960 """Return the series expansion of an expression about 0. 

1961 

1962 Parameters 

1963 ========== 

1964 

1965 expr : :class:`Expr` 

1966 a : :class:`Symbol` with respect to which expr is to be expanded 

1967 prec : order of the series expansion 

1968 

1969 Currently supports multivariate Taylor series expansion. This is much 

1970 faster that SymPy's series method as it uses sparse polynomial operations. 

1971 

1972 It automatically creates the simplest ring required to represent the series 

1973 expansion through repeated calls to sring. 

1974 

1975 Examples 

1976 ======== 

1977 

1978 >>> from sympy.polys.ring_series import rs_series 

1979 >>> from sympy import sin, cos, exp, tan, symbols, QQ 

1980 >>> a, b, c = symbols('a, b, c') 

1981 >>> rs_series(sin(a) + exp(a), a, 5) 

1982 1/24*a**4 + 1/2*a**2 + 2*a + 1 

1983 >>> series = rs_series(tan(a + b)*cos(a + c), a, 2) 

1984 >>> series.as_expr() 

1985 -a*sin(c)*tan(b) + a*cos(c)*tan(b)**2 + a*cos(c) + cos(c)*tan(b) 

1986 >>> series = rs_series(exp(a**QQ(1,3) + a**QQ(2, 5)), a, 1) 

1987 >>> series.as_expr() 

1988 a**(11/15) + a**(4/5)/2 + a**(2/5) + a**(2/3)/2 + a**(1/3) + 1 

1989 

1990 """ 

1991 R, series = sring(expr, domain=QQ, expand=False, series=True) 

1992 if a not in R.symbols: 

1993 R = R.add_gens([a, ]) 

1994 series = series.set_ring(R) 

1995 series = _rs_series(expr, series, a, prec) 

1996 R = series.ring 

1997 gen = R(a) 

1998 prec_got = series.degree(gen) + 1 

1999 

2000 if prec_got >= prec: 

2001 return rs_trunc(series, gen, prec) 

2002 else: 

2003 # increase the requested number of terms to get the desired 

2004 # number keep increasing (up to 9) until the received order 

2005 # is different than the original order and then predict how 

2006 # many additional terms are needed 

2007 for more in range(1, 9): 

2008 p1 = _rs_series(expr, series, a, prec=prec + more) 

2009 gen = gen.set_ring(p1.ring) 

2010 new_prec = p1.degree(gen) + 1 

2011 if new_prec != prec_got: 

2012 prec_do = ceiling(prec + (prec - prec_got)*more/(new_prec - 

2013 prec_got)) 

2014 p1 = _rs_series(expr, series, a, prec=prec_do) 

2015 while p1.degree(gen) + 1 < prec: 

2016 p1 = _rs_series(expr, series, a, prec=prec_do) 

2017 gen = gen.set_ring(p1.ring) 

2018 prec_do *= 2 

2019 break 

2020 else: 

2021 break 

2022 else: 

2023 raise ValueError('Could not calculate %s terms for %s' 

2024 % (str(prec), expr)) 

2025 return rs_trunc(p1, gen, prec)