modulargcd.py 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277
  1. from sympy.core.symbol import Dummy
  2. from sympy.ntheory import nextprime
  3. from sympy.ntheory.modular import crt
  4. from sympy.polys.domains import PolynomialRing
  5. from sympy.polys.galoistools import (
  6. gf_gcd, gf_from_dict, gf_gcdex, gf_div, gf_lcm)
  7. from sympy.polys.polyerrors import ModularGCDFailed
  8. from mpmath import sqrt
  9. import random
  10. def _trivial_gcd(f, g):
  11. """
  12. Compute the GCD of two polynomials in trivial cases, i.e. when one
  13. or both polynomials are zero.
  14. """
  15. ring = f.ring
  16. if not (f or g):
  17. return ring.zero, ring.zero, ring.zero
  18. elif not f:
  19. if g.LC < ring.domain.zero:
  20. return -g, ring.zero, -ring.one
  21. else:
  22. return g, ring.zero, ring.one
  23. elif not g:
  24. if f.LC < ring.domain.zero:
  25. return -f, -ring.one, ring.zero
  26. else:
  27. return f, ring.one, ring.zero
  28. return None
  29. def _gf_gcd(fp, gp, p):
  30. r"""
  31. Compute the GCD of two univariate polynomials in `\mathbb{Z}_p[x]`.
  32. """
  33. dom = fp.ring.domain
  34. while gp:
  35. rem = fp
  36. deg = gp.degree()
  37. lcinv = dom.invert(gp.LC, p)
  38. while True:
  39. degrem = rem.degree()
  40. if degrem < deg:
  41. break
  42. rem = (rem - gp.mul_monom((degrem - deg,)).mul_ground(lcinv * rem.LC)).trunc_ground(p)
  43. fp = gp
  44. gp = rem
  45. return fp.mul_ground(dom.invert(fp.LC, p)).trunc_ground(p)
  46. def _degree_bound_univariate(f, g):
  47. r"""
  48. Compute an upper bound for the degree of the GCD of two univariate
  49. integer polynomials `f` and `g`.
  50. The function chooses a suitable prime `p` and computes the GCD of
  51. `f` and `g` in `\mathbb{Z}_p[x]`. The choice of `p` guarantees that
  52. the degree in `\mathbb{Z}_p[x]` is greater than or equal to the degree
  53. in `\mathbb{Z}[x]`.
  54. Parameters
  55. ==========
  56. f : PolyElement
  57. univariate integer polynomial
  58. g : PolyElement
  59. univariate integer polynomial
  60. """
  61. gamma = f.ring.domain.gcd(f.LC, g.LC)
  62. p = 1
  63. p = nextprime(p)
  64. while gamma % p == 0:
  65. p = nextprime(p)
  66. fp = f.trunc_ground(p)
  67. gp = g.trunc_ground(p)
  68. hp = _gf_gcd(fp, gp, p)
  69. deghp = hp.degree()
  70. return deghp
  71. def _chinese_remainder_reconstruction_univariate(hp, hq, p, q):
  72. r"""
  73. Construct a polynomial `h_{pq}` in `\mathbb{Z}_{p q}[x]` such that
  74. .. math ::
  75. h_{pq} = h_p \; \mathrm{mod} \, p
  76. h_{pq} = h_q \; \mathrm{mod} \, q
  77. for relatively prime integers `p` and `q` and polynomials
  78. `h_p` and `h_q` in `\mathbb{Z}_p[x]` and `\mathbb{Z}_q[x]`
  79. respectively.
  80. The coefficients of the polynomial `h_{pq}` are computed with the
  81. Chinese Remainder Theorem. The symmetric representation in
  82. `\mathbb{Z}_p[x]`, `\mathbb{Z}_q[x]` and `\mathbb{Z}_{p q}[x]` is used.
  83. It is assumed that `h_p` and `h_q` have the same degree.
  84. Parameters
  85. ==========
  86. hp : PolyElement
  87. univariate integer polynomial with coefficients in `\mathbb{Z}_p`
  88. hq : PolyElement
  89. univariate integer polynomial with coefficients in `\mathbb{Z}_q`
  90. p : Integer
  91. modulus of `h_p`, relatively prime to `q`
  92. q : Integer
  93. modulus of `h_q`, relatively prime to `p`
  94. Examples
  95. ========
  96. >>> from sympy.polys.modulargcd import _chinese_remainder_reconstruction_univariate
  97. >>> from sympy.polys import ring, ZZ
  98. >>> R, x = ring("x", ZZ)
  99. >>> p = 3
  100. >>> q = 5
  101. >>> hp = -x**3 - 1
  102. >>> hq = 2*x**3 - 2*x**2 + x
  103. >>> hpq = _chinese_remainder_reconstruction_univariate(hp, hq, p, q)
  104. >>> hpq
  105. 2*x**3 + 3*x**2 + 6*x + 5
  106. >>> hpq.trunc_ground(p) == hp
  107. True
  108. >>> hpq.trunc_ground(q) == hq
  109. True
  110. """
  111. n = hp.degree()
  112. x = hp.ring.gens[0]
  113. hpq = hp.ring.zero
  114. for i in range(n+1):
  115. hpq[(i,)] = crt([p, q], [hp.coeff(x**i), hq.coeff(x**i)], symmetric=True)[0]
  116. hpq.strip_zero()
  117. return hpq
  118. def modgcd_univariate(f, g):
  119. r"""
  120. Computes the GCD of two polynomials in `\mathbb{Z}[x]` using a modular
  121. algorithm.
  122. The algorithm computes the GCD of two univariate integer polynomials
  123. `f` and `g` by computing the GCD in `\mathbb{Z}_p[x]` for suitable
  124. primes `p` and then reconstructing the coefficients with the Chinese
  125. Remainder Theorem. Trial division is only made for candidates which
  126. are very likely the desired GCD.
  127. Parameters
  128. ==========
  129. f : PolyElement
  130. univariate integer polynomial
  131. g : PolyElement
  132. univariate integer polynomial
  133. Returns
  134. =======
  135. h : PolyElement
  136. GCD of the polynomials `f` and `g`
  137. cff : PolyElement
  138. cofactor of `f`, i.e. `\frac{f}{h}`
  139. cfg : PolyElement
  140. cofactor of `g`, i.e. `\frac{g}{h}`
  141. Examples
  142. ========
  143. >>> from sympy.polys.modulargcd import modgcd_univariate
  144. >>> from sympy.polys import ring, ZZ
  145. >>> R, x = ring("x", ZZ)
  146. >>> f = x**5 - 1
  147. >>> g = x - 1
  148. >>> h, cff, cfg = modgcd_univariate(f, g)
  149. >>> h, cff, cfg
  150. (x - 1, x**4 + x**3 + x**2 + x + 1, 1)
  151. >>> cff * h == f
  152. True
  153. >>> cfg * h == g
  154. True
  155. >>> f = 6*x**2 - 6
  156. >>> g = 2*x**2 + 4*x + 2
  157. >>> h, cff, cfg = modgcd_univariate(f, g)
  158. >>> h, cff, cfg
  159. (2*x + 2, 3*x - 3, x + 1)
  160. >>> cff * h == f
  161. True
  162. >>> cfg * h == g
  163. True
  164. References
  165. ==========
  166. 1. [Monagan00]_
  167. """
  168. assert f.ring == g.ring and f.ring.domain.is_ZZ
  169. result = _trivial_gcd(f, g)
  170. if result is not None:
  171. return result
  172. ring = f.ring
  173. cf, f = f.primitive()
  174. cg, g = g.primitive()
  175. ch = ring.domain.gcd(cf, cg)
  176. bound = _degree_bound_univariate(f, g)
  177. if bound == 0:
  178. return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch)
  179. gamma = ring.domain.gcd(f.LC, g.LC)
  180. m = 1
  181. p = 1
  182. while True:
  183. p = nextprime(p)
  184. while gamma % p == 0:
  185. p = nextprime(p)
  186. fp = f.trunc_ground(p)
  187. gp = g.trunc_ground(p)
  188. hp = _gf_gcd(fp, gp, p)
  189. deghp = hp.degree()
  190. if deghp > bound:
  191. continue
  192. elif deghp < bound:
  193. m = 1
  194. bound = deghp
  195. continue
  196. hp = hp.mul_ground(gamma).trunc_ground(p)
  197. if m == 1:
  198. m = p
  199. hlastm = hp
  200. continue
  201. hm = _chinese_remainder_reconstruction_univariate(hp, hlastm, p, m)
  202. m *= p
  203. if not hm == hlastm:
  204. hlastm = hm
  205. continue
  206. h = hm.quo_ground(hm.content())
  207. fquo, frem = f.div(h)
  208. gquo, grem = g.div(h)
  209. if not frem and not grem:
  210. if h.LC < 0:
  211. ch = -ch
  212. h = h.mul_ground(ch)
  213. cff = fquo.mul_ground(cf // ch)
  214. cfg = gquo.mul_ground(cg // ch)
  215. return h, cff, cfg
  216. def _primitive(f, p):
  217. r"""
  218. Compute the content and the primitive part of a polynomial in
  219. `\mathbb{Z}_p[x_0, \ldots, x_{k-2}, y] \cong \mathbb{Z}_p[y][x_0, \ldots, x_{k-2}]`.
  220. Parameters
  221. ==========
  222. f : PolyElement
  223. integer polynomial in `\mathbb{Z}_p[x0, \ldots, x{k-2}, y]`
  224. p : Integer
  225. modulus of `f`
  226. Returns
  227. =======
  228. contf : PolyElement
  229. integer polynomial in `\mathbb{Z}_p[y]`, content of `f`
  230. ppf : PolyElement
  231. primitive part of `f`, i.e. `\frac{f}{contf}`
  232. Examples
  233. ========
  234. >>> from sympy.polys.modulargcd import _primitive
  235. >>> from sympy.polys import ring, ZZ
  236. >>> R, x, y = ring("x, y", ZZ)
  237. >>> p = 3
  238. >>> f = x**2*y**2 + x**2*y - y**2 - y
  239. >>> _primitive(f, p)
  240. (y**2 + y, x**2 - 1)
  241. >>> R, x, y, z = ring("x, y, z", ZZ)
  242. >>> f = x*y*z - y**2*z**2
  243. >>> _primitive(f, p)
  244. (z, x*y - y**2*z)
  245. """
  246. ring = f.ring
  247. dom = ring.domain
  248. k = ring.ngens
  249. coeffs = {}
  250. for monom, coeff in f.iterterms():
  251. if monom[:-1] not in coeffs:
  252. coeffs[monom[:-1]] = {}
  253. coeffs[monom[:-1]][monom[-1]] = coeff
  254. cont = []
  255. for coeff in iter(coeffs.values()):
  256. cont = gf_gcd(cont, gf_from_dict(coeff, p, dom), p, dom)
  257. yring = ring.clone(symbols=ring.symbols[k-1])
  258. contf = yring.from_dense(cont).trunc_ground(p)
  259. return contf, f.quo(contf.set_ring(ring))
  260. def _deg(f):
  261. r"""
  262. Compute the degree of a multivariate polynomial
  263. `f \in K[x_0, \ldots, x_{k-2}, y] \cong K[y][x_0, \ldots, x_{k-2}]`.
  264. Parameters
  265. ==========
  266. f : PolyElement
  267. polynomial in `K[x_0, \ldots, x_{k-2}, y]`
  268. Returns
  269. =======
  270. degf : Integer tuple
  271. degree of `f` in `x_0, \ldots, x_{k-2}`
  272. Examples
  273. ========
  274. >>> from sympy.polys.modulargcd import _deg
  275. >>> from sympy.polys import ring, ZZ
  276. >>> R, x, y = ring("x, y", ZZ)
  277. >>> f = x**2*y**2 + x**2*y - 1
  278. >>> _deg(f)
  279. (2,)
  280. >>> R, x, y, z = ring("x, y, z", ZZ)
  281. >>> f = x**2*y**2 + x**2*y - 1
  282. >>> _deg(f)
  283. (2, 2)
  284. >>> f = x*y*z - y**2*z**2
  285. >>> _deg(f)
  286. (1, 1)
  287. """
  288. k = f.ring.ngens
  289. degf = (0,) * (k-1)
  290. for monom in f.itermonoms():
  291. if monom[:-1] > degf:
  292. degf = monom[:-1]
  293. return degf
  294. def _LC(f):
  295. r"""
  296. Compute the leading coefficient of a multivariate polynomial
  297. `f \in K[x_0, \ldots, x_{k-2}, y] \cong K[y][x_0, \ldots, x_{k-2}]`.
  298. Parameters
  299. ==========
  300. f : PolyElement
  301. polynomial in `K[x_0, \ldots, x_{k-2}, y]`
  302. Returns
  303. =======
  304. lcf : PolyElement
  305. polynomial in `K[y]`, leading coefficient of `f`
  306. Examples
  307. ========
  308. >>> from sympy.polys.modulargcd import _LC
  309. >>> from sympy.polys import ring, ZZ
  310. >>> R, x, y = ring("x, y", ZZ)
  311. >>> f = x**2*y**2 + x**2*y - 1
  312. >>> _LC(f)
  313. y**2 + y
  314. >>> R, x, y, z = ring("x, y, z", ZZ)
  315. >>> f = x**2*y**2 + x**2*y - 1
  316. >>> _LC(f)
  317. 1
  318. >>> f = x*y*z - y**2*z**2
  319. >>> _LC(f)
  320. z
  321. """
  322. ring = f.ring
  323. k = ring.ngens
  324. yring = ring.clone(symbols=ring.symbols[k-1])
  325. y = yring.gens[0]
  326. degf = _deg(f)
  327. lcf = yring.zero
  328. for monom, coeff in f.iterterms():
  329. if monom[:-1] == degf:
  330. lcf += coeff*y**monom[-1]
  331. return lcf
  332. def _swap(f, i):
  333. """
  334. Make the variable `x_i` the leading one in a multivariate polynomial `f`.
  335. """
  336. ring = f.ring
  337. fswap = ring.zero
  338. for monom, coeff in f.iterterms():
  339. monomswap = (monom[i],) + monom[:i] + monom[i+1:]
  340. fswap[monomswap] = coeff
  341. return fswap
  342. def _degree_bound_bivariate(f, g):
  343. r"""
  344. Compute upper degree bounds for the GCD of two bivariate
  345. integer polynomials `f` and `g`.
  346. The GCD is viewed as a polynomial in `\mathbb{Z}[y][x]` and the
  347. function returns an upper bound for its degree and one for the degree
  348. of its content. This is done by choosing a suitable prime `p` and
  349. computing the GCD of the contents of `f \; \mathrm{mod} \, p` and
  350. `g \; \mathrm{mod} \, p`. The choice of `p` guarantees that the degree
  351. of the content in `\mathbb{Z}_p[y]` is greater than or equal to the
  352. degree in `\mathbb{Z}[y]`. To obtain the degree bound in the variable
  353. `x`, the polynomials are evaluated at `y = a` for a suitable
  354. `a \in \mathbb{Z}_p` and then their GCD in `\mathbb{Z}_p[x]` is
  355. computed. If no such `a` exists, i.e. the degree in `\mathbb{Z}_p[x]`
  356. is always smaller than the one in `\mathbb{Z}[y][x]`, then the bound is
  357. set to the minimum of the degrees of `f` and `g` in `x`.
  358. Parameters
  359. ==========
  360. f : PolyElement
  361. bivariate integer polynomial
  362. g : PolyElement
  363. bivariate integer polynomial
  364. Returns
  365. =======
  366. xbound : Integer
  367. upper bound for the degree of the GCD of the polynomials `f` and
  368. `g` in the variable `x`
  369. ycontbound : Integer
  370. upper bound for the degree of the content of the GCD of the
  371. polynomials `f` and `g` in the variable `y`
  372. References
  373. ==========
  374. 1. [Monagan00]_
  375. """
  376. ring = f.ring
  377. gamma1 = ring.domain.gcd(f.LC, g.LC)
  378. gamma2 = ring.domain.gcd(_swap(f, 1).LC, _swap(g, 1).LC)
  379. badprimes = gamma1 * gamma2
  380. p = 1
  381. p = nextprime(p)
  382. while badprimes % p == 0:
  383. p = nextprime(p)
  384. fp = f.trunc_ground(p)
  385. gp = g.trunc_ground(p)
  386. contfp, fp = _primitive(fp, p)
  387. contgp, gp = _primitive(gp, p)
  388. conthp = _gf_gcd(contfp, contgp, p) # polynomial in Z_p[y]
  389. ycontbound = conthp.degree()
  390. # polynomial in Z_p[y]
  391. delta = _gf_gcd(_LC(fp), _LC(gp), p)
  392. for a in range(p):
  393. if not delta.evaluate(0, a) % p:
  394. continue
  395. fpa = fp.evaluate(1, a).trunc_ground(p)
  396. gpa = gp.evaluate(1, a).trunc_ground(p)
  397. hpa = _gf_gcd(fpa, gpa, p)
  398. xbound = hpa.degree()
  399. return xbound, ycontbound
  400. return min(fp.degree(), gp.degree()), ycontbound
  401. def _chinese_remainder_reconstruction_multivariate(hp, hq, p, q):
  402. r"""
  403. Construct a polynomial `h_{pq}` in
  404. `\mathbb{Z}_{p q}[x_0, \ldots, x_{k-1}]` such that
  405. .. math ::
  406. h_{pq} = h_p \; \mathrm{mod} \, p
  407. h_{pq} = h_q \; \mathrm{mod} \, q
  408. for relatively prime integers `p` and `q` and polynomials
  409. `h_p` and `h_q` in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` and
  410. `\mathbb{Z}_q[x_0, \ldots, x_{k-1}]` respectively.
  411. The coefficients of the polynomial `h_{pq}` are computed with the
  412. Chinese Remainder Theorem. The symmetric representation in
  413. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`,
  414. `\mathbb{Z}_q[x_0, \ldots, x_{k-1}]` and
  415. `\mathbb{Z}_{p q}[x_0, \ldots, x_{k-1}]` is used.
  416. Parameters
  417. ==========
  418. hp : PolyElement
  419. multivariate integer polynomial with coefficients in `\mathbb{Z}_p`
  420. hq : PolyElement
  421. multivariate integer polynomial with coefficients in `\mathbb{Z}_q`
  422. p : Integer
  423. modulus of `h_p`, relatively prime to `q`
  424. q : Integer
  425. modulus of `h_q`, relatively prime to `p`
  426. Examples
  427. ========
  428. >>> from sympy.polys.modulargcd import _chinese_remainder_reconstruction_multivariate
  429. >>> from sympy.polys import ring, ZZ
  430. >>> R, x, y = ring("x, y", ZZ)
  431. >>> p = 3
  432. >>> q = 5
  433. >>> hp = x**3*y - x**2 - 1
  434. >>> hq = -x**3*y - 2*x*y**2 + 2
  435. >>> hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q)
  436. >>> hpq
  437. 4*x**3*y + 5*x**2 + 3*x*y**2 + 2
  438. >>> hpq.trunc_ground(p) == hp
  439. True
  440. >>> hpq.trunc_ground(q) == hq
  441. True
  442. >>> R, x, y, z = ring("x, y, z", ZZ)
  443. >>> p = 6
  444. >>> q = 5
  445. >>> hp = 3*x**4 - y**3*z + z
  446. >>> hq = -2*x**4 + z
  447. >>> hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q)
  448. >>> hpq
  449. 3*x**4 + 5*y**3*z + z
  450. >>> hpq.trunc_ground(p) == hp
  451. True
  452. >>> hpq.trunc_ground(q) == hq
  453. True
  454. """
  455. hpmonoms = set(hp.monoms())
  456. hqmonoms = set(hq.monoms())
  457. monoms = hpmonoms.intersection(hqmonoms)
  458. hpmonoms.difference_update(monoms)
  459. hqmonoms.difference_update(monoms)
  460. zero = hp.ring.domain.zero
  461. hpq = hp.ring.zero
  462. if isinstance(hp.ring.domain, PolynomialRing):
  463. crt_ = _chinese_remainder_reconstruction_multivariate
  464. else:
  465. def crt_(cp, cq, p, q):
  466. return crt([p, q], [cp, cq], symmetric=True)[0]
  467. for monom in monoms:
  468. hpq[monom] = crt_(hp[monom], hq[monom], p, q)
  469. for monom in hpmonoms:
  470. hpq[monom] = crt_(hp[monom], zero, p, q)
  471. for monom in hqmonoms:
  472. hpq[monom] = crt_(zero, hq[monom], p, q)
  473. return hpq
  474. def _interpolate_multivariate(evalpoints, hpeval, ring, i, p, ground=False):
  475. r"""
  476. Reconstruct a polynomial `h_p` in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`
  477. from a list of evaluation points in `\mathbb{Z}_p` and a list of
  478. polynomials in
  479. `\mathbb{Z}_p[x_0, \ldots, x_{i-1}, x_{i+1}, \ldots, x_{k-1}]`, which
  480. are the images of `h_p` evaluated in the variable `x_i`.
  481. It is also possible to reconstruct a parameter of the ground domain,
  482. i.e. if `h_p` is a polynomial over `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`.
  483. In this case, one has to set ``ground=True``.
  484. Parameters
  485. ==========
  486. evalpoints : list of Integer objects
  487. list of evaluation points in `\mathbb{Z}_p`
  488. hpeval : list of PolyElement objects
  489. list of polynomials in (resp. over)
  490. `\mathbb{Z}_p[x_0, \ldots, x_{i-1}, x_{i+1}, \ldots, x_{k-1}]`,
  491. images of `h_p` evaluated in the variable `x_i`
  492. ring : PolyRing
  493. `h_p` will be an element of this ring
  494. i : Integer
  495. index of the variable which has to be reconstructed
  496. p : Integer
  497. prime number, modulus of `h_p`
  498. ground : Boolean
  499. indicates whether `x_i` is in the ground domain, default is
  500. ``False``
  501. Returns
  502. =======
  503. hp : PolyElement
  504. interpolated polynomial in (resp. over)
  505. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`
  506. """
  507. hp = ring.zero
  508. if ground:
  509. domain = ring.domain.domain
  510. y = ring.domain.gens[i]
  511. else:
  512. domain = ring.domain
  513. y = ring.gens[i]
  514. for a, hpa in zip(evalpoints, hpeval):
  515. numer = ring.one
  516. denom = domain.one
  517. for b in evalpoints:
  518. if b == a:
  519. continue
  520. numer *= y - b
  521. denom *= a - b
  522. denom = domain.invert(denom, p)
  523. coeff = numer.mul_ground(denom)
  524. hp += hpa.set_ring(ring) * coeff
  525. return hp.trunc_ground(p)
  526. def modgcd_bivariate(f, g):
  527. r"""
  528. Computes the GCD of two polynomials in `\mathbb{Z}[x, y]` using a
  529. modular algorithm.
  530. The algorithm computes the GCD of two bivariate integer polynomials
  531. `f` and `g` by calculating the GCD in `\mathbb{Z}_p[x, y]` for
  532. suitable primes `p` and then reconstructing the coefficients with the
  533. Chinese Remainder Theorem. To compute the bivariate GCD over
  534. `\mathbb{Z}_p`, the polynomials `f \; \mathrm{mod} \, p` and
  535. `g \; \mathrm{mod} \, p` are evaluated at `y = a` for certain
  536. `a \in \mathbb{Z}_p` and then their univariate GCD in `\mathbb{Z}_p[x]`
  537. is computed. Interpolating those yields the bivariate GCD in
  538. `\mathbb{Z}_p[x, y]`. To verify the result in `\mathbb{Z}[x, y]`, trial
  539. division is done, but only for candidates which are very likely the
  540. desired GCD.
  541. Parameters
  542. ==========
  543. f : PolyElement
  544. bivariate integer polynomial
  545. g : PolyElement
  546. bivariate integer polynomial
  547. Returns
  548. =======
  549. h : PolyElement
  550. GCD of the polynomials `f` and `g`
  551. cff : PolyElement
  552. cofactor of `f`, i.e. `\frac{f}{h}`
  553. cfg : PolyElement
  554. cofactor of `g`, i.e. `\frac{g}{h}`
  555. Examples
  556. ========
  557. >>> from sympy.polys.modulargcd import modgcd_bivariate
  558. >>> from sympy.polys import ring, ZZ
  559. >>> R, x, y = ring("x, y", ZZ)
  560. >>> f = x**2 - y**2
  561. >>> g = x**2 + 2*x*y + y**2
  562. >>> h, cff, cfg = modgcd_bivariate(f, g)
  563. >>> h, cff, cfg
  564. (x + y, x - y, x + y)
  565. >>> cff * h == f
  566. True
  567. >>> cfg * h == g
  568. True
  569. >>> f = x**2*y - x**2 - 4*y + 4
  570. >>> g = x + 2
  571. >>> h, cff, cfg = modgcd_bivariate(f, g)
  572. >>> h, cff, cfg
  573. (x + 2, x*y - x - 2*y + 2, 1)
  574. >>> cff * h == f
  575. True
  576. >>> cfg * h == g
  577. True
  578. References
  579. ==========
  580. 1. [Monagan00]_
  581. """
  582. assert f.ring == g.ring and f.ring.domain.is_ZZ
  583. result = _trivial_gcd(f, g)
  584. if result is not None:
  585. return result
  586. ring = f.ring
  587. cf, f = f.primitive()
  588. cg, g = g.primitive()
  589. ch = ring.domain.gcd(cf, cg)
  590. xbound, ycontbound = _degree_bound_bivariate(f, g)
  591. if xbound == ycontbound == 0:
  592. return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch)
  593. fswap = _swap(f, 1)
  594. gswap = _swap(g, 1)
  595. degyf = fswap.degree()
  596. degyg = gswap.degree()
  597. ybound, xcontbound = _degree_bound_bivariate(fswap, gswap)
  598. if ybound == xcontbound == 0:
  599. return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch)
  600. # TODO: to improve performance, choose the main variable here
  601. gamma1 = ring.domain.gcd(f.LC, g.LC)
  602. gamma2 = ring.domain.gcd(fswap.LC, gswap.LC)
  603. badprimes = gamma1 * gamma2
  604. m = 1
  605. p = 1
  606. while True:
  607. p = nextprime(p)
  608. while badprimes % p == 0:
  609. p = nextprime(p)
  610. fp = f.trunc_ground(p)
  611. gp = g.trunc_ground(p)
  612. contfp, fp = _primitive(fp, p)
  613. contgp, gp = _primitive(gp, p)
  614. conthp = _gf_gcd(contfp, contgp, p) # monic polynomial in Z_p[y]
  615. degconthp = conthp.degree()
  616. if degconthp > ycontbound:
  617. continue
  618. elif degconthp < ycontbound:
  619. m = 1
  620. ycontbound = degconthp
  621. continue
  622. # polynomial in Z_p[y]
  623. delta = _gf_gcd(_LC(fp), _LC(gp), p)
  624. degcontfp = contfp.degree()
  625. degcontgp = contgp.degree()
  626. degdelta = delta.degree()
  627. N = min(degyf - degcontfp, degyg - degcontgp,
  628. ybound - ycontbound + degdelta) + 1
  629. if p < N:
  630. continue
  631. n = 0
  632. evalpoints = []
  633. hpeval = []
  634. unlucky = False
  635. for a in range(p):
  636. deltaa = delta.evaluate(0, a)
  637. if not deltaa % p:
  638. continue
  639. fpa = fp.evaluate(1, a).trunc_ground(p)
  640. gpa = gp.evaluate(1, a).trunc_ground(p)
  641. hpa = _gf_gcd(fpa, gpa, p) # monic polynomial in Z_p[x]
  642. deghpa = hpa.degree()
  643. if deghpa > xbound:
  644. continue
  645. elif deghpa < xbound:
  646. m = 1
  647. xbound = deghpa
  648. unlucky = True
  649. break
  650. hpa = hpa.mul_ground(deltaa).trunc_ground(p)
  651. evalpoints.append(a)
  652. hpeval.append(hpa)
  653. n += 1
  654. if n == N:
  655. break
  656. if unlucky:
  657. continue
  658. if n < N:
  659. continue
  660. hp = _interpolate_multivariate(evalpoints, hpeval, ring, 1, p)
  661. hp = _primitive(hp, p)[1]
  662. hp = hp * conthp.set_ring(ring)
  663. degyhp = hp.degree(1)
  664. if degyhp > ybound:
  665. continue
  666. if degyhp < ybound:
  667. m = 1
  668. ybound = degyhp
  669. continue
  670. hp = hp.mul_ground(gamma1).trunc_ground(p)
  671. if m == 1:
  672. m = p
  673. hlastm = hp
  674. continue
  675. hm = _chinese_remainder_reconstruction_multivariate(hp, hlastm, p, m)
  676. m *= p
  677. if not hm == hlastm:
  678. hlastm = hm
  679. continue
  680. h = hm.quo_ground(hm.content())
  681. fquo, frem = f.div(h)
  682. gquo, grem = g.div(h)
  683. if not frem and not grem:
  684. if h.LC < 0:
  685. ch = -ch
  686. h = h.mul_ground(ch)
  687. cff = fquo.mul_ground(cf // ch)
  688. cfg = gquo.mul_ground(cg // ch)
  689. return h, cff, cfg
  690. def _modgcd_multivariate_p(f, g, p, degbound, contbound):
  691. r"""
  692. Compute the GCD of two polynomials in
  693. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`.
  694. The algorithm reduces the problem step by step by evaluating the
  695. polynomials `f` and `g` at `x_{k-1} = a` for suitable
  696. `a \in \mathbb{Z}_p` and then calls itself recursively to compute the GCD
  697. in `\mathbb{Z}_p[x_0, \ldots, x_{k-2}]`. If these recursive calls are
  698. successful for enough evaluation points, the GCD in `k` variables is
  699. interpolated, otherwise the algorithm returns ``None``. Every time a GCD
  700. or a content is computed, their degrees are compared with the bounds. If
  701. a degree greater then the bound is encountered, then the current call
  702. returns ``None`` and a new evaluation point has to be chosen. If at some
  703. point the degree is smaller, the correspondent bound is updated and the
  704. algorithm fails.
  705. Parameters
  706. ==========
  707. f : PolyElement
  708. multivariate integer polynomial with coefficients in `\mathbb{Z}_p`
  709. g : PolyElement
  710. multivariate integer polynomial with coefficients in `\mathbb{Z}_p`
  711. p : Integer
  712. prime number, modulus of `f` and `g`
  713. degbound : list of Integer objects
  714. ``degbound[i]`` is an upper bound for the degree of the GCD of `f`
  715. and `g` in the variable `x_i`
  716. contbound : list of Integer objects
  717. ``contbound[i]`` is an upper bound for the degree of the content of
  718. the GCD in `\mathbb{Z}_p[x_i][x_0, \ldots, x_{i-1}]`,
  719. ``contbound[0]`` is not used can therefore be chosen
  720. arbitrarily.
  721. Returns
  722. =======
  723. h : PolyElement
  724. GCD of the polynomials `f` and `g` or ``None``
  725. References
  726. ==========
  727. 1. [Monagan00]_
  728. 2. [Brown71]_
  729. """
  730. ring = f.ring
  731. k = ring.ngens
  732. if k == 1:
  733. h = _gf_gcd(f, g, p).trunc_ground(p)
  734. degh = h.degree()
  735. if degh > degbound[0]:
  736. return None
  737. if degh < degbound[0]:
  738. degbound[0] = degh
  739. raise ModularGCDFailed
  740. return h
  741. degyf = f.degree(k-1)
  742. degyg = g.degree(k-1)
  743. contf, f = _primitive(f, p)
  744. contg, g = _primitive(g, p)
  745. conth = _gf_gcd(contf, contg, p) # polynomial in Z_p[y]
  746. degcontf = contf.degree()
  747. degcontg = contg.degree()
  748. degconth = conth.degree()
  749. if degconth > contbound[k-1]:
  750. return None
  751. if degconth < contbound[k-1]:
  752. contbound[k-1] = degconth
  753. raise ModularGCDFailed
  754. lcf = _LC(f)
  755. lcg = _LC(g)
  756. delta = _gf_gcd(lcf, lcg, p) # polynomial in Z_p[y]
  757. evaltest = delta
  758. for i in range(k-1):
  759. evaltest *= _gf_gcd(_LC(_swap(f, i)), _LC(_swap(g, i)), p)
  760. degdelta = delta.degree()
  761. N = min(degyf - degcontf, degyg - degcontg,
  762. degbound[k-1] - contbound[k-1] + degdelta) + 1
  763. if p < N:
  764. return None
  765. n = 0
  766. d = 0
  767. evalpoints = []
  768. heval = []
  769. points = list(range(p))
  770. while points:
  771. a = random.sample(points, 1)[0]
  772. points.remove(a)
  773. if not evaltest.evaluate(0, a) % p:
  774. continue
  775. deltaa = delta.evaluate(0, a) % p
  776. fa = f.evaluate(k-1, a).trunc_ground(p)
  777. ga = g.evaluate(k-1, a).trunc_ground(p)
  778. # polynomials in Z_p[x_0, ..., x_{k-2}]
  779. ha = _modgcd_multivariate_p(fa, ga, p, degbound, contbound)
  780. if ha is None:
  781. d += 1
  782. if d > n:
  783. return None
  784. continue
  785. if ha.is_ground:
  786. h = conth.set_ring(ring).trunc_ground(p)
  787. return h
  788. ha = ha.mul_ground(deltaa).trunc_ground(p)
  789. evalpoints.append(a)
  790. heval.append(ha)
  791. n += 1
  792. if n == N:
  793. h = _interpolate_multivariate(evalpoints, heval, ring, k-1, p)
  794. h = _primitive(h, p)[1] * conth.set_ring(ring)
  795. degyh = h.degree(k-1)
  796. if degyh > degbound[k-1]:
  797. return None
  798. if degyh < degbound[k-1]:
  799. degbound[k-1] = degyh
  800. raise ModularGCDFailed
  801. return h
  802. return None
  803. def modgcd_multivariate(f, g):
  804. r"""
  805. Compute the GCD of two polynomials in `\mathbb{Z}[x_0, \ldots, x_{k-1}]`
  806. using a modular algorithm.
  807. The algorithm computes the GCD of two multivariate integer polynomials
  808. `f` and `g` by calculating the GCD in
  809. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` for suitable primes `p` and then
  810. reconstructing the coefficients with the Chinese Remainder Theorem. To
  811. compute the multivariate GCD over `\mathbb{Z}_p` the recursive
  812. subroutine :func:`_modgcd_multivariate_p` is used. To verify the result in
  813. `\mathbb{Z}[x_0, \ldots, x_{k-1}]`, trial division is done, but only for
  814. candidates which are very likely the desired GCD.
  815. Parameters
  816. ==========
  817. f : PolyElement
  818. multivariate integer polynomial
  819. g : PolyElement
  820. multivariate integer polynomial
  821. Returns
  822. =======
  823. h : PolyElement
  824. GCD of the polynomials `f` and `g`
  825. cff : PolyElement
  826. cofactor of `f`, i.e. `\frac{f}{h}`
  827. cfg : PolyElement
  828. cofactor of `g`, i.e. `\frac{g}{h}`
  829. Examples
  830. ========
  831. >>> from sympy.polys.modulargcd import modgcd_multivariate
  832. >>> from sympy.polys import ring, ZZ
  833. >>> R, x, y = ring("x, y", ZZ)
  834. >>> f = x**2 - y**2
  835. >>> g = x**2 + 2*x*y + y**2
  836. >>> h, cff, cfg = modgcd_multivariate(f, g)
  837. >>> h, cff, cfg
  838. (x + y, x - y, x + y)
  839. >>> cff * h == f
  840. True
  841. >>> cfg * h == g
  842. True
  843. >>> R, x, y, z = ring("x, y, z", ZZ)
  844. >>> f = x*z**2 - y*z**2
  845. >>> g = x**2*z + z
  846. >>> h, cff, cfg = modgcd_multivariate(f, g)
  847. >>> h, cff, cfg
  848. (z, x*z - y*z, x**2 + 1)
  849. >>> cff * h == f
  850. True
  851. >>> cfg * h == g
  852. True
  853. References
  854. ==========
  855. 1. [Monagan00]_
  856. 2. [Brown71]_
  857. See also
  858. ========
  859. _modgcd_multivariate_p
  860. """
  861. assert f.ring == g.ring and f.ring.domain.is_ZZ
  862. result = _trivial_gcd(f, g)
  863. if result is not None:
  864. return result
  865. ring = f.ring
  866. k = ring.ngens
  867. # divide out integer content
  868. cf, f = f.primitive()
  869. cg, g = g.primitive()
  870. ch = ring.domain.gcd(cf, cg)
  871. gamma = ring.domain.gcd(f.LC, g.LC)
  872. badprimes = ring.domain.one
  873. for i in range(k):
  874. badprimes *= ring.domain.gcd(_swap(f, i).LC, _swap(g, i).LC)
  875. degbound = [min(fdeg, gdeg) for fdeg, gdeg in zip(f.degrees(), g.degrees())]
  876. contbound = list(degbound)
  877. m = 1
  878. p = 1
  879. while True:
  880. p = nextprime(p)
  881. while badprimes % p == 0:
  882. p = nextprime(p)
  883. fp = f.trunc_ground(p)
  884. gp = g.trunc_ground(p)
  885. try:
  886. # monic GCD of fp, gp in Z_p[x_0, ..., x_{k-2}, y]
  887. hp = _modgcd_multivariate_p(fp, gp, p, degbound, contbound)
  888. except ModularGCDFailed:
  889. m = 1
  890. continue
  891. if hp is None:
  892. continue
  893. hp = hp.mul_ground(gamma).trunc_ground(p)
  894. if m == 1:
  895. m = p
  896. hlastm = hp
  897. continue
  898. hm = _chinese_remainder_reconstruction_multivariate(hp, hlastm, p, m)
  899. m *= p
  900. if not hm == hlastm:
  901. hlastm = hm
  902. continue
  903. h = hm.primitive()[1]
  904. fquo, frem = f.div(h)
  905. gquo, grem = g.div(h)
  906. if not frem and not grem:
  907. if h.LC < 0:
  908. ch = -ch
  909. h = h.mul_ground(ch)
  910. cff = fquo.mul_ground(cf // ch)
  911. cfg = gquo.mul_ground(cg // ch)
  912. return h, cff, cfg
  913. def _gf_div(f, g, p):
  914. r"""
  915. Compute `\frac f g` modulo `p` for two univariate polynomials over
  916. `\mathbb Z_p`.
  917. """
  918. ring = f.ring
  919. densequo, denserem = gf_div(f.to_dense(), g.to_dense(), p, ring.domain)
  920. return ring.from_dense(densequo), ring.from_dense(denserem)
  921. def _rational_function_reconstruction(c, p, m):
  922. r"""
  923. Reconstruct a rational function `\frac a b` in `\mathbb Z_p(t)` from
  924. .. math::
  925. c = \frac a b \; \mathrm{mod} \, m,
  926. where `c` and `m` are polynomials in `\mathbb Z_p[t]` and `m` has
  927. positive degree.
  928. The algorithm is based on the Euclidean Algorithm. In general, `m` is
  929. not irreducible, so it is possible that `b` is not invertible modulo
  930. `m`. In that case ``None`` is returned.
  931. Parameters
  932. ==========
  933. c : PolyElement
  934. univariate polynomial in `\mathbb Z[t]`
  935. p : Integer
  936. prime number
  937. m : PolyElement
  938. modulus, not necessarily irreducible
  939. Returns
  940. =======
  941. frac : FracElement
  942. either `\frac a b` in `\mathbb Z(t)` or ``None``
  943. References
  944. ==========
  945. 1. [Hoeij04]_
  946. """
  947. ring = c.ring
  948. domain = ring.domain
  949. M = m.degree()
  950. N = M // 2
  951. D = M - N - 1
  952. r0, s0 = m, ring.zero
  953. r1, s1 = c, ring.one
  954. while r1.degree() > N:
  955. quo = _gf_div(r0, r1, p)[0]
  956. r0, r1 = r1, (r0 - quo*r1).trunc_ground(p)
  957. s0, s1 = s1, (s0 - quo*s1).trunc_ground(p)
  958. a, b = r1, s1
  959. if b.degree() > D or _gf_gcd(b, m, p) != 1:
  960. return None
  961. lc = b.LC
  962. if lc != 1:
  963. lcinv = domain.invert(lc, p)
  964. a = a.mul_ground(lcinv).trunc_ground(p)
  965. b = b.mul_ground(lcinv).trunc_ground(p)
  966. field = ring.to_field()
  967. return field(a) / field(b)
  968. def _rational_reconstruction_func_coeffs(hm, p, m, ring, k):
  969. r"""
  970. Reconstruct every coefficient `c_h` of a polynomial `h` in
  971. `\mathbb Z_p(t_k)[t_1, \ldots, t_{k-1}][x, z]` from the corresponding
  972. coefficient `c_{h_m}` of a polynomial `h_m` in
  973. `\mathbb Z_p[t_1, \ldots, t_k][x, z] \cong \mathbb Z_p[t_k][t_1, \ldots, t_{k-1}][x, z]`
  974. such that
  975. .. math::
  976. c_{h_m} = c_h \; \mathrm{mod} \, m,
  977. where `m \in \mathbb Z_p[t]`.
  978. The reconstruction is based on the Euclidean Algorithm. In general, `m`
  979. is not irreducible, so it is possible that this fails for some
  980. coefficient. In that case ``None`` is returned.
  981. Parameters
  982. ==========
  983. hm : PolyElement
  984. polynomial in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  985. p : Integer
  986. prime number, modulus of `\mathbb Z_p`
  987. m : PolyElement
  988. modulus, polynomial in `\mathbb Z[t]`, not necessarily irreducible
  989. ring : PolyRing
  990. `\mathbb Z(t_k)[t_1, \ldots, t_{k-1}][x, z]`, `h` will be an
  991. element of this ring
  992. k : Integer
  993. index of the parameter `t_k` which will be reconstructed
  994. Returns
  995. =======
  996. h : PolyElement
  997. reconstructed polynomial in
  998. `\mathbb Z(t_k)[t_1, \ldots, t_{k-1}][x, z]` or ``None``
  999. See also
  1000. ========
  1001. _rational_function_reconstruction
  1002. """
  1003. h = ring.zero
  1004. for monom, coeff in hm.iterterms():
  1005. if k == 0:
  1006. coeffh = _rational_function_reconstruction(coeff, p, m)
  1007. if not coeffh:
  1008. return None
  1009. else:
  1010. coeffh = ring.domain.zero
  1011. for mon, c in coeff.drop_to_ground(k).iterterms():
  1012. ch = _rational_function_reconstruction(c, p, m)
  1013. if not ch:
  1014. return None
  1015. coeffh[mon] = ch
  1016. h[monom] = coeffh
  1017. return h
  1018. def _gf_gcdex(f, g, p):
  1019. r"""
  1020. Extended Euclidean Algorithm for two univariate polynomials over
  1021. `\mathbb Z_p`.
  1022. Returns polynomials `s, t` and `h`, such that `h` is the GCD of `f` and
  1023. `g` and `sf + tg = h \; \mathrm{mod} \, p`.
  1024. """
  1025. ring = f.ring
  1026. s, t, h = gf_gcdex(f.to_dense(), g.to_dense(), p, ring.domain)
  1027. return ring.from_dense(s), ring.from_dense(t), ring.from_dense(h)
  1028. def _trunc(f, minpoly, p):
  1029. r"""
  1030. Compute the reduced representation of a polynomial `f` in
  1031. `\mathbb Z_p[z] / (\check m_{\alpha}(z))[x]`
  1032. Parameters
  1033. ==========
  1034. f : PolyElement
  1035. polynomial in `\mathbb Z[x, z]`
  1036. minpoly : PolyElement
  1037. polynomial `\check m_{\alpha} \in \mathbb Z[z]`, not necessarily
  1038. irreducible
  1039. p : Integer
  1040. prime number, modulus of `\mathbb Z_p`
  1041. Returns
  1042. =======
  1043. ftrunc : PolyElement
  1044. polynomial in `\mathbb Z[x, z]`, reduced modulo
  1045. `\check m_{\alpha}(z)` and `p`
  1046. """
  1047. ring = f.ring
  1048. minpoly = minpoly.set_ring(ring)
  1049. p_ = ring.ground_new(p)
  1050. return f.trunc_ground(p).rem([minpoly, p_]).trunc_ground(p)
  1051. def _euclidean_algorithm(f, g, minpoly, p):
  1052. r"""
  1053. Compute the monic GCD of two univariate polynomials in
  1054. `\mathbb{Z}_p[z]/(\check m_{\alpha}(z))[x]` with the Euclidean
  1055. Algorithm.
  1056. In general, `\check m_{\alpha}(z)` is not irreducible, so it is possible
  1057. that some leading coefficient is not invertible modulo
  1058. `\check m_{\alpha}(z)`. In that case ``None`` is returned.
  1059. Parameters
  1060. ==========
  1061. f, g : PolyElement
  1062. polynomials in `\mathbb Z[x, z]`
  1063. minpoly : PolyElement
  1064. polynomial in `\mathbb Z[z]`, not necessarily irreducible
  1065. p : Integer
  1066. prime number, modulus of `\mathbb Z_p`
  1067. Returns
  1068. =======
  1069. h : PolyElement
  1070. GCD of `f` and `g` in `\mathbb Z[z, x]` or ``None``, coefficients
  1071. are in `\left[ -\frac{p-1} 2, \frac{p-1} 2 \right]`
  1072. """
  1073. ring = f.ring
  1074. f = _trunc(f, minpoly, p)
  1075. g = _trunc(g, minpoly, p)
  1076. while g:
  1077. rem = f
  1078. deg = g.degree(0) # degree in x
  1079. lcinv, _, gcd = _gf_gcdex(ring.dmp_LC(g), minpoly, p)
  1080. if not gcd == 1:
  1081. return None
  1082. while True:
  1083. degrem = rem.degree(0) # degree in x
  1084. if degrem < deg:
  1085. break
  1086. quo = (lcinv * ring.dmp_LC(rem)).set_ring(ring)
  1087. rem = _trunc(rem - g.mul_monom((degrem - deg, 0))*quo, minpoly, p)
  1088. f = g
  1089. g = rem
  1090. lcfinv = _gf_gcdex(ring.dmp_LC(f), minpoly, p)[0].set_ring(ring)
  1091. return _trunc(f * lcfinv, minpoly, p)
  1092. def _trial_division(f, h, minpoly, p=None):
  1093. r"""
  1094. Check if `h` divides `f` in
  1095. `\mathbb K[t_1, \ldots, t_k][z]/(m_{\alpha}(z))`, where `\mathbb K` is
  1096. either `\mathbb Q` or `\mathbb Z_p`.
  1097. This algorithm is based on pseudo division and does not use any
  1098. fractions. By default `\mathbb K` is `\mathbb Q`, if a prime number `p`
  1099. is given, `\mathbb Z_p` is chosen instead.
  1100. Parameters
  1101. ==========
  1102. f, h : PolyElement
  1103. polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1104. minpoly : PolyElement
  1105. polynomial `m_{\alpha}(z)` in `\mathbb Z[t_1, \ldots, t_k][z]`
  1106. p : Integer or None
  1107. if `p` is given, `\mathbb K` is set to `\mathbb Z_p` instead of
  1108. `\mathbb Q`, default is ``None``
  1109. Returns
  1110. =======
  1111. rem : PolyElement
  1112. remainder of `\frac f h`
  1113. References
  1114. ==========
  1115. .. [1] [Hoeij02]_
  1116. """
  1117. ring = f.ring
  1118. zxring = ring.clone(symbols=(ring.symbols[1], ring.symbols[0]))
  1119. minpoly = minpoly.set_ring(ring)
  1120. rem = f
  1121. degrem = rem.degree()
  1122. degh = h.degree()
  1123. degm = minpoly.degree(1)
  1124. lch = _LC(h).set_ring(ring)
  1125. lcm = minpoly.LC
  1126. while rem and degrem >= degh:
  1127. # polynomial in Z[t_1, ..., t_k][z]
  1128. lcrem = _LC(rem).set_ring(ring)
  1129. rem = rem*lch - h.mul_monom((degrem - degh, 0))*lcrem
  1130. if p:
  1131. rem = rem.trunc_ground(p)
  1132. degrem = rem.degree(1)
  1133. while rem and degrem >= degm:
  1134. # polynomial in Z[t_1, ..., t_k][x]
  1135. lcrem = _LC(rem.set_ring(zxring)).set_ring(ring)
  1136. rem = rem.mul_ground(lcm) - minpoly.mul_monom((0, degrem - degm))*lcrem
  1137. if p:
  1138. rem = rem.trunc_ground(p)
  1139. degrem = rem.degree(1)
  1140. degrem = rem.degree()
  1141. return rem
  1142. def _evaluate_ground(f, i, a):
  1143. r"""
  1144. Evaluate a polynomial `f` at `a` in the `i`-th variable of the ground
  1145. domain.
  1146. """
  1147. ring = f.ring.clone(domain=f.ring.domain.ring.drop(i))
  1148. fa = ring.zero
  1149. for monom, coeff in f.iterterms():
  1150. fa[monom] = coeff.evaluate(i, a)
  1151. return fa
  1152. def _func_field_modgcd_p(f, g, minpoly, p):
  1153. r"""
  1154. Compute the GCD of two polynomials `f` and `g` in
  1155. `\mathbb Z_p(t_1, \ldots, t_k)[z]/(\check m_\alpha(z))[x]`.
  1156. The algorithm reduces the problem step by step by evaluating the
  1157. polynomials `f` and `g` at `t_k = a` for suitable `a \in \mathbb Z_p`
  1158. and then calls itself recursively to compute the GCD in
  1159. `\mathbb Z_p(t_1, \ldots, t_{k-1})[z]/(\check m_\alpha(z))[x]`. If these
  1160. recursive calls are successful, the GCD over `k` variables is
  1161. interpolated, otherwise the algorithm returns ``None``. After
  1162. interpolation, Rational Function Reconstruction is used to obtain the
  1163. correct coefficients. If this fails, a new evaluation point has to be
  1164. chosen, otherwise the desired polynomial is obtained by clearing
  1165. denominators. The result is verified with a fraction free trial
  1166. division.
  1167. Parameters
  1168. ==========
  1169. f, g : PolyElement
  1170. polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1171. minpoly : PolyElement
  1172. polynomial in `\mathbb Z[t_1, \ldots, t_k][z]`, not necessarily
  1173. irreducible
  1174. p : Integer
  1175. prime number, modulus of `\mathbb Z_p`
  1176. Returns
  1177. =======
  1178. h : PolyElement
  1179. primitive associate in `\mathbb Z[t_1, \ldots, t_k][x, z]` of the
  1180. GCD of the polynomials `f` and `g` or ``None``, coefficients are
  1181. in `\left[ -\frac{p-1} 2, \frac{p-1} 2 \right]`
  1182. References
  1183. ==========
  1184. 1. [Hoeij04]_
  1185. """
  1186. ring = f.ring
  1187. domain = ring.domain # Z[t_1, ..., t_k]
  1188. if isinstance(domain, PolynomialRing):
  1189. k = domain.ngens
  1190. else:
  1191. return _euclidean_algorithm(f, g, minpoly, p)
  1192. if k == 1:
  1193. qdomain = domain.ring.to_field()
  1194. else:
  1195. qdomain = domain.ring.drop_to_ground(k - 1)
  1196. qdomain = qdomain.clone(domain=qdomain.domain.ring.to_field())
  1197. qring = ring.clone(domain=qdomain) # = Z(t_k)[t_1, ..., t_{k-1}][x, z]
  1198. n = 1
  1199. d = 1
  1200. # polynomial in Z_p[t_1, ..., t_k][z]
  1201. gamma = ring.dmp_LC(f) * ring.dmp_LC(g)
  1202. # polynomial in Z_p[t_1, ..., t_k]
  1203. delta = minpoly.LC
  1204. evalpoints = []
  1205. heval = []
  1206. LMlist = []
  1207. points = list(range(p))
  1208. while points:
  1209. a = random.sample(points, 1)[0]
  1210. points.remove(a)
  1211. if k == 1:
  1212. test = delta.evaluate(k-1, a) % p == 0
  1213. else:
  1214. test = delta.evaluate(k-1, a).trunc_ground(p) == 0
  1215. if test:
  1216. continue
  1217. gammaa = _evaluate_ground(gamma, k-1, a)
  1218. minpolya = _evaluate_ground(minpoly, k-1, a)
  1219. if gammaa.rem([minpolya, gammaa.ring(p)]) == 0:
  1220. continue
  1221. fa = _evaluate_ground(f, k-1, a)
  1222. ga = _evaluate_ground(g, k-1, a)
  1223. # polynomial in Z_p[x, t_1, ..., t_{k-1}, z]/(minpoly)
  1224. ha = _func_field_modgcd_p(fa, ga, minpolya, p)
  1225. if ha is None:
  1226. d += 1
  1227. if d > n:
  1228. return None
  1229. continue
  1230. if ha == 1:
  1231. return ha
  1232. LM = [ha.degree()] + [0]*(k-1)
  1233. if k > 1:
  1234. for monom, coeff in ha.iterterms():
  1235. if monom[0] == LM[0] and coeff.LM > tuple(LM[1:]):
  1236. LM[1:] = coeff.LM
  1237. evalpoints_a = [a]
  1238. heval_a = [ha]
  1239. if k == 1:
  1240. m = qring.domain.get_ring().one
  1241. else:
  1242. m = qring.domain.domain.get_ring().one
  1243. t = m.ring.gens[0]
  1244. for b, hb, LMhb in zip(evalpoints, heval, LMlist):
  1245. if LMhb == LM:
  1246. evalpoints_a.append(b)
  1247. heval_a.append(hb)
  1248. m *= (t - b)
  1249. m = m.trunc_ground(p)
  1250. evalpoints.append(a)
  1251. heval.append(ha)
  1252. LMlist.append(LM)
  1253. n += 1
  1254. # polynomial in Z_p[t_1, ..., t_k][x, z]
  1255. h = _interpolate_multivariate(evalpoints_a, heval_a, ring, k-1, p, ground=True)
  1256. # polynomial in Z_p(t_k)[t_1, ..., t_{k-1}][x, z]
  1257. h = _rational_reconstruction_func_coeffs(h, p, m, qring, k-1)
  1258. if h is None:
  1259. continue
  1260. if k == 1:
  1261. dom = qring.domain.field
  1262. den = dom.ring.one
  1263. for coeff in h.itercoeffs():
  1264. den = dom.ring.from_dense(gf_lcm(den.to_dense(), coeff.denom.to_dense(),
  1265. p, dom.domain))
  1266. else:
  1267. dom = qring.domain.domain.field
  1268. den = dom.ring.one
  1269. for coeff in h.itercoeffs():
  1270. for c in coeff.itercoeffs():
  1271. den = dom.ring.from_dense(gf_lcm(den.to_dense(), c.denom.to_dense(),
  1272. p, dom.domain))
  1273. den = qring.domain_new(den.trunc_ground(p))
  1274. h = ring(h.mul_ground(den).as_expr()).trunc_ground(p)
  1275. if not _trial_division(f, h, minpoly, p) and not _trial_division(g, h, minpoly, p):
  1276. return h
  1277. return None
  1278. def _integer_rational_reconstruction(c, m, domain):
  1279. r"""
  1280. Reconstruct a rational number `\frac a b` from
  1281. .. math::
  1282. c = \frac a b \; \mathrm{mod} \, m,
  1283. where `c` and `m` are integers.
  1284. The algorithm is based on the Euclidean Algorithm. In general, `m` is
  1285. not a prime number, so it is possible that `b` is not invertible modulo
  1286. `m`. In that case ``None`` is returned.
  1287. Parameters
  1288. ==========
  1289. c : Integer
  1290. `c = \frac a b \; \mathrm{mod} \, m`
  1291. m : Integer
  1292. modulus, not necessarily prime
  1293. domain : IntegerRing
  1294. `a, b, c` are elements of ``domain``
  1295. Returns
  1296. =======
  1297. frac : Rational
  1298. either `\frac a b` in `\mathbb Q` or ``None``
  1299. References
  1300. ==========
  1301. 1. [Wang81]_
  1302. """
  1303. if c < 0:
  1304. c += m
  1305. r0, s0 = m, domain.zero
  1306. r1, s1 = c, domain.one
  1307. bound = sqrt(m / 2) # still correct if replaced by ZZ.sqrt(m // 2) ?
  1308. while r1 >= bound:
  1309. quo = r0 // r1
  1310. r0, r1 = r1, r0 - quo*r1
  1311. s0, s1 = s1, s0 - quo*s1
  1312. if abs(s1) >= bound:
  1313. return None
  1314. if s1 < 0:
  1315. a, b = -r1, -s1
  1316. elif s1 > 0:
  1317. a, b = r1, s1
  1318. else:
  1319. return None
  1320. field = domain.get_field()
  1321. return field(a) / field(b)
  1322. def _rational_reconstruction_int_coeffs(hm, m, ring):
  1323. r"""
  1324. Reconstruct every rational coefficient `c_h` of a polynomial `h` in
  1325. `\mathbb Q[t_1, \ldots, t_k][x, z]` from the corresponding integer
  1326. coefficient `c_{h_m}` of a polynomial `h_m` in
  1327. `\mathbb Z[t_1, \ldots, t_k][x, z]` such that
  1328. .. math::
  1329. c_{h_m} = c_h \; \mathrm{mod} \, m,
  1330. where `m \in \mathbb Z`.
  1331. The reconstruction is based on the Euclidean Algorithm. In general,
  1332. `m` is not a prime number, so it is possible that this fails for some
  1333. coefficient. In that case ``None`` is returned.
  1334. Parameters
  1335. ==========
  1336. hm : PolyElement
  1337. polynomial in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1338. m : Integer
  1339. modulus, not necessarily prime
  1340. ring : PolyRing
  1341. `\mathbb Q[t_1, \ldots, t_k][x, z]`, `h` will be an element of this
  1342. ring
  1343. Returns
  1344. =======
  1345. h : PolyElement
  1346. reconstructed polynomial in `\mathbb Q[t_1, \ldots, t_k][x, z]` or
  1347. ``None``
  1348. See also
  1349. ========
  1350. _integer_rational_reconstruction
  1351. """
  1352. h = ring.zero
  1353. if isinstance(ring.domain, PolynomialRing):
  1354. reconstruction = _rational_reconstruction_int_coeffs
  1355. domain = ring.domain.ring
  1356. else:
  1357. reconstruction = _integer_rational_reconstruction
  1358. domain = hm.ring.domain
  1359. for monom, coeff in hm.iterterms():
  1360. coeffh = reconstruction(coeff, m, domain)
  1361. if not coeffh:
  1362. return None
  1363. h[monom] = coeffh
  1364. return h
  1365. def _func_field_modgcd_m(f, g, minpoly):
  1366. r"""
  1367. Compute the GCD of two polynomials in
  1368. `\mathbb Q(t_1, \ldots, t_k)[z]/(m_{\alpha}(z))[x]` using a modular
  1369. algorithm.
  1370. The algorithm computes the GCD of two polynomials `f` and `g` by
  1371. calculating the GCD in
  1372. `\mathbb Z_p(t_1, \ldots, t_k)[z] / (\check m_{\alpha}(z))[x]` for
  1373. suitable primes `p` and the primitive associate `\check m_{\alpha}(z)`
  1374. of `m_{\alpha}(z)`. Then the coefficients are reconstructed with the
  1375. Chinese Remainder Theorem and Rational Reconstruction. To compute the
  1376. GCD over `\mathbb Z_p(t_1, \ldots, t_k)[z] / (\check m_{\alpha})[x]`,
  1377. the recursive subroutine ``_func_field_modgcd_p`` is used. To verify the
  1378. result in `\mathbb Q(t_1, \ldots, t_k)[z] / (m_{\alpha}(z))[x]`, a
  1379. fraction free trial division is used.
  1380. Parameters
  1381. ==========
  1382. f, g : PolyElement
  1383. polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1384. minpoly : PolyElement
  1385. irreducible polynomial in `\mathbb Z[t_1, \ldots, t_k][z]`
  1386. Returns
  1387. =======
  1388. h : PolyElement
  1389. the primitive associate in `\mathbb Z[t_1, \ldots, t_k][x, z]` of
  1390. the GCD of `f` and `g`
  1391. Examples
  1392. ========
  1393. >>> from sympy.polys.modulargcd import _func_field_modgcd_m
  1394. >>> from sympy.polys import ring, ZZ
  1395. >>> R, x, z = ring('x, z', ZZ)
  1396. >>> minpoly = (z**2 - 2).drop(0)
  1397. >>> f = x**2 + 2*x*z + 2
  1398. >>> g = x + z
  1399. >>> _func_field_modgcd_m(f, g, minpoly)
  1400. x + z
  1401. >>> D, t = ring('t', ZZ)
  1402. >>> R, x, z = ring('x, z', D)
  1403. >>> minpoly = (z**2-3).drop(0)
  1404. >>> f = x**2 + (t + 1)*x*z + 3*t
  1405. >>> g = x*z + 3*t
  1406. >>> _func_field_modgcd_m(f, g, minpoly)
  1407. x + t*z
  1408. References
  1409. ==========
  1410. 1. [Hoeij04]_
  1411. See also
  1412. ========
  1413. _func_field_modgcd_p
  1414. """
  1415. ring = f.ring
  1416. domain = ring.domain
  1417. if isinstance(domain, PolynomialRing):
  1418. k = domain.ngens
  1419. QQdomain = domain.ring.clone(domain=domain.domain.get_field())
  1420. QQring = ring.clone(domain=QQdomain)
  1421. else:
  1422. k = 0
  1423. QQring = ring.clone(domain=ring.domain.get_field())
  1424. cf, f = f.primitive()
  1425. cg, g = g.primitive()
  1426. # polynomial in Z[t_1, ..., t_k][z]
  1427. gamma = ring.dmp_LC(f) * ring.dmp_LC(g)
  1428. # polynomial in Z[t_1, ..., t_k]
  1429. delta = minpoly.LC
  1430. p = 1
  1431. primes = []
  1432. hplist = []
  1433. LMlist = []
  1434. while True:
  1435. p = nextprime(p)
  1436. if gamma.trunc_ground(p) == 0:
  1437. continue
  1438. if k == 0:
  1439. test = (delta % p == 0)
  1440. else:
  1441. test = (delta.trunc_ground(p) == 0)
  1442. if test:
  1443. continue
  1444. fp = f.trunc_ground(p)
  1445. gp = g.trunc_ground(p)
  1446. minpolyp = minpoly.trunc_ground(p)
  1447. hp = _func_field_modgcd_p(fp, gp, minpolyp, p)
  1448. if hp is None:
  1449. continue
  1450. if hp == 1:
  1451. return ring.one
  1452. LM = [hp.degree()] + [0]*k
  1453. if k > 0:
  1454. for monom, coeff in hp.iterterms():
  1455. if monom[0] == LM[0] and coeff.LM > tuple(LM[1:]):
  1456. LM[1:] = coeff.LM
  1457. hm = hp
  1458. m = p
  1459. for q, hq, LMhq in zip(primes, hplist, LMlist):
  1460. if LMhq == LM:
  1461. hm = _chinese_remainder_reconstruction_multivariate(hq, hm, q, m)
  1462. m *= q
  1463. primes.append(p)
  1464. hplist.append(hp)
  1465. LMlist.append(LM)
  1466. hm = _rational_reconstruction_int_coeffs(hm, m, QQring)
  1467. if hm is None:
  1468. continue
  1469. if k == 0:
  1470. h = hm.clear_denoms()[1]
  1471. else:
  1472. den = domain.domain.one
  1473. for coeff in hm.itercoeffs():
  1474. den = domain.domain.lcm(den, coeff.clear_denoms()[0])
  1475. h = hm.mul_ground(den)
  1476. # convert back to Z[t_1, ..., t_k][x, z] from Q[t_1, ..., t_k][x, z]
  1477. h = h.set_ring(ring)
  1478. h = h.primitive()[1]
  1479. if not (_trial_division(f.mul_ground(cf), h, minpoly) or
  1480. _trial_division(g.mul_ground(cg), h, minpoly)):
  1481. return h
  1482. def _to_ZZ_poly(f, ring):
  1483. r"""
  1484. Compute an associate of a polynomial
  1485. `f \in \mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` in
  1486. `\mathbb Z[x_1, \ldots, x_{n-1}][z] / (\check m_{\alpha}(z))[x_0]`,
  1487. where `\check m_{\alpha}(z) \in \mathbb Z[z]` is the primitive associate
  1488. of the minimal polynomial `m_{\alpha}(z)` of `\alpha` over
  1489. `\mathbb Q`.
  1490. Parameters
  1491. ==========
  1492. f : PolyElement
  1493. polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1494. ring : PolyRing
  1495. `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]`
  1496. Returns
  1497. =======
  1498. f_ : PolyElement
  1499. associate of `f` in
  1500. `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]`
  1501. """
  1502. f_ = ring.zero
  1503. if isinstance(ring.domain, PolynomialRing):
  1504. domain = ring.domain.domain
  1505. else:
  1506. domain = ring.domain
  1507. den = domain.one
  1508. for coeff in f.itercoeffs():
  1509. for c in coeff.rep:
  1510. if c:
  1511. den = domain.lcm(den, c.denominator)
  1512. for monom, coeff in f.iterterms():
  1513. coeff = coeff.rep
  1514. m = ring.domain.one
  1515. if isinstance(ring.domain, PolynomialRing):
  1516. m = m.mul_monom(monom[1:])
  1517. n = len(coeff)
  1518. for i in range(n):
  1519. if coeff[i]:
  1520. c = domain(coeff[i] * den) * m
  1521. if (monom[0], n-i-1) not in f_:
  1522. f_[(monom[0], n-i-1)] = c
  1523. else:
  1524. f_[(monom[0], n-i-1)] += c
  1525. return f_
  1526. def _to_ANP_poly(f, ring):
  1527. r"""
  1528. Convert a polynomial
  1529. `f \in \mathbb Z[x_1, \ldots, x_{n-1}][z]/(\check m_{\alpha}(z))[x_0]`
  1530. to a polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`,
  1531. where `\check m_{\alpha}(z) \in \mathbb Z[z]` is the primitive associate
  1532. of the minimal polynomial `m_{\alpha}(z)` of `\alpha` over
  1533. `\mathbb Q`.
  1534. Parameters
  1535. ==========
  1536. f : PolyElement
  1537. polynomial in `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]`
  1538. ring : PolyRing
  1539. `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1540. Returns
  1541. =======
  1542. f_ : PolyElement
  1543. polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1544. """
  1545. domain = ring.domain
  1546. f_ = ring.zero
  1547. if isinstance(f.ring.domain, PolynomialRing):
  1548. for monom, coeff in f.iterterms():
  1549. for mon, coef in coeff.iterterms():
  1550. m = (monom[0],) + mon
  1551. c = domain([domain.domain(coef)] + [0]*monom[1])
  1552. if m not in f_:
  1553. f_[m] = c
  1554. else:
  1555. f_[m] += c
  1556. else:
  1557. for monom, coeff in f.iterterms():
  1558. m = (monom[0],)
  1559. c = domain([domain.domain(coeff)] + [0]*monom[1])
  1560. if m not in f_:
  1561. f_[m] = c
  1562. else:
  1563. f_[m] += c
  1564. return f_
  1565. def _minpoly_from_dense(minpoly, ring):
  1566. r"""
  1567. Change representation of the minimal polynomial from ``DMP`` to
  1568. ``PolyElement`` for a given ring.
  1569. """
  1570. minpoly_ = ring.zero
  1571. for monom, coeff in minpoly.terms():
  1572. minpoly_[monom] = ring.domain(coeff)
  1573. return minpoly_
  1574. def _primitive_in_x0(f):
  1575. r"""
  1576. Compute the content in `x_0` and the primitive part of a polynomial `f`
  1577. in
  1578. `\mathbb Q(\alpha)[x_0, x_1, \ldots, x_{n-1}] \cong \mathbb Q(\alpha)[x_1, \ldots, x_{n-1}][x_0]`.
  1579. """
  1580. fring = f.ring
  1581. ring = fring.drop_to_ground(*range(1, fring.ngens))
  1582. dom = ring.domain.ring
  1583. f_ = ring(f.as_expr())
  1584. cont = dom.zero
  1585. for coeff in f_.itercoeffs():
  1586. cont = func_field_modgcd(cont, coeff)[0]
  1587. if cont == dom.one:
  1588. return cont, f
  1589. return cont, f.quo(cont.set_ring(fring))
  1590. # TODO: add support for algebraic function fields
  1591. def func_field_modgcd(f, g):
  1592. r"""
  1593. Compute the GCD of two polynomials `f` and `g` in
  1594. `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` using a modular algorithm.
  1595. The algorithm first computes the primitive associate
  1596. `\check m_{\alpha}(z)` of the minimal polynomial `m_{\alpha}` in
  1597. `\mathbb{Z}[z]` and the primitive associates of `f` and `g` in
  1598. `\mathbb{Z}[x_1, \ldots, x_{n-1}][z]/(\check m_{\alpha})[x_0]`. Then it
  1599. computes the GCD in
  1600. `\mathbb Q(x_1, \ldots, x_{n-1})[z]/(m_{\alpha}(z))[x_0]`.
  1601. This is done by calculating the GCD in
  1602. `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]` for
  1603. suitable primes `p` and then reconstructing the coefficients with the
  1604. Chinese Remainder Theorem and Rational Reconstuction. The GCD over
  1605. `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]` is
  1606. computed with a recursive subroutine, which evaluates the polynomials at
  1607. `x_{n-1} = a` for suitable evaluation points `a \in \mathbb Z_p` and
  1608. then calls itself recursively until the ground domain does no longer
  1609. contain any parameters. For
  1610. `\mathbb{Z}_p[z]/(\check m_{\alpha}(z))[x_0]` the Euclidean Algorithm is
  1611. used. The results of those recursive calls are then interpolated and
  1612. Rational Function Reconstruction is used to obtain the correct
  1613. coefficients. The results, both in
  1614. `\mathbb Q(x_1, \ldots, x_{n-1})[z]/(m_{\alpha}(z))[x_0]` and
  1615. `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]`, are
  1616. verified by a fraction free trial division.
  1617. Apart from the above GCD computation some GCDs in
  1618. `\mathbb Q(\alpha)[x_1, \ldots, x_{n-1}]` have to be calculated,
  1619. because treating the polynomials as univariate ones can result in
  1620. a spurious content of the GCD. For this ``func_field_modgcd`` is
  1621. called recursively.
  1622. Parameters
  1623. ==========
  1624. f, g : PolyElement
  1625. polynomials in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1626. Returns
  1627. =======
  1628. h : PolyElement
  1629. monic GCD of the polynomials `f` and `g`
  1630. cff : PolyElement
  1631. cofactor of `f`, i.e. `\frac f h`
  1632. cfg : PolyElement
  1633. cofactor of `g`, i.e. `\frac g h`
  1634. Examples
  1635. ========
  1636. >>> from sympy.polys.modulargcd import func_field_modgcd
  1637. >>> from sympy.polys import AlgebraicField, QQ, ring
  1638. >>> from sympy import sqrt
  1639. >>> A = AlgebraicField(QQ, sqrt(2))
  1640. >>> R, x = ring('x', A)
  1641. >>> f = x**2 - 2
  1642. >>> g = x + sqrt(2)
  1643. >>> h, cff, cfg = func_field_modgcd(f, g)
  1644. >>> h == x + sqrt(2)
  1645. True
  1646. >>> cff * h == f
  1647. True
  1648. >>> cfg * h == g
  1649. True
  1650. >>> R, x, y = ring('x, y', A)
  1651. >>> f = x**2 + 2*sqrt(2)*x*y + 2*y**2
  1652. >>> g = x + sqrt(2)*y
  1653. >>> h, cff, cfg = func_field_modgcd(f, g)
  1654. >>> h == x + sqrt(2)*y
  1655. True
  1656. >>> cff * h == f
  1657. True
  1658. >>> cfg * h == g
  1659. True
  1660. >>> f = x + sqrt(2)*y
  1661. >>> g = x + y
  1662. >>> h, cff, cfg = func_field_modgcd(f, g)
  1663. >>> h == R.one
  1664. True
  1665. >>> cff * h == f
  1666. True
  1667. >>> cfg * h == g
  1668. True
  1669. References
  1670. ==========
  1671. 1. [Hoeij04]_
  1672. """
  1673. ring = f.ring
  1674. domain = ring.domain
  1675. n = ring.ngens
  1676. assert ring == g.ring and domain.is_Algebraic
  1677. result = _trivial_gcd(f, g)
  1678. if result is not None:
  1679. return result
  1680. z = Dummy('z')
  1681. ZZring = ring.clone(symbols=ring.symbols + (z,), domain=domain.domain.get_ring())
  1682. if n == 1:
  1683. f_ = _to_ZZ_poly(f, ZZring)
  1684. g_ = _to_ZZ_poly(g, ZZring)
  1685. minpoly = ZZring.drop(0).from_dense(domain.mod.rep)
  1686. h = _func_field_modgcd_m(f_, g_, minpoly)
  1687. h = _to_ANP_poly(h, ring)
  1688. else:
  1689. # contx0f in Q(a)[x_1, ..., x_{n-1}], f in Q(a)[x_0, ..., x_{n-1}]
  1690. contx0f, f = _primitive_in_x0(f)
  1691. contx0g, g = _primitive_in_x0(g)
  1692. contx0h = func_field_modgcd(contx0f, contx0g)[0]
  1693. ZZring_ = ZZring.drop_to_ground(*range(1, n))
  1694. f_ = _to_ZZ_poly(f, ZZring_)
  1695. g_ = _to_ZZ_poly(g, ZZring_)
  1696. minpoly = _minpoly_from_dense(domain.mod, ZZring_.drop(0))
  1697. h = _func_field_modgcd_m(f_, g_, minpoly)
  1698. h = _to_ANP_poly(h, ring)
  1699. contx0h_, h = _primitive_in_x0(h)
  1700. h *= contx0h.set_ring(ring)
  1701. f *= contx0f.set_ring(ring)
  1702. g *= contx0g.set_ring(ring)
  1703. h = h.quo_ground(h.LC)
  1704. return h, f.quo(h), g.quo(h)