def homogenize(self,n,newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()``. The domain and codomain can be homogenized at different coordinates: ``n[0]`` for the domain and ``n[1]`` for the codomain. INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, then the two values of the tuple are assumed to be the same. OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize((2,0),'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize((2,0),'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) :: sage: A.<x>=AffineSpace(QQ,1) sage: H=End(A) sage: f=H([x^2-1]) sage: f.homogenize((1,0),'y') Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (y^2 : x^2 - y^2) """ A=self.domain() B=self.codomain() N=A.ambient_space().dimension_relative() NB=B.ambient_space().dimension_relative() #it is possible to homogenize the domain and codomain at different coordinates if isinstance(n,(tuple,list)): ind=tuple(n) else: ind=(n,n) #homogenize the domain Vars=list(A.ambient_space().variable_names()) Vars.insert(ind[0],newvar) S=PolynomialRing(A.base_ring(),Vars) #find the denominators if a rational function try: l=lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l=prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField(): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] else: F=[S(self[i]*l) for i in range(N)] #homogenize the codomain F.insert(ind[1],S(l)) d=max([F[i].degree() for i in range(N+1)]) F=[F[i].homogenize(newvar)*S.gen(N)**(d-F[i].degree()) for i in range(N+1)] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A)==True: from sage.schemes.projective.projective_space import ProjectiveSpace X=ProjectiveSpace(A.base_ring(),NB,Vars) else: X=A.projective_embedding(ind[1]).codomain() phi=S.hom(X.ambient_space().gens(),X.ambient_space().coordinate_ring()) F=[phi(f) for f in F] H=Hom(X,X) return(H(F))
def __init__(self, source, target, precision): Morphism.__init__(self, Hom(source, target, Rings())) self.SPN = target self.target_precision = precision
def homogenize(self, n): r""" Return the homogenization of this map. If it's domain is a subscheme, the domain of the homogenized map is the projective embedding of the domain. The domain and codomain can be homogenized at different coordinates: ``n[0]`` for the domain and ``n[1]`` for the codomain. INPUT: - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, then the two values of the tuple are assumed to be the same. OUTPUT: - :class:`SchemeMorphism_polynomial_projective_space`. EXAMPLES:: sage: A.<x,y> = AffineSpace(ZZ, 2) sage: H = Hom(A, A) sage: f = H([(x^2-2)/x^5, y^2]) sage: f.homogenize(2) Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x0 : x1 : x2) to (x0^2*x2^5 - 2*x2^7 : x0^5*x1^2 : x0^5*x2^2) :: sage: A.<x,y> = AffineSpace(CC, 2) sage: H = Hom(A, A) sage: f = H([(x^2-2)/(x*y), y^2-x]) sage: f.homogenize((2, 0)) Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x0 : x1 : x2) to (x0*x1*x2^2 : x0^2*x2^2 + (-2.00000000000000)*x2^4 : x0*x1^3 - x0^2*x1*x2) :: sage: A.<x,y> = AffineSpace(ZZ, 2) sage: X = A.subscheme([x-y^2]) sage: H = Hom(X, X) sage: f = H([9*y^2, 3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: x1^2 - x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x1^2 : 3*x1*x2 : x2^2) :: sage: R.<t> = PolynomialRing(ZZ) sage: A.<x,y> = AffineSpace(R, 2) sage: H = Hom(A, A) sage: f = H([(x^2-2)/y, y^2-x]) sage: f.homogenize((2, 0)) Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x0 : x1 : x2) to (x1*x2^2 : x0^2*x2 + (-2)*x2^3 : x1^3 - x0*x1*x2) :: sage: A.<x> = AffineSpace(QQ, 1) sage: H = End(A) sage: f = H([x^2-1]) sage: f.homogenize((1, 0)) Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x0 : x1) to (x1^2 : x0^2 - x1^2) :: sage: R.<a> = PolynomialRing(QQbar) sage: A.<x,y> = AffineSpace(R, 2) sage: H = End(A) sage: f = H([QQbar(sqrt(2))*x*y, a*x^2]) sage: f.homogenize(2) Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in a over Algebraic Field Defn: Defined on coordinates by sending (x0 : x1 : x2) to (1.414213562373095?*x0*x1 : a*x0^2 : x2^2) :: sage: P.<x,y,z> = AffineSpace(QQ, 3) sage: H = End(P) sage: f = H([x^2 - 2*x*y + z*x, z^2 -y^2 , 5*z*y]) sage: f.homogenize(2).dehomogenize(2) == f True :: sage: K.<c> = FunctionField(QQ) sage: A.<x> = AffineSpace(K, 1) sage: f = Hom(A, A)([x^2 + c]) sage: f.homogenize(1) Scheme endomorphism of Projective Space of dimension 1 over Rational function field in c over Rational Field Defn: Defined on coordinates by sending (x0 : x1) to (x0^2 + c*x1^2 : x1^2) :: sage: A.<z> = AffineSpace(QQbar, 1) sage: H = End(A) sage: f = H([2*z / (z^2+2*z+3)]) sage: f.homogenize(1) Scheme endomorphism of Projective Space of dimension 1 over Algebraic Field Defn: Defined on coordinates by sending (x0 : x1) to (x0*x1 : 1/2*x0^2 + x0*x1 + 3/2*x1^2) :: sage: A.<z> = AffineSpace(QQbar, 1) sage: H = End(A) sage: f = H([2*z / (z^2 + 2*z + 3)]) sage: f.homogenize(1) Scheme endomorphism of Projective Space of dimension 1 over Algebraic Field Defn: Defined on coordinates by sending (x0 : x1) to (x0*x1 : 1/2*x0^2 + x0*x1 + 3/2*x1^2) :: sage: R.<c,d> = QQbar[] sage: A.<x> = AffineSpace(R, 1) sage: H = Hom(A, A) sage: F = H([d*x^2 + c]) sage: F.homogenize(1) Scheme endomorphism of Projective Space of dimension 1 over Multivariate Polynomial Ring in c, d over Algebraic Field Defn: Defined on coordinates by sending (x0 : x1) to (d*x0^2 + c*x1^2 : x1^2) """ #it is possible to homogenize the domain and codomain at different coordinates if isinstance(n, (tuple, list)): ind = tuple(n) else: ind = (n, n) #homogenize the domain and codomain A = self.domain().projective_embedding(ind[0]).codomain() if self.is_endomorphism(): B = A H = End(A) else: B = self.codomain().projective_embedding(ind[1]).codomain() H = Hom(A, B) newvar = A.ambient_space().coordinate_ring().gen(ind[0]) N = A.ambient_space().dimension_relative() M = B.ambient_space().dimension_relative() #create dictionary for mapping of coordinate rings R = self.domain().ambient_space().coordinate_ring() S = A.ambient_space().coordinate_ring() Rvars = R.gens() vars = list(S.gens()) vars.remove(S.gen(ind[0])) D = dict([[Rvars[i], vars[i]] for i in range(N)]) #clear the denominators if a rational function L = [self[i].denominator() for i in range(M)] l = [prod(L[:j] + L[j + 1:M]) for j in range(M)] F = [S(R(self[i].numerator() * l[i]).subs(D)) for i in range(M)] #homogenize F.insert(ind[1], S(R(prod(L)).subs(D))) #coerce in case l is a constant try: #remove possible gcd of the polynomials g = gcd(F) F = [S(f / g) for f in F] #remove possible gcd of coefficients gc = gcd([f.content() for f in F]) F = [S(f / gc) for f in F] except (AttributeError, ValueError, NotImplementedError, TypeError, ArithmeticError): #no gcd pass d = max([F[i].degree() for i in range(M + 1)]) F = [ F[i].homogenize(str(newvar)) * newvar**(d - F[i].degree()) for i in range(M + 1) ] return (H(F))
def End(X, category=None): r""" Create the set of endomorphisms of ``X`` in the category category. INPUT: - ``X`` -- anything - ``category`` -- (optional) category in which to coerce ``X`` OUTPUT: A set of endomorphisms in category EXAMPLES:: sage: V = VectorSpace(QQ, 3) sage: End(V) Set of Morphisms (Linear Transformations) from Vector space of dimension 3 over Rational Field to Vector space of dimension 3 over Rational Field :: sage: G = AlternatingGroup(3) sage: S = End(G); S Set of Morphisms from Alternating group of order 3!/2 as a permutation group to Alternating group of order 3!/2 as a permutation group in Category of finite permutation groups sage: from sage.categories.homset import is_Endset sage: is_Endset(S) True sage: S.domain() Alternating group of order 3!/2 as a permutation group To avoid creating superfluous categories, a homset in a category ``Cs()`` is in the homset category of the lowest full super category ``Bs()`` of ``Cs()`` that implements ``Bs.Homsets`` (or the join thereof if there are several). For example, finite groups form a full subcategory of unital magmas: any unital magma morphism between two finite groups is a finite group morphism. Since finite groups currently implement nothing more than unital magmas about their homsets, we have:: sage: G = GL(3,3) sage: G.category() Category of finite groups sage: H = Hom(G,G) sage: H.homset_category() Category of groups sage: H.category() Category of endsets of unital magmas Similarly, a ring morphism just needs to preserve addition, multiplication, zero, and one. Accordingly, and since the category of rings implements nothing specific about its homsets, a ring homset is currently constructed in the category of homsets of unital magmas and unital additive magmas:: sage: H = Hom(ZZ,ZZ,Rings()) sage: H.category() Category of endsets of unital magmas and additive unital additive magmas """ return Hom(X, X, category)
def Hom(X, Y, category=None, check=True): """ Create the space of homomorphisms from X to Y in the category ``category``. INPUT: - ``X`` -- an object of a category - ``Y`` -- an object of a category - ``category`` -- a category in which the morphisms must be. (default: the meet of the categories of ``X`` and ``Y``) Both ``X`` and ``Y`` must belong to that category. - ``check`` -- a boolean (default: ``True``): whether to check the input, and in particular that ``X`` and ``Y`` belong to ``category``. OUTPUT: a homset in category EXAMPLES:: sage: V = VectorSpace(QQ,3) sage: Hom(V, V) Set of Morphisms (Linear Transformations) from Vector space of dimension 3 over Rational Field to Vector space of dimension 3 over Rational Field sage: G = AlternatingGroup(3) sage: Hom(G, G) Set of Morphisms from Alternating group of order 3!/2 as a permutation group to Alternating group of order 3!/2 as a permutation group in Category of finite permutation groups sage: Hom(ZZ, QQ, Sets()) Set of Morphisms from Integer Ring to Rational Field in Category of sets sage: Hom(FreeModule(ZZ,1), FreeModule(QQ,1)) Set of Morphisms from Ambient free module of rank 1 over the principal ideal domain Integer Ring to Vector space of dimension 1 over Rational Field in Category of commutative additive groups sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1)) Set of Morphisms from Vector space of dimension 1 over Rational Field to Ambient free module of rank 1 over the principal ideal domain Integer Ring in Category of commutative additive groups Here, we test against a memory leak that has been fixed at :trac:`11521` by using a weak cache:: sage: for p in prime_range(10^3): ... K = GF(p) ... a = K(0) sage: import gc sage: gc.collect() # random 624 sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF sage: L = [x for x in gc.get_objects() if isinstance(x, FF)] sage: len(L), L[0], L[len(L)-1] (2, Finite Field of size 2, Finite Field of size 997) To illustrate the choice of the category, we consider the following parents as running examples:: sage: X = ZZ; X Integer Ring sage: Y = SymmetricGroup(3); Y Symmetric group of order 3! as a permutation group By default, the smallest category containing both ``X`` and ``Y``, is used:: sage: Hom(X, Y) Set of Morphisms from Integer Ring to Symmetric group of order 3! as a permutation group in Join of Category of monoids and Category of enumerated sets Otherwise, if ``category`` is specified, then ``category`` is used, after checking that ``X`` and ``Y`` are indeed in ``category``:: sage: Hom(X, Y, Magmas()) Set of Morphisms from Integer Ring to Symmetric group of order 3! as a permutation group in Category of magmas sage: Hom(X, Y, Groups()) Traceback (most recent call last): ... ValueError: Integer Ring is not in Category of groups A parent (or a parent class of a category) may specify how to construct certain homsets by implementing a method ``_Hom_(self, codomain, category)``. This method should either construct the requested homset or raise a ``TypeError``. This hook is currently mostly used to create homsets in some specific subclass of :class:`Homset` (e.g. :class:`sage.rings.homset.RingHomset`):: sage: Hom(QQ,QQ).__class__ <class 'sage.rings.homset.RingHomset_generic_with_category'> Do not call this hook directly to create homsets, as it does not handle unique representation:: sage: Hom(QQ,QQ) == QQ._Hom_(QQ, category=QQ.category()) True sage: Hom(QQ,QQ) is QQ._Hom_(QQ, category=QQ.category()) False TESTS: Homset are unique parents:: sage: k = GF(5) sage: H1 = Hom(k,k) sage: H2 = Hom(k,k) sage: H1 is H2 True Moreover, if no category is provided, then the result is identical with the result for the meet of the categories of the domain and the codomain:: sage: Hom(QQ, ZZ) is Hom(QQ,ZZ, Category.meet([QQ.category(), ZZ.category()])) True Some doc tests in :mod:`sage.rings` (need to) break the unique parent assumption. But if domain or codomain are not unique parents, then the homset will not fit. That is to say, the hom set found in the cache will have a (co)domain that is equal to, but not identical with, the given (co)domain. By :trac:`9138`, we abandon the uniqueness of homsets, if the domain or codomain break uniqueness:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain sage: P.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex') sage: Q.<x,y,z>=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex') sage: P == Q True sage: P is Q False Hence, ``P`` and ``Q`` are not unique parents. By consequence, the following homsets aren't either:: sage: H1 = Hom(QQ,P) sage: H2 = Hom(QQ,Q) sage: H1 == H2 True sage: H1 is H2 False It is always the most recently constructed homset that remains in the cache:: sage: H2 is Hom(QQ,Q) True Variation on the theme:: sage: U1 = FreeModule(ZZ,2) sage: U2 = FreeModule(ZZ,2,inner_product_matrix=matrix([[1,0],[0,-1]])) sage: U1 == U2, U1 is U2 (True, False) sage: V = ZZ^3 sage: H1 = Hom(U1, V); H2 = Hom(U2, V) sage: H1 == H2, H1 is H2 (True, False) sage: H1 = Hom(V, U1); H2 = Hom(V, U2) sage: H1 == H2, H1 is H2 (True, False) Since :trac:`11900`, the meet of the categories of the given arguments is used to determine the default category of the homset. This can also be a join category, as in the following example:: sage: PA = Parent(category=Algebras(QQ)) sage: PJ = Parent(category=Rings() & Modules(QQ)) sage: Hom(PA,PJ) Set of Homomorphisms from <type 'sage.structure.parent.Parent'> to <type 'sage.structure.parent.Parent'> sage: Hom(PA,PJ).category() Category of homsets of unital magmas and right modules over Rational Field and left modules over Rational Field sage: Hom(PA,PJ, Rngs()) Set of Morphisms from <type 'sage.structure.parent.Parent'> to <type 'sage.structure.parent.Parent'> in Category of rngs .. TODO:: - Design decision: how much of the homset comes from the category of ``X`` and ``Y``, and how much from the specific ``X`` and ``Y``. In particular, do we need several parent classes depending on ``X`` and ``Y``, or does the difference only lie in the elements (i.e. the morphism), and of course how the parent calls their constructors. - Specify the protocol for the ``_Hom_`` hook in case of ambiguity (e.g. if both a parent and some category thereof provide one). TESTS: Facade parents over plain Python types are supported:: sage: R = sage.structure.parent.Set_PythonType(int) sage: S = sage.structure.parent.Set_PythonType(float) sage: Hom(R, S) Set of Morphisms from Set of Python objects of type 'int' to Set of Python objects of type 'float' in Category of sets Checks that the domain and codomain are in the specified category. Case of a non parent:: sage: S = SimplicialComplex([[1,2], [1,4]]); S.rename("S") sage: Hom(S, S, SimplicialComplexes()) Set of Morphisms from S to S in Category of simplicial complexes sage: H = Hom(Set(), S, Sets()) Traceback (most recent call last): ... ValueError: S is not in Category of sets sage: H = Hom(S, Set(), Sets()) Traceback (most recent call last): ... ValueError: S is not in Category of sets sage: H = Hom(S, S, ChainComplexes(QQ)) Traceback (most recent call last): ... ValueError: S is not in Category of chain complexes over Rational Field Those checks are done with the natural idiom ``X in category``, and not ``X.category().is_subcategory(category)`` as it used to be before :trac:`16275` (see :trac:`15801` for a real use case):: sage: class PermissiveCategory(Category): ....: def super_categories(self): return [Objects()] ....: def __contains__(self, X): return True sage: C = PermissiveCategory(); C.rename("Permissive category") sage: S.category().is_subcategory(C) False sage: S in C True sage: Hom(S, S, C) Set of Morphisms from S to S in Permissive category With ``check=False``, unitialized parents, as can appear upon unpickling, are supported. Case of a parent:: sage: cls = type(Set()) sage: S = unpickle_newobj(cls, ()) # A non parent sage: H = Hom(S, S, SimplicialComplexes(), check=False); sage: H = Hom(S, S, Sets(), check=False) sage: H = Hom(S, S, ChainComplexes(QQ), check=False) Case of a non parent:: sage: cls = type(SimplicialComplex([[1,2], [1,4]])) sage: S = unpickle_newobj(cls, ()) sage: H = Hom(S, S, Sets(), check=False) sage: H = Hom(S, S, Groups(), check=False) sage: H = Hom(S, S, SimplicialComplexes(), check=False) Typical example where unpickling involves calling Hom on an unitialized parent:: sage: P.<x,y> = QQ['x,y'] sage: Q = P.quotient([x^2-1,y^2-1]) sage: q = Q.an_element() sage: explain_pickle(dumps(Q)) pg_... ... = pg_dynamic_class('QuotientRing_generic_with_category', (pg_QuotientRing_generic, pg_getattr(..., 'parent_class')), None, None, pg_QuotientRing_generic) si... = unpickle_newobj(..., ()) ... si... = pg_unpickle_MPolynomialRing_libsingular(..., ('x', 'y'), ...) si... = ... pg_Hom(si..., si..., ...) ... sage: Q == loads(dumps(Q)) True """ # This should use cache_function instead # However some special handling is currently needed for # domains/docomains that break the unique parent condition. Also, # at some point, it somehow broke the coercion (see e.g. sage -t # sage.rings.real_mpfr). To be investigated. global _cache key = (X, Y, category) try: H = _cache[key] except KeyError: H = None if H is not None: # Return H unless the domain or codomain breaks the unique parent condition if H.domain() is X and H.codomain() is Y: return H # Determines the category if category is None: category = X.category()._meet_(Y.category()) # Recurse to make sure that Hom(X, Y) and Hom(X, Y, category) are identical # No need to check the input again H = Hom(X, Y, category, check=False) else: if check: if not isinstance(category, Category): raise TypeError( "Argument category (= {}) must be a category.".format( category)) for O in [X, Y]: try: category_mismatch = O not in category except Exception: # An error should not happen, this here is just to be on # the safe side. category_mismatch = True # A category mismatch does not necessarily mean that an error # should be raised. Instead, it could be the case that we are # unpickling an old pickle (that doesn't set the "check" # argument to False). In this case, it could be that the # (co)domain is not properly initialised, which we are # checking now. See trac #16275 and #14793. if category_mismatch and O._is_category_initialized(): # At this point, we can be rather sure that O is properly # initialised, and thus its string representation is # available for the following error message. It simply # belongs to the wrong category. raise ValueError("{} is not in {}".format(O, category)) # Construct H try: # _Hom_ hook from the parent H = X._Hom_(Y, category) except (AttributeError, TypeError): try: # Workaround in case the above fails, but the category # also provides a _Hom_ hook. # FIXME: # - If X._Hom_ actually comes from category and fails, it # will be called twice. # - This is bound to fail if X is an extension type and # does not actually inherit from category.parent_class H = category.parent_class._Hom_(X, Y, category=category) except (AttributeError, TypeError): # By default, construct a plain homset. H = Homset(X, Y, category=category, check=check) _cache[key] = H if isinstance(X, UniqueRepresentation) and isinstance( Y, UniqueRepresentation): if not isinstance(H, WithEqualityById): try: H.__class__ = dynamic_class(H.__class__.__name__ + "_with_equality_by_id", (WithEqualityById, H.__class__), doccls=H.__class__) except Exception: pass return H
def __init__(self, E=None, urst=None, F=None): r""" Constructor for WeierstrassIsomorphism class, INPUT: - ``E`` -- an EllipticCurve, or None (see below). - ``urst`` -- a 4-tuple `(u,r,s,t)`, or None (see below). - ``F`` -- an EllipticCurve, or None (see below). Given two Elliptic Curves ``E`` and ``F`` (represented by Weierstrass models as usual), and a transformation ``urst`` from ``E`` to ``F``, construct an isomorphism from ``E`` to ``F``. An exception is raised if ``urst(E)!=F``. At most one of ``E``, ``F``, ``urst`` can be None. If ``F==None`` then ``F`` is constructed as ``urst(E)``. If ``E==None`` then ``E`` is constructed as ``urst^-1(F)``. If ``urst==None`` then an isomorphism from ``E`` to ``F`` is constructed if possible, and an exception is raised if they are not isomorphic. Otherwise ``urst`` can be a tuple of length 4 or a object of type ``baseWI``. Users will not usually need to use this class directly, but instead use methods such as ``isomorphism`` of elliptic curves. EXAMPLES:: sage: from sage.schemes.elliptic_curves.weierstrass_morphism import * sage: WeierstrassIsomorphism(EllipticCurve([0,1,2,3,4]),(-1,2,3,4)) Generic morphism: From: Abelian group of points on Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field To: Abelian group of points on Elliptic Curve defined by y^2 - 6*x*y - 10*y = x^3 - 2*x^2 - 11*x - 2 over Rational Field Via: (u,r,s,t) = (-1, 2, 3, 4) sage: E=EllipticCurve([0,1,2,3,4]) sage: F=EllipticCurve(E.cremona_label()) sage: WeierstrassIsomorphism(E,None,F) Generic morphism: From: Abelian group of points on Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x^2 + 3*x + 5 over Rational Field Via: (u,r,s,t) = (1, 0, 0, -1) sage: w=WeierstrassIsomorphism(None,(1,0,0,-1),F) sage: w._domain_curve==E True """ from ell_generic import is_EllipticCurve if E!=None: if not is_EllipticCurve(E): raise ValueError, "First argument must be an elliptic curve or None" if F!=None: if not is_EllipticCurve(F): raise ValueError, "Third argument must be an elliptic curve or None" if urst!=None: if len(urst)!=4: raise ValueError, "Second argument must be [u,r,s,t] or None" if len([par for par in [E,urst,F] if par!=None])<2: raise ValueError, "At most 1 argument can be None" if F==None: # easy case baseWI.__init__(self,*urst) F=EllipticCurve(baseWI.__call__(self,list(E.a_invariants()))) Morphism.__init__(self, Hom(E(0).parent(), F(0).parent())) self._domain_curve = E self._codomain_curve = F return if E==None: # easy case in reverse baseWI.__init__(self,*urst) inv_urst=baseWI.__invert__(self) E=EllipticCurve(baseWI.__call__(inv_urst,list(F.a_invariants()))) Morphism.__init__(self, Hom(E(0).parent(), F(0).parent())) self._domain_curve = E self._codomain_curve = F return if urst==None: # try to construct the morphism urst=isomorphisms(E,F,True) if urst==None: raise ValueError, "Elliptic curves not isomorphic." baseWI.__init__(self, *urst) Morphism.__init__(self, Hom(E(0).parent(), F(0).parent())) self._domain_curve = E self._codomain_curve = F return # none of the parameters is None: baseWI.__init__(self,*urst) if F!=EllipticCurve(baseWI.__call__(self,list(E.a_invariants()))): raise ValueError, "second argument is not an isomorphism from first argument to third argument" else: Morphism.__init__(self, Hom(E(0).parent(), F(0).parent())) self._domain_curve = E self._codomain_curve = F return
def diff_map(self, codomain, coord_functions=None, chart1=None, chart2=None, name=None, latex_name=None): r""" Define a differentiable map between the current differentiable manifold and a differentiable manifold over the same topological field. See :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for a complete documentation. INPUT: - ``codomain`` -- the map codomain (a differentiable manifold over the same topological field as the current differentiable manifold) - ``coord_functions`` -- (default: ``None``) if not ``None``, must be either - (i) a dictionary of the coordinate expressions (as lists (or tuples) of the coordinates of the image expressed in terms of the coordinates of the considered point) with the pairs of charts (chart1, chart2) as keys (chart1 being a chart on the current manifold and chart2 a chart on ``codomain``) - (ii) a single coordinate expression in a given pair of charts, the latter being provided by the arguments ``chart1`` and ``chart2`` In both cases, if the dimension of the arrival manifold is 1, a single coordinate expression can be passed instead of a tuple with a single element - ``chart1`` -- (default: ``None``; used only in case (ii) above) chart on the current manifold defining the start coordinates involved in ``coord_functions`` for case (ii); if none is provided, the coordinates are assumed to refer to the manifold's default chart - ``chart2`` -- (default: ``None``; used only in case (ii) above) chart on ``codomain`` defining the arrival coordinates involved in ``coord_functions`` for case (ii); if none is provided, the coordinates are assumed to refer to the default chart of ``codomain`` - ``name`` -- (default: ``None``) name given to the differentiable map - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the differentiable map; if none is provided, the LaTeX symbol is set to ``name`` OUTPUT: - the differentiable map, as an instance of :class:`~sage.manifolds.differentiable.diff_map.DiffMap` EXAMPLES: A differentiable map between an open subset of `S^2` covered by regular spherical coordinates and `\RR^3`:: sage: M = Manifold(2, 'S^2') sage: U = M.open_subset('U') sage: c_spher.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: N = Manifold(3, 'R^3', r'\RR^3') sage: c_cart.<x,y,z> = N.chart() # Cartesian coord. on R^3 sage: Phi = U.diff_map(N, (sin(th)*cos(ph), sin(th)*sin(ph), cos(th)), ....: name='Phi', latex_name=r'\Phi') sage: Phi Differentiable map Phi from the Open subset U of the 2-dimensional differentiable manifold S^2 to the 3-dimensional differentiable manifold R^3 The same definition, but with a dictionary with pairs of charts as keys (case (i) above):: sage: Phi1 = U.diff_map(N, ....: {(c_spher, c_cart): (sin(th)*cos(ph), sin(th)*sin(ph), ....: cos(th))}, name='Phi', latex_name=r'\Phi') sage: Phi1 == Phi True The differentiable map acting on a point:: sage: p = U.point((pi/2, pi)) ; p Point on the 2-dimensional differentiable manifold S^2 sage: Phi(p) Point on the 3-dimensional differentiable manifold R^3 sage: Phi(p).coord(c_cart) (-1, 0, 0) sage: Phi1(p) == Phi(p) True See the documentation of class :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for more examples. """ homset = Hom(self, codomain) if coord_functions is None: coord_functions = {} if not isinstance(coord_functions, dict): # Turn coord_functions into a dictionary: if chart1 is None: chart1 = self._def_chart elif chart1 not in self._atlas: raise ValueError("{} is not a chart ".format(chart1) + "defined on the {}".format(self)) if chart2 is None: chart2 = codomain._def_chart elif chart2 not in codomain._atlas: raise ValueError("{} is not a chart ".format(chart2) + " defined on the {}".format(codomain)) coord_functions = {(chart1, chart2): coord_functions} return homset(coord_functions, name=name, latex_name=latex_name)
def linear_transformation(arg0, arg1=None, arg2=None, side='left'): r""" Create a linear transformation from a variety of possible inputs. FORMATS: In the following, ``D`` and ``C`` are vector spaces over the same field that are the domain and codomain (respectively) of the linear transformation. ``side`` is a keyword that is either 'left' or 'right'. When a matrix is used to specify a linear transformation, as in the first two call formats below, you may specify if the function is given by matrix multiplication with the vector on the left, or the vector on the right. The default is 'left'. Internally representations are always carried as the 'left' version, and the default text representation is this version. However, the matrix representation may be obtained as either version, no matter how it is created. - ``linear_transformation(A, side='left')`` Where ``A`` is a matrix. The domain and codomain are inferred from the dimension of the matrix and the base ring of the matrix. The base ring must be a field, or have its fraction field implemented in Sage. - ``linear_transformation(D, C, A, side='left')`` ``A`` is a matrix that behaves as above. However, now the domain and codomain are given explicitly. The matrix is checked for compatibility with the domain and codomain. Additionally, the domain and codomain may be supplied with alternate ("user") bases and the matrix is interpreted as being a representation relative to those bases. - ``linear_transformation(D, C, f)`` ``f`` is any function that can be applied to the basis elements of the domain and that produces elements of the codomain. The linear transformation returned is the unique linear transformation that extends this mapping on the basis elements. ``f`` may come from a function defined by a Python ``def`` statement, or may be defined as a ``lambda`` function. Alternatively, ``f`` may be specified by a callable symbolic function, see the examples below for a demonstration. - ``linear_transformation(D, C, images)`` ``images`` is a list, or tuple, of codomain elements, equal in number to the size of the basis of the domain. Each basis element of the domain is mapped to the corresponding element of the ``images`` list, and the linear transformation returned is the unique linear transformation that extends this mapping. OUTPUT: A linear transformation described by the input. This is a "vector space morphism", an object of the class :class:`sage.modules.vector_space_morphism`. EXAMPLES: We can define a linear transformation with just a matrix, understood to act on a vector placed on one side or the other. The field for the vector spaces used as domain and codomain is obtained from the base ring of the matrix, possibly promoting to a fraction field. :: sage: A = matrix(ZZ, [[1, -1, 4], [2, 0, 5]]) sage: phi = linear_transformation(A) sage: phi Vector space morphism represented by the matrix: [ 1 -1 4] [ 2 0 5] Domain: Vector space of dimension 2 over Rational Field Codomain: Vector space of dimension 3 over Rational Field sage: phi([1/2, 5]) (21/2, -1/2, 27) sage: B = matrix(Integers(7), [[1, 2, 1], [3, 5, 6]]) sage: rho = linear_transformation(B, side='right') sage: rho Vector space morphism represented by the matrix: [1 3] [2 5] [1 6] Domain: Vector space of dimension 3 over Ring of integers modulo 7 Codomain: Vector space of dimension 2 over Ring of integers modulo 7 sage: rho([2, 4, 6]) (2, 6) We can define a linear transformation with a matrix, while explicitly giving the domain and codomain. Matrix entries will be coerced into the common field of scalars for the vector spaces. :: sage: D = QQ^3 sage: C = QQ^2 sage: A = matrix([[1, 7], [2, -1], [0, 5]]) sage: A.parent() Full MatrixSpace of 3 by 2 dense matrices over Integer Ring sage: zeta = linear_transformation(D, C, A) sage: zeta.matrix().parent() Full MatrixSpace of 3 by 2 dense matrices over Rational Field sage: zeta Vector space morphism represented by the matrix: [ 1 7] [ 2 -1] [ 0 5] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field Matrix representations are relative to the bases for the domain and codomain. :: sage: u = vector(QQ, [1, -1]) sage: v = vector(QQ, [2, 3]) sage: D = (QQ^2).subspace_with_basis([u, v]) sage: x = vector(QQ, [2, 1]) sage: y = vector(QQ, [-1, 4]) sage: C = (QQ^2).subspace_with_basis([x, y]) sage: A = matrix(QQ, [[2, 5], [3, 7]]) sage: psi = linear_transformation(D, C, A) sage: psi Vector space morphism represented by the matrix: [2 5] [3 7] Domain: Vector space of degree 2 and dimension 2 over Rational Field User basis matrix: [ 1 -1] [ 2 3] Codomain: Vector space of degree 2 and dimension 2 over Rational Field User basis matrix: [ 2 1] [-1 4] sage: psi(u) == 2*x + 5*y True sage: psi(v) == 3*x + 7*y True Functions that act on the domain may be used to compute images of the domain's basis elements, and this mapping can be extended to a unique linear transformation. The function may be a Python function (via ``def`` or ``lambda``) or a Sage symbolic function. :: sage: def g(x): ....: return vector(QQ, [2*x[0]+x[2], 5*x[1]]) sage: phi = linear_transformation(QQ^3, QQ^2, g) sage: phi Vector space morphism represented by the matrix: [2 0] [0 5] [1 0] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: f = lambda x: vector(QQ, [2*x[0]+x[2], 5*x[1]]) sage: rho = linear_transformation(QQ^3, QQ^2, f) sage: rho Vector space morphism represented by the matrix: [2 0] [0 5] [1 0] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: x, y, z = var('x y z') sage: h(x, y, z) = [2*x + z, 5*y] sage: zeta = linear_transformation(QQ^3, QQ^2, h) sage: zeta Vector space morphism represented by the matrix: [2 0] [0 5] [1 0] Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: phi == rho True sage: rho == zeta True We create a linear transformation relative to non-standard bases, and capture its representation relative to standard bases. With this, we can build functions that create the same linear transformation relative to the nonstandard bases. :: sage: u = vector(QQ, [1, -1]) sage: v = vector(QQ, [2, 3]) sage: D = (QQ^2).subspace_with_basis([u, v]) sage: x = vector(QQ, [2, 1]) sage: y = vector(QQ, [-1, 4]) sage: C = (QQ^2).subspace_with_basis([x, y]) sage: A = matrix(QQ, [[2, 5], [3, 7]]) sage: psi = linear_transformation(D, C, A) sage: rho = psi.restrict_codomain(QQ^2).restrict_domain(QQ^2) sage: rho.matrix() [ -4/5 97/5] [ 1/5 -13/5] sage: f = lambda x: vector(QQ, [(-4/5)*x[0] + (1/5)*x[1], (97/5)*x[0] + (-13/5)*x[1]]) sage: psi = linear_transformation(D, C, f) sage: psi.matrix() [2 5] [3 7] sage: s, t = var('s t') sage: h(s, t) = [(-4/5)*s + (1/5)*t, (97/5)*s + (-13/5)*t] sage: zeta = linear_transformation(D, C, h) sage: zeta.matrix() [2 5] [3 7] Finally, we can give an explicit list of images for the basis elements of the domain. :: sage: x = polygen(QQ) sage: F.<a> = NumberField(x^3+x+1) sage: u = vector(F, [1, a, a^2]) sage: v = vector(F, [a, a^2, 2]) sage: w = u + v sage: D = F^3 sage: C = F^3 sage: rho = linear_transformation(D, C, [u, v, w]) sage: rho.matrix() [ 1 a a^2] [ a a^2 2] [ a + 1 a^2 + a a^2 + 2] sage: C = (F^3).subspace_with_basis([u, v]) sage: D = (F^3).subspace_with_basis([u, v]) sage: psi = linear_transformation(C, D, [u+v, u-v]) sage: psi.matrix() [ 1 1] [ 1 -1] TESTS: We test some bad inputs. First, the wrong things in the wrong places. :: sage: linear_transformation('junk') Traceback (most recent call last): ... TypeError: first argument must be a matrix or a vector space, not junk sage: linear_transformation(QQ^2, QQ^3, 'stuff') Traceback (most recent call last): ... TypeError: third argument must be a matrix, function, or list of images, not stuff sage: linear_transformation(QQ^2, 'garbage') Traceback (most recent call last): ... TypeError: if first argument is a vector space, then second argument must be a vector space, not garbage sage: linear_transformation(QQ^2, Integers(7)^2) Traceback (most recent call last): ... TypeError: vector spaces must have the same field of scalars, not Rational Field and Ring of integers modulo 7 Matrices must be over a field (or a ring that can be promoted to a field), and of the right size. :: sage: linear_transformation(matrix(Integers(6), [[2, 3],[4, 5]])) Traceback (most recent call last): ... TypeError: matrix must have entries from a field, or a ring with a fraction field, not Ring of integers modulo 6 sage: A = matrix(QQ, 3, 4, range(12)) sage: linear_transformation(QQ^4, QQ^4, A) Traceback (most recent call last): ... TypeError: domain dimension is incompatible with matrix size sage: linear_transformation(QQ^3, QQ^3, A, side='right') Traceback (most recent call last): ... TypeError: domain dimension is incompatible with matrix size sage: linear_transformation(QQ^3, QQ^3, A) Traceback (most recent call last): ... TypeError: codomain dimension is incompatible with matrix size sage: linear_transformation(QQ^4, QQ^4, A, side='right') Traceback (most recent call last): ... TypeError: codomain dimension is incompatible with matrix size Lists of images can be of the wrong number, or not really elements of the codomain. :: sage: linear_transformation(QQ^3, QQ^2, [vector(QQ, [1,2])]) Traceback (most recent call last): ... ValueError: number of images should equal the size of the domain's basis (=3), not 1 sage: C = (QQ^2).subspace_with_basis([vector(QQ, [1,1])]) sage: linear_transformation(QQ^1, C, [vector(QQ, [1,2])]) Traceback (most recent call last): ... ArithmeticError: some proposed image is not in the codomain, because element [1, 2] is not in free module Functions may not apply properly to domain elements, or return values outside the codomain. :: sage: f = lambda x: vector(QQ, [x[0], x[4]]) sage: linear_transformation(QQ^3, QQ^2, f) Traceback (most recent call last): ... ValueError: function cannot be applied properly to some basis element because vector index out of range sage: f = lambda x: vector(QQ, [x[0], x[1]]) sage: C = (QQ^2).span([vector(QQ, [1, 1])]) sage: linear_transformation(QQ^2, C, f) Traceback (most recent call last): ... ArithmeticError: some image of the function is not in the codomain, because element [1, 0] is not in free module A Sage symbolic function can come in a variety of forms that are not representative of a linear transformation. :: sage: x, y = var('x, y') sage: f(x, y) = [y, x, y] sage: linear_transformation(QQ^3, QQ^3, f) Traceback (most recent call last): ... ValueError: symbolic function has the wrong number of inputs for domain sage: linear_transformation(QQ^2, QQ^2, f) Traceback (most recent call last): ... ValueError: symbolic function has the wrong number of outputs for codomain sage: x, y = var('x y') sage: f(x, y) = [y, x*y] sage: linear_transformation(QQ^2, QQ^2, f) Traceback (most recent call last): ... ValueError: symbolic function must be linear in all the inputs: unable to convert y to a rational sage: x, y = var('x y') sage: f(x, y) = [x, 2*y] sage: C = (QQ^2).span([vector(QQ, [1, 1])]) sage: linear_transformation(QQ^2, C, f) Traceback (most recent call last): ... ArithmeticError: some image of the function is not in the codomain, because element [1, 0] is not in free module """ from sage.matrix.constructor import matrix from sage.modules.module import is_VectorSpace from sage.modules.free_module import VectorSpace from sage.categories.homset import Hom from sage.symbolic.ring import SR from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense from inspect import isfunction if not side in ['left', 'right']: raise ValueError( "side must be 'left' or 'right', not {0}".format(side)) if not (is_Matrix(arg0) or is_VectorSpace(arg0)): raise TypeError( 'first argument must be a matrix or a vector space, not {0}'. format(arg0)) if is_Matrix(arg0): R = arg0.base_ring() if not R.is_field(): try: R = R.fraction_field() except (NotImplementedError, TypeError): msg = 'matrix must have entries from a field, or a ring with a fraction field, not {0}' raise TypeError(msg.format(R)) if side == 'right': arg0 = arg0.transpose() side = 'left' arg2 = arg0 arg0 = VectorSpace(R, arg2.nrows()) arg1 = VectorSpace(R, arg2.ncols()) elif is_VectorSpace(arg0): if not is_VectorSpace(arg1): msg = 'if first argument is a vector space, then second argument must be a vector space, not {0}' raise TypeError(msg.format(arg1)) if arg0.base_ring() != arg1.base_ring(): msg = 'vector spaces must have the same field of scalars, not {0} and {1}' raise TypeError(msg.format(arg0.base_ring(), arg1.base_ring())) # Now arg0 = domain D, arg1 = codomain C, and # both are vector spaces with common field of scalars # use these to make a VectorSpaceHomSpace # arg2 might be a matrix that began in arg0 D = arg0 C = arg1 H = Hom(D, C, category=None) # Examine arg2 as the "rule" for the linear transformation # Pass on matrices, Python functions and lists to homspace call # Convert symbolic function here, to a matrix if is_Matrix(arg2): if side == 'right': arg2 = arg2.transpose() elif isinstance(arg2, (list, tuple)): pass elif isfunction(arg2): pass elif isinstance(arg2, Vector_callable_symbolic_dense): args = arg2.parent().base_ring()._arguments exprs = arg2.change_ring(SR) m = len(args) n = len(exprs) if m != D.degree(): raise ValueError( 'symbolic function has the wrong number of inputs for domain') if n != C.degree(): raise ValueError( 'symbolic function has the wrong number of outputs for codomain' ) arg2 = [[e.coefficient(a) for e in exprs] for a in args] try: arg2 = matrix(D.base_ring(), m, n, arg2) except TypeError as e: msg = 'symbolic function must be linear in all the inputs:\n' + e.args[ 0] raise ValueError(msg) # have matrix with respect to standard bases, now consider user bases images = [v * arg2 for v in D.basis()] try: arg2 = matrix([C.coordinates(C(a)) for a in images]) except (ArithmeticError, TypeError) as e: msg = 'some image of the function is not in the codomain, because\n' + e.args[ 0] raise ArithmeticError(msg) else: msg = 'third argument must be a matrix, function, or list of images, not {0}' raise TypeError(msg.format(arg2)) # arg2 now compatible with homspace H call method # __init__ will check matrix sizes versus domain/codomain dimensions return H(arg2)
def __init_extra__(self): """ Declare the canonical coercion from ``self.base_ring()`` to ``self``, if there has been none before. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: coercion_model = sage.structure.element.get_coercion_model() sage: coercion_model.discover_coercion(QQ, A) (Generic morphism: From: Rational Field To: An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field, None) sage: A(1) # indirect doctest B[word: ] """ # If self has an attribute _no_generic_basering_coercion # set to True, then this declaration is skipped. # This trick, introduced in #11900, is used in # sage.matrix.matrix_space.py and # sage.rings.polynomial.polynomial_ring. # It will hopefully be refactored into something more # conceptual later on. if getattr(self, '_no_generic_basering_coercion', False): return base_ring = self.base_ring() if base_ring is self: # There are rings that are their own base rings. No need to register that. return if self.is_coercion_cached(base_ring): # We will not use any generic stuff, since a (presumably) better conversion # has already been registered. return mor = None # This could be a morphism of Algebras(self.base_ring()); however, e.g., QQ is not in Algebras(QQ) H = Hom(base_ring, self, Rings()) # TODO: non associative ring! # Idea: There is a generic method "from_base_ring", that just does multiplication with # the multiplicative unit. However, the unit is constructed repeatedly, which is slow. # Hence, if the unit is available *now*, then we store it. # # However, if there is a specialised from_base_ring method, then it should be used! try: has_custom_conversion = self.category( ).parent_class.from_base_ring.__func__ is not self.from_base_ring.__func__ except AttributeError: # Sometimes from_base_ring is a lazy attribute has_custom_conversion = True if has_custom_conversion: mor = SetMorphism(function=self.from_base_ring, parent=H) try: self.register_coercion(mor) except AssertionError: pass return try: one = self.one() except (NotImplementedError, AttributeError, TypeError): # The unit is not available, yet. But there are cases # in which it will be available later. Hence: mor = SetMorphism(function=self.from_base_ring, parent=H) # try sanity of one._lmul_ if mor is None: try: if one._lmul_(base_ring.an_element()) is None: # There are cases in which lmul returns None, believe it or not. # One example: Hecke algebras. # In that case, the generic implementation of from_base_ring would # fail as well. Hence, unless it is overruled, we will not use it. #mor = SetMorphism(function = self.from_base_ring, parent = H) return except (NotImplementedError, AttributeError, TypeError): # it is possible that an_element or lmul are not implemented. return #mor = SetMorphism(function = self.from_base_ring, parent = H) mor = SetMorphism(function=one._lmul_, parent=H) try: self.register_coercion(mor) except AssertionError: pass
def subfield_from_elements(self, alpha, name=None, polred=True, threshold=None): r""" Return the subfield generated by the elements ``alpha``. INPUT: - ``alpha`` - list of elements in this number field - ``name`` - a name for the generator of the new number field - ``polred`` (boolean, default ``True``) - whether to optimize the generator of the newly created field - ``threshold`` (positive number, default ``None``) - threshold to be passed to the ``do_polred`` function EXAMPLES:: sage: from flatsurf.geometry.subfield import subfield_from_elements sage: x = polygen(QQ) sage: poly = x^4 - 4*x^2 + 1 sage: emb = AA.polynomial_root(poly, RIF(0.51, 0.52)) sage: K.<a> = NumberField(poly, embedding=emb) sage: sqrt2 = -a^3 + 3*a sage: sqrt3 = -a^2 + 2 sage: assert sqrt2 ** 2 == 2 and sqrt3 ** 2 == 3 sage: L, elts, phi = subfield_from_elements(K, [sqrt2, 1 - sqrt2/3]) sage: L Number Field in a0 with defining polynomial x^2 - 2 with a0 = 1.414213562373095? sage: elts [a0, -1/3*a0 + 1] sage: phi Ring morphism: From: Number Field in a0 with defining polynomial x^2 - 2 with a0 = 1.414213562373095? To: Number Field in a with defining polynomial x^4 - 4*x^2 + 1 with a = 0.5176380902050415? Defn: a0 |--> -a^3 + 3*a sage: assert phi(elts[0]) == sqrt2 sage: assert phi(elts[1]) == 1 - sqrt2/3 sage: L, elts, phi = subfield_from_elements(K, [1, sqrt3]) sage: assert phi(elts[0]) == 1 sage: assert phi(elts[1]) == sqrt3 TESTS:: sage: from flatsurf.geometry.subfield import subfield_from_elements sage: x = polygen(QQ) sage: p = x^8 - 12*x^6 + 23*x^4 - 12*x^2 + 1 sage: K.<a> = NumberField(p) sage: sqrt2 = 6/7*a^7 - 71/7*a^5 + 125/7*a^3 - 43/7*a sage: sqrt3 = 3/7*a^6 - 32/7*a^4 + 24/7*a^2 + 10/7 sage: sqrt5 = 8/7*a^6 - 90/7*a^4 + 120/7*a^2 - 27/7 sage: assert sqrt2**2 == 2 and sqrt3**2 == 3 and sqrt5**2 == 5 sage: L, elts, phi = subfield_from_elements(K, [sqrt2, sqrt3], name='b') sage: assert phi(elts[0]) == sqrt2 sage: assert phi(elts[1]) == sqrt3 sage: L, elts, phi = subfield_from_elements(K, [sqrt2, sqrt5], name='b') sage: assert phi(elts[0]) == sqrt2 sage: assert phi(elts[1]) == sqrt5 sage: L, elts, phi = subfield_from_elements(K, [sqrt3, sqrt5], name='b') sage: assert phi(elts[0]) == sqrt3 sage: assert phi(elts[1]) == sqrt5 sage: L, elts, phi = subfield_from_elements(K, [-149582/214245 + 21423/5581*sqrt2], name='b') sage: assert L.polynomial() == x^2 - 2 sage: L, elts, phi = subfield_from_elements(K, [131490/777 - 1359/22*sqrt3], name='b') sage: assert L.polynomial() == x^2 - 3 sage: L, elts, phi = subfield_from_elements(K, [12241829/177 - 321121/22459 * sqrt5], name='b') sage: assert L.polynomial() == x^2 - x - 1 sage: from sage.rings.qqbar import number_field_elements_from_algebraics sage: R.<x> = QQ[] sage: p1 = x^3 - x - 1 sage: roots1 = p1.roots(QQbar, False) sage: for _ in range(10): ....: p2 = R.random_element(degree=2) ....: while not p2.is_irreducible(): p2 = R.random_element(degree=2) ....: roots2 = p2.roots(QQbar, False) ....: K, (a1,b1,c1,a2,b2), phi = number_field_elements_from_algebraics(roots1 + roots2) ....: u1 = 1 - a1/17 + 3/7*a1**2 ....: u2 = 2 + 33/35 * a1 ....: L, (v1,v2), phi = subfield_from_elements(K, [u1, u2], threshold=100) ....: assert L.polynomial() == p1 ....: assert phi(v1) == u1 and phi(v2) == u2 """ V = VectorSpace(QQ, self.degree()) alpha = [self(a) for a in alpha] # Rational case if all(a.is_rational() for a in alpha): return (QQ, [QQ(a) for a in alpha], self.coerce_map_from(QQ)) # Saturate with multiplication vecs = [a.vector() for a in alpha] U = V.subspace(vecs) modified = True while modified: modified = False d = U.dimension() if d == self.degree(): return (self, alpha, Hom(self, self, Fields()).identity()) B = U.basis() for i in range(d): for j in range(i, d): v = (self(B[i]) * self(B[j])).vector() if v not in U: U += V.subspace([v]) modified = True # Strict subfield, find a generator vgen = None for b in U.basis(): if self(b).minpoly().degree() == d: vgen = b break if vgen is None: s = 1 while True: vgen = U.random_element(proba=0.5, x=-s, y=s) if self(vgen).minpoly().degree() == d: break s *= 2 # Minimize the generator via PARI polred gen = self(vgen) p = gen.minpoly() if polred: if threshold: fwd, back, q = do_polred(p, threshold) else: fwd, back, q = do_polred(p) else: q = p fwd = back = self.polynomial_ring().gen() new_gen = fwd(gen) assert new_gen.minpoly() == q K, hom = self.subfield(new_gen, name=name) # need to express the elements in the basis 1, a, a^2, ..., a^(d-1) M = matrix(QQ, [(new_gen**i).vector() for i in range(d)]) new_alpha = [K(M.solve_left(elt.vector())) for elt in alpha] return (K, new_alpha, hom)
def change_ring(self,R, check=True): r""" Returns a new :class:`SchemeMorphism_polynomial` which is ``self`` coerced to ``R``. If ``check`` is ``True``, then the initialization checks are performed. INPUT: - ``R`` -- ring - ``check`` -- Boolean OUTPUT: - A new :class: `SchemeMorphism_polynomial` which is ``self`` coerced to ``R``. EXAMPLES:: sage: P.<x,y>=ProjectiveSpace(ZZ,1) sage: H=Hom(P,P) sage: f=H([3*x^2,y^2]) sage: f.change_ring(GF(3)) Traceback (most recent call last): ... ValueError: polys (=[0, y^2]) must be of the same degree :: sage: P.<x,y,z>=ProjectiveSpace(QQ,2) sage: H=Hom(P,P) sage: f=H([5/2*x^3 + 3*x*y^2-y^3,3*z^3 + y*x^2, x^3-z^3]) sage: f.change_ring(GF(3)) Scheme endomorphism of Projective Space of dimension 2 over Finite Field of size 3 Defn: Defined on coordinates by sending (x : y : z) to (x^3 - y^3 : x^2*y : x^3 - z^3) :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: X=P.subscheme([5*x^2-y^2]) sage: H=Hom(X,X) sage: f=H([x,y]) sage: f.change_ring(GF(3)) Scheme endomorphism of Closed subscheme of Projective Space of dimension 1 over Finite Field of size 3 defined by: -x^2 - y^2 Defn: Defined on coordinates by sending (x : y) to (x : y) Check that :trac:'16834' is fixed:: sage: A.<x,y,z> = AffineSpace(RR,3) sage: h = Hom(A,A) sage: f = h([x^2+1.5,y^3,z^5-2.0]) sage: f.change_ring(CC) Scheme endomorphism of Affine Space of dimension 3 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x, y, z) to (x^2 + 1.50000000000000, y^3, z^5 - 2.00000000000000) :: sage: A.<x,y> = ProjectiveSpace(ZZ,1) sage: B.<u,v> = AffineSpace(QQ,2) sage: h = Hom(A,B) sage: f = h([x^2, y^2]) sage: f.change_ring(QQ) Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Affine Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^2, y^2) """ T=self.domain().change_ring(R) if self.is_endomorphism(): H=End(T) else: S=self.codomain().change_ring(R) H=Hom(T,S) G=[f.change_ring(R) for f in self._polys] return(H(G,check))
def reduce_base_field(self): """ Return this map defined over the field of definition of the coefficients. The base field of the map could be strictly larger than the field where all of the coefficients are defined. This function reduces the base field to the minimal possible. This can be done when the base ring is a number field, QQbar, a finite field, or algebraic closure of a finite field. OUTPUT: A scheme morphism. EXAMPLES:: sage: K.<t> = GF(5^4) sage: A.<x> = AffineSpace(K, 1) sage: A2.<a,b> = AffineSpace(K, 2) sage: H = End(A) sage: H2 = Hom(A,A2) sage: H3 = Hom(A2,A) sage: f = H([x^2 + 2*(t^3 + t^2 + t + 3)]) sage: f.reduce_base_field() Scheme endomorphism of Affine Space of dimension 1 over Finite Field in t2 of size 5^2 Defn: Defined on coordinates by sending (x) to (x^2 + (2*t2)) sage: f2 = H2([x^2 + 4, 2*x]) sage: f2.reduce_base_field() Scheme morphism: From: Affine Space of dimension 1 over Finite Field of size 5 To: Affine Space of dimension 2 over Finite Field of size 5 Defn: Defined on coordinates by sending (x) to (x^2 - 1, 2*x) sage: f3 = H3([a^2 + t*b]) sage: f3.reduce_base_field() Scheme morphism: From: Affine Space of dimension 2 over Finite Field in t of size 5^4 To: Affine Space of dimension 1 over Finite Field in t of size 5^4 Defn: Defined on coordinates by sending (a, b) to (a^2 + (t)*b) :: sage: K.<v> = CyclotomicField(4) sage: A.<x> = AffineSpace(K, 1) sage: H = End(A) sage: f = H([x^2 + v]) sage: g = f.reduce_base_field();g Scheme endomorphism of Affine Space of dimension 1 over Cyclotomic Field of order 4 and degree 2 Defn: Defined on coordinates by sending (x) to (x^2 + (v)) sage: g.base_ring() is K True :: sage: A.<x> = AffineSpace(QQbar, 1) sage: H = End(A) sage: f = H([(QQbar(sqrt(2))*x^2 + 1/QQbar(sqrt(3))) / (5*x)]) sage: f.reduce_base_field() Scheme endomorphism of Affine Space of dimension 1 over Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a = 1.931851652578137? Defn: Defined on coordinates by sending (x) to (((a^3 - 3*a)*x^2 + (1/3*a^2 - 2/3))/(5*x)) :: sage: R.<x> = PolynomialRing(QQ) sage: A.<x> =AffineSpace(QQbar,1) sage: H = End(A) sage: f = H([QQbar(3^(1/3))*x^2 + QQbar(sqrt(-2))]) sage: f.reduce_base_field() Scheme endomorphism of Affine Space of dimension 1 over Number Field in a with defining polynomial y^6 + 6*y^4 - 6*y^3 + 12*y^2 + 36*y + 17 with a = 1.442249570307409? + 1.414213562373095?*I Defn: Defined on coordinates by sending (x) to ((-48/269*a^5 + 27/269*a^4 - 320/269*a^3 + 468/269*a^2 - 772/269*a - 1092/269)*x^2 + (48/269*a^5 - 27/269*a^4 + 320/269*a^3 - 468/269*a^2 + 1041/269*a + 1092/269)) :: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3-x+1, embedding=(x^3+x+1).roots(ring=CC)[0][0]) sage: A.<x> = AffineSpace(K,1) sage: A2.<u,v> = AffineSpace(K,2) sage: H = Hom(A, A2) sage: f = H([x^2 + a*x + 3, 5*x]) sage: f.reduce_base_field() Scheme morphism: From: Affine Space of dimension 1 over Number Field in a with defining polynomial x^3 - x + 1 with a = -1.324717957244746? To: Affine Space of dimension 2 over Number Field in a with defining polynomial x^3 - x + 1 with a = -1.324717957244746? Defn: Defined on coordinates by sending (x) to (x^2 + (a)*x + 3, 5*x) :: sage: K.<v> = QuadraticField(2) sage: A.<x> =AffineSpace(K,1) sage: H = End(A) sage: f = H([3*x^2 + x + 1]) sage: f.reduce_base_field() Scheme endomorphism of Affine Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x) to (3*x^2 + x + 1) :: sage: K.<t> = GF(5^6) sage: A.<x> = AffineSpace(K, 1) sage: H = End(A) sage: f = H([x^2 + x*(t^3 + 2*t^2 + 4*t) + (t^5 + 3*t^4 + t^2 + 4*t)]) sage: f.reduce_base_field() Scheme endomorphism of Affine Space of dimension 1 over Finite Field in t of size 5^6 Defn: Defined on coordinates by sending (x) to (x^2 + (t^3 + 2*t^2 - t)*x + (t^5 - 2*t^4 + t^2 - t)) """ g = self.homogenize(0).reduce_base_field().dehomogenize(0) from sage.schemes.affine.affine_space import AffineSpace new_domain = AffineSpace(g.domain().base_ring(), self.domain().dimension_relative(), self.domain().variable_names()) new_codomain = AffineSpace(g.codomain().base_ring(), self.codomain().dimension_relative(), self.codomain().variable_names()) R = new_domain.coordinate_ring() H = Hom(new_domain, new_codomain) if isinstance(g[0], FractionFieldElement): return H([R(G.numerator()) / R(G.denominator()) for G in g]) return H([R(G) for G in g])
def free_module(self, base=None, basis=None, map=True): """ Return a free module `V` over a specified base ring together with maps to and from `V`. INPUT: - ``base`` -- a subring `R` so that this ring/field is isomorphic to a finite-rank free `R`-module `V` - ``basis`` -- a basis for this ring/field over the base - ``map`` -- boolean (default ``True``), whether to return `R`-linear maps to and from `V` OUTPUT: - A finite-rank free `R`-module `V` - An `R`-module isomorphism from `V` to this ring/field (only included if ``map`` is ``True``) - An `R`-module isomorphism from this ring/field to `V` (only included if ``map`` is ``True``) EXAMPLES:: sage: R.<x> = ZZ[] sage: K.<a> = Qq(125) sage: L.<pi> = K.extension(x^2-5) sage: V, from_V, to_V = K.free_module() sage: W, from_W, to_W = L.free_module() sage: W0, from_W0, to_W0 = L.free_module(base=Qp(5)) sage: to_V(a + O(5^7)) (O(5^7), 1 + O(5^7), O(5^7)) sage: to_W(a) (a + O(5^20), O(5^20)) sage: to_W0(a + O(5^7)) (O(5^7), 1 + O(5^7), O(5^7), O(5^7), O(5^7), O(5^7)) sage: to_W(pi) (O(5^21), 1 + O(5^20)) sage: to_W0(pi + O(pi^11)) (O(5^6), O(5^6), O(5^6), 1 + O(5^5), O(5^5), O(5^5)) sage: X, from_X, to_X = K.free_module(K) sage: to_X(a) (a + O(5^20)) """ if basis is not None: raise NotImplementedError B = self.base_ring() if base is None: base = B A = B.base_ring() d = self.relative_degree() if base is B: # May eventually want to take advantage of the fact that precision is flat V = B**d from_V = MapFreeModuleToOneStep to_V = MapOneStepToFreeModule elif base is A: d *= B.relative_degree() V = A**d from_V = MapFreeModuleToTwoStep to_V = MapTwoStepToFreeModule elif base is self: return super(pAdicExtensionGeneric, self).free_module(base=base, basis=basis, map=map) else: raise NotImplementedError FromV = Hom(V, self) ToV = Hom(self, V) from_V = FromV.__make_element_class__(from_V)(FromV) to_V = ToV.__make_element_class__(to_V)(ToV) return V, from_V, to_V
def __init__(self, matrices, f, g=None): r""" Create a chain homotopy between the given chain maps from a dictionary of matrices. EXAMPLES: If ``g`` is not specified, it is set equal to `f - (H \partial + \partial H)`. :: sage: from sage.homology.chain_homotopy import ChainHomotopy sage: C = ChainComplex({1: matrix(ZZ, 1, 2, (1,0)), 2: matrix(ZZ, 2, 1, (0, 2))}, degree_of_differential=-1) sage: D = ChainComplex({2: matrix(ZZ, 1, 1, (6,))}, degree_of_differential=-1) sage: f_d = {1: matrix(ZZ, 1, 2, (0,3)), 2: identity_matrix(ZZ, 1)} sage: f = Hom(C,D)(f_d) sage: H_d = {0: identity_matrix(ZZ, 1), 1: matrix(ZZ, 1, 2, (2,2))} sage: H = ChainHomotopy(H_d, f) sage: H._g.in_degree(0) [] sage: H._g.in_degree(1) [-13 -9] sage: H._g.in_degree(2) [-3] TESTS: Try to construct a chain homotopy in which the maps do not have matching domains and codomains:: sage: g = Hom(C,C)({}) # the zero chain map sage: H = ChainHomotopy(H_d, f, g) Traceback (most recent call last): ... ValueError: the chain maps are not compatible """ domain = f.domain() codomain = f.codomain() deg = domain.degree_of_differential() # Check that the chain complexes are compatible. This should # never arise, because first there should be errors in # constructing the chain maps. But just in case... if domain.degree_of_differential() != codomain.degree_of_differential( ): raise ValueError('the chain complexes are not compatible') if g is not None: # Check that the chain maps are compatible. if not (domain == g.domain() and codomain == g.codomain()): raise ValueError('the chain maps are not compatible') # Check that the data define a chain homotopy. for i in domain.differential(): if i in matrices and i + deg in matrices: if not (codomain.differential(i - deg) * matrices[i] + matrices[i + deg] * domain.differential(i) == f.in_degree(i) - g.in_degree(i)): raise ValueError( 'the data do not define a valid chain homotopy') elif i in matrices: if not (codomain.differential(i - deg) * matrices[i] == f.in_degree(i) - g.in_degree(i)): raise ValueError( 'the data do not define a valid chain homotopy') elif i + deg in matrices: if not (matrices[i + deg] * domain.differential(i) == f.in_degree(i) - g.in_degree(i)): raise ValueError( 'the data do not define a valid chain homotopy') else: # Define g. g_data = {} for i in domain.differential(): if i in matrices and i + deg in matrices: g_data[i] = f.in_degree( i) - matrices[i + deg] * domain.differential( i) - codomain.differential(i - deg) * matrices[i] elif i in matrices: g_data[i] = f.in_degree( i) - codomain.differential(i - deg) * matrices[i] elif i + deg in matrices: g_data[i] = f.in_degree( i) - matrices[i + deg] * domain.differential(i) g = ChainComplexMorphism(g_data, domain, codomain) self._matrix_dictionary = {} for i in matrices: m = matrices[i] # Use immutable matrices because they're hashable. m.set_immutable() self._matrix_dictionary[i] = m self._f = f self._g = g Morphism.__init__(self, Hom(domain, codomain))
def veronese_embedding(self, d, CS=None, order='lex'): r""" Return the degree ``d`` Veronese embedding of this projective subscheme. INPUT: - ``d`` -- a positive integer. - ``CS`` -- a projective ambient space to embed into. If the projective ambient space of this subscheme is of dimension `N`, the dimension of ``CS`` must be `\binom{N + d}{d} - 1`. This is constructed if not specified. Default: ``None``. - ``order`` -- a monomial order to use to arrange the monomials defining the embedding. The monomials will be arranged from greatest to least with respect to this order. Default: ``'lex'``. OUTPUT: - a scheme morphism from this subscheme to its image by the degree ``d`` Veronese embedding. EXAMPLES:: sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: L = P.subscheme([y - x]) sage: v = L.veronese_embedding(2) sage: v Scheme morphism: From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: -x + y To: Closed subscheme of Projective Space of dimension 5 over Rational Field defined by: -x4^2 + x3*x5, x2 - x4, x1 - x3, x0 - x3 Defn: Defined on coordinates by sending (x : y : z) to (x^2 : x*y : x*z : y^2 : y*z : z^2) sage: v.codomain().degree() 2 sage: C = P.subscheme([y*z - x^2]) sage: C.veronese_embedding(2).codomain().degree() 4 twisted cubic:: sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: Q.<u,v,s,t> = ProjectiveSpace(QQ, 3) sage: P.subscheme([]).veronese_embedding(3, Q) Scheme morphism: From: Closed subscheme of Projective Space of dimension 1 over Rational Field defined by: (no polynomials) To: Closed subscheme of Projective Space of dimension 3 over Rational Field defined by: -s^2 + v*t, -v*s + u*t, -v^2 + u*s Defn: Defined on coordinates by sending (x : y) to (x^3 : x^2*y : x*y^2 : y^3) """ # construct map between projective spaces v = self.ambient_space().veronese_embedding(d, CS, order) # return this map restricted to self and its image return Hom(self, v(self))(v.defining_polynomials())
def __init__(self, on_generators, domain=None, codomain=None, check=True): r""" Initialize ``self``. The keys of ``on_generators`` need to generate ``domain`` as a Lie algebra. .. TODO:: It might be possible to extract an explicit bracket relation that fails whenever some linear system fails to be solved. This would allow outputting an even more explicit error. TESTS: Test suite for a morphism:: sage: L.<X,Y,Z,W> = LieAlgebra(QQ, {('X','Y'): {'Z':1}, ('X','Z'): {'W':1}}) sage: K.<A,B,C> = LieAlgebra(QQ, {('A','B'): {'C':2}}) sage: phi = L.morphism({X+2*Y: A, X-Y: B}) sage: TestSuite(phi).run(skip=['_test_category']) Failure of inferring codomain:: sage: L.<X> = LieAlgebra(QQ, abelian=True) sage: L.morphism({X: int(1)}) Traceback (most recent call last): ... TypeError: codomain <type 'int'> is not a Lie algebra sage: from sage.algebras.lie_algebras.morphism import LieAlgebraMorphism_from_generators sage: LieAlgebraMorphism_from_generators({ZZ(1): X}) Traceback (most recent call last): ... TypeError: domain Integer Ring is not a Lie algebra sage: LieAlgebraMorphism_from_generators({}) Traceback (most recent call last): ... ValueError: no elements to infer domain from sage: LieAlgebraMorphism_from_generators({}, domain=L) Traceback (most recent call last): ... ValueError: no elements to infer codomain from """ from sage.categories.lie_algebras import LieAlgebras cm = get_coercion_model() if domain is None: if not on_generators: raise ValueError("no elements to infer domain from") domain = cm.common_parent(*on_generators) if domain not in LieAlgebras: raise TypeError("domain %s is not a Lie algebra" % domain) if codomain is None: if not on_generators: raise ValueError("no elements to infer codomain from") codomain = cm.common_parent(*list(on_generators.values())) if codomain not in LieAlgebras: raise TypeError("codomain %s is not a Lie algebra" % codomain) parent = Hom(domain, codomain) m = domain.module() cm = codomain.module() spanning_set = [X.to_vector() for X in on_generators] im_gens = [Y.to_vector() for Y in on_generators.values()] if not im_gens: LieAlgebraHomomorphism_im_gens.__init__(self, parent, [], check=check) return # helper function to solve linear systems Ax = b, where both x and b # are vectors of vectors def solve_linear_system(A, b, check): R = cm.base_ring() A_inv = A.solve_left(matrix.identity(A.ncols())) if check: # Verify validity of solution x = A_inv*b. Since b is a vector of # vectors, need to expand the matrix product by hand. M = A * A_inv for Mi, bk in zip(M.rows(), b): test_bk = sum((R(Mij) * bj for Mij,bj in zip(Mi,b)), cm.zero()) if test_bk != bk: raise ValueError("contradictory linear system") return [sum((R(Aij) * bk for Aij,bk in zip(Ai,b)), cm.zero()) for Ai in A_inv.rows()] bracketlength = 1 n = 0 while True: sm = m.submodule(spanning_set) A = matrix(sm.base_ring(), [sm.coordinate_vector(X) for X in spanning_set]) try: im_gens = solve_linear_system(A, im_gens, check) except ValueError: raise ValueError("this does not define a Lie algebra morphism; " "contradictory values for brackets of length %d" % bracketlength) spanning_set = list(sm.basis()) if n == len(spanning_set): # no increase in dimension => no further values will be computed break # compute brackets and repeat bracketlength += 1 n = len(spanning_set) for i,j in combinations(range(n), 2): # add the value of the bracket to known images Z = domain.bracket(spanning_set[i], spanning_set[j]) imZ = codomain.bracket(im_gens[i], im_gens[j]) spanning_set.append(Z.to_vector()) im_gens.append(imZ.to_vector()) # verify that sm is the full module m if not sm.has_coerce_map_from(m): raise ValueError("%s is not a generating set of %s" % (list(on_generators), domain)) A = matrix(m.base_ring(), spanning_set) im_gens = solve_linear_system(A, im_gens, check) LieAlgebraHomomorphism_im_gens.__init__(self, parent, im_gens, check=check)
def __init__(self, absolute_field, relative_field, embedding=None): r""" TESTS: If ``absolute_field`` is not a finite field, an error is raised:: sage: from sage.coding.relative_finite_field_extension import * sage: Fqm = RR sage: Fq.<a> = GF(4) sage: RelativeFiniteFieldExtension(Fqm, Fq) Traceback (most recent call last): ... ValueError: absolute_field has to be a finite field Same for ``relative_field``:: sage: from sage.coding.relative_finite_field_extension import * sage: Fqm.<aa> = GF(16) sage: Fq = RR sage: RelativeFiniteFieldExtension(Fqm, Fq) Traceback (most recent call last): ... ValueError: relative_field has to be a finite field If ``relative_field`` is not a subfield of ``absolute_field``, an exception is raised:: sage: from sage.coding.relative_finite_field_extension import * sage: Fqm.<aa> = GF(16) sage: Fq.<a> = GF(8) sage: RelativeFiniteFieldExtension(Fqm, Fq) Traceback (most recent call last): ... ValueError: relative_field has to be a subfield of absolute_field """ if not absolute_field.is_finite(): raise ValueError("absolute_field has to be a finite field") if not relative_field.is_finite(): raise ValueError("relative_field has to be a finite field") s = relative_field.degree() sm = absolute_field.degree() if not s.divides(sm): raise ValueError( "relative_field has to be a subfield of absolute_field") H = Hom(relative_field, absolute_field) if embedding is not None and embedding not in H: raise ValueError( "embedding has to be an embedding from relative_field to absolute_field" ) elif embedding is not None: self._phi = embedding else: self._phi = H[0] self._prime_field = relative_field.base_ring() self._relative_field = relative_field self._absolute_field = absolute_field alpha = relative_field.gen() beta = absolute_field.gen() self._alphas = [alpha**i for i in range(s)] self._betas = [beta**i for i in range(sm)] self._relative_field_degree = s self._absolute_field_degree = sm
def nth_iterate_map(self,n): r""" This function returns the nth iterate of ``self`` ALGORITHM: Uses a form of successive squaring to reducing computations. .. TODO:: This could be improved. INPUT: - ``n`` - a positive integer. OUTPUT: - A map between Affine spaces EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(2*y),y^2-3*x]) sage: f.nth_iterate_map(2) Scheme endomorphism of Affine Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x, y) to ((x^4 - 4*x^2 - 8*y^2 + 4)/(8*y^4 - 24*x*y^2), (2*y^5 - 12*x*y^3 + 18*x^2*y - 3*x^2 + 6)/(2*y)) :: sage: A.<x>=AffineSpace(QQ,1) sage: H=Hom(A,A) sage: f=H([(3*x^2-2)/(x)]) sage: f.nth_iterate_map(3) Scheme endomorphism of Affine Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x) to ((2187*x^8 - 6174*x^6 + 6300*x^4 - 2744*x^2 + 432)/(81*x^7 - 168*x^5 + 112*x^3 - 24*x)) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*x^2,3*y]) sage: f.nth_iterate_map(2) Scheme endomorphism of Closed subscheme of Affine Space of dimension 2 over Integer Ring defined by: -y^2 + x Defn: Defined on coordinates by sending (x, y) to (729*x^4, 9*y) """ if self.domain() != self.codomain(): raise TypeError("Domain and Codomain of function not equal") N=self.codomain().ambient_space().dimension_relative() F=list(self._polys) R=F[0].parent() Coord_ring=self.codomain().coordinate_ring() D=Integer(n).digits(2) if isinstance(Coord_ring, QuotientRing_generic): PHI=[Coord_ring.gen(i).lift() for i in range(N)] else: PHI=[Coord_ring.gen(i) for i in range(N)] for i in range(len(D)): T=[F[j] for j in range(N)] for k in range(D[i]): PHI=[PHI[j](T) for j in range(N)] if i!=len(D)-1: #avoid extra iterate F=[R(F[j](T)) for j in range(N)] #'square' H=Hom(self.domain(),self.codomain()) return(H(PHI))
def __init__(self, matrices, C, D, check=True): """ Create a morphism from a dictionary of matrices. EXAMPLES:: sage: S = simplicial_complexes.Sphere(1) sage: S Minimal triangulation of the 1-sphere sage: C = S.chain_complex() sage: C.differential() {0: [], 1: [-1 -1 0] [ 1 0 -1] [ 0 1 1], 2: []} sage: f = {0:zero_matrix(ZZ,3,3),1:zero_matrix(ZZ,3,3)} sage: G = Hom(C,C) sage: x = G(f) sage: x Chain complex endomorphism of Chain complex with at most 2 nonzero terms over Integer Ring sage: x._matrix_dictionary {0: [0 0 0] [0 0 0] [0 0 0], 1: [0 0 0] [0 0 0] [0 0 0]} Check that the bug in :trac:`13220` has been fixed:: sage: X = simplicial_complexes.Simplex(1) sage: Y = simplicial_complexes.Simplex(0) sage: g = Hom(X,Y)({0:0, 1:0}) sage: g.associated_chain_complex_morphism() Chain complex morphism: From: Chain complex with at most 2 nonzero terms over Integer Ring To: Chain complex with at most 1 nonzero terms over Integer Ring Check that an error is raised if the matrices are the wrong size:: sage: C = ChainComplex({0: zero_matrix(ZZ, 0, 1)}) sage: D = ChainComplex({0: zero_matrix(ZZ, 0, 2)}) sage: Hom(C,D)({0: matrix(1, 2, [1, 1])}) # 1x2 is the wrong size. Traceback (most recent call last): ... ValueError: matrix in degree 0 is not the right size sage: Hom(C,D)({0: matrix(2, 1, [1, 1])}) # 2x1 is right. Chain complex morphism: From: Chain complex with at most 1 nonzero terms over Integer Ring To: Chain complex with at most 1 nonzero terms over Integer Ring """ if not C.base_ring() == D.base_ring(): raise NotImplementedError( 'morphisms between chain complexes of different' ' base rings are not implemented') d = C.degree_of_differential() if d != D.degree_of_differential(): raise ValueError('degree of differential does not match') from sage.misc.misc import uniq degrees = uniq(list(C.differential()) + list(D.differential())) initial_matrices = dict(matrices) matrices = dict() for i in degrees: if i - d not in degrees: if not (C.free_module_rank(i) == D.free_module_rank(i) == 0): raise ValueError( '{} and {} are not rank 0 in degree {}'.format( C, D, i)) continue try: matrices[i] = initial_matrices.pop(i) except KeyError: matrices[i] = zero_matrix(C.base_ring(), D.differential(i).ncols(), C.differential(i).ncols(), sparse=True) if check: # All remaining matrices given must be 0x0. if not all(m.ncols() == m.nrows() == 0 for m in initial_matrices.values()): raise ValueError('the remaining matrices are not empty') # Check sizes of matrices. for i in matrices: if (matrices[i].nrows() != D.free_module_rank(i) or matrices[i].ncols() != C.free_module_rank(i)): raise ValueError( 'matrix in degree {} is not the right size'.format(i)) # Check commutativity. for i in degrees: if i - d not in degrees: if not (C.free_module_rank(i) == D.free_module_rank(i) == 0): raise ValueError( '{} and {} are not rank 0 in degree {}'.format( C, D, i)) continue if i + d not in degrees: if not (C.free_module_rank(i + d) == D.free_module_rank(i + d) == 0): raise ValueError( '{} and {} are not rank 0 in degree {}'.format( C, D, i + d)) continue Dm = D.differential(i) * matrices[i] mC = matrices[i + d] * C.differential(i) if mC != Dm: raise ValueError( 'matrices must define a chain complex morphism') self._matrix_dictionary = {} for i in matrices: m = matrices[i] # Use immutable matrices because they're hashable. m.set_immutable() self._matrix_dictionary[i] = m Morphism.__init__(self, Hom(C, D, ChainComplexes(C.base_ring())))
def __init__(self, map, base_ring=None, cohomology=False): """ INPUT: - ``map`` -- the map of simplicial complexes - ``base_ring`` -- a field (optional, default ``QQ``) - ``cohomology`` -- boolean (optional, default ``False``). If ``True``, return the induced map in cohomology rather than homology. EXAMPLES:: sage: from sage.homology.homology_morphism import InducedHomologyMorphism sage: K = simplicial_complexes.RandomComplex(8, 3) sage: H = Hom(K,K) sage: id = H.identity() sage: f = InducedHomologyMorphism(id, QQ) sage: f.to_matrix(0) == 1 and f.to_matrix(1) == 1 and f.to_matrix(2) == 1 True sage: f = InducedHomologyMorphism(id, ZZ) Traceback (most recent call last): ... ValueError: the coefficient ring must be a field sage: S1 = simplicial_complexes.Sphere(1).barycentric_subdivision() sage: S1.is_mutable() True sage: g = Hom(S1, S1).identity() sage: h = g.induced_homology_morphism(QQ) Traceback (most recent call last): ... ValueError: the domain and codomain complexes must be immutable sage: S1.set_immutable() sage: g = Hom(S1, S1).identity() sage: h = g.induced_homology_morphism(QQ) """ if map.domain().is_mutable() or map.codomain().is_mutable(): raise ValueError( 'the domain and codomain complexes must be immutable') if base_ring is None: base_ring = QQ if not base_ring.is_field(): raise ValueError('the coefficient ring must be a field') self._cohomology = cohomology self._map = map self._base_ring = base_ring if cohomology: domain = map.domain().cohomology_ring(base_ring=base_ring) codomain = map.codomain().cohomology_ring(base_ring=base_ring) Morphism.__init__( self, Hom(domain, codomain, category=GradedAlgebrasWithBasis(base_ring))) else: domain = map.domain().homology_with_basis(base_ring=base_ring, cohomology=cohomology) codomain = map.codomain().homology_with_basis( base_ring=base_ring, cohomology=cohomology) Morphism.__init__( self, Hom(domain, codomain, category=GradedModulesWithBasis(base_ring)))
def diffeomorphism(self, codomain, coord_functions=None, chart1=None, chart2=None, name=None, latex_name=None): r""" Define a diffeomorphism between the current manifold and another one. See :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for a complete documentation. INPUT: - ``codomain`` -- codomain of the diffeomorphism (the arrival manifold or some subset of it) - ``coord_functions`` -- (default: ``None``) if not ``None``, must be either - (i) a dictionary of the coordinate expressions (as lists (or tuples) of the coordinates of the image expressed in terms of the coordinates of the considered point) with the pairs of charts (chart1, chart2) as keys (chart1 being a chart on the current manifold and chart2 a chart on ``codomain``) - (ii) a single coordinate expression in a given pair of charts, the latter being provided by the arguments ``chart1`` and ``chart2`` In both cases, if the dimension of the arrival manifold is 1, a single coordinate expression can be passed instead of a tuple with a single element - ``chart1`` -- (default: ``None``; used only in case (ii) above) chart on the current manifold defining the start coordinates involved in ``coord_functions`` for case (ii); if none is provided, the coordinates are assumed to refer to the manifold's default chart - ``chart2`` -- (default: ``None``; used only in case (ii) above) chart on ``codomain`` defining the arrival coordinates involved in ``coord_functions`` for case (ii); if none is provided, the coordinates are assumed to refer to the default chart of ``codomain`` - ``name`` -- (default: ``None``) name given to the diffeomorphism - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the diffeomorphism; if none is provided, the LaTeX symbol is set to ``name`` OUTPUT: - the diffeomorphism, as an instance of :class:`~sage.manifolds.differentiable.diff_map.DiffMap` EXAMPLE: Diffeomorphism between the open unit disk in `\RR^2` and `\RR^2`:: sage: M = Manifold(2, 'M') # the open unit disk sage: forget() # for doctests only sage: c_xy.<x,y> = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord on M sage: c_xy.add_restrictions(x^2+y^2<1) sage: N = Manifold(2, 'N') # R^2 sage: c_XY.<X,Y> = N.chart() # canonical coordinates on R^2 sage: Phi = M.diffeomorphism(N, [x/sqrt(1-x^2-y^2), y/sqrt(1-x^2-y^2)], ....: name='Phi', latex_name=r'\Phi') sage: Phi Diffeomorphism Phi from the 2-dimensional differentiable manifold M to the 2-dimensional differentiable manifold N sage: Phi.display() Phi: M --> N (x, y) |--> (X, Y) = (x/sqrt(-x^2 - y^2 + 1), y/sqrt(-x^2 - y^2 + 1)) The inverse diffeomorphism:: sage: Phi^(-1) Diffeomorphism Phi^(-1) from the 2-dimensional differentiable manifold N to the 2-dimensional differentiable manifold M sage: (Phi^(-1)).display() Phi^(-1): N --> M (X, Y) |--> (x, y) = (X/sqrt(X^2 + Y^2 + 1), Y/sqrt(X^2 + Y^2 + 1)) See the documentation of class :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for more examples. """ homset = Hom(self, codomain) if coord_functions is None: coord_functions = {} if not isinstance(coord_functions, dict): # Turn coord_functions into a dictionary: if chart1 is None: chart1 = self._def_chart elif chart1 not in self._atlas: raise ValueError("{} is not a chart ".format(chart1) + "defined on the {}".format(self)) if chart2 is None: chart2 = codomain._def_chart elif chart2 not in codomain._atlas: raise ValueError("{} is not a chart ".format(chart2) + " defined on the {}".format(codomain)) coord_functions = {(chart1, chart2): coord_functions} return homset(coord_functions, name=name, latex_name=latex_name, is_isomorphism=True)
def _coerce_map_from_(self, R): """ Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are: - Anything with a coercion into ``self.base_ring()``. - Anything with a coercion into the base module of ``self``. - A tensor algebra whose base module has a coercion into the base module of ``self``. - A tensor module whose factors have a coercion into the base module of ``self``. TESTS:: sage: C = CombinatorialFreeModule(ZZ, Set([1,2])) sage: TAC = TensorAlgebra(C) sage: TAC.has_coerce_map_from(ZZ) True sage: TAC(1) == TAC.one() True sage: TAC.has_coerce_map_from(C) True sage: c = C.monomial(2) sage: TAC(c) B[2] sage: d = C.monomial(1) sage: TAC(c) * TAC(d) B[2] # B[1] sage: TAC(c-d) * TAC(c+d) -B[1] # B[1] - B[1] # B[2] + B[2] # B[1] + B[2] # B[2] sage: TCC = tensor((C,C)) sage: TAC.has_coerce_map_from(TCC) True sage: TAC(tensor([c, d])) B[2] # B[1] :: sage: D = CombinatorialFreeModule(ZZ, Set([2,4])) sage: TAD = TensorAlgebra(D) sage: f = C.module_morphism(on_basis=lambda x: D.monomial(2*x), codomain=D) sage: f.register_as_coercion() sage: TCD = tensor((C,D)) sage: TAD.has_coerce_map_from(TCC) True sage: TAD.has_coerce_map_from(TCD) True sage: TAC.has_coerce_map_from(TCD) False sage: TAD.has_coerce_map_from(TAC) True sage: TAD(3 * TAC([1, 2, 2, 1, 1])) 3*B[2] # B[4] # B[4] # B[2] # B[2] """ # Base ring coercions self_base_ring = self.base_ring() if self_base_ring == R: return BaseRingLift(Hom(self_base_ring, self)) if self_base_ring.has_coerce_map_from(R): return BaseRingLift(Hom(self_base_ring, self)) * self_base_ring.coerce_map_from(R) M = self._base_module # Base module coercions if R == M: return True if M.has_coerce_map_from(R): phi = M.coerce_map_from(R) return self.coerce_map_from(M) * phi # Tensor algebra coercions if isinstance(R, TensorAlgebra) and M.has_coerce_map_from(R._base_module): RM = R._base_module phi = M.coerce_map_from(RM) return R.module_morphism(lambda m: self._tensor_constructor_( [phi(RM.monomial(k)) for k in m.to_word_list()]), codomain=self) # Coercions from tensor products if (R in Modules(self_base_ring).WithBasis().TensorProducts() and isinstance(R, CombinatorialFreeModule_Tensor) and all(M.has_coerce_map_from(RM) for RM in R._sets)): modules = R._sets vector_map = [M.coerce_map_from(RM) for RM in R._sets] return R.module_morphism(lambda x: self._tensor_constructor_( [vector_map[i](M.monomial(x[i])) for i,M in enumerate(modules)]), codomain=self) return super(TensorAlgebra, self)._coerce_map_from_(R)
def _generic_convert_map(self, S): """ Return a generic map from a given homset to ``self``. INPUT: - ``S`` -- a homset OUTPUT: A map (by default: a Call morphism) from ``S`` to ``self``. EXAMPLES: By :trac:`14711`, conversion and coerce maps should be copied before using them outside of the coercion system:: sage: H = Hom(ZZ,QQ['t'], CommutativeAdditiveGroups()) sage: P.<t> = ZZ[] sage: f = P.hom([2*t]) sage: phi = H._generic_convert_map(f.parent()); phi Call morphism: From: Set of Homomorphisms from Univariate Polynomial Ring in t over Integer Ring to Univariate Polynomial Ring in t over Integer Ring To: Set of Morphisms from Integer Ring to Univariate Polynomial Ring in t over Rational Field in Category of commutative additive groups sage: H._generic_convert_map(f.parent())(f) Composite map: From: Integer Ring To: Univariate Polynomial Ring in t over Rational Field Defn: (map internal to coercion system -- copy before use) Polynomial base injection morphism: From: Integer Ring To: Univariate Polynomial Ring in t over Integer Ring then Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring Defn: t |--> 2*t then (map internal to coercion system -- copy before use) Ring morphism: From: Univariate Polynomial Ring in t over Integer Ring To: Univariate Polynomial Ring in t over Rational Field sage: copy(H._generic_convert_map(f.parent())(f)) Composite map: From: Integer Ring To: Univariate Polynomial Ring in t over Rational Field Defn: Polynomial base injection morphism: From: Integer Ring To: Univariate Polynomial Ring in t over Integer Ring then Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring Defn: t |--> 2*t then Ring morphism: From: Univariate Polynomial Ring in t over Integer Ring To: Univariate Polynomial Ring in t over Rational Field Defn: Induced from base ring by Natural morphism: From: Integer Ring To: Rational Field """ if self._element_constructor is None: from sage.categories.morphism import CallMorphism from sage.categories.homset import Hom return CallMorphism(Hom(S, self)) else: return Parent._generic_convert_map(self, S)
def __init__(self, dual_basis, scalar, scalar_name="", prefix=None): """ Generic dual base of a basis of symmetric functions. EXAMPLES:: sage: h = SFAElementary(QQ) sage: f = h.dual_basis(prefix = "m") sage: TestSuite(f).run() # long time (11s on sage.math, 2011) This class defines canonical coercions between self and self^*, as follow: Lookup for the canonical isomorphism from self to `P` (=powersum), and build the adjoint isomorphism from `P^*` to self^*. Since `P` is self-adjoint for this scalar product, derive an isomorphism from `P` to `self^*`, and by composition with the above get an isomorphism from self to `self^*` (and similarly for the isomorphism `self^*` to `self`). This should be striped down to just (auto?) defining canonical isomorphism by adjunction (as in MuPAD-Combinat), and let the coercion handle the rest. By transitivity, this defines indirect coercions to and from all other bases:: sage: s = SFASchur(QQ['t'].fraction_field()) sage: t = QQ['t'].fraction_field().gen() sage: zee_hl = lambda x: x.centralizer_size(t=t) sage: S = s.dual_basis(zee_hl) sage: S(s([2,1])) (-t/(t^5-2*t^4+t^3-t^2+2*t-1))*d_s[1, 1, 1] + ((-t^2-1)/(t^5-2*t^4+t^3-t^2+2*t-1))*d_s[2, 1] + (-t/(t^5-2*t^4+t^3-t^2+2*t-1))*d_s[3] """ self._dual_basis = dual_basis self._scalar = scalar self._scalar_name = scalar_name #Set up the cache self._to_self_cache = {} self._from_self_cache = {} self._transition_matrices = {} self._inverse_transition_matrices = {} # scalar_target = scalar(sage.combinat.partition.Partition_class( [1])).parent() scalar_target = (scalar_target(1) * dual_basis.base_ring()(1)).parent() self._p = sfa.SFAPower(scalar_target) if prefix is None: prefix = 'd_' + dual_basis.prefix() classical.SymmetricFunctionAlgebra_classical.__init__( self, scalar_target, "dual_" + dual_basis.basis_name(), prefix) # temporary until Hom(GradedHopfAlgebrasWithBasis work better) category = sage.categories.all.ModulesWithBasis(self.base_ring()) self.register_coercion( SetMorphism(Hom(self._dual_basis, self, category), self._dual_to_self)) self._dual_basis.register_coercion( SetMorphism(Hom(self, self._dual_basis, category), self._self_to_dual))
def __init_extra__(self): """ Declare the canonical coercion from ``self.base_ring()`` to ``self``, if there has been none before. EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: coercion_model = sage.structure.element.get_coercion_model() sage: coercion_model.discover_coercion(QQ, A) (Generic morphism: From: Rational Field To: An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field, None) sage: A(1) # indirect doctest B[word: ] """ # If self has an attribute _no_generic_basering_coercion # set to True, then this declaration is skipped. # This trick, introduced in #11900, is used in # sage.matrix.matrix_space.py and # sage.rings.polynomial.polynomial_ring. # It will hopefully be refactored into something more # conceptual later on. if getattr(self, '_no_generic_basering_coercion', False): return base_ring = self.base_ring() if base_ring is self: # There are rings that are their own base rings. No need to register that. return if self._is_coercion_cached(base_ring): # We will not use any generic stuff, since a (presumably) better conversion # has already been registered. return # This could be a morphism of Algebras(self.base_ring()); however, e.g., QQ is not in Algebras(QQ) H = Hom(base_ring, self, Rings()) # TODO: non associative ring! # We need to register a coercion from the base ring to self. # # There is a generic method from_base_ring(), that just does # multiplication with the multiplicative unit. However, the # unit is constructed repeatedly, which is slow. # So, if the unit is available *now*, then we can create a # faster coercion map. # # This only applies for the generic from_base_ring() method. # If there is a specialised from_base_ring(), then it should # be used unconditionally. generic_from_base_ring = self.category( ).parent_class.from_base_ring if type(self).from_base_ring != generic_from_base_ring: # Custom from_base_ring() use_from_base_ring = True if isinstance(generic_from_base_ring, lazy_attribute): # If the category implements from_base_ring() as lazy # attribute, then we always use it. # This is for backwards compatibility, see Trac #25181 use_from_base_ring = True else: try: one = self.one() use_from_base_ring = False except (NotImplementedError, AttributeError, TypeError): # The unit is not available, yet. But there are cases # in which it will be available later. So, we use # the generic from_base_ring() after all. use_from_base_ring = True mor = None if use_from_base_ring: mor = SetMorphism(function=self.from_base_ring, parent=H) else: # We have the multiplicative unit, so implement the # coercion from the base ring as multiplying with that. # # But first we check that it actually works. If not, # then the generic implementation of from_base_ring() # would fail as well so we don't use it. try: if one._lmul_(base_ring.an_element()) is not None: # There are cases in which lmul returns None, # which means that it's not implemented. # One example: Hecke algebras. mor = SetMorphism(function=one._lmul_, parent=H) except (NotImplementedError, AttributeError, TypeError): pass if mor is not None: try: self.register_coercion(mor) except AssertionError: pass
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree being determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] A finite subgroup of GL(12,Z) as a permutation group:: sage: imf=libgap.function_factory('ImfMatrixGroup') sage: GG = imf( 12, 3 ) sage: G = MatrixGroup(GG.GeneratorsOfGroup()) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. TESTS:: sage: A= matrix(QQ, 2, [0, 1, 1, 0]) sage: B= matrix(QQ, 2, [1, 0, 0, 1]) sage: a, b= MatrixGroup([A, B]).as_permutation_group().gens() sage: a.order(), b.order() (2, 1) The above example in GL(12,Z), reduced modulo 7:: sage: MS = MatrixSpace( GF(7), 12, 12) sage: G = MatrixGroup(map(MS, GG.GeneratorsOfGroup())) sage: G.cardinality() 21499084800 sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 Check that large degree is still working:: sage: Sp(6,3).as_permutation_group().cardinality() 9170703360 Check that ``_permutation_group_morphism`` works (:trac:`25706`):: sage: MG = GU(3,2).as_matrix_group() sage: PG = MG.as_permutation_group() # this constructs the morphism sage: mg = MG.an_element() sage: MG._permutation_group_morphism(mg) (1,2,6,19,35,33)(3,9,26,14,31,23)(4,13,5)(7,22,17)(8,24,12)(10,16,32,27,20,28)(11,30,18)(15,25,36,34,29,21) """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError("Group must be finite.") iso = self._libgap_().IsomorphismPermGroup() if algorithm == "smaller": iso = iso.Image().SmallerDegreePermutationRepresentation() PG = PermutationGroup(iso.Image().GeneratorsOfGroup().sage(), \ canonicalize=False) # applying gap() - as PermutationGroup is not libGAP def permutation_group_map(element): return PG(iso.ImageElm(element.gap()).sage()) from sage.categories.homset import Hom self._permutation_group_morphism = Hom(self, PG)(permutation_group_map) return PG
def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=None): r""" Generic dual basis of a basis of symmetric functions. INPUT: - ``dual_basis`` -- a basis of the ring of symmetric functions - ``scalar`` -- A function `z` on partitions which determines the scalar product on the power sum basis by `\langle p_{\mu}, p_{\mu} \rangle = z(\mu)`. (Independently on the function chosen, the power sum basis will always be orthogonal; the function ``scalar`` only determines the norms of the basis elements.) This defaults to the function ``zee`` defined in ``sage.combinat.sf.sfa``, that is, the function is defined by: .. MATH:: \lambda \mapsto \prod_{i = 1}^\infty m_i(\lambda)! i^{m_i(\lambda)}`, where `m_i(\lambda)` means the number of times `i` appears in `\lambda`. This default function gives the standard Hall scalar product on the ring of symmetric functions. - ``scalar_name`` -- (default: the empty string) a string giving a description of the scalar product specified by the parameter ``scalar`` - ``basis_name`` -- (optional) a string to serve as name for the basis to be generated (such as "forgotten" in "the forgotten basis"); don't set it to any of the already existing basis names (such as ``homogeneous``, ``monomial``, ``forgotten``, etc.). - ``prefix`` -- (default: ``'d'`` and the prefix for ``dual_basis``) a string to use as the symbol for the basis OUTPUT: The basis of the ring of symmetric functions dual to the basis ``dual_basis`` with respect to the scalar product determined by ``scalar``. EXAMPLES:: sage: e = SymmetricFunctions(QQ).e() sage: f = e.dual_basis(prefix = "m", basis_name="Forgotten symmetric functions"); f Symmetric Functions over Rational Field in the Forgotten symmetric functions basis sage: TestSuite(f).run(elements = [f[1,1]+2*f[2], f[1]+3*f[1,1]]) sage: TestSuite(f).run() # long time (11s on sage.math, 2011) This class defines canonical coercions between ``self`` and ``self^*``, as follow: Lookup for the canonical isomorphism from ``self`` to `P` (=powersum), and build the adjoint isomorphism from `P^*` to ``self^*``. Since `P` is self-adjoint for this scalar product, derive an isomorphism from `P` to ``self^*``, and by composition with the above get an isomorphism from ``self`` to ``self^*`` (and similarly for the isomorphism ``self^*`` to ``self``). This should be striped down to just (auto?) defining canonical isomorphism by adjunction (as in MuPAD-Combinat), and let the coercion handle the rest. Inversions may not be possible if the base ring is not a field:: sage: m = SymmetricFunctions(ZZ).m() sage: h = m.dual_basis(lambda x: 1) sage: h[2,1] Traceback (most recent call last): ... TypeError: no conversion of this rational to integer By transitivity, this defines indirect coercions to and from all other bases:: sage: s = SymmetricFunctions(QQ['t'].fraction_field()).s() sage: t = QQ['t'].fraction_field().gen() sage: zee_hl = lambda x: x.centralizer_size(t=t) sage: S = s.dual_basis(zee_hl) sage: S(s([2,1])) (-t/(t^5-2*t^4+t^3-t^2+2*t-1))*d_s[1, 1, 1] + ((-t^2-1)/(t^5-2*t^4+t^3-t^2+2*t-1))*d_s[2, 1] + (-t/(t^5-2*t^4+t^3-t^2+2*t-1))*d_s[3] TESTS: Regression test for :trac:`12489`. This ticket improving equality test revealed that the conversion back from the dual basis did not strip cancelled terms from the dictionary:: sage: y = e[1, 1, 1, 1] - 2*e[2, 1, 1] + e[2, 2] sage: sorted(f.element_class(f, dual = y)) [([1, 1, 1, 1], 6), ([2, 1, 1], 2), ([2, 2], 1)] """ self._dual_basis = dual_basis self._scalar = scalar self._scalar_name = scalar_name # Set up the cache # cache for the coordinates of the elements # of ``dual_basis`` with respect to ``self`` self._to_self_cache = {} # cache for the coordinates of the elements # of ``self`` with respect to ``dual_basis`` self._from_self_cache = {} # cache for transition matrices which contain the coordinates of # the elements of ``dual_basis`` with respect to ``self`` self._transition_matrices = {} # cache for transition matrices which contain the coordinates of # the elements of ``self`` with respect to ``dual_basis`` self._inverse_transition_matrices = {} scalar_target = scalar(sage.combinat.partition.Partition([1])).parent() scalar_target = (scalar_target.one() * dual_basis.base_ring().one()).parent() self._sym = sage.combinat.sf.sf.SymmetricFunctions(scalar_target) self._p = self._sym.power() if prefix is None: prefix = 'd_' + dual_basis.prefix() classical.SymmetricFunctionAlgebra_classical.__init__( self, self._sym, basis_name=basis_name, prefix=prefix) # temporary until Hom(GradedHopfAlgebrasWithBasis work better) category = sage.categories.all.ModulesWithBasis(self.base_ring()) self.register_coercion( SetMorphism(Hom(self._dual_basis, self, category), self._dual_to_self)) self._dual_basis.register_coercion( SetMorphism(Hom(self, self._dual_basis, category), self._self_to_dual))
def __init__(self, Sym, base, scalar, prefix, basis_name, leading_coeff=None): r""" Initialization of the symmetric function algebra defined via orthotriangular rules. INPUT: - ``self`` -- a basis determined by an orthotriangular definition - ``Sym`` -- ring of symmetric functions - ``base`` -- an instance of a basis of the ring of symmetric functions (e.g. the Schur functions) - ``scalar`` -- a function ``zee`` on partitions. The function ``zee`` determines the scalar product on the power sum basis with normalization `\langle p_{\mu}, p_{\mu} \rangle = \mathrm{zee}(\mu)`. - ``prefix`` -- the prefix used to display the basis - ``basis_name`` -- the name used for the basis .. NOTE:: The base ring is required to be a `\QQ`-algebra for this method to be useable, since the scalar product is defined by its values on the power sum basis. EXAMPLES:: sage: from sage.combinat.sf.sfa import zee sage: from sage.combinat.sf.orthotriang import SymmetricFunctionAlgebra_orthotriang sage: Sym = SymmetricFunctions(QQ) sage: m = Sym.m() sage: s = SymmetricFunctionAlgebra_orthotriang(Sym, m, zee, 's', 'Schur'); s Symmetric Functions over Rational Field in the Schur basis TESTS:: sage: TestSuite(s).run(elements = [s[1,1]+2*s[2], s[1]+3*s[1,1]]) sage: TestSuite(s).run(skip = ["_test_associativity", "_test_prod"]) # long time (7s on sage.math, 2011) Note: ``s.an_element()`` is of degree 4; so we skip ``_test_associativity`` and ``_test_prod`` which involve (currently?) expensive calculations up to degree 12. """ self._sym = Sym self._sf_base = base self._scalar = scalar self._leading_coeff = leading_coeff sfa.SymmetricFunctionAlgebra_generic.__init__(self, Sym, prefix=prefix, basis_name=basis_name) self._self_to_base_cache = {} self._base_to_self_cache = {} self.register_coercion(SetMorphism(Hom(base, self), self._base_to_self)) base.register_coercion(SetMorphism(Hom(self, base), self._self_to_base))
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree being determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] sage: MS = MatrixSpace( GF(7), 12, 12) sage: GG = gap("ImfMatrixGroup( 12, 3 )") sage: GG.GeneratorsOfGroup().Length() 3 sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n",""))) sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n",""))) sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n",""))) sage: G = MatrixGroup([g1, g2, g3]) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. TESTS:: sage: A= matrix(QQ, 2, [0, 1, 1, 0]) sage: B= matrix(QQ, 2, [1, 0, 0, 1]) sage: a, b= MatrixGroup([A, B]).as_permutation_group().gens() sage: a.order(), b.order() (2, 1) Check that ``_permutation_group_morphism`` works (:trac:`25706`):: sage: MG = GU(3,2).as_matrix_group() sage: PG = MG.as_permutation_group() # this constructs the morphism sage: mg = MG.an_element() sage: MG._permutation_group_morphism(mg) (1,2,6,19,35,33)(3,9,26,14,31,23)(4,13,5)(7,22,17)(8,24,12)(10,16,32,27,20,28)(11,30,18)(15,25,36,34,29,21) """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError("Group must be finite.") n = self.degree() MS = MatrixSpace(self.base_ring(), n, n) mats = [] # initializing list of mats by which the gens act on self for g in self.gens(): p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("iso:=IsomorphismPermGroup(Group("+mats_str+"))") gap_permutation_map = gap("iso;") if algorithm == "smaller": gap.eval("small:= SmallerDegreePermutationRepresentation( Image( iso ) );") C = gap("Image( small )") else: C = gap("Image( iso )") PG = PermutationGroup(gap_group=C, canonicalize=False) def permutation_group_map(element): return PG(gap_permutation_map.ImageElm(element.gap())) from sage.categories.homset import Hom self._permutation_group_morphism = Hom(self, PG)(permutation_group_map) return PG
def homogenize(self, n, newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()`` INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- the n-th projective embedding into projective space OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) """ A = self.domain() B = self.codomain() N = A.ambient_space().dimension_relative() NB = B.ambient_space().dimension_relative() Vars = list(A.ambient_space().variable_names()) + [newvar] S = PolynomialRing(A.base_ring(), Vars) try: l = lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l = prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring() == RealField() or self.domain().base_ring( ) == ComplexField(): F = [ S(((self[i] * l).numerator())._maxima_().divide( self[i].denominator())[0].sage()) for i in range(N) ] elif isinstance(self.domain().base_ring(), (PolynomialRing_general, MPolynomialRing_generic)): F = [ S(((self[i] * l).numerator())._maxima_().divide( self[i].denominator())[0].sage()) for i in range(N) ] else: F = [S(self[i] * l) for i in range(N)] F.insert(n, S(l)) d = max([F[i].degree() for i in range(N + 1)]) F = [ F[i].homogenize(newvar) * S.gen(N)**(d - F[i].degree()) for i in range(N + 1) ] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A) == True: from sage.schemes.projective.projective_space import ProjectiveSpace X = ProjectiveSpace(A.base_ring(), NB, Vars) else: X = A.projective_embedding(n).codomain() phi = S.hom(X.ambient_space().gens(), X.ambient_space().coordinate_ring()) F = [phi(f) for f in F] H = Hom(X, X) return (H(F))