Example #1
0
    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))
Example #2
0
 def __init__(self, source, target, precision):
     Morphism.__init__(self, Hom(source, target, Rings()))
     self.SPN = target
     self.target_precision = precision
Example #3
0
    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))
Example #4
0
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)
Example #5
0
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
Example #6
0
    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
Example #7
0
    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)
Example #8
0
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)
Example #9
0
        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
Example #10
0
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)
Example #11
0
    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))
Example #12
0
    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
Example #14
0
    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())
Example #16
0
    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
Example #18
0
    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))
Example #19
0
    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())))
Example #20
0
    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)))
Example #21
0
    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)
Example #22
0
    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)
Example #23
0
    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)
Example #24
0
    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))
Example #25
0
        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
Example #26
0
    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
Example #27
0
    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))
Example #28
0
    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))
Example #29
0
    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
Example #30
0
    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))