Navigation

Source code for sympy.polys.polyroots

"""Algorithms for computing symbolic roots of polynomials. """from__future__importprint_function,divisionimportmathfromsympy.core.symbolimportDummy,Symbol,symbolsfromsympy.coreimportS,I,pifromsympy.core.compatibilityimportorderedfromsympy.core.mulimportexpand_2argfromsympy.core.relationalimportEqfromsympy.core.sympifyimportsympifyfromsympy.core.numbersimportRational,igcdfromsympy.ntheoryimportdivisors,isprime,nextprimefromsympy.functionsimportexp,sqrt,re,im,Abs,cos,acos,sin,Piecewisefromsympy.functions.elementary.miscellaneousimportrootfromsympy.functions.elementary.complexesimportsignfromsympy.polys.polytoolsimportPoly,cancel,factor,gcd_list,discriminantfromsympy.polys.specialpolysimportcyclotomic_polyfromsympy.polys.polyerrorsimportPolynomialError,GeneratorsNeeded,DomainErrorfromsympy.polys.polyquinticconstimportPolyQuinticfromsympy.polys.rationaltoolsimporttogetherfromsympy.simplifyimportsimplify,powsimpfromsympy.utilitiesimportdefault_sort_key,publicfromsympy.core.compatibilityimportreduce,xrangedefroots_linear(f):"""Returns a list of roots of a linear polynomial."""r=-f.nth(0)/f.nth(1)dom=f.get_domain()ifnotdom.is_Numerical:ifdom.is_Composite:r=factor(r)else:r=simplify(r)return[r]defroots_quadratic(f):"""Returns a list of roots of a quadratic polynomial. If the domain is ZZ then the roots will be sorted with negatives coming before positives. The ordering will be the same for any numerical coefficients as long as the assumptions tested are correct, otherwise the ordering will not be sorted (but will be canonical). """a,b,c=f.all_coeffs()dom=f.get_domain()def_simplify(expr):ifdom.is_Composite:returnfactor(expr)else:returnsimplify(expr)ifcisS.Zero:r0,r1=S.Zero,-b/aifnotdom.is_Numerical:r1=_simplify(r1)elifr1.is_negative:r0,r1=r1,r0elifbisS.Zero:r=-c/aifnotdom.is_Numerical:r=_simplify(r)R=sqrt(r)r0=-Rr1=Relse:d=b**2-4*a*cA=2*aB=-b/Aifnotdom.is_Numerical:d=_simplify(d)B=_simplify(B)D=sqrt(d)/Ar0=B-Dr1=B+Difa.is_negative:r0,r1=r1,r0elifnotdom.is_Numerical:r0,r1=[expand_2arg(i)foriin(r0,r1)]return[r0,r1]defroots_cubic(f,trig=False):"""Returns a list of roots of a cubic polynomial."""iftrig:a,b,c,d=f.all_coeffs()p=(3*a*c-b**2)/3/a**2q=(2*b**3-9*a*b*c+27*a**2*d)/(27*a**3)D=18*a*b*c*d-4*b**3*d+b**2*c**2-4*a*c**3-27*a**2*d**2if(D>0)==True:rv=[]forkinrange(3):rv.append(2*sqrt(-p/3)*cos(acos(3*q/2/p*sqrt(-3/p))/3-k*2*pi/3))return[i-b/3/aforiinrv]_,a,b,c=f.monic().all_coeffs()ifcisS.Zero:x1,x2=roots([1,a,b],multiple=True)return[x1,S.Zero,x2]p=b-a**2/3q=c-a*b/3+2*a**3/27pon3=p/3aon3=a/3ifpisS.Zero:ifqisS.Zero:return[-aon3]*3else:ifq.is_real:if(q>0)==True:u1=-root(q,3)else:u1=root(-q,3)else:u1=root(-q,3)elifqisS.Zero:y1,y2=roots([1,0,p],multiple=True)return[tmp-aon3fortmpin[y1,S.Zero,y2]]elifq.is_realandq<0:u1=-root(-q/2+sqrt(q**2/4+pon3**3),3)else:u1=root(q/2+sqrt(q**2/4+pon3**3),3)coeff=I*sqrt(3)/2u2=u1*(-S.Half+coeff)u3=u1*(-S.Half-coeff)ifpisS.Zero:return[u1-aon3,u2-aon3,u3-aon3]soln=[-u1+pon3/u1-aon3,-u2+pon3/u2-aon3,-u3+pon3/u3-aon3]returnsolndef_roots_quartic_euler(p,q,r,a):""" Descartes-Euler solution of the quartic equation Parameters ========== p, q, r: coefficients of ``x**4 + p*x**2 + q*x + r`` a: shift of the roots Notes ===== This is a helper function for ``roots_quartic``. Look for solutions of the form :: ``x1 = sqrt(R) - sqrt(A + B*sqrt(R))`` ``x2 = -sqrt(R) - sqrt(A - B*sqrt(R))`` ``x3 = -sqrt(R) + sqrt(A - B*sqrt(R))`` ``x4 = sqrt(R) + sqrt(A + B*sqrt(R))`` To satisfy the quartic equation one must have ``p = -2*(R + A); q = -4*B*R; r = (R - A)**2 - B**2*R`` so that ``R`` must satisfy the Descartes-Euler resolvent equation ``64*R**3 + 32*p*R**2 + (4*p**2 - 16*r)*R - q**2 = 0`` If the resolvent does not have a rational solution, return None; in that case it is likely that the Ferrari method gives a simpler solution. Examples ======== >>> from sympy import S >>> from sympy.polys.polyroots import _roots_quartic_euler >>> p, q, r = -S(64)/5, -S(512)/125, -S(1024)/3125 >>> _roots_quartic_euler(p, q, r, S(0))[0] -sqrt(32*sqrt(5)/125 + 16/5) + 4*sqrt(5)/5 """fromsympy.solversimportsolve# solve the resolvent equationx=Symbol('x')eq=64*x**3+32*p*x**2+(4*p**2-16*r)*x-q**2xsols=list(roots(Poly(eq,x),cubics=False).keys())xsols=[solforsolinxsolsifsol.is_rational]ifnotxsols:returnNoneR=max(xsols)c1=sqrt(R)B=-q*c1/(4*R)A=-R-p/2c2=sqrt(A+B)c3=sqrt(A-B)return[c1-c2-a,-c1-c3-a,-c1+c3-a,c1+c2-a]defroots_quartic(f):r""" Returns a list of roots of a quartic polynomial. There are many references for solving quartic expressions available [1-5]. This reviewer has found that many of them require one to select from among 2 or more possible sets of solutions and that some solutions work when one is searching for real roots but don't work when searching for complex roots (though this is not always stated clearly). The following routine has been tested and found to be correct for 0, 2 or 4 complex roots. The quasisymmetric case solution [6] looks for quartics that have the form `x**4 + A*x**3 + B*x**2 + C*x + D = 0` where `(C/A)**2 = D`. Although no general solution that is always applicable for all coefficients is known to this reviewer, certain conditions are tested to determine the simplest 4 expressions that can be returned: 1) `f = c + a*(a**2/8 - b/2) == 0` 2) `g = d - a*(a*(3*a**2/256 - b/16) + c/4) = 0` 3) if `f != 0` and `g != 0` and `p = -d + a*c/4 - b**2/12` then a) `p == 0` b) `p != 0` Examples ======== >>> from sympy import Poly, symbols, I >>> from sympy.polys.polyroots import roots_quartic >>> r = roots_quartic(Poly('x**4-6*x**3+17*x**2-26*x+20')) >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I >>> sorted(str(tmp.evalf(n=2)) for tmp in r) ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + 1.0*I', '2.0 - 1.0*I'] References ========== 1. http://mathforum.org/dr.math/faq/faq.cubic.equations.html 2. http://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method 3. http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html 4. http://staff.bath.ac.uk/masjhd/JHD-CA.pdf 5. http://www.albmath.org/files/Math_5713.pdf 6. http://www.statemaster.com/encyclopedia/Quartic-equation 7. eqworld.ipmnet.ru/en/solutions/ae/ae0108.pdf """_,a,b,c,d=f.monic().all_coeffs()ifnotd:return[S.Zero]+roots([1,a,b,c],multiple=True)elif(c/a)**2==d:x,m=f.gen,c/ag=Poly(x**2+a*x+b-2*m,x)z1,z2=roots_quadratic(g)h1=Poly(x**2-z1*x+m,x)h2=Poly(x**2-z2*x+m,x)r1=roots_quadratic(h1)r2=roots_quadratic(h2)returnr1+r2else:a2=a**2e=b-3*a2/8f=c+a*(a2/8-b/2)g=d-a*(a*(3*a2/256-b/16)+c/4)aon4=a/4iffisS.Zero:y1,y2=[sqrt(tmp)fortmpinroots([1,e,g],multiple=True)]return[tmp-aon4fortmpin[-y1,-y2,y1,y2]]ifgisS.Zero:y=[S.Zero]+roots([1,0,e,f],multiple=True)return[tmp-aon4fortmpiny]else:# Descartes-Euler method, see [7]sols=_roots_quartic_euler(e,f,g,aon4)ifsols:returnsols# Ferrari method, see [1, 2]a2=a**2e=b-3*a2/8f=c+a*(a2/8-b/2)g=d-a*(a*(3*a2/256-b/16)+c/4)p=-e**2/12-gq=-e**3/108+e*g/3-f**2/8TH=Rational(1,3)def_ans(y):w=sqrt(e+2*y)arg1=3*e+2*yarg2=2*f/wans=[]forsin[-1,1]:root=sqrt(-(arg1+s*arg2))fortin[-1,1]:ans.append((s*w-t*root)/2-aon4)returnans# p == 0 casey1=-5*e/6-q**THifp.is_zero:return_ans(y1)# if p != 0 then u below is not 0root=sqrt(q**2/4+p**3/27)r=-q/2+root# or -q/2 - rootu=r**TH# primary root of solve(x**3 - r, x)y2=-5*e/6+u-p/u/3ifp.is_nonzero:return_ans(y2)# sort it out once they know the values of the coefficientsreturn[Piecewise((a1,Eq(p,0)),(a2,True))fora1,a2inzip(_ans(y1),_ans(y2))]defroots_binomial(f):"""Returns a list of roots of a binomial polynomial. If the domain is ZZ then the roots will be sorted with negatives coming before positives. The ordering will be the same for any numerical coefficients as long as the assumptions tested are correct, otherwise the ordering will not be sorted (but will be canonical). """n=f.degree()a,b=f.nth(n),f.nth(0)base=-cancel(b/a)alpha=root(base,n)ifalpha.is_number:alpha=alpha.expand(complex=True)# define some parameters that will allow us to order the roots.# If the domain is ZZ this is guaranteed to return roots sorted# with reals before non-real roots and non-real sorted according# to real part and imaginary part, e.g. -1, 1, -1 + I, 2 - Ineg=base.is_negativeeven=n%2==0ifneg:ifeven==Trueand(base+1).is_positive:big=Trueelse:big=False# get the indices in the right order so the computed# roots will be sorted when the domain is ZZks=[]imax=n//2ifeven:ks.append(imax)imax-=1ifnotneg:ks.append(0)foriinrange(imax,0,-1):ifneg:ks.extend([i,-i])else:ks.extend([-i,i])ifneg:ks.append(0)ifbig:foriinrange(0,len(ks),2):pair=ks[i:i+2]pair=list(reversed(pair))# compute the rootsroots,d=[],2*I*pi/nforkinks:zeta=exp(k*d).expand(complex=True)roots.append((alpha*zeta).expand(power_base=False))returnrootsdef_inv_totient_estimate(m):""" Find ``(L, U)`` such that ``L <= phi^-1(m) <= U``. Examples ======== >>> from sympy.polys.polyroots import _inv_totient_estimate >>> _inv_totient_estimate(192) (192, 840) >>> _inv_totient_estimate(400) (400, 1750) """primes=[d+1fordindivisors(m)ifisprime(d+1)]a,b=1,1forpinprimes:a*=pb*=p-1L=mU=int(math.ceil(m*(float(a)/b)))P=p=2primes=[]whileP<=U:p=nextprime(p)primes.append(p)P*=pP//=pb=1forpinprimes[:-1]:b*=p-1U=int(math.ceil(m*(float(P)/b)))returnL,Udefroots_cyclotomic(f,factor=False):"""Compute roots of cyclotomic polynomials. """L,U=_inv_totient_estimate(f.degree())forninxrange(L,U+1):g=cyclotomic_poly(n,f.gen,polys=True)iff==g:breakelse:# pragma: no coverraiseRuntimeError("failed to find index of a cyclotomic polynomial")roots=[]ifnotfactor:# get the indices in the right order so the computed# roots will be sortedh=n//2ks=[iforiinxrange(1,n+1)ifigcd(i,n)==1]ks.sort(key=lambdax:(x,-1)ifx<=helse(abs(x-n),1))d=2*I*pi/nforkinreversed(ks):roots.append(exp(k*d).expand(complex=True))else:g=Poly(f,extension=root(-1,n))forh,_inordered(g.factor_list()[1]):roots.append(-h.TC())returnrootsdefroots_quintic(f):""" Calulate exact roots of a solvable quintic """result=[]coeff_5,coeff_4,p,q,r,s=f.all_coeffs()# Eqn must be of the form x^5 + px^3 + qx^2 + rx + sifcoeff_4:returnresultifcoeff_5!=1:l=[p/coeff_5,q/coeff_5,r/coeff_5,s/coeff_5]ifnotall(coeff.is_Rationalforcoeffinl):returnresultf=Poly(f/coeff_5)quintic=PolyQuintic(f)# Eqn standardized. Algo for solving starts hereifnotf.is_irreducible:returnresultf20=quintic.f20# Check if f20 has linear factors over domain Ziff20.is_irreducible:returnresult# Now, we know that f is solvablefor_factorinf20.factor_list()[1]:if_factor[0].is_linear:theta=_factor[0].root(0)breakd=discriminant(f)delta=sqrt(d)# zeta = a fifth root of unityzeta1,zeta2,zeta3,zeta4=quintic.zetaT=quintic.T(theta,d)tol=S(1e-10)alpha=T[1]+T[2]*deltaalpha_bar=T[1]-T[2]*deltabeta=T[3]+T[4]*deltabeta_bar=T[3]-T[4]*deltadisc=alpha**2-4*betadisc_bar=alpha_bar**2-4*beta_barl0=quintic.l0(theta)l1=_quintic_simplify((-alpha+sqrt(disc))/S(2))l4=_quintic_simplify((-alpha-sqrt(disc))/S(2))l2=_quintic_simplify((-alpha_bar+sqrt(disc_bar))/S(2))l3=_quintic_simplify((-alpha_bar-sqrt(disc_bar))/S(2))order=quintic.order(theta,d)test=(order*delta.n())-((l1.n()-l4.n())*(l2.n()-l3.n()))# Comparing floats# Problems importing on topfromsympy.utilities.randtestimportcompifnotcomp(test,0,tol):l2,l3=l3,l2# Now we have correct order of l'sR1=l0+l1*zeta1+l2*zeta2+l3*zeta3+l4*zeta4R2=l0+l3*zeta1+l1*zeta2+l4*zeta3+l2*zeta4R3=l0+l2*zeta1+l4*zeta2+l1*zeta3+l3*zeta4R4=l0+l4*zeta1+l3*zeta2+l2*zeta3+l1*zeta4Res=[None,[None]*5,[None]*5,[None]*5,[None]*5]Res_n=[None,[None]*5,[None]*5,[None]*5,[None]*5]sol=Symbol('sol')# Simplifying improves performace a lot for exact expressionsR1=_quintic_simplify(R1)R2=_quintic_simplify(R2)R3=_quintic_simplify(R3)R4=_quintic_simplify(R4)# Solve imported here. Causing problems if imported as 'solve'# and hence the changed namefromsympy.solvers.solversimportsolveas_solvea,b=symbols('a b',cls=Dummy)_sol=_solve(sol**5-a-I*b,sol)foriinrange(5):_sol[i]=factor(_sol[i])R1=R1.as_real_imag()R2=R2.as_real_imag()R3=R3.as_real_imag()R4=R4.as_real_imag()fori,rootinenumerate(_sol):Res[1][i]=_quintic_simplify(root.subs({a:R1[0],b:R1[1]}))Res[2][i]=_quintic_simplify(root.subs({a:R2[0],b:R2[1]}))Res[3][i]=_quintic_simplify(root.subs({a:R3[0],b:R3[1]}))Res[4][i]=_quintic_simplify(root.subs({a:R4[0],b:R4[1]}))foriinrange(1,5):forjinrange(5):Res_n[i][j]=Res[i][j].n()Res[i][j]=_quintic_simplify(Res[i][j])r1=Res[1][0]r1_n=Res_n[1][0]foriinrange(5):ifcomp(im(r1_n*Res_n[4][i]),0,tol):r4=Res[4][i]breaku,v=quintic.uv(theta,d)sqrt5=math.sqrt(5)# Now we have various Res values. Each will be a list of five# values. We have to pick one r value from those five for each Resu,v=quintic.uv(theta,d)testplus=(u+v*delta*sqrt(5)).n()testminus=(u-v*delta*sqrt(5)).n()# Evaluated numbers suffixed with _n# We will use evaluated numbers for calculation. Much faster.r4_n=r4.n()r2=r3=Noneforiinrange(5):r2temp_n=Res_n[2][i]forjinrange(5):# Again storing away the exact number and using# evaluated numbers in computationsr3temp_n=Res_n[3][j]if(comp(r1_n*r2temp_n**2+r4_n*r3temp_n**2-testplus,0,tol)andcomp(r3temp_n*r1_n**2+r2temp_n*r4_n**2-testminus,0,tol)):r2=Res[2][i]r3=Res[3][j]breakifr2:break# Now, we have r's so we can get rootsx1=(r1+r2+r3+r4)/5x2=(r1*zeta4+r2*zeta3+r3*zeta2+r4*zeta1)/5x3=(r1*zeta3+r2*zeta1+r3*zeta4+r4*zeta2)/5x4=(r1*zeta2+r2*zeta4+r3*zeta1+r4*zeta3)/5x5=(r1*zeta1+r2*zeta2+r3*zeta3+r4*zeta4)/5result=[x1,x2,x3,x4,x5]# Now check if solutions are distinctsaw=set()forrinresult:r=r.n(2)ifrinsaw:# Roots were identical. Abort, return []# and fall back to usual solvereturn[]saw.add(r)returnresultdef_quintic_simplify(expr):expr=powsimp(expr)expr=cancel(expr)returntogether(expr)def_integer_basis(poly):"""Compute coefficient basis for a polynomial over integers. Returns the integer ``div`` such that substituting ``x = div*y`` ``p(x) = m*q(y)`` where the coefficients of ``q`` are smaller than those of ``p``. For example ``x**5 + 512*x + 1024 = 0`` with ``div = 4`` becomes ``y**5 + 2*y + 1 = 0`` Returns the integer ``div`` or ``None`` if there is no possible scaling. Examples ======== >>> from sympy.polys import Poly >>> from sympy.abc import x >>> from sympy.polys.polyroots import _integer_basis >>> p = Poly(x**5 + 512*x + 1024, x, domain='ZZ') >>> _integer_basis(p) 4 """monoms,coeffs=list(zip(*poly.terms()))monoms,=list(zip(*monoms))coeffs=list(map(abs,coeffs))ifcoeffs[0]<coeffs[-1]:coeffs=list(reversed(coeffs))n=monoms[0]monoms=[n-iforiinreversed(monoms)]else:returnNonemonoms=monoms[:-1]coeffs=coeffs[:-1]divs=reversed(divisors(gcd_list(coeffs))[1:])try:div=next(divs)exceptStopIteration:returnNonewhileTrue:formonom,coeffinzip(monoms,coeffs):ifcoeff%div**monom!=0:try:div=next(divs)exceptStopIteration:returnNoneelse:breakelse:returndivdefpreprocess_roots(poly):"""Try to get rid of symbolic coefficients from ``poly``. """coeff=S.Onetry:_,poly=poly.clear_denoms(convert=True)exceptDomainError:returncoeff,polypoly=poly.primitive()[1]poly=poly.retract()# TODO: This is fragile. Figure out how to make this independent of construct_domain().ifpoly.get_domain().is_Polyandall(c.is_termforcinpoly.rep.coeffs()):poly=poly.inject()strips=list(zip(*poly.monoms()))gens=list(poly.gens[1:])base,strips=strips[0],strips[1:]forgen,stripinzip(list(gens),strips):reverse=Falseifstrip[0]<strip[-1]:strip=reversed(strip)reverse=Trueratio=Nonefora,binzip(base,strip):ifnotaandnotb:continueelifnotaornotb:breakelifb%a!=0:breakelse:_ratio=b//aifratioisNone:ratio=_ratioelifratio!=_ratio:breakelse:ifreverse:ratio=-ratiopoly=poly.eval(gen,1)coeff*=gen**(-ratio)gens.remove(gen)ifgens:poly=poly.eject(*gens)ifpoly.is_univariateandpoly.get_domain().is_ZZ:basis=_integer_basis(poly)ifbasisisnotNone:n=poly.degree()deffunc(k,coeff):returncoeff//basis**(n-k[0])poly=poly.termwise(func)coeff*=basisreturncoeff,poly@public

[docs]defroots(f,*gens,**flags):""" Computes symbolic roots of a univariate polynomial. Given a univariate polynomial f with symbolic coefficients (or a list of the polynomial's coefficients), returns a dictionary with its roots and their multiplicities. Only roots expressible via radicals will be returned. To get a complete set of roots use RootOf class or numerical methods instead. By default cubic and quartic formulas are used in the algorithm. To disable them because of unreadable output set ``cubics=False`` or ``quartics=False`` respectively. If cubic roots are real but are expressed in terms of complex numbers (casus irreducibilis [1]) the ``trig`` flag can be set to True to have the solutions returned in terms of cosine and inverse cosine functions. To get roots from a specific domain set the ``filter`` flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting ``filter='C'``). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a list containing all those roots set the ``multiple`` flag to True; the list will have identical roots appearing next to each other in the result. (For a given Poly, the all_roots method will give the roots in sorted numerical order.) Examples ======== >>> from sympy import Poly, roots >>> from sympy.abc import x, y >>> roots(x**2 - 1, x) {-1: 1, 1: 1} >>> p = Poly(x**2-1, x) >>> roots(p) {-1: 1, 1: 1} >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) {-sqrt(y): 1, sqrt(y): 1} >>> roots(x**2 - y, x) {-sqrt(y): 1, sqrt(y): 1} >>> roots([1, 0, -1]) {-1: 1, 1: 1} References ========== 1. http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method """fromsympy.polys.polytoolsimportto_rational_coeffsflags=dict(flags)auto=flags.pop('auto',True)cubics=flags.pop('cubics',True)trig=flags.pop('trig',False)quartics=flags.pop('quartics',True)quintics=flags.pop('quintics',False)multiple=flags.pop('multiple',False)filter=flags.pop('filter',None)predicate=flags.pop('predicate',None)ifisinstance(f,list):ifgens:raiseValueError('redundant generators given')x=Dummy('x')poly,i={},len(f)-1forcoeffinf:poly[i],i=sympify(coeff),i-1f=Poly(poly,x,field=True)else:try:f=Poly(f,*gens,**flags)exceptGeneratorsNeeded:ifmultiple:return[]else:return{}iff.is_multivariate:raisePolynomialError('multivariate polynomials are not supported')def_update_dict(result,root,k):ifrootinresult:result[root]+=kelse:result[root]=kdef_try_decompose(f):"""Find roots using functional decomposition. """factors,roots=f.decompose(),[]forrootin_try_heuristics(factors[0]):roots.append(root)forfactorinfactors[1:]:previous,roots=list(roots),[]forrootinprevious:g=factor-Poly(root,f.gen)forrootin_try_heuristics(g):roots.append(root)returnrootsdef_try_heuristics(f):"""Find roots using formulas and some tricks. """iff.is_ground:return[]iff.is_monomial:return[S(0)]*f.degree()iff.length()==2:iff.degree()==1:returnlist(map(cancel,roots_linear(f)))else:returnroots_binomial(f)result=[]foriin[-1,1]:ifnotf.eval(i):f=f.quo(Poly(f.gen-i,f.gen))result.append(i)breakn=f.degree()ifn==1:result+=list(map(cancel,roots_linear(f)))elifn==2:result+=list(map(cancel,roots_quadratic(f)))eliff.is_cyclotomic:result+=roots_cyclotomic(f)elifn==3andcubics:result+=roots_cubic(f,trig=trig)elifn==4andquartics:result+=roots_quartic(f)elifn==5andquintics:result+=roots_quintic(f)returnresult(k,),f=f.terms_gcd()ifnotk:zeros={}else:zeros={S(0):k}coeff,f=preprocess_roots(f)ifautoandf.get_domain().has_Ring:f=f.to_field()rescale_x=Nonetranslate_x=Noneresult={}ifnotf.is_ground:ifnotf.get_domain().is_Exact:forrinf.nroots():_update_dict(result,r,1)eliff.degree()==1:result[roots_linear(f)[0]]=1eliff.degree()==2:forrinroots_quadratic(f):_update_dict(result,r,1)eliff.length()==2:forrinroots_binomial(f):_update_dict(result,r,1)else:_,factors=Poly(f.as_expr()).factor_list()iflen(factors)==1andfactors[0][1]==1:iff.get_domain().is_EX:res=to_rational_coeffs(f)ifres:ifres[0]isNone:translate_x,f=res[2:]else:rescale_x,f=res[1],res[-1]result=roots(f)ifnotresult:forrootin_try_decompose(f):_update_dict(result,root,1)else:forrootin_try_decompose(f):_update_dict(result,root,1)else:forfactor,kinfactors:forrin_try_heuristics(Poly(factor,f.gen,field=True)):_update_dict(result,r,k)ifcoeffisnotS.One:_result,result,=result,{}forroot,kin_result.items():result[coeff*root]=kresult.update(zeros)iffilternotin[None,'C']:handlers={'Z':lambdar:r.is_Integer,'Q':lambdar:r.is_Rational,'R':lambdar:r.is_real,'I':lambdar:r.is_imaginary,}try:query=handlers[filter]exceptKeyError:raiseValueError("Invalid filter: %s"%filter)forzeroindict(result).keys():ifnotquery(zero):delresult[zero]ifpredicateisnotNone:forzeroindict(result).keys():ifnotpredicate(zero):delresult[zero]ifrescale_x:result1={}fork,vinresult.items():result1[k*rescale_x]=vresult=result1iftranslate_x:result1={}fork,vinresult.items():result1[k+translate_x]=vresult=result1ifnotmultiple:returnresultelse:zeros=[]forzeroinordered(result):zeros.extend([zero]*result[zero])returnzeros