K i ZdZddlmZddlmZddlmZddlmZm Z ddl m Z ddl m Z ddlmZdd lmZdd lmZmZmZmZdd lmZeGd d e e eZdZy)z1Implementation of :class:`AlgebraicField` class. )AddMul)S)Dummysymbols)CharacteristicZero)Field) SimpleDomain)ANP)CoercionFailed DomainError NotAlgebraicIsomorphismFailed)publicceZdZdZeZdxZZdZdZ dZ dddZ dZ dZ d Zd Zddd Zd Zd ZdZdZdZdZdZdZdZdZdZdZdZdZdZdZ dZ!dZ"dZ#dZ$d Z%d!Z&d&d"Z'd#Z(d$Z)d'd%Z*y)(AlgebraicFieldaP#Algebraic number field :ref:`QQ(a)` A :ref:`QQ(a)` domain represents an `algebraic number field`_ `\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see :ref:`polys-domainsintro`). A :py:class:`~.Poly` created from an expression involving `algebraic numbers`_ will treat the algebraic numbers as generators if the generators argument is not specified. >>> from sympy import Poly, Symbol, sqrt >>> x = Symbol('x') >>> Poly(x**2 + sqrt(2)) Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ') That is a multivariate polynomial with ``sqrt(2)`` treated as one of the generators (variables). If the generators are explicitly specified then ``sqrt(2)`` will be considered to be a coefficient but by default the :ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)` domain the argument ``extension=True`` can be given. >>> Poly(x**2 + sqrt(2), x) Poly(x**2 + sqrt(2), x, domain='EX') >>> Poly(x**2 + sqrt(2), x, extension=True) Poly(x**2 + sqrt(2), x, domain='QQ') A generator of the algebraic field extension can also be specified explicitly which is particularly useful if the coefficients are all rational but an extension field is needed (e.g. to factor the polynomial). >>> Poly(x**2 + 1) Poly(x**2 + 1, x, domain='ZZ') >>> Poly(x**2 + 1, extension=sqrt(2)) Poly(x**2 + 1, x, domain='QQ') It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using the ``extension`` argument to :py:func:`~.factor` or by specifying the domain explicitly. >>> from sympy import factor, QQ >>> factor(x**2 - 2) x**2 - 2 >>> factor(x**2 - 2, extension=sqrt(2)) (x - sqrt(2))*(x + sqrt(2)) >>> factor(x**2 - 2, domain='QQ') (x - sqrt(2))*(x + sqrt(2)) >>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2))) (x - sqrt(2))*(x + sqrt(2)) The ``extension=True`` argument can be used but will only create an extension that contains the coefficients which is usually not enough to factorise the polynomial. >>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2) >>> factor(p) # treats sqrt(2) as a symbol (x + sqrt(2))*(x**2 - 2) >>> factor(p, extension=True) (x - sqrt(2))*(x + sqrt(2))**2 >>> factor(x**2 - 2, extension=True) # all rational coefficients x**2 - 2 It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel` and :py:func:`~.gcd` functions. >>> from sympy import cancel, gcd >>> cancel((x**2 - 2)/(x - sqrt(2))) (x**2 - 2)/(x - sqrt(2)) >>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2)) x + sqrt(2) >>> gcd(x**2 - 2, x - sqrt(2)) 1 >>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2)) x - sqrt(2) When using the domain directly :ref:`QQ(a)` can be used as a constructor to create instances which then support the operations ``+,-,*,**,/``. The :py:meth:`~.Domain.algebraic_field` method is used to construct a particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method can be used to create domain elements from normal SymPy expressions. >>> K = QQ.algebraic_field(sqrt(2)) >>> K QQ >>> xk = K.from_sympy(3 + 4*sqrt(2)) >>> xk # doctest: +SKIP ANP([4, 3], [1, 0, -2], QQ) Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have limited printing support. The raw display shows the internal representation of the element as the list ``[4, 3]`` representing the coefficients of ``1`` and ``sqrt(2)`` for this element in the form ``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`. The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use :py:meth:`~.Domain.to_sympy` to get a better printed form for the elements and to see the results of operations. >>> xk = K.from_sympy(3 + 4*sqrt(2)) >>> yk = K.from_sympy(2 + 3*sqrt(2)) >>> xk * yk # doctest: +SKIP ANP([17, 30], [1, 0, -2], QQ) >>> K.to_sympy(xk * yk) 17*sqrt(2) + 30 >>> K.to_sympy(xk + yk) 5 + 7*sqrt(2) >>> K.to_sympy(xk ** 2) 24*sqrt(2) + 41 >>> K.to_sympy(xk / yk) sqrt(2)/14 + 9/7 Any expression representing an algebraic number can be used to generate a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed. The function :py:func:`~.minpoly` function is used for this. >>> from sympy import exp, I, pi, minpoly >>> g = exp(2*I*pi/3) >>> g exp(2*I*pi/3) >>> g.is_algebraic True >>> minpoly(g, x) x**2 + x + 1 >>> factor(x**3 - 1, extension=g) (x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3)) It is also possible to make an algebraic field from multiple extension elements. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3)) >>> K QQ >>> p = x**4 - 5*x**2 + 6 >>> factor(p) (x**2 - 3)*(x**2 - 2) >>> factor(p, domain=K) (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3)) >>> factor(p, extension=[sqrt(2), sqrt(3)]) (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3)) Multiple extension elements are always combined together to make a single `primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays as ``QQ``. The minimal polynomial for the primitive element is computed using the :py:func:`~.primitive_element` function. >>> from sympy import primitive_element >>> primitive_element([sqrt(2), sqrt(3)], x) (x**4 - 10*x**2 + 1, [1, 1]) >>> minpoly(sqrt(2) + sqrt(3), x) x**4 - 10*x**2 + 1 The extension elements that generate the domain can be accessed from the domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for the primitive element as a :py:class:`~.DMP` instance is available as :py:attr:`~.mod`. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3)) >>> K QQ >>> K.ext sqrt(2) + sqrt(3) >>> K.orig_ext (sqrt(2), sqrt(3)) >>> K.mod # doctest: +SKIP DMP_Python([1, 0, -10, 0, 1], QQ) The `discriminant`_ of the field can be obtained from the :py:meth:`~.discriminant` method, and an `integral basis`_ from the :py:meth:`~.integral_basis` method. The latter returns a list of :py:class:`~.ANP` instances by default, but can be made to return instances of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt`` argument. The maximal order, or ring of integers, of the field can also be obtained from the :py:meth:`~.maximal_order` method, as a :py:class:`~sympy.polys.numberfields.modules.Submodule`. >>> zeta5 = exp(2*I*pi/5) >>> K = QQ.algebraic_field(zeta5) >>> K QQ >>> K.discriminant() 125 >>> K = QQ.algebraic_field(sqrt(5)) >>> K QQ >>> K.integral_basis(fmt='sympy') [1, 1/2 + sqrt(5)/2] >>> K.maximal_order() Submodule[[2, 0], [1, 1]]/2 The factorization of a rational prime into prime ideals of the field is computed by the :py:meth:`~.primes_above` method, which returns a list of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances. >>> zeta7 = exp(2*I*pi/7) >>> K = QQ.algebraic_field(zeta7) >>> K QQ >>> K.primes_above(11) [(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)] The Galois group of the Galois closure of the field can be computed (when the minimal polynomial of the field is of sufficiently small degree). >>> K.galois_group(by_name=True)[0] S6TransitiveSubgroups.C6 Notes ===== It is not currently possible to generate an algebraic extension over any domain other than :ref:`QQ`. Ideally it would be possible to generate extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two implementations of this kind of quotient ring/extension in the :py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension` classes. Each of those implementations needs some work to make them fully usable though. .. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field .. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number .. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field .. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis .. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory) .. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem TFNaliasc|js tdddlm}t |dk(rt |dt r |ddd}n|}|t |dk(rt|ddd}||_ ||||_ |jjj|_ |x|_ |_d|_|jfx|_|_||d|dg|_|j&j)|jj+||_|j&j-|jj+||_d|_d|_i|_y)a Parameters ========== dom : :py:class:`~.Domain` The base field over which this is an extension field. Currently only :ref:`QQ` is accepted. *ext : One or more :py:class:`~.Expr` Generators of the extension. These should be expressions that are algebraic over `\mathbb{Q}`. alias : str, :py:class:`~.Symbol`, None, optional (default=None) If provided, this will be used as the alias symbol for the primitive element of the :py:class:`~.AlgebraicField`. If ``None``, while ``ext`` consists of exactly one :py:class:`~.AlgebraicNumber`, its alias (if any) will be used. z&ground domain must be a rational fieldrto_number_fieldNrr)is_QQrsympy.polys.numberfieldsrlen isinstancetuplegetattrorig_extextminpolyrepmoddomaindomngensrgensunitdtypezeroto_listone_maximal_order _discriminant_nilradicals_mod_p)selfr&rr!rr s h/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/polys/domains/algebraicfield.py__init__zAlgebraicField.__init__sH&yyFG G< s8q=ZA61vabzHH =SX]CFGT2E   #3e4 88##'' "%$ dh $(HH;. ty#a&#a&)* JJOODHH$4$4$6< ::>>$(("2"2"4c:"!"$cl|j||jj|jSN)r*r$r,r&)r1elements r2newzAlgebraicField.newGs&zz'488#3#3#5txx@@r4cdt|jdzt|jzdzS)N<>)strr&r!r1s r2__str__zAlgebraicField.__str__Js'488}s"S]2S88r4ct|jj|j|j|j fSr6)hash __class____name__r*r&r!r=s r2__hash__zAlgebraicField.__hash__Ms,T^^,,djj$((DHHMNNr4ct|tr4|j|jk(xr|j|jk(StS)z0Returns ``True`` if two domains are equivalent. )rrr*r!NotImplemented)r1others r2__eq__zAlgebraicField.__eq__Ps7 e^ ,::,FUYY1F F! !r4cPt|jg|jf|zd|iS)z?Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. r)rr&r!)r1r extensions r2algebraic_fieldzAlgebraicField.algebraic_fieldWs&dhhP488+ *AP%PPr4c8|jj|S)z@Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. )r! field_elementr1as r2 to_alg_numzAlgebraicField.to_alg_num[sxx%%a((r4c\t|dst||_|j|S)z.Convert ``a`` of ``dtype`` to a SymPy object. _converter)hasattr_make_converterrQrMs r2to_sympyzAlgebraicField.to_sympy_s)t\*-d3DOq!!r4c ||jj|gS#t$rYnwxYwddlm} ||||j j S#ttf$rt|d|wxYw)z)Convert SymPy's expression to ``dtype``. rrz$ is not a valid algebraic number in ) r& from_sympyr rrr! native_coeffsrr)r1rNrs r2rVzAlgebraicField.from_sympygs ,,Q/01 1    = H4884BBDE E/0 H >?FH H Hs!$ 00&A!! BcF||jj||Sz.Convert a Python ``int`` object to ``dtype``. r&convertK1rNK0s r2from_ZZzAlgebraicField.from_ZZv"&&..B'((r4cF||jj||SrYrZr\s r2from_ZZ_pythonzAlgebraicField.from_ZZ_pythonzr`r4cF||jj||Sz3Convert a Python ``Fraction`` object to ``dtype``. rZr\s r2from_QQzAlgebraicField.from_QQ~r`r4cF||jj||SrdrZr\s r2from_QQ_pythonzAlgebraicField.from_QQ_pythonr`r4cF||jj||S)z,Convert a GMPY ``mpz`` object to ``dtype``. rZr\s r2 from_ZZ_gmpyzAlgebraicField.from_ZZ_gmpyr`r4cF||jj||S)z,Convert a GMPY ``mpq`` object to ``dtype``. rZr\s r2 from_QQ_gmpyzAlgebraicField.from_QQ_gmpyr`r4cF||jj||S)z.Convert a mpmath ``mpf`` object to ``dtype``. rZr\s r2from_RealFieldzAlgebraicField.from_RealFieldr`r4ctd|z)z)Returns a ring associated with ``self``. z#there is no ring associated with %s)rr=s r2get_ringzAlgebraicField.get_rings?$FGGr4cT|jj|jS)z#Returns True if ``a`` is positive. )r& is_positiveLCrMs r2rqzAlgebraicField.is_positivexx##ADDF++r4cT|jj|jS)z#Returns True if ``a`` is negative. )r& is_negativerrrMs r2ruzAlgebraicField.is_negativersr4cT|jj|jS)z'Returns True if ``a`` is non-positive. )r&is_nonpositiverrrMs r2rwzAlgebraicField.is_nonpositivexx&&qttv..r4cT|jj|jS)z'Returns True if ``a`` is non-negative. )r&is_nonnegativerrrMs r2rzzAlgebraicField.is_nonnegativerxr4c|S)zReturns numerator of ``a``. rMs r2numerzAlgebraicField.numersr4c|jS)zReturns denominator of ``a``. )r-rMs r2denomzAlgebraicField.denoms xxr4cB|j|j|S)z=Convert AlgebraicField element 'a' to another AlgebraicField rVrTr\s r2from_AlgebraicFieldz"AlgebraicField.from_AlgebraicField}}R[[^,,r4cB|j|j|S)z4Convert a GaussianInteger element 'a' to ``dtype``. rr\s r2from_GaussianIntegerRingz'AlgebraicField.from_GaussianIntegerRingrr4cB|j|j|S)z5Convert a GaussianRational element 'a' to ``dtype``. rr\s r2from_GaussianRationalFieldz)AlgebraicField.from_GaussianRationalFieldrr4cZddlm}|||j\}}||_||_y)Nr) round_two)radicals)sympy.polys.numberfields.basisrr0r.r/)r1rZKdKs r2 _do_round_twozAlgebraicField._do_round_twos+<4$*A*ABB r4cR|j|j|jS)z Compute the maximal order, or ring of integers, of the field. Returns ======= :py:class:`~sympy.polys.numberfields.modules.Submodule`. See Also ======== integral_basis )r.rr=s r2 maximal_orderzAlgebraicField.maximal_orders(    &    """r4c |j}|j}|jd}t|Dcgc]:}|j t t |dd|fj<}}|dk(r|Dcgc]}|j|c}S|dk(r|Dcgc]}|j|c}S|Scc}wcc}wcc}w)as Get an integral basis for the field. Parameters ========== fmt : str, None, optional (default=None) If ``None``, return a list of :py:class:`~.ANP` instances. If ``"sympy"``, convert each element of the list to an :py:class:`~.Expr`, using ``self.to_sympy()``. If ``"alg"``, convert each element of the list to an :py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``. Examples ======== >>> from sympy import QQ, AlgebraicNumber, sqrt >>> alpha = AlgebraicNumber(sqrt(5), alias='alpha') >>> k = QQ.algebraic_field(alpha) >>> B0 = k.integral_basis() >>> B1 = k.integral_basis(fmt='sympy') >>> B2 = k.integral_basis(fmt='alg') >>> print(B0[1]) # doctest: +SKIP ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ) >>> print(B1[1]) 1/2 + alpha/2 >>> print(B2[1]) alpha/2 + 1/2 In the last two cases we get legible expressions, which print somewhat differently because of the different types involved: >>> print(type(B1[1])) >>> print(type(B2[1])) See Also ======== to_sympy to_alg_num maximal_order rNsympyalg) r QQ_matrixshaperanger8listreversedflatrTrO)r1fmtrMnjBbs r2integral_basiszAlgebraicField.integral_basissZ   ! LL GGAJ?DQx H!TXXd8AadGLLN34 5 H H '>./0DMM!$0 0 E\0121DOOA&2 2 I02s?CC 'CcR|j|j|jS)z"Get the discriminant of the field.)r/rr=s r2 discriminantzAlgebraicField.discriminant s&    %    !!!r4cddlm}|j}|j}|jj |}|||||S)z@Compute the prime ideals lying above a given rational prime *p*.r) prime_decomp)rrradical)sympy.polys.numberfields.primesrrrr0get)r1prrrrads r2 primes_abovezAlgebraicField.primes_abovesH@    !    %%))!,A"S99r4cZ|jjj|||S)a Compute the Galois group of the Galois closure of this field. Examples ======== If the field is Galois, the order of the group will equal the degree of the field: >>> from sympy import QQ >>> from sympy.abc import x >>> k = QQ.alg_field_from_poly(x**4 + 1) >>> G, _ = k.galois_group() >>> G.order() 4 If the field is not Galois, then its Galois closure is a proper extension, and the order of the Galois group will be greater than the degree of the field: >>> k = QQ.alg_field_from_poly(x**4 - 2) >>> G, _ = k.galois_group() >>> G.order() 8 See Also ======== sympy.polys.numberfields.galoisgroups.galois_group )by_name max_tries randomize)r!minpoly_of_element galois_group)r1rrrs r2rzAlgebraicField.galois_groups3@xx**,99yI:G Gr4r6)FF)+rB __module__ __qualname____doc__r r*is_AlgebraicField is_Algebraic is_Numericalhas_assoc_Ringhas_assoc_Fieldr3r8r>rCrGrJrOrTrVr_rbrergrirkrmrorqrurwrzr}rrrrrrrrrrr|r4r2rrsbH E'++ LNO(,H%TA9O"15Q)" H)))))))H,,//--- #&5n" :!Gr4rc 4|jj}|jj}|jj|j s5t |jjDcgc]}||z }}nddl m }t|jj\}}tdt|t }t#t||} |j|} t | j$D cgc]} | j&j)| } } t| |D cic]\} }| ||}} }| j&j+|}| jDcgc]\}}| j|||}}}| j,|g}t d|jjD]*}|d|zj/|}|j1|,|Dcgc]!}|jj3| #}}|Dcgc]}|j5}}|Dcgc]'}t#dt7j8|D)}}t;j<|Dcgc]6}|Dcgc](}||j?|t@jB*c}8c}}fd}|Scc}wcc} wcc}} wcc}}wcc}wcc}wcc}wcc}wcc}}w) z/Construct the converter to convert back to Exprr)r"za:)clsc3HK|]}|jdddyw)Nr) as_coeff_Mul).0ts r2 z"_make_converter..ps CQ!.."4R4(Cs "c |jddd}Dcgc]}tdt||D }}|Dcgc] } | }}tdt|D}|Scc}wcc}w)z!Convert a to Expr using converterNrc3,K|] \}}||zywr6r|)rmijajs r2rz5_make_converter..converter..ys.converter..{sH$!QC1IHs)r,sumzipr) rNaimi coeffs_domr coeffs_sympyres algebraicsmatrixtoexprs r2 converterz"_make_converter..convertervsw YY[2 GMNc<B <<N N+56aq 6 6H#lJ*GHI O6s #A1A6)"r!as_exprr&rVrTis_Addrr$degree sympy.polys.numberfields.minpolyr"ras_coefficients_dictitemsrrrdictr'ringmonomial_basis from_dictr-remappendxreplaceexpandr make_argssetunionrrZero)Kr!todomrpowersr"r(coeffssymssym2genRimonomsmrext_dictext_polysgminpolys ext_poly_nrtermsrNrrrrrs @@@r2rSrS<s %%--/C EE  E UU^^F ::"' "78Q#q&88 =C446<<>? fCI;'U3s4' EE$K49!''NCq!&&''*CC,/,?@DAqAuQxK@@66##H-J MM* % & :@@A!))+&&w/@@#) )Qahhj )F )NT TTC#--2BC C TE Te$J@J K161uQUU1aff%&6 KF k9&D@LA* U6 KsB; K*"K/K4"K:&L;L,L " L+-L LLN)rsympy.core.addrsympy.core.mulrsympy.core.singletonrsympy.core.symbolrr&sympy.polys.domains.characteristiczeror sympy.polys.domains.fieldr sympy.polys.domains.simpledomainr sympy.polys.polyclassesr sympy.polys.polyerrorsr rrrsympy.utilitiesrrrSr|r4r2rsV7",E+9'__"iGU. iGiGXBr4