monomials.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. """Tools and arithmetics for monomials of distributed polynomials. """
  2. from itertools import combinations_with_replacement, product
  3. from textwrap import dedent
  4. from sympy.core import Mul, S, Tuple, sympify
  5. from sympy.polys.polyerrors import ExactQuotientFailed
  6. from sympy.polys.polyutils import PicklableWithSlots, dict_from_expr
  7. from sympy.utilities import public
  8. from sympy.utilities.iterables import is_sequence, iterable
  9. @public
  10. def itermonomials(variables, max_degrees, min_degrees=None):
  11. r"""
  12. ``max_degrees`` and ``min_degrees`` are either both integers or both lists.
  13. Unless otherwise specified, ``min_degrees`` is either ``0`` or
  14. ``[0, ..., 0]``.
  15. A generator of all monomials ``monom`` is returned, such that
  16. either
  17. ``min_degree <= total_degree(monom) <= max_degree``,
  18. or
  19. ``min_degrees[i] <= degree_list(monom)[i] <= max_degrees[i]``,
  20. for all ``i``.
  21. Case I. ``max_degrees`` and ``min_degrees`` are both integers
  22. =============================================================
  23. Given a set of variables $V$ and a min_degree $N$ and a max_degree $M$
  24. generate a set of monomials of degree less than or equal to $N$ and greater
  25. than or equal to $M$. The total number of monomials in commutative
  26. variables is huge and is given by the following formula if $M = 0$:
  27. .. math::
  28. \frac{(\#V + N)!}{\#V! N!}
  29. For example if we would like to generate a dense polynomial of
  30. a total degree $N = 50$ and $M = 0$, which is the worst case, in 5
  31. variables, assuming that exponents and all of coefficients are 32-bit long
  32. and stored in an array we would need almost 80 GiB of memory! Fortunately
  33. most polynomials, that we will encounter, are sparse.
  34. Consider monomials in commutative variables $x$ and $y$
  35. and non-commutative variables $a$ and $b$::
  36. >>> from sympy import symbols
  37. >>> from sympy.polys.monomials import itermonomials
  38. >>> from sympy.polys.orderings import monomial_key
  39. >>> from sympy.abc import x, y
  40. >>> sorted(itermonomials([x, y], 2), key=monomial_key('grlex', [y, x]))
  41. [1, x, y, x**2, x*y, y**2]
  42. >>> sorted(itermonomials([x, y], 3), key=monomial_key('grlex', [y, x]))
  43. [1, x, y, x**2, x*y, y**2, x**3, x**2*y, x*y**2, y**3]
  44. >>> a, b = symbols('a, b', commutative=False)
  45. >>> set(itermonomials([a, b, x], 2))
  46. {1, a, a**2, b, b**2, x, x**2, a*b, b*a, x*a, x*b}
  47. >>> sorted(itermonomials([x, y], 2, 1), key=monomial_key('grlex', [y, x]))
  48. [x, y, x**2, x*y, y**2]
  49. Case II. ``max_degrees`` and ``min_degrees`` are both lists
  50. ===========================================================
  51. If ``max_degrees = [d_1, ..., d_n]`` and
  52. ``min_degrees = [e_1, ..., e_n]``, the number of monomials generated
  53. is:
  54. .. math::
  55. (d_1 - e_1 + 1) (d_2 - e_2 + 1) \cdots (d_n - e_n + 1)
  56. Let us generate all monomials ``monom`` in variables $x$ and $y$
  57. such that ``[1, 2][i] <= degree_list(monom)[i] <= [2, 4][i]``,
  58. ``i = 0, 1`` ::
  59. >>> from sympy import symbols
  60. >>> from sympy.polys.monomials import itermonomials
  61. >>> from sympy.polys.orderings import monomial_key
  62. >>> from sympy.abc import x, y
  63. >>> sorted(itermonomials([x, y], [2, 4], [1, 2]), reverse=True, key=monomial_key('lex', [x, y]))
  64. [x**2*y**4, x**2*y**3, x**2*y**2, x*y**4, x*y**3, x*y**2]
  65. """
  66. n = len(variables)
  67. if is_sequence(max_degrees):
  68. if len(max_degrees) != n:
  69. raise ValueError('Argument sizes do not match')
  70. if min_degrees is None:
  71. min_degrees = [0]*n
  72. elif not is_sequence(min_degrees):
  73. raise ValueError('min_degrees is not a list')
  74. else:
  75. if len(min_degrees) != n:
  76. raise ValueError('Argument sizes do not match')
  77. if any(i < 0 for i in min_degrees):
  78. raise ValueError("min_degrees cannot contain negative numbers")
  79. total_degree = False
  80. else:
  81. max_degree = max_degrees
  82. if max_degree < 0:
  83. raise ValueError("max_degrees cannot be negative")
  84. if min_degrees is None:
  85. min_degree = 0
  86. else:
  87. if min_degrees < 0:
  88. raise ValueError("min_degrees cannot be negative")
  89. min_degree = min_degrees
  90. total_degree = True
  91. if total_degree:
  92. if min_degree > max_degree:
  93. return
  94. if not variables or max_degree == 0:
  95. yield S.One
  96. return
  97. # Force to list in case of passed tuple or other incompatible collection
  98. variables = list(variables) + [S.One]
  99. if all(variable.is_commutative for variable in variables):
  100. monomials_list_comm = []
  101. for item in combinations_with_replacement(variables, max_degree):
  102. powers = {variable: 0 for variable in variables}
  103. for variable in item:
  104. if variable != 1:
  105. powers[variable] += 1
  106. if sum(powers.values()) >= min_degree:
  107. monomials_list_comm.append(Mul(*item))
  108. yield from set(monomials_list_comm)
  109. else:
  110. monomials_list_non_comm = []
  111. for item in product(variables, repeat=max_degree):
  112. powers = {variable: 0 for variable in variables}
  113. for variable in item:
  114. if variable != 1:
  115. powers[variable] += 1
  116. if sum(powers.values()) >= min_degree:
  117. monomials_list_non_comm.append(Mul(*item))
  118. yield from set(monomials_list_non_comm)
  119. else:
  120. if any(min_degrees[i] > max_degrees[i] for i in range(n)):
  121. raise ValueError('min_degrees[i] must be <= max_degrees[i] for all i')
  122. power_lists = []
  123. for var, min_d, max_d in zip(variables, min_degrees, max_degrees):
  124. power_lists.append([var**i for i in range(min_d, max_d + 1)])
  125. for powers in product(*power_lists):
  126. yield Mul(*powers)
  127. def monomial_count(V, N):
  128. r"""
  129. Computes the number of monomials.
  130. The number of monomials is given by the following formula:
  131. .. math::
  132. \frac{(\#V + N)!}{\#V! N!}
  133. where `N` is a total degree and `V` is a set of variables.
  134. Examples
  135. ========
  136. >>> from sympy.polys.monomials import itermonomials, monomial_count
  137. >>> from sympy.polys.orderings import monomial_key
  138. >>> from sympy.abc import x, y
  139. >>> monomial_count(2, 2)
  140. 6
  141. >>> M = list(itermonomials([x, y], 2))
  142. >>> sorted(M, key=monomial_key('grlex', [y, x]))
  143. [1, x, y, x**2, x*y, y**2]
  144. >>> len(M)
  145. 6
  146. """
  147. from sympy.functions.combinatorial.factorials import factorial
  148. return factorial(V + N) / factorial(V) / factorial(N)
  149. def monomial_mul(A, B):
  150. """
  151. Multiplication of tuples representing monomials.
  152. Examples
  153. ========
  154. Lets multiply `x**3*y**4*z` with `x*y**2`::
  155. >>> from sympy.polys.monomials import monomial_mul
  156. >>> monomial_mul((3, 4, 1), (1, 2, 0))
  157. (4, 6, 1)
  158. which gives `x**4*y**5*z`.
  159. """
  160. return tuple([ a + b for a, b in zip(A, B) ])
  161. def monomial_div(A, B):
  162. """
  163. Division of tuples representing monomials.
  164. Examples
  165. ========
  166. Lets divide `x**3*y**4*z` by `x*y**2`::
  167. >>> from sympy.polys.monomials import monomial_div
  168. >>> monomial_div((3, 4, 1), (1, 2, 0))
  169. (2, 2, 1)
  170. which gives `x**2*y**2*z`. However::
  171. >>> monomial_div((3, 4, 1), (1, 2, 2)) is None
  172. True
  173. `x*y**2*z**2` does not divide `x**3*y**4*z`.
  174. """
  175. C = monomial_ldiv(A, B)
  176. if all(c >= 0 for c in C):
  177. return tuple(C)
  178. else:
  179. return None
  180. def monomial_ldiv(A, B):
  181. """
  182. Division of tuples representing monomials.
  183. Examples
  184. ========
  185. Lets divide `x**3*y**4*z` by `x*y**2`::
  186. >>> from sympy.polys.monomials import monomial_ldiv
  187. >>> monomial_ldiv((3, 4, 1), (1, 2, 0))
  188. (2, 2, 1)
  189. which gives `x**2*y**2*z`.
  190. >>> monomial_ldiv((3, 4, 1), (1, 2, 2))
  191. (2, 2, -1)
  192. which gives `x**2*y**2*z**-1`.
  193. """
  194. return tuple([ a - b for a, b in zip(A, B) ])
  195. def monomial_pow(A, n):
  196. """Return the n-th pow of the monomial. """
  197. return tuple([ a*n for a in A ])
  198. def monomial_gcd(A, B):
  199. """
  200. Greatest common divisor of tuples representing monomials.
  201. Examples
  202. ========
  203. Lets compute GCD of `x*y**4*z` and `x**3*y**2`::
  204. >>> from sympy.polys.monomials import monomial_gcd
  205. >>> monomial_gcd((1, 4, 1), (3, 2, 0))
  206. (1, 2, 0)
  207. which gives `x*y**2`.
  208. """
  209. return tuple([ min(a, b) for a, b in zip(A, B) ])
  210. def monomial_lcm(A, B):
  211. """
  212. Least common multiple of tuples representing monomials.
  213. Examples
  214. ========
  215. Lets compute LCM of `x*y**4*z` and `x**3*y**2`::
  216. >>> from sympy.polys.monomials import monomial_lcm
  217. >>> monomial_lcm((1, 4, 1), (3, 2, 0))
  218. (3, 4, 1)
  219. which gives `x**3*y**4*z`.
  220. """
  221. return tuple([ max(a, b) for a, b in zip(A, B) ])
  222. def monomial_divides(A, B):
  223. """
  224. Does there exist a monomial X such that XA == B?
  225. Examples
  226. ========
  227. >>> from sympy.polys.monomials import monomial_divides
  228. >>> monomial_divides((1, 2), (3, 4))
  229. True
  230. >>> monomial_divides((1, 2), (0, 2))
  231. False
  232. """
  233. return all(a <= b for a, b in zip(A, B))
  234. def monomial_max(*monoms):
  235. """
  236. Returns maximal degree for each variable in a set of monomials.
  237. Examples
  238. ========
  239. Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`.
  240. We wish to find out what is the maximal degree for each of `x`, `y`
  241. and `z` variables::
  242. >>> from sympy.polys.monomials import monomial_max
  243. >>> monomial_max((3,4,5), (0,5,1), (6,3,9))
  244. (6, 5, 9)
  245. """
  246. M = list(monoms[0])
  247. for N in monoms[1:]:
  248. for i, n in enumerate(N):
  249. M[i] = max(M[i], n)
  250. return tuple(M)
  251. def monomial_min(*monoms):
  252. """
  253. Returns minimal degree for each variable in a set of monomials.
  254. Examples
  255. ========
  256. Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`.
  257. We wish to find out what is the minimal degree for each of `x`, `y`
  258. and `z` variables::
  259. >>> from sympy.polys.monomials import monomial_min
  260. >>> monomial_min((3,4,5), (0,5,1), (6,3,9))
  261. (0, 3, 1)
  262. """
  263. M = list(monoms[0])
  264. for N in monoms[1:]:
  265. for i, n in enumerate(N):
  266. M[i] = min(M[i], n)
  267. return tuple(M)
  268. def monomial_deg(M):
  269. """
  270. Returns the total degree of a monomial.
  271. Examples
  272. ========
  273. The total degree of `xy^2` is 3:
  274. >>> from sympy.polys.monomials import monomial_deg
  275. >>> monomial_deg((1, 2))
  276. 3
  277. """
  278. return sum(M)
  279. def term_div(a, b, domain):
  280. """Division of two terms in over a ring/field. """
  281. a_lm, a_lc = a
  282. b_lm, b_lc = b
  283. monom = monomial_div(a_lm, b_lm)
  284. if domain.is_Field:
  285. if monom is not None:
  286. return monom, domain.quo(a_lc, b_lc)
  287. else:
  288. return None
  289. else:
  290. if not (monom is None or a_lc % b_lc):
  291. return monom, domain.quo(a_lc, b_lc)
  292. else:
  293. return None
  294. class MonomialOps:
  295. """Code generator of fast monomial arithmetic functions. """
  296. def __init__(self, ngens):
  297. self.ngens = ngens
  298. def _build(self, code, name):
  299. ns = {}
  300. exec(code, ns)
  301. return ns[name]
  302. def _vars(self, name):
  303. return [ "%s%s" % (name, i) for i in range(self.ngens) ]
  304. def mul(self):
  305. name = "monomial_mul"
  306. template = dedent("""\
  307. def %(name)s(A, B):
  308. (%(A)s,) = A
  309. (%(B)s,) = B
  310. return (%(AB)s,)
  311. """)
  312. A = self._vars("a")
  313. B = self._vars("b")
  314. AB = [ "%s + %s" % (a, b) for a, b in zip(A, B) ]
  315. code = template % {"name": name, "A": ", ".join(A), "B": ", ".join(B), "AB": ", ".join(AB)}
  316. return self._build(code, name)
  317. def pow(self):
  318. name = "monomial_pow"
  319. template = dedent("""\
  320. def %(name)s(A, k):
  321. (%(A)s,) = A
  322. return (%(Ak)s,)
  323. """)
  324. A = self._vars("a")
  325. Ak = [ "%s*k" % a for a in A ]
  326. code = template % {"name": name, "A": ", ".join(A), "Ak": ", ".join(Ak)}
  327. return self._build(code, name)
  328. def mulpow(self):
  329. name = "monomial_mulpow"
  330. template = dedent("""\
  331. def %(name)s(A, B, k):
  332. (%(A)s,) = A
  333. (%(B)s,) = B
  334. return (%(ABk)s,)
  335. """)
  336. A = self._vars("a")
  337. B = self._vars("b")
  338. ABk = [ "%s + %s*k" % (a, b) for a, b in zip(A, B) ]
  339. code = template % {"name": name, "A": ", ".join(A), "B": ", ".join(B), "ABk": ", ".join(ABk)}
  340. return self._build(code, name)
  341. def ldiv(self):
  342. name = "monomial_ldiv"
  343. template = dedent("""\
  344. def %(name)s(A, B):
  345. (%(A)s,) = A
  346. (%(B)s,) = B
  347. return (%(AB)s,)
  348. """)
  349. A = self._vars("a")
  350. B = self._vars("b")
  351. AB = [ "%s - %s" % (a, b) for a, b in zip(A, B) ]
  352. code = template % {"name": name, "A": ", ".join(A), "B": ", ".join(B), "AB": ", ".join(AB)}
  353. return self._build(code, name)
  354. def div(self):
  355. name = "monomial_div"
  356. template = dedent("""\
  357. def %(name)s(A, B):
  358. (%(A)s,) = A
  359. (%(B)s,) = B
  360. %(RAB)s
  361. return (%(R)s,)
  362. """)
  363. A = self._vars("a")
  364. B = self._vars("b")
  365. RAB = [ "r%(i)s = a%(i)s - b%(i)s\n if r%(i)s < 0: return None" % {"i": i} for i in range(self.ngens) ]
  366. R = self._vars("r")
  367. code = template % {"name": name, "A": ", ".join(A), "B": ", ".join(B), "RAB": "\n ".join(RAB), "R": ", ".join(R)}
  368. return self._build(code, name)
  369. def lcm(self):
  370. name = "monomial_lcm"
  371. template = dedent("""\
  372. def %(name)s(A, B):
  373. (%(A)s,) = A
  374. (%(B)s,) = B
  375. return (%(AB)s,)
  376. """)
  377. A = self._vars("a")
  378. B = self._vars("b")
  379. AB = [ "%s if %s >= %s else %s" % (a, a, b, b) for a, b in zip(A, B) ]
  380. code = template % {"name": name, "A": ", ".join(A), "B": ", ".join(B), "AB": ", ".join(AB)}
  381. return self._build(code, name)
  382. def gcd(self):
  383. name = "monomial_gcd"
  384. template = dedent("""\
  385. def %(name)s(A, B):
  386. (%(A)s,) = A
  387. (%(B)s,) = B
  388. return (%(AB)s,)
  389. """)
  390. A = self._vars("a")
  391. B = self._vars("b")
  392. AB = [ "%s if %s <= %s else %s" % (a, a, b, b) for a, b in zip(A, B) ]
  393. code = template % {"name": name, "A": ", ".join(A), "B": ", ".join(B), "AB": ", ".join(AB)}
  394. return self._build(code, name)
  395. @public
  396. class Monomial(PicklableWithSlots):
  397. """Class representing a monomial, i.e. a product of powers. """
  398. __slots__ = ('exponents', 'gens')
  399. def __init__(self, monom, gens=None):
  400. if not iterable(monom):
  401. rep, gens = dict_from_expr(sympify(monom), gens=gens)
  402. if len(rep) == 1 and list(rep.values())[0] == 1:
  403. monom = list(rep.keys())[0]
  404. else:
  405. raise ValueError("Expected a monomial got {}".format(monom))
  406. self.exponents = tuple(map(int, monom))
  407. self.gens = gens
  408. def rebuild(self, exponents, gens=None):
  409. return self.__class__(exponents, gens or self.gens)
  410. def __len__(self):
  411. return len(self.exponents)
  412. def __iter__(self):
  413. return iter(self.exponents)
  414. def __getitem__(self, item):
  415. return self.exponents[item]
  416. def __hash__(self):
  417. return hash((self.__class__.__name__, self.exponents, self.gens))
  418. def __str__(self):
  419. if self.gens:
  420. return "*".join([ "%s**%s" % (gen, exp) for gen, exp in zip(self.gens, self.exponents) ])
  421. else:
  422. return "%s(%s)" % (self.__class__.__name__, self.exponents)
  423. def as_expr(self, *gens):
  424. """Convert a monomial instance to a SymPy expression. """
  425. gens = gens or self.gens
  426. if not gens:
  427. raise ValueError(
  428. "Cannot convert %s to an expression without generators" % self)
  429. return Mul(*[ gen**exp for gen, exp in zip(gens, self.exponents) ])
  430. def __eq__(self, other):
  431. if isinstance(other, Monomial):
  432. exponents = other.exponents
  433. elif isinstance(other, (tuple, Tuple)):
  434. exponents = other
  435. else:
  436. return False
  437. return self.exponents == exponents
  438. def __ne__(self, other):
  439. return not self == other
  440. def __mul__(self, other):
  441. if isinstance(other, Monomial):
  442. exponents = other.exponents
  443. elif isinstance(other, (tuple, Tuple)):
  444. exponents = other
  445. else:
  446. raise NotImplementedError
  447. return self.rebuild(monomial_mul(self.exponents, exponents))
  448. def __truediv__(self, other):
  449. if isinstance(other, Monomial):
  450. exponents = other.exponents
  451. elif isinstance(other, (tuple, Tuple)):
  452. exponents = other
  453. else:
  454. raise NotImplementedError
  455. result = monomial_div(self.exponents, exponents)
  456. if result is not None:
  457. return self.rebuild(result)
  458. else:
  459. raise ExactQuotientFailed(self, Monomial(other))
  460. __floordiv__ = __truediv__
  461. def __pow__(self, other):
  462. n = int(other)
  463. if not n:
  464. return self.rebuild([0]*len(self))
  465. elif n > 0:
  466. exponents = self.exponents
  467. for i in range(1, n):
  468. exponents = monomial_mul(exponents, self.exponents)
  469. return self.rebuild(exponents)
  470. else:
  471. raise ValueError("a non-negative integer expected, got %s" % other)
  472. def gcd(self, other):
  473. """Greatest common divisor of monomials. """
  474. if isinstance(other, Monomial):
  475. exponents = other.exponents
  476. elif isinstance(other, (tuple, Tuple)):
  477. exponents = other
  478. else:
  479. raise TypeError(
  480. "an instance of Monomial class expected, got %s" % other)
  481. return self.rebuild(monomial_gcd(self.exponents, exponents))
  482. def lcm(self, other):
  483. """Least common multiple of monomials. """
  484. if isinstance(other, Monomial):
  485. exponents = other.exponents
  486. elif isinstance(other, (tuple, Tuple)):
  487. exponents = other
  488. else:
  489. raise TypeError(
  490. "an instance of Monomial class expected, got %s" % other)
  491. return self.rebuild(monomial_lcm(self.exponents, exponents))