guess.py 17 KB

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