コード例 #1
0
ファイル: kplt.py プロジェクト: joelypoley/kplt
def connecting_ideal(O_1, O_2):
    """Returns an O_1, O_2-connecting ideal.

    Args:
        O_1: A maximal order in a rational quaternion algebra.
        O_2: A maximal order in the same quaternion algebra.

    Returns:
        An ideal I that is a left O_1 ideal and a right O_2 ideal. Moreover I
        is a subset of O_1.
    """
    # There exists some integer d such that d*O_2 is a subset of O_1. We
    # first compute d.
    mat_1 = matrix([x.coefficient_tuple() for x in O_1.basis()])
    mat_2 = matrix([x.coefficient_tuple() for x in O_2.basis()])
    matcoeff = mat_2 * ~mat_1
    d = lcm(x.denominator() for x in matcoeff.coefficients())

    # J is a subset of O_1 and is a O_1, O_2-ideal by construction.
    J = left_ideal([d * x * y for x in O_1.basis() for y in O_2.basis()], O_1)

    assert J.left_order() == O_1
    assert J.right_order() == O_2
    assert all(x in O_1 for x in J.basis())

    return J
コード例 #2
0
 def __div__(self, other):
     r"""
     Divide modular forms, rescaling if necessary.
     """
     if isinstance(other, OrthogonalModularForm):
         if not self.gram_matrix() == other.gram_matrix():
             raise ValueError('Incompatible Gram matrices')
         self_scale = self.scale()
         other_scale = other.scale()
         if self_scale != 1 or other_scale != 1:
             new_scale = lcm(self.scale(), other.scale())
             X1 = self.rescale(new_scale // self_scale)
             X2 = other.rescale(new_scale // other_scale)
             return OrthogonalModularForm(
                 self.__weight - other.weight(),
                 self.__weilrep,
                 X1.true_fourier_expansion() * X2.inverse(),
                 scale=new_scale,
                 weylvec=self.weyl_vector() - other.weyl_vector(),
                 qexp_representation=self.qexp_representation())
         return OrthogonalModularForm(
             self.weight() - other.weight(),
             self.__weilrep,
             self.true_fourier_expansion() * other.inverse(),
             scale=1,
             weylvec=self.weyl_vector() - other.weyl_vector(),
             qexp_representation=self.qexp_representation())
     else:
         return OrthogonalModularForm(
             self.weight(),
             self.__weilrep,
             self.true_fourier_expansion() / other,
             scale=self.scale(),
             weylvec=self.weyl_vector(),
             qexp_representation=self.qexp_representation())
コード例 #3
0
 def __sub__(self, other):
     r"""
     Subtract modular forms, rescaling if necessary.
     """
     if not other:
         return self
     if not self.gram_matrix() == other.gram_matrix():
         raise ValueError('Incompatible Gram matrices')
     if not self.weight() == other.weight():
         raise ValueError('Incompatible weights')
     self_v = self.weyl_vector()
     other_v = other.weyl_vector()
     if self_v or other_v:
         if not denominator(self_v - other_v) == 1:
             raise ValueError('Incompatible characters')
     self_scale = self.scale()
     other_scale = other.scale()
     if not self_scale == other_scale:
         new_scale = lcm(self_scale, other_scale)
         X1 = self.rescale(new_scale // self_scale)
         X2 = other.rescale(new_scale // other_scale)
         return OrthogonalModularForm(
             self.__weight,
             self.__weilrep,
             X1.true_fourier_expansion() - X2.true_fourier_expansion(),
             scale=new_scale,
             weylvec=self_v,
             qexp_representation=self.qexp_representation())
     return OrthogonalModularForm(
         self.__weight,
         self.__weilrep,
         self.true_fourier_expansion() - other.true_fourier_expansion(),
         scale=self_scale,
         weylvec=self_v,
         qexp_representation=self.qexp_representation())
コード例 #4
0
 def __eq__(self, other):
     self_scale = self.scale()
     other_scale = other.scale()
     if self_scale == other_scale:
         return self.true_fourier_expansion(
         ) == other.true_fourier_expansion()
     else:
         new_scale = lcm(self_scale, other_scale)
         X1 = self.rescale(new_scale // self_scale)
         X2 = other.rescale(new_scale // other_scale)
         return X1.true_fourier_expansion() == X2.true_fourier_expansion()
コード例 #5
0
ファイル: hgm.py プロジェクト: edgarcosta/amortizedHGM
    def _numden(self, t, start=0, shift=0, offset=0, verbose=False):
        r"""
        Return f and g, such that
            P_{m+1} = f(k) / g(k) P_m
        where
            P_m = t^m \prod_i (alpha)_m / (beta)_m,
            shift mod p = floor(start*(p-1)) + offset,
            m = floor(start*(p-1)) + k,
            and
            1 <= k < floor(end*(p-1)) - floor(start*(p-1)).


        EXAMPLES::

            sage: H = AmortizingHypergeometricData(100, alpha_beta=([1/6,5/6],[0,0]))
            sage: start, end, p = 0, 1/6, 97
            sage: shift, offset = H._starts_to_rationals[start][p % start.denominator()]
            sage: f, g = H._numden(t=1, shift=shift, offset=offset, start=start)
            sage: f
            36*k^2 + 36*k + 5
            sage: g
            36*k^2 + 72*k + 36

        TESTS::

            sage: for cyca, cycb, start, end, p, t in [
            ....:     ([6], [1, 1], 0, 1/6, 97, 1),
            ....:     ([4, 2, 2], [3, 1, 1], 1/3, 1/2, 97, 1),
            ....:     ([22], [1, 1, 20], 3/20, 5/22, 1087, 1),
            ....:     ([22], [1, 1, 20], 3/20, 5/22, 1087, 1337/507734),
            ....:     ([22], [1, 1, 20], 3/20, 5/22, 1019, 1337/507734)]:
            ....:     H = AmortizingHypergeometricData(p+40, cyclotomic=(cyca, cycb))
            ....:     shift, offset = H._starts_to_rationals[start][p % start.denominator()]
            ....:     f, g = H._numden(t=t, shift=shift, offset=offset, start=start)
            ....:     for k in range(1, floor(end * (p-1)) - floor(start * (p-1))):
            ....:         m = floor(start * (p-1)) + k
            ....:         quoval = GF(p)(t) * H.pochhammer_quotient(p, m+1)/H.pochhammer_quotient(p, m)
            ....:         if GF(p)(f(k)/g(k)) != quoval:
            ....:             print((cyca, cycb), offset, (start, end), (floor(start * (p-1)), floor(end * (p-1))), m, p, t, GF(p)(f(k)/g(k)), quoval)

        """
        RQ = QQ['k']
        RZ = ZZ['k']
        k = RQ.gen() - offset
        # We shift the term corresponding to a or b by 1 because we're taking
        # the fractional part of a negative number when less than start.
        f = prod(a + shift + k + (1 if a <= start else 0)
                 for a in self.alpha()) * t.numerator()
        g = prod(b + shift + k + (1 if b <= start else 0)
                 for b in self.beta()) * t.denominator()
        d = lcm(f.denominator(), g.denominator())

        return RZ(d * f), RZ(d * g)
コード例 #6
0
def perm_order(p, n=None):
    r"""
    Return the multiplicative order of the permutation ``p``.

    EXAMPLES::

        sage: from surface_dynamics.misc.permutation import perm_init, perm_order
        sage: p = perm_init('(1,3)(2,4,6)(5)')
        sage: perm_order(p)
        6
    """
    return lcm(perm_cycle_type(p))
コード例 #7
0
 def __mul__(self, other):
     r"""
     Multiply modular forms, rescaling if necessary.
     """
     if isinstance(other, OrthogonalModularForm):
         if not self.gram_matrix() == other.gram_matrix():
             raise ValueError('Incompatible Gram matrices')
         self_scale = self.scale()
         other_scale = other.scale()
         if self_scale != 1 or other_scale != 1:
             new_scale = lcm(self.scale(), other.scale())
             X1 = self.rescale(new_scale // self_scale)
             X2 = other.rescale(new_scale // other_scale)
         else:
             new_scale = 1
             X1 = self
             X2 = other
         f1 = X1.true_fourier_expansion()
         f2 = X2.true_fourier_expansion()
         f = f1 * f2
         if f1.valuation() < 0 or f2.valuation() < 0 and f.valuation >= 0:
             r = PowerSeriesRing(f.base_ring(), 't')
             f = r(f)
         return OrthogonalModularForm(
             self.__weight + other.weight(),
             self.__weilrep,
             f,
             scale=new_scale,
             weylvec=self.weyl_vector() + other.weyl_vector(),
             qexp_representation=self.qexp_representation())
     else:
         return OrthogonalModularForm(
             self.weight(),
             self.__weilrep,
             self.true_fourier_expansion() * other,
             scale=self.scale(),
             weylvec=self.weyl_vector(),
             qexp_representation=self.qexp_representation())
コード例 #8
0
def is_Q_curve(E, maxp=100, certificate=False, verbose=False):
    r"""
    Return whether ``E`` is a `\QQ`-curve, with optional certificate.

    INPUT:

    - ``E`` (elliptic curve) -- an elliptic curve over a number field.

    - ``maxp`` (int, default 100): bound on primes used for checking
      necessary local conditions.  The result will not depend on this,
      but using a larger value may return ``False`` faster.

    - ``certificate`` (bool, default ``False``): if ``True`` then a
      second value is returned giving a certificate for the
      `\QQ`-curve property.

    OUTPUT:

    If ``certificate`` is ``False``: either ``True`` (if `E` is a
    `\QQ`-curve), or ``False``.

    If ``certificate`` is ``True``: a tuple consisting of a boolean
    flag as before and a certificate, defined as follows:

    - when the flag is ``True``, so `E` is a `\QQ`-curve:

        - either {'CM':`D`} where `D` is a negative discriminant, when
          `E` has potential CM with discriminant `D`;

        - otherwise {'CM': `0`, 'core_poly': `f`, 'rho': `\rho`, 'r':
          `r`, 'N': `N`}, when `E` is a non-CM `\QQ`-curve, where the
          core polynomial `f` is an irreducible monic polynomial over
          `QQ` of degree `2^\rho`, all of whose roots are
          `j`-invariants of curves isogenous to `E`, the core level
          `N` is a square-free integer with `r` prime factors which is
          the LCM of the degrees of the isogenies between these
          conjugates.  For example, if there exists a curve `E'`
          isogenous to `E` with `j(E')=j\in\QQ`, then the certificate
          is {'CM':0, 'r':0, 'rho':0, 'core_poly': x-j, 'N':1}.

    - when the flag is ``False``, so `E` is not a `\QQ`-curve, the
      certificate is a prime `p` such that the reductions of `E` at
      the primes dividing `p` are inconsistent with the property of
      being a `\QQ`-curve.  See the ALGORITHM section for details.

    ALGORITHM:

    See [CrNa2020]_ for details.

    1. If `E` has rational `j`-invariant, or has CM, then return
    ``True``.

    2. Replace `E` by a curve defined over `K=\QQ(j(E))`. Let `N` be
    the conductor norm.

    3. For all primes `p\mid N` check that the valuations of `j` at
    all `P\mid p` are either all negative or all non-negative; if not,
    return ``False``.

    4. For `p\le maxp`, `p\not\mid N`, check that either `E` is
    ordinary mod `P` for all `P\mid p`, or `E` is supersingular mod
    `P` for all `P\mid p`; if neither, return ``False``.  If all are
    ordinary, check that the integers `a_P(E)^2-4N(P)` have the same
    square-free part; if not, return ``False``.

    5. Compute the `K`-isogeny class of `E` using the "heuristic"
    option (which is faster, but not guaranteed to be complete).
    Check whether the set of `j`-invariants of curves in the class of
    `2`-power degree contains a complete Galois orbit.  If so, return
    ``True``.

    6. Otherwise repeat step 4 for more primes, and if still
    undecided, repeat Step 5 without the "heuristic" option, to get
    the complete `K`-isogeny class (which will probably be no bigger
    than before).  Now return ``True`` if the set of `j`-invariants of
    curves in the class contains a complete Galois orbit, otherwise
    return ``False``.

    EXAMPLES:

    A non-CM curve over `\QQ` and a CM curve over `\QQ` are both
    trivially `\QQ`-curves::

        sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
        sage: E = EllipticCurve([1,2,3,4,5])
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0}

        sage: E = EllipticCurve(j=8000)
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': -8}

    A non-`\QQ`-curve over a quartic field.  The local data at bad
    primes above `3` is inconsistent::

        sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
        sage: R.<x> = PolynomialRing(QQ)
        sage: K.<a> = NumberField(R([3, 0, -5, 0, 1]))
        sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])])
        sage: is_Q_curve(E, certificate=True, verbose=True)
        Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve
        No: inconsistency at the 2 primes dividing 3
        - potentially multiplicative: [True, False]
        (False, 3)

    A non-`\QQ`-curve over a quadratic field.  The local data at bad
    primes is consistent, but the local test at good primes above `13`
    is not::

        sage: K.<a> = NumberField(R([-10, 0, 1]))
        sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])])
        sage: is_Q_curve(E, certificate=True, verbose=True)
        Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve
        Applying local tests at good primes above p<=100
        No: inconsistency at the 2 ordinary primes dividing 13
        - Frobenius discriminants mod squares: [-1, -3]
        No: local test at p=13 failed
        (False, 13)

    A quadratic `\QQ`-curve with CM discriminant `-15` (`j`-invariant not in `\QQ`)::

        sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
        sage: R.<x> = PolynomialRing(QQ)
        sage: K.<a> = NumberField(R([-1, -1, 1]))
        sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])])
        sage: is_Q_curve(E, certificate=True, verbose=True)
        Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve
        Yes: E is CM (discriminant -15)
        (True, {'CM': -15})

    An example over `\QQ(\sqrt{2},\sqrt{3})`.  The `j`-invariant is in
    `\QQ(\sqrt{6})`, so computations will be done over that field, and
    in fact there is an isogenous curve with rational `j`, so we have
    a so-called rational `\QQ`-curve::

        sage: K.<a> = NumberField(R([1, 0, -4, 0, 1]))
        sage: E = EllipticCurve([K([-2,-4,1,1]),K([0,1,0,0]),K([0,1,0,0]),K([-4780,9170,1265,-2463]),K([163923,-316598,-43876,84852])])
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0}

    Over the same field, a so-called strict `\QQ`-curve which is not
    isogenous to one with rational `j`, but whose core field is
    quadratic. In fact the isogeny class over `K` consists of `6`
    curves, four with conjugate quartic `j`-invariants and `2` with
    quadratic conjugate `j`-invariants in `\QQ(\sqrt{3})` (but which
    are not base-changes from the quadratic subfield)::

        sage: E = EllipticCurve([K([0,-3,0,1]),K([1,4,0,-1]),K([0,0,0,0]),K([-2,-16,0,4]),K([-19,-32,4,8])])
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': 0,
        'N': 2,
        'core_degs': [1, 2],
        'core_poly': x^2 - 840064*x + 1593413632,
        'r': 1,
        'rho': 1}

    """
    from sage.rings.number_field.number_field_base import is_NumberField

    if verbose:
        print("Checking whether {} is a Q-curve".format(E))

    try:
        assert is_NumberField(E.base_field())
    except (AttributeError, AssertionError):
        raise TypeError(
            "{} must be an elliptic curve defined over a number field in is_Q_curve()"
        )

    from sage.rings.integer_ring import ZZ
    from sage.arith.functions import lcm
    from sage.libs.pari import pari
    from sage.rings.number_field.number_field import NumberField
    from sage.schemes.elliptic_curves.constructor import EllipticCurve
    from sage.schemes.elliptic_curves.cm import cm_j_invariants_and_orders, is_cm_j_invariant

    # Step 1

    # all curves with rational j-invariant are Q-curves:
    jE = E.j_invariant()
    if jE in QQ:
        if verbose:
            print("Yes: j(E) is in QQ")
        if certificate:
            # test for CM
            for d, f, j in cm_j_invariants_and_orders(QQ):
                if jE == j:
                    return True, {'CM': d * f**2}
            # else not CM
            return True, {
                'CM': ZZ(0),
                'r': ZZ(0),
                'rho': ZZ(0),
                'N': ZZ(1),
                'core_poly': polygen(QQ)
            }
        else:
            return True

    # CM curves are Q-curves:
    flag, df = is_cm_j_invariant(jE)
    if flag:
        d, f = df
        D = d * f**2
        if verbose:
            print("Yes: E is CM (discriminant {})".format(D))
        if certificate:
            return True, {'CM': D}
        else:
            return True

    # Step 2: replace E by a curve defined over Q(j(E)):

    K = E.base_field()
    jpoly = jE.minpoly()
    if jpoly.degree() < K.degree():
        if verbose:
            print("switching to smaller base field: j's minpoly is {}".format(
                jpoly))
        f = pari(jpoly).polredbest().sage({'x': jpoly.parent().gen()})
        K2 = NumberField(f, 'b')
        jE = jpoly.roots(K2)[0][0]
        if verbose:
            print("New j is {} over {}, with minpoly {}".format(
                jE, K2, jE.minpoly()))
        #assert jE.minpoly()==jpoly
        E = EllipticCurve(j=jE)
        K = K2
        if verbose:
            print("New test curve is {}".format(E))

    # Step 3: check primes of bad reduction

    NN = E.conductor().norm()
    for p in NN.support():
        Plist = K.primes_above(p)
        if len(Plist) < 2:
            continue
        # pot_mult = potential multiplicative reduction
        pot_mult = [jE.valuation(P) < 0 for P in Plist]
        consistent = all(pot_mult) or not any(pot_mult)
        if not consistent:
            if verbose:
                print("No: inconsistency at the {} primes dividing {}".format(
                    len(Plist), p))
                print("  - potentially multiplicative: {}".format(pot_mult))
            if certificate:
                return False, p
            else:
                return False

    # Step 4 check: primes P of good reduction above p<=B:

    if verbose:
        print("Applying local tests at good primes above p<={}".format(maxp))

    res4, p = Step4Test(E, B=maxp, oldB=0, verbose=verbose)
    if not res4:
        if verbose:
            print("No: local test at p={} failed".format(p))
        if certificate:
            return False, p
        else:
            return False

    if verbose:
        print("...all local tests pass for p<={}".format(maxp))

    # Step 5: compute the (partial) K-isogeny class of E and test the
    # set of j-invariants in the class:

    C = E.isogeny_class(algorithm='heuristic', minimal_models=False)
    jC = [E2.j_invariant() for E2 in C]
    centrejpols = conjugacy_test(jC, verbose=verbose)
    if centrejpols:
        if verbose:
            print(
                "Yes: the isogeny class contains a complete conjugacy class of j-invariants"
            )
        if certificate:
            for f in centrejpols:
                rho = f.degree().valuation(2)
                centre_indices = [i for i, j in enumerate(jC) if f(j) == 0]
                M = C.matrix()
                core_degs = [M[centre_indices[0], i] for i in centre_indices]
                level = lcm(core_degs)
                if level.is_squarefree():
                    r = len(level.prime_divisors())
                    cert = {
                        'CM': ZZ(0),
                        'core_poly': f,
                        'rho': rho,
                        'r': r,
                        'N': level,
                        'core_degs': core_degs
                    }
                    return True, cert
            print("No central curve found")
        else:
            return True

    # Now we are undecided.  This can happen if either (1) E is not a
    # Q-curve but we did not use enough primes in Step 4 to detect
    # this, or (2) E is a Q-curve but in Step 5 we did not compute the
    # complete isogeny class.  Case (2) is most unlikely since the
    # heuristic bound used in computing isogeny classes means that we
    # have all isogenous curves linked to E by an isogeny of degree
    # supported on primes<1000.

    # We first rerun Step 4 with a larger bound.

    xmaxp = 10 * maxp
    if verbose:
        print(
            "Undecided after first round, so we apply more local tests, up to {}"
            .format(xmaxp))

    res4, p = Step4Test(E, B=xmaxp, oldB=maxp, verbose=verbose)
    if not res4:
        if verbose:
            print("No: local test at p={} failed".format(p))
        if certificate:
            return False, p
        else:
            return False

    # Now we rerun Step 5 using a rigorous computation of the complete
    # isogeny class.  This will probably contain no more curves than
    # before, in which case -- since we already tested that the set of
    # j-invariants does not contain a complete Galois conjugacy class
    # -- we can deduce that E is not a Q-curve.

    if verbose:
        print("...all local tests pass for p<={}".format(xmaxp))
        print("We now compute the complete isogeny class...")

    Cfull = E.isogeny_class(minimal_models=False)
    jCfull = [E2.j_invariant() for E2 in Cfull]

    if len(jC) == len(jCfull):
        if verbose:
            print("...and find that we already had the complete class:so No")
        if certificate:
            return False, 0
        else:
            return False
    if verbose:
        print(
            "...and find that the class contains {} curves, not just the {} we computed originally"
            .format(len(jCfull), len(jC)))
    centrejpols = conjugacy_test(jCfull, verbose=verbose)
    if cert:
        if verbose:
            print(
                "Yes: the isogeny class contains a complete conjugacy class of j-invariants"
            )
        if certificate:
            return True, centrejpols
        else:
            return True
    if verbose:
        print(
            "No: the isogeny class does *not* contain a complete conjugacy class of j-invariants"
        )
    if certificate:
        return False, 0
    else:
        return False
コード例 #9
0
def matrix_representation(K, F, v):
    r""" Return a matrix representation of a given list of elements.

    INPUT:

    - ``K`` -- a field
    - ``F`` -- a finitely generated field extension of `K`
    - ``v`` -- a vector over `F`, or a list with entries in `F`

    OUTPUT: a pair `(A, w)`, where `A` is a matrix with entries in `K` and
    `w` is a vector over `F`, such that the entries of `w` are `K`-linearly
    independent and

    .. MATH::

             v = w*A^t.

    It follows that all the entries of `v` are contained in the `K`-span of the
    entries of `w`, and that the left kernel of `A` is the space of `K`-relations
    between the entries of `v`.

    EXAMPLES::

        sage: from regular_models.RR_spaces.function_spaces import matrix_representation
        sage: K = GF(2)
        sage: F1.<a> = K.extension(3)
        sage: v = [a, a^2, a + a^2]
        sage: A, w = matrix_representation(K, F1, v)
        sage: w*A.transpose()
        (a, a^2, a^2 + a)
        sage: F2.<x> = FunctionField(F1)
        sage: v = vector(F2, [(x+1)/x, a*x + 1])
        sage: A, w = matrix_representation(K, F2, v)
        sage: w*A.transpose() == v
        True
        sage: R.<y> = F2[]
        sage: F3.<y> = F2.extension(y^2 - x^3 - a)
        sage: v = vector(F3, [y*x, y*a, x])
        sage: A, w = matrix_representation(K, F3, v)
        sage: w*A.transpose() == v
        True

    """
    from sage.arith.functions import lcm
    from sage.modules.free_module_element import vector
    from sage.all import matrix
    assert F.has_coerce_map_from(K), "K must be a subfield of F"
    v = vector(F, [F(v[i]) for i in range(len(v))])
    if F == K:
        return matrix(v).transpose(), vector(K, [K.one()])

    elif is_finite_simple_extension(K, F):
        # F is a finite extension of its base field K
        n = F.degree()
        # the entries of w are the standard basis of F/K
        w = vector(F, [F.gen()**i for i in range(n)])
        A = matrix(
            K,
            [vector_for_finite_extension(K, F, v[j]) for j in range(len(v))])
        # test result
        # assert v == w*A.transpose()
        return A, w

    elif is_rational_function_field(K, F):
        # F is the rational function field over K
        x = F.gen()
        n = len(v)
        h = lcm([v[j].denominator() for j in range(n)])
        g_list = [F._ring(v[j] * h) for j in range(n)]
        N = max([g.degree() for g in g_list]) + 1
        w = vector(F, [x**i / h for i in range(N)])
        A = matrix(K, n, N)
        for i in range(n):
            for j in range(N):
                A[i, j] = g_list[i][j]
        # vector_list = []
        # for g in g_list:
        #     g_vector = vector(K, N, [g[i] for i in range(N)])
        #     vector_list.append(g_vector)
        # A = matrix(K, vector_list)
        assert v == w * A.transpose()
        return A, w

    elif is_composite(K, F):
        # F is a finite extension of its base field, which properly contains K
        F0 = intermediate_field(K, F)
        A, w = matrix_representation(F0, F, v)
        m, n = A.dimensions()
        a = vector(F0, m * n)
        for i in range(m):
            for j in range(n):
                a[j + i * n] = A[i, j]
        B, u = matrix_representation(K, F0, a)
        _, r = B.dimensions()
        x = vector(F, r * n)
        C = matrix(K, m, r * n)
        for j in range(n):
            for k in range(r):
                mu = j + n * k
                x[mu] += w[j] * u[k]
                for i in range(m):
                    ell = j + n * i
                    C[i, mu] += B[ell, k]
        # test the result
        assert x * C.transpose() == v
        return C, x
    else:
        raise NotImplementedError()
コード例 #10
0
def jacobian(*X):
    r"""
    Compute the Jacobian (Rankin--Cohen--Ibukiyama) operator.

    INPUT:
    - ``X`` -- a list [F_1, ..., F_N] of N orthogonal modular forms for the same Gram matrix, where N = 3 + (number of self's gram matrix rows)

    OUTPUT: OrthogonalModularForm. (If F_1, ..., F_N have weights k_1, ..., k_N then the result has weight k_1 + ... + k_N + N - 1.)

    EXAMPLES::

        sage: from weilrep import *
        sage: jacobian([ParamodularForms(1).eisenstein_series(k, 7) for k in [4, 6, 10, 12]]) / (-589927441461779261030400000/2354734631251) #funny multiple
        (r^-1 - r)*q^3*s^2 + (-r^-1 + r)*q^2*s^3 + (-r^-3 - 69*r^-1 + 69*r + r^3)*q^4*s^2 + (r^-3 + 69*r^-1 - 69*r - r^3)*q^2*s^4 + O(q, s)^7
    """
    N = len(X)
    if N == 1:
        X = X[0]
        N = len(X)
    Xref = X[0]
    nvars = Xref.nvars()
    f = Xref.true_fourier_expansion()
    t, = f.parent().gens()
    rb_x = f.base_ring()
    x, = rb_x.gens()
    r_list = rb_x.base_ring().gens()
    if N != nvars + 1:
        raise ValueError('The Jacobian requires %d modular forms.' %
                         (nvars + 1))
    k = N - 1
    v = vector([0] * nvars)
    r_deriv = [[] for _ in r_list]
    t_deriv = []
    x_deriv = []
    u = []
    S = Xref.gram_matrix()
    new_scale = lcm(x.scale() for x in X)
    for y in X:
        if y.gram_matrix() != S:
            raise ValueError('These forms do not have the same Gram matrix.')
        f = y.rescale(new_scale // y.scale()).true_fourier_expansion()
        t_deriv.append(t * f.derivative())
        if nvars > 1:
            x_deriv.append(f.map_coefficients(lambda a: x * a.derivative()))
            if nvars > 2:
                for i, r in enumerate(r_list):
                    r_deriv[i].append(
                        f.map_coefficients(lambda a: rb_x([
                            r * y.derivative(r) for y in list(a)
                        ]) * (x**(a.polynomial_construction()[1]))))
        y_k = y.weight()
        k += y_k
        v += y.weyl_vector()
        u.append(y_k * f)
    L = [u, t_deriv]
    if nvars > 1:
        L.append(x_deriv)
        if nvars > 2:
            L.extend(r_deriv)
    return OrthogonalModularForm(
        k,
        Xref.weilrep(),
        matrix(L).determinant(),
        scale=new_scale,
        weylvec=v,
        qexp_representation=Xref.qexp_representation())
コード例 #11
0
ファイル: grading.py プロジェクト: ehaka/lie-algebra-gradings
def torsion_free_gradings(L):
    r"""
    Return a complete list of gradings of the Lie algebra over torsion
    free abelian groups.

    The list is guaranteed to be complete in the following sense:
    If `\mathfrak{g} = \bigoplus_{a\in A} \mathfrak{g}_a` is any grading
    of the Lie algebra `\mathfrak{g}` over a torsion free abelian group
    `A`, then there exists
    - a grading `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^m} \mathfrak{g}_n`,
    - an automorphism `\Phi\in\mathrm{Aut}(\mathfrak{g})`, and
    - a homomorphism `\varphi\colon\mathbb{Z}^m\to A`
    such that the grading `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^m} \Phi(\mathfrak{g}_{\varphi(n)})`
    is exactly the same as the original `A`-grading.
    However, the list is not guaranteed to be reduced up to
    automorphism, so the above choices are not in general unique.

    EXAMPLES:

    We list all gradings of the Heisenberg Lie algebra over torsion free
    abelian groups::

        sage: from lie_gradings.gradings.grading import torsion_free_gradings
        sage: L = lie_algebras.Heisenberg(QQ, 1)
        sage: torsion_free_gradings(L)
        [Grading over Additive abelian group isomorphic to Z + Z of Heisenberg algebra of rank 1 over Rational Field with nonzero layers
           (1, 0) : (p1,)
           (0, 1) : (q1,)
           (1, 1) : (z,)
         ,
         Grading over Additive abelian group isomorphic to Z of Heisenberg algebra of rank 1 over Rational Field with nonzero layers
           (1) : (p1, z)
           (0) : (q1,)
         ,
         Grading over Additive abelian group isomorphic to Z of Heisenberg algebra of rank 1 over Rational Field with nonzero layers
           (1) : (q1, z)
           (0) : (p1,)
         ,
         Grading over Additive abelian group isomorphic to Z of Heisenberg algebra of rank 1 over Rational Field with nonzero layers
           (1) : (p1, q1)
           (2) : (z,)
         ,
         Grading over Trivial group of Heisenberg algebra of rank 1 over Rational Field with nonzero layers
           () : (p1, q1, z)
         ]
    """
    maxgrading = maximal_grading(L)
    V = FreeModule(ZZ, len(maxgrading.magma().gens()))
    weights = maxgrading.layers().keys()

    # The torsion-free gradings are enumerated by torsion-free quotients
    # of the grading group of the maximal grading.
    diffset = set([tuple(b - a) for a, b in combinations(weights, 2)])
    subspaces = []
    for d in range(len(diffset) + 1):
        for B in combinations(diffset, d):
            W = V.submodule(B)
            if W not in subspaces:
                subspaces.append(W)

    # for each subspace, define the quotient grading
    projected_gradings = []
    for W in subspaces:
        Q = V.quotient(W)

        # check if quotient is not torsion-free
        if any(qi > 0 for qi in Q.invariants()):
            continue

        quot_layers = {}
        for n in weights:
            pi_n = tuple(Q(V(tuple(n))))
            if pi_n not in quot_layers:
                quot_layers[pi_n] = []
            quot_layers[pi_n].extend(maxgrading.layers()[n])

        # expand away denominators to get an integer vector grading
        A = AdditiveAbelianGroup(Q.invariants())
        denoms = [
            pi_n_k.denominator() for pi_n in quot_layers for pi_n_k in pi_n
        ]
        mult = lcm(denoms)
        proj_layers = {
            tuple(mult * pi_nk for pi_nk in pi_n): l
            for pi_n, l in quot_layers.items()
        }
        proj_grading = grading(L, proj_layers, magma=A, projections=True)
        projected_gradings.append(proj_grading)
    return projected_gradings
コード例 #12
0
ファイル: taut_euler_class.py プロジェクト: henryseg/Veering
def order_of_euler_class(delta, E):
    """
    Given the coboundary operator delta and an Euler two-cocycle E,
    returns k if [E] is k--torsion.  By convention, returns zero if
    [E] is non-torsion.  Note that the trivial element is 1--torsion.
    """

    delta = Matrix(delta)
    E = vector(E)

    # Note that E is a coboundary if there is a one-cocycle C solving
    #
    # E = C*delta
    #
    # We can find C (if it exists at all) using Smith normal form.

    D, U, V = delta.smith_form()
    assert D == U*delta*V

    # So we are trying to solve
    #
    # C*delta = C*U.inverse()*D*V.inverse() = E
    #
    # for a one-cochain C.  Multiply by V to get
    #
    # C*delta*V = C*U.inverse()*D = E*V
    #
    # Now set
    #
    # B = C*U.inverse(), and so B*U = C
    #
    # and rewrite to get
    #
    # B*U*delta*V = B*D = E*V
    #
    # So define E' by:

    Ep = E*V

    # Finally we attempt to solve B * D = Ep.  Note that D is
    # diagonal: so if we can solve all of the equations

    # B[i] * D[i][i] == Ep[i]

    # with B[i] integers, then [E] = 0 in cohomology.

    diag = diagonal(D)

    if any( (diag[i] == 0 and Ep[i] != 0) for i in range(len(Ep)) ):
        return 0

    # All zeros are at the end in Smith normal form.  Since we've
    # passed the above we can now remove them.

    first_zero = diag.index(0)
    diag = diag[:first_zero]
    Ep = Ep[:first_zero]

    # Since diag[i] is (now) never zero we can divide to get the
    # fractions Ep[i]/diag[i] and then find the scaling that makes
    # them simultaneously integral.

    denoms = [ diag[i] / gcd(Ep[i], diag[i]) for i in range(len(Ep)) ]
    return lcm(denoms)