modulargcd.py 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278
  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. domain = hp.ring.domain
  461. zero = domain.zero
  462. hpq = hp.ring.zero
  463. if isinstance(hp.ring.domain, PolynomialRing):
  464. crt_ = _chinese_remainder_reconstruction_multivariate
  465. else:
  466. def crt_(cp, cq, p, q):
  467. return domain(crt([p, q], [cp, cq], symmetric=True)[0])
  468. for monom in monoms:
  469. hpq[monom] = crt_(hp[monom], hq[monom], p, q)
  470. for monom in hpmonoms:
  471. hpq[monom] = crt_(hp[monom], zero, p, q)
  472. for monom in hqmonoms:
  473. hpq[monom] = crt_(zero, hq[monom], p, q)
  474. return hpq
  475. def _interpolate_multivariate(evalpoints, hpeval, ring, i, p, ground=False):
  476. r"""
  477. Reconstruct a polynomial `h_p` in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`
  478. from a list of evaluation points in `\mathbb{Z}_p` and a list of
  479. polynomials in
  480. `\mathbb{Z}_p[x_0, \ldots, x_{i-1}, x_{i+1}, \ldots, x_{k-1}]`, which
  481. are the images of `h_p` evaluated in the variable `x_i`.
  482. It is also possible to reconstruct a parameter of the ground domain,
  483. i.e. if `h_p` is a polynomial over `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`.
  484. In this case, one has to set ``ground=True``.
  485. Parameters
  486. ==========
  487. evalpoints : list of Integer objects
  488. list of evaluation points in `\mathbb{Z}_p`
  489. hpeval : list of PolyElement objects
  490. list of polynomials in (resp. over)
  491. `\mathbb{Z}_p[x_0, \ldots, x_{i-1}, x_{i+1}, \ldots, x_{k-1}]`,
  492. images of `h_p` evaluated in the variable `x_i`
  493. ring : PolyRing
  494. `h_p` will be an element of this ring
  495. i : Integer
  496. index of the variable which has to be reconstructed
  497. p : Integer
  498. prime number, modulus of `h_p`
  499. ground : Boolean
  500. indicates whether `x_i` is in the ground domain, default is
  501. ``False``
  502. Returns
  503. =======
  504. hp : PolyElement
  505. interpolated polynomial in (resp. over)
  506. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`
  507. """
  508. hp = ring.zero
  509. if ground:
  510. domain = ring.domain.domain
  511. y = ring.domain.gens[i]
  512. else:
  513. domain = ring.domain
  514. y = ring.gens[i]
  515. for a, hpa in zip(evalpoints, hpeval):
  516. numer = ring.one
  517. denom = domain.one
  518. for b in evalpoints:
  519. if b == a:
  520. continue
  521. numer *= y - b
  522. denom *= a - b
  523. denom = domain.invert(denom, p)
  524. coeff = numer.mul_ground(denom)
  525. hp += hpa.set_ring(ring) * coeff
  526. return hp.trunc_ground(p)
  527. def modgcd_bivariate(f, g):
  528. r"""
  529. Computes the GCD of two polynomials in `\mathbb{Z}[x, y]` using a
  530. modular algorithm.
  531. The algorithm computes the GCD of two bivariate integer polynomials
  532. `f` and `g` by calculating the GCD in `\mathbb{Z}_p[x, y]` for
  533. suitable primes `p` and then reconstructing the coefficients with the
  534. Chinese Remainder Theorem. To compute the bivariate GCD over
  535. `\mathbb{Z}_p`, the polynomials `f \; \mathrm{mod} \, p` and
  536. `g \; \mathrm{mod} \, p` are evaluated at `y = a` for certain
  537. `a \in \mathbb{Z}_p` and then their univariate GCD in `\mathbb{Z}_p[x]`
  538. is computed. Interpolating those yields the bivariate GCD in
  539. `\mathbb{Z}_p[x, y]`. To verify the result in `\mathbb{Z}[x, y]`, trial
  540. division is done, but only for candidates which are very likely the
  541. desired GCD.
  542. Parameters
  543. ==========
  544. f : PolyElement
  545. bivariate integer polynomial
  546. g : PolyElement
  547. bivariate integer polynomial
  548. Returns
  549. =======
  550. h : PolyElement
  551. GCD of the polynomials `f` and `g`
  552. cff : PolyElement
  553. cofactor of `f`, i.e. `\frac{f}{h}`
  554. cfg : PolyElement
  555. cofactor of `g`, i.e. `\frac{g}{h}`
  556. Examples
  557. ========
  558. >>> from sympy.polys.modulargcd import modgcd_bivariate
  559. >>> from sympy.polys import ring, ZZ
  560. >>> R, x, y = ring("x, y", ZZ)
  561. >>> f = x**2 - y**2
  562. >>> g = x**2 + 2*x*y + y**2
  563. >>> h, cff, cfg = modgcd_bivariate(f, g)
  564. >>> h, cff, cfg
  565. (x + y, x - y, x + y)
  566. >>> cff * h == f
  567. True
  568. >>> cfg * h == g
  569. True
  570. >>> f = x**2*y - x**2 - 4*y + 4
  571. >>> g = x + 2
  572. >>> h, cff, cfg = modgcd_bivariate(f, g)
  573. >>> h, cff, cfg
  574. (x + 2, x*y - x - 2*y + 2, 1)
  575. >>> cff * h == f
  576. True
  577. >>> cfg * h == g
  578. True
  579. References
  580. ==========
  581. 1. [Monagan00]_
  582. """
  583. assert f.ring == g.ring and f.ring.domain.is_ZZ
  584. result = _trivial_gcd(f, g)
  585. if result is not None:
  586. return result
  587. ring = f.ring
  588. cf, f = f.primitive()
  589. cg, g = g.primitive()
  590. ch = ring.domain.gcd(cf, cg)
  591. xbound, ycontbound = _degree_bound_bivariate(f, g)
  592. if xbound == ycontbound == 0:
  593. return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch)
  594. fswap = _swap(f, 1)
  595. gswap = _swap(g, 1)
  596. degyf = fswap.degree()
  597. degyg = gswap.degree()
  598. ybound, xcontbound = _degree_bound_bivariate(fswap, gswap)
  599. if ybound == xcontbound == 0:
  600. return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch)
  601. # TODO: to improve performance, choose the main variable here
  602. gamma1 = ring.domain.gcd(f.LC, g.LC)
  603. gamma2 = ring.domain.gcd(fswap.LC, gswap.LC)
  604. badprimes = gamma1 * gamma2
  605. m = 1
  606. p = 1
  607. while True:
  608. p = nextprime(p)
  609. while badprimes % p == 0:
  610. p = nextprime(p)
  611. fp = f.trunc_ground(p)
  612. gp = g.trunc_ground(p)
  613. contfp, fp = _primitive(fp, p)
  614. contgp, gp = _primitive(gp, p)
  615. conthp = _gf_gcd(contfp, contgp, p) # monic polynomial in Z_p[y]
  616. degconthp = conthp.degree()
  617. if degconthp > ycontbound:
  618. continue
  619. elif degconthp < ycontbound:
  620. m = 1
  621. ycontbound = degconthp
  622. continue
  623. # polynomial in Z_p[y]
  624. delta = _gf_gcd(_LC(fp), _LC(gp), p)
  625. degcontfp = contfp.degree()
  626. degcontgp = contgp.degree()
  627. degdelta = delta.degree()
  628. N = min(degyf - degcontfp, degyg - degcontgp,
  629. ybound - ycontbound + degdelta) + 1
  630. if p < N:
  631. continue
  632. n = 0
  633. evalpoints = []
  634. hpeval = []
  635. unlucky = False
  636. for a in range(p):
  637. deltaa = delta.evaluate(0, a)
  638. if not deltaa % p:
  639. continue
  640. fpa = fp.evaluate(1, a).trunc_ground(p)
  641. gpa = gp.evaluate(1, a).trunc_ground(p)
  642. hpa = _gf_gcd(fpa, gpa, p) # monic polynomial in Z_p[x]
  643. deghpa = hpa.degree()
  644. if deghpa > xbound:
  645. continue
  646. elif deghpa < xbound:
  647. m = 1
  648. xbound = deghpa
  649. unlucky = True
  650. break
  651. hpa = hpa.mul_ground(deltaa).trunc_ground(p)
  652. evalpoints.append(a)
  653. hpeval.append(hpa)
  654. n += 1
  655. if n == N:
  656. break
  657. if unlucky:
  658. continue
  659. if n < N:
  660. continue
  661. hp = _interpolate_multivariate(evalpoints, hpeval, ring, 1, p)
  662. hp = _primitive(hp, p)[1]
  663. hp = hp * conthp.set_ring(ring)
  664. degyhp = hp.degree(1)
  665. if degyhp > ybound:
  666. continue
  667. if degyhp < ybound:
  668. m = 1
  669. ybound = degyhp
  670. continue
  671. hp = hp.mul_ground(gamma1).trunc_ground(p)
  672. if m == 1:
  673. m = p
  674. hlastm = hp
  675. continue
  676. hm = _chinese_remainder_reconstruction_multivariate(hp, hlastm, p, m)
  677. m *= p
  678. if not hm == hlastm:
  679. hlastm = hm
  680. continue
  681. h = hm.quo_ground(hm.content())
  682. fquo, frem = f.div(h)
  683. gquo, grem = g.div(h)
  684. if not frem and not grem:
  685. if h.LC < 0:
  686. ch = -ch
  687. h = h.mul_ground(ch)
  688. cff = fquo.mul_ground(cf // ch)
  689. cfg = gquo.mul_ground(cg // ch)
  690. return h, cff, cfg
  691. def _modgcd_multivariate_p(f, g, p, degbound, contbound):
  692. r"""
  693. Compute the GCD of two polynomials in
  694. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`.
  695. The algorithm reduces the problem step by step by evaluating the
  696. polynomials `f` and `g` at `x_{k-1} = a` for suitable
  697. `a \in \mathbb{Z}_p` and then calls itself recursively to compute the GCD
  698. in `\mathbb{Z}_p[x_0, \ldots, x_{k-2}]`. If these recursive calls are
  699. successful for enough evaluation points, the GCD in `k` variables is
  700. interpolated, otherwise the algorithm returns ``None``. Every time a GCD
  701. or a content is computed, their degrees are compared with the bounds. If
  702. a degree greater then the bound is encountered, then the current call
  703. returns ``None`` and a new evaluation point has to be chosen. If at some
  704. point the degree is smaller, the correspondent bound is updated and the
  705. algorithm fails.
  706. Parameters
  707. ==========
  708. f : PolyElement
  709. multivariate integer polynomial with coefficients in `\mathbb{Z}_p`
  710. g : PolyElement
  711. multivariate integer polynomial with coefficients in `\mathbb{Z}_p`
  712. p : Integer
  713. prime number, modulus of `f` and `g`
  714. degbound : list of Integer objects
  715. ``degbound[i]`` is an upper bound for the degree of the GCD of `f`
  716. and `g` in the variable `x_i`
  717. contbound : list of Integer objects
  718. ``contbound[i]`` is an upper bound for the degree of the content of
  719. the GCD in `\mathbb{Z}_p[x_i][x_0, \ldots, x_{i-1}]`,
  720. ``contbound[0]`` is not used can therefore be chosen
  721. arbitrarily.
  722. Returns
  723. =======
  724. h : PolyElement
  725. GCD of the polynomials `f` and `g` or ``None``
  726. References
  727. ==========
  728. 1. [Monagan00]_
  729. 2. [Brown71]_
  730. """
  731. ring = f.ring
  732. k = ring.ngens
  733. if k == 1:
  734. h = _gf_gcd(f, g, p).trunc_ground(p)
  735. degh = h.degree()
  736. if degh > degbound[0]:
  737. return None
  738. if degh < degbound[0]:
  739. degbound[0] = degh
  740. raise ModularGCDFailed
  741. return h
  742. degyf = f.degree(k-1)
  743. degyg = g.degree(k-1)
  744. contf, f = _primitive(f, p)
  745. contg, g = _primitive(g, p)
  746. conth = _gf_gcd(contf, contg, p) # polynomial in Z_p[y]
  747. degcontf = contf.degree()
  748. degcontg = contg.degree()
  749. degconth = conth.degree()
  750. if degconth > contbound[k-1]:
  751. return None
  752. if degconth < contbound[k-1]:
  753. contbound[k-1] = degconth
  754. raise ModularGCDFailed
  755. lcf = _LC(f)
  756. lcg = _LC(g)
  757. delta = _gf_gcd(lcf, lcg, p) # polynomial in Z_p[y]
  758. evaltest = delta
  759. for i in range(k-1):
  760. evaltest *= _gf_gcd(_LC(_swap(f, i)), _LC(_swap(g, i)), p)
  761. degdelta = delta.degree()
  762. N = min(degyf - degcontf, degyg - degcontg,
  763. degbound[k-1] - contbound[k-1] + degdelta) + 1
  764. if p < N:
  765. return None
  766. n = 0
  767. d = 0
  768. evalpoints = []
  769. heval = []
  770. points = list(range(p))
  771. while points:
  772. a = random.sample(points, 1)[0]
  773. points.remove(a)
  774. if not evaltest.evaluate(0, a) % p:
  775. continue
  776. deltaa = delta.evaluate(0, a) % p
  777. fa = f.evaluate(k-1, a).trunc_ground(p)
  778. ga = g.evaluate(k-1, a).trunc_ground(p)
  779. # polynomials in Z_p[x_0, ..., x_{k-2}]
  780. ha = _modgcd_multivariate_p(fa, ga, p, degbound, contbound)
  781. if ha is None:
  782. d += 1
  783. if d > n:
  784. return None
  785. continue
  786. if ha.is_ground:
  787. h = conth.set_ring(ring).trunc_ground(p)
  788. return h
  789. ha = ha.mul_ground(deltaa).trunc_ground(p)
  790. evalpoints.append(a)
  791. heval.append(ha)
  792. n += 1
  793. if n == N:
  794. h = _interpolate_multivariate(evalpoints, heval, ring, k-1, p)
  795. h = _primitive(h, p)[1] * conth.set_ring(ring)
  796. degyh = h.degree(k-1)
  797. if degyh > degbound[k-1]:
  798. return None
  799. if degyh < degbound[k-1]:
  800. degbound[k-1] = degyh
  801. raise ModularGCDFailed
  802. return h
  803. return None
  804. def modgcd_multivariate(f, g):
  805. r"""
  806. Compute the GCD of two polynomials in `\mathbb{Z}[x_0, \ldots, x_{k-1}]`
  807. using a modular algorithm.
  808. The algorithm computes the GCD of two multivariate integer polynomials
  809. `f` and `g` by calculating the GCD in
  810. `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` for suitable primes `p` and then
  811. reconstructing the coefficients with the Chinese Remainder Theorem. To
  812. compute the multivariate GCD over `\mathbb{Z}_p` the recursive
  813. subroutine :func:`_modgcd_multivariate_p` is used. To verify the result in
  814. `\mathbb{Z}[x_0, \ldots, x_{k-1}]`, trial division is done, but only for
  815. candidates which are very likely the desired GCD.
  816. Parameters
  817. ==========
  818. f : PolyElement
  819. multivariate integer polynomial
  820. g : PolyElement
  821. multivariate integer polynomial
  822. Returns
  823. =======
  824. h : PolyElement
  825. GCD of the polynomials `f` and `g`
  826. cff : PolyElement
  827. cofactor of `f`, i.e. `\frac{f}{h}`
  828. cfg : PolyElement
  829. cofactor of `g`, i.e. `\frac{g}{h}`
  830. Examples
  831. ========
  832. >>> from sympy.polys.modulargcd import modgcd_multivariate
  833. >>> from sympy.polys import ring, ZZ
  834. >>> R, x, y = ring("x, y", ZZ)
  835. >>> f = x**2 - y**2
  836. >>> g = x**2 + 2*x*y + y**2
  837. >>> h, cff, cfg = modgcd_multivariate(f, g)
  838. >>> h, cff, cfg
  839. (x + y, x - y, x + y)
  840. >>> cff * h == f
  841. True
  842. >>> cfg * h == g
  843. True
  844. >>> R, x, y, z = ring("x, y, z", ZZ)
  845. >>> f = x*z**2 - y*z**2
  846. >>> g = x**2*z + z
  847. >>> h, cff, cfg = modgcd_multivariate(f, g)
  848. >>> h, cff, cfg
  849. (z, x*z - y*z, x**2 + 1)
  850. >>> cff * h == f
  851. True
  852. >>> cfg * h == g
  853. True
  854. References
  855. ==========
  856. 1. [Monagan00]_
  857. 2. [Brown71]_
  858. See also
  859. ========
  860. _modgcd_multivariate_p
  861. """
  862. assert f.ring == g.ring and f.ring.domain.is_ZZ
  863. result = _trivial_gcd(f, g)
  864. if result is not None:
  865. return result
  866. ring = f.ring
  867. k = ring.ngens
  868. # divide out integer content
  869. cf, f = f.primitive()
  870. cg, g = g.primitive()
  871. ch = ring.domain.gcd(cf, cg)
  872. gamma = ring.domain.gcd(f.LC, g.LC)
  873. badprimes = ring.domain.one
  874. for i in range(k):
  875. badprimes *= ring.domain.gcd(_swap(f, i).LC, _swap(g, i).LC)
  876. degbound = [min(fdeg, gdeg) for fdeg, gdeg in zip(f.degrees(), g.degrees())]
  877. contbound = list(degbound)
  878. m = 1
  879. p = 1
  880. while True:
  881. p = nextprime(p)
  882. while badprimes % p == 0:
  883. p = nextprime(p)
  884. fp = f.trunc_ground(p)
  885. gp = g.trunc_ground(p)
  886. try:
  887. # monic GCD of fp, gp in Z_p[x_0, ..., x_{k-2}, y]
  888. hp = _modgcd_multivariate_p(fp, gp, p, degbound, contbound)
  889. except ModularGCDFailed:
  890. m = 1
  891. continue
  892. if hp is None:
  893. continue
  894. hp = hp.mul_ground(gamma).trunc_ground(p)
  895. if m == 1:
  896. m = p
  897. hlastm = hp
  898. continue
  899. hm = _chinese_remainder_reconstruction_multivariate(hp, hlastm, p, m)
  900. m *= p
  901. if not hm == hlastm:
  902. hlastm = hm
  903. continue
  904. h = hm.primitive()[1]
  905. fquo, frem = f.div(h)
  906. gquo, grem = g.div(h)
  907. if not frem and not grem:
  908. if h.LC < 0:
  909. ch = -ch
  910. h = h.mul_ground(ch)
  911. cff = fquo.mul_ground(cf // ch)
  912. cfg = gquo.mul_ground(cg // ch)
  913. return h, cff, cfg
  914. def _gf_div(f, g, p):
  915. r"""
  916. Compute `\frac f g` modulo `p` for two univariate polynomials over
  917. `\mathbb Z_p`.
  918. """
  919. ring = f.ring
  920. densequo, denserem = gf_div(f.to_dense(), g.to_dense(), p, ring.domain)
  921. return ring.from_dense(densequo), ring.from_dense(denserem)
  922. def _rational_function_reconstruction(c, p, m):
  923. r"""
  924. Reconstruct a rational function `\frac a b` in `\mathbb Z_p(t)` from
  925. .. math::
  926. c = \frac a b \; \mathrm{mod} \, m,
  927. where `c` and `m` are polynomials in `\mathbb Z_p[t]` and `m` has
  928. positive degree.
  929. The algorithm is based on the Euclidean Algorithm. In general, `m` is
  930. not irreducible, so it is possible that `b` is not invertible modulo
  931. `m`. In that case ``None`` is returned.
  932. Parameters
  933. ==========
  934. c : PolyElement
  935. univariate polynomial in `\mathbb Z[t]`
  936. p : Integer
  937. prime number
  938. m : PolyElement
  939. modulus, not necessarily irreducible
  940. Returns
  941. =======
  942. frac : FracElement
  943. either `\frac a b` in `\mathbb Z(t)` or ``None``
  944. References
  945. ==========
  946. 1. [Hoeij04]_
  947. """
  948. ring = c.ring
  949. domain = ring.domain
  950. M = m.degree()
  951. N = M // 2
  952. D = M - N - 1
  953. r0, s0 = m, ring.zero
  954. r1, s1 = c, ring.one
  955. while r1.degree() > N:
  956. quo = _gf_div(r0, r1, p)[0]
  957. r0, r1 = r1, (r0 - quo*r1).trunc_ground(p)
  958. s0, s1 = s1, (s0 - quo*s1).trunc_ground(p)
  959. a, b = r1, s1
  960. if b.degree() > D or _gf_gcd(b, m, p) != 1:
  961. return None
  962. lc = b.LC
  963. if lc != 1:
  964. lcinv = domain.invert(lc, p)
  965. a = a.mul_ground(lcinv).trunc_ground(p)
  966. b = b.mul_ground(lcinv).trunc_ground(p)
  967. field = ring.to_field()
  968. return field(a) / field(b)
  969. def _rational_reconstruction_func_coeffs(hm, p, m, ring, k):
  970. r"""
  971. Reconstruct every coefficient `c_h` of a polynomial `h` in
  972. `\mathbb Z_p(t_k)[t_1, \ldots, t_{k-1}][x, z]` from the corresponding
  973. coefficient `c_{h_m}` of a polynomial `h_m` in
  974. `\mathbb Z_p[t_1, \ldots, t_k][x, z] \cong \mathbb Z_p[t_k][t_1, \ldots, t_{k-1}][x, z]`
  975. such that
  976. .. math::
  977. c_{h_m} = c_h \; \mathrm{mod} \, m,
  978. where `m \in \mathbb Z_p[t]`.
  979. The reconstruction is based on the Euclidean Algorithm. In general, `m`
  980. is not irreducible, so it is possible that this fails for some
  981. coefficient. In that case ``None`` is returned.
  982. Parameters
  983. ==========
  984. hm : PolyElement
  985. polynomial in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  986. p : Integer
  987. prime number, modulus of `\mathbb Z_p`
  988. m : PolyElement
  989. modulus, polynomial in `\mathbb Z[t]`, not necessarily irreducible
  990. ring : PolyRing
  991. `\mathbb Z(t_k)[t_1, \ldots, t_{k-1}][x, z]`, `h` will be an
  992. element of this ring
  993. k : Integer
  994. index of the parameter `t_k` which will be reconstructed
  995. Returns
  996. =======
  997. h : PolyElement
  998. reconstructed polynomial in
  999. `\mathbb Z(t_k)[t_1, \ldots, t_{k-1}][x, z]` or ``None``
  1000. See also
  1001. ========
  1002. _rational_function_reconstruction
  1003. """
  1004. h = ring.zero
  1005. for monom, coeff in hm.iterterms():
  1006. if k == 0:
  1007. coeffh = _rational_function_reconstruction(coeff, p, m)
  1008. if not coeffh:
  1009. return None
  1010. else:
  1011. coeffh = ring.domain.zero
  1012. for mon, c in coeff.drop_to_ground(k).iterterms():
  1013. ch = _rational_function_reconstruction(c, p, m)
  1014. if not ch:
  1015. return None
  1016. coeffh[mon] = ch
  1017. h[monom] = coeffh
  1018. return h
  1019. def _gf_gcdex(f, g, p):
  1020. r"""
  1021. Extended Euclidean Algorithm for two univariate polynomials over
  1022. `\mathbb Z_p`.
  1023. Returns polynomials `s, t` and `h`, such that `h` is the GCD of `f` and
  1024. `g` and `sf + tg = h \; \mathrm{mod} \, p`.
  1025. """
  1026. ring = f.ring
  1027. s, t, h = gf_gcdex(f.to_dense(), g.to_dense(), p, ring.domain)
  1028. return ring.from_dense(s), ring.from_dense(t), ring.from_dense(h)
  1029. def _trunc(f, minpoly, p):
  1030. r"""
  1031. Compute the reduced representation of a polynomial `f` in
  1032. `\mathbb Z_p[z] / (\check m_{\alpha}(z))[x]`
  1033. Parameters
  1034. ==========
  1035. f : PolyElement
  1036. polynomial in `\mathbb Z[x, z]`
  1037. minpoly : PolyElement
  1038. polynomial `\check m_{\alpha} \in \mathbb Z[z]`, not necessarily
  1039. irreducible
  1040. p : Integer
  1041. prime number, modulus of `\mathbb Z_p`
  1042. Returns
  1043. =======
  1044. ftrunc : PolyElement
  1045. polynomial in `\mathbb Z[x, z]`, reduced modulo
  1046. `\check m_{\alpha}(z)` and `p`
  1047. """
  1048. ring = f.ring
  1049. minpoly = minpoly.set_ring(ring)
  1050. p_ = ring.ground_new(p)
  1051. return f.trunc_ground(p).rem([minpoly, p_]).trunc_ground(p)
  1052. def _euclidean_algorithm(f, g, minpoly, p):
  1053. r"""
  1054. Compute the monic GCD of two univariate polynomials in
  1055. `\mathbb{Z}_p[z]/(\check m_{\alpha}(z))[x]` with the Euclidean
  1056. Algorithm.
  1057. In general, `\check m_{\alpha}(z)` is not irreducible, so it is possible
  1058. that some leading coefficient is not invertible modulo
  1059. `\check m_{\alpha}(z)`. In that case ``None`` is returned.
  1060. Parameters
  1061. ==========
  1062. f, g : PolyElement
  1063. polynomials in `\mathbb Z[x, z]`
  1064. minpoly : PolyElement
  1065. polynomial in `\mathbb Z[z]`, not necessarily irreducible
  1066. p : Integer
  1067. prime number, modulus of `\mathbb Z_p`
  1068. Returns
  1069. =======
  1070. h : PolyElement
  1071. GCD of `f` and `g` in `\mathbb Z[z, x]` or ``None``, coefficients
  1072. are in `\left[ -\frac{p-1} 2, \frac{p-1} 2 \right]`
  1073. """
  1074. ring = f.ring
  1075. f = _trunc(f, minpoly, p)
  1076. g = _trunc(g, minpoly, p)
  1077. while g:
  1078. rem = f
  1079. deg = g.degree(0) # degree in x
  1080. lcinv, _, gcd = _gf_gcdex(ring.dmp_LC(g), minpoly, p)
  1081. if not gcd == 1:
  1082. return None
  1083. while True:
  1084. degrem = rem.degree(0) # degree in x
  1085. if degrem < deg:
  1086. break
  1087. quo = (lcinv * ring.dmp_LC(rem)).set_ring(ring)
  1088. rem = _trunc(rem - g.mul_monom((degrem - deg, 0))*quo, minpoly, p)
  1089. f = g
  1090. g = rem
  1091. lcfinv = _gf_gcdex(ring.dmp_LC(f), minpoly, p)[0].set_ring(ring)
  1092. return _trunc(f * lcfinv, minpoly, p)
  1093. def _trial_division(f, h, minpoly, p=None):
  1094. r"""
  1095. Check if `h` divides `f` in
  1096. `\mathbb K[t_1, \ldots, t_k][z]/(m_{\alpha}(z))`, where `\mathbb K` is
  1097. either `\mathbb Q` or `\mathbb Z_p`.
  1098. This algorithm is based on pseudo division and does not use any
  1099. fractions. By default `\mathbb K` is `\mathbb Q`, if a prime number `p`
  1100. is given, `\mathbb Z_p` is chosen instead.
  1101. Parameters
  1102. ==========
  1103. f, h : PolyElement
  1104. polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1105. minpoly : PolyElement
  1106. polynomial `m_{\alpha}(z)` in `\mathbb Z[t_1, \ldots, t_k][z]`
  1107. p : Integer or None
  1108. if `p` is given, `\mathbb K` is set to `\mathbb Z_p` instead of
  1109. `\mathbb Q`, default is ``None``
  1110. Returns
  1111. =======
  1112. rem : PolyElement
  1113. remainder of `\frac f h`
  1114. References
  1115. ==========
  1116. .. [1] [Hoeij02]_
  1117. """
  1118. ring = f.ring
  1119. zxring = ring.clone(symbols=(ring.symbols[1], ring.symbols[0]))
  1120. minpoly = minpoly.set_ring(ring)
  1121. rem = f
  1122. degrem = rem.degree()
  1123. degh = h.degree()
  1124. degm = minpoly.degree(1)
  1125. lch = _LC(h).set_ring(ring)
  1126. lcm = minpoly.LC
  1127. while rem and degrem >= degh:
  1128. # polynomial in Z[t_1, ..., t_k][z]
  1129. lcrem = _LC(rem).set_ring(ring)
  1130. rem = rem*lch - h.mul_monom((degrem - degh, 0))*lcrem
  1131. if p:
  1132. rem = rem.trunc_ground(p)
  1133. degrem = rem.degree(1)
  1134. while rem and degrem >= degm:
  1135. # polynomial in Z[t_1, ..., t_k][x]
  1136. lcrem = _LC(rem.set_ring(zxring)).set_ring(ring)
  1137. rem = rem.mul_ground(lcm) - minpoly.mul_monom((0, degrem - degm))*lcrem
  1138. if p:
  1139. rem = rem.trunc_ground(p)
  1140. degrem = rem.degree(1)
  1141. degrem = rem.degree()
  1142. return rem
  1143. def _evaluate_ground(f, i, a):
  1144. r"""
  1145. Evaluate a polynomial `f` at `a` in the `i`-th variable of the ground
  1146. domain.
  1147. """
  1148. ring = f.ring.clone(domain=f.ring.domain.ring.drop(i))
  1149. fa = ring.zero
  1150. for monom, coeff in f.iterterms():
  1151. fa[monom] = coeff.evaluate(i, a)
  1152. return fa
  1153. def _func_field_modgcd_p(f, g, minpoly, p):
  1154. r"""
  1155. Compute the GCD of two polynomials `f` and `g` in
  1156. `\mathbb Z_p(t_1, \ldots, t_k)[z]/(\check m_\alpha(z))[x]`.
  1157. The algorithm reduces the problem step by step by evaluating the
  1158. polynomials `f` and `g` at `t_k = a` for suitable `a \in \mathbb Z_p`
  1159. and then calls itself recursively to compute the GCD in
  1160. `\mathbb Z_p(t_1, \ldots, t_{k-1})[z]/(\check m_\alpha(z))[x]`. If these
  1161. recursive calls are successful, the GCD over `k` variables is
  1162. interpolated, otherwise the algorithm returns ``None``. After
  1163. interpolation, Rational Function Reconstruction is used to obtain the
  1164. correct coefficients. If this fails, a new evaluation point has to be
  1165. chosen, otherwise the desired polynomial is obtained by clearing
  1166. denominators. The result is verified with a fraction free trial
  1167. division.
  1168. Parameters
  1169. ==========
  1170. f, g : PolyElement
  1171. polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1172. minpoly : PolyElement
  1173. polynomial in `\mathbb Z[t_1, \ldots, t_k][z]`, not necessarily
  1174. irreducible
  1175. p : Integer
  1176. prime number, modulus of `\mathbb Z_p`
  1177. Returns
  1178. =======
  1179. h : PolyElement
  1180. primitive associate in `\mathbb Z[t_1, \ldots, t_k][x, z]` of the
  1181. GCD of the polynomials `f` and `g` or ``None``, coefficients are
  1182. in `\left[ -\frac{p-1} 2, \frac{p-1} 2 \right]`
  1183. References
  1184. ==========
  1185. 1. [Hoeij04]_
  1186. """
  1187. ring = f.ring
  1188. domain = ring.domain # Z[t_1, ..., t_k]
  1189. if isinstance(domain, PolynomialRing):
  1190. k = domain.ngens
  1191. else:
  1192. return _euclidean_algorithm(f, g, minpoly, p)
  1193. if k == 1:
  1194. qdomain = domain.ring.to_field()
  1195. else:
  1196. qdomain = domain.ring.drop_to_ground(k - 1)
  1197. qdomain = qdomain.clone(domain=qdomain.domain.ring.to_field())
  1198. qring = ring.clone(domain=qdomain) # = Z(t_k)[t_1, ..., t_{k-1}][x, z]
  1199. n = 1
  1200. d = 1
  1201. # polynomial in Z_p[t_1, ..., t_k][z]
  1202. gamma = ring.dmp_LC(f) * ring.dmp_LC(g)
  1203. # polynomial in Z_p[t_1, ..., t_k]
  1204. delta = minpoly.LC
  1205. evalpoints = []
  1206. heval = []
  1207. LMlist = []
  1208. points = list(range(p))
  1209. while points:
  1210. a = random.sample(points, 1)[0]
  1211. points.remove(a)
  1212. if k == 1:
  1213. test = delta.evaluate(k-1, a) % p == 0
  1214. else:
  1215. test = delta.evaluate(k-1, a).trunc_ground(p) == 0
  1216. if test:
  1217. continue
  1218. gammaa = _evaluate_ground(gamma, k-1, a)
  1219. minpolya = _evaluate_ground(minpoly, k-1, a)
  1220. if gammaa.rem([minpolya, gammaa.ring(p)]) == 0:
  1221. continue
  1222. fa = _evaluate_ground(f, k-1, a)
  1223. ga = _evaluate_ground(g, k-1, a)
  1224. # polynomial in Z_p[x, t_1, ..., t_{k-1}, z]/(minpoly)
  1225. ha = _func_field_modgcd_p(fa, ga, minpolya, p)
  1226. if ha is None:
  1227. d += 1
  1228. if d > n:
  1229. return None
  1230. continue
  1231. if ha == 1:
  1232. return ha
  1233. LM = [ha.degree()] + [0]*(k-1)
  1234. if k > 1:
  1235. for monom, coeff in ha.iterterms():
  1236. if monom[0] == LM[0] and coeff.LM > tuple(LM[1:]):
  1237. LM[1:] = coeff.LM
  1238. evalpoints_a = [a]
  1239. heval_a = [ha]
  1240. if k == 1:
  1241. m = qring.domain.get_ring().one
  1242. else:
  1243. m = qring.domain.domain.get_ring().one
  1244. t = m.ring.gens[0]
  1245. for b, hb, LMhb in zip(evalpoints, heval, LMlist):
  1246. if LMhb == LM:
  1247. evalpoints_a.append(b)
  1248. heval_a.append(hb)
  1249. m *= (t - b)
  1250. m = m.trunc_ground(p)
  1251. evalpoints.append(a)
  1252. heval.append(ha)
  1253. LMlist.append(LM)
  1254. n += 1
  1255. # polynomial in Z_p[t_1, ..., t_k][x, z]
  1256. h = _interpolate_multivariate(evalpoints_a, heval_a, ring, k-1, p, ground=True)
  1257. # polynomial in Z_p(t_k)[t_1, ..., t_{k-1}][x, z]
  1258. h = _rational_reconstruction_func_coeffs(h, p, m, qring, k-1)
  1259. if h is None:
  1260. continue
  1261. if k == 1:
  1262. dom = qring.domain.field
  1263. den = dom.ring.one
  1264. for coeff in h.itercoeffs():
  1265. den = dom.ring.from_dense(gf_lcm(den.to_dense(), coeff.denom.to_dense(),
  1266. p, dom.domain))
  1267. else:
  1268. dom = qring.domain.domain.field
  1269. den = dom.ring.one
  1270. for coeff in h.itercoeffs():
  1271. for c in coeff.itercoeffs():
  1272. den = dom.ring.from_dense(gf_lcm(den.to_dense(), c.denom.to_dense(),
  1273. p, dom.domain))
  1274. den = qring.domain_new(den.trunc_ground(p))
  1275. h = ring(h.mul_ground(den).as_expr()).trunc_ground(p)
  1276. if not _trial_division(f, h, minpoly, p) and not _trial_division(g, h, minpoly, p):
  1277. return h
  1278. return None
  1279. def _integer_rational_reconstruction(c, m, domain):
  1280. r"""
  1281. Reconstruct a rational number `\frac a b` from
  1282. .. math::
  1283. c = \frac a b \; \mathrm{mod} \, m,
  1284. where `c` and `m` are integers.
  1285. The algorithm is based on the Euclidean Algorithm. In general, `m` is
  1286. not a prime number, so it is possible that `b` is not invertible modulo
  1287. `m`. In that case ``None`` is returned.
  1288. Parameters
  1289. ==========
  1290. c : Integer
  1291. `c = \frac a b \; \mathrm{mod} \, m`
  1292. m : Integer
  1293. modulus, not necessarily prime
  1294. domain : IntegerRing
  1295. `a, b, c` are elements of ``domain``
  1296. Returns
  1297. =======
  1298. frac : Rational
  1299. either `\frac a b` in `\mathbb Q` or ``None``
  1300. References
  1301. ==========
  1302. 1. [Wang81]_
  1303. """
  1304. if c < 0:
  1305. c += m
  1306. r0, s0 = m, domain.zero
  1307. r1, s1 = c, domain.one
  1308. bound = sqrt(m / 2) # still correct if replaced by ZZ.sqrt(m // 2) ?
  1309. while int(r1) >= bound:
  1310. quo = r0 // r1
  1311. r0, r1 = r1, r0 - quo*r1
  1312. s0, s1 = s1, s0 - quo*s1
  1313. if abs(int(s1)) >= bound:
  1314. return None
  1315. if s1 < 0:
  1316. a, b = -r1, -s1
  1317. elif s1 > 0:
  1318. a, b = r1, s1
  1319. else:
  1320. return None
  1321. field = domain.get_field()
  1322. return field(a) / field(b)
  1323. def _rational_reconstruction_int_coeffs(hm, m, ring):
  1324. r"""
  1325. Reconstruct every rational coefficient `c_h` of a polynomial `h` in
  1326. `\mathbb Q[t_1, \ldots, t_k][x, z]` from the corresponding integer
  1327. coefficient `c_{h_m}` of a polynomial `h_m` in
  1328. `\mathbb Z[t_1, \ldots, t_k][x, z]` such that
  1329. .. math::
  1330. c_{h_m} = c_h \; \mathrm{mod} \, m,
  1331. where `m \in \mathbb Z`.
  1332. The reconstruction is based on the Euclidean Algorithm. In general,
  1333. `m` is not a prime number, so it is possible that this fails for some
  1334. coefficient. In that case ``None`` is returned.
  1335. Parameters
  1336. ==========
  1337. hm : PolyElement
  1338. polynomial in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1339. m : Integer
  1340. modulus, not necessarily prime
  1341. ring : PolyRing
  1342. `\mathbb Q[t_1, \ldots, t_k][x, z]`, `h` will be an element of this
  1343. ring
  1344. Returns
  1345. =======
  1346. h : PolyElement
  1347. reconstructed polynomial in `\mathbb Q[t_1, \ldots, t_k][x, z]` or
  1348. ``None``
  1349. See also
  1350. ========
  1351. _integer_rational_reconstruction
  1352. """
  1353. h = ring.zero
  1354. if isinstance(ring.domain, PolynomialRing):
  1355. reconstruction = _rational_reconstruction_int_coeffs
  1356. domain = ring.domain.ring
  1357. else:
  1358. reconstruction = _integer_rational_reconstruction
  1359. domain = hm.ring.domain
  1360. for monom, coeff in hm.iterterms():
  1361. coeffh = reconstruction(coeff, m, domain)
  1362. if not coeffh:
  1363. return None
  1364. h[monom] = coeffh
  1365. return h
  1366. def _func_field_modgcd_m(f, g, minpoly):
  1367. r"""
  1368. Compute the GCD of two polynomials in
  1369. `\mathbb Q(t_1, \ldots, t_k)[z]/(m_{\alpha}(z))[x]` using a modular
  1370. algorithm.
  1371. The algorithm computes the GCD of two polynomials `f` and `g` by
  1372. calculating the GCD in
  1373. `\mathbb Z_p(t_1, \ldots, t_k)[z] / (\check m_{\alpha}(z))[x]` for
  1374. suitable primes `p` and the primitive associate `\check m_{\alpha}(z)`
  1375. of `m_{\alpha}(z)`. Then the coefficients are reconstructed with the
  1376. Chinese Remainder Theorem and Rational Reconstruction. To compute the
  1377. GCD over `\mathbb Z_p(t_1, \ldots, t_k)[z] / (\check m_{\alpha})[x]`,
  1378. the recursive subroutine ``_func_field_modgcd_p`` is used. To verify the
  1379. result in `\mathbb Q(t_1, \ldots, t_k)[z] / (m_{\alpha}(z))[x]`, a
  1380. fraction free trial division is used.
  1381. Parameters
  1382. ==========
  1383. f, g : PolyElement
  1384. polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]`
  1385. minpoly : PolyElement
  1386. irreducible polynomial in `\mathbb Z[t_1, \ldots, t_k][z]`
  1387. Returns
  1388. =======
  1389. h : PolyElement
  1390. the primitive associate in `\mathbb Z[t_1, \ldots, t_k][x, z]` of
  1391. the GCD of `f` and `g`
  1392. Examples
  1393. ========
  1394. >>> from sympy.polys.modulargcd import _func_field_modgcd_m
  1395. >>> from sympy.polys import ring, ZZ
  1396. >>> R, x, z = ring('x, z', ZZ)
  1397. >>> minpoly = (z**2 - 2).drop(0)
  1398. >>> f = x**2 + 2*x*z + 2
  1399. >>> g = x + z
  1400. >>> _func_field_modgcd_m(f, g, minpoly)
  1401. x + z
  1402. >>> D, t = ring('t', ZZ)
  1403. >>> R, x, z = ring('x, z', D)
  1404. >>> minpoly = (z**2-3).drop(0)
  1405. >>> f = x**2 + (t + 1)*x*z + 3*t
  1406. >>> g = x*z + 3*t
  1407. >>> _func_field_modgcd_m(f, g, minpoly)
  1408. x + t*z
  1409. References
  1410. ==========
  1411. 1. [Hoeij04]_
  1412. See also
  1413. ========
  1414. _func_field_modgcd_p
  1415. """
  1416. ring = f.ring
  1417. domain = ring.domain
  1418. if isinstance(domain, PolynomialRing):
  1419. k = domain.ngens
  1420. QQdomain = domain.ring.clone(domain=domain.domain.get_field())
  1421. QQring = ring.clone(domain=QQdomain)
  1422. else:
  1423. k = 0
  1424. QQring = ring.clone(domain=ring.domain.get_field())
  1425. cf, f = f.primitive()
  1426. cg, g = g.primitive()
  1427. # polynomial in Z[t_1, ..., t_k][z]
  1428. gamma = ring.dmp_LC(f) * ring.dmp_LC(g)
  1429. # polynomial in Z[t_1, ..., t_k]
  1430. delta = minpoly.LC
  1431. p = 1
  1432. primes = []
  1433. hplist = []
  1434. LMlist = []
  1435. while True:
  1436. p = nextprime(p)
  1437. if gamma.trunc_ground(p) == 0:
  1438. continue
  1439. if k == 0:
  1440. test = (delta % p == 0)
  1441. else:
  1442. test = (delta.trunc_ground(p) == 0)
  1443. if test:
  1444. continue
  1445. fp = f.trunc_ground(p)
  1446. gp = g.trunc_ground(p)
  1447. minpolyp = minpoly.trunc_ground(p)
  1448. hp = _func_field_modgcd_p(fp, gp, minpolyp, p)
  1449. if hp is None:
  1450. continue
  1451. if hp == 1:
  1452. return ring.one
  1453. LM = [hp.degree()] + [0]*k
  1454. if k > 0:
  1455. for monom, coeff in hp.iterterms():
  1456. if monom[0] == LM[0] and coeff.LM > tuple(LM[1:]):
  1457. LM[1:] = coeff.LM
  1458. hm = hp
  1459. m = p
  1460. for q, hq, LMhq in zip(primes, hplist, LMlist):
  1461. if LMhq == LM:
  1462. hm = _chinese_remainder_reconstruction_multivariate(hq, hm, q, m)
  1463. m *= q
  1464. primes.append(p)
  1465. hplist.append(hp)
  1466. LMlist.append(LM)
  1467. hm = _rational_reconstruction_int_coeffs(hm, m, QQring)
  1468. if hm is None:
  1469. continue
  1470. if k == 0:
  1471. h = hm.clear_denoms()[1]
  1472. else:
  1473. den = domain.domain.one
  1474. for coeff in hm.itercoeffs():
  1475. den = domain.domain.lcm(den, coeff.clear_denoms()[0])
  1476. h = hm.mul_ground(den)
  1477. # convert back to Z[t_1, ..., t_k][x, z] from Q[t_1, ..., t_k][x, z]
  1478. h = h.set_ring(ring)
  1479. h = h.primitive()[1]
  1480. if not (_trial_division(f.mul_ground(cf), h, minpoly) or
  1481. _trial_division(g.mul_ground(cg), h, minpoly)):
  1482. return h
  1483. def _to_ZZ_poly(f, ring):
  1484. r"""
  1485. Compute an associate of a polynomial
  1486. `f \in \mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` in
  1487. `\mathbb Z[x_1, \ldots, x_{n-1}][z] / (\check m_{\alpha}(z))[x_0]`,
  1488. where `\check m_{\alpha}(z) \in \mathbb Z[z]` is the primitive associate
  1489. of the minimal polynomial `m_{\alpha}(z)` of `\alpha` over
  1490. `\mathbb Q`.
  1491. Parameters
  1492. ==========
  1493. f : PolyElement
  1494. polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1495. ring : PolyRing
  1496. `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]`
  1497. Returns
  1498. =======
  1499. f_ : PolyElement
  1500. associate of `f` in
  1501. `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]`
  1502. """
  1503. f_ = ring.zero
  1504. if isinstance(ring.domain, PolynomialRing):
  1505. domain = ring.domain.domain
  1506. else:
  1507. domain = ring.domain
  1508. den = domain.one
  1509. for coeff in f.itercoeffs():
  1510. for c in coeff.to_list():
  1511. if c:
  1512. den = domain.lcm(den, c.denominator)
  1513. for monom, coeff in f.iterterms():
  1514. coeff = coeff.to_list()
  1515. m = ring.domain.one
  1516. if isinstance(ring.domain, PolynomialRing):
  1517. m = m.mul_monom(monom[1:])
  1518. n = len(coeff)
  1519. for i in range(n):
  1520. if coeff[i]:
  1521. c = domain.convert(coeff[i] * den) * m
  1522. if (monom[0], n-i-1) not in f_:
  1523. f_[(monom[0], n-i-1)] = c
  1524. else:
  1525. f_[(monom[0], n-i-1)] += c
  1526. return f_
  1527. def _to_ANP_poly(f, ring):
  1528. r"""
  1529. Convert a polynomial
  1530. `f \in \mathbb Z[x_1, \ldots, x_{n-1}][z]/(\check m_{\alpha}(z))[x_0]`
  1531. to a polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`,
  1532. where `\check m_{\alpha}(z) \in \mathbb Z[z]` is the primitive associate
  1533. of the minimal polynomial `m_{\alpha}(z)` of `\alpha` over
  1534. `\mathbb Q`.
  1535. Parameters
  1536. ==========
  1537. f : PolyElement
  1538. polynomial in `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]`
  1539. ring : PolyRing
  1540. `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1541. Returns
  1542. =======
  1543. f_ : PolyElement
  1544. polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1545. """
  1546. domain = ring.domain
  1547. f_ = ring.zero
  1548. if isinstance(f.ring.domain, PolynomialRing):
  1549. for monom, coeff in f.iterterms():
  1550. for mon, coef in coeff.iterterms():
  1551. m = (monom[0],) + mon
  1552. c = domain([domain.domain(coef)] + [0]*monom[1])
  1553. if m not in f_:
  1554. f_[m] = c
  1555. else:
  1556. f_[m] += c
  1557. else:
  1558. for monom, coeff in f.iterterms():
  1559. m = (monom[0],)
  1560. c = domain([domain.domain(coeff)] + [0]*monom[1])
  1561. if m not in f_:
  1562. f_[m] = c
  1563. else:
  1564. f_[m] += c
  1565. return f_
  1566. def _minpoly_from_dense(minpoly, ring):
  1567. r"""
  1568. Change representation of the minimal polynomial from ``DMP`` to
  1569. ``PolyElement`` for a given ring.
  1570. """
  1571. minpoly_ = ring.zero
  1572. for monom, coeff in minpoly.terms():
  1573. minpoly_[monom] = ring.domain(coeff)
  1574. return minpoly_
  1575. def _primitive_in_x0(f):
  1576. r"""
  1577. Compute the content in `x_0` and the primitive part of a polynomial `f`
  1578. in
  1579. `\mathbb Q(\alpha)[x_0, x_1, \ldots, x_{n-1}] \cong \mathbb Q(\alpha)[x_1, \ldots, x_{n-1}][x_0]`.
  1580. """
  1581. fring = f.ring
  1582. ring = fring.drop_to_ground(*range(1, fring.ngens))
  1583. dom = ring.domain.ring
  1584. f_ = ring(f.as_expr())
  1585. cont = dom.zero
  1586. for coeff in f_.itercoeffs():
  1587. cont = func_field_modgcd(cont, coeff)[0]
  1588. if cont == dom.one:
  1589. return cont, f
  1590. return cont, f.quo(cont.set_ring(fring))
  1591. # TODO: add support for algebraic function fields
  1592. def func_field_modgcd(f, g):
  1593. r"""
  1594. Compute the GCD of two polynomials `f` and `g` in
  1595. `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` using a modular algorithm.
  1596. The algorithm first computes the primitive associate
  1597. `\check m_{\alpha}(z)` of the minimal polynomial `m_{\alpha}` in
  1598. `\mathbb{Z}[z]` and the primitive associates of `f` and `g` in
  1599. `\mathbb{Z}[x_1, \ldots, x_{n-1}][z]/(\check m_{\alpha})[x_0]`. Then it
  1600. computes the GCD in
  1601. `\mathbb Q(x_1, \ldots, x_{n-1})[z]/(m_{\alpha}(z))[x_0]`.
  1602. This is done by calculating the GCD in
  1603. `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]` for
  1604. suitable primes `p` and then reconstructing the coefficients with the
  1605. Chinese Remainder Theorem and Rational Reconstruction. The GCD over
  1606. `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]` is
  1607. computed with a recursive subroutine, which evaluates the polynomials at
  1608. `x_{n-1} = a` for suitable evaluation points `a \in \mathbb Z_p` and
  1609. then calls itself recursively until the ground domain does no longer
  1610. contain any parameters. For
  1611. `\mathbb{Z}_p[z]/(\check m_{\alpha}(z))[x_0]` the Euclidean Algorithm is
  1612. used. The results of those recursive calls are then interpolated and
  1613. Rational Function Reconstruction is used to obtain the correct
  1614. coefficients. The results, both in
  1615. `\mathbb Q(x_1, \ldots, x_{n-1})[z]/(m_{\alpha}(z))[x_0]` and
  1616. `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]`, are
  1617. verified by a fraction free trial division.
  1618. Apart from the above GCD computation some GCDs in
  1619. `\mathbb Q(\alpha)[x_1, \ldots, x_{n-1}]` have to be calculated,
  1620. because treating the polynomials as univariate ones can result in
  1621. a spurious content of the GCD. For this ``func_field_modgcd`` is
  1622. called recursively.
  1623. Parameters
  1624. ==========
  1625. f, g : PolyElement
  1626. polynomials in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`
  1627. Returns
  1628. =======
  1629. h : PolyElement
  1630. monic GCD of the polynomials `f` and `g`
  1631. cff : PolyElement
  1632. cofactor of `f`, i.e. `\frac f h`
  1633. cfg : PolyElement
  1634. cofactor of `g`, i.e. `\frac g h`
  1635. Examples
  1636. ========
  1637. >>> from sympy.polys.modulargcd import func_field_modgcd
  1638. >>> from sympy.polys import AlgebraicField, QQ, ring
  1639. >>> from sympy import sqrt
  1640. >>> A = AlgebraicField(QQ, sqrt(2))
  1641. >>> R, x = ring('x', A)
  1642. >>> f = x**2 - 2
  1643. >>> g = x + sqrt(2)
  1644. >>> h, cff, cfg = func_field_modgcd(f, g)
  1645. >>> h == x + sqrt(2)
  1646. True
  1647. >>> cff * h == f
  1648. True
  1649. >>> cfg * h == g
  1650. True
  1651. >>> R, x, y = ring('x, y', A)
  1652. >>> f = x**2 + 2*sqrt(2)*x*y + 2*y**2
  1653. >>> g = x + sqrt(2)*y
  1654. >>> h, cff, cfg = func_field_modgcd(f, g)
  1655. >>> h == x + sqrt(2)*y
  1656. True
  1657. >>> cff * h == f
  1658. True
  1659. >>> cfg * h == g
  1660. True
  1661. >>> f = x + sqrt(2)*y
  1662. >>> g = x + y
  1663. >>> h, cff, cfg = func_field_modgcd(f, g)
  1664. >>> h == R.one
  1665. True
  1666. >>> cff * h == f
  1667. True
  1668. >>> cfg * h == g
  1669. True
  1670. References
  1671. ==========
  1672. 1. [Hoeij04]_
  1673. """
  1674. ring = f.ring
  1675. domain = ring.domain
  1676. n = ring.ngens
  1677. assert ring == g.ring and domain.is_Algebraic
  1678. result = _trivial_gcd(f, g)
  1679. if result is not None:
  1680. return result
  1681. z = Dummy('z')
  1682. ZZring = ring.clone(symbols=ring.symbols + (z,), domain=domain.domain.get_ring())
  1683. if n == 1:
  1684. f_ = _to_ZZ_poly(f, ZZring)
  1685. g_ = _to_ZZ_poly(g, ZZring)
  1686. minpoly = ZZring.drop(0).from_dense(domain.mod.to_list())
  1687. h = _func_field_modgcd_m(f_, g_, minpoly)
  1688. h = _to_ANP_poly(h, ring)
  1689. else:
  1690. # contx0f in Q(a)[x_1, ..., x_{n-1}], f in Q(a)[x_0, ..., x_{n-1}]
  1691. contx0f, f = _primitive_in_x0(f)
  1692. contx0g, g = _primitive_in_x0(g)
  1693. contx0h = func_field_modgcd(contx0f, contx0g)[0]
  1694. ZZring_ = ZZring.drop_to_ground(*range(1, n))
  1695. f_ = _to_ZZ_poly(f, ZZring_)
  1696. g_ = _to_ZZ_poly(g, ZZring_)
  1697. minpoly = _minpoly_from_dense(domain.mod, ZZring_.drop(0))
  1698. h = _func_field_modgcd_m(f_, g_, minpoly)
  1699. h = _to_ANP_poly(h, ring)
  1700. contx0h_, h = _primitive_in_x0(h)
  1701. h *= contx0h.set_ring(ring)
  1702. f *= contx0f.set_ring(ring)
  1703. g *= contx0g.set_ring(ring)
  1704. h = h.quo_ground(h.LC)
  1705. return h, f.quo(h), g.quo(h)