| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111 | r"""Modules in number fields.The classes defined here allow us to work with finitely generated, freemodules, whose generators are algebraic numbers.There is an abstract base class called :py:class:`~.Module`, which has twoconcrete subclasses, :py:class:`~.PowerBasis` and :py:class:`~.Submodule`.Every module is defined by its basis, or set of generators:* For a :py:class:`~.PowerBasis`, the generators are the first $n$ powers  (starting with the zeroth) of an algebraic integer $\theta$ of degree $n$.  The :py:class:`~.PowerBasis` is constructed by passing either the minimal  polynomial of $\theta$, or an :py:class:`~.AlgebraicField` having $\theta$  as its primitive element.* For a :py:class:`~.Submodule`, the generators are a set of  $\mathbb{Q}$-linear combinations of the generators of another module. That  other module is then the "parent" of the :py:class:`~.Submodule`. The  coefficients of the $\mathbb{Q}$-linear combinations may be given by an  integer matrix, and a positive integer denominator. Each column of the matrix  defines a generator.>>> from sympy.polys import Poly, cyclotomic_poly, ZZ>>> from sympy.abc import x>>> from sympy.polys.matrices import DomainMatrix, DM>>> from sympy.polys.numberfields.modules import PowerBasis>>> T = Poly(cyclotomic_poly(5, x))>>> A = PowerBasis(T)>>> print(A)PowerBasis(x**4 + x**3 + x**2 + x + 1)>>> B = A.submodule_from_matrix(2 * DomainMatrix.eye(4, ZZ), denom=3)>>> print(B)Submodule[[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]/3>>> print(B.parent)PowerBasis(x**4 + x**3 + x**2 + x + 1)Thus, every module is either a :py:class:`~.PowerBasis`,or a :py:class:`~.Submodule`, some ancestor of which is a:py:class:`~.PowerBasis`. (If ``S`` is a :py:class:`~.Submodule`, then itsancestors are ``S.parent``, ``S.parent.parent``, and so on).The :py:class:`~.ModuleElement` class represents a linear combination of thegenerators of any module. Critically, the coefficients of this linearcombination are not restricted to be integers, but may be any rationalnumbers. This is necessary so that any and all algebraic integers berepresentable, starting from the power basis in a primitive element $\theta$for the number field in question. For example, in a quadratic field$\mathbb{Q}(\sqrt{d})$ where $d \equiv 1 \mod{4}$, a denominator of $2$ isneeded.A :py:class:`~.ModuleElement` can be constructed from an integer column vectorand a denominator:>>> U = Poly(x**2 - 5)>>> M = PowerBasis(U)>>> e = M(DM([[1], [1]], ZZ), denom=2)>>> print(e)[1, 1]/2>>> print(e.module)PowerBasis(x**2 - 5)The :py:class:`~.PowerBasisElement` class is a subclass of:py:class:`~.ModuleElement` that represents elements of a:py:class:`~.PowerBasis`, and adds functionality pertinent to elementsrepresented directly over powers of the primitive element $\theta$.Arithmetic with module elements===============================While a :py:class:`~.ModuleElement` represents a linear combination over thegenerators of a particular module, recall that every module is either a:py:class:`~.PowerBasis` or a descendant (along a chain of:py:class:`~.Submodule` objects) thereof, so that in fact every:py:class:`~.ModuleElement` represents an algebraic number in some field$\mathbb{Q}(\theta)$, where $\theta$ is the defining element of some:py:class:`~.PowerBasis`. It thus makes sense to talk about the number fieldto which a given :py:class:`~.ModuleElement` belongs.This means that any two :py:class:`~.ModuleElement` instances can be added,subtracted, multiplied, or divided, provided they belong to the same numberfield. Similarly, since $\mathbb{Q}$ is a subfield of every number field,any :py:class:`~.ModuleElement` may be added, multiplied, etc. by anyrational number.>>> from sympy import QQ>>> from sympy.polys.numberfields.modules import to_col>>> T = Poly(cyclotomic_poly(5))>>> A = PowerBasis(T)>>> C = A.submodule_from_matrix(3 * DomainMatrix.eye(4, ZZ))>>> e = A(to_col([0, 2, 0, 0]), denom=3)>>> f = A(to_col([0, 0, 0, 7]), denom=5)>>> g = C(to_col([1, 1, 1, 1]))>>> e + f[0, 10, 0, 21]/15>>> e - f[0, 10, 0, -21]/15>>> e - g[-9, -7, -9, -9]/3>>> e + QQ(7, 10)[21, 20, 0, 0]/30>>> e * f[-14, -14, -14, -14]/15>>> e ** 2[0, 0, 4, 0]/9>>> f // g[7, 7, 7, 7]/15>>> f * QQ(2, 3)[0, 0, 0, 14]/15However, care must be taken with arithmetic operations on:py:class:`~.ModuleElement`, because the module $C$ to which the result willbelong will be the nearest common ancestor (NCA) of the modules $A$, $B$ towhich the two operands belong, and $C$ may be different from either or bothof $A$ and $B$.>>> A = PowerBasis(T)>>> B = A.submodule_from_matrix(2 * DomainMatrix.eye(4, ZZ))>>> C = A.submodule_from_matrix(3 * DomainMatrix.eye(4, ZZ))>>> print((B(0) * C(0)).module == A)TrueBefore the arithmetic operation is performed, copies of the two operands areautomatically converted into elements of the NCA (the operands themselves arenot modified). This upward conversion along an ancestor chain is easy: it justrequires the successive multiplication by the defining matrix of each:py:class:`~.Submodule`.Conversely, downward conversion, i.e. representing a given:py:class:`~.ModuleElement` in a submodule, is also supported -- namely bythe :py:meth:`~sympy.polys.numberfields.modules.Submodule.represent` method-- but is not guaranteed to succeed in general, since the given element maynot belong to the submodule. The main circumstance in which this issue tendsto arise is with multiplication, since modules, while closed under addition,need not be closed under multiplication.Multiplication--------------Generally speaking, a module need not be closed under multiplication, i.e. neednot form a ring. However, many of the modules we work with in the context ofnumber fields are in fact rings, and our classes do support multiplication.Specifically, any :py:class:`~.Module` can attempt to compute its ownmultiplication table, but this does not happen unless an attempt is made tomultiply two :py:class:`~.ModuleElement` instances belonging to it.>>> A = PowerBasis(T)>>> print(A._mult_tab is None)True>>> a = A(0)*A(1)>>> print(A._mult_tab is None)FalseEvery :py:class:`~.PowerBasis` is, by its nature, closed under multiplication,so instances of :py:class:`~.PowerBasis` can always successfully compute theirmultiplication table.When a :py:class:`~.Submodule` attempts to compute its multiplication table,it converts each of its own generators into elements of its parent module,multiplies them there, in every possible pairing, and then tries torepresent the results in itself, i.e. as $\mathbb{Z}$-linear combinationsover its own generators. This will succeed if and only if the submodule isin fact closed under multiplication.Module Homomorphisms====================Many important number theoretic algorithms require the calculation of thekernel of one or more module homomorphisms. Accordingly we have severallightweight classes, :py:class:`~.ModuleHomomorphism`,:py:class:`~.ModuleEndomorphism`, :py:class:`~.InnerEndomorphism`, and:py:class:`~.EndomorphismRing`, which provide the minimal necessary machineryto support this."""from sympy.core.numbers import igcd, ilcmfrom sympy.core.symbol import Dummyfrom sympy.polys.polyclasses import ANPfrom sympy.polys.polytools import Polyfrom sympy.polys.densetools import dup_clear_denomsfrom sympy.polys.domains.algebraicfield import AlgebraicFieldfrom sympy.polys.domains.finitefield import FFfrom sympy.polys.domains.rationalfield import QQfrom sympy.polys.domains.integerring import ZZfrom sympy.polys.matrices.domainmatrix import DomainMatrixfrom sympy.polys.matrices.exceptions import DMBadInputErrorfrom sympy.polys.matrices.normalforms import hermite_normal_formfrom sympy.polys.polyerrors import CoercionFailed, UnificationFailedfrom sympy.polys.polyutils import IntegerPowerablefrom .exceptions import ClosureFailure, MissingUnityError, StructureErrorfrom .utilities import AlgIntPowers, is_rat, get_num_denomdef to_col(coeffs):    r"""Transform a list of integer coefficients into a column vector."""    return DomainMatrix([[ZZ(c) for c in coeffs]], (1, len(coeffs)), ZZ).transpose()class Module:    """    Generic finitely-generated module.    This is an abstract base class, and should not be instantiated directly.    The two concrete subclasses are :py:class:`~.PowerBasis` and    :py:class:`~.Submodule`.    Every :py:class:`~.Submodule` is derived from another module, referenced    by its ``parent`` attribute. If ``S`` is a submodule, then we refer to    ``S.parent``, ``S.parent.parent``, and so on, as the "ancestors" of    ``S``. Thus, every :py:class:`~.Module` is either a    :py:class:`~.PowerBasis` or a :py:class:`~.Submodule`, some ancestor of    which is a :py:class:`~.PowerBasis`.    """    @property    def n(self):        """The number of generators of this module."""        raise NotImplementedError    def mult_tab(self):        """        Get the multiplication table for this module (if closed under mult).        Explanation        ===========        Computes a dictionary ``M`` of dictionaries of lists, representing the        upper triangular half of the multiplication table.        In other words, if ``0 <= i <= j < self.n``, then ``M[i][j]`` is the        list ``c`` of coefficients such that        ``g[i] * g[j] == sum(c[k]*g[k], k in range(self.n))``,        where ``g`` is the list of generators of this module.        If ``j < i`` then ``M[i][j]`` is undefined.        Examples        ========        >>> from sympy.polys import Poly, cyclotomic_poly        >>> from sympy.polys.numberfields.modules import PowerBasis        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> print(A.mult_tab())  # doctest: +SKIP        {0: {0: [1, 0, 0, 0], 1: [0, 1, 0, 0], 2: [0, 0, 1, 0],     3: [0, 0, 0, 1]},                          1: {1: [0, 0, 1, 0], 2: [0, 0, 0, 1],     3: [-1, -1, -1, -1]},                                           2: {2: [-1, -1, -1, -1], 3: [1, 0, 0, 0]},                                                                3: {3: [0, 1, 0, 0]}}        Returns        =======        dict of dict of lists        Raises        ======        ClosureFailure            If the module is not closed under multiplication.        """        raise NotImplementedError    @property    def parent(self):        """        The parent module, if any, for this module.        Explanation        ===========        For a :py:class:`~.Submodule` this is its ``parent`` attribute; for a        :py:class:`~.PowerBasis` this is ``None``.        Returns        =======        :py:class:`~.Module`, ``None``        See Also        ========        Module        """        return None    def represent(self, elt):        r"""        Represent a module element as an integer-linear combination over the        generators of this module.        Explanation        ===========        In our system, to "represent" always means to write a        :py:class:`~.ModuleElement` as a :ref:`ZZ`-linear combination over the        generators of the present :py:class:`~.Module`. Furthermore, the        incoming :py:class:`~.ModuleElement` must belong to an ancestor of        the present :py:class:`~.Module` (or to the present        :py:class:`~.Module` itself).        The most common application is to represent a        :py:class:`~.ModuleElement` in a :py:class:`~.Submodule`. For example,        this is involved in computing multiplication tables.        On the other hand, representing in a :py:class:`~.PowerBasis` is an        odd case, and one which tends not to arise in practice, except for        example when using a :py:class:`~.ModuleEndomorphism` on a        :py:class:`~.PowerBasis`.        In such a case, (1) the incoming :py:class:`~.ModuleElement` must        belong to the :py:class:`~.PowerBasis` itself (since the latter has no        proper ancestors) and (2) it is "representable" iff it belongs to        $\mathbb{Z}[\theta]$ (although generally a        :py:class:`~.PowerBasisElement` may represent any element of        $\mathbb{Q}(\theta)$, i.e. any algebraic number).        Examples        ========        >>> from sympy import Poly, cyclotomic_poly        >>> from sympy.polys.numberfields.modules import PowerBasis, to_col        >>> from sympy.abc import zeta        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> a = A(to_col([2, 4, 6, 8]))        The :py:class:`~.ModuleElement` ``a`` has all even coefficients.        If we represent ``a`` in the submodule ``B = 2*A``, the coefficients in        the column vector will be halved:        >>> B = A.submodule_from_gens([2*A(i) for i in range(4)])        >>> b = B.represent(a)        >>> print(b.transpose())  # doctest: +SKIP        DomainMatrix([[1, 2, 3, 4]], (1, 4), ZZ)        However, the element of ``B`` so defined still represents the same        algebraic number:        >>> print(a.poly(zeta).as_expr())        8*zeta**3 + 6*zeta**2 + 4*zeta + 2        >>> print(B(b).over_power_basis().poly(zeta).as_expr())        8*zeta**3 + 6*zeta**2 + 4*zeta + 2        Parameters        ==========        elt : :py:class:`~.ModuleElement`            The module element to be represented. Must belong to some ancestor            module of this module (including this module itself).        Returns        =======        :py:class:`~.DomainMatrix` over :ref:`ZZ`            This will be a column vector, representing the coefficients of a            linear combination of this module's generators, which equals the            given element.        Raises        ======        ClosureFailure            If the given element cannot be represented as a :ref:`ZZ`-linear            combination over this module.        See Also        ========        .Submodule.represent        .PowerBasis.represent        """        raise NotImplementedError    def ancestors(self, include_self=False):        """        Return the list of ancestor modules of this module, from the        foundational :py:class:`~.PowerBasis` downward, optionally including        ``self``.        See Also        ========        Module        """        c = self.parent        a = [] if c is None else c.ancestors(include_self=True)        if include_self:            a.append(self)        return a    def power_basis_ancestor(self):        """        Return the :py:class:`~.PowerBasis` that is an ancestor of this module.        See Also        ========        Module        """        if isinstance(self, PowerBasis):            return self        c = self.parent        if c is not None:            return c.power_basis_ancestor()        return None    def nearest_common_ancestor(self, other):        """        Locate the nearest common ancestor of this module and another.        Returns        =======        :py:class:`~.Module`, ``None``        See Also        ========        Module        """        sA = self.ancestors(include_self=True)        oA = other.ancestors(include_self=True)        nca = None        for sa, oa in zip(sA, oA):            if sa == oa:                nca = sa            else:                break        return nca    @property    def number_field(self):        r"""        Return the associated :py:class:`~.AlgebraicField`, if any.        Explanation        ===========        A :py:class:`~.PowerBasis` can be constructed on a :py:class:`~.Poly`        $f$ or on an :py:class:`~.AlgebraicField` $K$. In the latter case, the        :py:class:`~.PowerBasis` and all its descendant modules will return $K$        as their ``.number_field`` property, while in the former case they will        all return ``None``.        Returns        =======        :py:class:`~.AlgebraicField`, ``None``        """        return self.power_basis_ancestor().number_field    def is_compat_col(self, col):        """Say whether *col* is a suitable column vector for this module."""        return isinstance(col, DomainMatrix) and col.shape == (self.n, 1) and col.domain.is_ZZ    def __call__(self, spec, denom=1):        r"""        Generate a :py:class:`~.ModuleElement` belonging to this module.        Examples        ========        >>> from sympy.polys import Poly, cyclotomic_poly        >>> from sympy.polys.numberfields.modules import PowerBasis, to_col        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> e = A(to_col([1, 2, 3, 4]), denom=3)        >>> print(e)  # doctest: +SKIP        [1, 2, 3, 4]/3        >>> f = A(2)        >>> print(f)  # doctest: +SKIP        [0, 0, 1, 0]        Parameters        ==========        spec : :py:class:`~.DomainMatrix`, int            Specifies the numerators of the coefficients of the            :py:class:`~.ModuleElement`. Can be either a column vector over            :ref:`ZZ`, whose length must equal the number $n$ of generators of            this module, or else an integer ``j``, $0 \leq j < n$, which is a            shorthand for column $j$ of $I_n$, the $n \times n$ identity            matrix.        denom : int, optional (default=1)            Denominator for the coefficients of the            :py:class:`~.ModuleElement`.        Returns        =======        :py:class:`~.ModuleElement`            The coefficients are the entries of the *spec* vector, divided by            *denom*.        """        if isinstance(spec, int) and 0 <= spec < self.n:            spec = DomainMatrix.eye(self.n, ZZ)[:, spec].to_dense()        if not self.is_compat_col(spec):            raise ValueError('Compatible column vector required.')        return make_mod_elt(self, spec, denom=denom)    def starts_with_unity(self):        """Say whether the module's first generator equals unity."""        raise NotImplementedError    def basis_elements(self):        """        Get list of :py:class:`~.ModuleElement` being the generators of this        module.        """        return [self(j) for j in range(self.n)]    def zero(self):        """Return a :py:class:`~.ModuleElement` representing zero."""        return self(0) * 0    def one(self):        """        Return a :py:class:`~.ModuleElement` representing unity,        and belonging to the first ancestor of this module (including        itself) that starts with unity.        """        return self.element_from_rational(1)    def element_from_rational(self, a):        """        Return a :py:class:`~.ModuleElement` representing a rational number.        Explanation        ===========        The returned :py:class:`~.ModuleElement` will belong to the first        module on this module's ancestor chain (including this module        itself) that starts with unity.        Examples        ========        >>> from sympy.polys import Poly, cyclotomic_poly, QQ        >>> from sympy.polys.numberfields.modules import PowerBasis        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> a = A.element_from_rational(QQ(2, 3))        >>> print(a)  # doctest: +SKIP        [2, 0, 0, 0]/3        Parameters        ==========        a : int, :ref:`ZZ`, :ref:`QQ`        Returns        =======        :py:class:`~.ModuleElement`        """        raise NotImplementedError    def submodule_from_gens(self, gens, hnf=True, hnf_modulus=None):        """        Form the submodule generated by a list of :py:class:`~.ModuleElement`        belonging to this module.        Examples        ========        >>> from sympy.polys import Poly, cyclotomic_poly        >>> from sympy.polys.numberfields.modules import PowerBasis        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> gens = [A(0), 2*A(1), 3*A(2), 4*A(3)//5]        >>> B = A.submodule_from_gens(gens)        >>> print(B)  # doctest: +SKIP        Submodule[[5, 0, 0, 0], [0, 10, 0, 0], [0, 0, 15, 0], [0, 0, 0, 4]]/5        Parameters        ==========        gens : list of :py:class:`~.ModuleElement` belonging to this module.        hnf : boolean, optional (default=True)            If True, we will reduce the matrix into Hermite Normal Form before            forming the :py:class:`~.Submodule`.        hnf_modulus : int, None, optional (default=None)            Modulus for use in the HNF reduction algorithm. See            :py:func:`~sympy.polys.matrices.normalforms.hermite_normal_form`.        Returns        =======        :py:class:`~.Submodule`        See Also        ========        submodule_from_matrix        """        if not all(g.module == self for g in gens):            raise ValueError('Generators must belong to this module.')        n = len(gens)        if n == 0:            raise ValueError('Need at least one generator.')        m = gens[0].n        d = gens[0].denom if n == 1 else ilcm(*[g.denom for g in gens])        B = DomainMatrix.zeros((m, 0), ZZ).hstack(*[(d // g.denom) * g.col for g in gens])        if hnf:            B = hermite_normal_form(B, D=hnf_modulus)        return self.submodule_from_matrix(B, denom=d)    def submodule_from_matrix(self, B, denom=1):        """        Form the submodule generated by the elements of this module indicated        by the columns of a matrix, with an optional denominator.        Examples        ========        >>> from sympy.polys import Poly, cyclotomic_poly, ZZ        >>> from sympy.polys.matrices import DM        >>> from sympy.polys.numberfields.modules import PowerBasis        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> B = A.submodule_from_matrix(DM([        ...     [0, 10, 0, 0],        ...     [0,  0, 7, 0],        ... ], ZZ).transpose(), denom=15)        >>> print(B)  # doctest: +SKIP        Submodule[[0, 10, 0, 0], [0, 0, 7, 0]]/15        Parameters        ==========        B : :py:class:`~.DomainMatrix` over :ref:`ZZ`            Each column gives the numerators of the coefficients of one            generator of the submodule. Thus, the number of rows of *B* must            equal the number of generators of the present module.        denom : int, optional (default=1)            Common denominator for all generators of the submodule.        Returns        =======        :py:class:`~.Submodule`        Raises        ======        ValueError            If the given matrix *B* is not over :ref:`ZZ` or its number of rows            does not equal the number of generators of the present module.        See Also        ========        submodule_from_gens        """        m, n = B.shape        if not B.domain.is_ZZ:            raise ValueError('Matrix must be over ZZ.')        if not m == self.n:            raise ValueError('Matrix row count must match base module.')        return Submodule(self, B, denom=denom)    def whole_submodule(self):        """        Return a submodule equal to this entire module.        Explanation        ===========        This is useful when you have a :py:class:`~.PowerBasis` and want to        turn it into a :py:class:`~.Submodule` (in order to use methods        belonging to the latter).        """        B = DomainMatrix.eye(self.n, ZZ)        return self.submodule_from_matrix(B)    def endomorphism_ring(self):        """Form the :py:class:`~.EndomorphismRing` for this module."""        return EndomorphismRing(self)class PowerBasis(Module):    """The module generated by the powers of an algebraic integer."""    def __init__(self, T):        """        Parameters        ==========        T : :py:class:`~.Poly`, :py:class:`~.AlgebraicField`            Either (1) the monic, irreducible, univariate polynomial over            :ref:`ZZ`, a root of which is the generator of the power basis,            or (2) an :py:class:`~.AlgebraicField` whose primitive element            is the generator of the power basis.        """        K = None        if isinstance(T, AlgebraicField):            K, T = T, T.ext.minpoly_of_element()        # Sometimes incoming Polys are formally over QQ, although all their        # coeffs are integral. We want them to be formally over ZZ.        T = T.set_domain(ZZ)        self.K = K        self.T = T        self._n = T.degree()        self._mult_tab = None    @property    def number_field(self):        return self.K    def __repr__(self):        return f'PowerBasis({self.T.as_expr()})'    def __eq__(self, other):        if isinstance(other, PowerBasis):            return self.T == other.T        return NotImplemented    @property    def n(self):        return self._n    def mult_tab(self):        if self._mult_tab is None:            self.compute_mult_tab()        return self._mult_tab    def compute_mult_tab(self):        theta_pow = AlgIntPowers(self.T)        M = {}        n = self.n        for u in range(n):            M[u] = {}            for v in range(u, n):                M[u][v] = theta_pow[u + v]        self._mult_tab = M    def represent(self, elt):        r"""        Represent a module element as an integer-linear combination over the        generators of this module.        See Also        ========        .Module.represent        .Submodule.represent        """        if elt.module == self and elt.denom == 1:            return elt.column()        else:            raise ClosureFailure('Element not representable in ZZ[theta].')    def starts_with_unity(self):        return True    def element_from_rational(self, a):        return self(0) * a    def element_from_poly(self, f):        """        Produce an element of this module, representing *f* after reduction mod        our defining minimal polynomial.        Parameters        ==========        f : :py:class:`~.Poly` over :ref:`ZZ` in same var as our defining poly.        Returns        =======        :py:class:`~.PowerBasisElement`        """        n, k = self.n, f.degree()        if k >= n:            f = f % self.T        if f == 0:            return self.zero()        d, c = dup_clear_denoms(f.rep.rep, QQ, convert=True)        c = list(reversed(c))        ell = len(c)        z = [ZZ(0)] * (n - ell)        col = to_col(c + z)        return self(col, denom=d)    def _element_from_rep_and_mod(self, rep, mod):        """        Produce a PowerBasisElement representing a given algebraic number.        Parameters        ==========        rep : list of coeffs            Represents the number as polynomial in the primitive element of the            field.        mod : list of coeffs            Represents the minimal polynomial of the primitive element of the            field.        Returns        =======        :py:class:`~.PowerBasisElement`        """        if mod != self.T.rep.rep:            raise UnificationFailed('Element does not appear to be in the same field.')        return self.element_from_poly(Poly(rep, self.T.gen))    def element_from_ANP(self, a):        """Convert an ANP into a PowerBasisElement. """        return self._element_from_rep_and_mod(a.rep, a.mod)    def element_from_alg_num(self, a):        """Convert an AlgebraicNumber into a PowerBasisElement. """        return self._element_from_rep_and_mod(a.rep.rep, a.minpoly.rep.rep)class Submodule(Module, IntegerPowerable):    """A submodule of another module."""    def __init__(self, parent, matrix, denom=1, mult_tab=None):        """        Parameters        ==========        parent : :py:class:`~.Module`            The module from which this one is derived.        matrix : :py:class:`~.DomainMatrix` over :ref:`ZZ`            The matrix whose columns define this submodule's generators as            linear combinations over the parent's generators.        denom : int, optional (default=1)            Denominator for the coefficients given by the matrix.        mult_tab : dict, ``None``, optional            If already known, the multiplication table for this module may be            supplied.        """        self._parent = parent        self._matrix = matrix        self._denom = denom        self._mult_tab = mult_tab        self._n = matrix.shape[1]        self._QQ_matrix = None        self._starts_with_unity = None        self._is_sq_maxrank_HNF = None    def __repr__(self):        r = 'Submodule' + repr(self.matrix.transpose().to_Matrix().tolist())        if self.denom > 1:            r += f'/{self.denom}'        return r    def reduced(self):        """        Produce a reduced version of this submodule.        Explanation        ===========        In the reduced version, it is guaranteed that 1 is the only positive        integer dividing both the submodule's denominator, and every entry in        the submodule's matrix.        Returns        =======        :py:class:`~.Submodule`        """        if self.denom == 1:            return self        g = igcd(self.denom, *self.coeffs)        if g == 1:            return self        return type(self)(self.parent, (self.matrix / g).convert_to(ZZ), denom=self.denom // g, mult_tab=self._mult_tab)    def discard_before(self, r):        """        Produce a new module by discarding all generators before a given        index *r*.        """        W = self.matrix[:, r:]        s = self.n - r        M = None        mt = self._mult_tab        if mt is not None:            M = {}            for u in range(s):                M[u] = {}                for v in range(u, s):                    M[u][v] = mt[r + u][r + v][r:]        return Submodule(self.parent, W, denom=self.denom, mult_tab=M)    @property    def n(self):        return self._n    def mult_tab(self):        if self._mult_tab is None:            self.compute_mult_tab()        return self._mult_tab    def compute_mult_tab(self):        gens = self.basis_element_pullbacks()        M = {}        n = self.n        for u in range(n):            M[u] = {}            for v in range(u, n):                M[u][v] = self.represent(gens[u] * gens[v]).flat()        self._mult_tab = M    @property    def parent(self):        return self._parent    @property    def matrix(self):        return self._matrix    @property    def coeffs(self):        return self.matrix.flat()    @property    def denom(self):        return self._denom    @property    def QQ_matrix(self):        """        :py:class:`~.DomainMatrix` over :ref:`QQ`, equal to        ``self.matrix / self.denom``, and guaranteed to be dense.        Explanation        ===========        Depending on how it is formed, a :py:class:`~.DomainMatrix` may have        an internal representation that is sparse or dense. We guarantee a        dense representation here, so that tests for equivalence of submodules        always come out as expected.        Examples        ========        >>> from sympy.polys import Poly, cyclotomic_poly, ZZ        >>> from sympy.abc import x        >>> from sympy.polys.matrices import DomainMatrix        >>> from sympy.polys.numberfields.modules import PowerBasis        >>> T = Poly(cyclotomic_poly(5, x))        >>> A = PowerBasis(T)        >>> B = A.submodule_from_matrix(3*DomainMatrix.eye(4, ZZ), denom=6)        >>> C = A.submodule_from_matrix(DomainMatrix.eye(4, ZZ), denom=2)        >>> print(B.QQ_matrix == C.QQ_matrix)        True        Returns        =======        :py:class:`~.DomainMatrix` over :ref:`QQ`        """        if self._QQ_matrix is None:            self._QQ_matrix = (self.matrix / self.denom).to_dense()        return self._QQ_matrix    def starts_with_unity(self):        if self._starts_with_unity is None:            self._starts_with_unity = self(0).equiv(1)        return self._starts_with_unity    def is_sq_maxrank_HNF(self):        if self._is_sq_maxrank_HNF is None:            self._is_sq_maxrank_HNF = is_sq_maxrank_HNF(self._matrix)        return self._is_sq_maxrank_HNF    def is_power_basis_submodule(self):        return isinstance(self.parent, PowerBasis)    def element_from_rational(self, a):        if self.starts_with_unity():            return self(0) * a        else:            return self.parent.element_from_rational(a)    def basis_element_pullbacks(self):        """        Return list of this submodule's basis elements as elements of the        submodule's parent module.        """        return [e.to_parent() for e in self.basis_elements()]    def represent(self, elt):        """        Represent a module element as an integer-linear combination over the        generators of this module.        See Also        ========        .Module.represent        .PowerBasis.represent        """        if elt.module == self:            return elt.column()        elif elt.module == self.parent:            try:                # The given element should be a ZZ-linear combination over our                # basis vectors; however, due to the presence of denominators,                # we need to solve over QQ.                A = self.QQ_matrix                b = elt.QQ_col                x = A._solve(b)[0].transpose()                x = x.convert_to(ZZ)            except DMBadInputError:                raise ClosureFailure('Element outside QQ-span of this basis.')            except CoercionFailed:                raise ClosureFailure('Element in QQ-span but not ZZ-span of this basis.')            return x        elif isinstance(self.parent, Submodule):            coeffs_in_parent = self.parent.represent(elt)            parent_element = self.parent(coeffs_in_parent)            return self.represent(parent_element)        else:            raise ClosureFailure('Element outside ancestor chain of this module.')    def is_compat_submodule(self, other):        return isinstance(other, Submodule) and other.parent == self.parent    def __eq__(self, other):        if self.is_compat_submodule(other):            return other.QQ_matrix == self.QQ_matrix        return NotImplemented    def add(self, other, hnf=True, hnf_modulus=None):        """        Add this :py:class:`~.Submodule` to another.        Explanation        ===========        This represents the module generated by the union of the two modules'        sets of generators.        Parameters        ==========        other : :py:class:`~.Submodule`        hnf : boolean, optional (default=True)            If ``True``, reduce the matrix of the combined module to its            Hermite Normal Form.        hnf_modulus : :ref:`ZZ`, None, optional            If a positive integer is provided, use this as modulus in the            HNF reduction. See            :py:func:`~sympy.polys.matrices.normalforms.hermite_normal_form`.        Returns        =======        :py:class:`~.Submodule`        """        d, e = self.denom, other.denom        m = ilcm(d, e)        a, b = m // d, m // e        B = (a * self.matrix).hstack(b * other.matrix)        if hnf:            B = hermite_normal_form(B, D=hnf_modulus)        return self.parent.submodule_from_matrix(B, denom=m)    def __add__(self, other):        if self.is_compat_submodule(other):            return self.add(other)        return NotImplemented    __radd__ = __add__    def mul(self, other, hnf=True, hnf_modulus=None):        """        Multiply this :py:class:`~.Submodule` by a rational number, a        :py:class:`~.ModuleElement`, or another :py:class:`~.Submodule`.        Explanation        ===========        To multiply by a rational number or :py:class:`~.ModuleElement` means        to form the submodule whose generators are the products of this        quantity with all the generators of the present submodule.        To multiply by another :py:class:`~.Submodule` means to form the        submodule whose generators are all the products of one generator from        the one submodule, and one generator from the other.        Parameters        ==========        other : int, :ref:`ZZ`, :ref:`QQ`, :py:class:`~.ModuleElement`, :py:class:`~.Submodule`        hnf : boolean, optional (default=True)            If ``True``, reduce the matrix of the product module to its            Hermite Normal Form.        hnf_modulus : :ref:`ZZ`, None, optional            If a positive integer is provided, use this as modulus in the            HNF reduction. See            :py:func:`~sympy.polys.matrices.normalforms.hermite_normal_form`.        Returns        =======        :py:class:`~.Submodule`        """        if is_rat(other):            a, b = get_num_denom(other)            if a == b == 1:                return self            else:                return Submodule(self.parent,                             self.matrix * a, denom=self.denom * b,                             mult_tab=None).reduced()        elif isinstance(other, ModuleElement) and other.module == self.parent:            # The submodule is multiplied by an element of the parent module.            # We presume this means we want a new submodule of the parent module.            gens = [other * e for e in self.basis_element_pullbacks()]            return self.parent.submodule_from_gens(gens, hnf=hnf, hnf_modulus=hnf_modulus)        elif self.is_compat_submodule(other):            # This case usually means you're multiplying ideals, and want another            # ideal, i.e. another submodule of the same parent module.            alphas, betas = self.basis_element_pullbacks(), other.basis_element_pullbacks()            gens = [a * b for a in alphas for b in betas]            return self.parent.submodule_from_gens(gens, hnf=hnf, hnf_modulus=hnf_modulus)        return NotImplemented    def __mul__(self, other):        return self.mul(other)    __rmul__ = __mul__    def _first_power(self):        return self    def reduce_element(self, elt):        r"""        If this submodule $B$ has defining matrix $W$ in square, maximal-rank        Hermite normal form, then, given an element $x$ of the parent module        $A$, we produce an element $y \in A$ such that $x - y \in B$, and the        $i$th coordinate of $y$ satisfies $0 \leq y_i < w_{i,i}$. This        representative $y$ is unique, in the sense that every element of        the coset $x + B$ reduces to it under this procedure.        Explanation        ===========        In the special case where $A$ is a power basis for a number field $K$,        and $B$ is a submodule representing an ideal $I$, this operation        represents one of a few important ways of reducing an element of $K$        modulo $I$ to obtain a "small" representative. See [Cohen00]_ Section        1.4.3.        Examples        ========        >>> from sympy import QQ, Poly, symbols        >>> t = symbols('t')        >>> k = QQ.alg_field_from_poly(Poly(t**3 + t**2 - 2*t + 8))        >>> Zk = k.maximal_order()        >>> A = Zk.parent        >>> B = (A(2) - 3*A(0))*Zk        >>> B.reduce_element(A(2))        [3, 0, 0]        Parameters        ==========        elt : :py:class:`~.ModuleElement`            An element of this submodule's parent module.        Returns        =======        elt : :py:class:`~.ModuleElement`            An element of this submodule's parent module.        Raises        ======        NotImplementedError            If the given :py:class:`~.ModuleElement` does not belong to this            submodule's parent module.        StructureError            If this submodule's defining matrix is not in square, maximal-rank            Hermite normal form.        References        ==========        .. [Cohen00] Cohen, H. *Advanced Topics in Computational Number           Theory.*        """        if not elt.module == self.parent:            raise NotImplementedError        if not self.is_sq_maxrank_HNF():            msg = "Reduction not implemented unless matrix square max-rank HNF"            raise StructureError(msg)        B = self.basis_element_pullbacks()        a = elt        for i in range(self.n - 1, -1, -1):            b = B[i]            q = a.coeffs[i]*b.denom // (b.coeffs[i]*a.denom)            a -= q*b        return adef is_sq_maxrank_HNF(dm):    r"""    Say whether a :py:class:`~.DomainMatrix` is in that special case of Hermite    Normal Form, in which the matrix is also square and of maximal rank.    Explanation    ===========    We commonly work with :py:class:`~.Submodule` instances whose matrix is in    this form, and it can be useful to be able to check that this condition is    satisfied.    For example this is the case with the :py:class:`~.Submodule` ``ZK``    returned by :py:func:`~sympy.polys.numberfields.basis.round_two`, which    represents the maximal order in a number field, and with ideals formed    therefrom, such as ``2 * ZK``.    """    if dm.domain.is_ZZ and dm.is_square and dm.is_upper:        n = dm.shape[0]        for i in range(n):            d = dm[i, i].element            if d <= 0:                return False            for j in range(i + 1, n):                if not (0 <= dm[i, j].element < d):                    return False        return True    return Falsedef make_mod_elt(module, col, denom=1):    r"""    Factory function which builds a :py:class:`~.ModuleElement`, but ensures    that it is a :py:class:`~.PowerBasisElement` if the module is a    :py:class:`~.PowerBasis`.    """    if isinstance(module, PowerBasis):        return PowerBasisElement(module, col, denom=denom)    else:        return ModuleElement(module, col, denom=denom)class ModuleElement(IntegerPowerable):    r"""    Represents an element of a :py:class:`~.Module`.    NOTE: Should not be constructed directly. Use the    :py:meth:`~.Module.__call__` method or the :py:func:`make_mod_elt()`    factory function instead.    """    def __init__(self, module, col, denom=1):        """        Parameters        ==========        module : :py:class:`~.Module`            The module to which this element belongs.        col : :py:class:`~.DomainMatrix` over :ref:`ZZ`            Column vector giving the numerators of the coefficients of this            element.        denom : int, optional (default=1)            Denominator for the coefficients of this element.        """        self.module = module        self.col = col        self.denom = denom        self._QQ_col = None    def __repr__(self):        r = str([int(c) for c in self.col.flat()])        if self.denom > 1:            r += f'/{self.denom}'        return r    def reduced(self):        """        Produce a reduced version of this ModuleElement, i.e. one in which the        gcd of the denominator together with all numerator coefficients is 1.        """        if self.denom == 1:            return self        g = igcd(self.denom, *self.coeffs)        if g == 1:            return self        return type(self)(self.module,                            (self.col / g).convert_to(ZZ),                            denom=self.denom // g)    def reduced_mod_p(self, p):        """        Produce a version of this :py:class:`~.ModuleElement` in which all        numerator coefficients have been reduced mod *p*.        """        return make_mod_elt(self.module,                            self.col.convert_to(FF(p)).convert_to(ZZ),                            denom=self.denom)    @classmethod    def from_int_list(cls, module, coeffs, denom=1):        """        Make a :py:class:`~.ModuleElement` from a list of ints (instead of a        column vector).        """        col = to_col(coeffs)        return cls(module, col, denom=denom)    @property    def n(self):        """The length of this element's column."""        return self.module.n    def __len__(self):        return self.n    def column(self, domain=None):        """        Get a copy of this element's column, optionally converting to a domain.        """        return self.col.convert_to(domain)    @property    def coeffs(self):        return self.col.flat()    @property    def QQ_col(self):        """        :py:class:`~.DomainMatrix` over :ref:`QQ`, equal to        ``self.col / self.denom``, and guaranteed to be dense.        See Also        ========        .Submodule.QQ_matrix        """        if self._QQ_col is None:            self._QQ_col = (self.col / self.denom).to_dense()        return self._QQ_col    def to_parent(self):        """        Transform into a :py:class:`~.ModuleElement` belonging to the parent of        this element's module.        """        if not isinstance(self.module, Submodule):            raise ValueError('Not an element of a Submodule.')        return make_mod_elt(            self.module.parent, self.module.matrix * self.col,            denom=self.module.denom * self.denom)    def to_ancestor(self, anc):        """        Transform into a :py:class:`~.ModuleElement` belonging to a given        ancestor of this element's module.        Parameters        ==========        anc : :py:class:`~.Module`        """        if anc == self.module:            return self        else:            return self.to_parent().to_ancestor(anc)    def over_power_basis(self):        """        Transform into a :py:class:`~.PowerBasisElement` over our        :py:class:`~.PowerBasis` ancestor.        """        e = self        while not isinstance(e.module, PowerBasis):            e = e.to_parent()        return e    def is_compat(self, other):        """        Test whether other is another :py:class:`~.ModuleElement` with same        module.        """        return isinstance(other, ModuleElement) and other.module == self.module    def unify(self, other):        """        Try to make a compatible pair of :py:class:`~.ModuleElement`, one        equivalent to this one, and one equivalent to the other.        Explanation        ===========        We search for the nearest common ancestor module for the pair of        elements, and represent each one there.        Returns        =======        Pair ``(e1, e2)``            Each ``ei`` is a :py:class:`~.ModuleElement`, they belong to the            same :py:class:`~.Module`, ``e1`` is equivalent to ``self``, and            ``e2`` is equivalent to ``other``.        Raises        ======        UnificationFailed            If ``self`` and ``other`` have no common ancestor module.        """        if self.module == other.module:            return self, other        nca = self.module.nearest_common_ancestor(other.module)        if nca is not None:            return self.to_ancestor(nca), other.to_ancestor(nca)        raise UnificationFailed(f"Cannot unify {self} with {other}")    def __eq__(self, other):        if self.is_compat(other):            return self.QQ_col == other.QQ_col        return NotImplemented    def equiv(self, other):        """        A :py:class:`~.ModuleElement` may test as equivalent to a rational        number or another :py:class:`~.ModuleElement`, if they represent the        same algebraic number.        Explanation        ===========        This method is intended to check equivalence only in those cases in        which it is easy to test; namely, when *other* is either a        :py:class:`~.ModuleElement` that can be unified with this one (i.e. one        which shares a common :py:class:`~.PowerBasis` ancestor), or else a        rational number (which is easy because every :py:class:`~.PowerBasis`        represents every rational number).        Parameters        ==========        other : int, :ref:`ZZ`, :ref:`QQ`, :py:class:`~.ModuleElement`        Returns        =======        bool        Raises        ======        UnificationFailed            If ``self`` and ``other`` do not share a common            :py:class:`~.PowerBasis` ancestor.        """        if self == other:            return True        elif isinstance(other, ModuleElement):            a, b = self.unify(other)            return a == b        elif is_rat(other):            if isinstance(self, PowerBasisElement):                return self == self.module(0) * other            else:                return self.over_power_basis().equiv(other)        return False    def __add__(self, other):        """        A :py:class:`~.ModuleElement` can be added to a rational number, or to        another :py:class:`~.ModuleElement`.        Explanation        ===========        When the other summand is a rational number, it will be converted into        a :py:class:`~.ModuleElement` (belonging to the first ancestor of this        module that starts with unity).        In all cases, the sum belongs to the nearest common ancestor (NCA) of        the modules of the two summands. If the NCA does not exist, we return        ``NotImplemented``.        """        if self.is_compat(other):            d, e = self.denom, other.denom            m = ilcm(d, e)            u, v = m // d, m // e            col = to_col([u * a + v * b for a, b in zip(self.coeffs, other.coeffs)])            return type(self)(self.module, col, denom=m).reduced()        elif isinstance(other, ModuleElement):            try:                a, b = self.unify(other)            except UnificationFailed:                return NotImplemented            return a + b        elif is_rat(other):            return self + self.module.element_from_rational(other)        return NotImplemented    __radd__ = __add__    def __neg__(self):        return self * -1    def __sub__(self, other):        return self + (-other)    def __rsub__(self, other):        return -self + other    def __mul__(self, other):        """        A :py:class:`~.ModuleElement` can be multiplied by a rational number,        or by another :py:class:`~.ModuleElement`.        Explanation        ===========        When the multiplier is a rational number, the product is computed by        operating directly on the coefficients of this        :py:class:`~.ModuleElement`.        When the multiplier is another :py:class:`~.ModuleElement`, the product        will belong to the nearest common ancestor (NCA) of the modules of the        two operands, and that NCA must have a multiplication table. If the NCA        does not exist, we return ``NotImplemented``. If the NCA does not have        a mult. table, ``ClosureFailure`` will be raised.        """        if self.is_compat(other):            M = self.module.mult_tab()            A, B = self.col.flat(), other.col.flat()            n = self.n            C = [0] * n            for u in range(n):                for v in range(u, n):                    c = A[u] * B[v]                    if v > u:                        c += A[v] * B[u]                    if c != 0:                        R = M[u][v]                        for k in range(n):                            C[k] += c * R[k]            d = self.denom * other.denom            return self.from_int_list(self.module, C, denom=d)        elif isinstance(other, ModuleElement):            try:                a, b = self.unify(other)            except UnificationFailed:                return NotImplemented            return a * b        elif is_rat(other):            a, b = get_num_denom(other)            if a == b == 1:                return self            else:                return make_mod_elt(self.module,                                 self.col * a, denom=self.denom * b).reduced()        return NotImplemented    __rmul__ = __mul__    def _zeroth_power(self):        return self.module.one()    def _first_power(self):        return self    def __floordiv__(self, a):        if is_rat(a):            a = QQ(a)            return self * (1/a)        elif isinstance(a, ModuleElement):            return self * (1//a)        return NotImplemented    def __rfloordiv__(self, a):        return a // self.over_power_basis()    def __mod__(self, m):        r"""        Reduce this :py:class:`~.ModuleElement` mod a :py:class:`~.Submodule`.        Parameters        ==========        m : int, :ref:`ZZ`, :ref:`QQ`, :py:class:`~.Submodule`            If a :py:class:`~.Submodule`, reduce ``self`` relative to this.            If an integer or rational, reduce relative to the            :py:class:`~.Submodule` that is our own module times this constant.        See Also        ========        .Submodule.reduce_element        """        if is_rat(m):            m = m * self.module.whole_submodule()        if isinstance(m, Submodule) and m.parent == self.module:            return m.reduce_element(self)        return NotImplementedclass PowerBasisElement(ModuleElement):    r"""    Subclass for :py:class:`~.ModuleElement` instances whose module is a    :py:class:`~.PowerBasis`.    """    @property    def T(self):        """Access the defining polynomial of the :py:class:`~.PowerBasis`."""        return self.module.T    def numerator(self, x=None):        """Obtain the numerator as a polynomial over :ref:`ZZ`."""        x = x or self.T.gen        return Poly(reversed(self.coeffs), x, domain=ZZ)    def poly(self, x=None):        """Obtain the number as a polynomial over :ref:`QQ`."""        return self.numerator(x=x) // self.denom    @property    def is_rational(self):        """Say whether this element represents a rational number."""        return self.col[1:, :].is_zero_matrix    @property    def generator(self):        """        Return a :py:class:`~.Symbol` to be used when expressing this element        as a polynomial.        If we have an associated :py:class:`~.AlgebraicField` whose primitive        element has an alias symbol, we use that. Otherwise we use the variable        of the minimal polynomial defining the power basis to which we belong.        """        K = self.module.number_field        return K.ext.alias if K and K.ext.is_aliased else self.T.gen    def as_expr(self, x=None):        """Create a Basic expression from ``self``. """        return self.poly(x or self.generator).as_expr()    def norm(self, T=None):        """Compute the norm of this number."""        T = T or self.T        x = T.gen        A = self.numerator(x=x)        return T.resultant(A) // self.denom ** self.n    def inverse(self):        f = self.poly()        f_inv = f.invert(self.T)        return self.module.element_from_poly(f_inv)    def __rfloordiv__(self, a):        return self.inverse() * a    def _negative_power(self, e, modulo=None):        return self.inverse() ** abs(e)    def to_ANP(self):        """Convert to an equivalent :py:class:`~.ANP`. """        return ANP(list(reversed(self.QQ_col.flat())), QQ.map(self.T.rep.rep), QQ)    def to_alg_num(self):        """        Try to convert to an equivalent :py:class:`~.AlgebraicNumber`.        Explanation        ===========        In general, the conversion from an :py:class:`~.AlgebraicNumber` to a        :py:class:`~.PowerBasisElement` throws away information, because an        :py:class:`~.AlgebraicNumber` specifies a complex embedding, while a        :py:class:`~.PowerBasisElement` does not. However, in some cases it is        possible to convert a :py:class:`~.PowerBasisElement` back into an        :py:class:`~.AlgebraicNumber`, namely when the associated        :py:class:`~.PowerBasis` has a reference to an        :py:class:`~.AlgebraicField`.        Returns        =======        :py:class:`~.AlgebraicNumber`        Raises        ======        StructureError            If the :py:class:`~.PowerBasis` to which this element belongs does            not have an associated :py:class:`~.AlgebraicField`.        """        K = self.module.number_field        if K:            return K.to_alg_num(self.to_ANP())        raise StructureError("No associated AlgebraicField")class ModuleHomomorphism:    r"""A homomorphism from one module to another."""    def __init__(self, domain, codomain, mapping):        r"""        Parameters        ==========        domain : :py:class:`~.Module`            The domain of the mapping.        codomain : :py:class:`~.Module`            The codomain of the mapping.        mapping : callable            An arbitrary callable is accepted, but should be chosen so as            to represent an actual module homomorphism. In particular, should            accept elements of *domain* and return elements of *codomain*.        Examples        ========        >>> from sympy import Poly, cyclotomic_poly        >>> from sympy.polys.numberfields.modules import PowerBasis, ModuleHomomorphism        >>> T = Poly(cyclotomic_poly(5))        >>> A = PowerBasis(T)        >>> B = A.submodule_from_gens([2*A(j) for j in range(4)])        >>> phi = ModuleHomomorphism(A, B, lambda x: 6*x)        >>> print(phi.matrix())  # doctest: +SKIP        DomainMatrix([[3, 0, 0, 0], [0, 3, 0, 0], [0, 0, 3, 0], [0, 0, 0, 3]], (4, 4), ZZ)        """        self.domain = domain        self.codomain = codomain        self.mapping = mapping    def matrix(self, modulus=None):        r"""        Compute the matrix of this homomorphism.        Parameters        ==========        modulus : int, optional            A positive prime number $p$ if the matrix should be reduced mod            $p$.        Returns        =======        :py:class:`~.DomainMatrix`            The matrix is over :ref:`ZZ`, or else over :ref:`GF(p)` if a            modulus was given.        """        basis = self.domain.basis_elements()        cols = [self.codomain.represent(self.mapping(elt)) for elt in basis]        if not cols:            return DomainMatrix.zeros((self.codomain.n, 0), ZZ).to_dense()        M = cols[0].hstack(*cols[1:])        if modulus:            M = M.convert_to(FF(modulus))        return M    def kernel(self, modulus=None):        r"""        Compute a Submodule representing the kernel of this homomorphism.        Parameters        ==========        modulus : int, optional            A positive prime number $p$ if the kernel should be computed mod            $p$.        Returns        =======        :py:class:`~.Submodule`            This submodule's generators span the kernel of this            homomorphism over :ref:`ZZ`, or else over :ref:`GF(p)` if a            modulus was given.        """        M = self.matrix(modulus=modulus)        if modulus is None:            M = M.convert_to(QQ)        # Note: Even when working over a finite field, what we want here is        # the pullback into the integers, so in this case the conversion to ZZ        # below is appropriate. When working over ZZ, the kernel should be a        # ZZ-submodule, so, while the conversion to QQ above was required in        # order for the nullspace calculation to work, conversion back to ZZ        # afterward should always work.        # TODO:        #  Watch <https://github.com/sympy/sympy/issues/21834>, which calls        #  for fraction-free algorithms. If this is implemented, we can skip        #  the conversion to `QQ` above.        K = M.nullspace().convert_to(ZZ).transpose()        return self.domain.submodule_from_matrix(K)class ModuleEndomorphism(ModuleHomomorphism):    r"""A homomorphism from one module to itself."""    def __init__(self, domain, mapping):        r"""        Parameters        ==========        domain : :py:class:`~.Module`            The common domain and codomain of the mapping.        mapping : callable            An arbitrary callable is accepted, but should be chosen so as            to represent an actual module endomorphism. In particular, should            accept and return elements of *domain*.        """        super().__init__(domain, domain, mapping)class InnerEndomorphism(ModuleEndomorphism):    r"""    An inner endomorphism on a module, i.e. the endomorphism corresponding to    multiplication by a fixed element.    """    def __init__(self, domain, multiplier):        r"""        Parameters        ==========        domain : :py:class:`~.Module`            The domain and codomain of the endomorphism.        multiplier : :py:class:`~.ModuleElement`            The element $a$ defining the mapping as $x \mapsto a x$.        """        super().__init__(domain, lambda x: multiplier * x)        self.multiplier = multiplierclass EndomorphismRing:    r"""The ring of endomorphisms on a module."""    def __init__(self, domain):        """        Parameters        ==========        domain : :py:class:`~.Module`            The domain and codomain of the endomorphisms.        """        self.domain = domain    def inner_endomorphism(self, multiplier):        r"""        Form an inner endomorphism belonging to this endomorphism ring.        Parameters        ==========        multiplier : :py:class:`~.ModuleElement`            Element $a$ defining the inner endomorphism $x \mapsto a x$.        Returns        =======        :py:class:`~.InnerEndomorphism`        """        return InnerEndomorphism(self.domain, multiplier)    def represent(self, element):        r"""        Represent an element of this endomorphism ring, as a single column        vector.        Explanation        ===========        Let $M$ be a module, and $E$ its ring of endomorphisms. Let $N$ be        another module, and consider a homomorphism $\varphi: N \rightarrow E$.        In the event that $\varphi$ is to be represented by a matrix $A$, each        column of $A$ must represent an element of $E$. This is possible when        the elements of $E$ are themselves representable as matrices, by        stacking the columns of such a matrix into a single column.        This method supports calculating such matrices $A$, by representing        an element of this endomorphism ring first as a matrix, and then        stacking that matrix's columns into a single column.        Examples        ========        Note that in these examples we print matrix transposes, to make their        columns easier to inspect.        >>> from sympy import Poly, cyclotomic_poly        >>> from sympy.polys.numberfields.modules import PowerBasis        >>> from sympy.polys.numberfields.modules import ModuleHomomorphism        >>> T = Poly(cyclotomic_poly(5))        >>> M = PowerBasis(T)        >>> E = M.endomorphism_ring()        Let $\zeta$ be a primitive 5th root of unity, a generator of our field,        and consider the inner endomorphism $\tau$ on the ring of integers,        induced by $\zeta$:        >>> zeta = M(1)        >>> tau = E.inner_endomorphism(zeta)        >>> tau.matrix().transpose()  # doctest: +SKIP        DomainMatrix(            [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [-1, -1, -1, -1]],            (4, 4), ZZ)        The matrix representation of $\tau$ is as expected. The first column        shows that multiplying by $\zeta$ carries $1$ to $\zeta$, the second        column that it carries $\zeta$ to $\zeta^2$, and so forth.        The ``represent`` method of the endomorphism ring ``E`` stacks these        into a single column:        >>> E.represent(tau).transpose()  # doctest: +SKIP        DomainMatrix(            [[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1]],            (1, 16), ZZ)        This is useful when we want to consider a homomorphism $\varphi$ having        ``E`` as codomain:        >>> phi = ModuleHomomorphism(M, E, lambda x: E.inner_endomorphism(x))        and we want to compute the matrix of such a homomorphism:        >>> phi.matrix().transpose()  # doctest: +SKIP        DomainMatrix(            [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],            [0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1],            [0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1, 1, 0, 0, 0],            [0, 0, 0, 1, -1, -1, -1, -1, 1, 0, 0, 0, 0, 1, 0, 0]],            (4, 16), ZZ)        Note that the stacked matrix of $\tau$ occurs as the second column in        this example. This is because $\zeta$ is the second basis element of        ``M``, and $\varphi(\zeta) = \tau$.        Parameters        ==========        element : :py:class:`~.ModuleEndomorphism` belonging to this ring.        Returns        =======        :py:class:`~.DomainMatrix`            Column vector equalling the vertical stacking of all the columns            of the matrix that represents the given *element* as a mapping.        """        if isinstance(element, ModuleEndomorphism) and element.domain == self.domain:            M = element.matrix()            # Transform the matrix into a single column, which should reproduce            # the original columns, one after another.            m, n = M.shape            if n == 0:                return M            return M[:, 0].vstack(*[M[:, j] for j in range(1, n)])        raise NotImplementedErrordef find_min_poly(alpha, domain, x=None, powers=None):    r"""    Find a polynomial of least degree (not necessarily irreducible) satisfied    by an element of a finitely-generated ring with unity.    Examples    ========    For the $n$th cyclotomic field, $n$ an odd prime, consider the quadratic    equation whose roots are the two periods of length $(n-1)/2$. Article 356    of Gauss tells us that we should get $x^2 + x - (n-1)/4$ or    $x^2 + x + (n+1)/4$ according to whether $n$ is 1 or 3 mod 4, respectively.    >>> from sympy import Poly, cyclotomic_poly, primitive_root, QQ    >>> from sympy.abc import x    >>> from sympy.polys.numberfields.modules import PowerBasis, find_min_poly    >>> n = 13    >>> g = primitive_root(n)    >>> C = PowerBasis(Poly(cyclotomic_poly(n, x)))    >>> ee = [g**(2*k+1) % n for k in range((n-1)//2)]    >>> eta = sum(C(e) for e in ee)    >>> print(find_min_poly(eta, QQ, x=x).as_expr())    x**2 + x - 3    >>> n = 19    >>> g = primitive_root(n)    >>> C = PowerBasis(Poly(cyclotomic_poly(n, x)))    >>> ee = [g**(2*k+2) % n for k in range((n-1)//2)]    >>> eta = sum(C(e) for e in ee)    >>> print(find_min_poly(eta, QQ, x=x).as_expr())    x**2 + x + 5    Parameters    ==========    alpha : :py:class:`~.ModuleElement`        The element whose min poly is to be found, and whose module has        multiplication and starts with unity.    domain : :py:class:`~.Domain`        The desired domain of the polynomial.    x : :py:class:`~.Symbol`, optional        The desired variable for the polynomial.    powers : list, optional        If desired, pass an empty list. The powers of *alpha* (as        :py:class:`~.ModuleElement` instances) from the zeroth up to the degree        of the min poly will be recorded here, as we compute them.    Returns    =======    :py:class:`~.Poly`, ``None``        The minimal polynomial for alpha, or ``None`` if no polynomial could be        found over the desired domain.    Raises    ======    MissingUnityError        If the module to which alpha belongs does not start with unity.    ClosureFailure        If the module to which alpha belongs is not closed under        multiplication.    """    R = alpha.module    if not R.starts_with_unity():        raise MissingUnityError("alpha must belong to finitely generated ring with unity.")    if powers is None:        powers = []    one = R(0)    powers.append(one)    powers_matrix = one.column(domain=domain)    ak = alpha    m = None    for k in range(1, R.n + 1):        powers.append(ak)        ak_col = ak.column(domain=domain)        try:            X = powers_matrix._solve(ak_col)[0]        except DMBadInputError:            # This means alpha^k still isn't in the domain-span of the lower powers.            powers_matrix = powers_matrix.hstack(ak_col)            ak *= alpha        else:            # alpha^k is in the domain-span of the lower powers, so we have found a            # minimal-degree poly for alpha.            coeffs = [1] + [-c for c in reversed(X.to_list_flat())]            x = x or Dummy('x')            if domain.is_FF:                m = Poly(coeffs, x, modulus=domain.mod)            else:                m = Poly(coeffs, x, domain=domain)            break    return m
 |