guess.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. """Various algorithms for helping identifying numbers and sequences."""
  2. from sympy.concrete.products import (Product, product)
  3. from sympy.core import Function, S
  4. from sympy.core.add import Add
  5. from sympy.core.numbers import Integer, Rational
  6. from sympy.core.symbol import Symbol, symbols
  7. from sympy.core.sympify import sympify
  8. from sympy.functions.elementary.exponential import exp
  9. from sympy.functions.elementary.integers import floor
  10. from sympy.integrals.integrals import integrate
  11. from sympy.polys.polyfuncs import rational_interpolate as rinterp
  12. from sympy.polys.polytools import lcm
  13. from sympy.simplify.radsimp import denom
  14. from sympy.utilities import public
  15. @public
  16. def find_simple_recurrence_vector(l):
  17. """
  18. This function is used internally by other functions from the
  19. sympy.concrete.guess module. While most users may want to rather use the
  20. function find_simple_recurrence when looking for recurrence relations
  21. among rational numbers, the current function may still be useful when
  22. some post-processing has to be done.
  23. Explanation
  24. ===========
  25. The function returns a vector of length n when a recurrence relation of
  26. order n is detected in the sequence of rational numbers v.
  27. If the returned vector has a length 1, then the returned value is always
  28. the list [0], which means that no relation has been found.
  29. While the functions is intended to be used with rational numbers, it should
  30. work for other kinds of real numbers except for some cases involving
  31. quadratic numbers; for that reason it should be used with some caution when
  32. the argument is not a list of rational numbers.
  33. Examples
  34. ========
  35. >>> from sympy.concrete.guess import find_simple_recurrence_vector
  36. >>> from sympy import fibonacci
  37. >>> find_simple_recurrence_vector([fibonacci(k) for k in range(12)])
  38. [1, -1, -1]
  39. See Also
  40. ========
  41. See the function sympy.concrete.guess.find_simple_recurrence which is more
  42. user-friendly.
  43. """
  44. q1 = [0]
  45. q2 = [1]
  46. b, z = 0, len(l) >> 1
  47. while len(q2) <= z:
  48. while l[b]==0:
  49. b += 1
  50. if b == len(l):
  51. c = 1
  52. for x in q2:
  53. c = lcm(c, denom(x))
  54. if q2[0]*c < 0: c = -c
  55. for k in range(len(q2)):
  56. q2[k] = int(q2[k]*c)
  57. return q2
  58. a = S.One/l[b]
  59. m = [a]
  60. for k in range(b+1, len(l)):
  61. m.append(-sum(l[j+1]*m[b-j-1] for j in range(b, k))*a)
  62. l, m = m, [0] * max(len(q2), b+len(q1))
  63. for k, q in enumerate(q2):
  64. m[k] = a*q
  65. for k, q in enumerate(q1):
  66. m[k+b] += q
  67. while m[-1]==0: m.pop() # because trailing zeros can occur
  68. q1, q2, b = q2, m, 1
  69. return [0]
  70. @public
  71. def find_simple_recurrence(v, A=Function('a'), N=Symbol('n')):
  72. """
  73. Detects and returns a recurrence relation from a sequence of several integer
  74. (or rational) terms. The name of the function in the returned expression is
  75. 'a' by default; the main variable is 'n' by default. The smallest index in
  76. the returned expression is always n (and never n-1, n-2, etc.).
  77. Examples
  78. ========
  79. >>> from sympy.concrete.guess import find_simple_recurrence
  80. >>> from sympy import fibonacci
  81. >>> find_simple_recurrence([fibonacci(k) for k in range(12)])
  82. -a(n) - a(n + 1) + a(n + 2)
  83. >>> from sympy import Function, Symbol
  84. >>> a = [1, 1, 1]
  85. >>> for k in range(15): a.append(5*a[-1]-3*a[-2]+8*a[-3])
  86. >>> find_simple_recurrence(a, A=Function('f'), N=Symbol('i'))
  87. -8*f(i) + 3*f(i + 1) - 5*f(i + 2) + f(i + 3)
  88. """
  89. p = find_simple_recurrence_vector(v)
  90. n = len(p)
  91. if n <= 1: return S.Zero
  92. return Add(*[A(N+n-1-k)*p[k] for k in range(n)])
  93. @public
  94. def rationalize(x, maxcoeff=10000):
  95. """
  96. Helps identifying a rational number from a float (or mpmath.mpf) value by
  97. using a continued fraction. The algorithm stops as soon as a large partial
  98. quotient is detected (greater than 10000 by default).
  99. Examples
  100. ========
  101. >>> from sympy.concrete.guess import rationalize
  102. >>> from mpmath import cos, pi
  103. >>> rationalize(cos(pi/3))
  104. 1/2
  105. >>> from mpmath import mpf
  106. >>> rationalize(mpf("0.333333333333333"))
  107. 1/3
  108. While the function is rather intended to help 'identifying' rational
  109. values, it may be used in some cases for approximating real numbers.
  110. (Though other functions may be more relevant in that case.)
  111. >>> rationalize(pi, maxcoeff = 250)
  112. 355/113
  113. See Also
  114. ========
  115. Several other methods can approximate a real number as a rational, like:
  116. * fractions.Fraction.from_decimal
  117. * fractions.Fraction.from_float
  118. * mpmath.identify
  119. * mpmath.pslq by using the following syntax: mpmath.pslq([x, 1])
  120. * mpmath.findpoly by using the following syntax: mpmath.findpoly(x, 1)
  121. * sympy.simplify.nsimplify (which is a more general function)
  122. The main difference between the current function and all these variants is
  123. that control focuses on magnitude of partial quotients here rather than on
  124. global precision of the approximation. If the real is "known to be" a
  125. rational number, the current function should be able to detect it correctly
  126. with the default settings even when denominator is great (unless its
  127. expansion contains unusually big partial quotients) which may occur
  128. when studying sequences of increasing numbers. If the user cares more
  129. on getting simple fractions, other methods may be more convenient.
  130. """
  131. p0, p1 = 0, 1
  132. q0, q1 = 1, 0
  133. a = floor(x)
  134. while a < maxcoeff or q1==0:
  135. p = a*p1 + p0
  136. q = a*q1 + q0
  137. p0, p1 = p1, p
  138. q0, q1 = q1, q
  139. if x==a: break
  140. x = 1/(x-a)
  141. a = floor(x)
  142. return sympify(p) / q
  143. @public
  144. def guess_generating_function_rational(v, X=Symbol('x')):
  145. """
  146. Tries to "guess" a rational generating function for a sequence of rational
  147. numbers v.
  148. Examples
  149. ========
  150. >>> from sympy.concrete.guess import guess_generating_function_rational
  151. >>> from sympy import fibonacci
  152. >>> l = [fibonacci(k) for k in range(5,15)]
  153. >>> guess_generating_function_rational(l)
  154. (3*x + 5)/(-x**2 - x + 1)
  155. See Also
  156. ========
  157. sympy.series.approximants
  158. mpmath.pade
  159. """
  160. # a) compute the denominator as q
  161. q = find_simple_recurrence_vector(v)
  162. n = len(q)
  163. if n <= 1: return None
  164. # b) compute the numerator as p
  165. p = [sum(v[i-k]*q[k] for k in range(min(i+1, n)))
  166. for i in range(len(v)>>1)]
  167. return (sum(p[k]*X**k for k in range(len(p)))
  168. / sum(q[k]*X**k for k in range(n)))
  169. @public
  170. def guess_generating_function(v, X=Symbol('x'), types=['all'], maxsqrtn=2):
  171. """
  172. Tries to "guess" a generating function for a sequence of rational numbers v.
  173. Only a few patterns are implemented yet.
  174. Explanation
  175. ===========
  176. The function returns a dictionary where keys are the name of a given type of
  177. generating function. Six types are currently implemented:
  178. type | formal definition
  179. -------+----------------------------------------------------------------
  180. ogf | f(x) = Sum( a_k * x^k , k: 0..infinity )
  181. egf | f(x) = Sum( a_k * x^k / k! , k: 0..infinity )
  182. lgf | f(x) = Sum( (-1)^(k+1) a_k * x^k / k , k: 1..infinity )
  183. | (with initial index being hold as 1 rather than 0)
  184. hlgf | f(x) = Sum( a_k * x^k / k , k: 1..infinity )
  185. | (with initial index being hold as 1 rather than 0)
  186. lgdogf | f(x) = derivate( log(Sum( a_k * x^k, k: 0..infinity )), x)
  187. lgdegf | f(x) = derivate( log(Sum( a_k * x^k / k!, k: 0..infinity )), x)
  188. In order to spare time, the user can select only some types of generating
  189. functions (default being ['all']). While forgetting to use a list in the
  190. case of a single type may seem to work most of the time as in: types='ogf'
  191. this (convenient) syntax may lead to unexpected extra results in some cases.
  192. Discarding a type when calling the function does not mean that the type will
  193. not be present in the returned dictionary; it only means that no extra
  194. computation will be performed for that type, but the function may still add
  195. it in the result when it can be easily converted from another type.
  196. Two generating functions (lgdogf and lgdegf) are not even computed if the
  197. initial term of the sequence is 0; it may be useful in that case to try
  198. again after having removed the leading zeros.
  199. Examples
  200. ========
  201. >>> from sympy.concrete.guess import guess_generating_function as ggf
  202. >>> ggf([k+1 for k in range(12)], types=['ogf', 'lgf', 'hlgf'])
  203. {'hlgf': 1/(1 - x), 'lgf': 1/(x + 1), 'ogf': 1/(x**2 - 2*x + 1)}
  204. >>> from sympy import sympify
  205. >>> l = sympify("[3/2, 11/2, 0, -121/2, -363/2, 121]")
  206. >>> ggf(l)
  207. {'ogf': (x + 3/2)/(11*x**2 - 3*x + 1)}
  208. >>> from sympy import fibonacci
  209. >>> ggf([fibonacci(k) for k in range(5, 15)], types=['ogf'])
  210. {'ogf': (3*x + 5)/(-x**2 - x + 1)}
  211. >>> from sympy import factorial
  212. >>> ggf([factorial(k) for k in range(12)], types=['ogf', 'egf', 'lgf'])
  213. {'egf': 1/(1 - x)}
  214. >>> ggf([k+1 for k in range(12)], types=['egf'])
  215. {'egf': (x + 1)*exp(x), 'lgdegf': (x + 2)/(x + 1)}
  216. N-th root of a rational function can also be detected (below is an example
  217. coming from the sequence A108626 from https://oeis.org).
  218. The greatest n-th root to be tested is specified as maxsqrtn (default 2).
  219. >>> ggf([1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])['ogf']
  220. sqrt(1/(x**4 + 2*x**2 - 4*x + 1))
  221. References
  222. ==========
  223. .. [1] "Concrete Mathematics", R.L. Graham, D.E. Knuth, O. Patashnik
  224. .. [2] https://oeis.org/wiki/Generating_functions
  225. """
  226. # List of all types of all g.f. known by the algorithm
  227. if 'all' in types:
  228. types = ('ogf', 'egf', 'lgf', 'hlgf', 'lgdogf', 'lgdegf')
  229. result = {}
  230. # Ordinary Generating Function (ogf)
  231. if 'ogf' in types:
  232. # Perform some convolutions of the sequence with itself
  233. t = [1] + [0]*(len(v) - 1)
  234. for d in range(max(1, maxsqrtn)):
  235. t = [sum(t[n-i]*v[i] for i in range(n+1)) for n in range(len(v))]
  236. g = guess_generating_function_rational(t, X=X)
  237. if g:
  238. result['ogf'] = g**Rational(1, d+1)
  239. break
  240. # Exponential Generating Function (egf)
  241. if 'egf' in types:
  242. # Transform sequence (division by factorial)
  243. w, f = [], S.One
  244. for i, k in enumerate(v):
  245. f *= i if i else 1
  246. w.append(k/f)
  247. # Perform some convolutions of the sequence with itself
  248. t = [1] + [0]*(len(w) - 1)
  249. for d in range(max(1, maxsqrtn)):
  250. t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
  251. g = guess_generating_function_rational(t, X=X)
  252. if g:
  253. result['egf'] = g**Rational(1, d+1)
  254. break
  255. # Logarithmic Generating Function (lgf)
  256. if 'lgf' in types:
  257. # Transform sequence (multiplication by (-1)^(n+1) / n)
  258. w, f = [], S.NegativeOne
  259. for i, k in enumerate(v):
  260. f = -f
  261. w.append(f*k/Integer(i+1))
  262. # Perform some convolutions of the sequence with itself
  263. t = [1] + [0]*(len(w) - 1)
  264. for d in range(max(1, maxsqrtn)):
  265. t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
  266. g = guess_generating_function_rational(t, X=X)
  267. if g:
  268. result['lgf'] = g**Rational(1, d+1)
  269. break
  270. # Hyperbolic logarithmic Generating Function (hlgf)
  271. if 'hlgf' in types:
  272. # Transform sequence (division by n+1)
  273. w = []
  274. for i, k in enumerate(v):
  275. w.append(k/Integer(i+1))
  276. # Perform some convolutions of the sequence with itself
  277. t = [1] + [0]*(len(w) - 1)
  278. for d in range(max(1, maxsqrtn)):
  279. t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
  280. g = guess_generating_function_rational(t, X=X)
  281. if g:
  282. result['hlgf'] = g**Rational(1, d+1)
  283. break
  284. # Logarithmic derivative of ordinary generating Function (lgdogf)
  285. if v[0] != 0 and ('lgdogf' in types
  286. or ('ogf' in types and 'ogf' not in result)):
  287. # Transform sequence by computing f'(x)/f(x)
  288. # because log(f(x)) = integrate( f'(x)/f(x) )
  289. a, w = sympify(v[0]), []
  290. for n in range(len(v)-1):
  291. w.append(
  292. (v[n+1]*(n+1) - sum(w[-i-1]*v[i+1] for i in range(n)))/a)
  293. # Perform some convolutions of the sequence with itself
  294. t = [1] + [0]*(len(w) - 1)
  295. for d in range(max(1, maxsqrtn)):
  296. t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
  297. g = guess_generating_function_rational(t, X=X)
  298. if g:
  299. result['lgdogf'] = g**Rational(1, d+1)
  300. if 'ogf' not in result:
  301. result['ogf'] = exp(integrate(result['lgdogf'], X))
  302. break
  303. # Logarithmic derivative of exponential generating Function (lgdegf)
  304. if v[0] != 0 and ('lgdegf' in types
  305. or ('egf' in types and 'egf' not in result)):
  306. # Transform sequence / step 1 (division by factorial)
  307. z, f = [], S.One
  308. for i, k in enumerate(v):
  309. f *= i if i else 1
  310. z.append(k/f)
  311. # Transform sequence / step 2 by computing f'(x)/f(x)
  312. # because log(f(x)) = integrate( f'(x)/f(x) )
  313. a, w = z[0], []
  314. for n in range(len(z)-1):
  315. w.append(
  316. (z[n+1]*(n+1) - sum(w[-i-1]*z[i+1] for i in range(n)))/a)
  317. # Perform some convolutions of the sequence with itself
  318. t = [1] + [0]*(len(w) - 1)
  319. for d in range(max(1, maxsqrtn)):
  320. t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
  321. g = guess_generating_function_rational(t, X=X)
  322. if g:
  323. result['lgdegf'] = g**Rational(1, d+1)
  324. if 'egf' not in result:
  325. result['egf'] = exp(integrate(result['lgdegf'], X))
  326. break
  327. return result
  328. @public
  329. def guess(l, all=False, evaluate=True, niter=2, variables=None):
  330. """
  331. This function is adapted from the Rate.m package for Mathematica
  332. written by Christian Krattenthaler.
  333. It tries to guess a formula from a given sequence of rational numbers.
  334. Explanation
  335. ===========
  336. In order to speed up the process, the 'all' variable is set to False by
  337. default, stopping the computation as some results are returned during an
  338. iteration; the variable can be set to True if more iterations are needed
  339. (other formulas may be found; however they may be equivalent to the first
  340. ones).
  341. Another option is the 'evaluate' variable (default is True); setting it
  342. to False will leave the involved products unevaluated.
  343. By default, the number of iterations is set to 2 but a greater value (up
  344. to len(l)-1) can be specified with the optional 'niter' variable.
  345. More and more convoluted results are found when the order of the
  346. iteration gets higher:
  347. * first iteration returns polynomial or rational functions;
  348. * second iteration returns products of rising factorials and their
  349. inverses;
  350. * third iteration returns products of products of rising factorials
  351. and their inverses;
  352. * etc.
  353. The returned formulas contain symbols i0, i1, i2, ... where the main
  354. variables is i0 (and auxiliary variables are i1, i2, ...). A list of
  355. other symbols can be provided in the 'variables' option; the length of
  356. the least should be the value of 'niter' (more is acceptable but only
  357. the first symbols will be used); in this case, the main variable will be
  358. the first symbol in the list.
  359. Examples
  360. ========
  361. >>> from sympy.concrete.guess import guess
  362. >>> guess([1,2,6,24,120], evaluate=False)
  363. [Product(i1 + 1, (i1, 1, i0 - 1))]
  364. >>> from sympy import symbols
  365. >>> r = guess([1,2,7,42,429,7436,218348,10850216], niter=4)
  366. >>> i0 = symbols("i0")
  367. >>> [r[0].subs(i0,n).doit() for n in range(1,10)]
  368. [1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460]
  369. """
  370. if any(a==0 for a in l[:-1]):
  371. return []
  372. N = len(l)
  373. niter = min(N-1, niter)
  374. myprod = product if evaluate else Product
  375. g = []
  376. res = []
  377. if variables is None:
  378. symb = symbols('i:'+str(niter))
  379. else:
  380. symb = variables
  381. for k, s in enumerate(symb):
  382. g.append(l)
  383. n, r = len(l), []
  384. for i in range(n-2-1, -1, -1):
  385. ri = rinterp(enumerate(g[k][:-1], start=1), i, X=s)
  386. if ((denom(ri).subs({s:n}) != 0)
  387. and (ri.subs({s:n}) - g[k][-1] == 0)
  388. and ri not in r):
  389. r.append(ri)
  390. if r:
  391. for i in range(k-1, -1, -1):
  392. r = [g[i][0]
  393. * myprod(v, (symb[i+1], 1, symb[i]-1)) for v in r]
  394. if not all: return r
  395. res += r
  396. l = [Rational(l[i+1], l[i]) for i in range(N-k-1)]
  397. return res