Path: blob/master/src/sage/modules/fg_pid/fgp_module.py
8815 views
r"""1Finitely generated modules over a PID23You can use Sage to compute with finitely generated modules (FGM's)4over a principal ideal domain R presented as a quotient V/W, where V5and W are free.67NOTE: Currently this is only enabled over R=ZZ, since it has not been8tested and debugged over more general PIDs. All algorithms make sense9whenever there is a Hermite form implementation. In theory the10obstruction to extending the implementation is only that one has to11decide how elements print. If you're annoyed that by this, fix things12and post a patch!1314We represent M=V/W as a pair (V,W) with W contained in V, and we15internally represent elements of M non-canonically as elements x of16V. We also fix independent generators g[i] for M in V, and when we17print out elements of V we print their coordinates with respect to the18g[i]; over `\ZZ` this is canonical, since each coefficient is reduce19modulo the additive order of g[i]. To obtain the vector in V20corresponding to x in M, use x.lift().2122Morphisms between finitely generated R modules are well supported.23You create a homomorphism by simply giving the images of generators of24M0 in M1. Given a morphism phi:M0-->M1, you can compute the image of25phi, the kernel of phi, and using y=phi.lift(x) you can lift an26elements x in M1 to an element y in M0, if such a y exists.2728TECHNICAL NOTE: For efficiency, we introduce a notion of optimized29representation for quotient modules. The optimized representation of30M=V/W is the quotient V'/W' where V' has as basis lifts of the31generators g[i] for M. We internally store a morphism from M0=V0/W032to M1=V1/W1 by giving a morphism from the optimized representation V0'33of M0 to V1 that sends W0 into W1.34353637The following TUTORIAL illustrates several of the above points.3839First we create free modules V0 and W0 and the quotient module M0.40Notice that everything works fine even though V0 and W0 are not41contained inside `\ZZ^n`, which is extremely convenient. ::4243sage: V0 = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W0 = V0.span([V0.0+2*V0.1, 9*V0.0+2*V0.1, 4*V0.2])44sage: M0 = V0/W0; M045Finitely generated module V/W over Integer Ring with invariants (4, 16)4647The invariants are computed using the Smith normal form algorithm, and48determine the structure of this finitely generated module.4950You can get the V and W used in constructing the quotient module using51V() and W() methods::5253sage: M0.V()54Free module of degree 3 and rank 3 over Integer Ring55Echelon basis matrix:56[1/2 0 0]57[ 0 2 0]58[ 0 0 1]59sage: M0.W()60Free module of degree 3 and rank 3 over Integer Ring61Echelon basis matrix:62[1/2 4 0]63[ 0 32 0]64[ 0 0 4]6566We note that the optimized representation of M0, mentioned above in67the technical note has a V that need not be equal to V0, in general. ::6869sage: M0.optimized()[0].V()70Free module of degree 3 and rank 2 over Integer Ring71User basis matrix:72[0 0 1]73[0 2 0]7475Create elements of M0 either by coercing in elements of V0, getting76generators, or coercing in a list or tuple or coercing in 0. Coercing77in a list or tuple takes the corresponding linear combination of the78generators of M0. ::7980sage: M0(V0.0)81(0, 14)82sage: M0(V0.0 + W0.0) # no difference modulo W083(0, 14)84sage: M0([3,20])85(3, 4)86sage: 3*M0.0 + 20*M0.187(3, 4)8889We make an element of M0 by taking a difference of two generators, and90lift it. We also illustrate making an element from a list, which91coerces to V0, then take the equivalence class modulo W0. ::9293sage: x = M0.0 - M0.1; x94(1, 15)95sage: x.lift()96(0, -2, 1)97sage: M0(vector([1/2,0,0]))98(0, 14)99sage: x.additive_order()10016101102103Similarly, we construct V1 and W1, and the quotient M1, in a completely different1042-dimensional ambient space. ::105106sage: V1 = span([[1/2,0],[3/2,2]],ZZ); W1 = V1.span([2*V1.0, 3*V1.1])107sage: M1 = V1/W1; M1108Finitely generated module V/W over Integer Ring with invariants (6)109110We create the homomorphism from M0 to M1 that sends both generators of111M0 to 3 times the generator of M1. This is well defined since 3 times112the generator has order 2. ::113114sage: f = M0.hom([3*M1.0, 3*M1.0]); f115Morphism from module over Integer Ring with invariants (4, 16) to module with invariants (6,) that sends the generators to [(3), (3)]116117We evaluate the homomorphism on our element x of the domain, and on the118first generator of the domain. We also evaluate at an element of V0,119which is coerced into M0. ::120121sage: f(x)122(0)123sage: f(M0.0)124(3)125sage: f(V0.1)126(3)127128Here we illustrate lifting an element of the image of f, i.e., finding129an element of M0 that maps to a given element of M1::130131sage: y = f.lift(3*M1.0); y132(0, 13)133sage: f(y)134(3)135136We compute the kernel of f, i.e., the submodule of elements of M0 that137map to 0. Note that the kernel is not explicitly represented as a138submodule, but as another quotient V/W where V is contained in V0.139You can explicitly coerce elements of the kernel into M0 though. ::140141sage: K = f.kernel(); K142Finitely generated module V/W over Integer Ring with invariants (2, 16)143144sage: M0(K.0)145(2, 0)146sage: M0(K.1)147(3, 1)148sage: f(M0(K.0))149(0)150sage: f(M0(K.1))151(0)152153We compute the image of f. ::154155sage: f.image()156Finitely generated module V/W over Integer Ring with invariants (2)157158Notice how the elements of the image are written as (0) and (1),159despite the image being naturally a submodule of M1, which has160elements (0), (1), (2), (3), (4), (5). However, below we coerce the161element (1) of the image into the codomain, and get (3)::162163sage: list(f.image())164[(0), (1)]165sage: list(M1)166[(0), (1), (2), (3), (4), (5)]167sage: x = f.image().0; x168(1)169sage: M1(x)170(3)171172173TESTS::174175sage: from sage.modules.fg_pid.fgp_module import FGP_Module176sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ)177sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])178sage: Q = FGP_Module(V, W); Q179Finitely generated module V/W over Integer Ring with invariants (4, 12)180sage: Q([1,3])181(1, 3)182sage: Q(V([1,3,4]))183(0, 11)184sage: Q(W([1,16,0]))185(0, 0)186sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],QQ)187sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1])188sage: Q = FGP_Module(V, W); Q189Finitely generated module V/W over Rational Field with invariants (0)190sage: q = Q.an_element(); q191(1)192sage: q*(1/2)193(1/2)194sage: (1/2)*q195(1/2)196197AUTHOR:198199- William Stein, 2009200"""201202####################################################################################203# Copyright (C) 2009 William Stein <[email protected]>204#205# Distributed under the terms of the GNU General Public License (GPL)206#207# This code is distributed in the hope that it will be useful,208# but WITHOUT ANY WARRANTY; without even the implied warranty of209# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU210# General Public License for more details.211#212# The full text of the GPL is available at:213#214# http://www.gnu.org/licenses/215####################################################################################216217from sage.modules.module import Module218from sage.modules.free_module import is_FreeModule219from sage.structure.sequence import Sequence220from fgp_element import DEBUG, FGP_Element221from fgp_morphism import FGP_Morphism, FGP_Homset222from sage.rings.all import Integer, ZZ, lcm223from sage.misc.cachefunc import cached_method224225import sage.misc.weak_dict226_fgp_module = sage.misc.weak_dict.WeakValueDictionary()227228229230231def FGP_Module(V, W, check=True):232"""233INPUT:234235- ``V`` -- a free R-module236237- ``W`` -- a free R-submodule of ``V``238239- ``check`` -- bool (default: ``True``); if ``True``, more checks240on correctness are performed; in particular, we check the data241types of ``V`` and ``W``, and that ``W`` is a submodule of ``V``242with the same base ring.243244OUTPUT:245246- the quotient ``V/W`` as a finitely generated R-module247248249EXAMPLES::250251sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])252sage: import sage.modules.fg_pid.fgp_module253sage: Q = sage.modules.fg_pid.fgp_module.FGP_Module(V, W)254sage: type(Q)255<class 'sage.modules.fg_pid.fgp_module.FGP_Module_class_with_category'>256sage: Q is sage.modules.fg_pid.fgp_module.FGP_Module(V, W, check=False)257True258"""259key = (V,V.basis_matrix(),W,W.basis_matrix())260try:261return _fgp_module[key]262except KeyError: pass263M = FGP_Module_class(V, W, check=check)264_fgp_module[key] = M265return M266267268def is_FGP_Module(x):269"""270Return true of x is an FGP module, i.e., a finitely generated271module over a PID represented as a quotient of finitely generated272free modules over a PID.273274EXAMPLES::275276sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]); Q = V/W277sage: sage.modules.fg_pid.fgp_module.is_FGP_Module(V)278False279sage: sage.modules.fg_pid.fgp_module.is_FGP_Module(Q)280True281"""282return isinstance(x, FGP_Module_class)283284285class FGP_Module_class(Module):286"""287A finitely generated module over a PID presented as a quotient V/W.288289INPUT:290291- ``V`` -- an R-module292293- ``W`` -- an R-submodule of V294295- ``check`` -- bool (default: True)296297EXAMPLES::298299sage: A = (ZZ^1)/span([[100]], ZZ); A300Finitely generated module V/W over Integer Ring with invariants (100)301sage: A.V()302Ambient free module of rank 1 over the principal ideal domain Integer Ring303sage: A.W()304Free module of degree 1 and rank 1 over Integer Ring305Echelon basis matrix:306[100]307308sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])309sage: Q = V/W; Q310Finitely generated module V/W over Integer Ring with invariants (4, 12)311sage: type(Q)312<class 'sage.modules.fg_pid.fgp_module.FGP_Module_class_with_category'>313"""314315# The class to be used for creating elements of this316# module. Should be overridden in derived classes.317Element = FGP_Element318319def __init__(self, V, W, check=True):320"""321INPUT:322323- ``V`` -- an R-module324325- ``W`` -- an R-submodule of V326327- ``check`` -- bool (default: True); if True, more checks on328correctness are performed; in particular, we check329the data types of V and W, and that W is a330submodule of V with the same base ring.331332EXAMPLES::333334sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])335sage: Q = V/W; Q336Finitely generated module V/W over Integer Ring with invariants (4, 12)337sage: type(Q)338<class 'sage.modules.fg_pid.fgp_module.FGP_Module_class_with_category'>339"""340if check:341if not is_FreeModule(V):342raise TypeError, "V must be a FreeModule"343if not is_FreeModule(W):344raise TypeError, "W must be a FreeModule"345if not W.is_submodule(V):346raise ValueError, "W must be a submodule of V"347if V.base_ring() != W.base_ring():348raise ValueError, "W and V must have the same base ring"349self._W = W350self._V = V351Module.__init__(self, base=V.base_ring())352353# Note: There once was a354# def _subquotient_class():355# method that returned a functionoid to construct new modules, so356# you would call module._subquotient_class()(V,W,check). This has357# been replaced with module._module_constructor(V,W,check).358359def _module_constructor(self, V, W, check=True):360r"""361Construct a quotient module ``V/W``.362363This should be overridden in derived classes.364365INPUT:366367- ``V`` -- an R-module.368369- ``W`` -- an R-submodule of ``V``.370371- ``check`` -- bool (default: True).372373OUTPUT:374375The quotient ``V/W``.376377EXAMPLES::378379sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])380sage: Q = V/W; Q381Finitely generated module V/W over Integer Ring with invariants (4, 12)382sage: Q._module_constructor(V,W)383Finitely generated module V/W over Integer Ring with invariants (4, 12)384"""385return FGP_Module(V,W,check)386387def _coerce_map_from_(self, S):388"""389Return whether ``S`` canonically coerces to ``self``a.390391INPUT:392393- ``S`` -- anything.394395OUTPUT:396397Boolean.398399EXAMPLES::400401sage: V = span([[5, -1/2]],ZZ); W = span([[20,-2]],ZZ); Q = V/W; phi=Q.hom([2*Q.0])402sage: Q._coerce_map_from_(ZZ)403False404sage: Q._coerce_map_from_(phi.kernel())405True406sage: Q._coerce_map_from_(Q)407True408sage: Q._coerce_map_from_(phi.image())409True410sage: Q._coerce_map_from_(V/V.zero_submodule())411True412sage: Q._coerce_map_from_(V/V)413False414sage: Q._coerce_map_from_(ZZ^2)415False416417Of course, `V` canonically coerces to `Q`, as does twice `V`::418419sage: Q._coerce_map_from_(V)420True421sage: Q._coerce_map_from_(V.scale(2))422True423"""424if is_FGP_Module(S):425return S.has_canonical_map_to(self)426return self._V.has_coerce_map_from(S)427428def _repr_(self):429"""430Return string representation of this module.431432EXAMPLES::433434sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])435sage: (V/W)._repr_()436'Finitely generated module V/W over Integer Ring with invariants (4, 12)'437"""438I = str(self.invariants()).replace(',)',')')439return "Finitely generated module V/W over %s with invariants %s"%(self.base_ring(), I)440441def __div__(self, other):442"""443Return the quotient self/other, where other must be a444submodule of self.445446EXAMPLES::447448sage: V = span([[5, -1/2]],ZZ); W = span([[20,-2]],ZZ); Q = V/W; phi=Q.hom([2*Q.0])449sage: Q450Finitely generated module V/W over Integer Ring with invariants (4)451sage: Q/phi.kernel()452Finitely generated module V/W over Integer Ring with invariants (2)453sage: Q/Q454Finitely generated module V/W over Integer Ring with invariants ()455"""456if not is_FGP_Module(other):457if is_FreeModule(other):458other = other / other.zero_submodule()459else:460raise TypeError, "other must be an FGP module"461if not other.is_submodule(self):462raise ValueError, "other must be a submodule of self"463return self._module_constructor(self._V, other._V+self._W)464465def __eq__(self, other):466"""467EXAMPLES::468469sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])470sage: Q = V/W471sage: Q == Q472True473sage: loads(dumps(Q)) == Q474True475sage: Q == V476False477sage: Q == V/V.zero_submodule()478False479"""480if not is_FGP_Module(other):481return False482return self._V == other._V and self._W == other._W483484def __ne__(self, other):485"""486True iff self is not equal to other.487488This may not be needed for modules created using the function489:func:`FGP_Module`, since those have uniqueness built into490them, but if the modules are created directly using the491__init__ method for this class, then this may fail; in492particular, for modules M and N with ``M == N`` returning493True, it may be the case that ``M != N`` may also return True.494In particular, for derived classes whose __init__ methods just495call the __init__ method for this class need this. See496http://trac.sagemath.org/sage_trac/ticket/9940 for497illustrations.498499EXAMPLES:500501Make sure that the problems in502http://trac.sagemath.org/sage_trac/ticket/9940 are fixed::503504sage: G = AdditiveAbelianGroup([0,0])505sage: H = AdditiveAbelianGroup([0,0])506sage: G == H507True508sage: G != H # indirect doctest509False510511sage: N1 = ToricLattice(3)512sage: sublattice1 = N1.submodule([(1,1,0), (3,2,1)])513sage: Q1 = N1/sublattice1514sage: N2 = ToricLattice(3)515sage: sublattice2 = N2.submodule([(1,1,0), (3,2,1)])516sage: Q2 = N2/sublattice2517sage: Q1 == Q2518True519sage: Q1 != Q2520False521"""522return not self.__eq__(other)523524# __le__ is a synonym for `is_submodule`: see below525526def __lt__(self, other):527"""528True iff self is a proper submodule of other.529530EXAMPLES::531532sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)533sage: A = V/W; B = W/W2534sage: B < A535False536sage: A = V/W2; B = W/W2537sage: B < A538True539sage: A < A540False541"""542return self.__le__(other) and not self.__eq__(other)543544def __gt__(self, other):545"""546True iff other is a proper submodule of self.547548EXAMPLES::549550sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)551sage: A = V/W; B = W/W2552sage: A > B553False554sage: A = V/W2; B = W/W2555sage: A > B556True557sage: A > A558False559"""560return self.__ge__(other) and not self.__eq__(other)561562def __ge__(self, other):563"""564True iff other is a submodule of self.565566EXAMPLES::567568sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)569sage: A = V/W; B = W/W2570sage: A >= B571False572sage: A = V/W2; B = W/W2573sage: A >= B574True575sage: A >= A576True577"""578return other.is_submodule(self)579580581def _element_constructor_(self, x, check=True):582"""583INPUT:584585- ``x`` -- a vector, an fgp module element, or a list or tuple:586587- list or tuple: take the corresponding linear combination of588the generators of self.589590- vector: coerce vector into V and define the591corresponding element of V/W592593- fgp module element: lift to element of ambient vector594space and try to put into V. If x is in self already,595just return x.596597- `check` -- bool (default: True)598599EXAMPLES::600601sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])602sage: Q = V/W603sage: x = Q(V.0-V.1); x # indirect doctest604(0, 3)605sage: type(x)606<class 'sage.modules.fg_pid.fgp_element.FGP_Module_class_with_category.element_class'>607sage: x is Q(x)608True609sage: x.parent() is Q610True611"""612# print '_element_constructor_', x, check613if isinstance(x, (list,tuple)):614try:615x = self.optimized()[0].V().linear_combination_of_basis(x)616except ValueError, msg:617raise TypeError, msg618elif isinstance(x, FGP_Element):619x = x.lift()620return self.element_class(self, self._V(x))621622623def linear_combination_of_smith_form_gens(self, x):624r"""625Compute a linear combination of the optimised generators of this module626as returned by :meth:`.smith_form_gens`.627628EXAMPLE::629630sage: X = ZZ**2 / span([[3,0],[0,2]], ZZ)631sage: X.linear_combination_of_smith_form_gens([1])632(1)633634"""635try:636x = self.optimized()[0].V().linear_combination_of_basis(x)637except ValueError, msg:638raise TypeError, msg639return self.element_class(self, self._V(x))640641def __contains__(self, x):642"""643Return true if x is contained in self.644645EXAMPLES::646647sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])648sage: Q = V/W; Q649Finitely generated module V/W over Integer Ring with invariants (4, 12)650sage: Q.0 in Q651True652sage: 0 in Q653True654sage: vector([1,2,3/7]) in Q655False656sage: vector([1,2,3]) in Q657True658sage: Q.0 - Q.1 in Q659True660"""661if hasattr(x, 'parent') and x.parent() == self:662return True663try:664self(x)665return True666except TypeError:667return False668669def submodule(self, x):670"""671Return the submodule defined by x.672673INPUT:674675- ``x`` -- list, tuple, or FGP module676677EXAMPLES::678679sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])680sage: Q = V/W; Q681Finitely generated module V/W over Integer Ring with invariants (4, 12)682sage: Q.gens()683((1, 0), (0, 1))684685We create submodules generated by a list or tuple of elements::686687sage: Q.submodule([Q.0])688Finitely generated module V/W over Integer Ring with invariants (4)689sage: Q.submodule([Q.1])690Finitely generated module V/W over Integer Ring with invariants (12)691sage: Q.submodule((Q.0, Q.0 + 3*Q.1))692Finitely generated module V/W over Integer Ring with invariants (4, 4)693694A submodule defined by a submodule::695696sage: A = Q.submodule((Q.0, Q.0 + 3*Q.1)); A697Finitely generated module V/W over Integer Ring with invariants (4, 4)698sage: Q.submodule(A)699Finitely generated module V/W over Integer Ring with invariants (4, 4)700701Inclusion is checked::702703sage: A.submodule(Q)704Traceback (most recent call last):705...706ValueError: x.V() must be contained in self's V.707"""708if is_FGP_Module(x):709if not x._W.is_submodule(self._W):710raise ValueError, "x.W() must be contained in self's W."711712V = x._V713if not V.is_submodule(self._V):714raise ValueError, "x.V() must be contained in self's V."715716return x717718if not isinstance(x, (list, tuple)):719raise TypeError, "x must be a list, tuple, or FGP module"720721x = Sequence(x)722if is_FGP_Module(x.universe()):723# TODO: possibly inefficient in some cases724x = [self(v).lift() for v in x]725V = self._V.submodule(x) + self._W726return self._module_constructor(V, self._W)727728def has_canonical_map_to(self, A):729"""730Return True if self has a canonical map to A, relative to the731given presentation of A. This means that A is a finitely732generated quotient module, self.V() is a submodule of A.V()733and self.W() is a submodule of A.W(), i.e., that there is a734natural map induced by inclusion of the V's. Note that we do735*not* require that this natural map be injective; for this use736:meth:`is_submodule`.737738EXAMPLES::739740sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])741sage: Q = V/W; Q742Finitely generated module V/W over Integer Ring with invariants (4, 12)743sage: A = Q.submodule((Q.0, Q.0 + 3*Q.1)); A744Finitely generated module V/W over Integer Ring with invariants (4, 4)745sage: A.has_canonical_map_to(Q)746True747sage: Q.has_canonical_map_to(A)748False749750"""751if not is_FGP_Module(A):752return False753if self.cardinality() == 0 and self.base_ring() == A.base_ring():754return True755return self.V().is_submodule(A.V()) and self.W().is_submodule(A.W())756757def is_submodule(self, A):758"""759Return True if self is a submodule of A. More precisely, this returns True if760if ``self.V()`` is a submodule of ``A.V()``, with ``self.W()`` equal to ``A.W()``.761762Compare :meth:`.has_canonical_map_to`.763764EXAMPLES::765766sage: V = ZZ^2; W = V.span([[1,2]]); W2 = W.scale(2)767sage: A = V/W; B = W/W2768sage: B.is_submodule(A)769False770sage: A = V/W2; B = W/W2771sage: B.is_submodule(A)772True773774This example illustrates that this command works in a subtle cases.::775776sage: A = ZZ^1777sage: Q3 = A / A.span([[3]])778sage: Q6 = A / A.span([[6]])779sage: Q6.is_submodule(Q3)780False781sage: Q6.has_canonical_map_to(Q3)782True783sage: Q = A.span([[2]]) / A.span([[6]])784sage: Q.is_submodule(Q6)785True786"""787if not self.has_canonical_map_to(A):788return False789790return self.V().is_submodule(A.V()) and self.W() == A.W()791792__le__ = is_submodule793794def V(self):795"""796If this module was constructed as a quotient V/W, returns V.797798EXAMPLES::799800sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])801sage: Q = V/W802sage: Q.V()803Free module of degree 3 and rank 3 over Integer Ring804Echelon basis matrix:805[1/2 0 0]806[ 0 1 0]807[ 0 0 1]808809"""810return self._V811812def cover(self):813"""814If this module was constructed as V/W, returns the cover module V. This is the same as self.V().815816EXAMPLES::817818sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])819sage: Q = V/W820sage: Q.V()821Free module of degree 3 and rank 3 over Integer Ring822Echelon basis matrix:823[1/2 0 0]824[ 0 1 0]825[ 0 0 1]826"""827return self.V()828829def W(self):830"""831If this module was constructed as a quotient V/W, returns W.832833EXAMPLES::834835sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])836sage: Q = V/W837sage: Q.W()838Free module of degree 3 and rank 3 over Integer Ring839Echelon basis matrix:840[1/2 8 0]841[ 0 12 0]842[ 0 0 4]843"""844return self._W845846def relations(self):847"""848If this module was constructed as V/W, returns the relations module V. This is the same as self.W().849850EXAMPLES::851852sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])853sage: Q = V/W854sage: Q.relations()855Free module of degree 3 and rank 3 over Integer Ring856Echelon basis matrix:857[1/2 8 0]858[ 0 12 0]859[ 0 0 4]860"""861return self.W()862863@cached_method864def _relative_matrix(self):865"""866V has a fixed choice of basis, and W has a fixed choice of867basis, and both V and W are free R-modules. Since W is868contained in V, we can write each basis element of W as an869R-linear combination of the basis for V. This function870returns that matrix over R, where each row corresponds to a871basis element of W.872873EXAMPLES::874875sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])876sage: Q = V/W877sage: Q._relative_matrix()878[ 1 8 0]879[ 0 12 0]880[ 0 0 4]881"""882V = self._V883W = self._W884A = V.coordinate_module(W).basis_matrix().change_ring(self.base_ring())885return A886887@cached_method888def _smith_form(self):889"""890Return matrices S, U, and V such that S = U*R*V, and S is in891Smith normal form, and R is the relative matrix that defines892self (see :meth:`._relative_matrix`).893894EXAMPLES::895896sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])897sage: Q = V/W898sage: Q._smith_form()899(900[ 1 0 0] [1 0 0] [ 1 0 -8]901[ 0 4 0] [0 0 1] [ 0 0 1]902[ 0 0 12], [0 1 0], [ 0 1 0]903)904"""905return self._relative_matrix().smith_form()906907def base_ring(self):908"""909EXAMPLES::910911sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])912sage: Q = V/W913sage: Q.base_ring()914Integer Ring915"""916return self._V.base_ring()917918@cached_method919def invariants(self, include_ones=False):920"""921Return the diagonal entries of the smith form of the relative922matrix that defines self (see :meth:`._relative_matrix`)923padded with zeros, excluding 1's by default. Thus if v is the924list of integers returned, then self is abstractly isomorphic to925the product of cyclic groups `Z/nZ` where `n` is in `v`.926927INPUT:928929- ``include_ones`` -- bool (default: False); if True, also930include 1's in the output list.931932EXAMPLES::933934sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])935sage: Q = V/W936sage: Q.invariants()937(4, 12)938939An example with 1 and 0 rows::940941sage: V = ZZ^3; W = V.span([[1,2,0],[0,1,0], [0,2,0]]); Q = V/W; Q942Finitely generated module V/W over Integer Ring with invariants (0)943sage: Q.invariants()944(0,)945sage: Q.invariants(include_ones=True)946(1, 1, 0)947948"""949D,_,_ = self._smith_form()950951v = [D[i,i] for i in range(D.nrows())] + [Integer(0)]*(D.ncols()-D.nrows())952w = tuple([x for x in v if x != 1])953v = tuple(v)954self.invariants.set_cache(v, True)955self.invariants.set_cache(w, False)956return self.invariants(include_ones)957958def gens(self):959"""960Returns tuple of elements `g_0,...,g_n` of self such that the module generated by961the gi is isomorphic to the direct sum of R/ei*R, where ei are the962invariants of self and R is the base ring.963964Note that these are not generally uniquely determined, and depending on965how Smith normal form is implemented for the base ring, they may not966even be deterministic.967968This can safely be overridden in all derived classes.969970EXAMPLES::971972sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])973sage: Q = V/W974sage: Q.gens()975((1, 0), (0, 1))976sage: Q.0977(1, 0)978"""979return self.smith_form_gens()980981@cached_method982def smith_form_gens(self):983"""984Return a set of generators for self which are in Smith normal form.985986EXAMPLES::987988sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])989sage: Q = V/W990sage: Q.smith_form_gens()991((1, 0), (0, 1))992sage: [x.lift() for x in Q.smith_form_gens()]993[(0, 0, 1), (0, 1, 0)]994"""995# Get the rightmost transformation in the Smith form996_, _, X = self._smith_form()997# Invert it to get a matrix whose rows (in terms of the basis for V)998# are the gi (including 1 invariants).999Y = X**(-1)1000# Get the basis matrix for V1001B = self._V.basis_matrix()1002# Multiply to express the gi in terms of the ambient vector space.1003Z = Y*B1004# Make gens out of the rows of Z that correspond to non-1 invariants.1005v = self.invariants(include_ones=True)1006non1 = [i for i in range(Z.nrows()) if v[i] != 1]1007Z = Z.matrix_from_rows(non1)1008self._gens = tuple([self(z, check=DEBUG) for z in Z.rows()])1009return self._gens10101011def coordinate_vector(self, x, reduce=False):1012"""1013Return coordinates of x with respect to the optimized1014representation of self.10151016INPUT:10171018- ``x`` -- element of self10191020- ``reduce`` -- (default: False); if True, reduce1021coefficients modulo invariants; this is1022ignored if the base ring isn't ZZ.10231024OUTPUT:10251026The coordinates as a vector. That is, the same type as1027``self.V()``, but in general with fewer entries.10281029EXAMPLES::10301031sage: V = span([[1/4,0,0],[3/4,4,2],[0,0,2]],ZZ); W = V.span([4*V.0+12*V.1])1032sage: Q = V/W; Q1033Finitely generated module V/W over Integer Ring with invariants (4, 0, 0)1034sage: Q.coordinate_vector(-Q.0)1035(-1, 0, 0)1036sage: Q.coordinate_vector(-Q.0, reduce=True)1037(3, 0, 0)10381039If x isn't in self, it is coerced in::10401041sage: Q.coordinate_vector(V.0)1042(1, 0, -3)1043sage: Q.coordinate_vector(Q(V.0))1044(1, 0, -3)104510461047TESTS::10481049sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])1050sage: Q = V/W; Q1051Finitely generated module V/W over Integer Ring with invariants (4, 12)1052sage: Q.coordinate_vector(Q.0 - Q.1)1053(1, -1)10541055sage: O, X = Q.optimized()1056sage: O.V()1057Free module of degree 3 and rank 2 over Integer Ring1058User basis matrix:1059[0 0 1]1060[0 2 0]1061sage: phi = Q.hom([Q.0, 4*Q.1])1062sage: x = Q(V.0); x1063(0, 4)1064sage: Q.coordinate_vector(x, reduce=True)1065(0, 4)1066sage: Q.coordinate_vector(-x, reduce=False)1067(0, -4)1068sage: x == 4*Q.11069True1070sage: x = Q(V.1); x1071(0, 1)1072sage: Q.coordinate_vector(x)1073(0, 1)1074sage: x == Q.11075True1076sage: x = Q(V.2); x1077(1, 0)1078sage: Q.coordinate_vector(x)1079(1, 0)1080sage: x == Q.01081True1082"""1083try: T = self.__T1084except AttributeError:1085self.optimized() # computes T as side effect -- see the "optimized" method.1086T = self.__T10871088x = self(x)10891090c = self._V.coordinate_vector(x.lift())1091b = (c*T).change_ring(self.base_ring())1092if reduce and self.base_ring() == ZZ:10931094I = self.invariants()1095return b.parent()([b[i] if I[i] == 0 else b[i] % I[i] for i in range(len(I))])10961097else:1098# Don't know (or not requested) canonical way to reduce1099# each entry yet, or how to compute invariants.1100return b11011102def gen(self, i):1103"""1104Return the i-th generator of self.11051106INPUT:11071108- ``i`` -- integer11091110EXAMPLES::11111112sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])1113sage: Q = V/W; Q1114Finitely generated module V/W over Integer Ring with invariants (4, 12)1115sage: Q.gen(0)1116(1, 0)1117sage: Q.gen(1)1118(0, 1)1119sage: Q.gen(2)1120Traceback (most recent call last):1121...1122ValueError: Generator 2 not defined1123sage: Q.gen(-1)1124Traceback (most recent call last):1125...1126ValueError: Generator -1 not defined1127"""1128v = self.gens()1129if i < 0 or i >= len(v):1130raise ValueError, "Generator %s not defined"%i1131return v[i]11321133def smith_form_gen(self, i):1134"""1135Return the i-th generator of self. A private name (so we can freely1136override gen() in derived classes).11371138INPUT:11391140- ``i`` -- integer11411142EXAMPLES::11431144sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])1145sage: Q = V/W; Q1146Finitely generated module V/W over Integer Ring with invariants (4, 12)1147sage: Q.smith_form_gen(0)1148(1, 0)1149sage: Q.smith_form_gen(1)1150(0, 1)1151"""1152v = self.smith_form_gens()1153if i < 0 or i >= len(v):1154raise ValueError, "Smith form generator %s not defined"%i1155return v[i]11561157def optimized(self):1158"""1159Return a module isomorphic to this one, but with V replaced by1160a submodule of V such that the generators of self all lift1161trivially to generators of V. Replace W by the intersection1162of V and W. This has the advantage that V has small dimension1163and any homomorphism from self trivially extends to a1164homomorphism from V.11651166OUTPUT:11671168- ``Q`` -- an optimized quotient V0/W0 with V0 a submodule of V1169such that phi: V0/W0 --> V/W is an isomorphism11701171- ``Z`` -- matrix such that if x is in self.V() and1172c gives the coordinates of x in terms of the1173basis for self.V(), then c*Z is in V01174and c*Z maps to x via phi above.11751176EXAMPLES::11771178sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])1179sage: Q = V/W1180sage: O, X = Q.optimized(); O1181Finitely generated module V/W over Integer Ring with invariants (4, 12)1182sage: O.V()1183Free module of degree 3 and rank 2 over Integer Ring1184User basis matrix:1185[0 0 1]1186[0 1 0]1187sage: O.W()1188Free module of degree 3 and rank 2 over Integer Ring1189Echelon basis matrix:1190[ 0 12 0]1191[ 0 0 4]1192sage: X1193[0 4 0]1194[0 1 0]1195[0 0 1]1196sage: OV = O.V()1197sage: Q(OV([0,-8,0])) == V.01198True1199sage: Q(OV([0,1,0])) == V.11200True1201sage: Q(OV([0,0,1])) == V.21202True1203"""1204try:1205if self.__optimized is True:1206return self, None1207return self.__optimized1208except AttributeError: pass1209V = self._V.span_of_basis([x.lift() for x in self.smith_form_gens()])1210M = self._module_constructor(V, self._W.intersection(V))1211# Compute matrix T of linear transformation from self._V to V.1212# This matrix T gives each basis element of self._V in terms1213# of our new optimized V, modulo the W's.1214A = V.basis_matrix().stack(self._W.basis_matrix())1215B, d = A._clear_denom()1216H, U = B.hermite_form(transformation=True)1217Y = H.solve_left(d*self._V.basis_matrix())1218T = Y * U.matrix_from_columns(range(V.rank()))1219self.__T = T12201221# Finally we multiply by V.basis_matrix() so X gives vectors1222# in the ambient space instead of coefficients of linear1223# combinations of the basis for V.1224X = T * V.basis_matrix()12251226self.__optimized = M, X1227return M, X122812291230def hom(self, im_gens, codomain=None, check=True):1231"""1232Homomorphism defined by giving the images of ``self.gens()`` in some1233fixed fg R-module.12341235.. note ::12361237We do not assume that the generators given by ``self.gens()`` are1238the same as the Smith form generators, since this may not be true1239for a general derived class.12401241INPUTS:12421243- ``im_gens`` -- a list of the images of ``self.gens()`` in some1244R-module124512461247EXAMPLES::12481249sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])1250sage: Q = V/W1251sage: phi = Q.hom([3*Q.1, Q.0])1252sage: phi1253Morphism from module over Integer Ring with invariants (4, 12) to module with invariants (4, 12) that sends the generators to [(0, 3), (1, 0)]1254sage: phi(Q.0)1255(0, 3)1256sage: phi(Q.1)1257(1, 0)1258sage: Q.0 == phi(Q.1)1259True12601261This example illustrates creating a morphism to a free module.1262The free module is turned into an FGP module (i.e., quotient1263V/W with W=0), and the morphism is constructed::12641265sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1])1266sage: Q = V/W; Q1267Finitely generated module V/W over Integer Ring with invariants (2, 0, 0)1268sage: phi = Q.hom([0,V.0,V.1]); phi1269Morphism from module over Integer Ring with invariants (2, 0, 0) to module with invariants (0, 0, 0) that sends the generators to [(0, 0, 0), (1, 0, 0), (0, 1, 0)]1270sage: phi.domain()1271Finitely generated module V/W over Integer Ring with invariants (2, 0, 0)1272sage: phi.codomain()1273Finitely generated module V/W over Integer Ring with invariants (0, 0, 0)1274sage: phi(Q.0)1275(0, 0, 0)1276sage: phi(Q.1)1277(1, 0, 0)1278sage: phi(Q.2) == V.11279True12801281Constructing two zero maps from the zero module::12821283sage: A = (ZZ^2)/(ZZ^2); A1284Finitely generated module V/W over Integer Ring with invariants ()1285sage: A.hom([])1286Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []1287sage: A.hom([]).codomain() is A1288True1289sage: B = (ZZ^3)/(ZZ^3)1290sage: A.hom([],codomain=B)1291Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []1292sage: phi = A.hom([],codomain=B); phi1293Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []1294sage: phi(A(0))1295()1296sage: phi(A(0)) == B(0)1297True129812991300A degenerate case::13011302sage: A = (ZZ^2)/(ZZ^2)1303sage: phi = A.hom([]); phi1304Morphism from module over Integer Ring with invariants () to module with invariants () that sends the generators to []1305sage: phi(A(0))1306()13071308The code checks that the morphism is valid. In the example1309below we try to send a generator of order 2 to an element of1310order 14::13111312sage: V = span([[1/14,3/14],[0,1/2]],ZZ); W = ZZ^21313sage: Q = V/W; Q1314Finitely generated module V/W over Integer Ring with invariants (2, 14)1315sage: Q([1,11]).additive_order()1316141317sage: f = Q.hom([Q([1,11]), Q([1,3])]); f1318Traceback (most recent call last):1319...1320ValueError: phi must send optimized submodule of M.W() into N.W()13211322"""1323if len(im_gens) == 0:1324# 0 map1325N = self if codomain is None else codomain1326else:1327if codomain is None:1328im_gens = Sequence(im_gens)1329N = im_gens.universe()1330else:1331N = codomain1332im_gens = Sequence(im_gens, universe=N)13331334if is_FreeModule(N):1335# If im_smith_gens aren't in an R-module, but are in a Free-module,1336# then we quotient out by the 0 submodule and get an R-module.1337N = FGP_Module(N, N.zero_submodule(), check=DEBUG)1338im_gens = Sequence(im_gens, universe=N)13391340if len(im_gens) == 0:1341VO = self.optimized()[0].V()1342H = VO.Hom(N.V())1343return FGP_Morphism(self.Hom(N), H(0), check=DEBUG)13441345if self.gens() == self.smith_form_gens():1346return self._hom_from_smith(im_gens, check)1347else:1348return self._hom_general(im_gens, check)134913501351def _hom_general(self, im_gens, check=True):1352"""1353Homomorphism defined by giving the images of ``self.gens()`` in some1354fixed fg R-module. We do not assume that the generators given by1355``self.gens()`` are the same as the Smith form generators, since this1356may not be true for a general derived class.13571358INPUT:13591360- ``im_gens`` - a Sequence object giving the images of ``self.gens()``,1361whose universe is some fixed fg R-module13621363EXAMPLE::13641365sage: class SillyModule(sage.modules.fg_pid.fgp_module.FGP_Module_class):1366... def gens(self):1367... return tuple(flatten([[x,x] for x in self.smith_form_gens()]))1368sage: A = SillyModule(ZZ**1, span([[3]], ZZ))1369sage: A.gen(0)1370(1)1371sage: A.gen(1)1372(1)1373sage: B = ZZ**1 / span([[3]], ZZ)1374sage: A.hom([B.0, 2*B.0], B)1375Traceback (most recent call last):1376...1377ValueError: Images do not determine a valid homomorphism1378sage: A.hom([B.0, B.0], B) # indirect doctest1379Morphism from module over Integer Ring with invariants (3,) to module with invariants (3,) that sends the generators to [(1), (1)]13801381"""1382m = self.ngens()1383A = ZZ**m1384q = A.hom([x.lift() for x in self.gens()], self.V())1385B = q.inverse_image(self.W())1386N = im_gens.universe()1387r = A.hom([x.lift() for x in im_gens], N.V())1388if check:1389if not r(B).is_submodule(N.W()):1390raise ValueError, "Images do not determine a valid homomorphism"1391smith_images = Sequence([N(r(q.lift(x.lift()))) for x in self.smith_form_gens()])1392return self._hom_from_smith(smith_images, check=DEBUG)13931394def _hom_from_smith(self, im_smith_gens, check=True):1395"""1396Homomorphism defined by giving the images of the Smith-form generators1397of self in some fixed fg R-module.13981399INPUTS:14001401- ``im_gens`` -- a Sequence object giving the images of the Smith-form1402generators of self, whose universe is some fixed fg R-module14031404EXAMPLE::14051406sage: class SillyModule(sage.modules.fg_pid.fgp_module.FGP_Module_class):1407... def gens(self):1408... return tuple(flatten([[x,x] for x in self.smith_form_gens()]))1409sage: A = SillyModule(ZZ**1, span([[3]], ZZ))1410sage: A.gen(0)1411(1)1412sage: A.gen(1)1413(1)1414sage: B = ZZ**1 / span([[3]], ZZ)1415sage: A._hom_from_smith(Sequence([B.0]))1416Morphism from module over Integer Ring with invariants (3,) to module with invariants (3,) that sends the generators to [(1), (1)]1417"""1418if len(im_smith_gens) != len(self.smith_form_gens()):1419raise ValueError, "im_gens must have length the same as self.smith_form_gens()"14201421# replace self by representation in which smith-gens g_i are a basis for V.1422M, _ = self.optimized()1423# Define morphism from M to N1424f = M.V().hom([x.lift() for x in im_smith_gens])1425N = im_smith_gens.universe()1426homspace = self.Hom(N)1427phi = FGP_Morphism(homspace, f, check=DEBUG)1428return phi14291430def Hom(self, N):1431"""1432EXAMPLES::14331434sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 9*V.0+2*V.1, 4*V.2])1435sage: Q = V/W1436sage: Q.Hom(Q)1437Set of Morphisms from Finitely generated module V/W over Integer Ring with invariants (4, 16) to Finitely generated module V/W over Integer Ring with invariants (4, 16) in Category of modules over Integer Ring1438sage: M = V/V.zero_submodule()1439sage: M.Hom(Q)1440Set of Morphisms from Finitely generated module V/W over Integer Ring with invariants (0, 0, 0) to Finitely generated module V/W over Integer Ring with invariants (4, 16) in Category of modules over Integer Ring1441"""1442return FGP_Homset(self, N)14431444def random_element(self, *args, **kwds):1445"""1446Create a random element of self=V/W, by creating a random element of V and1447reducing it modulo W.14481449All arguments are passed onto the random_element method of V.14501451EXAMPLES::14521453sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])1454sage: Q = V/W1455sage: Q.random_element()1456(1, 10)1457"""1458return self(self._V.random_element(*args, **kwds))14591460def cardinality(self):1461"""1462Return the cardinality of this module as a set.14631464EXAMPLES::14651466sage: V = ZZ^2; W = V.span([[1,2],[3,4]]); A = V/W; A1467Finitely generated module V/W over Integer Ring with invariants (2)1468sage: A.cardinality()146921470sage: V = ZZ^2; W = V.span([[1,2]]); A = V/W; A1471Finitely generated module V/W over Integer Ring with invariants (0)1472sage: A.cardinality()1473+Infinity1474sage: V = QQ^2; W = V.span([[1,2]]); A = V/W; A1475Vector space quotient V/W of dimension 1 over Rational Field where1476V: Vector space of dimension 2 over Rational Field1477W: Vector space of degree 2 and dimension 1 over Rational Field1478Basis matrix:1479[1 2]1480sage: A.cardinality()1481+Infinity1482"""1483try:1484return self.__cardinality1485except AttributeError:1486pass1487from sage.rings.all import infinity1488from sage.misc.all import prod1489v = self.invariants()1490self.__cardinality = infinity if 0 in v else prod(v)1491return self.__cardinality14921493def __iter__(self):1494"""1495Return iterator over all elements of self.14961497EXAMPLES::14981499sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 4*V.0+2*V.1, 4*V.2])1500sage: Q = V/W; Q1501Finitely generated module V/W over Integer Ring with invariants (2, 12)1502sage: z = list(V/W)1503sage: print z1504[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11)]1505sage: len(z)15062415071508We test that the trivial module is handled correctly (cf. trac #6561)::15091510sage: A = (ZZ**1)/(ZZ**1); list(A) == [A(0)]1511True1512"""1513if self.base_ring() != ZZ:1514raise NotImplementedError, "only implemented over ZZ"1515v = self.invariants()1516if 0 in v:1517raise NotImplementedError, "currently self must be finite to iterate over"1518B = self.optimized()[0].V().basis_matrix()1519V = self.base_ring()**B.nrows()1520from sage.misc.mrange import cartesian_product_iterator1521for a in cartesian_product_iterator([range(k) for k in v]):1522b = V(a) * B1523yield self(b)15241525def is_finite(self):1526"""1527Return True if self is finite and False otherwise.15281529EXAMPLES::15301531sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 9*V.0+2*V.1, 4*V.2])1532sage: Q = V/W; Q1533Finitely generated module V/W over Integer Ring with invariants (4, 16)1534sage: Q.is_finite()1535True1536sage: Q = V/V.zero_submodule(); Q1537Finitely generated module V/W over Integer Ring with invariants (0, 0, 0)1538sage: Q.is_finite()1539False1540"""1541return 0 not in self.invariants()15421543def annihilator(self):1544"""1545Return the ideal of the base ring that annihilates self. This1546is precisely the ideal generated by the LCM of the invariants1547of self if self is finite, and is 0 otherwise.15481549EXAMPLES::15501551sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([V.0+2*V.1, 9*V.0+2*V.1, 4*V.2])1552sage: Q = V/W; Q.annihilator()1553Principal ideal (16) of Integer Ring1554sage: Q.annihilator().gen()15551615561557sage: Q = V/V.span([V.0]); Q1558Finitely generated module V/W over Integer Ring with invariants (0, 0)1559sage: Q.annihilator()1560Principal ideal (0) of Integer Ring1561"""1562if not self.is_finite():1563g = 01564else:1565g = reduce(lcm, self.invariants())1566return self.base_ring().ideal(g)15671568def ngens(self):1569r"""1570Return the number of generators of self.15711572(Note for developers: This is just the length of :meth:`.gens`, rather1573than of the minimal set of generators as returned by1574:meth:`.smith_form_gens`; these are the same in the1575:class:`~sage.modules.fg_pid.fgp_module.FGP_Module_class`, but not1576necessarily in derived classes.)15771578EXAMPLES::15791580sage: A = (ZZ**2) / span([[4,0],[0,3]], ZZ)1581sage: A.ngens()1582115831584This works (but please don't do it in production code!) ::15851586sage: A.gens = lambda: [1,2,"Barcelona!"]1587sage: A.ngens()158831589"""1590return len(self.gens())15911592def __hash__(self):1593r"""1594Calculate a hash for self.15951596EXAMPLES::15971598sage: A = (ZZ**2) / span([[4,0],[0,3]], ZZ)1599sage: hash(A)16001328975982 # 32-bit1601-7071641102956720018 # 64-bit1602"""1603return hash( (self.V(), self.W()) )16041605##############################################################1606# Useful for testing1607##############################################################16081609def random_fgp_module(n, R=ZZ, finite=False):1610"""1611Return a random FGP module inside a rank n free module over R.16121613INPUT:16141615- ``n`` -- nonnegative integer16161617- ``R`` -- base ring (default: ZZ)16181619- ``finite`` -- bool (default: True); if True, make the random module finite.16201621EXAMPLES::16221623sage: import sage.modules.fg_pid.fgp_module as fgp1624sage: fgp.random_fgp_module(4)1625Finitely generated module V/W over Integer Ring with invariants (4)1626"""1627K = R.fraction_field()1628V = K**n1629i = ZZ.random_element(max(n,1))1630A = V.span([V.random_element() for _ in range(i)], R)1631if not finite:1632i = ZZ.random_element(i+1)1633while True:1634B = A.span([A.random_element() for _ in range(i)], R)1635#Q = A/B1636Q = FGP_Module_class(A,B,check=DEBUG)1637if not finite or Q.is_finite():1638return Q16391640def random_fgp_morphism_0(*args, **kwds):1641"""1642Construct a random fgp module using random_fgp_module,1643then construct a random morphism that sends each generator1644to a random multiple of itself. Inputs are the same1645as to random_fgp_module.16461647EXAMPLES::16481649sage: import sage.modules.fg_pid.fgp_module as fgp1650sage: fgp.random_fgp_morphism_0(4)1651Morphism from module over Integer Ring with invariants (4,) to module with invariants (4,) that sends the generators to [(0)]16521653"""1654A = random_fgp_module(*args, **kwds)1655return A.hom([ZZ.random_element()*g for g in A.smith_form_gens()])16561657def test_morphism_0(*args, **kwds):1658"""1659EXAMPLES::16601661sage: import sage.modules.fg_pid.fgp_module as fgp1662sage: s = 0 # we set a seed so results clearly and easily reproducible across runs.1663sage: set_random_seed(s); v = [fgp.test_morphism_0(1) for _ in range(30)]1664sage: set_random_seed(s); v = [fgp.test_morphism_0(2) for _ in range(30)]1665sage: set_random_seed(s); v = [fgp.test_morphism_0(3) for _ in range(10)]1666sage: set_random_seed(s); v = [fgp.test_morphism_0(i) for i in range(1,20)]1667sage: set_random_seed(s); v = [fgp.test_morphism_0(4) for _ in range(50)] # long time1668"""1669phi = random_fgp_morphism_0(*args, **kwds)1670K = phi.kernel()1671I = phi.image()1672from sage.misc.all import prod1673if prod(K.invariants()):1674assert prod(phi.domain().invariants()) % prod(K.invariants()) == 01675assert I.is_submodule(phi.codomain())1676if len(I.smith_form_gens()) > 0:1677x = phi.lift(I.smith_form_gen(0))1678assert phi(x) == I.smith_form_gen(0)16791680168116821683