polyfuncs.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. """High-level polynomials manipulation functions. """
  2. from sympy.core import S, Basic, Add, Mul, symbols, Dummy
  3. from sympy.polys.polyerrors import (
  4. PolificationFailed, ComputationFailed,
  5. MultivariatePolynomialError, OptionError)
  6. from sympy.polys.polyoptions import allowed_flags
  7. from sympy.polys.polytools import (
  8. poly_from_expr, parallel_poly_from_expr, Poly)
  9. from sympy.polys.specialpolys import (
  10. symmetric_poly, interpolating_poly)
  11. from sympy.utilities import numbered_symbols, take, public
  12. @public
  13. def symmetrize(F, *gens, **args):
  14. r"""
  15. Rewrite a polynomial in terms of elementary symmetric polynomials.
  16. A symmetric polynomial is a multivariate polynomial that remains invariant
  17. under any variable permutation, i.e., if `f = f(x_1, x_2, \dots, x_n)`,
  18. then `f = f(x_{i_1}, x_{i_2}, \dots, x_{i_n})`, where
  19. `(i_1, i_2, \dots, i_n)` is a permutation of `(1, 2, \dots, n)` (an
  20. element of the group `S_n`).
  21. Returns a tuple of symmetric polynomials ``(f1, f2, ..., fn)`` such that
  22. ``f = f1 + f2 + ... + fn``.
  23. Examples
  24. ========
  25. >>> from sympy.polys.polyfuncs import symmetrize
  26. >>> from sympy.abc import x, y
  27. >>> symmetrize(x**2 + y**2)
  28. (-2*x*y + (x + y)**2, 0)
  29. >>> symmetrize(x**2 + y**2, formal=True)
  30. (s1**2 - 2*s2, 0, [(s1, x + y), (s2, x*y)])
  31. >>> symmetrize(x**2 - y**2)
  32. (-2*x*y + (x + y)**2, -2*y**2)
  33. >>> symmetrize(x**2 - y**2, formal=True)
  34. (s1**2 - 2*s2, -2*y**2, [(s1, x + y), (s2, x*y)])
  35. """
  36. allowed_flags(args, ['formal', 'symbols'])
  37. iterable = True
  38. if not hasattr(F, '__iter__'):
  39. iterable = False
  40. F = [F]
  41. try:
  42. F, opt = parallel_poly_from_expr(F, *gens, **args)
  43. except PolificationFailed as exc:
  44. result = []
  45. for expr in exc.exprs:
  46. if expr.is_Number:
  47. result.append((expr, S.Zero))
  48. else:
  49. raise ComputationFailed('symmetrize', len(F), exc)
  50. if not iterable:
  51. result, = result
  52. if not exc.opt.formal:
  53. return result
  54. else:
  55. if iterable:
  56. return result, []
  57. else:
  58. return result + ([],)
  59. polys, symbols = [], opt.symbols
  60. gens, dom = opt.gens, opt.domain
  61. for i in range(len(gens)):
  62. poly = symmetric_poly(i + 1, gens, polys=True)
  63. polys.append((next(symbols), poly.set_domain(dom)))
  64. indices = list(range(len(gens) - 1))
  65. weights = list(range(len(gens), 0, -1))
  66. result = []
  67. for f in F:
  68. symmetric = []
  69. if not f.is_homogeneous:
  70. symmetric.append(f.TC())
  71. f -= f.TC().as_poly(f.gens)
  72. while f:
  73. _height, _monom, _coeff = -1, None, None
  74. for i, (monom, coeff) in enumerate(f.terms()):
  75. if all(monom[i] >= monom[i + 1] for i in indices):
  76. height = max([n*m for n, m in zip(weights, monom)])
  77. if height > _height:
  78. _height, _monom, _coeff = height, monom, coeff
  79. if _height != -1:
  80. monom, coeff = _monom, _coeff
  81. else:
  82. break
  83. exponents = []
  84. for m1, m2 in zip(monom, monom[1:] + (0,)):
  85. exponents.append(m1 - m2)
  86. term = [s**n for (s, _), n in zip(polys, exponents)]
  87. poly = [p**n for (_, p), n in zip(polys, exponents)]
  88. symmetric.append(Mul(coeff, *term))
  89. product = poly[0].mul(coeff)
  90. for p in poly[1:]:
  91. product = product.mul(p)
  92. f -= product
  93. result.append((Add(*symmetric), f.as_expr()))
  94. polys = [(s, p.as_expr()) for s, p in polys]
  95. if not opt.formal:
  96. for i, (sym, non_sym) in enumerate(result):
  97. result[i] = (sym.subs(polys), non_sym)
  98. if not iterable:
  99. result, = result
  100. if not opt.formal:
  101. return result
  102. else:
  103. if iterable:
  104. return result, polys
  105. else:
  106. return result + (polys,)
  107. @public
  108. def horner(f, *gens, **args):
  109. """
  110. Rewrite a polynomial in Horner form.
  111. Among other applications, evaluation of a polynomial at a point is optimal
  112. when it is applied using the Horner scheme ([1]).
  113. Examples
  114. ========
  115. >>> from sympy.polys.polyfuncs import horner
  116. >>> from sympy.abc import x, y, a, b, c, d, e
  117. >>> horner(9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5)
  118. x*(x*(x*(9*x + 8) + 7) + 6) + 5
  119. >>> horner(a*x**4 + b*x**3 + c*x**2 + d*x + e)
  120. e + x*(d + x*(c + x*(a*x + b)))
  121. >>> f = 4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y
  122. >>> horner(f, wrt=x)
  123. x*(x*y*(4*y + 2) + y*(2*y + 1))
  124. >>> horner(f, wrt=y)
  125. y*(x*y*(4*x + 2) + x*(2*x + 1))
  126. References
  127. ==========
  128. [1] - https://en.wikipedia.org/wiki/Horner_scheme
  129. """
  130. allowed_flags(args, [])
  131. try:
  132. F, opt = poly_from_expr(f, *gens, **args)
  133. except PolificationFailed as exc:
  134. return exc.expr
  135. form, gen = S.Zero, F.gen
  136. if F.is_univariate:
  137. for coeff in F.all_coeffs():
  138. form = form*gen + coeff
  139. else:
  140. F, gens = Poly(F, gen), gens[1:]
  141. for coeff in F.all_coeffs():
  142. form = form*gen + horner(coeff, *gens, **args)
  143. return form
  144. @public
  145. def interpolate(data, x):
  146. """
  147. Construct an interpolating polynomial for the data points
  148. evaluated at point x (which can be symbolic or numeric).
  149. Examples
  150. ========
  151. >>> from sympy.polys.polyfuncs import interpolate
  152. >>> from sympy.abc import a, b, x
  153. A list is interpreted as though it were paired with a range starting
  154. from 1:
  155. >>> interpolate([1, 4, 9, 16], x)
  156. x**2
  157. This can be made explicit by giving a list of coordinates:
  158. >>> interpolate([(1, 1), (2, 4), (3, 9)], x)
  159. x**2
  160. The (x, y) coordinates can also be given as keys and values of a
  161. dictionary (and the points need not be equispaced):
  162. >>> interpolate([(-1, 2), (1, 2), (2, 5)], x)
  163. x**2 + 1
  164. >>> interpolate({-1: 2, 1: 2, 2: 5}, x)
  165. x**2 + 1
  166. If the interpolation is going to be used only once then the
  167. value of interest can be passed instead of passing a symbol:
  168. >>> interpolate([1, 4, 9], 5)
  169. 25
  170. Symbolic coordinates are also supported:
  171. >>> [(i,interpolate((a, b), i)) for i in range(1, 4)]
  172. [(1, a), (2, b), (3, -a + 2*b)]
  173. """
  174. n = len(data)
  175. if isinstance(data, dict):
  176. if x in data:
  177. return S(data[x])
  178. X, Y = list(zip(*data.items()))
  179. else:
  180. if isinstance(data[0], tuple):
  181. X, Y = list(zip(*data))
  182. if x in X:
  183. return S(Y[X.index(x)])
  184. else:
  185. if x in range(1, n + 1):
  186. return S(data[x - 1])
  187. Y = list(data)
  188. X = list(range(1, n + 1))
  189. try:
  190. return interpolating_poly(n, x, X, Y).expand()
  191. except ValueError:
  192. d = Dummy()
  193. return interpolating_poly(n, d, X, Y).expand().subs(d, x)
  194. @public
  195. def rational_interpolate(data, degnum, X=symbols('x')):
  196. """
  197. Returns a rational interpolation, where the data points are element of
  198. any integral domain.
  199. The first argument contains the data (as a list of coordinates). The
  200. ``degnum`` argument is the degree in the numerator of the rational
  201. function. Setting it too high will decrease the maximal degree in the
  202. denominator for the same amount of data.
  203. Examples
  204. ========
  205. >>> from sympy.polys.polyfuncs import rational_interpolate
  206. >>> data = [(1, -210), (2, -35), (3, 105), (4, 231), (5, 350), (6, 465)]
  207. >>> rational_interpolate(data, 2)
  208. (105*x**2 - 525)/(x + 1)
  209. Values do not need to be integers:
  210. >>> from sympy import sympify
  211. >>> x = [1, 2, 3, 4, 5, 6]
  212. >>> y = sympify("[-1, 0, 2, 22/5, 7, 68/7]")
  213. >>> rational_interpolate(zip(x, y), 2)
  214. (3*x**2 - 7*x + 2)/(x + 1)
  215. The symbol for the variable can be changed if needed:
  216. >>> from sympy import symbols
  217. >>> z = symbols('z')
  218. >>> rational_interpolate(data, 2, X=z)
  219. (105*z**2 - 525)/(z + 1)
  220. References
  221. ==========
  222. .. [1] Algorithm is adapted from:
  223. http://axiom-wiki.newsynthesis.org/RationalInterpolation
  224. """
  225. from sympy.matrices.dense import ones
  226. xdata, ydata = list(zip(*data))
  227. k = len(xdata) - degnum - 1
  228. if k < 0:
  229. raise OptionError("Too few values for the required degree.")
  230. c = ones(degnum + k + 1, degnum + k + 2)
  231. for j in range(max(degnum, k)):
  232. for i in range(degnum + k + 1):
  233. c[i, j + 1] = c[i, j]*xdata[i]
  234. for j in range(k + 1):
  235. for i in range(degnum + k + 1):
  236. c[i, degnum + k + 1 - j] = -c[i, k - j]*ydata[i]
  237. r = c.nullspace()[0]
  238. return (sum(r[i] * X**i for i in range(degnum + 1))
  239. / sum(r[i + degnum + 1] * X**i for i in range(k + 1)))
  240. @public
  241. def viete(f, roots=None, *gens, **args):
  242. """
  243. Generate Viete's formulas for ``f``.
  244. Examples
  245. ========
  246. >>> from sympy.polys.polyfuncs import viete
  247. >>> from sympy import symbols
  248. >>> x, a, b, c, r1, r2 = symbols('x,a:c,r1:3')
  249. >>> viete(a*x**2 + b*x + c, [r1, r2], x)
  250. [(r1 + r2, -b/a), (r1*r2, c/a)]
  251. """
  252. allowed_flags(args, [])
  253. if isinstance(roots, Basic):
  254. gens, roots = (roots,) + gens, None
  255. try:
  256. f, opt = poly_from_expr(f, *gens, **args)
  257. except PolificationFailed as exc:
  258. raise ComputationFailed('viete', 1, exc)
  259. if f.is_multivariate:
  260. raise MultivariatePolynomialError(
  261. "multivariate polynomials are not allowed")
  262. n = f.degree()
  263. if n < 1:
  264. raise ValueError(
  265. "Cannot derive Viete's formulas for a constant polynomial")
  266. if roots is None:
  267. roots = numbered_symbols('r', start=1)
  268. roots = take(roots, n)
  269. if n != len(roots):
  270. raise ValueError("required %s roots, got %s" % (n, len(roots)))
  271. lc, coeffs = f.LC(), f.all_coeffs()
  272. result, sign = [], -1
  273. for i, coeff in enumerate(coeffs[1:]):
  274. poly = symmetric_poly(i + 1, roots)
  275. coeff = sign*(coeff/lc)
  276. result.append((poly, coeff))
  277. sign = -sign
  278. return result