rootisolation.py 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197
  1. """Real and complex root isolation and refinement algorithms. """
  2. from sympy.polys.densearith import (
  3. dup_neg, dup_rshift, dup_rem,
  4. dup_l2_norm_squared)
  5. from sympy.polys.densebasic import (
  6. dup_LC, dup_TC, dup_degree,
  7. dup_strip, dup_reverse,
  8. dup_convert,
  9. dup_terms_gcd)
  10. from sympy.polys.densetools import (
  11. dup_clear_denoms,
  12. dup_mirror, dup_scale, dup_shift,
  13. dup_transform,
  14. dup_diff,
  15. dup_eval, dmp_eval_in,
  16. dup_sign_variations,
  17. dup_real_imag)
  18. from sympy.polys.euclidtools import (
  19. dup_discriminant)
  20. from sympy.polys.factortools import (
  21. dup_factor_list)
  22. from sympy.polys.polyerrors import (
  23. RefinementFailed,
  24. DomainError,
  25. PolynomialError)
  26. from sympy.polys.sqfreetools import (
  27. dup_sqf_part, dup_sqf_list)
  28. def dup_sturm(f, K):
  29. """
  30. Computes the Sturm sequence of ``f`` in ``F[x]``.
  31. Given a univariate, square-free polynomial ``f(x)`` returns the
  32. associated Sturm sequence ``f_0(x), ..., f_n(x)`` defined by::
  33. f_0(x), f_1(x) = f(x), f'(x)
  34. f_n = -rem(f_{n-2}(x), f_{n-1}(x))
  35. Examples
  36. ========
  37. >>> from sympy.polys import ring, QQ
  38. >>> R, x = ring("x", QQ)
  39. >>> R.dup_sturm(x**3 - 2*x**2 + x - 3)
  40. [x**3 - 2*x**2 + x - 3, 3*x**2 - 4*x + 1, 2/9*x + 25/9, -2079/4]
  41. References
  42. ==========
  43. .. [1] [Davenport88]_
  44. """
  45. if not K.is_Field:
  46. raise DomainError("Cannot compute Sturm sequence over %s" % K)
  47. f = dup_sqf_part(f, K)
  48. sturm = [f, dup_diff(f, 1, K)]
  49. while sturm[-1]:
  50. s = dup_rem(sturm[-2], sturm[-1], K)
  51. sturm.append(dup_neg(s, K))
  52. return sturm[:-1]
  53. def dup_root_upper_bound(f, K):
  54. """Compute the LMQ upper bound for the positive roots of `f`;
  55. LMQ (Local Max Quadratic) was developed by Akritas-Strzebonski-Vigklas.
  56. References
  57. ==========
  58. .. [1] Alkiviadis G. Akritas: "Linear and Quadratic Complexity Bounds on the
  59. Values of the Positive Roots of Polynomials"
  60. Journal of Universal Computer Science, Vol. 15, No. 3, 523-537, 2009.
  61. """
  62. n, P = len(f), []
  63. t = n * [K.one]
  64. if dup_LC(f, K) < 0:
  65. f = dup_neg(f, K)
  66. f = list(reversed(f))
  67. for i in range(0, n):
  68. if f[i] >= 0:
  69. continue
  70. a, QL = K.log(-f[i], 2), []
  71. for j in range(i + 1, n):
  72. if f[j] <= 0:
  73. continue
  74. q = t[j] + a - K.log(f[j], 2)
  75. QL.append([q // (j - i), j])
  76. if not QL:
  77. continue
  78. q = min(QL)
  79. t[q[1]] = t[q[1]] + 1
  80. P.append(q[0])
  81. if not P:
  82. return None
  83. else:
  84. return K.get_field()(2)**(max(P) + 1)
  85. def dup_root_lower_bound(f, K):
  86. """Compute the LMQ lower bound for the positive roots of `f`;
  87. LMQ (Local Max Quadratic) was developed by Akritas-Strzebonski-Vigklas.
  88. References
  89. ==========
  90. .. [1] Alkiviadis G. Akritas: "Linear and Quadratic Complexity Bounds on the
  91. Values of the Positive Roots of Polynomials"
  92. Journal of Universal Computer Science, Vol. 15, No. 3, 523-537, 2009.
  93. """
  94. bound = dup_root_upper_bound(dup_reverse(f), K)
  95. if bound is not None:
  96. return 1/bound
  97. else:
  98. return None
  99. def dup_cauchy_upper_bound(f, K):
  100. """
  101. Compute the Cauchy upper bound on the absolute value of all roots of f,
  102. real or complex.
  103. References
  104. ==========
  105. .. [1] https://en.wikipedia.org/wiki/Geometrical_properties_of_polynomial_roots#Lagrange's_and_Cauchy's_bounds
  106. """
  107. n = dup_degree(f)
  108. if n < 1:
  109. raise PolynomialError('Polynomial has no roots.')
  110. if K.is_ZZ:
  111. L = K.get_field()
  112. f, K = dup_convert(f, K, L), L
  113. elif not K.is_QQ or K.is_RR or K.is_CC:
  114. # We need to compute absolute value, and we are not supporting cases
  115. # where this would take us outside the domain (or its quotient field).
  116. raise DomainError('Cauchy bound not supported over %s' % K)
  117. else:
  118. f = f[:]
  119. while K.is_zero(f[-1]):
  120. f.pop()
  121. if len(f) == 1:
  122. # Monomial. All roots are zero.
  123. return K.zero
  124. lc = f[0]
  125. return K.one + max(abs(n / lc) for n in f[1:])
  126. def dup_cauchy_lower_bound(f, K):
  127. """Compute the Cauchy lower bound on the absolute value of all non-zero
  128. roots of f, real or complex."""
  129. g = dup_reverse(f)
  130. if len(g) < 2:
  131. raise PolynomialError('Polynomial has no non-zero roots.')
  132. if K.is_ZZ:
  133. K = K.get_field()
  134. b = dup_cauchy_upper_bound(g, K)
  135. return K.one / b
  136. def dup_mignotte_sep_bound_squared(f, K):
  137. """
  138. Return the square of the Mignotte lower bound on separation between
  139. distinct roots of f. The square is returned so that the bound lies in
  140. K or its quotient field.
  141. References
  142. ==========
  143. .. [1] Mignotte, Maurice. "Some useful bounds." Computer algebra.
  144. Springer, Vienna, 1982. 259-263.
  145. http://people.dm.unipi.it/gianni/AC-EAG/Mignotte.pdf
  146. """
  147. n = dup_degree(f)
  148. if n < 2:
  149. raise PolynomialError('Polynomials of degree < 2 have no distinct roots.')
  150. if K.is_ZZ:
  151. L = K.get_field()
  152. f, K = dup_convert(f, K, L), L
  153. elif not K.is_QQ or K.is_RR or K.is_CC:
  154. # We need to compute absolute value, and we are not supporting cases
  155. # where this would take us outside the domain (or its quotient field).
  156. raise DomainError('Mignotte bound not supported over %s' % K)
  157. D = dup_discriminant(f, K)
  158. l2sq = dup_l2_norm_squared(f, K)
  159. return K(3)*K.abs(D) / ( K(n)**(n+1) * l2sq**(n-1) )
  160. def _mobius_from_interval(I, field):
  161. """Convert an open interval to a Mobius transform. """
  162. s, t = I
  163. a, c = field.numer(s), field.denom(s)
  164. b, d = field.numer(t), field.denom(t)
  165. return a, b, c, d
  166. def _mobius_to_interval(M, field):
  167. """Convert a Mobius transform to an open interval. """
  168. a, b, c, d = M
  169. s, t = field(a, c), field(b, d)
  170. if s <= t:
  171. return (s, t)
  172. else:
  173. return (t, s)
  174. def dup_step_refine_real_root(f, M, K, fast=False):
  175. """One step of positive real root refinement algorithm. """
  176. a, b, c, d = M
  177. if a == b and c == d:
  178. return f, (a, b, c, d)
  179. A = dup_root_lower_bound(f, K)
  180. if A is not None:
  181. A = K(int(A))
  182. else:
  183. A = K.zero
  184. if fast and A > 16:
  185. f = dup_scale(f, A, K)
  186. a, c, A = A*a, A*c, K.one
  187. if A >= K.one:
  188. f = dup_shift(f, A, K)
  189. b, d = A*a + b, A*c + d
  190. if not dup_eval(f, K.zero, K):
  191. return f, (b, b, d, d)
  192. f, g = dup_shift(f, K.one, K), f
  193. a1, b1, c1, d1 = a, a + b, c, c + d
  194. if not dup_eval(f, K.zero, K):
  195. return f, (b1, b1, d1, d1)
  196. k = dup_sign_variations(f, K)
  197. if k == 1:
  198. a, b, c, d = a1, b1, c1, d1
  199. else:
  200. f = dup_shift(dup_reverse(g), K.one, K)
  201. if not dup_eval(f, K.zero, K):
  202. f = dup_rshift(f, 1, K)
  203. a, b, c, d = b, a + b, d, c + d
  204. return f, (a, b, c, d)
  205. def dup_inner_refine_real_root(f, M, K, eps=None, steps=None, disjoint=None, fast=False, mobius=False):
  206. """Refine a positive root of `f` given a Mobius transform or an interval. """
  207. F = K.get_field()
  208. if len(M) == 2:
  209. a, b, c, d = _mobius_from_interval(M, F)
  210. else:
  211. a, b, c, d = M
  212. while not c:
  213. f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c,
  214. d), K, fast=fast)
  215. if eps is not None and steps is not None:
  216. for i in range(0, steps):
  217. if abs(F(a, c) - F(b, d)) >= eps:
  218. f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
  219. else:
  220. break
  221. else:
  222. if eps is not None:
  223. while abs(F(a, c) - F(b, d)) >= eps:
  224. f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
  225. if steps is not None:
  226. for i in range(0, steps):
  227. f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
  228. if disjoint is not None:
  229. while True:
  230. u, v = _mobius_to_interval((a, b, c, d), F)
  231. if v <= disjoint or disjoint <= u:
  232. break
  233. else:
  234. f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
  235. if not mobius:
  236. return _mobius_to_interval((a, b, c, d), F)
  237. else:
  238. return f, (a, b, c, d)
  239. def dup_outer_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False):
  240. """Refine a positive root of `f` given an interval `(s, t)`. """
  241. a, b, c, d = _mobius_from_interval((s, t), K.get_field())
  242. f = dup_transform(f, dup_strip([a, b]),
  243. dup_strip([c, d]), K)
  244. if dup_sign_variations(f, K) != 1:
  245. raise RefinementFailed("there should be exactly one root in (%s, %s) interval" % (s, t))
  246. return dup_inner_refine_real_root(f, (a, b, c, d), K, eps=eps, steps=steps, disjoint=disjoint, fast=fast)
  247. def dup_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False):
  248. """Refine real root's approximating interval to the given precision. """
  249. if K.is_QQ:
  250. (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring()
  251. elif not K.is_ZZ:
  252. raise DomainError("real root refinement not supported over %s" % K)
  253. if s == t:
  254. return (s, t)
  255. if s > t:
  256. s, t = t, s
  257. negative = False
  258. if s < 0:
  259. if t <= 0:
  260. f, s, t, negative = dup_mirror(f, K), -t, -s, True
  261. else:
  262. raise ValueError("Cannot refine a real root in (%s, %s)" % (s, t))
  263. if negative and disjoint is not None:
  264. if disjoint < 0:
  265. disjoint = -disjoint
  266. else:
  267. disjoint = None
  268. s, t = dup_outer_refine_real_root(
  269. f, s, t, K, eps=eps, steps=steps, disjoint=disjoint, fast=fast)
  270. if negative:
  271. return (-t, -s)
  272. else:
  273. return ( s, t)
  274. def dup_inner_isolate_real_roots(f, K, eps=None, fast=False):
  275. """Internal function for isolation positive roots up to given precision.
  276. References
  277. ==========
  278. 1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root
  279. Isolation Methods . Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
  280. 2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the
  281. Performance of the Continued Fractions Method Using new Bounds of Positive Roots. Nonlinear
  282. Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
  283. """
  284. a, b, c, d = K.one, K.zero, K.zero, K.one
  285. k = dup_sign_variations(f, K)
  286. if k == 0:
  287. return []
  288. if k == 1:
  289. roots = [dup_inner_refine_real_root(
  290. f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True)]
  291. else:
  292. roots, stack = [], [(a, b, c, d, f, k)]
  293. while stack:
  294. a, b, c, d, f, k = stack.pop()
  295. A = dup_root_lower_bound(f, K)
  296. if A is not None:
  297. A = K(int(A))
  298. else:
  299. A = K.zero
  300. if fast and A > 16:
  301. f = dup_scale(f, A, K)
  302. a, c, A = A*a, A*c, K.one
  303. if A >= K.one:
  304. f = dup_shift(f, A, K)
  305. b, d = A*a + b, A*c + d
  306. if not dup_TC(f, K):
  307. roots.append((f, (b, b, d, d)))
  308. f = dup_rshift(f, 1, K)
  309. k = dup_sign_variations(f, K)
  310. if k == 0:
  311. continue
  312. if k == 1:
  313. roots.append(dup_inner_refine_real_root(
  314. f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True))
  315. continue
  316. f1 = dup_shift(f, K.one, K)
  317. a1, b1, c1, d1, r = a, a + b, c, c + d, 0
  318. if not dup_TC(f1, K):
  319. roots.append((f1, (b1, b1, d1, d1)))
  320. f1, r = dup_rshift(f1, 1, K), 1
  321. k1 = dup_sign_variations(f1, K)
  322. k2 = k - k1 - r
  323. a2, b2, c2, d2 = b, a + b, d, c + d
  324. if k2 > 1:
  325. f2 = dup_shift(dup_reverse(f), K.one, K)
  326. if not dup_TC(f2, K):
  327. f2 = dup_rshift(f2, 1, K)
  328. k2 = dup_sign_variations(f2, K)
  329. else:
  330. f2 = None
  331. if k1 < k2:
  332. a1, a2, b1, b2 = a2, a1, b2, b1
  333. c1, c2, d1, d2 = c2, c1, d2, d1
  334. f1, f2, k1, k2 = f2, f1, k2, k1
  335. if not k1:
  336. continue
  337. if f1 is None:
  338. f1 = dup_shift(dup_reverse(f), K.one, K)
  339. if not dup_TC(f1, K):
  340. f1 = dup_rshift(f1, 1, K)
  341. if k1 == 1:
  342. roots.append(dup_inner_refine_real_root(
  343. f1, (a1, b1, c1, d1), K, eps=eps, fast=fast, mobius=True))
  344. else:
  345. stack.append((a1, b1, c1, d1, f1, k1))
  346. if not k2:
  347. continue
  348. if f2 is None:
  349. f2 = dup_shift(dup_reverse(f), K.one, K)
  350. if not dup_TC(f2, K):
  351. f2 = dup_rshift(f2, 1, K)
  352. if k2 == 1:
  353. roots.append(dup_inner_refine_real_root(
  354. f2, (a2, b2, c2, d2), K, eps=eps, fast=fast, mobius=True))
  355. else:
  356. stack.append((a2, b2, c2, d2, f2, k2))
  357. return roots
  358. def _discard_if_outside_interval(f, M, inf, sup, K, negative, fast, mobius):
  359. """Discard an isolating interval if outside ``(inf, sup)``. """
  360. F = K.get_field()
  361. while True:
  362. u, v = _mobius_to_interval(M, F)
  363. if negative:
  364. u, v = -v, -u
  365. if (inf is None or u >= inf) and (sup is None or v <= sup):
  366. if not mobius:
  367. return u, v
  368. else:
  369. return f, M
  370. elif (sup is not None and u > sup) or (inf is not None and v < inf):
  371. return None
  372. else:
  373. f, M = dup_step_refine_real_root(f, M, K, fast=fast)
  374. def dup_inner_isolate_positive_roots(f, K, eps=None, inf=None, sup=None, fast=False, mobius=False):
  375. """Iteratively compute disjoint positive root isolation intervals. """
  376. if sup is not None and sup < 0:
  377. return []
  378. roots = dup_inner_isolate_real_roots(f, K, eps=eps, fast=fast)
  379. F, results = K.get_field(), []
  380. if inf is not None or sup is not None:
  381. for f, M in roots:
  382. result = _discard_if_outside_interval(f, M, inf, sup, K, False, fast, mobius)
  383. if result is not None:
  384. results.append(result)
  385. elif not mobius:
  386. for f, M in roots:
  387. u, v = _mobius_to_interval(M, F)
  388. results.append((u, v))
  389. else:
  390. results = roots
  391. return results
  392. def dup_inner_isolate_negative_roots(f, K, inf=None, sup=None, eps=None, fast=False, mobius=False):
  393. """Iteratively compute disjoint negative root isolation intervals. """
  394. if inf is not None and inf >= 0:
  395. return []
  396. roots = dup_inner_isolate_real_roots(dup_mirror(f, K), K, eps=eps, fast=fast)
  397. F, results = K.get_field(), []
  398. if inf is not None or sup is not None:
  399. for f, M in roots:
  400. result = _discard_if_outside_interval(f, M, inf, sup, K, True, fast, mobius)
  401. if result is not None:
  402. results.append(result)
  403. elif not mobius:
  404. for f, M in roots:
  405. u, v = _mobius_to_interval(M, F)
  406. results.append((-v, -u))
  407. else:
  408. results = roots
  409. return results
  410. def _isolate_zero(f, K, inf, sup, basis=False, sqf=False):
  411. """Handle special case of CF algorithm when ``f`` is homogeneous. """
  412. j, f = dup_terms_gcd(f, K)
  413. if j > 0:
  414. F = K.get_field()
  415. if (inf is None or inf <= 0) and (sup is None or 0 <= sup):
  416. if not sqf:
  417. if not basis:
  418. return [((F.zero, F.zero), j)], f
  419. else:
  420. return [((F.zero, F.zero), j, [K.one, K.zero])], f
  421. else:
  422. return [(F.zero, F.zero)], f
  423. return [], f
  424. def dup_isolate_real_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False):
  425. """Isolate real roots of a square-free polynomial using the Vincent-Akritas-Strzebonski (VAS) CF approach.
  426. References
  427. ==========
  428. .. [1] Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative
  429. Study of Two Real Root Isolation Methods. Nonlinear Analysis:
  430. Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
  431. .. [2] Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S.
  432. Vigklas: Improving the Performance of the Continued Fractions
  433. Method Using New Bounds of Positive Roots. Nonlinear Analysis:
  434. Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
  435. """
  436. if K.is_QQ:
  437. (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring()
  438. elif not K.is_ZZ:
  439. raise DomainError("isolation of real roots not supported over %s" % K)
  440. if dup_degree(f) <= 0:
  441. return []
  442. I_zero, f = _isolate_zero(f, K, inf, sup, basis=False, sqf=True)
  443. I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
  444. I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
  445. roots = sorted(I_neg + I_zero + I_pos)
  446. if not blackbox:
  447. return roots
  448. else:
  449. return [ RealInterval((a, b), f, K) for (a, b) in roots ]
  450. def dup_isolate_real_roots(f, K, eps=None, inf=None, sup=None, basis=False, fast=False):
  451. """Isolate real roots using Vincent-Akritas-Strzebonski (VAS) continued fractions approach.
  452. References
  453. ==========
  454. .. [1] Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative
  455. Study of Two Real Root Isolation Methods. Nonlinear Analysis:
  456. Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
  457. .. [2] Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S.
  458. Vigklas: Improving the Performance of the Continued Fractions
  459. Method Using New Bounds of Positive Roots.
  460. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
  461. """
  462. if K.is_QQ:
  463. (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring()
  464. elif not K.is_ZZ:
  465. raise DomainError("isolation of real roots not supported over %s" % K)
  466. if dup_degree(f) <= 0:
  467. return []
  468. I_zero, f = _isolate_zero(f, K, inf, sup, basis=basis, sqf=False)
  469. _, factors = dup_sqf_list(f, K)
  470. if len(factors) == 1:
  471. ((f, k),) = factors
  472. I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
  473. I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
  474. I_neg = [ ((u, v), k) for u, v in I_neg ]
  475. I_pos = [ ((u, v), k) for u, v in I_pos ]
  476. else:
  477. I_neg, I_pos = _real_isolate_and_disjoin(factors, K,
  478. eps=eps, inf=inf, sup=sup, basis=basis, fast=fast)
  479. return sorted(I_neg + I_zero + I_pos)
  480. def dup_isolate_real_roots_list(polys, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False):
  481. """Isolate real roots of a list of square-free polynomial using Vincent-Akritas-Strzebonski (VAS) CF approach.
  482. References
  483. ==========
  484. .. [1] Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative
  485. Study of Two Real Root Isolation Methods. Nonlinear Analysis:
  486. Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
  487. .. [2] Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S.
  488. Vigklas: Improving the Performance of the Continued Fractions
  489. Method Using New Bounds of Positive Roots.
  490. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
  491. """
  492. if K.is_QQ:
  493. K, F, polys = K.get_ring(), K, polys[:]
  494. for i, p in enumerate(polys):
  495. polys[i] = dup_clear_denoms(p, F, K, convert=True)[1]
  496. elif not K.is_ZZ:
  497. raise DomainError("isolation of real roots not supported over %s" % K)
  498. zeros, factors_dict = False, {}
  499. if (inf is None or inf <= 0) and (sup is None or 0 <= sup):
  500. zeros, zero_indices = True, {}
  501. for i, p in enumerate(polys):
  502. j, p = dup_terms_gcd(p, K)
  503. if zeros and j > 0:
  504. zero_indices[i] = j
  505. for f, k in dup_factor_list(p, K)[1]:
  506. f = tuple(f)
  507. if f not in factors_dict:
  508. factors_dict[f] = {i: k}
  509. else:
  510. factors_dict[f][i] = k
  511. factors_list = []
  512. for f, indices in factors_dict.items():
  513. factors_list.append((list(f), indices))
  514. I_neg, I_pos = _real_isolate_and_disjoin(factors_list, K, eps=eps,
  515. inf=inf, sup=sup, strict=strict, basis=basis, fast=fast)
  516. F = K.get_field()
  517. if not zeros or not zero_indices:
  518. I_zero = []
  519. else:
  520. if not basis:
  521. I_zero = [((F.zero, F.zero), zero_indices)]
  522. else:
  523. I_zero = [((F.zero, F.zero), zero_indices, [K.one, K.zero])]
  524. return sorted(I_neg + I_zero + I_pos)
  525. def _disjoint_p(M, N, strict=False):
  526. """Check if Mobius transforms define disjoint intervals. """
  527. a1, b1, c1, d1 = M
  528. a2, b2, c2, d2 = N
  529. a1d1, b1c1 = a1*d1, b1*c1
  530. a2d2, b2c2 = a2*d2, b2*c2
  531. if a1d1 == b1c1 and a2d2 == b2c2:
  532. return True
  533. if a1d1 > b1c1:
  534. a1, c1, b1, d1 = b1, d1, a1, c1
  535. if a2d2 > b2c2:
  536. a2, c2, b2, d2 = b2, d2, a2, c2
  537. if not strict:
  538. return a2*d1 >= c2*b1 or b2*c1 <= d2*a1
  539. else:
  540. return a2*d1 > c2*b1 or b2*c1 < d2*a1
  541. def _real_isolate_and_disjoin(factors, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False):
  542. """Isolate real roots of a list of polynomials and disjoin intervals. """
  543. I_pos, I_neg = [], []
  544. for i, (f, k) in enumerate(factors):
  545. for F, M in dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True):
  546. I_pos.append((F, M, k, f))
  547. for G, N in dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True):
  548. I_neg.append((G, N, k, f))
  549. for i, (f, M, k, F) in enumerate(I_pos):
  550. for j, (g, N, m, G) in enumerate(I_pos[i + 1:]):
  551. while not _disjoint_p(M, N, strict=strict):
  552. f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True)
  553. g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True)
  554. I_pos[i + j + 1] = (g, N, m, G)
  555. I_pos[i] = (f, M, k, F)
  556. for i, (f, M, k, F) in enumerate(I_neg):
  557. for j, (g, N, m, G) in enumerate(I_neg[i + 1:]):
  558. while not _disjoint_p(M, N, strict=strict):
  559. f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True)
  560. g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True)
  561. I_neg[i + j + 1] = (g, N, m, G)
  562. I_neg[i] = (f, M, k, F)
  563. if strict:
  564. for i, (f, M, k, F) in enumerate(I_neg):
  565. if not M[0]:
  566. while not M[0]:
  567. f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True)
  568. I_neg[i] = (f, M, k, F)
  569. break
  570. for j, (g, N, m, G) in enumerate(I_pos):
  571. if not N[0]:
  572. while not N[0]:
  573. g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True)
  574. I_pos[j] = (g, N, m, G)
  575. break
  576. field = K.get_field()
  577. I_neg = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_neg ]
  578. I_pos = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_pos ]
  579. if not basis:
  580. I_neg = [ ((-v, -u), k) for ((u, v), k, _) in I_neg ]
  581. I_pos = [ (( u, v), k) for ((u, v), k, _) in I_pos ]
  582. else:
  583. I_neg = [ ((-v, -u), k, f) for ((u, v), k, f) in I_neg ]
  584. I_pos = [ (( u, v), k, f) for ((u, v), k, f) in I_pos ]
  585. return I_neg, I_pos
  586. def dup_count_real_roots(f, K, inf=None, sup=None):
  587. """Returns the number of distinct real roots of ``f`` in ``[inf, sup]``. """
  588. if dup_degree(f) <= 0:
  589. return 0
  590. if not K.is_Field:
  591. R, K = K, K.get_field()
  592. f = dup_convert(f, R, K)
  593. sturm = dup_sturm(f, K)
  594. if inf is None:
  595. signs_inf = dup_sign_variations([ dup_LC(s, K)*(-1)**dup_degree(s) for s in sturm ], K)
  596. else:
  597. signs_inf = dup_sign_variations([ dup_eval(s, inf, K) for s in sturm ], K)
  598. if sup is None:
  599. signs_sup = dup_sign_variations([ dup_LC(s, K) for s in sturm ], K)
  600. else:
  601. signs_sup = dup_sign_variations([ dup_eval(s, sup, K) for s in sturm ], K)
  602. count = abs(signs_inf - signs_sup)
  603. if inf is not None and not dup_eval(f, inf, K):
  604. count += 1
  605. return count
  606. OO = 'OO' # Origin of (re, im) coordinate system
  607. Q1 = 'Q1' # Quadrant #1 (++): re > 0 and im > 0
  608. Q2 = 'Q2' # Quadrant #2 (-+): re < 0 and im > 0
  609. Q3 = 'Q3' # Quadrant #3 (--): re < 0 and im < 0
  610. Q4 = 'Q4' # Quadrant #4 (+-): re > 0 and im < 0
  611. A1 = 'A1' # Axis #1 (+0): re > 0 and im = 0
  612. A2 = 'A2' # Axis #2 (0+): re = 0 and im > 0
  613. A3 = 'A3' # Axis #3 (-0): re < 0 and im = 0
  614. A4 = 'A4' # Axis #4 (0-): re = 0 and im < 0
  615. _rules_simple = {
  616. # Q --> Q (same) => no change
  617. (Q1, Q1): 0,
  618. (Q2, Q2): 0,
  619. (Q3, Q3): 0,
  620. (Q4, Q4): 0,
  621. # A -- CCW --> Q => +1/4 (CCW)
  622. (A1, Q1): 1,
  623. (A2, Q2): 1,
  624. (A3, Q3): 1,
  625. (A4, Q4): 1,
  626. # A -- CW --> Q => -1/4 (CCW)
  627. (A1, Q4): 2,
  628. (A2, Q1): 2,
  629. (A3, Q2): 2,
  630. (A4, Q3): 2,
  631. # Q -- CCW --> A => +1/4 (CCW)
  632. (Q1, A2): 3,
  633. (Q2, A3): 3,
  634. (Q3, A4): 3,
  635. (Q4, A1): 3,
  636. # Q -- CW --> A => -1/4 (CCW)
  637. (Q1, A1): 4,
  638. (Q2, A2): 4,
  639. (Q3, A3): 4,
  640. (Q4, A4): 4,
  641. # Q -- CCW --> Q => +1/2 (CCW)
  642. (Q1, Q2): +5,
  643. (Q2, Q3): +5,
  644. (Q3, Q4): +5,
  645. (Q4, Q1): +5,
  646. # Q -- CW --> Q => -1/2 (CW)
  647. (Q1, Q4): -5,
  648. (Q2, Q1): -5,
  649. (Q3, Q2): -5,
  650. (Q4, Q3): -5,
  651. }
  652. _rules_ambiguous = {
  653. # A -- CCW --> Q => { +1/4 (CCW), -9/4 (CW) }
  654. (A1, OO, Q1): -1,
  655. (A2, OO, Q2): -1,
  656. (A3, OO, Q3): -1,
  657. (A4, OO, Q4): -1,
  658. # A -- CW --> Q => { -1/4 (CCW), +7/4 (CW) }
  659. (A1, OO, Q4): -2,
  660. (A2, OO, Q1): -2,
  661. (A3, OO, Q2): -2,
  662. (A4, OO, Q3): -2,
  663. # Q -- CCW --> A => { +1/4 (CCW), -9/4 (CW) }
  664. (Q1, OO, A2): -3,
  665. (Q2, OO, A3): -3,
  666. (Q3, OO, A4): -3,
  667. (Q4, OO, A1): -3,
  668. # Q -- CW --> A => { -1/4 (CCW), +7/4 (CW) }
  669. (Q1, OO, A1): -4,
  670. (Q2, OO, A2): -4,
  671. (Q3, OO, A3): -4,
  672. (Q4, OO, A4): -4,
  673. # A -- OO --> A => { +1 (CCW), -1 (CW) }
  674. (A1, A3): 7,
  675. (A2, A4): 7,
  676. (A3, A1): 7,
  677. (A4, A2): 7,
  678. (A1, OO, A3): 7,
  679. (A2, OO, A4): 7,
  680. (A3, OO, A1): 7,
  681. (A4, OO, A2): 7,
  682. # Q -- DIA --> Q => { +1 (CCW), -1 (CW) }
  683. (Q1, Q3): 8,
  684. (Q2, Q4): 8,
  685. (Q3, Q1): 8,
  686. (Q4, Q2): 8,
  687. (Q1, OO, Q3): 8,
  688. (Q2, OO, Q4): 8,
  689. (Q3, OO, Q1): 8,
  690. (Q4, OO, Q2): 8,
  691. # A --- R ---> A => { +1/2 (CCW), -3/2 (CW) }
  692. (A1, A2): 9,
  693. (A2, A3): 9,
  694. (A3, A4): 9,
  695. (A4, A1): 9,
  696. (A1, OO, A2): 9,
  697. (A2, OO, A3): 9,
  698. (A3, OO, A4): 9,
  699. (A4, OO, A1): 9,
  700. # A --- L ---> A => { +3/2 (CCW), -1/2 (CW) }
  701. (A1, A4): 10,
  702. (A2, A1): 10,
  703. (A3, A2): 10,
  704. (A4, A3): 10,
  705. (A1, OO, A4): 10,
  706. (A2, OO, A1): 10,
  707. (A3, OO, A2): 10,
  708. (A4, OO, A3): 10,
  709. # Q --- 1 ---> A => { +3/4 (CCW), -5/4 (CW) }
  710. (Q1, A3): 11,
  711. (Q2, A4): 11,
  712. (Q3, A1): 11,
  713. (Q4, A2): 11,
  714. (Q1, OO, A3): 11,
  715. (Q2, OO, A4): 11,
  716. (Q3, OO, A1): 11,
  717. (Q4, OO, A2): 11,
  718. # Q --- 2 ---> A => { +5/4 (CCW), -3/4 (CW) }
  719. (Q1, A4): 12,
  720. (Q2, A1): 12,
  721. (Q3, A2): 12,
  722. (Q4, A3): 12,
  723. (Q1, OO, A4): 12,
  724. (Q2, OO, A1): 12,
  725. (Q3, OO, A2): 12,
  726. (Q4, OO, A3): 12,
  727. # A --- 1 ---> Q => { +5/4 (CCW), -3/4 (CW) }
  728. (A1, Q3): 13,
  729. (A2, Q4): 13,
  730. (A3, Q1): 13,
  731. (A4, Q2): 13,
  732. (A1, OO, Q3): 13,
  733. (A2, OO, Q4): 13,
  734. (A3, OO, Q1): 13,
  735. (A4, OO, Q2): 13,
  736. # A --- 2 ---> Q => { +3/4 (CCW), -5/4 (CW) }
  737. (A1, Q2): 14,
  738. (A2, Q3): 14,
  739. (A3, Q4): 14,
  740. (A4, Q1): 14,
  741. (A1, OO, Q2): 14,
  742. (A2, OO, Q3): 14,
  743. (A3, OO, Q4): 14,
  744. (A4, OO, Q1): 14,
  745. # Q --> OO --> Q => { +1/2 (CCW), -3/2 (CW) }
  746. (Q1, OO, Q2): 15,
  747. (Q2, OO, Q3): 15,
  748. (Q3, OO, Q4): 15,
  749. (Q4, OO, Q1): 15,
  750. # Q --> OO --> Q => { +3/2 (CCW), -1/2 (CW) }
  751. (Q1, OO, Q4): 16,
  752. (Q2, OO, Q1): 16,
  753. (Q3, OO, Q2): 16,
  754. (Q4, OO, Q3): 16,
  755. # A --> OO --> A => { +2 (CCW), 0 (CW) }
  756. (A1, OO, A1): 17,
  757. (A2, OO, A2): 17,
  758. (A3, OO, A3): 17,
  759. (A4, OO, A4): 17,
  760. # Q --> OO --> Q => { +2 (CCW), 0 (CW) }
  761. (Q1, OO, Q1): 18,
  762. (Q2, OO, Q2): 18,
  763. (Q3, OO, Q3): 18,
  764. (Q4, OO, Q4): 18,
  765. }
  766. _values = {
  767. 0: [( 0, 1)],
  768. 1: [(+1, 4)],
  769. 2: [(-1, 4)],
  770. 3: [(+1, 4)],
  771. 4: [(-1, 4)],
  772. -1: [(+9, 4), (+1, 4)],
  773. -2: [(+7, 4), (-1, 4)],
  774. -3: [(+9, 4), (+1, 4)],
  775. -4: [(+7, 4), (-1, 4)],
  776. +5: [(+1, 2)],
  777. -5: [(-1, 2)],
  778. 7: [(+1, 1), (-1, 1)],
  779. 8: [(+1, 1), (-1, 1)],
  780. 9: [(+1, 2), (-3, 2)],
  781. 10: [(+3, 2), (-1, 2)],
  782. 11: [(+3, 4), (-5, 4)],
  783. 12: [(+5, 4), (-3, 4)],
  784. 13: [(+5, 4), (-3, 4)],
  785. 14: [(+3, 4), (-5, 4)],
  786. 15: [(+1, 2), (-3, 2)],
  787. 16: [(+3, 2), (-1, 2)],
  788. 17: [(+2, 1), ( 0, 1)],
  789. 18: [(+2, 1), ( 0, 1)],
  790. }
  791. def _classify_point(re, im):
  792. """Return the half-axis (or origin) on which (re, im) point is located. """
  793. if not re and not im:
  794. return OO
  795. if not re:
  796. if im > 0:
  797. return A2
  798. else:
  799. return A4
  800. elif not im:
  801. if re > 0:
  802. return A1
  803. else:
  804. return A3
  805. def _intervals_to_quadrants(intervals, f1, f2, s, t, F):
  806. """Generate a sequence of extended quadrants from a list of critical points. """
  807. if not intervals:
  808. return []
  809. Q = []
  810. if not f1:
  811. (a, b), _, _ = intervals[0]
  812. if a == b == s:
  813. if len(intervals) == 1:
  814. if dup_eval(f2, t, F) > 0:
  815. return [OO, A2]
  816. else:
  817. return [OO, A4]
  818. else:
  819. (a, _), _, _ = intervals[1]
  820. if dup_eval(f2, (s + a)/2, F) > 0:
  821. Q.extend([OO, A2])
  822. f2_sgn = +1
  823. else:
  824. Q.extend([OO, A4])
  825. f2_sgn = -1
  826. intervals = intervals[1:]
  827. else:
  828. if dup_eval(f2, s, F) > 0:
  829. Q.append(A2)
  830. f2_sgn = +1
  831. else:
  832. Q.append(A4)
  833. f2_sgn = -1
  834. for (a, _), indices, _ in intervals:
  835. Q.append(OO)
  836. if indices[1] % 2 == 1:
  837. f2_sgn = -f2_sgn
  838. if a != t:
  839. if f2_sgn > 0:
  840. Q.append(A2)
  841. else:
  842. Q.append(A4)
  843. return Q
  844. if not f2:
  845. (a, b), _, _ = intervals[0]
  846. if a == b == s:
  847. if len(intervals) == 1:
  848. if dup_eval(f1, t, F) > 0:
  849. return [OO, A1]
  850. else:
  851. return [OO, A3]
  852. else:
  853. (a, _), _, _ = intervals[1]
  854. if dup_eval(f1, (s + a)/2, F) > 0:
  855. Q.extend([OO, A1])
  856. f1_sgn = +1
  857. else:
  858. Q.extend([OO, A3])
  859. f1_sgn = -1
  860. intervals = intervals[1:]
  861. else:
  862. if dup_eval(f1, s, F) > 0:
  863. Q.append(A1)
  864. f1_sgn = +1
  865. else:
  866. Q.append(A3)
  867. f1_sgn = -1
  868. for (a, _), indices, _ in intervals:
  869. Q.append(OO)
  870. if indices[0] % 2 == 1:
  871. f1_sgn = -f1_sgn
  872. if a != t:
  873. if f1_sgn > 0:
  874. Q.append(A1)
  875. else:
  876. Q.append(A3)
  877. return Q
  878. re = dup_eval(f1, s, F)
  879. im = dup_eval(f2, s, F)
  880. if not re or not im:
  881. Q.append(_classify_point(re, im))
  882. if len(intervals) == 1:
  883. re = dup_eval(f1, t, F)
  884. im = dup_eval(f2, t, F)
  885. else:
  886. (a, _), _, _ = intervals[1]
  887. re = dup_eval(f1, (s + a)/2, F)
  888. im = dup_eval(f2, (s + a)/2, F)
  889. intervals = intervals[1:]
  890. if re > 0:
  891. f1_sgn = +1
  892. else:
  893. f1_sgn = -1
  894. if im > 0:
  895. f2_sgn = +1
  896. else:
  897. f2_sgn = -1
  898. sgn = {
  899. (+1, +1): Q1,
  900. (-1, +1): Q2,
  901. (-1, -1): Q3,
  902. (+1, -1): Q4,
  903. }
  904. Q.append(sgn[(f1_sgn, f2_sgn)])
  905. for (a, b), indices, _ in intervals:
  906. if a == b:
  907. re = dup_eval(f1, a, F)
  908. im = dup_eval(f2, a, F)
  909. cls = _classify_point(re, im)
  910. if cls is not None:
  911. Q.append(cls)
  912. if 0 in indices:
  913. if indices[0] % 2 == 1:
  914. f1_sgn = -f1_sgn
  915. if 1 in indices:
  916. if indices[1] % 2 == 1:
  917. f2_sgn = -f2_sgn
  918. if not (a == b and b == t):
  919. Q.append(sgn[(f1_sgn, f2_sgn)])
  920. return Q
  921. def _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=None):
  922. """Transform sequences of quadrants to a sequence of rules. """
  923. if exclude is True:
  924. edges = [1, 1, 0, 0]
  925. corners = {
  926. (0, 1): 1,
  927. (1, 2): 1,
  928. (2, 3): 0,
  929. (3, 0): 1,
  930. }
  931. else:
  932. edges = [0, 0, 0, 0]
  933. corners = {
  934. (0, 1): 0,
  935. (1, 2): 0,
  936. (2, 3): 0,
  937. (3, 0): 0,
  938. }
  939. if exclude is not None and exclude is not True:
  940. exclude = set(exclude)
  941. for i, edge in enumerate(['S', 'E', 'N', 'W']):
  942. if edge in exclude:
  943. edges[i] = 1
  944. for i, corner in enumerate(['SW', 'SE', 'NE', 'NW']):
  945. if corner in exclude:
  946. corners[((i - 1) % 4, i)] = 1
  947. QQ, rules = [Q_L1, Q_L2, Q_L3, Q_L4], []
  948. for i, Q in enumerate(QQ):
  949. if not Q:
  950. continue
  951. if Q[-1] == OO:
  952. Q = Q[:-1]
  953. if Q[0] == OO:
  954. j, Q = (i - 1) % 4, Q[1:]
  955. qq = (QQ[j][-2], OO, Q[0])
  956. if qq in _rules_ambiguous:
  957. rules.append((_rules_ambiguous[qq], corners[(j, i)]))
  958. else:
  959. raise NotImplementedError("3 element rule (corner): " + str(qq))
  960. q1, k = Q[0], 1
  961. while k < len(Q):
  962. q2, k = Q[k], k + 1
  963. if q2 != OO:
  964. qq = (q1, q2)
  965. if qq in _rules_simple:
  966. rules.append((_rules_simple[qq], 0))
  967. elif qq in _rules_ambiguous:
  968. rules.append((_rules_ambiguous[qq], edges[i]))
  969. else:
  970. raise NotImplementedError("2 element rule (inside): " + str(qq))
  971. else:
  972. qq, k = (q1, q2, Q[k]), k + 1
  973. if qq in _rules_ambiguous:
  974. rules.append((_rules_ambiguous[qq], edges[i]))
  975. else:
  976. raise NotImplementedError("3 element rule (edge): " + str(qq))
  977. q1 = qq[-1]
  978. return rules
  979. def _reverse_intervals(intervals):
  980. """Reverse intervals for traversal from right to left and from top to bottom. """
  981. return [ ((b, a), indices, f) for (a, b), indices, f in reversed(intervals) ]
  982. def _winding_number(T, field):
  983. """Compute the winding number of the input polynomial, i.e. the number of roots. """
  984. return int(sum([ field(*_values[t][i]) for t, i in T ]) / field(2))
  985. def dup_count_complex_roots(f, K, inf=None, sup=None, exclude=None):
  986. """Count all roots in [u + v*I, s + t*I] rectangle using Collins-Krandick algorithm. """
  987. if not K.is_ZZ and not K.is_QQ:
  988. raise DomainError("complex root counting is not supported over %s" % K)
  989. if K.is_ZZ:
  990. R, F = K, K.get_field()
  991. else:
  992. R, F = K.get_ring(), K
  993. f = dup_convert(f, K, F)
  994. if inf is None or sup is None:
  995. _, lc = dup_degree(f), abs(dup_LC(f, F))
  996. B = 2*max([ F.quo(abs(c), lc) for c in f ])
  997. if inf is None:
  998. (u, v) = (-B, -B)
  999. else:
  1000. (u, v) = inf
  1001. if sup is None:
  1002. (s, t) = (+B, +B)
  1003. else:
  1004. (s, t) = sup
  1005. f1, f2 = dup_real_imag(f, F)
  1006. f1L1F = dmp_eval_in(f1, v, 1, 1, F)
  1007. f2L1F = dmp_eval_in(f2, v, 1, 1, F)
  1008. _, f1L1R = dup_clear_denoms(f1L1F, F, R, convert=True)
  1009. _, f2L1R = dup_clear_denoms(f2L1F, F, R, convert=True)
  1010. f1L2F = dmp_eval_in(f1, s, 0, 1, F)
  1011. f2L2F = dmp_eval_in(f2, s, 0, 1, F)
  1012. _, f1L2R = dup_clear_denoms(f1L2F, F, R, convert=True)
  1013. _, f2L2R = dup_clear_denoms(f2L2F, F, R, convert=True)
  1014. f1L3F = dmp_eval_in(f1, t, 1, 1, F)
  1015. f2L3F = dmp_eval_in(f2, t, 1, 1, F)
  1016. _, f1L3R = dup_clear_denoms(f1L3F, F, R, convert=True)
  1017. _, f2L3R = dup_clear_denoms(f2L3F, F, R, convert=True)
  1018. f1L4F = dmp_eval_in(f1, u, 0, 1, F)
  1019. f2L4F = dmp_eval_in(f2, u, 0, 1, F)
  1020. _, f1L4R = dup_clear_denoms(f1L4F, F, R, convert=True)
  1021. _, f2L4R = dup_clear_denoms(f2L4F, F, R, convert=True)
  1022. S_L1 = [f1L1R, f2L1R]
  1023. S_L2 = [f1L2R, f2L2R]
  1024. S_L3 = [f1L3R, f2L3R]
  1025. S_L4 = [f1L4R, f2L4R]
  1026. I_L1 = dup_isolate_real_roots_list(S_L1, R, inf=u, sup=s, fast=True, basis=True, strict=True)
  1027. I_L2 = dup_isolate_real_roots_list(S_L2, R, inf=v, sup=t, fast=True, basis=True, strict=True)
  1028. I_L3 = dup_isolate_real_roots_list(S_L3, R, inf=u, sup=s, fast=True, basis=True, strict=True)
  1029. I_L4 = dup_isolate_real_roots_list(S_L4, R, inf=v, sup=t, fast=True, basis=True, strict=True)
  1030. I_L3 = _reverse_intervals(I_L3)
  1031. I_L4 = _reverse_intervals(I_L4)
  1032. Q_L1 = _intervals_to_quadrants(I_L1, f1L1F, f2L1F, u, s, F)
  1033. Q_L2 = _intervals_to_quadrants(I_L2, f1L2F, f2L2F, v, t, F)
  1034. Q_L3 = _intervals_to_quadrants(I_L3, f1L3F, f2L3F, s, u, F)
  1035. Q_L4 = _intervals_to_quadrants(I_L4, f1L4F, f2L4F, t, v, F)
  1036. T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=exclude)
  1037. return _winding_number(T, F)
  1038. def _vertical_bisection(N, a, b, I, Q, F1, F2, f1, f2, F):
  1039. """Vertical bisection step in Collins-Krandick root isolation algorithm. """
  1040. (u, v), (s, t) = a, b
  1041. I_L1, I_L2, I_L3, I_L4 = I
  1042. Q_L1, Q_L2, Q_L3, Q_L4 = Q
  1043. f1L1F, f1L2F, f1L3F, f1L4F = F1
  1044. f2L1F, f2L2F, f2L3F, f2L4F = F2
  1045. x = (u + s) / 2
  1046. f1V = dmp_eval_in(f1, x, 0, 1, F)
  1047. f2V = dmp_eval_in(f2, x, 0, 1, F)
  1048. I_V = dup_isolate_real_roots_list([f1V, f2V], F, inf=v, sup=t, fast=True, strict=True, basis=True)
  1049. I_L1_L, I_L1_R = [], []
  1050. I_L2_L, I_L2_R = I_V, I_L2
  1051. I_L3_L, I_L3_R = [], []
  1052. I_L4_L, I_L4_R = I_L4, _reverse_intervals(I_V)
  1053. for I in I_L1:
  1054. (a, b), indices, h = I
  1055. if a == b:
  1056. if a == x:
  1057. I_L1_L.append(I)
  1058. I_L1_R.append(I)
  1059. elif a < x:
  1060. I_L1_L.append(I)
  1061. else:
  1062. I_L1_R.append(I)
  1063. else:
  1064. if b <= x:
  1065. I_L1_L.append(I)
  1066. elif a >= x:
  1067. I_L1_R.append(I)
  1068. else:
  1069. a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True)
  1070. if b <= x:
  1071. I_L1_L.append(((a, b), indices, h))
  1072. if a >= x:
  1073. I_L1_R.append(((a, b), indices, h))
  1074. for I in I_L3:
  1075. (b, a), indices, h = I
  1076. if a == b:
  1077. if a == x:
  1078. I_L3_L.append(I)
  1079. I_L3_R.append(I)
  1080. elif a < x:
  1081. I_L3_L.append(I)
  1082. else:
  1083. I_L3_R.append(I)
  1084. else:
  1085. if b <= x:
  1086. I_L3_L.append(I)
  1087. elif a >= x:
  1088. I_L3_R.append(I)
  1089. else:
  1090. a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True)
  1091. if b <= x:
  1092. I_L3_L.append(((b, a), indices, h))
  1093. if a >= x:
  1094. I_L3_R.append(((b, a), indices, h))
  1095. Q_L1_L = _intervals_to_quadrants(I_L1_L, f1L1F, f2L1F, u, x, F)
  1096. Q_L2_L = _intervals_to_quadrants(I_L2_L, f1V, f2V, v, t, F)
  1097. Q_L3_L = _intervals_to_quadrants(I_L3_L, f1L3F, f2L3F, x, u, F)
  1098. Q_L4_L = Q_L4
  1099. Q_L1_R = _intervals_to_quadrants(I_L1_R, f1L1F, f2L1F, x, s, F)
  1100. Q_L2_R = Q_L2
  1101. Q_L3_R = _intervals_to_quadrants(I_L3_R, f1L3F, f2L3F, s, x, F)
  1102. Q_L4_R = _intervals_to_quadrants(I_L4_R, f1V, f2V, t, v, F)
  1103. T_L = _traverse_quadrants(Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L, exclude=True)
  1104. T_R = _traverse_quadrants(Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R, exclude=True)
  1105. N_L = _winding_number(T_L, F)
  1106. N_R = _winding_number(T_R, F)
  1107. I_L = (I_L1_L, I_L2_L, I_L3_L, I_L4_L)
  1108. Q_L = (Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L)
  1109. I_R = (I_L1_R, I_L2_R, I_L3_R, I_L4_R)
  1110. Q_R = (Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R)
  1111. F1_L = (f1L1F, f1V, f1L3F, f1L4F)
  1112. F2_L = (f2L1F, f2V, f2L3F, f2L4F)
  1113. F1_R = (f1L1F, f1L2F, f1L3F, f1V)
  1114. F2_R = (f2L1F, f2L2F, f2L3F, f2V)
  1115. a, b = (u, v), (x, t)
  1116. c, d = (x, v), (s, t)
  1117. D_L = (N_L, a, b, I_L, Q_L, F1_L, F2_L)
  1118. D_R = (N_R, c, d, I_R, Q_R, F1_R, F2_R)
  1119. return D_L, D_R
  1120. def _horizontal_bisection(N, a, b, I, Q, F1, F2, f1, f2, F):
  1121. """Horizontal bisection step in Collins-Krandick root isolation algorithm. """
  1122. (u, v), (s, t) = a, b
  1123. I_L1, I_L2, I_L3, I_L4 = I
  1124. Q_L1, Q_L2, Q_L3, Q_L4 = Q
  1125. f1L1F, f1L2F, f1L3F, f1L4F = F1
  1126. f2L1F, f2L2F, f2L3F, f2L4F = F2
  1127. y = (v + t) / 2
  1128. f1H = dmp_eval_in(f1, y, 1, 1, F)
  1129. f2H = dmp_eval_in(f2, y, 1, 1, F)
  1130. I_H = dup_isolate_real_roots_list([f1H, f2H], F, inf=u, sup=s, fast=True, strict=True, basis=True)
  1131. I_L1_B, I_L1_U = I_L1, I_H
  1132. I_L2_B, I_L2_U = [], []
  1133. I_L3_B, I_L3_U = _reverse_intervals(I_H), I_L3
  1134. I_L4_B, I_L4_U = [], []
  1135. for I in I_L2:
  1136. (a, b), indices, h = I
  1137. if a == b:
  1138. if a == y:
  1139. I_L2_B.append(I)
  1140. I_L2_U.append(I)
  1141. elif a < y:
  1142. I_L2_B.append(I)
  1143. else:
  1144. I_L2_U.append(I)
  1145. else:
  1146. if b <= y:
  1147. I_L2_B.append(I)
  1148. elif a >= y:
  1149. I_L2_U.append(I)
  1150. else:
  1151. a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True)
  1152. if b <= y:
  1153. I_L2_B.append(((a, b), indices, h))
  1154. if a >= y:
  1155. I_L2_U.append(((a, b), indices, h))
  1156. for I in I_L4:
  1157. (b, a), indices, h = I
  1158. if a == b:
  1159. if a == y:
  1160. I_L4_B.append(I)
  1161. I_L4_U.append(I)
  1162. elif a < y:
  1163. I_L4_B.append(I)
  1164. else:
  1165. I_L4_U.append(I)
  1166. else:
  1167. if b <= y:
  1168. I_L4_B.append(I)
  1169. elif a >= y:
  1170. I_L4_U.append(I)
  1171. else:
  1172. a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True)
  1173. if b <= y:
  1174. I_L4_B.append(((b, a), indices, h))
  1175. if a >= y:
  1176. I_L4_U.append(((b, a), indices, h))
  1177. Q_L1_B = Q_L1
  1178. Q_L2_B = _intervals_to_quadrants(I_L2_B, f1L2F, f2L2F, v, y, F)
  1179. Q_L3_B = _intervals_to_quadrants(I_L3_B, f1H, f2H, s, u, F)
  1180. Q_L4_B = _intervals_to_quadrants(I_L4_B, f1L4F, f2L4F, y, v, F)
  1181. Q_L1_U = _intervals_to_quadrants(I_L1_U, f1H, f2H, u, s, F)
  1182. Q_L2_U = _intervals_to_quadrants(I_L2_U, f1L2F, f2L2F, y, t, F)
  1183. Q_L3_U = Q_L3
  1184. Q_L4_U = _intervals_to_quadrants(I_L4_U, f1L4F, f2L4F, t, y, F)
  1185. T_B = _traverse_quadrants(Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B, exclude=True)
  1186. T_U = _traverse_quadrants(Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U, exclude=True)
  1187. N_B = _winding_number(T_B, F)
  1188. N_U = _winding_number(T_U, F)
  1189. I_B = (I_L1_B, I_L2_B, I_L3_B, I_L4_B)
  1190. Q_B = (Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B)
  1191. I_U = (I_L1_U, I_L2_U, I_L3_U, I_L4_U)
  1192. Q_U = (Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U)
  1193. F1_B = (f1L1F, f1L2F, f1H, f1L4F)
  1194. F2_B = (f2L1F, f2L2F, f2H, f2L4F)
  1195. F1_U = (f1H, f1L2F, f1L3F, f1L4F)
  1196. F2_U = (f2H, f2L2F, f2L3F, f2L4F)
  1197. a, b = (u, v), (s, y)
  1198. c, d = (u, y), (s, t)
  1199. D_B = (N_B, a, b, I_B, Q_B, F1_B, F2_B)
  1200. D_U = (N_U, c, d, I_U, Q_U, F1_U, F2_U)
  1201. return D_B, D_U
  1202. def _depth_first_select(rectangles):
  1203. """Find a rectangle of minimum area for bisection. """
  1204. min_area, j = None, None
  1205. for i, (_, (u, v), (s, t), _, _, _, _) in enumerate(rectangles):
  1206. area = (s - u)*(t - v)
  1207. if min_area is None or area < min_area:
  1208. min_area, j = area, i
  1209. return rectangles.pop(j)
  1210. def _rectangle_small_p(a, b, eps):
  1211. """Return ``True`` if the given rectangle is small enough. """
  1212. (u, v), (s, t) = a, b
  1213. if eps is not None:
  1214. return s - u < eps and t - v < eps
  1215. else:
  1216. return True
  1217. def dup_isolate_complex_roots_sqf(f, K, eps=None, inf=None, sup=None, blackbox=False):
  1218. """Isolate complex roots of a square-free polynomial using Collins-Krandick algorithm. """
  1219. if not K.is_ZZ and not K.is_QQ:
  1220. raise DomainError("isolation of complex roots is not supported over %s" % K)
  1221. if dup_degree(f) <= 0:
  1222. return []
  1223. if K.is_ZZ:
  1224. F = K.get_field()
  1225. else:
  1226. F = K
  1227. f = dup_convert(f, K, F)
  1228. lc = abs(dup_LC(f, F))
  1229. B = 2*max([ F.quo(abs(c), lc) for c in f ])
  1230. (u, v), (s, t) = (-B, F.zero), (B, B)
  1231. if inf is not None:
  1232. u = inf
  1233. if sup is not None:
  1234. s = sup
  1235. if v < 0 or t <= v or s <= u:
  1236. raise ValueError("not a valid complex isolation rectangle")
  1237. f1, f2 = dup_real_imag(f, F)
  1238. f1L1 = dmp_eval_in(f1, v, 1, 1, F)
  1239. f2L1 = dmp_eval_in(f2, v, 1, 1, F)
  1240. f1L2 = dmp_eval_in(f1, s, 0, 1, F)
  1241. f2L2 = dmp_eval_in(f2, s, 0, 1, F)
  1242. f1L3 = dmp_eval_in(f1, t, 1, 1, F)
  1243. f2L3 = dmp_eval_in(f2, t, 1, 1, F)
  1244. f1L4 = dmp_eval_in(f1, u, 0, 1, F)
  1245. f2L4 = dmp_eval_in(f2, u, 0, 1, F)
  1246. S_L1 = [f1L1, f2L1]
  1247. S_L2 = [f1L2, f2L2]
  1248. S_L3 = [f1L3, f2L3]
  1249. S_L4 = [f1L4, f2L4]
  1250. I_L1 = dup_isolate_real_roots_list(S_L1, F, inf=u, sup=s, fast=True, strict=True, basis=True)
  1251. I_L2 = dup_isolate_real_roots_list(S_L2, F, inf=v, sup=t, fast=True, strict=True, basis=True)
  1252. I_L3 = dup_isolate_real_roots_list(S_L3, F, inf=u, sup=s, fast=True, strict=True, basis=True)
  1253. I_L4 = dup_isolate_real_roots_list(S_L4, F, inf=v, sup=t, fast=True, strict=True, basis=True)
  1254. I_L3 = _reverse_intervals(I_L3)
  1255. I_L4 = _reverse_intervals(I_L4)
  1256. Q_L1 = _intervals_to_quadrants(I_L1, f1L1, f2L1, u, s, F)
  1257. Q_L2 = _intervals_to_quadrants(I_L2, f1L2, f2L2, v, t, F)
  1258. Q_L3 = _intervals_to_quadrants(I_L3, f1L3, f2L3, s, u, F)
  1259. Q_L4 = _intervals_to_quadrants(I_L4, f1L4, f2L4, t, v, F)
  1260. T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4)
  1261. N = _winding_number(T, F)
  1262. if not N:
  1263. return []
  1264. I = (I_L1, I_L2, I_L3, I_L4)
  1265. Q = (Q_L1, Q_L2, Q_L3, Q_L4)
  1266. F1 = (f1L1, f1L2, f1L3, f1L4)
  1267. F2 = (f2L1, f2L2, f2L3, f2L4)
  1268. rectangles, roots = [(N, (u, v), (s, t), I, Q, F1, F2)], []
  1269. while rectangles:
  1270. N, (u, v), (s, t), I, Q, F1, F2 = _depth_first_select(rectangles)
  1271. if s - u > t - v:
  1272. D_L, D_R = _vertical_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F)
  1273. N_L, a, b, I_L, Q_L, F1_L, F2_L = D_L
  1274. N_R, c, d, I_R, Q_R, F1_R, F2_R = D_R
  1275. if N_L >= 1:
  1276. if N_L == 1 and _rectangle_small_p(a, b, eps):
  1277. roots.append(ComplexInterval(a, b, I_L, Q_L, F1_L, F2_L, f1, f2, F))
  1278. else:
  1279. rectangles.append(D_L)
  1280. if N_R >= 1:
  1281. if N_R == 1 and _rectangle_small_p(c, d, eps):
  1282. roots.append(ComplexInterval(c, d, I_R, Q_R, F1_R, F2_R, f1, f2, F))
  1283. else:
  1284. rectangles.append(D_R)
  1285. else:
  1286. D_B, D_U = _horizontal_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F)
  1287. N_B, a, b, I_B, Q_B, F1_B, F2_B = D_B
  1288. N_U, c, d, I_U, Q_U, F1_U, F2_U = D_U
  1289. if N_B >= 1:
  1290. if N_B == 1 and _rectangle_small_p(a, b, eps):
  1291. roots.append(ComplexInterval(
  1292. a, b, I_B, Q_B, F1_B, F2_B, f1, f2, F))
  1293. else:
  1294. rectangles.append(D_B)
  1295. if N_U >= 1:
  1296. if N_U == 1 and _rectangle_small_p(c, d, eps):
  1297. roots.append(ComplexInterval(
  1298. c, d, I_U, Q_U, F1_U, F2_U, f1, f2, F))
  1299. else:
  1300. rectangles.append(D_U)
  1301. _roots, roots = sorted(roots, key=lambda r: (r.ax, r.ay)), []
  1302. for root in _roots:
  1303. roots.extend([root.conjugate(), root])
  1304. if blackbox:
  1305. return roots
  1306. else:
  1307. return [ r.as_tuple() for r in roots ]
  1308. def dup_isolate_all_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False):
  1309. """Isolate real and complex roots of a square-free polynomial ``f``. """
  1310. return (
  1311. dup_isolate_real_roots_sqf( f, K, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox),
  1312. dup_isolate_complex_roots_sqf(f, K, eps=eps, inf=inf, sup=sup, blackbox=blackbox))
  1313. def dup_isolate_all_roots(f, K, eps=None, inf=None, sup=None, fast=False):
  1314. """Isolate real and complex roots of a non-square-free polynomial ``f``. """
  1315. if not K.is_ZZ and not K.is_QQ:
  1316. raise DomainError("isolation of real and complex roots is not supported over %s" % K)
  1317. _, factors = dup_sqf_list(f, K)
  1318. if len(factors) == 1:
  1319. ((f, k),) = factors
  1320. real_part, complex_part = dup_isolate_all_roots_sqf(
  1321. f, K, eps=eps, inf=inf, sup=sup, fast=fast)
  1322. real_part = [ ((a, b), k) for (a, b) in real_part ]
  1323. complex_part = [ ((a, b), k) for (a, b) in complex_part ]
  1324. return real_part, complex_part
  1325. else:
  1326. raise NotImplementedError( "only trivial square-free polynomials are supported")
  1327. class RealInterval:
  1328. """A fully qualified representation of a real isolation interval. """
  1329. def __init__(self, data, f, dom):
  1330. """Initialize new real interval with complete information. """
  1331. if len(data) == 2:
  1332. s, t = data
  1333. self.neg = False
  1334. if s < 0:
  1335. if t <= 0:
  1336. f, s, t, self.neg = dup_mirror(f, dom), -t, -s, True
  1337. else:
  1338. raise ValueError("Cannot refine a real root in (%s, %s)" % (s, t))
  1339. a, b, c, d = _mobius_from_interval((s, t), dom.get_field())
  1340. f = dup_transform(f, dup_strip([a, b]),
  1341. dup_strip([c, d]), dom)
  1342. self.mobius = a, b, c, d
  1343. else:
  1344. self.mobius = data[:-1]
  1345. self.neg = data[-1]
  1346. self.f, self.dom = f, dom
  1347. @property
  1348. def func(self):
  1349. return RealInterval
  1350. @property
  1351. def args(self):
  1352. i = self
  1353. return (i.mobius + (i.neg,), i.f, i.dom)
  1354. def __eq__(self, other):
  1355. if type(other) is not type(self):
  1356. return False
  1357. return self.args == other.args
  1358. @property
  1359. def a(self):
  1360. """Return the position of the left end. """
  1361. field = self.dom.get_field()
  1362. a, b, c, d = self.mobius
  1363. if not self.neg:
  1364. if a*d < b*c:
  1365. return field(a, c)
  1366. return field(b, d)
  1367. else:
  1368. if a*d > b*c:
  1369. return -field(a, c)
  1370. return -field(b, d)
  1371. @property
  1372. def b(self):
  1373. """Return the position of the right end. """
  1374. was = self.neg
  1375. self.neg = not was
  1376. rv = -self.a
  1377. self.neg = was
  1378. return rv
  1379. @property
  1380. def dx(self):
  1381. """Return width of the real isolating interval. """
  1382. return self.b - self.a
  1383. @property
  1384. def center(self):
  1385. """Return the center of the real isolating interval. """
  1386. return (self.a + self.b)/2
  1387. @property
  1388. def max_denom(self):
  1389. """Return the largest denominator occurring in either endpoint. """
  1390. return max(self.a.denominator, self.b.denominator)
  1391. def as_tuple(self):
  1392. """Return tuple representation of real isolating interval. """
  1393. return (self.a, self.b)
  1394. def __repr__(self):
  1395. return "(%s, %s)" % (self.a, self.b)
  1396. def __contains__(self, item):
  1397. """
  1398. Say whether a complex number belongs to this real interval.
  1399. Parameters
  1400. ==========
  1401. item : pair (re, im) or number re
  1402. Either a pair giving the real and imaginary parts of the number,
  1403. or else a real number.
  1404. """
  1405. if isinstance(item, tuple):
  1406. re, im = item
  1407. else:
  1408. re, im = item, 0
  1409. return im == 0 and self.a <= re <= self.b
  1410. def is_disjoint(self, other):
  1411. """Return ``True`` if two isolation intervals are disjoint. """
  1412. if isinstance(other, RealInterval):
  1413. return (self.b < other.a or other.b < self.a)
  1414. assert isinstance(other, ComplexInterval)
  1415. return (self.b < other.ax or other.bx < self.a
  1416. or other.ay*other.by > 0)
  1417. def _inner_refine(self):
  1418. """Internal one step real root refinement procedure. """
  1419. if self.mobius is None:
  1420. return self
  1421. f, mobius = dup_inner_refine_real_root(
  1422. self.f, self.mobius, self.dom, steps=1, mobius=True)
  1423. return RealInterval(mobius + (self.neg,), f, self.dom)
  1424. def refine_disjoint(self, other):
  1425. """Refine an isolating interval until it is disjoint with another one. """
  1426. expr = self
  1427. while not expr.is_disjoint(other):
  1428. expr, other = expr._inner_refine(), other._inner_refine()
  1429. return expr, other
  1430. def refine_size(self, dx):
  1431. """Refine an isolating interval until it is of sufficiently small size. """
  1432. expr = self
  1433. while not (expr.dx < dx):
  1434. expr = expr._inner_refine()
  1435. return expr
  1436. def refine_step(self, steps=1):
  1437. """Perform several steps of real root refinement algorithm. """
  1438. expr = self
  1439. for _ in range(steps):
  1440. expr = expr._inner_refine()
  1441. return expr
  1442. def refine(self):
  1443. """Perform one step of real root refinement algorithm. """
  1444. return self._inner_refine()
  1445. class ComplexInterval:
  1446. """A fully qualified representation of a complex isolation interval.
  1447. The printed form is shown as (ax, bx) x (ay, by) where (ax, ay)
  1448. and (bx, by) are the coordinates of the southwest and northeast
  1449. corners of the interval's rectangle, respectively.
  1450. Examples
  1451. ========
  1452. >>> from sympy import CRootOf, S
  1453. >>> from sympy.abc import x
  1454. >>> CRootOf.clear_cache() # for doctest reproducibility
  1455. >>> root = CRootOf(x**10 - 2*x + 3, 9)
  1456. >>> i = root._get_interval(); i
  1457. (3/64, 3/32) x (9/8, 75/64)
  1458. The real part of the root lies within the range [0, 3/4] while
  1459. the imaginary part lies within the range [9/8, 3/2]:
  1460. >>> root.n(3)
  1461. 0.0766 + 1.14*I
  1462. The width of the ranges in the x and y directions on the complex
  1463. plane are:
  1464. >>> i.dx, i.dy
  1465. (3/64, 3/64)
  1466. The center of the range is
  1467. >>> i.center
  1468. (9/128, 147/128)
  1469. The northeast coordinate of the rectangle bounding the root in the
  1470. complex plane is given by attribute b and the x and y components
  1471. are accessed by bx and by:
  1472. >>> i.b, i.bx, i.by
  1473. ((3/32, 75/64), 3/32, 75/64)
  1474. The southwest coordinate is similarly given by i.a
  1475. >>> i.a, i.ax, i.ay
  1476. ((3/64, 9/8), 3/64, 9/8)
  1477. Although the interval prints to show only the real and imaginary
  1478. range of the root, all the information of the underlying root
  1479. is contained as properties of the interval.
  1480. For example, an interval with a nonpositive imaginary range is
  1481. considered to be the conjugate. Since the y values of y are in the
  1482. range [0, 1/4] it is not the conjugate:
  1483. >>> i.conj
  1484. False
  1485. The conjugate's interval is
  1486. >>> ic = i.conjugate(); ic
  1487. (3/64, 3/32) x (-75/64, -9/8)
  1488. NOTE: the values printed still represent the x and y range
  1489. in which the root -- conjugate, in this case -- is located,
  1490. but the underlying a and b values of a root and its conjugate
  1491. are the same:
  1492. >>> assert i.a == ic.a and i.b == ic.b
  1493. What changes are the reported coordinates of the bounding rectangle:
  1494. >>> (i.ax, i.ay), (i.bx, i.by)
  1495. ((3/64, 9/8), (3/32, 75/64))
  1496. >>> (ic.ax, ic.ay), (ic.bx, ic.by)
  1497. ((3/64, -75/64), (3/32, -9/8))
  1498. The interval can be refined once:
  1499. >>> i # for reference, this is the current interval
  1500. (3/64, 3/32) x (9/8, 75/64)
  1501. >>> i.refine()
  1502. (3/64, 3/32) x (9/8, 147/128)
  1503. Several refinement steps can be taken:
  1504. >>> i.refine_step(2) # 2 steps
  1505. (9/128, 3/32) x (9/8, 147/128)
  1506. It is also possible to refine to a given tolerance:
  1507. >>> tol = min(i.dx, i.dy)/2
  1508. >>> i.refine_size(tol)
  1509. (9/128, 21/256) x (9/8, 291/256)
  1510. A disjoint interval is one whose bounding rectangle does not
  1511. overlap with another. An interval, necessarily, is not disjoint with
  1512. itself, but any interval is disjoint with a conjugate since the
  1513. conjugate rectangle will always be in the lower half of the complex
  1514. plane and the non-conjugate in the upper half:
  1515. >>> i.is_disjoint(i), i.is_disjoint(i.conjugate())
  1516. (False, True)
  1517. The following interval j is not disjoint from i:
  1518. >>> close = CRootOf(x**10 - 2*x + 300/S(101), 9)
  1519. >>> j = close._get_interval(); j
  1520. (75/1616, 75/808) x (225/202, 1875/1616)
  1521. >>> i.is_disjoint(j)
  1522. False
  1523. The two can be made disjoint, however:
  1524. >>> newi, newj = i.refine_disjoint(j)
  1525. >>> newi
  1526. (39/512, 159/2048) x (2325/2048, 4653/4096)
  1527. >>> newj
  1528. (3975/51712, 2025/25856) x (29325/25856, 117375/103424)
  1529. Even though the real ranges overlap, the imaginary do not, so
  1530. the roots have been resolved as distinct. Intervals are disjoint
  1531. when either the real or imaginary component of the intervals is
  1532. distinct. In the case above, the real components have not been
  1533. resolved (so we do not know, yet, which root has the smaller real
  1534. part) but the imaginary part of ``close`` is larger than ``root``:
  1535. >>> close.n(3)
  1536. 0.0771 + 1.13*I
  1537. >>> root.n(3)
  1538. 0.0766 + 1.14*I
  1539. """
  1540. def __init__(self, a, b, I, Q, F1, F2, f1, f2, dom, conj=False):
  1541. """Initialize new complex interval with complete information. """
  1542. # a and b are the SW and NE corner of the bounding interval,
  1543. # (ax, ay) and (bx, by), respectively, for the NON-CONJUGATE
  1544. # root (the one with the positive imaginary part); when working
  1545. # with the conjugate, the a and b value are still non-negative
  1546. # but the ay, by are reversed and have oppositite sign
  1547. self.a, self.b = a, b
  1548. self.I, self.Q = I, Q
  1549. self.f1, self.F1 = f1, F1
  1550. self.f2, self.F2 = f2, F2
  1551. self.dom = dom
  1552. self.conj = conj
  1553. @property
  1554. def func(self):
  1555. return ComplexInterval
  1556. @property
  1557. def args(self):
  1558. i = self
  1559. return (i.a, i.b, i.I, i.Q, i.F1, i.F2, i.f1, i.f2, i.dom, i.conj)
  1560. def __eq__(self, other):
  1561. if type(other) is not type(self):
  1562. return False
  1563. return self.args == other.args
  1564. @property
  1565. def ax(self):
  1566. """Return ``x`` coordinate of south-western corner. """
  1567. return self.a[0]
  1568. @property
  1569. def ay(self):
  1570. """Return ``y`` coordinate of south-western corner. """
  1571. if not self.conj:
  1572. return self.a[1]
  1573. else:
  1574. return -self.b[1]
  1575. @property
  1576. def bx(self):
  1577. """Return ``x`` coordinate of north-eastern corner. """
  1578. return self.b[0]
  1579. @property
  1580. def by(self):
  1581. """Return ``y`` coordinate of north-eastern corner. """
  1582. if not self.conj:
  1583. return self.b[1]
  1584. else:
  1585. return -self.a[1]
  1586. @property
  1587. def dx(self):
  1588. """Return width of the complex isolating interval. """
  1589. return self.b[0] - self.a[0]
  1590. @property
  1591. def dy(self):
  1592. """Return height of the complex isolating interval. """
  1593. return self.b[1] - self.a[1]
  1594. @property
  1595. def center(self):
  1596. """Return the center of the complex isolating interval. """
  1597. return ((self.ax + self.bx)/2, (self.ay + self.by)/2)
  1598. @property
  1599. def max_denom(self):
  1600. """Return the largest denominator occurring in either endpoint. """
  1601. return max(self.ax.denominator, self.bx.denominator,
  1602. self.ay.denominator, self.by.denominator)
  1603. def as_tuple(self):
  1604. """Return tuple representation of the complex isolating
  1605. interval's SW and NE corners, respectively. """
  1606. return ((self.ax, self.ay), (self.bx, self.by))
  1607. def __repr__(self):
  1608. return "(%s, %s) x (%s, %s)" % (self.ax, self.bx, self.ay, self.by)
  1609. def conjugate(self):
  1610. """This complex interval really is located in lower half-plane. """
  1611. return ComplexInterval(self.a, self.b, self.I, self.Q,
  1612. self.F1, self.F2, self.f1, self.f2, self.dom, conj=True)
  1613. def __contains__(self, item):
  1614. """
  1615. Say whether a complex number belongs to this complex rectangular
  1616. region.
  1617. Parameters
  1618. ==========
  1619. item : pair (re, im) or number re
  1620. Either a pair giving the real and imaginary parts of the number,
  1621. or else a real number.
  1622. """
  1623. if isinstance(item, tuple):
  1624. re, im = item
  1625. else:
  1626. re, im = item, 0
  1627. return self.ax <= re <= self.bx and self.ay <= im <= self.by
  1628. def is_disjoint(self, other):
  1629. """Return ``True`` if two isolation intervals are disjoint. """
  1630. if isinstance(other, RealInterval):
  1631. return other.is_disjoint(self)
  1632. if self.conj != other.conj: # above and below real axis
  1633. return True
  1634. re_distinct = (self.bx < other.ax or other.bx < self.ax)
  1635. if re_distinct:
  1636. return True
  1637. im_distinct = (self.by < other.ay or other.by < self.ay)
  1638. return im_distinct
  1639. def _inner_refine(self):
  1640. """Internal one step complex root refinement procedure. """
  1641. (u, v), (s, t) = self.a, self.b
  1642. I, Q = self.I, self.Q
  1643. f1, F1 = self.f1, self.F1
  1644. f2, F2 = self.f2, self.F2
  1645. dom = self.dom
  1646. if s - u > t - v:
  1647. D_L, D_R = _vertical_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom)
  1648. if D_L[0] == 1:
  1649. _, a, b, I, Q, F1, F2 = D_L
  1650. else:
  1651. _, a, b, I, Q, F1, F2 = D_R
  1652. else:
  1653. D_B, D_U = _horizontal_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom)
  1654. if D_B[0] == 1:
  1655. _, a, b, I, Q, F1, F2 = D_B
  1656. else:
  1657. _, a, b, I, Q, F1, F2 = D_U
  1658. return ComplexInterval(a, b, I, Q, F1, F2, f1, f2, dom, self.conj)
  1659. def refine_disjoint(self, other):
  1660. """Refine an isolating interval until it is disjoint with another one. """
  1661. expr = self
  1662. while not expr.is_disjoint(other):
  1663. expr, other = expr._inner_refine(), other._inner_refine()
  1664. return expr, other
  1665. def refine_size(self, dx, dy=None):
  1666. """Refine an isolating interval until it is of sufficiently small size. """
  1667. if dy is None:
  1668. dy = dx
  1669. expr = self
  1670. while not (expr.dx < dx and expr.dy < dy):
  1671. expr = expr._inner_refine()
  1672. return expr
  1673. def refine_step(self, steps=1):
  1674. """Perform several steps of complex root refinement algorithm. """
  1675. expr = self
  1676. for _ in range(steps):
  1677. expr = expr._inner_refine()
  1678. return expr
  1679. def refine(self):
  1680. """Perform one step of complex root refinement algorithm. """
  1681. return self._inner_refine()