subresultants_qq_zz.py 86 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558
  1. """
  2. This module contains functions for the computation
  3. of Euclidean, (generalized) Sturmian, (modified) subresultant
  4. polynomial remainder sequences (prs's) of two polynomials;
  5. included are also three functions for the computation of the
  6. resultant of two polynomials.
  7. Except for the function res_z(), which computes the resultant
  8. of two polynomials, the pseudo-remainder function prem()
  9. of sympy is _not_ used by any of the functions in the module.
  10. Instead of prem() we use the function
  11. rem_z().
  12. Included is also the function quo_z().
  13. An explanation of why we avoid prem() can be found in the
  14. references stated in the docstring of rem_z().
  15. 1. Theoretical background:
  16. ==========================
  17. Consider the polynomials f, g in Z[x] of degrees deg(f) = n and
  18. deg(g) = m with n >= m.
  19. Definition 1:
  20. =============
  21. The sign sequence of a polynomial remainder sequence (prs) is the
  22. sequence of signs of the leading coefficients of its polynomials.
  23. Sign sequences can be computed with the function:
  24. sign_seq(poly_seq, x)
  25. Definition 2:
  26. =============
  27. A polynomial remainder sequence (prs) is called complete if the
  28. degree difference between any two consecutive polynomials is 1;
  29. otherwise, it called incomplete.
  30. It is understood that f, g belong to the sequences mentioned in
  31. the two definitions above.
  32. 1A. Euclidean and subresultant prs's:
  33. =====================================
  34. The subresultant prs of f, g is a sequence of polynomials in Z[x]
  35. analogous to the Euclidean prs, the sequence obtained by applying
  36. on f, g Euclid's algorithm for polynomial greatest common divisors
  37. (gcd) in Q[x].
  38. The subresultant prs differs from the Euclidean prs in that the
  39. coefficients of each polynomial in the former sequence are determinants
  40. --- also referred to as subresultants --- of appropriately selected
  41. sub-matrices of sylvester1(f, g, x), Sylvester's matrix of 1840 of
  42. dimensions (n + m) * (n + m).
  43. Recall that the determinant of sylvester1(f, g, x) itself is
  44. called the resultant of f, g and serves as a criterion of whether
  45. the two polynomials have common roots or not.
  46. In SymPy the resultant is computed with the function
  47. resultant(f, g, x). This function does _not_ evaluate the
  48. determinant of sylvester(f, g, x, 1); instead, it returns
  49. the last member of the subresultant prs of f, g, multiplied
  50. (if needed) by an appropriate power of -1; see the caveat below.
  51. In this module we use three functions to compute the
  52. resultant of f, g:
  53. a) res(f, g, x) computes the resultant by evaluating
  54. the determinant of sylvester(f, g, x, 1);
  55. b) res_q(f, g, x) computes the resultant recursively, by
  56. performing polynomial divisions in Q[x] with the function rem();
  57. c) res_z(f, g, x) computes the resultant recursively, by
  58. performing polynomial divisions in Z[x] with the function prem().
  59. Caveat: If Df = degree(f, x) and Dg = degree(g, x), then:
  60. resultant(f, g, x) = (-1)**(Df*Dg) * resultant(g, f, x).
  61. For complete prs's the sign sequence of the Euclidean prs of f, g
  62. is identical to the sign sequence of the subresultant prs of f, g
  63. and the coefficients of one sequence are easily computed from the
  64. coefficients of the other.
  65. For incomplete prs's the polynomials in the subresultant prs, generally
  66. differ in sign from those of the Euclidean prs, and --- unlike the
  67. case of complete prs's --- it is not at all obvious how to compute
  68. the coefficients of one sequence from the coefficients of the other.
  69. 1B. Sturmian and modified subresultant prs's:
  70. =============================================
  71. For the same polynomials f, g in Z[x] mentioned above, their ``modified''
  72. subresultant prs is a sequence of polynomials similar to the Sturmian
  73. prs, the sequence obtained by applying in Q[x] Sturm's algorithm on f, g.
  74. The two sequences differ in that the coefficients of each polynomial
  75. in the modified subresultant prs are the determinants --- also referred
  76. to as modified subresultants --- of appropriately selected sub-matrices
  77. of sylvester2(f, g, x), Sylvester's matrix of 1853 of dimensions 2n x 2n.
  78. The determinant of sylvester2 itself is called the modified resultant
  79. of f, g and it also can serve as a criterion of whether the two
  80. polynomials have common roots or not.
  81. For complete prs's the sign sequence of the Sturmian prs of f, g is
  82. identical to the sign sequence of the modified subresultant prs of
  83. f, g and the coefficients of one sequence are easily computed from
  84. the coefficients of the other.
  85. For incomplete prs's the polynomials in the modified subresultant prs,
  86. generally differ in sign from those of the Sturmian prs, and --- unlike
  87. the case of complete prs's --- it is not at all obvious how to compute
  88. the coefficients of one sequence from the coefficients of the other.
  89. As Sylvester pointed out, the coefficients of the polynomial remainders
  90. obtained as (modified) subresultants are the smallest possible without
  91. introducing rationals and without computing (integer) greatest common
  92. divisors.
  93. 1C. On terminology:
  94. ===================
  95. Whence the terminology? Well generalized Sturmian prs's are
  96. ``modifications'' of Euclidean prs's; the hint came from the title
  97. of the Pell-Gordon paper of 1917.
  98. In the literature one also encounters the name ``non signed'' and
  99. ``signed'' prs for Euclidean and Sturmian prs respectively.
  100. Likewise ``non signed'' and ``signed'' subresultant prs for
  101. subresultant and modified subresultant prs respectively.
  102. 2. Functions in the module:
  103. ===========================
  104. No function utilizes SymPy's function prem().
  105. 2A. Matrices:
  106. =============
  107. The functions sylvester(f, g, x, method=1) and
  108. sylvester(f, g, x, method=2) compute either Sylvester matrix.
  109. They can be used to compute (modified) subresultant prs's by
  110. direct determinant evaluation.
  111. The function bezout(f, g, x, method='prs') provides a matrix of
  112. smaller dimensions than either Sylvester matrix. It is the function
  113. of choice for computing (modified) subresultant prs's by direct
  114. determinant evaluation.
  115. sylvester(f, g, x, method=1)
  116. sylvester(f, g, x, method=2)
  117. bezout(f, g, x, method='prs')
  118. The following identity holds:
  119. bezout(f, g, x, method='prs') =
  120. backward_eye(deg(f))*bezout(f, g, x, method='bz')*backward_eye(deg(f))
  121. 2B. Subresultant and modified subresultant prs's by
  122. ===================================================
  123. determinant evaluations:
  124. =======================
  125. We use the Sylvester matrices of 1840 and 1853 to
  126. compute, respectively, subresultant and modified
  127. subresultant polynomial remainder sequences. However,
  128. for large matrices this approach takes a lot of time.
  129. Instead of utilizing the Sylvester matrices, we can
  130. employ the Bezout matrix which is of smaller dimensions.
  131. subresultants_sylv(f, g, x)
  132. modified_subresultants_sylv(f, g, x)
  133. subresultants_bezout(f, g, x)
  134. modified_subresultants_bezout(f, g, x)
  135. 2C. Subresultant prs's by ONE determinant evaluation:
  136. =====================================================
  137. All three functions in this section evaluate one determinant
  138. per remainder polynomial; this is the determinant of an
  139. appropriately selected sub-matrix of sylvester1(f, g, x),
  140. Sylvester's matrix of 1840.
  141. To compute the remainder polynomials the function
  142. subresultants_rem(f, g, x) employs rem(f, g, x).
  143. By contrast, the other two functions implement Van Vleck's ideas
  144. of 1900 and compute the remainder polynomials by trinagularizing
  145. sylvester2(f, g, x), Sylvester's matrix of 1853.
  146. subresultants_rem(f, g, x)
  147. subresultants_vv(f, g, x)
  148. subresultants_vv_2(f, g, x).
  149. 2E. Euclidean, Sturmian prs's in Q[x]:
  150. ======================================
  151. euclid_q(f, g, x)
  152. sturm_q(f, g, x)
  153. 2F. Euclidean, Sturmian and (modified) subresultant prs's P-G:
  154. ==============================================================
  155. All functions in this section are based on the Pell-Gordon (P-G)
  156. theorem of 1917.
  157. Computations are done in Q[x], employing the function rem(f, g, x)
  158. for the computation of the remainder polynomials.
  159. euclid_pg(f, g, x)
  160. sturm pg(f, g, x)
  161. subresultants_pg(f, g, x)
  162. modified_subresultants_pg(f, g, x)
  163. 2G. Euclidean, Sturmian and (modified) subresultant prs's A-M-V:
  164. ================================================================
  165. All functions in this section are based on the Akritas-Malaschonok-
  166. Vigklas (A-M-V) theorem of 2015.
  167. Computations are done in Z[x], employing the function rem_z(f, g, x)
  168. for the computation of the remainder polynomials.
  169. euclid_amv(f, g, x)
  170. sturm_amv(f, g, x)
  171. subresultants_amv(f, g, x)
  172. modified_subresultants_amv(f, g, x)
  173. 2Ga. Exception:
  174. ===============
  175. subresultants_amv_q(f, g, x)
  176. This function employs rem(f, g, x) for the computation of
  177. the remainder polynomials, despite the fact that it implements
  178. the A-M-V Theorem.
  179. It is included in our module in order to show that theorems P-G
  180. and A-M-V can be implemented utilizing either the function
  181. rem(f, g, x) or the function rem_z(f, g, x).
  182. For clearly historical reasons --- since the Collins-Brown-Traub
  183. coefficients-reduction factor beta_i was not available in 1917 ---
  184. we have implemented the Pell-Gordon theorem with the function
  185. rem(f, g, x) and the A-M-V Theorem with the function rem_z(f, g, x).
  186. 2H. Resultants:
  187. ===============
  188. res(f, g, x)
  189. res_q(f, g, x)
  190. res_z(f, g, x)
  191. """
  192. from sympy.concrete.summations import summation
  193. from sympy.core.function import expand
  194. from sympy.core.numbers import nan
  195. from sympy.core.singleton import S
  196. from sympy.core.symbol import Dummy as var
  197. from sympy.functions.elementary.complexes import Abs, sign
  198. from sympy.functions.elementary.integers import floor
  199. from sympy.matrices.dense import eye, Matrix, zeros
  200. from sympy.printing.pretty.pretty import pretty_print as pprint
  201. from sympy.simplify.simplify import simplify
  202. from sympy.polys.domains import QQ
  203. from sympy.polys.polytools import degree, LC, Poly, pquo, quo, prem, rem
  204. from sympy.polys.polyerrors import PolynomialError
  205. def sylvester(f, g, x, method = 1):
  206. '''
  207. The input polynomials f, g are in Z[x] or in Q[x]. Let m = degree(f, x),
  208. n = degree(g, x) and mx = max(m, n).
  209. a. If method = 1 (default), computes sylvester1, Sylvester's matrix of 1840
  210. of dimension (m + n) x (m + n). The determinants of properly chosen
  211. submatrices of this matrix (a.k.a. subresultants) can be
  212. used to compute the coefficients of the Euclidean PRS of f, g.
  213. b. If method = 2, computes sylvester2, Sylvester's matrix of 1853
  214. of dimension (2*mx) x (2*mx). The determinants of properly chosen
  215. submatrices of this matrix (a.k.a. ``modified'' subresultants) can be
  216. used to compute the coefficients of the Sturmian PRS of f, g.
  217. Applications of these Matrices can be found in the references below.
  218. Especially, for applications of sylvester2, see the first reference!!
  219. References
  220. ==========
  221. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On a Theorem
  222. by Van Vleck Regarding Sturm Sequences. Serdica Journal of Computing,
  223. Vol. 7, No 4, 101-134, 2013.
  224. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  225. and Modified Subresultant Polynomial Remainder Sequences.''
  226. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  227. '''
  228. # obtain degrees of polys
  229. m, n = degree( Poly(f, x), x), degree( Poly(g, x), x)
  230. # Special cases:
  231. # A:: case m = n < 0 (i.e. both polys are 0)
  232. if m == n and n < 0:
  233. return Matrix([])
  234. # B:: case m = n = 0 (i.e. both polys are constants)
  235. if m == n and n == 0:
  236. return Matrix([])
  237. # C:: m == 0 and n < 0 or m < 0 and n == 0
  238. # (i.e. one poly is constant and the other is 0)
  239. if m == 0 and n < 0:
  240. return Matrix([])
  241. elif m < 0 and n == 0:
  242. return Matrix([])
  243. # D:: m >= 1 and n < 0 or m < 0 and n >=1
  244. # (i.e. one poly is of degree >=1 and the other is 0)
  245. if m >= 1 and n < 0:
  246. return Matrix([0])
  247. elif m < 0 and n >= 1:
  248. return Matrix([0])
  249. fp = Poly(f, x).all_coeffs()
  250. gp = Poly(g, x).all_coeffs()
  251. # Sylvester's matrix of 1840 (default; a.k.a. sylvester1)
  252. if method <= 1:
  253. M = zeros(m + n)
  254. k = 0
  255. for i in range(n):
  256. j = k
  257. for coeff in fp:
  258. M[i, j] = coeff
  259. j = j + 1
  260. k = k + 1
  261. k = 0
  262. for i in range(n, m + n):
  263. j = k
  264. for coeff in gp:
  265. M[i, j] = coeff
  266. j = j + 1
  267. k = k + 1
  268. return M
  269. # Sylvester's matrix of 1853 (a.k.a sylvester2)
  270. if method >= 2:
  271. if len(fp) < len(gp):
  272. h = []
  273. for i in range(len(gp) - len(fp)):
  274. h.append(0)
  275. fp[ : 0] = h
  276. else:
  277. h = []
  278. for i in range(len(fp) - len(gp)):
  279. h.append(0)
  280. gp[ : 0] = h
  281. mx = max(m, n)
  282. dim = 2*mx
  283. M = zeros( dim )
  284. k = 0
  285. for i in range( mx ):
  286. j = k
  287. for coeff in fp:
  288. M[2*i, j] = coeff
  289. j = j + 1
  290. j = k
  291. for coeff in gp:
  292. M[2*i + 1, j] = coeff
  293. j = j + 1
  294. k = k + 1
  295. return M
  296. def process_matrix_output(poly_seq, x):
  297. """
  298. poly_seq is a polynomial remainder sequence computed either by
  299. (modified_)subresultants_bezout or by (modified_)subresultants_sylv.
  300. This function removes from poly_seq all zero polynomials as well
  301. as all those whose degree is equal to the degree of a preceding
  302. polynomial in poly_seq, as we scan it from left to right.
  303. """
  304. L = poly_seq[:] # get a copy of the input sequence
  305. d = degree(L[1], x)
  306. i = 2
  307. while i < len(L):
  308. d_i = degree(L[i], x)
  309. if d_i < 0: # zero poly
  310. L.remove(L[i])
  311. i = i - 1
  312. if d == d_i: # poly degree equals degree of previous poly
  313. L.remove(L[i])
  314. i = i - 1
  315. if d_i >= 0:
  316. d = d_i
  317. i = i + 1
  318. return L
  319. def subresultants_sylv(f, g, x):
  320. """
  321. The input polynomials f, g are in Z[x] or in Q[x]. It is assumed
  322. that deg(f) >= deg(g).
  323. Computes the subresultant polynomial remainder sequence (prs)
  324. of f, g by evaluating determinants of appropriately selected
  325. submatrices of sylvester(f, g, x, 1). The dimensions of the
  326. latter are (deg(f) + deg(g)) x (deg(f) + deg(g)).
  327. Each coefficient is computed by evaluating the determinant of the
  328. corresponding submatrix of sylvester(f, g, x, 1).
  329. If the subresultant prs is complete, then the output coincides
  330. with the Euclidean sequence of the polynomials f, g.
  331. References:
  332. ===========
  333. 1. G.M.Diaz-Toca,L.Gonzalez-Vega: Various New Expressions for Subresultants
  334. and Their Applications. Appl. Algebra in Engin., Communic. and Comp.,
  335. Vol. 15, 233-266, 2004.
  336. """
  337. # make sure neither f nor g is 0
  338. if f == 0 or g == 0:
  339. return [f, g]
  340. n = degF = degree(f, x)
  341. m = degG = degree(g, x)
  342. # make sure proper degrees
  343. if n == 0 and m == 0:
  344. return [f, g]
  345. if n < m:
  346. n, m, degF, degG, f, g = m, n, degG, degF, g, f
  347. if n > 0 and m == 0:
  348. return [f, g]
  349. SR_L = [f, g] # subresultant list
  350. # form matrix sylvester(f, g, x, 1)
  351. S = sylvester(f, g, x, 1)
  352. # pick appropriate submatrices of S
  353. # and form subresultant polys
  354. j = m - 1
  355. while j > 0:
  356. Sp = S[:, :] # copy of S
  357. # delete last j rows of coeffs of g
  358. for ind in range(m + n - j, m + n):
  359. Sp.row_del(m + n - j)
  360. # delete last j rows of coeffs of f
  361. for ind in range(m - j, m):
  362. Sp.row_del(m - j)
  363. # evaluate determinants and form coefficients list
  364. coeff_L, k, l = [], Sp.rows, 0
  365. while l <= j:
  366. coeff_L.append(Sp[:, 0:k].det())
  367. Sp.col_swap(k - 1, k + l)
  368. l += 1
  369. # form poly and append to SP_L
  370. SR_L.append(Poly(coeff_L, x).as_expr())
  371. j -= 1
  372. # j = 0
  373. SR_L.append(S.det())
  374. return process_matrix_output(SR_L, x)
  375. def modified_subresultants_sylv(f, g, x):
  376. """
  377. The input polynomials f, g are in Z[x] or in Q[x]. It is assumed
  378. that deg(f) >= deg(g).
  379. Computes the modified subresultant polynomial remainder sequence (prs)
  380. of f, g by evaluating determinants of appropriately selected
  381. submatrices of sylvester(f, g, x, 2). The dimensions of the
  382. latter are (2*deg(f)) x (2*deg(f)).
  383. Each coefficient is computed by evaluating the determinant of the
  384. corresponding submatrix of sylvester(f, g, x, 2).
  385. If the modified subresultant prs is complete, then the output coincides
  386. with the Sturmian sequence of the polynomials f, g.
  387. References:
  388. ===========
  389. 1. A. G. Akritas,G.I. Malaschonok and P.S. Vigklas:
  390. Sturm Sequences and Modified Subresultant Polynomial Remainder
  391. Sequences. Serdica Journal of Computing, Vol. 8, No 1, 29--46, 2014.
  392. """
  393. # make sure neither f nor g is 0
  394. if f == 0 or g == 0:
  395. return [f, g]
  396. n = degF = degree(f, x)
  397. m = degG = degree(g, x)
  398. # make sure proper degrees
  399. if n == 0 and m == 0:
  400. return [f, g]
  401. if n < m:
  402. n, m, degF, degG, f, g = m, n, degG, degF, g, f
  403. if n > 0 and m == 0:
  404. return [f, g]
  405. SR_L = [f, g] # modified subresultant list
  406. # form matrix sylvester(f, g, x, 2)
  407. S = sylvester(f, g, x, 2)
  408. # pick appropriate submatrices of S
  409. # and form modified subresultant polys
  410. j = m - 1
  411. while j > 0:
  412. # delete last 2*j rows of pairs of coeffs of f, g
  413. Sp = S[0:2*n - 2*j, :] # copy of first 2*n - 2*j rows of S
  414. # evaluate determinants and form coefficients list
  415. coeff_L, k, l = [], Sp.rows, 0
  416. while l <= j:
  417. coeff_L.append(Sp[:, 0:k].det())
  418. Sp.col_swap(k - 1, k + l)
  419. l += 1
  420. # form poly and append to SP_L
  421. SR_L.append(Poly(coeff_L, x).as_expr())
  422. j -= 1
  423. # j = 0
  424. SR_L.append(S.det())
  425. return process_matrix_output(SR_L, x)
  426. def res(f, g, x):
  427. """
  428. The input polynomials f, g are in Z[x] or in Q[x].
  429. The output is the resultant of f, g computed by evaluating
  430. the determinant of the matrix sylvester(f, g, x, 1).
  431. References:
  432. ===========
  433. 1. J. S. Cohen: Computer Algebra and Symbolic Computation
  434. - Mathematical Methods. A. K. Peters, 2003.
  435. """
  436. if f == 0 or g == 0:
  437. raise PolynomialError("The resultant of %s and %s is not defined" % (f, g))
  438. else:
  439. return sylvester(f, g, x, 1).det()
  440. def res_q(f, g, x):
  441. """
  442. The input polynomials f, g are in Z[x] or in Q[x].
  443. The output is the resultant of f, g computed recursively
  444. by polynomial divisions in Q[x], using the function rem.
  445. See Cohen's book p. 281.
  446. References:
  447. ===========
  448. 1. J. S. Cohen: Computer Algebra and Symbolic Computation
  449. - Mathematical Methods. A. K. Peters, 2003.
  450. """
  451. m = degree(f, x)
  452. n = degree(g, x)
  453. if m < n:
  454. return (-1)**(m*n) * res_q(g, f, x)
  455. elif n == 0: # g is a constant
  456. return g**m
  457. else:
  458. r = rem(f, g, x)
  459. if r == 0:
  460. return 0
  461. else:
  462. s = degree(r, x)
  463. l = LC(g, x)
  464. return (-1)**(m*n) * l**(m-s)*res_q(g, r, x)
  465. def res_z(f, g, x):
  466. """
  467. The input polynomials f, g are in Z[x] or in Q[x].
  468. The output is the resultant of f, g computed recursively
  469. by polynomial divisions in Z[x], using the function prem().
  470. See Cohen's book p. 283.
  471. References:
  472. ===========
  473. 1. J. S. Cohen: Computer Algebra and Symbolic Computation
  474. - Mathematical Methods. A. K. Peters, 2003.
  475. """
  476. m = degree(f, x)
  477. n = degree(g, x)
  478. if m < n:
  479. return (-1)**(m*n) * res_z(g, f, x)
  480. elif n == 0: # g is a constant
  481. return g**m
  482. else:
  483. r = prem(f, g, x)
  484. if r == 0:
  485. return 0
  486. else:
  487. delta = m - n + 1
  488. w = (-1)**(m*n) * res_z(g, r, x)
  489. s = degree(r, x)
  490. l = LC(g, x)
  491. k = delta * n - m + s
  492. return quo(w, l**k, x)
  493. def sign_seq(poly_seq, x):
  494. """
  495. Given a sequence of polynomials poly_seq, it returns
  496. the sequence of signs of the leading coefficients of
  497. the polynomials in poly_seq.
  498. """
  499. return [sign(LC(poly_seq[i], x)) for i in range(len(poly_seq))]
  500. def bezout(p, q, x, method='bz'):
  501. """
  502. The input polynomials p, q are in Z[x] or in Q[x]. Let
  503. mx = max(degree(p, x), degree(q, x)).
  504. The default option bezout(p, q, x, method='bz') returns Bezout's
  505. symmetric matrix of p and q, of dimensions (mx) x (mx). The
  506. determinant of this matrix is equal to the determinant of sylvester2,
  507. Sylvester's matrix of 1853, whose dimensions are (2*mx) x (2*mx);
  508. however the subresultants of these two matrices may differ.
  509. The other option, bezout(p, q, x, 'prs'), is of interest to us
  510. in this module because it returns a matrix equivalent to sylvester2.
  511. In this case all subresultants of the two matrices are identical.
  512. Both the subresultant polynomial remainder sequence (prs) and
  513. the modified subresultant prs of p and q can be computed by
  514. evaluating determinants of appropriately selected submatrices of
  515. bezout(p, q, x, 'prs') --- one determinant per coefficient of the
  516. remainder polynomials.
  517. The matrices bezout(p, q, x, 'bz') and bezout(p, q, x, 'prs')
  518. are related by the formula
  519. bezout(p, q, x, 'prs') =
  520. backward_eye(deg(p)) * bezout(p, q, x, 'bz') * backward_eye(deg(p)),
  521. where backward_eye() is the backward identity function.
  522. References
  523. ==========
  524. 1. G.M.Diaz-Toca,L.Gonzalez-Vega: Various New Expressions for Subresultants
  525. and Their Applications. Appl. Algebra in Engin., Communic. and Comp.,
  526. Vol. 15, 233-266, 2004.
  527. """
  528. # obtain degrees of polys
  529. m, n = degree( Poly(p, x), x), degree( Poly(q, x), x)
  530. # Special cases:
  531. # A:: case m = n < 0 (i.e. both polys are 0)
  532. if m == n and n < 0:
  533. return Matrix([])
  534. # B:: case m = n = 0 (i.e. both polys are constants)
  535. if m == n and n == 0:
  536. return Matrix([])
  537. # C:: m == 0 and n < 0 or m < 0 and n == 0
  538. # (i.e. one poly is constant and the other is 0)
  539. if m == 0 and n < 0:
  540. return Matrix([])
  541. elif m < 0 and n == 0:
  542. return Matrix([])
  543. # D:: m >= 1 and n < 0 or m < 0 and n >=1
  544. # (i.e. one poly is of degree >=1 and the other is 0)
  545. if m >= 1 and n < 0:
  546. return Matrix([0])
  547. elif m < 0 and n >= 1:
  548. return Matrix([0])
  549. y = var('y')
  550. # expr is 0 when x = y
  551. expr = p * q.subs({x:y}) - p.subs({x:y}) * q
  552. # hence expr is exactly divisible by x - y
  553. poly = Poly( quo(expr, x-y), x, y)
  554. # form Bezout matrix and store them in B as indicated to get
  555. # the LC coefficient of each poly either in the first position
  556. # of each row (method='prs') or in the last (method='bz').
  557. mx = max(m, n)
  558. B = zeros(mx)
  559. for i in range(mx):
  560. for j in range(mx):
  561. if method == 'prs':
  562. B[mx - 1 - i, mx - 1 - j] = poly.nth(i, j)
  563. else:
  564. B[i, j] = poly.nth(i, j)
  565. return B
  566. def backward_eye(n):
  567. '''
  568. Returns the backward identity matrix of dimensions n x n.
  569. Needed to "turn" the Bezout matrices
  570. so that the leading coefficients are first.
  571. See docstring of the function bezout(p, q, x, method='bz').
  572. '''
  573. M = eye(n) # identity matrix of order n
  574. for i in range(int(M.rows / 2)):
  575. M.row_swap(0 + i, M.rows - 1 - i)
  576. return M
  577. def subresultants_bezout(p, q, x):
  578. """
  579. The input polynomials p, q are in Z[x] or in Q[x]. It is assumed
  580. that degree(p, x) >= degree(q, x).
  581. Computes the subresultant polynomial remainder sequence
  582. of p, q by evaluating determinants of appropriately selected
  583. submatrices of bezout(p, q, x, 'prs'). The dimensions of the
  584. latter are deg(p) x deg(p).
  585. Each coefficient is computed by evaluating the determinant of the
  586. corresponding submatrix of bezout(p, q, x, 'prs').
  587. bezout(p, q, x, 'prs) is used instead of sylvester(p, q, x, 1),
  588. Sylvester's matrix of 1840, because the dimensions of the latter
  589. are (deg(p) + deg(q)) x (deg(p) + deg(q)).
  590. If the subresultant prs is complete, then the output coincides
  591. with the Euclidean sequence of the polynomials p, q.
  592. References
  593. ==========
  594. 1. G.M.Diaz-Toca,L.Gonzalez-Vega: Various New Expressions for Subresultants
  595. and Their Applications. Appl. Algebra in Engin., Communic. and Comp.,
  596. Vol. 15, 233-266, 2004.
  597. """
  598. # make sure neither p nor q is 0
  599. if p == 0 or q == 0:
  600. return [p, q]
  601. f, g = p, q
  602. n = degF = degree(f, x)
  603. m = degG = degree(g, x)
  604. # make sure proper degrees
  605. if n == 0 and m == 0:
  606. return [f, g]
  607. if n < m:
  608. n, m, degF, degG, f, g = m, n, degG, degF, g, f
  609. if n > 0 and m == 0:
  610. return [f, g]
  611. SR_L = [f, g] # subresultant list
  612. F = LC(f, x)**(degF - degG)
  613. # form the bezout matrix
  614. B = bezout(f, g, x, 'prs')
  615. # pick appropriate submatrices of B
  616. # and form subresultant polys
  617. if degF > degG:
  618. j = 2
  619. if degF == degG:
  620. j = 1
  621. while j <= degF:
  622. M = B[0:j, :]
  623. k, coeff_L = j - 1, []
  624. while k <= degF - 1:
  625. coeff_L.append(M[:, 0:j].det())
  626. if k < degF - 1:
  627. M.col_swap(j - 1, k + 1)
  628. k = k + 1
  629. # apply Theorem 2.1 in the paper by Toca & Vega 2004
  630. # to get correct signs
  631. SR_L.append(int((-1)**(j*(j-1)/2)) * (Poly(coeff_L, x) / F).as_expr())
  632. j = j + 1
  633. return process_matrix_output(SR_L, x)
  634. def modified_subresultants_bezout(p, q, x):
  635. """
  636. The input polynomials p, q are in Z[x] or in Q[x]. It is assumed
  637. that degree(p, x) >= degree(q, x).
  638. Computes the modified subresultant polynomial remainder sequence
  639. of p, q by evaluating determinants of appropriately selected
  640. submatrices of bezout(p, q, x, 'prs'). The dimensions of the
  641. latter are deg(p) x deg(p).
  642. Each coefficient is computed by evaluating the determinant of the
  643. corresponding submatrix of bezout(p, q, x, 'prs').
  644. bezout(p, q, x, 'prs') is used instead of sylvester(p, q, x, 2),
  645. Sylvester's matrix of 1853, because the dimensions of the latter
  646. are 2*deg(p) x 2*deg(p).
  647. If the modified subresultant prs is complete, and LC( p ) > 0, the output
  648. coincides with the (generalized) Sturm's sequence of the polynomials p, q.
  649. References
  650. ==========
  651. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  652. and Modified Subresultant Polynomial Remainder Sequences.''
  653. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  654. 2. G.M.Diaz-Toca,L.Gonzalez-Vega: Various New Expressions for Subresultants
  655. and Their Applications. Appl. Algebra in Engin., Communic. and Comp.,
  656. Vol. 15, 233-266, 2004.
  657. """
  658. # make sure neither p nor q is 0
  659. if p == 0 or q == 0:
  660. return [p, q]
  661. f, g = p, q
  662. n = degF = degree(f, x)
  663. m = degG = degree(g, x)
  664. # make sure proper degrees
  665. if n == 0 and m == 0:
  666. return [f, g]
  667. if n < m:
  668. n, m, degF, degG, f, g = m, n, degG, degF, g, f
  669. if n > 0 and m == 0:
  670. return [f, g]
  671. SR_L = [f, g] # subresultant list
  672. # form the bezout matrix
  673. B = bezout(f, g, x, 'prs')
  674. # pick appropriate submatrices of B
  675. # and form subresultant polys
  676. if degF > degG:
  677. j = 2
  678. if degF == degG:
  679. j = 1
  680. while j <= degF:
  681. M = B[0:j, :]
  682. k, coeff_L = j - 1, []
  683. while k <= degF - 1:
  684. coeff_L.append(M[:, 0:j].det())
  685. if k < degF - 1:
  686. M.col_swap(j - 1, k + 1)
  687. k = k + 1
  688. ## Theorem 2.1 in the paper by Toca & Vega 2004 is _not needed_
  689. ## in this case since
  690. ## the bezout matrix is equivalent to sylvester2
  691. SR_L.append(( Poly(coeff_L, x)).as_expr())
  692. j = j + 1
  693. return process_matrix_output(SR_L, x)
  694. def sturm_pg(p, q, x, method=0):
  695. """
  696. p, q are polynomials in Z[x] or Q[x]. It is assumed
  697. that degree(p, x) >= degree(q, x).
  698. Computes the (generalized) Sturm sequence of p and q in Z[x] or Q[x].
  699. If q = diff(p, x, 1) it is the usual Sturm sequence.
  700. A. If method == 0, default, the remainder coefficients of the sequence
  701. are (in absolute value) ``modified'' subresultants, which for non-monic
  702. polynomials are greater than the coefficients of the corresponding
  703. subresultants by the factor Abs(LC(p)**( deg(p)- deg(q))).
  704. B. If method == 1, the remainder coefficients of the sequence are (in
  705. absolute value) subresultants, which for non-monic polynomials are
  706. smaller than the coefficients of the corresponding ``modified''
  707. subresultants by the factor Abs(LC(p)**( deg(p)- deg(q))).
  708. If the Sturm sequence is complete, method=0 and LC( p ) > 0, the coefficients
  709. of the polynomials in the sequence are ``modified'' subresultants.
  710. That is, they are determinants of appropriately selected submatrices of
  711. sylvester2, Sylvester's matrix of 1853. In this case the Sturm sequence
  712. coincides with the ``modified'' subresultant prs, of the polynomials
  713. p, q.
  714. If the Sturm sequence is incomplete and method=0 then the signs of the
  715. coefficients of the polynomials in the sequence may differ from the signs
  716. of the coefficients of the corresponding polynomials in the ``modified''
  717. subresultant prs; however, the absolute values are the same.
  718. To compute the coefficients, no determinant evaluation takes place. Instead,
  719. polynomial divisions in Q[x] are performed, using the function rem(p, q, x);
  720. the coefficients of the remainders computed this way become (``modified'')
  721. subresultants with the help of the Pell-Gordon Theorem of 1917.
  722. See also the function euclid_pg(p, q, x).
  723. References
  724. ==========
  725. 1. Pell A. J., R. L. Gordon. The Modified Remainders Obtained in Finding
  726. the Highest Common Factor of Two Polynomials. Annals of MatheMatics,
  727. Second Series, 18 (1917), No. 4, 188-193.
  728. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  729. and Modified Subresultant Polynomial Remainder Sequences.''
  730. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  731. """
  732. # make sure neither p nor q is 0
  733. if p == 0 or q == 0:
  734. return [p, q]
  735. # make sure proper degrees
  736. d0 = degree(p, x)
  737. d1 = degree(q, x)
  738. if d0 == 0 and d1 == 0:
  739. return [p, q]
  740. if d1 > d0:
  741. d0, d1 = d1, d0
  742. p, q = q, p
  743. if d0 > 0 and d1 == 0:
  744. return [p,q]
  745. # make sure LC(p) > 0
  746. flag = 0
  747. if LC(p,x) < 0:
  748. flag = 1
  749. p = -p
  750. q = -q
  751. # initialize
  752. lcf = LC(p, x)**(d0 - d1) # lcf * subr = modified subr
  753. a0, a1 = p, q # the input polys
  754. sturm_seq = [a0, a1] # the output list
  755. del0 = d0 - d1 # degree difference
  756. rho1 = LC(a1, x) # leading coeff of a1
  757. exp_deg = d1 - 1 # expected degree of a2
  758. a2 = - rem(a0, a1, domain=QQ) # first remainder
  759. rho2 = LC(a2,x) # leading coeff of a2
  760. d2 = degree(a2, x) # actual degree of a2
  761. deg_diff_new = exp_deg - d2 # expected - actual degree
  762. del1 = d1 - d2 # degree difference
  763. # mul_fac is the factor by which a2 is multiplied to
  764. # get integer coefficients
  765. mul_fac_old = rho1**(del0 + del1 - deg_diff_new)
  766. # append accordingly
  767. if method == 0:
  768. sturm_seq.append( simplify(lcf * a2 * Abs(mul_fac_old)))
  769. else:
  770. sturm_seq.append( simplify( a2 * Abs(mul_fac_old)))
  771. # main loop
  772. deg_diff_old = deg_diff_new
  773. while d2 > 0:
  774. a0, a1, d0, d1 = a1, a2, d1, d2 # update polys and degrees
  775. del0 = del1 # update degree difference
  776. exp_deg = d1 - 1 # new expected degree
  777. a2 = - rem(a0, a1, domain=QQ) # new remainder
  778. rho3 = LC(a2, x) # leading coeff of a2
  779. d2 = degree(a2, x) # actual degree of a2
  780. deg_diff_new = exp_deg - d2 # expected - actual degree
  781. del1 = d1 - d2 # degree difference
  782. # take into consideration the power
  783. # rho1**deg_diff_old that was "left out"
  784. expo_old = deg_diff_old # rho1 raised to this power
  785. expo_new = del0 + del1 - deg_diff_new # rho2 raised to this power
  786. # update variables and append
  787. mul_fac_new = rho2**(expo_new) * rho1**(expo_old) * mul_fac_old
  788. deg_diff_old, mul_fac_old = deg_diff_new, mul_fac_new
  789. rho1, rho2 = rho2, rho3
  790. if method == 0:
  791. sturm_seq.append( simplify(lcf * a2 * Abs(mul_fac_old)))
  792. else:
  793. sturm_seq.append( simplify( a2 * Abs(mul_fac_old)))
  794. if flag: # change the sign of the sequence
  795. sturm_seq = [-i for i in sturm_seq]
  796. # gcd is of degree > 0 ?
  797. m = len(sturm_seq)
  798. if sturm_seq[m - 1] == nan or sturm_seq[m - 1] == 0:
  799. sturm_seq.pop(m - 1)
  800. return sturm_seq
  801. def sturm_q(p, q, x):
  802. """
  803. p, q are polynomials in Z[x] or Q[x]. It is assumed
  804. that degree(p, x) >= degree(q, x).
  805. Computes the (generalized) Sturm sequence of p and q in Q[x].
  806. Polynomial divisions in Q[x] are performed, using the function rem(p, q, x).
  807. The coefficients of the polynomials in the Sturm sequence can be uniquely
  808. determined from the corresponding coefficients of the polynomials found
  809. either in:
  810. (a) the ``modified'' subresultant prs, (references 1, 2)
  811. or in
  812. (b) the subresultant prs (reference 3).
  813. References
  814. ==========
  815. 1. Pell A. J., R. L. Gordon. The Modified Remainders Obtained in Finding
  816. the Highest Common Factor of Two Polynomials. Annals of MatheMatics,
  817. Second Series, 18 (1917), No. 4, 188-193.
  818. 2 Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  819. and Modified Subresultant Polynomial Remainder Sequences.''
  820. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  821. 3. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  822. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  823. """
  824. # make sure neither p nor q is 0
  825. if p == 0 or q == 0:
  826. return [p, q]
  827. # make sure proper degrees
  828. d0 = degree(p, x)
  829. d1 = degree(q, x)
  830. if d0 == 0 and d1 == 0:
  831. return [p, q]
  832. if d1 > d0:
  833. d0, d1 = d1, d0
  834. p, q = q, p
  835. if d0 > 0 and d1 == 0:
  836. return [p,q]
  837. # make sure LC(p) > 0
  838. flag = 0
  839. if LC(p,x) < 0:
  840. flag = 1
  841. p = -p
  842. q = -q
  843. # initialize
  844. a0, a1 = p, q # the input polys
  845. sturm_seq = [a0, a1] # the output list
  846. a2 = -rem(a0, a1, domain=QQ) # first remainder
  847. d2 = degree(a2, x) # degree of a2
  848. sturm_seq.append( a2 )
  849. # main loop
  850. while d2 > 0:
  851. a0, a1, d0, d1 = a1, a2, d1, d2 # update polys and degrees
  852. a2 = -rem(a0, a1, domain=QQ) # new remainder
  853. d2 = degree(a2, x) # actual degree of a2
  854. sturm_seq.append( a2 )
  855. if flag: # change the sign of the sequence
  856. sturm_seq = [-i for i in sturm_seq]
  857. # gcd is of degree > 0 ?
  858. m = len(sturm_seq)
  859. if sturm_seq[m - 1] == nan or sturm_seq[m - 1] == 0:
  860. sturm_seq.pop(m - 1)
  861. return sturm_seq
  862. def sturm_amv(p, q, x, method=0):
  863. """
  864. p, q are polynomials in Z[x] or Q[x]. It is assumed
  865. that degree(p, x) >= degree(q, x).
  866. Computes the (generalized) Sturm sequence of p and q in Z[x] or Q[x].
  867. If q = diff(p, x, 1) it is the usual Sturm sequence.
  868. A. If method == 0, default, the remainder coefficients of the
  869. sequence are (in absolute value) ``modified'' subresultants, which
  870. for non-monic polynomials are greater than the coefficients of the
  871. corresponding subresultants by the factor Abs(LC(p)**( deg(p)- deg(q))).
  872. B. If method == 1, the remainder coefficients of the sequence are (in
  873. absolute value) subresultants, which for non-monic polynomials are
  874. smaller than the coefficients of the corresponding ``modified''
  875. subresultants by the factor Abs( LC(p)**( deg(p)- deg(q)) ).
  876. If the Sturm sequence is complete, method=0 and LC( p ) > 0, then the
  877. coefficients of the polynomials in the sequence are ``modified'' subresultants.
  878. That is, they are determinants of appropriately selected submatrices of
  879. sylvester2, Sylvester's matrix of 1853. In this case the Sturm sequence
  880. coincides with the ``modified'' subresultant prs, of the polynomials
  881. p, q.
  882. If the Sturm sequence is incomplete and method=0 then the signs of the
  883. coefficients of the polynomials in the sequence may differ from the signs
  884. of the coefficients of the corresponding polynomials in the ``modified''
  885. subresultant prs; however, the absolute values are the same.
  886. To compute the coefficients, no determinant evaluation takes place.
  887. Instead, we first compute the euclidean sequence of p and q using
  888. euclid_amv(p, q, x) and then: (a) change the signs of the remainders in the
  889. Euclidean sequence according to the pattern "-, -, +, +, -, -, +, +,..."
  890. (see Lemma 1 in the 1st reference or Theorem 3 in the 2nd reference)
  891. and (b) if method=0, assuming deg(p) > deg(q), we multiply the remainder
  892. coefficients of the Euclidean sequence times the factor
  893. Abs( LC(p)**( deg(p)- deg(q)) ) to make them modified subresultants.
  894. See also the function sturm_pg(p, q, x).
  895. References
  896. ==========
  897. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  898. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  899. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On the Remainders
  900. Obtained in Finding the Greatest Common Divisor of Two Polynomials.'' Serdica
  901. Journal of Computing 9(2) (2015), 123-138.
  902. 3. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Subresultant Polynomial
  903. Remainder Sequences Obtained by Polynomial Divisions in Q[x] or in Z[x].''
  904. Serdica Journal of Computing 10 (2016), No.3-4, 197-217.
  905. """
  906. # compute the euclidean sequence
  907. prs = euclid_amv(p, q, x)
  908. # defensive
  909. if prs == [] or len(prs) == 2:
  910. return prs
  911. # the coefficients in prs are subresultants and hence are smaller
  912. # than the corresponding subresultants by the factor
  913. # Abs( LC(prs[0])**( deg(prs[0]) - deg(prs[1])) ); Theorem 2, 2nd reference.
  914. lcf = Abs( LC(prs[0])**( degree(prs[0], x) - degree(prs[1], x) ) )
  915. # the signs of the first two polys in the sequence stay the same
  916. sturm_seq = [prs[0], prs[1]]
  917. # change the signs according to "-, -, +, +, -, -, +, +,..."
  918. # and multiply times lcf if needed
  919. flag = 0
  920. m = len(prs)
  921. i = 2
  922. while i <= m-1:
  923. if flag == 0:
  924. sturm_seq.append( - prs[i] )
  925. i = i + 1
  926. if i == m:
  927. break
  928. sturm_seq.append( - prs[i] )
  929. i = i + 1
  930. flag = 1
  931. elif flag == 1:
  932. sturm_seq.append( prs[i] )
  933. i = i + 1
  934. if i == m:
  935. break
  936. sturm_seq.append( prs[i] )
  937. i = i + 1
  938. flag = 0
  939. # subresultants or modified subresultants?
  940. if method == 0 and lcf > 1:
  941. aux_seq = [sturm_seq[0], sturm_seq[1]]
  942. for i in range(2, m):
  943. aux_seq.append(simplify(sturm_seq[i] * lcf ))
  944. sturm_seq = aux_seq
  945. return sturm_seq
  946. def euclid_pg(p, q, x):
  947. """
  948. p, q are polynomials in Z[x] or Q[x]. It is assumed
  949. that degree(p, x) >= degree(q, x).
  950. Computes the Euclidean sequence of p and q in Z[x] or Q[x].
  951. If the Euclidean sequence is complete the coefficients of the polynomials
  952. in the sequence are subresultants. That is, they are determinants of
  953. appropriately selected submatrices of sylvester1, Sylvester's matrix of 1840.
  954. In this case the Euclidean sequence coincides with the subresultant prs
  955. of the polynomials p, q.
  956. If the Euclidean sequence is incomplete the signs of the coefficients of the
  957. polynomials in the sequence may differ from the signs of the coefficients of
  958. the corresponding polynomials in the subresultant prs; however, the absolute
  959. values are the same.
  960. To compute the Euclidean sequence, no determinant evaluation takes place.
  961. We first compute the (generalized) Sturm sequence of p and q using
  962. sturm_pg(p, q, x, 1), in which case the coefficients are (in absolute value)
  963. equal to subresultants. Then we change the signs of the remainders in the
  964. Sturm sequence according to the pattern "-, -, +, +, -, -, +, +,..." ;
  965. see Lemma 1 in the 1st reference or Theorem 3 in the 2nd reference as well as
  966. the function sturm_pg(p, q, x).
  967. References
  968. ==========
  969. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  970. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  971. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On the Remainders
  972. Obtained in Finding the Greatest Common Divisor of Two Polynomials.'' Serdica
  973. Journal of Computing 9(2) (2015), 123-138.
  974. 3. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Subresultant Polynomial
  975. Remainder Sequences Obtained by Polynomial Divisions in Q[x] or in Z[x].''
  976. Serdica Journal of Computing 10 (2016), No.3-4, 197-217.
  977. """
  978. # compute the sturmian sequence using the Pell-Gordon (or AMV) theorem
  979. # with the coefficients in the prs being (in absolute value) subresultants
  980. prs = sturm_pg(p, q, x, 1) ## any other method would do
  981. # defensive
  982. if prs == [] or len(prs) == 2:
  983. return prs
  984. # the signs of the first two polys in the sequence stay the same
  985. euclid_seq = [prs[0], prs[1]]
  986. # change the signs according to "-, -, +, +, -, -, +, +,..."
  987. flag = 0
  988. m = len(prs)
  989. i = 2
  990. while i <= m-1:
  991. if flag == 0:
  992. euclid_seq.append(- prs[i] )
  993. i = i + 1
  994. if i == m:
  995. break
  996. euclid_seq.append(- prs[i] )
  997. i = i + 1
  998. flag = 1
  999. elif flag == 1:
  1000. euclid_seq.append(prs[i] )
  1001. i = i + 1
  1002. if i == m:
  1003. break
  1004. euclid_seq.append(prs[i] )
  1005. i = i + 1
  1006. flag = 0
  1007. return euclid_seq
  1008. def euclid_q(p, q, x):
  1009. """
  1010. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1011. that degree(p, x) >= degree(q, x).
  1012. Computes the Euclidean sequence of p and q in Q[x].
  1013. Polynomial divisions in Q[x] are performed, using the function rem(p, q, x).
  1014. The coefficients of the polynomials in the Euclidean sequence can be uniquely
  1015. determined from the corresponding coefficients of the polynomials found
  1016. either in:
  1017. (a) the ``modified'' subresultant polynomial remainder sequence,
  1018. (references 1, 2)
  1019. or in
  1020. (b) the subresultant polynomial remainder sequence (references 3).
  1021. References
  1022. ==========
  1023. 1. Pell A. J., R. L. Gordon. The Modified Remainders Obtained in Finding
  1024. the Highest Common Factor of Two Polynomials. Annals of MatheMatics,
  1025. Second Series, 18 (1917), No. 4, 188-193.
  1026. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  1027. and Modified Subresultant Polynomial Remainder Sequences.''
  1028. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  1029. 3. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  1030. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  1031. """
  1032. # make sure neither p nor q is 0
  1033. if p == 0 or q == 0:
  1034. return [p, q]
  1035. # make sure proper degrees
  1036. d0 = degree(p, x)
  1037. d1 = degree(q, x)
  1038. if d0 == 0 and d1 == 0:
  1039. return [p, q]
  1040. if d1 > d0:
  1041. d0, d1 = d1, d0
  1042. p, q = q, p
  1043. if d0 > 0 and d1 == 0:
  1044. return [p,q]
  1045. # make sure LC(p) > 0
  1046. flag = 0
  1047. if LC(p,x) < 0:
  1048. flag = 1
  1049. p = -p
  1050. q = -q
  1051. # initialize
  1052. a0, a1 = p, q # the input polys
  1053. euclid_seq = [a0, a1] # the output list
  1054. a2 = rem(a0, a1, domain=QQ) # first remainder
  1055. d2 = degree(a2, x) # degree of a2
  1056. euclid_seq.append( a2 )
  1057. # main loop
  1058. while d2 > 0:
  1059. a0, a1, d0, d1 = a1, a2, d1, d2 # update polys and degrees
  1060. a2 = rem(a0, a1, domain=QQ) # new remainder
  1061. d2 = degree(a2, x) # actual degree of a2
  1062. euclid_seq.append( a2 )
  1063. if flag: # change the sign of the sequence
  1064. euclid_seq = [-i for i in euclid_seq]
  1065. # gcd is of degree > 0 ?
  1066. m = len(euclid_seq)
  1067. if euclid_seq[m - 1] == nan or euclid_seq[m - 1] == 0:
  1068. euclid_seq.pop(m - 1)
  1069. return euclid_seq
  1070. def euclid_amv(f, g, x):
  1071. """
  1072. f, g are polynomials in Z[x] or Q[x]. It is assumed
  1073. that degree(f, x) >= degree(g, x).
  1074. Computes the Euclidean sequence of p and q in Z[x] or Q[x].
  1075. If the Euclidean sequence is complete the coefficients of the polynomials
  1076. in the sequence are subresultants. That is, they are determinants of
  1077. appropriately selected submatrices of sylvester1, Sylvester's matrix of 1840.
  1078. In this case the Euclidean sequence coincides with the subresultant prs,
  1079. of the polynomials p, q.
  1080. If the Euclidean sequence is incomplete the signs of the coefficients of the
  1081. polynomials in the sequence may differ from the signs of the coefficients of
  1082. the corresponding polynomials in the subresultant prs; however, the absolute
  1083. values are the same.
  1084. To compute the coefficients, no determinant evaluation takes place.
  1085. Instead, polynomial divisions in Z[x] or Q[x] are performed, using
  1086. the function rem_z(f, g, x); the coefficients of the remainders
  1087. computed this way become subresultants with the help of the
  1088. Collins-Brown-Traub formula for coefficient reduction.
  1089. References
  1090. ==========
  1091. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  1092. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  1093. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Subresultant Polynomial
  1094. remainder Sequences Obtained by Polynomial Divisions in Q[x] or in Z[x].''
  1095. Serdica Journal of Computing 10 (2016), No.3-4, 197-217.
  1096. """
  1097. # make sure neither f nor g is 0
  1098. if f == 0 or g == 0:
  1099. return [f, g]
  1100. # make sure proper degrees
  1101. d0 = degree(f, x)
  1102. d1 = degree(g, x)
  1103. if d0 == 0 and d1 == 0:
  1104. return [f, g]
  1105. if d1 > d0:
  1106. d0, d1 = d1, d0
  1107. f, g = g, f
  1108. if d0 > 0 and d1 == 0:
  1109. return [f, g]
  1110. # initialize
  1111. a0 = f
  1112. a1 = g
  1113. euclid_seq = [a0, a1]
  1114. deg_dif_p1, c = degree(a0, x) - degree(a1, x) + 1, -1
  1115. # compute the first polynomial of the prs
  1116. i = 1
  1117. a2 = rem_z(a0, a1, x) / Abs( (-1)**deg_dif_p1 ) # first remainder
  1118. euclid_seq.append( a2 )
  1119. d2 = degree(a2, x) # actual degree of a2
  1120. # main loop
  1121. while d2 >= 1:
  1122. a0, a1, d0, d1 = a1, a2, d1, d2 # update polys and degrees
  1123. i += 1
  1124. sigma0 = -LC(a0)
  1125. c = (sigma0**(deg_dif_p1 - 1)) / (c**(deg_dif_p1 - 2))
  1126. deg_dif_p1 = degree(a0, x) - d2 + 1
  1127. a2 = rem_z(a0, a1, x) / Abs( (c**(deg_dif_p1 - 1)) * sigma0 )
  1128. euclid_seq.append( a2 )
  1129. d2 = degree(a2, x) # actual degree of a2
  1130. # gcd is of degree > 0 ?
  1131. m = len(euclid_seq)
  1132. if euclid_seq[m - 1] == nan or euclid_seq[m - 1] == 0:
  1133. euclid_seq.pop(m - 1)
  1134. return euclid_seq
  1135. def modified_subresultants_pg(p, q, x):
  1136. """
  1137. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1138. that degree(p, x) >= degree(q, x).
  1139. Computes the ``modified'' subresultant prs of p and q in Z[x] or Q[x];
  1140. the coefficients of the polynomials in the sequence are
  1141. ``modified'' subresultants. That is, they are determinants of appropriately
  1142. selected submatrices of sylvester2, Sylvester's matrix of 1853.
  1143. To compute the coefficients, no determinant evaluation takes place. Instead,
  1144. polynomial divisions in Q[x] are performed, using the function rem(p, q, x);
  1145. the coefficients of the remainders computed this way become ``modified''
  1146. subresultants with the help of the Pell-Gordon Theorem of 1917.
  1147. If the ``modified'' subresultant prs is complete, and LC( p ) > 0, it coincides
  1148. with the (generalized) Sturm sequence of the polynomials p, q.
  1149. References
  1150. ==========
  1151. 1. Pell A. J., R. L. Gordon. The Modified Remainders Obtained in Finding
  1152. the Highest Common Factor of Two Polynomials. Annals of MatheMatics,
  1153. Second Series, 18 (1917), No. 4, 188-193.
  1154. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  1155. and Modified Subresultant Polynomial Remainder Sequences.''
  1156. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  1157. """
  1158. # make sure neither p nor q is 0
  1159. if p == 0 or q == 0:
  1160. return [p, q]
  1161. # make sure proper degrees
  1162. d0 = degree(p,x)
  1163. d1 = degree(q,x)
  1164. if d0 == 0 and d1 == 0:
  1165. return [p, q]
  1166. if d1 > d0:
  1167. d0, d1 = d1, d0
  1168. p, q = q, p
  1169. if d0 > 0 and d1 == 0:
  1170. return [p,q]
  1171. # initialize
  1172. k = var('k') # index in summation formula
  1173. u_list = [] # of elements (-1)**u_i
  1174. subres_l = [p, q] # mod. subr. prs output list
  1175. a0, a1 = p, q # the input polys
  1176. del0 = d0 - d1 # degree difference
  1177. degdif = del0 # save it
  1178. rho_1 = LC(a0) # lead. coeff (a0)
  1179. # Initialize Pell-Gordon variables
  1180. rho_list_minus_1 = sign( LC(a0, x)) # sign of LC(a0)
  1181. rho1 = LC(a1, x) # leading coeff of a1
  1182. rho_list = [ sign(rho1)] # of signs
  1183. p_list = [del0] # of degree differences
  1184. u = summation(k, (k, 1, p_list[0])) # value of u
  1185. u_list.append(u) # of u values
  1186. v = sum(p_list) # v value
  1187. # first remainder
  1188. exp_deg = d1 - 1 # expected degree of a2
  1189. a2 = - rem(a0, a1, domain=QQ) # first remainder
  1190. rho2 = LC(a2, x) # leading coeff of a2
  1191. d2 = degree(a2, x) # actual degree of a2
  1192. deg_diff_new = exp_deg - d2 # expected - actual degree
  1193. del1 = d1 - d2 # degree difference
  1194. # mul_fac is the factor by which a2 is multiplied to
  1195. # get integer coefficients
  1196. mul_fac_old = rho1**(del0 + del1 - deg_diff_new)
  1197. # update Pell-Gordon variables
  1198. p_list.append(1 + deg_diff_new) # deg_diff_new is 0 for complete seq
  1199. # apply Pell-Gordon formula (7) in second reference
  1200. num = 1 # numerator of fraction
  1201. for u in u_list:
  1202. num *= (-1)**u
  1203. num = num * (-1)**v
  1204. # denominator depends on complete / incomplete seq
  1205. if deg_diff_new == 0: # complete seq
  1206. den = 1
  1207. for k in range(len(rho_list)):
  1208. den *= rho_list[k]**(p_list[k] + p_list[k + 1])
  1209. den = den * rho_list_minus_1
  1210. else: # incomplete seq
  1211. den = 1
  1212. for k in range(len(rho_list)-1):
  1213. den *= rho_list[k]**(p_list[k] + p_list[k + 1])
  1214. den = den * rho_list_minus_1
  1215. expo = (p_list[len(rho_list) - 1] + p_list[len(rho_list)] - deg_diff_new)
  1216. den = den * rho_list[len(rho_list) - 1]**expo
  1217. # the sign of the determinant depends on sg(num / den)
  1218. if sign(num / den) > 0:
  1219. subres_l.append( simplify(rho_1**degdif*a2* Abs(mul_fac_old) ) )
  1220. else:
  1221. subres_l.append(- simplify(rho_1**degdif*a2* Abs(mul_fac_old) ) )
  1222. # update Pell-Gordon variables
  1223. k = var('k')
  1224. rho_list.append( sign(rho2))
  1225. u = summation(k, (k, 1, p_list[len(p_list) - 1]))
  1226. u_list.append(u)
  1227. v = sum(p_list)
  1228. deg_diff_old=deg_diff_new
  1229. # main loop
  1230. while d2 > 0:
  1231. a0, a1, d0, d1 = a1, a2, d1, d2 # update polys and degrees
  1232. del0 = del1 # update degree difference
  1233. exp_deg = d1 - 1 # new expected degree
  1234. a2 = - rem(a0, a1, domain=QQ) # new remainder
  1235. rho3 = LC(a2, x) # leading coeff of a2
  1236. d2 = degree(a2, x) # actual degree of a2
  1237. deg_diff_new = exp_deg - d2 # expected - actual degree
  1238. del1 = d1 - d2 # degree difference
  1239. # take into consideration the power
  1240. # rho1**deg_diff_old that was "left out"
  1241. expo_old = deg_diff_old # rho1 raised to this power
  1242. expo_new = del0 + del1 - deg_diff_new # rho2 raised to this power
  1243. mul_fac_new = rho2**(expo_new) * rho1**(expo_old) * mul_fac_old
  1244. # update variables
  1245. deg_diff_old, mul_fac_old = deg_diff_new, mul_fac_new
  1246. rho1, rho2 = rho2, rho3
  1247. # update Pell-Gordon variables
  1248. p_list.append(1 + deg_diff_new) # deg_diff_new is 0 for complete seq
  1249. # apply Pell-Gordon formula (7) in second reference
  1250. num = 1 # numerator
  1251. for u in u_list:
  1252. num *= (-1)**u
  1253. num = num * (-1)**v
  1254. # denominator depends on complete / incomplete seq
  1255. if deg_diff_new == 0: # complete seq
  1256. den = 1
  1257. for k in range(len(rho_list)):
  1258. den *= rho_list[k]**(p_list[k] + p_list[k + 1])
  1259. den = den * rho_list_minus_1
  1260. else: # incomplete seq
  1261. den = 1
  1262. for k in range(len(rho_list)-1):
  1263. den *= rho_list[k]**(p_list[k] + p_list[k + 1])
  1264. den = den * rho_list_minus_1
  1265. expo = (p_list[len(rho_list) - 1] + p_list[len(rho_list)] - deg_diff_new)
  1266. den = den * rho_list[len(rho_list) - 1]**expo
  1267. # the sign of the determinant depends on sg(num / den)
  1268. if sign(num / den) > 0:
  1269. subres_l.append( simplify(rho_1**degdif*a2* Abs(mul_fac_old) ) )
  1270. else:
  1271. subres_l.append(- simplify(rho_1**degdif*a2* Abs(mul_fac_old) ) )
  1272. # update Pell-Gordon variables
  1273. k = var('k')
  1274. rho_list.append( sign(rho2))
  1275. u = summation(k, (k, 1, p_list[len(p_list) - 1]))
  1276. u_list.append(u)
  1277. v = sum(p_list)
  1278. # gcd is of degree > 0 ?
  1279. m = len(subres_l)
  1280. if subres_l[m - 1] == nan or subres_l[m - 1] == 0:
  1281. subres_l.pop(m - 1)
  1282. # LC( p ) < 0
  1283. m = len(subres_l) # list may be shorter now due to deg(gcd ) > 0
  1284. if LC( p ) < 0:
  1285. aux_seq = [subres_l[0], subres_l[1]]
  1286. for i in range(2, m):
  1287. aux_seq.append(simplify(subres_l[i] * (-1) ))
  1288. subres_l = aux_seq
  1289. return subres_l
  1290. def subresultants_pg(p, q, x):
  1291. """
  1292. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1293. that degree(p, x) >= degree(q, x).
  1294. Computes the subresultant prs of p and q in Z[x] or Q[x], from
  1295. the modified subresultant prs of p and q.
  1296. The coefficients of the polynomials in these two sequences differ only
  1297. in sign and the factor LC(p)**( deg(p)- deg(q)) as stated in
  1298. Theorem 2 of the reference.
  1299. The coefficients of the polynomials in the output sequence are
  1300. subresultants. That is, they are determinants of appropriately
  1301. selected submatrices of sylvester1, Sylvester's matrix of 1840.
  1302. If the subresultant prs is complete, then it coincides with the
  1303. Euclidean sequence of the polynomials p, q.
  1304. References
  1305. ==========
  1306. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: "On the Remainders
  1307. Obtained in Finding the Greatest Common Divisor of Two Polynomials."
  1308. Serdica Journal of Computing 9(2) (2015), 123-138.
  1309. """
  1310. # compute the modified subresultant prs
  1311. lst = modified_subresultants_pg(p,q,x) ## any other method would do
  1312. # defensive
  1313. if lst == [] or len(lst) == 2:
  1314. return lst
  1315. # the coefficients in lst are modified subresultants and, hence, are
  1316. # greater than those of the corresponding subresultants by the factor
  1317. # LC(lst[0])**( deg(lst[0]) - deg(lst[1])); see Theorem 2 in reference.
  1318. lcf = LC(lst[0])**( degree(lst[0], x) - degree(lst[1], x) )
  1319. # Initialize the subresultant prs list
  1320. subr_seq = [lst[0], lst[1]]
  1321. # compute the degree sequences m_i and j_i of Theorem 2 in reference.
  1322. deg_seq = [degree(Poly(poly, x), x) for poly in lst]
  1323. deg = deg_seq[0]
  1324. deg_seq_s = deg_seq[1:-1]
  1325. m_seq = [m-1 for m in deg_seq_s]
  1326. j_seq = [deg - m for m in m_seq]
  1327. # compute the AMV factors of Theorem 2 in reference.
  1328. fact = [(-1)**( j*(j-1)/S(2) ) for j in j_seq]
  1329. # shortened list without the first two polys
  1330. lst_s = lst[2:]
  1331. # poly lst_s[k] is multiplied times fact[k], divided by lcf
  1332. # and appended to the subresultant prs list
  1333. m = len(fact)
  1334. for k in range(m):
  1335. if sign(fact[k]) == -1:
  1336. subr_seq.append(-lst_s[k] / lcf)
  1337. else:
  1338. subr_seq.append(lst_s[k] / lcf)
  1339. return subr_seq
  1340. def subresultants_amv_q(p, q, x):
  1341. """
  1342. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1343. that degree(p, x) >= degree(q, x).
  1344. Computes the subresultant prs of p and q in Q[x];
  1345. the coefficients of the polynomials in the sequence are
  1346. subresultants. That is, they are determinants of appropriately
  1347. selected submatrices of sylvester1, Sylvester's matrix of 1840.
  1348. To compute the coefficients, no determinant evaluation takes place.
  1349. Instead, polynomial divisions in Q[x] are performed, using the
  1350. function rem(p, q, x); the coefficients of the remainders
  1351. computed this way become subresultants with the help of the
  1352. Akritas-Malaschonok-Vigklas Theorem of 2015.
  1353. If the subresultant prs is complete, then it coincides with the
  1354. Euclidean sequence of the polynomials p, q.
  1355. References
  1356. ==========
  1357. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  1358. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  1359. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Subresultant Polynomial
  1360. remainder Sequences Obtained by Polynomial Divisions in Q[x] or in Z[x].''
  1361. Serdica Journal of Computing 10 (2016), No.3-4, 197-217.
  1362. """
  1363. # make sure neither p nor q is 0
  1364. if p == 0 or q == 0:
  1365. return [p, q]
  1366. # make sure proper degrees
  1367. d0 = degree(p, x)
  1368. d1 = degree(q, x)
  1369. if d0 == 0 and d1 == 0:
  1370. return [p, q]
  1371. if d1 > d0:
  1372. d0, d1 = d1, d0
  1373. p, q = q, p
  1374. if d0 > 0 and d1 == 0:
  1375. return [p, q]
  1376. # initialize
  1377. i, s = 0, 0 # counters for remainders & odd elements
  1378. p_odd_index_sum = 0 # contains the sum of p_1, p_3, etc
  1379. subres_l = [p, q] # subresultant prs output list
  1380. a0, a1 = p, q # the input polys
  1381. sigma1 = LC(a1, x) # leading coeff of a1
  1382. p0 = d0 - d1 # degree difference
  1383. if p0 % 2 == 1:
  1384. s += 1
  1385. phi = floor( (s + 1) / 2 )
  1386. mul_fac = 1
  1387. d2 = d1
  1388. # main loop
  1389. while d2 > 0:
  1390. i += 1
  1391. a2 = rem(a0, a1, domain= QQ) # new remainder
  1392. if i == 1:
  1393. sigma2 = LC(a2, x)
  1394. else:
  1395. sigma3 = LC(a2, x)
  1396. sigma1, sigma2 = sigma2, sigma3
  1397. d2 = degree(a2, x)
  1398. p1 = d1 - d2
  1399. psi = i + phi + p_odd_index_sum
  1400. # new mul_fac
  1401. mul_fac = sigma1**(p0 + 1) * mul_fac
  1402. ## compute the sign of the first fraction in formula (9) of the paper
  1403. # numerator
  1404. num = (-1)**psi
  1405. # denominator
  1406. den = sign(mul_fac)
  1407. # the sign of the determinant depends on sign( num / den ) != 0
  1408. if sign(num / den) > 0:
  1409. subres_l.append( simplify(expand(a2* Abs(mul_fac))))
  1410. else:
  1411. subres_l.append(- simplify(expand(a2* Abs(mul_fac))))
  1412. ## bring into mul_fac the missing power of sigma if there was a degree gap
  1413. if p1 - 1 > 0:
  1414. mul_fac = mul_fac * sigma1**(p1 - 1)
  1415. # update AMV variables
  1416. a0, a1, d0, d1 = a1, a2, d1, d2
  1417. p0 = p1
  1418. if p0 % 2 ==1:
  1419. s += 1
  1420. phi = floor( (s + 1) / 2 )
  1421. if i%2 == 1:
  1422. p_odd_index_sum += p0 # p_i has odd index
  1423. # gcd is of degree > 0 ?
  1424. m = len(subres_l)
  1425. if subres_l[m - 1] == nan or subres_l[m - 1] == 0:
  1426. subres_l.pop(m - 1)
  1427. return subres_l
  1428. def compute_sign(base, expo):
  1429. '''
  1430. base != 0 and expo >= 0 are integers;
  1431. returns the sign of base**expo without
  1432. evaluating the power itself!
  1433. '''
  1434. sb = sign(base)
  1435. if sb == 1:
  1436. return 1
  1437. pe = expo % 2
  1438. if pe == 0:
  1439. return -sb
  1440. else:
  1441. return sb
  1442. def rem_z(p, q, x):
  1443. '''
  1444. Intended mainly for p, q polynomials in Z[x] so that,
  1445. on dividing p by q, the remainder will also be in Z[x]. (However,
  1446. it also works fine for polynomials in Q[x].) It is assumed
  1447. that degree(p, x) >= degree(q, x).
  1448. It premultiplies p by the _absolute_ value of the leading coefficient
  1449. of q, raised to the power deg(p) - deg(q) + 1 and then performs
  1450. polynomial division in Q[x], using the function rem(p, q, x).
  1451. By contrast the function prem(p, q, x) does _not_ use the absolute
  1452. value of the leading coefficient of q.
  1453. This results not only in ``messing up the signs'' of the Euclidean and
  1454. Sturmian prs's as mentioned in the second reference,
  1455. but also in violation of the main results of the first and third
  1456. references --- Theorem 4 and Theorem 1 respectively. Theorems 4 and 1
  1457. establish a one-to-one correspondence between the Euclidean and the
  1458. Sturmian prs of p, q, on one hand, and the subresultant prs of p, q,
  1459. on the other.
  1460. References
  1461. ==========
  1462. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On the Remainders
  1463. Obtained in Finding the Greatest Common Divisor of Two Polynomials.''
  1464. Serdica Journal of Computing, 9(2) (2015), 123-138.
  1465. 2. https://planetMath.org/sturmstheorem
  1466. 3. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result on
  1467. the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  1468. '''
  1469. if (p.as_poly().is_univariate and q.as_poly().is_univariate and
  1470. p.as_poly().gens == q.as_poly().gens):
  1471. delta = (degree(p, x) - degree(q, x) + 1)
  1472. return rem(Abs(LC(q, x))**delta * p, q, x)
  1473. else:
  1474. return prem(p, q, x)
  1475. def quo_z(p, q, x):
  1476. """
  1477. Intended mainly for p, q polynomials in Z[x] so that,
  1478. on dividing p by q, the quotient will also be in Z[x]. (However,
  1479. it also works fine for polynomials in Q[x].) It is assumed
  1480. that degree(p, x) >= degree(q, x).
  1481. It premultiplies p by the _absolute_ value of the leading coefficient
  1482. of q, raised to the power deg(p) - deg(q) + 1 and then performs
  1483. polynomial division in Q[x], using the function quo(p, q, x).
  1484. By contrast the function pquo(p, q, x) does _not_ use the absolute
  1485. value of the leading coefficient of q.
  1486. See also function rem_z(p, q, x) for additional comments and references.
  1487. """
  1488. if (p.as_poly().is_univariate and q.as_poly().is_univariate and
  1489. p.as_poly().gens == q.as_poly().gens):
  1490. delta = (degree(p, x) - degree(q, x) + 1)
  1491. return quo(Abs(LC(q, x))**delta * p, q, x)
  1492. else:
  1493. return pquo(p, q, x)
  1494. def subresultants_amv(f, g, x):
  1495. """
  1496. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1497. that degree(f, x) >= degree(g, x).
  1498. Computes the subresultant prs of p and q in Z[x] or Q[x];
  1499. the coefficients of the polynomials in the sequence are
  1500. subresultants. That is, they are determinants of appropriately
  1501. selected submatrices of sylvester1, Sylvester's matrix of 1840.
  1502. To compute the coefficients, no determinant evaluation takes place.
  1503. Instead, polynomial divisions in Z[x] or Q[x] are performed, using
  1504. the function rem_z(p, q, x); the coefficients of the remainders
  1505. computed this way become subresultants with the help of the
  1506. Akritas-Malaschonok-Vigklas Theorem of 2015 and the Collins-Brown-
  1507. Traub formula for coefficient reduction.
  1508. If the subresultant prs is complete, then it coincides with the
  1509. Euclidean sequence of the polynomials p, q.
  1510. References
  1511. ==========
  1512. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``A Basic Result
  1513. on the Theory of Subresultants.'' Serdica Journal of Computing 10 (2016), No.1, 31-48.
  1514. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Subresultant Polynomial
  1515. remainder Sequences Obtained by Polynomial Divisions in Q[x] or in Z[x].''
  1516. Serdica Journal of Computing 10 (2016), No.3-4, 197-217.
  1517. """
  1518. # make sure neither f nor g is 0
  1519. if f == 0 or g == 0:
  1520. return [f, g]
  1521. # make sure proper degrees
  1522. d0 = degree(f, x)
  1523. d1 = degree(g, x)
  1524. if d0 == 0 and d1 == 0:
  1525. return [f, g]
  1526. if d1 > d0:
  1527. d0, d1 = d1, d0
  1528. f, g = g, f
  1529. if d0 > 0 and d1 == 0:
  1530. return [f, g]
  1531. # initialize
  1532. a0 = f
  1533. a1 = g
  1534. subres_l = [a0, a1]
  1535. deg_dif_p1, c = degree(a0, x) - degree(a1, x) + 1, -1
  1536. # initialize AMV variables
  1537. sigma1 = LC(a1, x) # leading coeff of a1
  1538. i, s = 0, 0 # counters for remainders & odd elements
  1539. p_odd_index_sum = 0 # contains the sum of p_1, p_3, etc
  1540. p0 = deg_dif_p1 - 1
  1541. if p0 % 2 == 1:
  1542. s += 1
  1543. phi = floor( (s + 1) / 2 )
  1544. # compute the first polynomial of the prs
  1545. i += 1
  1546. a2 = rem_z(a0, a1, x) / Abs( (-1)**deg_dif_p1 ) # first remainder
  1547. sigma2 = LC(a2, x) # leading coeff of a2
  1548. d2 = degree(a2, x) # actual degree of a2
  1549. p1 = d1 - d2 # degree difference
  1550. # sgn_den is the factor, the denominator 1st fraction of (9),
  1551. # by which a2 is multiplied to get integer coefficients
  1552. sgn_den = compute_sign( sigma1, p0 + 1 )
  1553. ## compute sign of the 1st fraction in formula (9) of the paper
  1554. # numerator
  1555. psi = i + phi + p_odd_index_sum
  1556. num = (-1)**psi
  1557. # denominator
  1558. den = sgn_den
  1559. # the sign of the determinant depends on sign(num / den) != 0
  1560. if sign(num / den) > 0:
  1561. subres_l.append( a2 )
  1562. else:
  1563. subres_l.append( -a2 )
  1564. # update AMV variable
  1565. if p1 % 2 == 1:
  1566. s += 1
  1567. # bring in the missing power of sigma if there was gap
  1568. if p1 - 1 > 0:
  1569. sgn_den = sgn_den * compute_sign( sigma1, p1 - 1 )
  1570. # main loop
  1571. while d2 >= 1:
  1572. phi = floor( (s + 1) / 2 )
  1573. if i%2 == 1:
  1574. p_odd_index_sum += p1 # p_i has odd index
  1575. a0, a1, d0, d1 = a1, a2, d1, d2 # update polys and degrees
  1576. p0 = p1 # update degree difference
  1577. i += 1
  1578. sigma0 = -LC(a0)
  1579. c = (sigma0**(deg_dif_p1 - 1)) / (c**(deg_dif_p1 - 2))
  1580. deg_dif_p1 = degree(a0, x) - d2 + 1
  1581. a2 = rem_z(a0, a1, x) / Abs( (c**(deg_dif_p1 - 1)) * sigma0 )
  1582. sigma3 = LC(a2, x) # leading coeff of a2
  1583. d2 = degree(a2, x) # actual degree of a2
  1584. p1 = d1 - d2 # degree difference
  1585. psi = i + phi + p_odd_index_sum
  1586. # update variables
  1587. sigma1, sigma2 = sigma2, sigma3
  1588. # new sgn_den
  1589. sgn_den = compute_sign( sigma1, p0 + 1 ) * sgn_den
  1590. # compute the sign of the first fraction in formula (9) of the paper
  1591. # numerator
  1592. num = (-1)**psi
  1593. # denominator
  1594. den = sgn_den
  1595. # the sign of the determinant depends on sign( num / den ) != 0
  1596. if sign(num / den) > 0:
  1597. subres_l.append( a2 )
  1598. else:
  1599. subres_l.append( -a2 )
  1600. # update AMV variable
  1601. if p1 % 2 ==1:
  1602. s += 1
  1603. # bring in the missing power of sigma if there was gap
  1604. if p1 - 1 > 0:
  1605. sgn_den = sgn_den * compute_sign( sigma1, p1 - 1 )
  1606. # gcd is of degree > 0 ?
  1607. m = len(subres_l)
  1608. if subres_l[m - 1] == nan or subres_l[m - 1] == 0:
  1609. subres_l.pop(m - 1)
  1610. return subres_l
  1611. def modified_subresultants_amv(p, q, x):
  1612. """
  1613. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1614. that degree(p, x) >= degree(q, x).
  1615. Computes the modified subresultant prs of p and q in Z[x] or Q[x],
  1616. from the subresultant prs of p and q.
  1617. The coefficients of the polynomials in the two sequences differ only
  1618. in sign and the factor LC(p)**( deg(p)- deg(q)) as stated in
  1619. Theorem 2 of the reference.
  1620. The coefficients of the polynomials in the output sequence are
  1621. modified subresultants. That is, they are determinants of appropriately
  1622. selected submatrices of sylvester2, Sylvester's matrix of 1853.
  1623. If the modified subresultant prs is complete, and LC( p ) > 0, it coincides
  1624. with the (generalized) Sturm's sequence of the polynomials p, q.
  1625. References
  1626. ==========
  1627. 1. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: "On the Remainders
  1628. Obtained in Finding the Greatest Common Divisor of Two Polynomials."
  1629. Serdica Journal of Computing, Serdica Journal of Computing, 9(2) (2015), 123-138.
  1630. """
  1631. # compute the subresultant prs
  1632. lst = subresultants_amv(p,q,x) ## any other method would do
  1633. # defensive
  1634. if lst == [] or len(lst) == 2:
  1635. return lst
  1636. # the coefficients in lst are subresultants and, hence, smaller than those
  1637. # of the corresponding modified subresultants by the factor
  1638. # LC(lst[0])**( deg(lst[0]) - deg(lst[1])); see Theorem 2.
  1639. lcf = LC(lst[0])**( degree(lst[0], x) - degree(lst[1], x) )
  1640. # Initialize the modified subresultant prs list
  1641. subr_seq = [lst[0], lst[1]]
  1642. # compute the degree sequences m_i and j_i of Theorem 2
  1643. deg_seq = [degree(Poly(poly, x), x) for poly in lst]
  1644. deg = deg_seq[0]
  1645. deg_seq_s = deg_seq[1:-1]
  1646. m_seq = [m-1 for m in deg_seq_s]
  1647. j_seq = [deg - m for m in m_seq]
  1648. # compute the AMV factors of Theorem 2
  1649. fact = [(-1)**( j*(j-1)/S(2) ) for j in j_seq]
  1650. # shortened list without the first two polys
  1651. lst_s = lst[2:]
  1652. # poly lst_s[k] is multiplied times fact[k] and times lcf
  1653. # and appended to the subresultant prs list
  1654. m = len(fact)
  1655. for k in range(m):
  1656. if sign(fact[k]) == -1:
  1657. subr_seq.append( simplify(-lst_s[k] * lcf) )
  1658. else:
  1659. subr_seq.append( simplify(lst_s[k] * lcf) )
  1660. return subr_seq
  1661. def correct_sign(deg_f, deg_g, s1, rdel, cdel):
  1662. """
  1663. Used in various subresultant prs algorithms.
  1664. Evaluates the determinant, (a.k.a. subresultant) of a properly selected
  1665. submatrix of s1, Sylvester's matrix of 1840, to get the correct sign
  1666. and value of the leading coefficient of a given polynomial remainder.
  1667. deg_f, deg_g are the degrees of the original polynomials p, q for which the
  1668. matrix s1 = sylvester(p, q, x, 1) was constructed.
  1669. rdel denotes the expected degree of the remainder; it is the number of
  1670. rows to be deleted from each group of rows in s1 as described in the
  1671. reference below.
  1672. cdel denotes the expected degree minus the actual degree of the remainder;
  1673. it is the number of columns to be deleted --- starting with the last column
  1674. forming the square matrix --- from the matrix resulting after the row deletions.
  1675. References
  1676. ==========
  1677. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``Sturm Sequences
  1678. and Modified Subresultant Polynomial Remainder Sequences.''
  1679. Serdica Journal of Computing, Vol. 8, No 1, 29-46, 2014.
  1680. """
  1681. M = s1[:, :] # copy of matrix s1
  1682. # eliminate rdel rows from the first deg_g rows
  1683. for i in range(M.rows - deg_f - 1, M.rows - deg_f - rdel - 1, -1):
  1684. M.row_del(i)
  1685. # eliminate rdel rows from the last deg_f rows
  1686. for i in range(M.rows - 1, M.rows - rdel - 1, -1):
  1687. M.row_del(i)
  1688. # eliminate cdel columns
  1689. for i in range(cdel):
  1690. M.col_del(M.rows - 1)
  1691. # define submatrix
  1692. Md = M[:, 0: M.rows]
  1693. return Md.det()
  1694. def subresultants_rem(p, q, x):
  1695. """
  1696. p, q are polynomials in Z[x] or Q[x]. It is assumed
  1697. that degree(p, x) >= degree(q, x).
  1698. Computes the subresultant prs of p and q in Z[x] or Q[x];
  1699. the coefficients of the polynomials in the sequence are
  1700. subresultants. That is, they are determinants of appropriately
  1701. selected submatrices of sylvester1, Sylvester's matrix of 1840.
  1702. To compute the coefficients polynomial divisions in Q[x] are
  1703. performed, using the function rem(p, q, x). The coefficients
  1704. of the remainders computed this way become subresultants by evaluating
  1705. one subresultant per remainder --- that of the leading coefficient.
  1706. This way we obtain the correct sign and value of the leading coefficient
  1707. of the remainder and we easily ``force'' the rest of the coefficients
  1708. to become subresultants.
  1709. If the subresultant prs is complete, then it coincides with the
  1710. Euclidean sequence of the polynomials p, q.
  1711. References
  1712. ==========
  1713. 1. Akritas, A. G.:``Three New Methods for Computing Subresultant
  1714. Polynomial Remainder Sequences (PRS's).'' Serdica Journal of Computing 9(1) (2015), 1-26.
  1715. """
  1716. # make sure neither p nor q is 0
  1717. if p == 0 or q == 0:
  1718. return [p, q]
  1719. # make sure proper degrees
  1720. f, g = p, q
  1721. n = deg_f = degree(f, x)
  1722. m = deg_g = degree(g, x)
  1723. if n == 0 and m == 0:
  1724. return [f, g]
  1725. if n < m:
  1726. n, m, deg_f, deg_g, f, g = m, n, deg_g, deg_f, g, f
  1727. if n > 0 and m == 0:
  1728. return [f, g]
  1729. # initialize
  1730. s1 = sylvester(f, g, x, 1)
  1731. sr_list = [f, g] # subresultant list
  1732. # main loop
  1733. while deg_g > 0:
  1734. r = rem(p, q, x)
  1735. d = degree(r, x)
  1736. if d < 0:
  1737. return sr_list
  1738. # make coefficients subresultants evaluating ONE determinant
  1739. exp_deg = deg_g - 1 # expected degree
  1740. sign_value = correct_sign(n, m, s1, exp_deg, exp_deg - d)
  1741. r = simplify((r / LC(r, x)) * sign_value)
  1742. # append poly with subresultant coeffs
  1743. sr_list.append(r)
  1744. # update degrees and polys
  1745. deg_f, deg_g = deg_g, d
  1746. p, q = q, r
  1747. # gcd is of degree > 0 ?
  1748. m = len(sr_list)
  1749. if sr_list[m - 1] == nan or sr_list[m - 1] == 0:
  1750. sr_list.pop(m - 1)
  1751. return sr_list
  1752. def pivot(M, i, j):
  1753. '''
  1754. M is a matrix, and M[i, j] specifies the pivot element.
  1755. All elements below M[i, j], in the j-th column, will
  1756. be zeroed, if they are not already 0, according to
  1757. Dodgson-Bareiss' integer preserving transformations.
  1758. References
  1759. ==========
  1760. 1. Akritas, A. G.: ``A new method for computing polynomial greatest
  1761. common divisors and polynomial remainder sequences.''
  1762. Numerische MatheMatik 52, 119-127, 1988.
  1763. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On a Theorem
  1764. by Van Vleck Regarding Sturm Sequences.''
  1765. Serdica Journal of Computing, 7, No 4, 101-134, 2013.
  1766. '''
  1767. ma = M[:, :] # copy of matrix M
  1768. rs = ma.rows # No. of rows
  1769. cs = ma.cols # No. of cols
  1770. for r in range(i+1, rs):
  1771. if ma[r, j] != 0:
  1772. for c in range(j + 1, cs):
  1773. ma[r, c] = ma[i, j] * ma[r, c] - ma[i, c] * ma[r, j]
  1774. ma[r, j] = 0
  1775. return ma
  1776. def rotate_r(L, k):
  1777. '''
  1778. Rotates right by k. L is a row of a matrix or a list.
  1779. '''
  1780. ll = list(L)
  1781. if ll == []:
  1782. return []
  1783. for i in range(k):
  1784. el = ll.pop(len(ll) - 1)
  1785. ll.insert(0, el)
  1786. return ll if isinstance(L, list) else Matrix([ll])
  1787. def rotate_l(L, k):
  1788. '''
  1789. Rotates left by k. L is a row of a matrix or a list.
  1790. '''
  1791. ll = list(L)
  1792. if ll == []:
  1793. return []
  1794. for i in range(k):
  1795. el = ll.pop(0)
  1796. ll.insert(len(ll) - 1, el)
  1797. return ll if isinstance(L, list) else Matrix([ll])
  1798. def row2poly(row, deg, x):
  1799. '''
  1800. Converts the row of a matrix to a poly of degree deg and variable x.
  1801. Some entries at the beginning and/or at the end of the row may be zero.
  1802. '''
  1803. k = 0
  1804. poly = []
  1805. leng = len(row)
  1806. # find the beginning of the poly ; i.e. the first
  1807. # non-zero element of the row
  1808. while row[k] == 0:
  1809. k = k + 1
  1810. # append the next deg + 1 elements to poly
  1811. for j in range( deg + 1):
  1812. if k + j <= leng:
  1813. poly.append(row[k + j])
  1814. return Poly(poly, x)
  1815. def create_ma(deg_f, deg_g, row1, row2, col_num):
  1816. '''
  1817. Creates a ``small'' matrix M to be triangularized.
  1818. deg_f, deg_g are the degrees of the divident and of the
  1819. divisor polynomials respectively, deg_g > deg_f.
  1820. The coefficients of the divident poly are the elements
  1821. in row2 and those of the divisor poly are the elements
  1822. in row1.
  1823. col_num defines the number of columns of the matrix M.
  1824. '''
  1825. if deg_g - deg_f >= 1:
  1826. print('Reverse degrees')
  1827. return
  1828. m = zeros(deg_f - deg_g + 2, col_num)
  1829. for i in range(deg_f - deg_g + 1):
  1830. m[i, :] = rotate_r(row1, i)
  1831. m[deg_f - deg_g + 1, :] = row2
  1832. return m
  1833. def find_degree(M, deg_f):
  1834. '''
  1835. Finds the degree of the poly corresponding (after triangularization)
  1836. to the _last_ row of the ``small'' matrix M, created by create_ma().
  1837. deg_f is the degree of the divident poly.
  1838. If _last_ row is all 0's returns None.
  1839. '''
  1840. j = deg_f
  1841. for i in range(0, M.cols):
  1842. if M[M.rows - 1, i] == 0:
  1843. j = j - 1
  1844. else:
  1845. return j if j >= 0 else 0
  1846. def final_touches(s2, r, deg_g):
  1847. """
  1848. s2 is sylvester2, r is the row pointer in s2,
  1849. deg_g is the degree of the poly last inserted in s2.
  1850. After a gcd of degree > 0 has been found with Van Vleck's
  1851. method, and was inserted into s2, if its last term is not
  1852. in the last column of s2, then it is inserted as many
  1853. times as needed, rotated right by one each time, until
  1854. the condition is met.
  1855. """
  1856. R = s2.row(r-1)
  1857. # find the first non zero term
  1858. for i in range(s2.cols):
  1859. if R[0,i] == 0:
  1860. continue
  1861. else:
  1862. break
  1863. # missing rows until last term is in last column
  1864. mr = s2.cols - (i + deg_g + 1)
  1865. # insert them by replacing the existing entries in the row
  1866. i = 0
  1867. while mr != 0 and r + i < s2.rows :
  1868. s2[r + i, : ] = rotate_r(R, i + 1)
  1869. i += 1
  1870. mr -= 1
  1871. return s2
  1872. def subresultants_vv(p, q, x, method = 0):
  1873. """
  1874. p, q are polynomials in Z[x] (intended) or Q[x]. It is assumed
  1875. that degree(p, x) >= degree(q, x).
  1876. Computes the subresultant prs of p, q by triangularizing,
  1877. in Z[x] or in Q[x], all the smaller matrices encountered in the
  1878. process of triangularizing sylvester2, Sylvester's matrix of 1853;
  1879. see references 1 and 2 for Van Vleck's method. With each remainder,
  1880. sylvester2 gets updated and is prepared to be printed if requested.
  1881. If sylvester2 has small dimensions and you want to see the final,
  1882. triangularized matrix use this version with method=1; otherwise,
  1883. use either this version with method=0 (default) or the faster version,
  1884. subresultants_vv_2(p, q, x), where sylvester2 is used implicitly.
  1885. Sylvester's matrix sylvester1 is also used to compute one
  1886. subresultant per remainder; namely, that of the leading
  1887. coefficient, in order to obtain the correct sign and to
  1888. force the remainder coefficients to become subresultants.
  1889. If the subresultant prs is complete, then it coincides with the
  1890. Euclidean sequence of the polynomials p, q.
  1891. If the final, triangularized matrix s2 is printed, then:
  1892. (a) if deg(p) - deg(q) > 1 or deg( gcd(p, q) ) > 0, several
  1893. of the last rows in s2 will remain unprocessed;
  1894. (b) if deg(p) - deg(q) == 0, p will not appear in the final matrix.
  1895. References
  1896. ==========
  1897. 1. Akritas, A. G.: ``A new method for computing polynomial greatest
  1898. common divisors and polynomial remainder sequences.''
  1899. Numerische MatheMatik 52, 119-127, 1988.
  1900. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On a Theorem
  1901. by Van Vleck Regarding Sturm Sequences.''
  1902. Serdica Journal of Computing, 7, No 4, 101-134, 2013.
  1903. 3. Akritas, A. G.:``Three New Methods for Computing Subresultant
  1904. Polynomial Remainder Sequences (PRS's).'' Serdica Journal of Computing 9(1) (2015), 1-26.
  1905. """
  1906. # make sure neither p nor q is 0
  1907. if p == 0 or q == 0:
  1908. return [p, q]
  1909. # make sure proper degrees
  1910. f, g = p, q
  1911. n = deg_f = degree(f, x)
  1912. m = deg_g = degree(g, x)
  1913. if n == 0 and m == 0:
  1914. return [f, g]
  1915. if n < m:
  1916. n, m, deg_f, deg_g, f, g = m, n, deg_g, deg_f, g, f
  1917. if n > 0 and m == 0:
  1918. return [f, g]
  1919. # initialize
  1920. s1 = sylvester(f, g, x, 1)
  1921. s2 = sylvester(f, g, x, 2)
  1922. sr_list = [f, g]
  1923. col_num = 2 * n # columns in s2
  1924. # make two rows (row0, row1) of poly coefficients
  1925. row0 = Poly(f, x, domain = QQ).all_coeffs()
  1926. leng0 = len(row0)
  1927. for i in range(col_num - leng0):
  1928. row0.append(0)
  1929. row0 = Matrix([row0])
  1930. row1 = Poly(g,x, domain = QQ).all_coeffs()
  1931. leng1 = len(row1)
  1932. for i in range(col_num - leng1):
  1933. row1.append(0)
  1934. row1 = Matrix([row1])
  1935. # row pointer for deg_f - deg_g == 1; may be reset below
  1936. r = 2
  1937. # modify first rows of s2 matrix depending on poly degrees
  1938. if deg_f - deg_g > 1:
  1939. r = 1
  1940. # replacing the existing entries in the rows of s2,
  1941. # insert row0 (deg_f - deg_g - 1) times, rotated each time
  1942. for i in range(deg_f - deg_g - 1):
  1943. s2[r + i, : ] = rotate_r(row0, i + 1)
  1944. r = r + deg_f - deg_g - 1
  1945. # insert row1 (deg_f - deg_g) times, rotated each time
  1946. for i in range(deg_f - deg_g):
  1947. s2[r + i, : ] = rotate_r(row1, r + i)
  1948. r = r + deg_f - deg_g
  1949. if deg_f - deg_g == 0:
  1950. r = 0
  1951. # main loop
  1952. while deg_g > 0:
  1953. # create a small matrix M, and triangularize it;
  1954. M = create_ma(deg_f, deg_g, row1, row0, col_num)
  1955. # will need only the first and last rows of M
  1956. for i in range(deg_f - deg_g + 1):
  1957. M1 = pivot(M, i, i)
  1958. M = M1[:, :]
  1959. # treat last row of M as poly; find its degree
  1960. d = find_degree(M, deg_f)
  1961. if d is None:
  1962. break
  1963. exp_deg = deg_g - 1
  1964. # evaluate one determinant & make coefficients subresultants
  1965. sign_value = correct_sign(n, m, s1, exp_deg, exp_deg - d)
  1966. poly = row2poly(M[M.rows - 1, :], d, x)
  1967. temp2 = LC(poly, x)
  1968. poly = simplify((poly / temp2) * sign_value)
  1969. # update s2 by inserting first row of M as needed
  1970. row0 = M[0, :]
  1971. for i in range(deg_g - d):
  1972. s2[r + i, :] = rotate_r(row0, r + i)
  1973. r = r + deg_g - d
  1974. # update s2 by inserting last row of M as needed
  1975. row1 = rotate_l(M[M.rows - 1, :], deg_f - d)
  1976. row1 = (row1 / temp2) * sign_value
  1977. for i in range(deg_g - d):
  1978. s2[r + i, :] = rotate_r(row1, r + i)
  1979. r = r + deg_g - d
  1980. # update degrees
  1981. deg_f, deg_g = deg_g, d
  1982. # append poly with subresultant coeffs
  1983. sr_list.append(poly)
  1984. # final touches to print the s2 matrix
  1985. if method != 0 and s2.rows > 2:
  1986. s2 = final_touches(s2, r, deg_g)
  1987. pprint(s2)
  1988. elif method != 0 and s2.rows == 2:
  1989. s2[1, :] = rotate_r(s2.row(1), 1)
  1990. pprint(s2)
  1991. return sr_list
  1992. def subresultants_vv_2(p, q, x):
  1993. """
  1994. p, q are polynomials in Z[x] (intended) or Q[x]. It is assumed
  1995. that degree(p, x) >= degree(q, x).
  1996. Computes the subresultant prs of p, q by triangularizing,
  1997. in Z[x] or in Q[x], all the smaller matrices encountered in the
  1998. process of triangularizing sylvester2, Sylvester's matrix of 1853;
  1999. see references 1 and 2 for Van Vleck's method.
  2000. If the sylvester2 matrix has big dimensions use this version,
  2001. where sylvester2 is used implicitly. If you want to see the final,
  2002. triangularized matrix sylvester2, then use the first version,
  2003. subresultants_vv(p, q, x, 1).
  2004. sylvester1, Sylvester's matrix of 1840, is also used to compute
  2005. one subresultant per remainder; namely, that of the leading
  2006. coefficient, in order to obtain the correct sign and to
  2007. ``force'' the remainder coefficients to become subresultants.
  2008. If the subresultant prs is complete, then it coincides with the
  2009. Euclidean sequence of the polynomials p, q.
  2010. References
  2011. ==========
  2012. 1. Akritas, A. G.: ``A new method for computing polynomial greatest
  2013. common divisors and polynomial remainder sequences.''
  2014. Numerische MatheMatik 52, 119-127, 1988.
  2015. 2. Akritas, A. G., G.I. Malaschonok and P.S. Vigklas: ``On a Theorem
  2016. by Van Vleck Regarding Sturm Sequences.''
  2017. Serdica Journal of Computing, 7, No 4, 101-134, 2013.
  2018. 3. Akritas, A. G.:``Three New Methods for Computing Subresultant
  2019. Polynomial Remainder Sequences (PRS's).'' Serdica Journal of Computing 9(1) (2015), 1-26.
  2020. """
  2021. # make sure neither p nor q is 0
  2022. if p == 0 or q == 0:
  2023. return [p, q]
  2024. # make sure proper degrees
  2025. f, g = p, q
  2026. n = deg_f = degree(f, x)
  2027. m = deg_g = degree(g, x)
  2028. if n == 0 and m == 0:
  2029. return [f, g]
  2030. if n < m:
  2031. n, m, deg_f, deg_g, f, g = m, n, deg_g, deg_f, g, f
  2032. if n > 0 and m == 0:
  2033. return [f, g]
  2034. # initialize
  2035. s1 = sylvester(f, g, x, 1)
  2036. sr_list = [f, g] # subresultant list
  2037. col_num = 2 * n # columns in sylvester2
  2038. # make two rows (row0, row1) of poly coefficients
  2039. row0 = Poly(f, x, domain = QQ).all_coeffs()
  2040. leng0 = len(row0)
  2041. for i in range(col_num - leng0):
  2042. row0.append(0)
  2043. row0 = Matrix([row0])
  2044. row1 = Poly(g,x, domain = QQ).all_coeffs()
  2045. leng1 = len(row1)
  2046. for i in range(col_num - leng1):
  2047. row1.append(0)
  2048. row1 = Matrix([row1])
  2049. # main loop
  2050. while deg_g > 0:
  2051. # create a small matrix M, and triangularize it
  2052. M = create_ma(deg_f, deg_g, row1, row0, col_num)
  2053. for i in range(deg_f - deg_g + 1):
  2054. M1 = pivot(M, i, i)
  2055. M = M1[:, :]
  2056. # treat last row of M as poly; find its degree
  2057. d = find_degree(M, deg_f)
  2058. if d is None:
  2059. return sr_list
  2060. exp_deg = deg_g - 1
  2061. # evaluate one determinant & make coefficients subresultants
  2062. sign_value = correct_sign(n, m, s1, exp_deg, exp_deg - d)
  2063. poly = row2poly(M[M.rows - 1, :], d, x)
  2064. poly = simplify((poly / LC(poly, x)) * sign_value)
  2065. # append poly with subresultant coeffs
  2066. sr_list.append(poly)
  2067. # update degrees and rows
  2068. deg_f, deg_g = deg_g, d
  2069. row0 = row1
  2070. row1 = Poly(poly, x, domain = QQ).all_coeffs()
  2071. leng1 = len(row1)
  2072. for i in range(col_num - leng1):
  2073. row1.append(0)
  2074. row1 = Matrix([row1])
  2075. return sr_list