recurr.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. r"""
  2. This module is intended for solving recurrences or, in other words,
  3. difference equations. Currently supported are linear, inhomogeneous
  4. equations with polynomial or rational coefficients.
  5. The solutions are obtained among polynomials, rational functions,
  6. hypergeometric terms, or combinations of hypergeometric term which
  7. are pairwise dissimilar.
  8. ``rsolve_X`` functions were meant as a low level interface
  9. for ``rsolve`` which would use Mathematica's syntax.
  10. Given a recurrence relation:
  11. .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) +
  12. ... + a_{0}(n) y(n) = f(n)
  13. where `k > 0` and `a_{i}(n)` are polynomials in `n`. To use
  14. ``rsolve_X`` we need to put all coefficients in to a list ``L`` of
  15. `k+1` elements the following way:
  16. ``L = [a_{0}(n), ..., a_{k-1}(n), a_{k}(n)]``
  17. where ``L[i]``, for `i=0, \ldots, k`, maps to
  18. `a_{i}(n) y(n+i)` (`y(n+i)` is implicit).
  19. For example if we would like to compute `m`-th Bernoulli polynomial
  20. up to a constant (example was taken from rsolve_poly docstring),
  21. then we would use `b(n+1) - b(n) = m n^{m-1}` recurrence, which
  22. has solution `b(n) = B_m + C`.
  23. Then ``L = [-1, 1]`` and `f(n) = m n^(m-1)` and finally for `m=4`:
  24. >>> from sympy import Symbol, bernoulli, rsolve_poly
  25. >>> n = Symbol('n', integer=True)
  26. >>> rsolve_poly([-1, 1], 4*n**3, n)
  27. C0 + n**4 - 2*n**3 + n**2
  28. >>> bernoulli(4, n)
  29. n**4 - 2*n**3 + n**2 - 1/30
  30. For the sake of completeness, `f(n)` can be:
  31. [1] a polynomial -> rsolve_poly
  32. [2] a rational function -> rsolve_ratio
  33. [3] a hypergeometric function -> rsolve_hyper
  34. """
  35. from collections import defaultdict
  36. from sympy.core.singleton import S
  37. from sympy.core.numbers import Rational, I
  38. from sympy.core.symbol import Symbol, Wild, Dummy
  39. from sympy.core.relational import Equality
  40. from sympy.core.add import Add
  41. from sympy.core.mul import Mul
  42. from sympy.core.sorting import default_sort_key
  43. from sympy.core.sympify import sympify
  44. from sympy.simplify import simplify, hypersimp, hypersimilar # type: ignore
  45. from sympy.solvers import solve, solve_undetermined_coeffs
  46. from sympy.polys import Poly, quo, gcd, lcm, roots, resultant
  47. from sympy.functions import binomial, factorial, FallingFactorial, RisingFactorial
  48. from sympy.matrices import Matrix, casoratian
  49. from sympy.utilities.iterables import numbered_symbols
  50. def rsolve_poly(coeffs, f, n, shift=0, **hints):
  51. r"""
  52. Given linear recurrence operator `\operatorname{L}` of order
  53. `k` with polynomial coefficients and inhomogeneous equation
  54. `\operatorname{L} y = f`, where `f` is a polynomial, we seek for
  55. all polynomial solutions over field `K` of characteristic zero.
  56. The algorithm performs two basic steps:
  57. (1) Compute degree `N` of the general polynomial solution.
  58. (2) Find all polynomials of degree `N` or less
  59. of `\operatorname{L} y = f`.
  60. There are two methods for computing the polynomial solutions.
  61. If the degree bound is relatively small, i.e. it's smaller than
  62. or equal to the order of the recurrence, then naive method of
  63. undetermined coefficients is being used. This gives system
  64. of algebraic equations with `N+1` unknowns.
  65. In the other case, the algorithm performs transformation of the
  66. initial equation to an equivalent one, for which the system of
  67. algebraic equations has only `r` indeterminates. This method is
  68. quite sophisticated (in comparison with the naive one) and was
  69. invented together by Abramov, Bronstein and Petkovsek.
  70. It is possible to generalize the algorithm implemented here to
  71. the case of linear q-difference and differential equations.
  72. Lets say that we would like to compute `m`-th Bernoulli polynomial
  73. up to a constant. For this we can use `b(n+1) - b(n) = m n^{m-1}`
  74. recurrence, which has solution `b(n) = B_m + C`. For example:
  75. >>> from sympy import Symbol, rsolve_poly
  76. >>> n = Symbol('n', integer=True)
  77. >>> rsolve_poly([-1, 1], 4*n**3, n)
  78. C0 + n**4 - 2*n**3 + n**2
  79. References
  80. ==========
  81. .. [1] S. A. Abramov, M. Bronstein and M. Petkovsek, On polynomial
  82. solutions of linear operator equations, in: T. Levelt, ed.,
  83. Proc. ISSAC '95, ACM Press, New York, 1995, 290-296.
  84. .. [2] M. Petkovsek, Hypergeometric solutions of linear recurrences
  85. with polynomial coefficients, J. Symbolic Computation,
  86. 14 (1992), 243-264.
  87. .. [3] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996.
  88. """
  89. f = sympify(f)
  90. if not f.is_polynomial(n):
  91. return None
  92. homogeneous = f.is_zero
  93. r = len(coeffs) - 1
  94. coeffs = [Poly(coeff, n) for coeff in coeffs]
  95. polys = [Poly(0, n)]*(r + 1)
  96. terms = [(S.Zero, S.NegativeInfinity)]*(r + 1)
  97. for i in range(r + 1):
  98. for j in range(i, r + 1):
  99. polys[i] += coeffs[j]*(binomial(j, i).as_poly(n))
  100. if not polys[i].is_zero:
  101. (exp,), coeff = polys[i].LT()
  102. terms[i] = (coeff, exp)
  103. d = b = terms[0][1]
  104. for i in range(1, r + 1):
  105. if terms[i][1] > d:
  106. d = terms[i][1]
  107. if terms[i][1] - i > b:
  108. b = terms[i][1] - i
  109. d, b = int(d), int(b)
  110. x = Dummy('x')
  111. degree_poly = S.Zero
  112. for i in range(r + 1):
  113. if terms[i][1] - i == b:
  114. degree_poly += terms[i][0]*FallingFactorial(x, i)
  115. nni_roots = list(roots(degree_poly, x, filter='Z',
  116. predicate=lambda r: r >= 0).keys())
  117. if nni_roots:
  118. N = [max(nni_roots)]
  119. else:
  120. N = []
  121. if homogeneous:
  122. N += [-b - 1]
  123. else:
  124. N += [f.as_poly(n).degree() - b, -b - 1]
  125. N = int(max(N))
  126. if N < 0:
  127. if homogeneous:
  128. if hints.get('symbols', False):
  129. return (S.Zero, [])
  130. else:
  131. return S.Zero
  132. else:
  133. return None
  134. if N <= r:
  135. C = []
  136. y = E = S.Zero
  137. for i in range(N + 1):
  138. C.append(Symbol('C' + str(i + shift)))
  139. y += C[i] * n**i
  140. for i in range(r + 1):
  141. E += coeffs[i].as_expr()*y.subs(n, n + i)
  142. solutions = solve_undetermined_coeffs(E - f, C, n)
  143. if solutions is not None:
  144. C = [c for c in C if (c not in solutions)]
  145. result = y.subs(solutions)
  146. else:
  147. return None # TBD
  148. else:
  149. A = r
  150. U = N + A + b + 1
  151. nni_roots = list(roots(polys[r], filter='Z',
  152. predicate=lambda r: r >= 0).keys())
  153. if nni_roots != []:
  154. a = max(nni_roots) + 1
  155. else:
  156. a = S.Zero
  157. def _zero_vector(k):
  158. return [S.Zero] * k
  159. def _one_vector(k):
  160. return [S.One] * k
  161. def _delta(p, k):
  162. B = S.One
  163. D = p.subs(n, a + k)
  164. for i in range(1, k + 1):
  165. B *= Rational(i - k - 1, i)
  166. D += B * p.subs(n, a + k - i)
  167. return D
  168. alpha = {}
  169. for i in range(-A, d + 1):
  170. I = _one_vector(d + 1)
  171. for k in range(1, d + 1):
  172. I[k] = I[k - 1] * (x + i - k + 1)/k
  173. alpha[i] = S.Zero
  174. for j in range(A + 1):
  175. for k in range(d + 1):
  176. B = binomial(k, i + j)
  177. D = _delta(polys[j].as_expr(), k)
  178. alpha[i] += I[k]*B*D
  179. V = Matrix(U, A, lambda i, j: int(i == j))
  180. if homogeneous:
  181. for i in range(A, U):
  182. v = _zero_vector(A)
  183. for k in range(1, A + b + 1):
  184. if i - k < 0:
  185. break
  186. B = alpha[k - A].subs(x, i - k)
  187. for j in range(A):
  188. v[j] += B * V[i - k, j]
  189. denom = alpha[-A].subs(x, i)
  190. for j in range(A):
  191. V[i, j] = -v[j] / denom
  192. else:
  193. G = _zero_vector(U)
  194. for i in range(A, U):
  195. v = _zero_vector(A)
  196. g = S.Zero
  197. for k in range(1, A + b + 1):
  198. if i - k < 0:
  199. break
  200. B = alpha[k - A].subs(x, i - k)
  201. for j in range(A):
  202. v[j] += B * V[i - k, j]
  203. g += B * G[i - k]
  204. denom = alpha[-A].subs(x, i)
  205. for j in range(A):
  206. V[i, j] = -v[j] / denom
  207. G[i] = (_delta(f, i - A) - g) / denom
  208. P, Q = _one_vector(U), _zero_vector(A)
  209. for i in range(1, U):
  210. P[i] = (P[i - 1] * (n - a - i + 1)/i).expand()
  211. for i in range(A):
  212. Q[i] = Add(*[(v*p).expand() for v, p in zip(V[:, i], P)])
  213. if not homogeneous:
  214. h = Add(*[(g*p).expand() for g, p in zip(G, P)])
  215. C = [Symbol('C' + str(i + shift)) for i in range(A)]
  216. g = lambda i: Add(*[c*_delta(q, i) for c, q in zip(C, Q)])
  217. if homogeneous:
  218. E = [g(i) for i in range(N + 1, U)]
  219. else:
  220. E = [g(i) + _delta(h, i) for i in range(N + 1, U)]
  221. if E != []:
  222. solutions = solve(E, *C)
  223. if not solutions:
  224. if homogeneous:
  225. if hints.get('symbols', False):
  226. return (S.Zero, [])
  227. else:
  228. return S.Zero
  229. else:
  230. return None
  231. else:
  232. solutions = {}
  233. if homogeneous:
  234. result = S.Zero
  235. else:
  236. result = h
  237. for c, q in list(zip(C, Q)):
  238. if c in solutions:
  239. s = solutions[c]*q
  240. C.remove(c)
  241. else:
  242. s = c*q
  243. result += s.expand()
  244. if hints.get('symbols', False):
  245. return (result, C)
  246. else:
  247. return result
  248. def rsolve_ratio(coeffs, f, n, **hints):
  249. r"""
  250. Given linear recurrence operator `\operatorname{L}` of order `k`
  251. with polynomial coefficients and inhomogeneous equation
  252. `\operatorname{L} y = f`, where `f` is a polynomial, we seek
  253. for all rational solutions over field `K` of characteristic zero.
  254. This procedure accepts only polynomials, however if you are
  255. interested in solving recurrence with rational coefficients
  256. then use ``rsolve`` which will pre-process the given equation
  257. and run this procedure with polynomial arguments.
  258. The algorithm performs two basic steps:
  259. (1) Compute polynomial `v(n)` which can be used as universal
  260. denominator of any rational solution of equation
  261. `\operatorname{L} y = f`.
  262. (2) Construct new linear difference equation by substitution
  263. `y(n) = u(n)/v(n)` and solve it for `u(n)` finding all its
  264. polynomial solutions. Return ``None`` if none were found.
  265. Algorithm implemented here is a revised version of the original
  266. Abramov's algorithm, developed in 1989. The new approach is much
  267. simpler to implement and has better overall efficiency. This
  268. method can be easily adapted to q-difference equations case.
  269. Besides finding rational solutions alone, this functions is
  270. an important part of Hyper algorithm were it is used to find
  271. particular solution of inhomogeneous part of a recurrence.
  272. Examples
  273. ========
  274. >>> from sympy.abc import x
  275. >>> from sympy.solvers.recurr import rsolve_ratio
  276. >>> rsolve_ratio([-2*x**3 + x**2 + 2*x - 1, 2*x**3 + x**2 - 6*x,
  277. ... - 2*x**3 - 11*x**2 - 18*x - 9, 2*x**3 + 13*x**2 + 22*x + 8], 0, x)
  278. C2*(2*x - 3)/(2*(x**2 - 1))
  279. References
  280. ==========
  281. .. [1] S. A. Abramov, Rational solutions of linear difference
  282. and q-difference equations with polynomial coefficients,
  283. in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York,
  284. 1995, 285-289
  285. See Also
  286. ========
  287. rsolve_hyper
  288. """
  289. f = sympify(f)
  290. if not f.is_polynomial(n):
  291. return None
  292. coeffs = list(map(sympify, coeffs))
  293. r = len(coeffs) - 1
  294. A, B = coeffs[r], coeffs[0]
  295. A = A.subs(n, n - r).expand()
  296. h = Dummy('h')
  297. res = resultant(A, B.subs(n, n + h), n)
  298. if not res.is_polynomial(h):
  299. p, q = res.as_numer_denom()
  300. res = quo(p, q, h)
  301. nni_roots = list(roots(res, h, filter='Z',
  302. predicate=lambda r: r >= 0).keys())
  303. if not nni_roots:
  304. return rsolve_poly(coeffs, f, n, **hints)
  305. else:
  306. C, numers = S.One, [S.Zero]*(r + 1)
  307. for i in range(int(max(nni_roots)), -1, -1):
  308. d = gcd(A, B.subs(n, n + i), n)
  309. A = quo(A, d, n)
  310. B = quo(B, d.subs(n, n - i), n)
  311. C *= Mul(*[d.subs(n, n - j) for j in range(i + 1)])
  312. denoms = [C.subs(n, n + i) for i in range(r + 1)]
  313. for i in range(r + 1):
  314. g = gcd(coeffs[i], denoms[i], n)
  315. numers[i] = quo(coeffs[i], g, n)
  316. denoms[i] = quo(denoms[i], g, n)
  317. for i in range(r + 1):
  318. numers[i] *= Mul(*(denoms[:i] + denoms[i + 1:]))
  319. result = rsolve_poly(numers, f * Mul(*denoms), n, **hints)
  320. if result is not None:
  321. if hints.get('symbols', False):
  322. return (simplify(result[0] / C), result[1])
  323. else:
  324. return simplify(result / C)
  325. else:
  326. return None
  327. def rsolve_hyper(coeffs, f, n, **hints):
  328. r"""
  329. Given linear recurrence operator `\operatorname{L}` of order `k`
  330. with polynomial coefficients and inhomogeneous equation
  331. `\operatorname{L} y = f` we seek for all hypergeometric solutions
  332. over field `K` of characteristic zero.
  333. The inhomogeneous part can be either hypergeometric or a sum
  334. of a fixed number of pairwise dissimilar hypergeometric terms.
  335. The algorithm performs three basic steps:
  336. (1) Group together similar hypergeometric terms in the
  337. inhomogeneous part of `\operatorname{L} y = f`, and find
  338. particular solution using Abramov's algorithm.
  339. (2) Compute generating set of `\operatorname{L}` and find basis
  340. in it, so that all solutions are linearly independent.
  341. (3) Form final solution with the number of arbitrary
  342. constants equal to dimension of basis of `\operatorname{L}`.
  343. Term `a(n)` is hypergeometric if it is annihilated by first order
  344. linear difference equations with polynomial coefficients or, in
  345. simpler words, if consecutive term ratio is a rational function.
  346. The output of this procedure is a linear combination of fixed
  347. number of hypergeometric terms. However the underlying method
  348. can generate larger class of solutions - D'Alembertian terms.
  349. Note also that this method not only computes the kernel of the
  350. inhomogeneous equation, but also reduces in to a basis so that
  351. solutions generated by this procedure are linearly independent
  352. Examples
  353. ========
  354. >>> from sympy.solvers import rsolve_hyper
  355. >>> from sympy.abc import x
  356. >>> rsolve_hyper([-1, -1, 1], 0, x)
  357. C0*(1/2 - sqrt(5)/2)**x + C1*(1/2 + sqrt(5)/2)**x
  358. >>> rsolve_hyper([-1, 1], 1 + x, x)
  359. C0 + x*(x + 1)/2
  360. References
  361. ==========
  362. .. [1] M. Petkovsek, Hypergeometric solutions of linear recurrences
  363. with polynomial coefficients, J. Symbolic Computation,
  364. 14 (1992), 243-264.
  365. .. [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996.
  366. """
  367. from sympy.concrete import product
  368. coeffs = list(map(sympify, coeffs))
  369. f = sympify(f)
  370. r, kernel, symbols = len(coeffs) - 1, [], set()
  371. if not f.is_zero:
  372. if f.is_Add:
  373. similar = {}
  374. for g in f.expand().args:
  375. if not g.is_hypergeometric(n):
  376. return None
  377. for h in similar.keys():
  378. if hypersimilar(g, h, n):
  379. similar[h] += g
  380. break
  381. else:
  382. similar[g] = S.Zero
  383. inhomogeneous = []
  384. for g, h in similar.items():
  385. inhomogeneous.append(g + h)
  386. elif f.is_hypergeometric(n):
  387. inhomogeneous = [f]
  388. else:
  389. return None
  390. for i, g in enumerate(inhomogeneous):
  391. coeff, polys = S.One, coeffs[:]
  392. denoms = [S.One]*(r + 1)
  393. s = hypersimp(g, n)
  394. for j in range(1, r + 1):
  395. coeff *= s.subs(n, n + j - 1)
  396. p, q = coeff.as_numer_denom()
  397. polys[j] *= p
  398. denoms[j] = q
  399. for j in range(r + 1):
  400. polys[j] *= Mul(*(denoms[:j] + denoms[j + 1:]))
  401. R = rsolve_poly(polys, Mul(*denoms), n)
  402. if not (R is None or R is S.Zero):
  403. inhomogeneous[i] *= R
  404. else:
  405. return None
  406. result = Add(*inhomogeneous)
  407. else:
  408. result = S.Zero
  409. Z = Dummy('Z')
  410. p, q = coeffs[0], coeffs[r].subs(n, n - r + 1)
  411. p_factors = [z for z in roots(p, n).keys()]
  412. q_factors = [z for z in roots(q, n).keys()]
  413. factors = [(S.One, S.One)]
  414. for p in p_factors:
  415. for q in q_factors:
  416. if p.is_integer and q.is_integer and p <= q:
  417. continue
  418. else:
  419. factors += [(n - p, n - q)]
  420. p = [(n - p, S.One) for p in p_factors]
  421. q = [(S.One, n - q) for q in q_factors]
  422. factors = p + factors + q
  423. for A, B in factors:
  424. polys, degrees = [], []
  425. D = A*B.subs(n, n + r - 1)
  426. for i in range(r + 1):
  427. a = Mul(*[A.subs(n, n + j) for j in range(i)])
  428. b = Mul(*[B.subs(n, n + j) for j in range(i, r)])
  429. poly = quo(coeffs[i]*a*b, D, n)
  430. polys.append(poly.as_poly(n))
  431. if not poly.is_zero:
  432. degrees.append(polys[i].degree())
  433. if degrees:
  434. d, poly = max(degrees), S.Zero
  435. else:
  436. return None
  437. for i in range(r + 1):
  438. coeff = polys[i].nth(d)
  439. if coeff is not S.Zero:
  440. poly += coeff * Z**i
  441. for z in roots(poly, Z).keys():
  442. if z.is_zero:
  443. continue
  444. recurr_coeffs = [polys[i].as_expr()*z**i for i in range(r + 1)]
  445. if d == 0 and 0 != Add(*[recurr_coeffs[j]*j for j in range(1, r + 1)]):
  446. # faster inline check (than calling rsolve_poly) for a
  447. # constant solution to a constant coefficient recurrence.
  448. C = Symbol("C" + str(len(symbols)))
  449. s = [C]
  450. else:
  451. C, s = rsolve_poly(recurr_coeffs, 0, n, len(symbols), symbols=True)
  452. if C is not None and C is not S.Zero:
  453. symbols |= set(s)
  454. ratio = z * A * C.subs(n, n + 1) / B / C
  455. ratio = simplify(ratio)
  456. # If there is a nonnegative root in the denominator of the ratio,
  457. # this indicates that the term y(n_root) is zero, and one should
  458. # start the product with the term y(n_root + 1).
  459. n0 = 0
  460. for n_root in roots(ratio.as_numer_denom()[1], n).keys():
  461. if n_root.has(I):
  462. return None
  463. elif (n0 < (n_root + 1)) == True:
  464. n0 = n_root + 1
  465. K = product(ratio, (n, n0, n - 1))
  466. if K.has(factorial, FallingFactorial, RisingFactorial):
  467. K = simplify(K)
  468. if casoratian(kernel + [K], n, zero=False) != 0:
  469. kernel.append(K)
  470. kernel.sort(key=default_sort_key)
  471. sk = list(zip(numbered_symbols('C'), kernel))
  472. if sk:
  473. for C, ker in sk:
  474. result += C * ker
  475. else:
  476. return None
  477. if hints.get('symbols', False):
  478. # XXX: This returns the symbols in a non-deterministic order
  479. symbols |= {s for s, k in sk}
  480. return (result, list(symbols))
  481. else:
  482. return result
  483. def rsolve(f, y, init=None):
  484. r"""
  485. Solve univariate recurrence with rational coefficients.
  486. Given `k`-th order linear recurrence `\operatorname{L} y = f`,
  487. or equivalently:
  488. .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) +
  489. \cdots + a_{0}(n) y(n) = f(n)
  490. where `a_{i}(n)`, for `i=0, \ldots, k`, are polynomials or rational
  491. functions in `n`, and `f` is a hypergeometric function or a sum
  492. of a fixed number of pairwise dissimilar hypergeometric terms in
  493. `n`, finds all solutions or returns ``None``, if none were found.
  494. Initial conditions can be given as a dictionary in two forms:
  495. (1) ``{ n_0 : v_0, n_1 : v_1, ..., n_m : v_m}``
  496. (2) ``{y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m}``
  497. or as a list ``L`` of values:
  498. ``L = [v_0, v_1, ..., v_m]``
  499. where ``L[i] = v_i``, for `i=0, \ldots, m`, maps to `y(n_i)`.
  500. Examples
  501. ========
  502. Lets consider the following recurrence:
  503. .. math:: (n - 1) y(n + 2) - (n^2 + 3 n - 2) y(n + 1) +
  504. 2 n (n + 1) y(n) = 0
  505. >>> from sympy import Function, rsolve
  506. >>> from sympy.abc import n
  507. >>> y = Function('y')
  508. >>> f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n)
  509. >>> rsolve(f, y(n))
  510. 2**n*C0 + C1*factorial(n)
  511. >>> rsolve(f, y(n), {y(0):0, y(1):3})
  512. 3*2**n - 3*factorial(n)
  513. See Also
  514. ========
  515. rsolve_poly, rsolve_ratio, rsolve_hyper
  516. """
  517. if isinstance(f, Equality):
  518. f = f.lhs - f.rhs
  519. n = y.args[0]
  520. k = Wild('k', exclude=(n,))
  521. # Preprocess user input to allow things like
  522. # y(n) + a*(y(n + 1) + y(n - 1))/2
  523. f = f.expand().collect(y.func(Wild('m', integer=True)))
  524. h_part = defaultdict(list)
  525. i_part = []
  526. for g in Add.make_args(f):
  527. coeff, dep = g.as_coeff_mul(y.func)
  528. if not dep:
  529. i_part.append(coeff)
  530. continue
  531. for h in dep:
  532. if h.is_Function and h.func == y.func:
  533. result = h.args[0].match(n + k)
  534. if result is not None:
  535. h_part[int(result[k])].append(coeff)
  536. continue
  537. raise ValueError(
  538. "'%s(%s + k)' expected, got '%s'" % (y.func, n, h))
  539. for k in h_part:
  540. h_part[k] = Add(*h_part[k])
  541. h_part.default_factory = lambda: 0
  542. i_part = Add(*i_part)
  543. for k, coeff in h_part.items():
  544. h_part[k] = simplify(coeff)
  545. common = S.One
  546. if not i_part.is_zero and not i_part.is_hypergeometric(n) and \
  547. not (i_part.is_Add and all(map(lambda x: x.is_hypergeometric(n), i_part.expand().args))):
  548. raise ValueError("The independent term should be a sum of hypergeometric functions, got '%s'" % i_part)
  549. for coeff in h_part.values():
  550. if coeff.is_rational_function(n):
  551. if not coeff.is_polynomial(n):
  552. common = lcm(common, coeff.as_numer_denom()[1], n)
  553. else:
  554. raise ValueError(
  555. "Polynomial or rational function expected, got '%s'" % coeff)
  556. i_numer, i_denom = i_part.as_numer_denom()
  557. if i_denom.is_polynomial(n):
  558. common = lcm(common, i_denom, n)
  559. if common is not S.One:
  560. for k, coeff in h_part.items():
  561. numer, denom = coeff.as_numer_denom()
  562. h_part[k] = numer*quo(common, denom, n)
  563. i_part = i_numer*quo(common, i_denom, n)
  564. K_min = min(h_part.keys())
  565. if K_min < 0:
  566. K = abs(K_min)
  567. H_part = defaultdict(lambda: S.Zero)
  568. i_part = i_part.subs(n, n + K).expand()
  569. common = common.subs(n, n + K).expand()
  570. for k, coeff in h_part.items():
  571. H_part[k + K] = coeff.subs(n, n + K).expand()
  572. else:
  573. H_part = h_part
  574. K_max = max(H_part.keys())
  575. coeffs = [H_part[i] for i in range(K_max + 1)]
  576. result = rsolve_hyper(coeffs, -i_part, n, symbols=True)
  577. if result is None:
  578. return None
  579. solution, symbols = result
  580. if init in ({}, []):
  581. init = None
  582. if symbols and init is not None:
  583. if isinstance(init, list):
  584. init = {i: init[i] for i in range(len(init))}
  585. equations = []
  586. for k, v in init.items():
  587. try:
  588. i = int(k)
  589. except TypeError:
  590. if k.is_Function and k.func == y.func:
  591. i = int(k.args[0])
  592. else:
  593. raise ValueError("Integer or term expected, got '%s'" % k)
  594. eq = solution.subs(n, i) - v
  595. if eq.has(S.NaN):
  596. eq = solution.limit(n, i) - v
  597. equations.append(eq)
  598. result = solve(equations, *symbols)
  599. if not result:
  600. return None
  601. else:
  602. solution = solution.subs(result)
  603. return solution