identification.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. """
  2. Implements the PSLQ algorithm for integer relation detection,
  3. and derivative algorithms for constant recognition.
  4. """
  5. from .libmp.backend import xrange
  6. from .libmp import int_types, sqrt_fixed
  7. # round to nearest integer (can be done more elegantly...)
  8. def round_fixed(x, prec):
  9. return ((x + (1<<(prec-1))) >> prec) << prec
  10. class IdentificationMethods(object):
  11. pass
  12. def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False):
  13. r"""
  14. Given a vector of real numbers `x = [x_0, x_1, ..., x_n]`, ``pslq(x)``
  15. uses the PSLQ algorithm to find a list of integers
  16. `[c_0, c_1, ..., c_n]` such that
  17. .. math ::
  18. |c_1 x_1 + c_2 x_2 + ... + c_n x_n| < \mathrm{tol}
  19. and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector
  20. exists, :func:`~mpmath.pslq` returns ``None``. The tolerance defaults to
  21. 3/4 of the working precision.
  22. **Examples**
  23. Find rational approximations for `\pi`::
  24. >>> from mpmath import *
  25. >>> mp.dps = 15; mp.pretty = True
  26. >>> pslq([-1, pi], tol=0.01)
  27. [22, 7]
  28. >>> pslq([-1, pi], tol=0.001)
  29. [355, 113]
  30. >>> mpf(22)/7; mpf(355)/113; +pi
  31. 3.14285714285714
  32. 3.14159292035398
  33. 3.14159265358979
  34. Pi is not a rational number with denominator less than 1000::
  35. >>> pslq([-1, pi])
  36. >>>
  37. To within the standard precision, it can however be approximated
  38. by at least one rational number with denominator less than `10^{12}`::
  39. >>> p, q = pslq([-1, pi], maxcoeff=10**12)
  40. >>> print(p); print(q)
  41. 238410049439
  42. 75888275702
  43. >>> mpf(p)/q
  44. 3.14159265358979
  45. The PSLQ algorithm can be applied to long vectors. For example,
  46. we can investigate the rational (in)dependence of integer square
  47. roots::
  48. >>> mp.dps = 30
  49. >>> pslq([sqrt(n) for n in range(2, 5+1)])
  50. >>>
  51. >>> pslq([sqrt(n) for n in range(2, 6+1)])
  52. >>>
  53. >>> pslq([sqrt(n) for n in range(2, 8+1)])
  54. [2, 0, 0, 0, 0, 0, -1]
  55. **Machin formulas**
  56. A famous formula for `\pi` is Machin's,
  57. .. math ::
  58. \frac{\pi}{4} = 4 \operatorname{acot} 5 - \operatorname{acot} 239
  59. There are actually infinitely many formulas of this type. Two
  60. others are
  61. .. math ::
  62. \frac{\pi}{4} = \operatorname{acot} 1
  63. \frac{\pi}{4} = 12 \operatorname{acot} 49 + 32 \operatorname{acot} 57
  64. + 5 \operatorname{acot} 239 + 12 \operatorname{acot} 110443
  65. We can easily verify the formulas using the PSLQ algorithm::
  66. >>> mp.dps = 30
  67. >>> pslq([pi/4, acot(1)])
  68. [1, -1]
  69. >>> pslq([pi/4, acot(5), acot(239)])
  70. [1, -4, 1]
  71. >>> pslq([pi/4, acot(49), acot(57), acot(239), acot(110443)])
  72. [1, -12, -32, 5, -12]
  73. We could try to generate a custom Machin-like formula by running
  74. the PSLQ algorithm with a few inverse cotangent values, for example
  75. acot(2), acot(3) ... acot(10). Unfortunately, there is a linear
  76. dependence among these values, resulting in only that dependence
  77. being detected, with a zero coefficient for `\pi`::
  78. >>> pslq([pi] + [acot(n) for n in range(2,11)])
  79. [0, 1, -1, 0, 0, 0, -1, 0, 0, 0]
  80. We get better luck by removing linearly dependent terms::
  81. >>> pslq([pi] + [acot(n) for n in range(2,11) if n not in (3, 5)])
  82. [1, -8, 0, 0, 4, 0, 0, 0]
  83. In other words, we found the following formula::
  84. >>> 8*acot(2) - 4*acot(7)
  85. 3.14159265358979323846264338328
  86. >>> +pi
  87. 3.14159265358979323846264338328
  88. **Algorithm**
  89. This is a fairly direct translation to Python of the pseudocode given by
  90. David Bailey, "The PSLQ Integer Relation Algorithm":
  91. http://www.cecm.sfu.ca/organics/papers/bailey/paper/html/node3.html
  92. The present implementation uses fixed-point instead of floating-point
  93. arithmetic, since this is significantly (about 7x) faster.
  94. """
  95. n = len(x)
  96. if n < 2:
  97. raise ValueError("n cannot be less than 2")
  98. # At too low precision, the algorithm becomes meaningless
  99. prec = ctx.prec
  100. if prec < 53:
  101. raise ValueError("prec cannot be less than 53")
  102. if verbose and prec // max(2,n) < 5:
  103. print("Warning: precision for PSLQ may be too low")
  104. target = int(prec * 0.75)
  105. if tol is None:
  106. tol = ctx.mpf(2)**(-target)
  107. else:
  108. tol = ctx.convert(tol)
  109. extra = 60
  110. prec += extra
  111. if verbose:
  112. print("PSLQ using prec %i and tol %s" % (prec, ctx.nstr(tol)))
  113. tol = ctx.to_fixed(tol, prec)
  114. assert tol
  115. # Convert to fixed-point numbers. The dummy None is added so we can
  116. # use 1-based indexing. (This just allows us to be consistent with
  117. # Bailey's indexing. The algorithm is 100 lines long, so debugging
  118. # a single wrong index can be painful.)
  119. x = [None] + [ctx.to_fixed(ctx.mpf(xk), prec) for xk in x]
  120. # Sanity check on magnitudes
  121. minx = min(abs(xx) for xx in x[1:])
  122. if not minx:
  123. raise ValueError("PSLQ requires a vector of nonzero numbers")
  124. if minx < tol//100:
  125. if verbose:
  126. print("STOPPING: (one number is too small)")
  127. return None
  128. g = sqrt_fixed((4<<prec)//3, prec)
  129. A = {}
  130. B = {}
  131. H = {}
  132. # Initialization
  133. # step 1
  134. for i in xrange(1, n+1):
  135. for j in xrange(1, n+1):
  136. A[i,j] = B[i,j] = (i==j) << prec
  137. H[i,j] = 0
  138. # step 2
  139. s = [None] + [0] * n
  140. for k in xrange(1, n+1):
  141. t = 0
  142. for j in xrange(k, n+1):
  143. t += (x[j]**2 >> prec)
  144. s[k] = sqrt_fixed(t, prec)
  145. t = s[1]
  146. y = x[:]
  147. for k in xrange(1, n+1):
  148. y[k] = (x[k] << prec) // t
  149. s[k] = (s[k] << prec) // t
  150. # step 3
  151. for i in xrange(1, n+1):
  152. for j in xrange(i+1, n):
  153. H[i,j] = 0
  154. if i <= n-1:
  155. if s[i]:
  156. H[i,i] = (s[i+1] << prec) // s[i]
  157. else:
  158. H[i,i] = 0
  159. for j in range(1, i):
  160. sjj1 = s[j]*s[j+1]
  161. if sjj1:
  162. H[i,j] = ((-y[i]*y[j])<<prec)//sjj1
  163. else:
  164. H[i,j] = 0
  165. # step 4
  166. for i in xrange(2, n+1):
  167. for j in xrange(i-1, 0, -1):
  168. #t = floor(H[i,j]/H[j,j] + 0.5)
  169. if H[j,j]:
  170. t = round_fixed((H[i,j] << prec)//H[j,j], prec)
  171. else:
  172. #t = 0
  173. continue
  174. y[j] = y[j] + (t*y[i] >> prec)
  175. for k in xrange(1, j+1):
  176. H[i,k] = H[i,k] - (t*H[j,k] >> prec)
  177. for k in xrange(1, n+1):
  178. A[i,k] = A[i,k] - (t*A[j,k] >> prec)
  179. B[k,j] = B[k,j] + (t*B[k,i] >> prec)
  180. # Main algorithm
  181. for REP in range(maxsteps):
  182. # Step 1
  183. m = -1
  184. szmax = -1
  185. for i in range(1, n):
  186. h = H[i,i]
  187. sz = (g**i * abs(h)) >> (prec*(i-1))
  188. if sz > szmax:
  189. m = i
  190. szmax = sz
  191. # Step 2
  192. y[m], y[m+1] = y[m+1], y[m]
  193. for i in xrange(1,n+1): H[m,i], H[m+1,i] = H[m+1,i], H[m,i]
  194. for i in xrange(1,n+1): A[m,i], A[m+1,i] = A[m+1,i], A[m,i]
  195. for i in xrange(1,n+1): B[i,m], B[i,m+1] = B[i,m+1], B[i,m]
  196. # Step 3
  197. if m <= n - 2:
  198. t0 = sqrt_fixed((H[m,m]**2 + H[m,m+1]**2)>>prec, prec)
  199. # A zero element probably indicates that the precision has
  200. # been exhausted. XXX: this could be spurious, due to
  201. # using fixed-point arithmetic
  202. if not t0:
  203. break
  204. t1 = (H[m,m] << prec) // t0
  205. t2 = (H[m,m+1] << prec) // t0
  206. for i in xrange(m, n+1):
  207. t3 = H[i,m]
  208. t4 = H[i,m+1]
  209. H[i,m] = (t1*t3+t2*t4) >> prec
  210. H[i,m+1] = (-t2*t3+t1*t4) >> prec
  211. # Step 4
  212. for i in xrange(m+1, n+1):
  213. for j in xrange(min(i-1, m+1), 0, -1):
  214. try:
  215. t = round_fixed((H[i,j] << prec)//H[j,j], prec)
  216. # Precision probably exhausted
  217. except ZeroDivisionError:
  218. break
  219. y[j] = y[j] + ((t*y[i]) >> prec)
  220. for k in xrange(1, j+1):
  221. H[i,k] = H[i,k] - (t*H[j,k] >> prec)
  222. for k in xrange(1, n+1):
  223. A[i,k] = A[i,k] - (t*A[j,k] >> prec)
  224. B[k,j] = B[k,j] + (t*B[k,i] >> prec)
  225. # Until a relation is found, the error typically decreases
  226. # slowly (e.g. a factor 1-10) with each step TODO: we could
  227. # compare err from two successive iterations. If there is a
  228. # large drop (several orders of magnitude), that indicates a
  229. # "high quality" relation was detected. Reporting this to
  230. # the user somehow might be useful.
  231. best_err = maxcoeff<<prec
  232. for i in xrange(1, n+1):
  233. err = abs(y[i])
  234. # Maybe we are done?
  235. if err < tol:
  236. # We are done if the coefficients are acceptable
  237. vec = [int(round_fixed(B[j,i], prec) >> prec) for j in \
  238. range(1,n+1)]
  239. if max(abs(v) for v in vec) < maxcoeff:
  240. if verbose:
  241. print("FOUND relation at iter %i/%i, error: %s" % \
  242. (REP, maxsteps, ctx.nstr(err / ctx.mpf(2)**prec, 1)))
  243. return vec
  244. best_err = min(err, best_err)
  245. # Calculate a lower bound for the norm. We could do this
  246. # more exactly (using the Euclidean norm) but there is probably
  247. # no practical benefit.
  248. recnorm = max(abs(h) for h in H.values())
  249. if recnorm:
  250. norm = ((1 << (2*prec)) // recnorm) >> prec
  251. norm //= 100
  252. else:
  253. norm = ctx.inf
  254. if verbose:
  255. print("%i/%i: Error: %8s Norm: %s" % \
  256. (REP, maxsteps, ctx.nstr(best_err / ctx.mpf(2)**prec, 1), norm))
  257. if norm >= maxcoeff:
  258. break
  259. if verbose:
  260. print("CANCELLING after step %i/%i." % (REP, maxsteps))
  261. print("Could not find an integer relation. Norm bound: %s" % norm)
  262. return None
  263. def findpoly(ctx, x, n=1, **kwargs):
  264. r"""
  265. ``findpoly(x, n)`` returns the coefficients of an integer
  266. polynomial `P` of degree at most `n` such that `P(x) \approx 0`.
  267. If no polynomial having `x` as a root can be found,
  268. :func:`~mpmath.findpoly` returns ``None``.
  269. :func:`~mpmath.findpoly` works by successively calling :func:`~mpmath.pslq` with
  270. the vectors `[1, x]`, `[1, x, x^2]`, `[1, x, x^2, x^3]`, ...,
  271. `[1, x, x^2, .., x^n]` as input. Keyword arguments given to
  272. :func:`~mpmath.findpoly` are forwarded verbatim to :func:`~mpmath.pslq`. In
  273. particular, you can specify a tolerance for `P(x)` with ``tol``
  274. and a maximum permitted coefficient size with ``maxcoeff``.
  275. For large values of `n`, it is recommended to run :func:`~mpmath.findpoly`
  276. at high precision; preferably 50 digits or more.
  277. **Examples**
  278. By default (degree `n = 1`), :func:`~mpmath.findpoly` simply finds a linear
  279. polynomial with a rational root::
  280. >>> from mpmath import *
  281. >>> mp.dps = 15; mp.pretty = True
  282. >>> findpoly(0.7)
  283. [-10, 7]
  284. The generated coefficient list is valid input to ``polyval`` and
  285. ``polyroots``::
  286. >>> nprint(polyval(findpoly(phi, 2), phi), 1)
  287. -2.0e-16
  288. >>> for r in polyroots(findpoly(phi, 2)):
  289. ... print(r)
  290. ...
  291. -0.618033988749895
  292. 1.61803398874989
  293. Numbers of the form `m + n \sqrt p` for integers `(m, n, p)` are
  294. solutions to quadratic equations. As we find here, `1+\sqrt 2`
  295. is a root of the polynomial `x^2 - 2x - 1`::
  296. >>> findpoly(1+sqrt(2), 2)
  297. [1, -2, -1]
  298. >>> findroot(lambda x: x**2 - 2*x - 1, 1)
  299. 2.4142135623731
  300. Despite only containing square roots, the following number results
  301. in a polynomial of degree 4::
  302. >>> findpoly(sqrt(2)+sqrt(3), 4)
  303. [1, 0, -10, 0, 1]
  304. In fact, `x^4 - 10x^2 + 1` is the *minimal polynomial* of
  305. `r = \sqrt 2 + \sqrt 3`, meaning that a rational polynomial of
  306. lower degree having `r` as a root does not exist. Given sufficient
  307. precision, :func:`~mpmath.findpoly` will usually find the correct
  308. minimal polynomial of a given algebraic number.
  309. **Non-algebraic numbers**
  310. If :func:`~mpmath.findpoly` fails to find a polynomial with given
  311. coefficient size and tolerance constraints, that means no such
  312. polynomial exists.
  313. We can verify that `\pi` is not an algebraic number of degree 3 with
  314. coefficients less than 1000::
  315. >>> mp.dps = 15
  316. >>> findpoly(pi, 3)
  317. >>>
  318. It is always possible to find an algebraic approximation of a number
  319. using one (or several) of the following methods:
  320. 1. Increasing the permitted degree
  321. 2. Allowing larger coefficients
  322. 3. Reducing the tolerance
  323. One example of each method is shown below::
  324. >>> mp.dps = 15
  325. >>> findpoly(pi, 4)
  326. [95, -545, 863, -183, -298]
  327. >>> findpoly(pi, 3, maxcoeff=10000)
  328. [836, -1734, -2658, -457]
  329. >>> findpoly(pi, 3, tol=1e-7)
  330. [-4, 22, -29, -2]
  331. It is unknown whether Euler's constant is transcendental (or even
  332. irrational). We can use :func:`~mpmath.findpoly` to check that if is
  333. an algebraic number, its minimal polynomial must have degree
  334. at least 7 and a coefficient of magnitude at least 1000000::
  335. >>> mp.dps = 200
  336. >>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100, maxsteps=1000)
  337. >>>
  338. Note that the high precision and strict tolerance is necessary
  339. for such high-degree runs, since otherwise unwanted low-accuracy
  340. approximations will be detected. It may also be necessary to set
  341. maxsteps high to prevent a premature exit (before the coefficient
  342. bound has been reached). Running with ``verbose=True`` to get an
  343. idea what is happening can be useful.
  344. """
  345. x = ctx.mpf(x)
  346. if n < 1:
  347. raise ValueError("n cannot be less than 1")
  348. if x == 0:
  349. return [1, 0]
  350. xs = [ctx.mpf(1)]
  351. for i in range(1,n+1):
  352. xs.append(x**i)
  353. a = ctx.pslq(xs, **kwargs)
  354. if a is not None:
  355. return a[::-1]
  356. def fracgcd(p, q):
  357. x, y = p, q
  358. while y:
  359. x, y = y, x % y
  360. if x != 1:
  361. p //= x
  362. q //= x
  363. if q == 1:
  364. return p
  365. return p, q
  366. def pslqstring(r, constants):
  367. q = r[0]
  368. r = r[1:]
  369. s = []
  370. for i in range(len(r)):
  371. p = r[i]
  372. if p:
  373. z = fracgcd(-p,q)
  374. cs = constants[i][1]
  375. if cs == '1':
  376. cs = ''
  377. else:
  378. cs = '*' + cs
  379. if isinstance(z, int_types):
  380. if z > 0: term = str(z) + cs
  381. else: term = ("(%s)" % z) + cs
  382. else:
  383. term = ("(%s/%s)" % z) + cs
  384. s.append(term)
  385. s = ' + '.join(s)
  386. if '+' in s or '*' in s:
  387. s = '(' + s + ')'
  388. return s or '0'
  389. def prodstring(r, constants):
  390. q = r[0]
  391. r = r[1:]
  392. num = []
  393. den = []
  394. for i in range(len(r)):
  395. p = r[i]
  396. if p:
  397. z = fracgcd(-p,q)
  398. cs = constants[i][1]
  399. if isinstance(z, int_types):
  400. if abs(z) == 1: t = cs
  401. else: t = '%s**%s' % (cs, abs(z))
  402. ([num,den][z<0]).append(t)
  403. else:
  404. t = '%s**(%s/%s)' % (cs, abs(z[0]), z[1])
  405. ([num,den][z[0]<0]).append(t)
  406. num = '*'.join(num)
  407. den = '*'.join(den)
  408. if num and den: return "(%s)/(%s)" % (num, den)
  409. if num: return num
  410. if den: return "1/(%s)" % den
  411. def quadraticstring(ctx,t,a,b,c):
  412. if c < 0:
  413. a,b,c = -a,-b,-c
  414. u1 = (-b+ctx.sqrt(b**2-4*a*c))/(2*c)
  415. u2 = (-b-ctx.sqrt(b**2-4*a*c))/(2*c)
  416. if abs(u1-t) < abs(u2-t):
  417. if b: s = '((%s+sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c)
  418. else: s = '(sqrt(%s)/%s)' % (-4*a*c,2*c)
  419. else:
  420. if b: s = '((%s-sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c)
  421. else: s = '(-sqrt(%s)/%s)' % (-4*a*c,2*c)
  422. return s
  423. # Transformation y = f(x,c), with inverse function x = f(y,c)
  424. # The third entry indicates whether the transformation is
  425. # redundant when c = 1
  426. transforms = [
  427. (lambda ctx,x,c: x*c, '$y/$c', 0),
  428. (lambda ctx,x,c: x/c, '$c*$y', 1),
  429. (lambda ctx,x,c: c/x, '$c/$y', 0),
  430. (lambda ctx,x,c: (x*c)**2, 'sqrt($y)/$c', 0),
  431. (lambda ctx,x,c: (x/c)**2, '$c*sqrt($y)', 1),
  432. (lambda ctx,x,c: (c/x)**2, '$c/sqrt($y)', 0),
  433. (lambda ctx,x,c: c*x**2, 'sqrt($y)/sqrt($c)', 1),
  434. (lambda ctx,x,c: x**2/c, 'sqrt($c)*sqrt($y)', 1),
  435. (lambda ctx,x,c: c/x**2, 'sqrt($c)/sqrt($y)', 1),
  436. (lambda ctx,x,c: ctx.sqrt(x*c), '$y**2/$c', 0),
  437. (lambda ctx,x,c: ctx.sqrt(x/c), '$c*$y**2', 1),
  438. (lambda ctx,x,c: ctx.sqrt(c/x), '$c/$y**2', 0),
  439. (lambda ctx,x,c: c*ctx.sqrt(x), '$y**2/$c**2', 1),
  440. (lambda ctx,x,c: ctx.sqrt(x)/c, '$c**2*$y**2', 1),
  441. (lambda ctx,x,c: c/ctx.sqrt(x), '$c**2/$y**2', 1),
  442. (lambda ctx,x,c: ctx.exp(x*c), 'log($y)/$c', 0),
  443. (lambda ctx,x,c: ctx.exp(x/c), '$c*log($y)', 1),
  444. (lambda ctx,x,c: ctx.exp(c/x), '$c/log($y)', 0),
  445. (lambda ctx,x,c: c*ctx.exp(x), 'log($y/$c)', 1),
  446. (lambda ctx,x,c: ctx.exp(x)/c, 'log($c*$y)', 1),
  447. (lambda ctx,x,c: c/ctx.exp(x), 'log($c/$y)', 0),
  448. (lambda ctx,x,c: ctx.ln(x*c), 'exp($y)/$c', 0),
  449. (lambda ctx,x,c: ctx.ln(x/c), '$c*exp($y)', 1),
  450. (lambda ctx,x,c: ctx.ln(c/x), '$c/exp($y)', 0),
  451. (lambda ctx,x,c: c*ctx.ln(x), 'exp($y/$c)', 1),
  452. (lambda ctx,x,c: ctx.ln(x)/c, 'exp($c*$y)', 1),
  453. (lambda ctx,x,c: c/ctx.ln(x), 'exp($c/$y)', 0),
  454. ]
  455. def identify(ctx, x, constants=[], tol=None, maxcoeff=1000, full=False,
  456. verbose=False):
  457. r"""
  458. Given a real number `x`, ``identify(x)`` attempts to find an exact
  459. formula for `x`. This formula is returned as a string. If no match
  460. is found, ``None`` is returned. With ``full=True``, a list of
  461. matching formulas is returned.
  462. As a simple example, :func:`~mpmath.identify` will find an algebraic
  463. formula for the golden ratio::
  464. >>> from mpmath import *
  465. >>> mp.dps = 15; mp.pretty = True
  466. >>> identify(phi)
  467. '((1+sqrt(5))/2)'
  468. :func:`~mpmath.identify` can identify simple algebraic numbers and simple
  469. combinations of given base constants, as well as certain basic
  470. transformations thereof. More specifically, :func:`~mpmath.identify`
  471. looks for the following:
  472. 1. Fractions
  473. 2. Quadratic algebraic numbers
  474. 3. Rational linear combinations of the base constants
  475. 4. Any of the above after first transforming `x` into `f(x)` where
  476. `f(x)` is `1/x`, `\sqrt x`, `x^2`, `\log x` or `\exp x`, either
  477. directly or with `x` or `f(x)` multiplied or divided by one of
  478. the base constants
  479. 5. Products of fractional powers of the base constants and
  480. small integers
  481. Base constants can be given as a list of strings representing mpmath
  482. expressions (:func:`~mpmath.identify` will ``eval`` the strings to numerical
  483. values and use the original strings for the output), or as a dict of
  484. formula:value pairs.
  485. In order not to produce spurious results, :func:`~mpmath.identify` should
  486. be used with high precision; preferably 50 digits or more.
  487. **Examples**
  488. Simple identifications can be performed safely at standard
  489. precision. Here the default recognition of rational, algebraic,
  490. and exp/log of algebraic numbers is demonstrated::
  491. >>> mp.dps = 15
  492. >>> identify(0.22222222222222222)
  493. '(2/9)'
  494. >>> identify(1.9662210973805663)
  495. 'sqrt(((24+sqrt(48))/8))'
  496. >>> identify(4.1132503787829275)
  497. 'exp((sqrt(8)/2))'
  498. >>> identify(0.881373587019543)
  499. 'log(((2+sqrt(8))/2))'
  500. By default, :func:`~mpmath.identify` does not recognize `\pi`. At standard
  501. precision it finds a not too useful approximation. At slightly
  502. increased precision, this approximation is no longer accurate
  503. enough and :func:`~mpmath.identify` more correctly returns ``None``::
  504. >>> identify(pi)
  505. '(2**(176/117)*3**(20/117)*5**(35/39))/(7**(92/117))'
  506. >>> mp.dps = 30
  507. >>> identify(pi)
  508. >>>
  509. Numbers such as `\pi`, and simple combinations of user-defined
  510. constants, can be identified if they are provided explicitly::
  511. >>> identify(3*pi-2*e, ['pi', 'e'])
  512. '(3*pi + (-2)*e)'
  513. Here is an example using a dict of constants. Note that the
  514. constants need not be "atomic"; :func:`~mpmath.identify` can just
  515. as well express the given number in terms of expressions
  516. given by formulas::
  517. >>> identify(pi+e, {'a':pi+2, 'b':2*e})
  518. '((-2) + 1*a + (1/2)*b)'
  519. Next, we attempt some identifications with a set of base constants.
  520. It is necessary to increase the precision a bit.
  521. >>> mp.dps = 50
  522. >>> base = ['sqrt(2)','pi','log(2)']
  523. >>> identify(0.25, base)
  524. '(1/4)'
  525. >>> identify(3*pi + 2*sqrt(2) + 5*log(2)/7, base)
  526. '(2*sqrt(2) + 3*pi + (5/7)*log(2))'
  527. >>> identify(exp(pi+2), base)
  528. 'exp((2 + 1*pi))'
  529. >>> identify(1/(3+sqrt(2)), base)
  530. '((3/7) + (-1/7)*sqrt(2))'
  531. >>> identify(sqrt(2)/(3*pi+4), base)
  532. 'sqrt(2)/(4 + 3*pi)'
  533. >>> identify(5**(mpf(1)/3)*pi*log(2)**2, base)
  534. '5**(1/3)*pi*log(2)**2'
  535. An example of an erroneous solution being found when too low
  536. precision is used::
  537. >>> mp.dps = 15
  538. >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)'])
  539. '((11/25) + (-158/75)*pi + (76/75)*e + (44/15)*sqrt(2))'
  540. >>> mp.dps = 50
  541. >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)'])
  542. '1/(3*pi + (-4)*e + 2*sqrt(2))'
  543. **Finding approximate solutions**
  544. The tolerance ``tol`` defaults to 3/4 of the working precision.
  545. Lowering the tolerance is useful for finding approximate matches.
  546. We can for example try to generate approximations for pi::
  547. >>> mp.dps = 15
  548. >>> identify(pi, tol=1e-2)
  549. '(22/7)'
  550. >>> identify(pi, tol=1e-3)
  551. '(355/113)'
  552. >>> identify(pi, tol=1e-10)
  553. '(5**(339/269))/(2**(64/269)*3**(13/269)*7**(92/269))'
  554. With ``full=True``, and by supplying a few base constants,
  555. ``identify`` can generate almost endless lists of approximations
  556. for any number (the output below has been truncated to show only
  557. the first few)::
  558. >>> for p in identify(pi, ['e', 'catalan'], tol=1e-5, full=True):
  559. ... print(p)
  560. ... # doctest: +ELLIPSIS
  561. e/log((6 + (-4/3)*e))
  562. (3**3*5*e*catalan**2)/(2*7**2)
  563. sqrt(((-13) + 1*e + 22*catalan))
  564. log(((-6) + 24*e + 4*catalan)/e)
  565. exp(catalan*((-1/5) + (8/15)*e))
  566. catalan*(6 + (-6)*e + 15*catalan)
  567. sqrt((5 + 26*e + (-3)*catalan))/e
  568. e*sqrt(((-27) + 2*e + 25*catalan))
  569. log(((-1) + (-11)*e + 59*catalan))
  570. ((3/20) + (21/20)*e + (3/20)*catalan)
  571. ...
  572. The numerical values are roughly as close to `\pi` as permitted by the
  573. specified tolerance:
  574. >>> e/log(6-4*e/3)
  575. 3.14157719846001
  576. >>> 135*e*catalan**2/98
  577. 3.14166950419369
  578. >>> sqrt(e-13+22*catalan)
  579. 3.14158000062992
  580. >>> log(24*e-6+4*catalan)-1
  581. 3.14158791577159
  582. **Symbolic processing**
  583. The output formula can be evaluated as a Python expression.
  584. Note however that if fractions (like '2/3') are present in
  585. the formula, Python's :func:`~mpmath.eval()` may erroneously perform
  586. integer division. Note also that the output is not necessarily
  587. in the algebraically simplest form::
  588. >>> identify(sqrt(2))
  589. '(sqrt(8)/2)'
  590. As a solution to both problems, consider using SymPy's
  591. :func:`~mpmath.sympify` to convert the formula into a symbolic expression.
  592. SymPy can be used to pretty-print or further simplify the formula
  593. symbolically::
  594. >>> from sympy import sympify # doctest: +SKIP
  595. >>> sympify(identify(sqrt(2))) # doctest: +SKIP
  596. 2**(1/2)
  597. Sometimes :func:`~mpmath.identify` can simplify an expression further than
  598. a symbolic algorithm::
  599. >>> from sympy import simplify # doctest: +SKIP
  600. >>> x = sympify('-1/(-3/2+(1/2)*5**(1/2))*(3/2-1/2*5**(1/2))**(1/2)') # doctest: +SKIP
  601. >>> x # doctest: +SKIP
  602. (3/2 - 5**(1/2)/2)**(-1/2)
  603. >>> x = simplify(x) # doctest: +SKIP
  604. >>> x # doctest: +SKIP
  605. 2/(6 - 2*5**(1/2))**(1/2)
  606. >>> mp.dps = 30 # doctest: +SKIP
  607. >>> x = sympify(identify(x.evalf(30))) # doctest: +SKIP
  608. >>> x # doctest: +SKIP
  609. 1/2 + 5**(1/2)/2
  610. (In fact, this functionality is available directly in SymPy as the
  611. function :func:`~mpmath.nsimplify`, which is essentially a wrapper for
  612. :func:`~mpmath.identify`.)
  613. **Miscellaneous issues and limitations**
  614. The input `x` must be a real number. All base constants must be
  615. positive real numbers and must not be rationals or rational linear
  616. combinations of each other.
  617. The worst-case computation time grows quickly with the number of
  618. base constants. Already with 3 or 4 base constants,
  619. :func:`~mpmath.identify` may require several seconds to finish. To search
  620. for relations among a large number of constants, you should
  621. consider using :func:`~mpmath.pslq` directly.
  622. The extended transformations are applied to x, not the constants
  623. separately. As a result, ``identify`` will for example be able to
  624. recognize ``exp(2*pi+3)`` with ``pi`` given as a base constant, but
  625. not ``2*exp(pi)+3``. It will be able to recognize the latter if
  626. ``exp(pi)`` is given explicitly as a base constant.
  627. """
  628. solutions = []
  629. def addsolution(s):
  630. if verbose: print("Found: ", s)
  631. solutions.append(s)
  632. x = ctx.mpf(x)
  633. # Further along, x will be assumed positive
  634. if x == 0:
  635. if full: return ['0']
  636. else: return '0'
  637. if x < 0:
  638. sol = ctx.identify(-x, constants, tol, maxcoeff, full, verbose)
  639. if sol is None:
  640. return sol
  641. if full:
  642. return ["-(%s)"%s for s in sol]
  643. else:
  644. return "-(%s)" % sol
  645. if tol:
  646. tol = ctx.mpf(tol)
  647. else:
  648. tol = ctx.eps**0.7
  649. M = maxcoeff
  650. if constants:
  651. if isinstance(constants, dict):
  652. constants = [(ctx.mpf(v), name) for (name, v) in sorted(constants.items())]
  653. else:
  654. namespace = dict((name, getattr(ctx,name)) for name in dir(ctx))
  655. constants = [(eval(p, namespace), p) for p in constants]
  656. else:
  657. constants = []
  658. # We always want to find at least rational terms
  659. if 1 not in [value for (name, value) in constants]:
  660. constants = [(ctx.mpf(1), '1')] + constants
  661. # PSLQ with simple algebraic and functional transformations
  662. for ft, ftn, red in transforms:
  663. for c, cn in constants:
  664. if red and cn == '1':
  665. continue
  666. t = ft(ctx,x,c)
  667. # Prevent exponential transforms from wreaking havoc
  668. if abs(t) > M**2 or abs(t) < tol:
  669. continue
  670. # Linear combination of base constants
  671. r = ctx.pslq([t] + [a[0] for a in constants], tol, M)
  672. s = None
  673. if r is not None and max(abs(uw) for uw in r) <= M and r[0]:
  674. s = pslqstring(r, constants)
  675. # Quadratic algebraic numbers
  676. else:
  677. q = ctx.pslq([ctx.one, t, t**2], tol, M)
  678. if q is not None and len(q) == 3 and q[2]:
  679. aa, bb, cc = q
  680. if max(abs(aa),abs(bb),abs(cc)) <= M:
  681. s = quadraticstring(ctx,t,aa,bb,cc)
  682. if s:
  683. if cn == '1' and ('/$c' in ftn):
  684. s = ftn.replace('$y', s).replace('/$c', '')
  685. else:
  686. s = ftn.replace('$y', s).replace('$c', cn)
  687. addsolution(s)
  688. if not full: return solutions[0]
  689. if verbose:
  690. print(".")
  691. # Check for a direct multiplicative formula
  692. if x != 1:
  693. # Allow fractional powers of fractions
  694. ilogs = [2,3,5,7]
  695. # Watch out for existing fractional powers of fractions
  696. logs = []
  697. for a, s in constants:
  698. if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1)) for i in ilogs):
  699. logs.append((ctx.ln(a), s))
  700. logs = [(ctx.ln(i),str(i)) for i in ilogs] + logs
  701. r = ctx.pslq([ctx.ln(x)] + [a[0] for a in logs], tol, M)
  702. if r is not None and max(abs(uw) for uw in r) <= M and r[0]:
  703. addsolution(prodstring(r, logs))
  704. if not full: return solutions[0]
  705. if full:
  706. return sorted(solutions, key=len)
  707. else:
  708. return None
  709. IdentificationMethods.pslq = pslq
  710. IdentificationMethods.findpoly = findpoly
  711. IdentificationMethods.identify = identify
  712. if __name__ == '__main__':
  713. import doctest
  714. doctest.testmod()