def apply_morphism(self, phi): r""" Apply the morphism ``phi`` to every element of this ideal. Returns an ideal in the domain of ``phi``. EXAMPLES:: sage: psi = CC['x'].hom([-CC['x'].0]) sage: J = ideal([CC['x'].0 + 1]); J Principal ideal (x + 1.00000000000000) of Univariate Polynomial Ring in x over Complex Field with 53 bits of precision sage: psi(J) Principal ideal (x - 1.00000000000000) of Univariate Polynomial Ring in x over Complex Field with 53 bits of precision sage: J.apply_morphism(psi) Principal ideal (x - 1.00000000000000) of Univariate Polynomial Ring in x over Complex Field with 53 bits of precision :: sage: psi = ZZ['x'].hom([-ZZ['x'].0]) sage: J = ideal([ZZ['x'].0, 2]); J Ideal (x, 2) of Univariate Polynomial Ring in x over Integer Ring sage: psi(J) Ideal (-x, 2) of Univariate Polynomial Ring in x over Integer Ring sage: J.apply_morphism(psi) Ideal (-x, 2) of Univariate Polynomial Ring in x over Integer Ring TESTS:: sage: K.<a> = NumberField(x^2 + 1) sage: A = K.ideal(a) sage: taus = K.embeddings(K) sage: A.apply_morphism(taus[0]) # identity Fractional ideal (a) sage: A.apply_morphism(taus[1]) # complex conjugation Fractional ideal (-a) sage: A.apply_morphism(taus[0]) == A.apply_morphism(taus[1]) True :: sage: K.<a> = NumberField(x^2 + 5) sage: B = K.ideal([2, a + 1]); B Fractional ideal (2, a + 1) sage: taus = K.embeddings(K) sage: B.apply_morphism(taus[0]) # identity Fractional ideal (2, a + 1) Since 2 is totally ramified, complex conjugation fixes it:: sage: B.apply_morphism(taus[1]) # complex conjugation Fractional ideal (2, a + 1) sage: taus[1](B) Fractional ideal (2, a + 1) """ from sage.categories.morphism import is_Morphism if not is_Morphism(phi): raise TypeError("phi must be a morphism") # delegate: morphisms know how to apply themselves to ideals return phi(self)
def __init__(self, parent, phi, check=True): """ A morphism between finitely generated modules over a PID. EXAMPLES:: sage: 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]) sage: Q = V/W; Q Finitely generated module V/W over Integer Ring with invariants (4, 12) sage: phi = Q.hom([Q.0+3*Q.1, -Q.1]); phi Morphism from module over Integer Ring with invariants (4, 12) to module with invariants (4, 12) that sends the generators to [(1, 3), (0, 11)] sage: phi(Q.0) == Q.0 + 3*Q.1 True sage: phi(Q.1) == -Q.1 True For full documentation, see :class:`FGP_Morphism`. """ Morphism.__init__(self, parent) M = parent.domain() N = parent.codomain() if isinstance(phi, FGP_Morphism): if check: if phi.parent() != parent: raise TypeError phi = phi._phi check = False # no need # input: phi is a morphism from MO = M.optimized().V() to N.V() # that sends MO.W() to N.W() if check: if not is_Morphism(phi) and M == N: A = M.optimized()[0].V() B = N.V() s = M.base_ring()(phi) * B.coordinate_module(A).basis_matrix() phi = A.Hom(B)(s) MO, _ = M.optimized() if phi.domain() != MO.V(): raise ValueError( "domain of phi must be the covering module for the optimized covering module of the domain" ) if phi.codomain() != N.V(): raise ValueError( "codomain of phi must be the covering module the codomain." ) # check that MO.W() gets sent into N.W() # todo (optimize): this is slow: for x in MO.W().basis(): if phi(x) not in N.W(): raise ValueError( "phi must send optimized submodule of M.W() into N.W()" ) self._phi = phi
def __init__(self, parent, phi, check=True): """ A morphism between finitely generated modules over a PID. EXAMPLES:: sage: 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]) sage: Q = V/W; Q Finitely generated module V/W over Integer Ring with invariants (4, 12) sage: phi = Q.hom([Q.0+3*Q.1, -Q.1]); phi Morphism from module over Integer Ring with invariants (4, 12) to module with invariants (4, 12) that sends the generators to [(1, 3), (0, 11)] sage: phi(Q.0) == Q.0 + 3*Q.1 True sage: phi(Q.1) == -Q.1 True For full documentation, see :class:`FGP_Morphism`. """ Morphism.__init__(self, parent) M = parent.domain() N = parent.codomain() if isinstance(phi, FGP_Morphism): if check: if phi.parent() != parent: raise TypeError phi = phi._phi check = False # no need # input: phi is a morphism from MO = M.optimized().V() to N.V() # that sends MO.W() to N.W() if check: if not is_Morphism(phi) and M == N: A = M.optimized()[0].V() B = N.V() s = M.base_ring()(phi) * B.coordinate_module(A).basis_matrix() phi = A.Hom(B)(s) MO, _ = M.optimized() if phi.domain() != MO.V(): raise ValueError("domain of phi must be the covering module for the optimized covering module of the domain") if phi.codomain() != N.V(): raise ValueError("codomain of phi must be the covering module the codomain.") # check that MO.W() gets sent into N.W() # todo (optimize): this is slow: for x in MO.W().basis(): if phi(x) not in N.W(): raise ValueError("phi must send optimized submodule of M.W() into N.W()") self._phi = phi
def __init__(self, domain, codomain, data={}): """ Initialize ``self``. Type ``QuiverRepHom?`` for more information. TESTS:: sage: Q = DiGraph({1:{2:['a', 'b']}, 2:{3:['c']}}).path_semigroup() sage: spaces = {1: QQ^2, 2: QQ^2, 3:QQ^1} sage: maps = {(1, 2, 'a'): [[1, 0], [0, 0]], (1, 2, 'b'): [[0, 0], [0, 1]], (2, 3, 'c'): [[1], [1]]} sage: M = Q.representation(QQ, spaces, maps) sage: spaces2 = {2: QQ^1, 3: QQ^1} sage: S = Q.representation(QQ, spaces2) sage: f = S.hom(M) sage: f.is_zero() True sage: maps2 = {2:[1, -1], 3:1} sage: g = S.hom(maps2, M) sage: x = M({2: (1, -1)}) sage: y = M({3: (1,)}) sage: h = S.hom([x, y], M) sage: g == h True sage: Proj = Q.P(GF(7), 3) sage: Simp = Q.S(GF(7), 3) sage: im = Simp({3: (1,)}) sage: Proj.hom(im, Simp).is_surjective() True TESTS:: sage: Q = DiGraph({1:{2:['a']}}).path_semigroup() sage: H1 = Q.P(GF(3), 2).Hom(Q.S(GF(3), 2)) sage: H2 = Q.P(GF(3), 2).Hom(Q.S(GF(3), 1)) sage: H1.an_element() in H1 # indirect doctest True """ # The data of a representation is held in the following private # variables: # # * _quiver # The quiver of the representation. # * _base_ring # The base ring of the representation. # * _domain # The QuiverRep object that is the domain of the homomorphism. # * _codomain # The QuiverRep object that is the codomain of the homomorphism. # * _vector # A vector in some free module over the base ring of a length such # that each coordinate corresponds to an entry in the matrix of a # homomorphism attached to a vertex. # # The variable data can also be a vector of appropriate length. When # this is the case it will be loaded directly into _vector and then # _assert_valid_hom is called. from sage.quivers.representation import QuiverRepElement, QuiverRep_with_path_basis self._domain = domain self._codomain = codomain self._quiver = domain._quiver self._base_ring = domain.base_ring() # Check that the quiver and base ring match if codomain._quiver != self._quiver: raise ValueError( "the quivers of the domain and codomain must be equal") if codomain.base_ring() != self._base_ring: raise ValueError( "the base ring of the domain and codomain must be equal") # Get the dimensions of the spaces mat_dims = {} domain_dims = {} codomain_dims = {} for v in self._quiver: domain_dims[v] = domain._spaces[v].dimension() codomain_dims[v] = codomain._spaces[v].dimension() mat_dims[v] = domain_dims[v] * codomain_dims[v] total_dim = sum(mat_dims.values()) # Handle the case when data is a vector if data in self._base_ring**total_dim: self._vector = data self._assert_valid_hom() super(QuiverRepHom, self).__init__(domain.Hom(codomain)) return # If data is not a dict, create one if isinstance(data, dict): maps_dict = data else: # If data is not a list create one, then create a dict from it if isinstance(data, list): im_list = data else: # If data is a QuiverRepHom, create a list from it if isinstance(data, QuiverRepHom): f = data._domain.coerce_map_from(domain) g = self._codomain.coerce_map_from(data._codomain) im_list = [g(data(f(x))) for x in domain.gens()] # The only case left is that data is a QuiverRepElement else: if not isinstance(data, QuiverRepElement): raise TypeError("input data must be dictionary, list, " "QuiverRepElement or vector") if not isinstance(domain, QuiverRep_with_path_basis): raise TypeError( "if data is a QuiverRepElement then domain " "must be a QuiverRep_with_path_basis.") if data not in codomain: raise ValueError( "if data is a QuiverRepElement then it must " "be an element of codomain") im_list = [ codomain.right_edge_action(data, p) for v in domain._quiver for p in domain._bases[v] ] # WARNING: This code assumes that the function QuiverRep.gens() returns # the generators ordered first by vertex and then by the order of the # gens() method of the space associated to that vertex. In particular # this is the order that corresponds to how maps are represented via # matrices # Get the gens of the domain and check that im_list is the right length dom_gens = domain.gens() if len(im_list) != len(dom_gens): raise ValueError( ("domain is dimension {} but only {} images" " were supplied").format(len(dom_gens), len(im_list))) # Get the matrices of the maps start_index = 0 maps_dict = {} for v in self._quiver: maps_dict[v] = [] dim = domain._spaces[v].dimension() for i in range(start_index, start_index + dim): if len(im_list[i].support()) != 0 and im_list[i].support( ) != [v]: # If the element doesn't have the correct support raise # an error here, otherwise we might create a valid hom # that does not map the generators to the supplied # images raise ValueError( ("generator supported at vertex {} cannot" " map to element with support {}").format( v, im_list[i].support())) else: # If the support works out add the images coordinates # as a row of the matrix maps_dict[v].append(codomain._spaces[v].coordinates( im_list[i]._elems[v])) start_index += dim # Get the coordinates of the vector from sage.categories.morphism import is_Morphism vector = [] for v in self._quiver: if v in maps_dict: if is_Morphism(maps_dict[v]): if hasattr(maps_dict[v], 'matrix'): m = maps_dict[v].matrix() else: gens_images = [ codomain._spaces[v].coordinate_vector( maps_dict[v](x)) for x in domain._spaces[v].gens() ] m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v], gens_images) else: m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v], maps_dict[v]) else: m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v]) for i in range(0, domain_dims[v]): vector += list(m[i]) # Wrap as a vector, check it, and return self._vector = (self._base_ring**total_dim)(vector) self._assert_valid_hom() super(QuiverRepHom, self).__init__(domain.Hom(codomain))
def __init__(self, domain, codomain, data={}): """ Initialize ``self``. Type ``QuiverRepHom?`` for more information. TESTS:: sage: Q = DiGraph({1:{2:['a', 'b']}, 2:{3:['c']}}).path_semigroup() sage: spaces = {1: QQ^2, 2: QQ^2, 3:QQ^1} sage: maps = {(1, 2, 'a'): [[1, 0], [0, 0]], (1, 2, 'b'): [[0, 0], [0, 1]], (2, 3, 'c'): [[1], [1]]} sage: M = Q.representation(QQ, spaces, maps) sage: spaces2 = {2: QQ^1, 3: QQ^1} sage: S = Q.representation(QQ, spaces2) sage: f = S.hom(M) sage: f.is_zero() True sage: maps2 = {2:[1, -1], 3:1} sage: g = S.hom(maps2, M) sage: x = M({2: (1, -1)}) sage: y = M({3: (1,)}) sage: h = S.hom([x, y], M) sage: g == h True sage: Proj = Q.P(GF(7), 3) sage: Simp = Q.S(GF(7), 3) sage: im = Simp({3: (1,)}) sage: Proj.hom(im, Simp).is_surjective() True TESTS:: sage: Q = DiGraph({1:{2:['a']}}).path_semigroup() sage: H1 = Q.P(GF(3), 2).Hom(Q.S(GF(3), 2)) sage: H2 = Q.P(GF(3), 2).Hom(Q.S(GF(3), 1)) sage: H1.an_element() in H1 # indirect doctest True """ # The data of a representation is held in the following private # variables: # # * _quiver # The quiver of the representation. # * _base_ring # The base ring of the representation. # * _domain # The QuiverRep object that is the domain of the homomorphism. # * _codomain # The QuiverRep object that is the codomain of the homomorphism. # * _vector # A vector in some free module over the base ring of a length such # that each coordinate corresponds to an entry in the matrix of a # homomorphism attached to a vertex. # # The variable data can also be a vector of appropriate length. When # this is the case it will be loaded directly into _vector and then # _assert_valid_hom is called. from sage.quivers.representation import QuiverRepElement, QuiverRep_with_path_basis self._domain = domain self._codomain = codomain self._quiver = domain._quiver self._base_ring = domain.base_ring() # Check that the quiver and base ring match if codomain._quiver != self._quiver: raise ValueError("the quivers of the domain and codomain must be equal") if codomain.base_ring() != self._base_ring: raise ValueError("the base ring of the domain and codomain must be equal") # Get the dimensions of the spaces mat_dims = {} domain_dims = {} codomain_dims = {} for v in self._quiver: domain_dims[v] = domain._spaces[v].dimension() codomain_dims[v] = codomain._spaces[v].dimension() mat_dims[v] = domain_dims[v]*codomain_dims[v] total_dim = sum(mat_dims.values()) # Handle the case when data is a vector if data in self._base_ring**total_dim: self._vector = data self._assert_valid_hom() super(QuiverRepHom, self).__init__(domain.Hom(codomain)) return # If data is not a dict, create one if isinstance(data, dict): maps_dict = data else: # If data is not a list create one, then create a dict from it if isinstance(data, list): im_list = data else: # If data is a QuiverRepHom, create a list from it if isinstance(data, QuiverRepHom): f = data._domain.coerce_map_from(domain) g = self._codomain.coerce_map_from(data._codomain) im_list = [g(data(f(x))) for x in domain.gens()] # The only case left is that data is a QuiverRepElement else: if not isinstance(data, QuiverRepElement): raise TypeError("input data must be dictionary, list, " "QuiverRepElement or vector") if not isinstance(domain, QuiverRep_with_path_basis): raise TypeError("if data is a QuiverRepElement then domain " "must be a QuiverRep_with_path_basis.") if data not in codomain: raise ValueError("if data is a QuiverRepElement then it must " "be an element of codomain") im_list = [codomain.right_edge_action(data, p) for v in domain._quiver for p in domain._bases[v]] # WARNING: This code assumes that the function QuiverRep.gens() returns # the generators ordered first by vertex and then by the order of the # gens() method of the space associated to that vertex. In particular # this is the order that corresponds to how maps are represented via # matrices # Get the gens of the domain and check that im_list is the right length dom_gens = domain.gens() if len(im_list) != len(dom_gens): raise ValueError(("domain is dimension {} but only {} images" " were supplied").format(len(dom_gens), len(im_list))) # Get the matrices of the maps start_index = 0 maps_dict = {} for v in self._quiver: maps_dict[v] = [] dim = domain._spaces[v].dimension() for i in range(start_index, start_index + dim): if len(im_list[i].support()) != 0 and im_list[i].support() != [v]: # If the element doesn't have the correct support raise # an error here, otherwise we might create a valid hom # that does not map the generators to the supplied # images raise ValueError(("generator supported at vertex {} cannot" " map to element with support {}").format( v, im_list[i].support())) else: # If the support works out add the images coordinates # as a row of the matrix maps_dict[v].append(codomain._spaces[v].coordinates(im_list[i]._elems[v])) start_index += dim # Get the coordinates of the vector from sage.categories.morphism import is_Morphism vector = [] for v in self._quiver: if v in maps_dict: if is_Morphism(maps_dict[v]): if hasattr(maps_dict[v], 'matrix'): m = maps_dict[v].matrix() else: gens_images = [codomain._spaces[v].coordinate_vector(maps_dict[v](x)) for x in domain._spaces[v].gens()] m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v], gens_images) else: m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v], maps_dict[v]) else: m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v]) for i in range(0, domain_dims[v]): vector += list(m[i]) # Wrap as a vector, check it, and return self._vector = (self._base_ring**total_dim)(vector) self._assert_valid_hom() super(QuiverRepHom, self).__init__(domain.Hom(codomain))