monomials.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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 = dict()
  103. for variable in variables:
  104. powers[variable] = 0
  105. for variable in item:
  106. if variable != 1:
  107. powers[variable] += 1
  108. if sum(powers.values()) >= min_degree:
  109. monomials_list_comm.append(Mul(*item))
  110. yield from set(monomials_list_comm)
  111. else:
  112. monomials_list_non_comm = []
  113. for item in product(variables, repeat=max_degree):
  114. powers = dict()
  115. for variable in variables:
  116. powers[variable] = 0
  117. for variable in item:
  118. if variable != 1:
  119. powers[variable] += 1
  120. if sum(powers.values()) >= min_degree:
  121. monomials_list_non_comm.append(Mul(*item))
  122. yield from set(monomials_list_non_comm)
  123. else:
  124. if any(min_degrees[i] > max_degrees[i] for i in range(n)):
  125. raise ValueError('min_degrees[i] must be <= max_degrees[i] for all i')
  126. power_lists = []
  127. for var, min_d, max_d in zip(variables, min_degrees, max_degrees):
  128. power_lists.append([var**i for i in range(min_d, max_d + 1)])
  129. for powers in product(*power_lists):
  130. yield Mul(*powers)
  131. def monomial_count(V, N):
  132. r"""
  133. Computes the number of monomials.
  134. The number of monomials is given by the following formula:
  135. .. math::
  136. \frac{(\#V + N)!}{\#V! N!}
  137. where `N` is a total degree and `V` is a set of variables.
  138. Examples
  139. ========
  140. >>> from sympy.polys.monomials import itermonomials, monomial_count
  141. >>> from sympy.polys.orderings import monomial_key
  142. >>> from sympy.abc import x, y
  143. >>> monomial_count(2, 2)
  144. 6
  145. >>> M = list(itermonomials([x, y], 2))
  146. >>> sorted(M, key=monomial_key('grlex', [y, x]))
  147. [1, x, y, x**2, x*y, y**2]
  148. >>> len(M)
  149. 6
  150. """
  151. from sympy.functions.combinatorial.factorials import factorial
  152. return factorial(V + N) / factorial(V) / factorial(N)
  153. def monomial_mul(A, B):
  154. """
  155. Multiplication of tuples representing monomials.
  156. Examples
  157. ========
  158. Lets multiply `x**3*y**4*z` with `x*y**2`::
  159. >>> from sympy.polys.monomials import monomial_mul
  160. >>> monomial_mul((3, 4, 1), (1, 2, 0))
  161. (4, 6, 1)
  162. which gives `x**4*y**5*z`.
  163. """
  164. return tuple([ a + b for a, b in zip(A, B) ])
  165. def monomial_div(A, B):
  166. """
  167. Division of tuples representing monomials.
  168. Examples
  169. ========
  170. Lets divide `x**3*y**4*z` by `x*y**2`::
  171. >>> from sympy.polys.monomials import monomial_div
  172. >>> monomial_div((3, 4, 1), (1, 2, 0))
  173. (2, 2, 1)
  174. which gives `x**2*y**2*z`. However::
  175. >>> monomial_div((3, 4, 1), (1, 2, 2)) is None
  176. True
  177. `x*y**2*z**2` does not divide `x**3*y**4*z`.
  178. """
  179. C = monomial_ldiv(A, B)
  180. if all(c >= 0 for c in C):
  181. return tuple(C)
  182. else:
  183. return None
  184. def monomial_ldiv(A, B):
  185. """
  186. Division of tuples representing monomials.
  187. Examples
  188. ========
  189. Lets divide `x**3*y**4*z` by `x*y**2`::
  190. >>> from sympy.polys.monomials import monomial_ldiv
  191. >>> monomial_ldiv((3, 4, 1), (1, 2, 0))
  192. (2, 2, 1)
  193. which gives `x**2*y**2*z`.
  194. >>> monomial_ldiv((3, 4, 1), (1, 2, 2))
  195. (2, 2, -1)
  196. which gives `x**2*y**2*z**-1`.
  197. """
  198. return tuple([ a - b for a, b in zip(A, B) ])
  199. def monomial_pow(A, n):
  200. """Return the n-th pow of the monomial. """
  201. return tuple([ a*n for a in A ])
  202. def monomial_gcd(A, B):
  203. """
  204. Greatest common divisor of tuples representing monomials.
  205. Examples
  206. ========
  207. Lets compute GCD of `x*y**4*z` and `x**3*y**2`::
  208. >>> from sympy.polys.monomials import monomial_gcd
  209. >>> monomial_gcd((1, 4, 1), (3, 2, 0))
  210. (1, 2, 0)
  211. which gives `x*y**2`.
  212. """
  213. return tuple([ min(a, b) for a, b in zip(A, B) ])
  214. def monomial_lcm(A, B):
  215. """
  216. Least common multiple of tuples representing monomials.
  217. Examples
  218. ========
  219. Lets compute LCM of `x*y**4*z` and `x**3*y**2`::
  220. >>> from sympy.polys.monomials import monomial_lcm
  221. >>> monomial_lcm((1, 4, 1), (3, 2, 0))
  222. (3, 4, 1)
  223. which gives `x**3*y**4*z`.
  224. """
  225. return tuple([ max(a, b) for a, b in zip(A, B) ])
  226. def monomial_divides(A, B):
  227. """
  228. Does there exist a monomial X such that XA == B?
  229. Examples
  230. ========
  231. >>> from sympy.polys.monomials import monomial_divides
  232. >>> monomial_divides((1, 2), (3, 4))
  233. True
  234. >>> monomial_divides((1, 2), (0, 2))
  235. False
  236. """
  237. return all(a <= b for a, b in zip(A, B))
  238. def monomial_max(*monoms):
  239. """
  240. Returns maximal degree for each variable in a set of monomials.
  241. Examples
  242. ========
  243. Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`.
  244. We wish to find out what is the maximal degree for each of `x`, `y`
  245. and `z` variables::
  246. >>> from sympy.polys.monomials import monomial_max
  247. >>> monomial_max((3,4,5), (0,5,1), (6,3,9))
  248. (6, 5, 9)
  249. """
  250. M = list(monoms[0])
  251. for N in monoms[1:]:
  252. for i, n in enumerate(N):
  253. M[i] = max(M[i], n)
  254. return tuple(M)
  255. def monomial_min(*monoms):
  256. """
  257. Returns minimal degree for each variable in a set of monomials.
  258. Examples
  259. ========
  260. Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`.
  261. We wish to find out what is the minimal degree for each of `x`, `y`
  262. and `z` variables::
  263. >>> from sympy.polys.monomials import monomial_min
  264. >>> monomial_min((3,4,5), (0,5,1), (6,3,9))
  265. (0, 3, 1)
  266. """
  267. M = list(monoms[0])
  268. for N in monoms[1:]:
  269. for i, n in enumerate(N):
  270. M[i] = min(M[i], n)
  271. return tuple(M)
  272. def monomial_deg(M):
  273. """
  274. Returns the total degree of a monomial.
  275. Examples
  276. ========
  277. The total degree of `xy^2` is 3:
  278. >>> from sympy.polys.monomials import monomial_deg
  279. >>> monomial_deg((1, 2))
  280. 3
  281. """
  282. return sum(M)
  283. def term_div(a, b, domain):
  284. """Division of two terms in over a ring/field. """
  285. a_lm, a_lc = a
  286. b_lm, b_lc = b
  287. monom = monomial_div(a_lm, b_lm)
  288. if domain.is_Field:
  289. if monom is not None:
  290. return monom, domain.quo(a_lc, b_lc)
  291. else:
  292. return None
  293. else:
  294. if not (monom is None or a_lc % b_lc):
  295. return monom, domain.quo(a_lc, b_lc)
  296. else:
  297. return None
  298. class MonomialOps:
  299. """Code generator of fast monomial arithmetic functions. """
  300. def __init__(self, ngens):
  301. self.ngens = ngens
  302. def _build(self, code, name):
  303. ns = {}
  304. exec(code, ns)
  305. return ns[name]
  306. def _vars(self, name):
  307. return [ "%s%s" % (name, i) for i in range(self.ngens) ]
  308. def mul(self):
  309. name = "monomial_mul"
  310. template = dedent("""\
  311. def %(name)s(A, B):
  312. (%(A)s,) = A
  313. (%(B)s,) = B
  314. return (%(AB)s,)
  315. """)
  316. A = self._vars("a")
  317. B = self._vars("b")
  318. AB = [ "%s + %s" % (a, b) for a, b in zip(A, B) ]
  319. code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB))
  320. return self._build(code, name)
  321. def pow(self):
  322. name = "monomial_pow"
  323. template = dedent("""\
  324. def %(name)s(A, k):
  325. (%(A)s,) = A
  326. return (%(Ak)s,)
  327. """)
  328. A = self._vars("a")
  329. Ak = [ "%s*k" % a for a in A ]
  330. code = template % dict(name=name, A=", ".join(A), Ak=", ".join(Ak))
  331. return self._build(code, name)
  332. def mulpow(self):
  333. name = "monomial_mulpow"
  334. template = dedent("""\
  335. def %(name)s(A, B, k):
  336. (%(A)s,) = A
  337. (%(B)s,) = B
  338. return (%(ABk)s,)
  339. """)
  340. A = self._vars("a")
  341. B = self._vars("b")
  342. ABk = [ "%s + %s*k" % (a, b) for a, b in zip(A, B) ]
  343. code = template % dict(name=name, A=", ".join(A), B=", ".join(B), ABk=", ".join(ABk))
  344. return self._build(code, name)
  345. def ldiv(self):
  346. name = "monomial_ldiv"
  347. template = dedent("""\
  348. def %(name)s(A, B):
  349. (%(A)s,) = A
  350. (%(B)s,) = B
  351. return (%(AB)s,)
  352. """)
  353. A = self._vars("a")
  354. B = self._vars("b")
  355. AB = [ "%s - %s" % (a, b) for a, b in zip(A, B) ]
  356. code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB))
  357. return self._build(code, name)
  358. def div(self):
  359. name = "monomial_div"
  360. template = dedent("""\
  361. def %(name)s(A, B):
  362. (%(A)s,) = A
  363. (%(B)s,) = B
  364. %(RAB)s
  365. return (%(R)s,)
  366. """)
  367. A = self._vars("a")
  368. B = self._vars("b")
  369. RAB = [ "r%(i)s = a%(i)s - b%(i)s\n if r%(i)s < 0: return None" % dict(i=i) for i in range(self.ngens) ]
  370. R = self._vars("r")
  371. code = template % dict(name=name, A=", ".join(A), B=", ".join(B), RAB="\n ".join(RAB), R=", ".join(R))
  372. return self._build(code, name)
  373. def lcm(self):
  374. name = "monomial_lcm"
  375. template = dedent("""\
  376. def %(name)s(A, B):
  377. (%(A)s,) = A
  378. (%(B)s,) = B
  379. return (%(AB)s,)
  380. """)
  381. A = self._vars("a")
  382. B = self._vars("b")
  383. AB = [ "%s if %s >= %s else %s" % (a, a, b, b) for a, b in zip(A, B) ]
  384. code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB))
  385. return self._build(code, name)
  386. def gcd(self):
  387. name = "monomial_gcd"
  388. template = dedent("""\
  389. def %(name)s(A, B):
  390. (%(A)s,) = A
  391. (%(B)s,) = B
  392. return (%(AB)s,)
  393. """)
  394. A = self._vars("a")
  395. B = self._vars("b")
  396. AB = [ "%s if %s <= %s else %s" % (a, a, b, b) for a, b in zip(A, B) ]
  397. code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB))
  398. return self._build(code, name)
  399. @public
  400. class Monomial(PicklableWithSlots):
  401. """Class representing a monomial, i.e. a product of powers. """
  402. __slots__ = ('exponents', 'gens')
  403. def __init__(self, monom, gens=None):
  404. if not iterable(monom):
  405. rep, gens = dict_from_expr(sympify(monom), gens=gens)
  406. if len(rep) == 1 and list(rep.values())[0] == 1:
  407. monom = list(rep.keys())[0]
  408. else:
  409. raise ValueError("Expected a monomial got {}".format(monom))
  410. self.exponents = tuple(map(int, monom))
  411. self.gens = gens
  412. def rebuild(self, exponents, gens=None):
  413. return self.__class__(exponents, gens or self.gens)
  414. def __len__(self):
  415. return len(self.exponents)
  416. def __iter__(self):
  417. return iter(self.exponents)
  418. def __getitem__(self, item):
  419. return self.exponents[item]
  420. def __hash__(self):
  421. return hash((self.__class__.__name__, self.exponents, self.gens))
  422. def __str__(self):
  423. if self.gens:
  424. return "*".join([ "%s**%s" % (gen, exp) for gen, exp in zip(self.gens, self.exponents) ])
  425. else:
  426. return "%s(%s)" % (self.__class__.__name__, self.exponents)
  427. def as_expr(self, *gens):
  428. """Convert a monomial instance to a SymPy expression. """
  429. gens = gens or self.gens
  430. if not gens:
  431. raise ValueError(
  432. "Cannot convert %s to an expression without generators" % self)
  433. return Mul(*[ gen**exp for gen, exp in zip(gens, self.exponents) ])
  434. def __eq__(self, other):
  435. if isinstance(other, Monomial):
  436. exponents = other.exponents
  437. elif isinstance(other, (tuple, Tuple)):
  438. exponents = other
  439. else:
  440. return False
  441. return self.exponents == exponents
  442. def __ne__(self, other):
  443. return not self == other
  444. def __mul__(self, other):
  445. if isinstance(other, Monomial):
  446. exponents = other.exponents
  447. elif isinstance(other, (tuple, Tuple)):
  448. exponents = other
  449. else:
  450. raise NotImplementedError
  451. return self.rebuild(monomial_mul(self.exponents, exponents))
  452. def __truediv__(self, other):
  453. if isinstance(other, Monomial):
  454. exponents = other.exponents
  455. elif isinstance(other, (tuple, Tuple)):
  456. exponents = other
  457. else:
  458. raise NotImplementedError
  459. result = monomial_div(self.exponents, exponents)
  460. if result is not None:
  461. return self.rebuild(result)
  462. else:
  463. raise ExactQuotientFailed(self, Monomial(other))
  464. __floordiv__ = __truediv__
  465. def __pow__(self, other):
  466. n = int(other)
  467. if not n:
  468. return self.rebuild([0]*len(self))
  469. elif n > 0:
  470. exponents = self.exponents
  471. for i in range(1, n):
  472. exponents = monomial_mul(exponents, self.exponents)
  473. return self.rebuild(exponents)
  474. else:
  475. raise ValueError("a non-negative integer expected, got %s" % other)
  476. def gcd(self, other):
  477. """Greatest common divisor of monomials. """
  478. if isinstance(other, Monomial):
  479. exponents = other.exponents
  480. elif isinstance(other, (tuple, Tuple)):
  481. exponents = other
  482. else:
  483. raise TypeError(
  484. "an instance of Monomial class expected, got %s" % other)
  485. return self.rebuild(monomial_gcd(self.exponents, exponents))
  486. def lcm(self, other):
  487. """Least common multiple of monomials. """
  488. if isinstance(other, Monomial):
  489. exponents = other.exponents
  490. elif isinstance(other, (tuple, Tuple)):
  491. exponents = other
  492. else:
  493. raise TypeError(
  494. "an instance of Monomial class expected, got %s" % other)
  495. return self.rebuild(monomial_lcm(self.exponents, exponents))