def adjoint(self):
    """
    This gives the adjoint (integral) quadratic form associated to the
    given form, essentially defined by taking the adjoint of the matrix.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ, 2, [1,2,5])
        sage: Q.adjoint()
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 5 -2 ]
        [ * 1 ]

    ::

        sage: Q = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5])
        sage: Q.adjoint()
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 39 2 8 ]
        [ * 19 4 ]
        [ * * 8 ]

    """
    if is_odd(self.dim()):
        return QuadraticForm(self.matrix().adjoint()*2)
    else:
        return QuadraticForm(self.matrix().adjoint())
Esempio n. 2
0
def HyperbolicPlane_quadratic_form(R, r=1):
    """
    Constructs the direct sum of `r` copies of the quadratic form `xy`
    representing a hyperbolic plane defined over the base ring `R`.

    INPUT:

    - `R`: a ring
    - `n` (integer, default 1) number of copies

    EXAMPLES::

        sage: HyperbolicPlane_quadratic_form(ZZ)
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 0 1 ]
        [ * 0 ]

    """
    r = ZZ(r)
    ## Check that the multiplicity is a natural number
    if r < 1:
        raise TypeError("The multiplicity r must be a natural number.")

    H = QuadraticForm(R, 2, [0, 1, 0])
    return sum([H for i in range(r - 1)], H)
Esempio n. 3
0
    def signature(self, new_convention=True):
        """
        Returns the signature of the link, computed from the Goeritz matrix using
        the algorithm of Gordon and Litherland::

            sage: K = Link('4a1')
            sage: K.signature()
            0
            sage: L = Link('9^3_12')
            sage: Lbar = L.mirror()
            sage: L.signature() + Lbar.signature()
            0
            sage: M = Link(braid_closure=[1, 2, 1, 2, 1, 2, 1, 2])
            sage: M.signature()
            -6

        SnapPy 3.0 switched the sign convention for the signature so
        that "positive knots have negative signatures".  You can
        recover the previous default by::

          sage: L = Link('3a1')
          sage: L.signature()
          -2
          sage: L.signature(new_convention=False)
          2
        """
        m, G = self.goeritz_matrix(return_graph=True)
        correction = sum([
            e[2]['sign'] for e in G.edges(sort=False)
            if e[2]['sign'] == e[2]['crossing'].sign
        ])
        ans = QuadraticForm(QQ, m).signature() + correction
        if new_convention:
            ans = -ans
        return ans
Esempio n. 4
0
def BezoutianQuadraticForm(f, g):
    r"""
    Compute the Bezoutian of two polynomials defined over a common base ring.  This is defined by

    .. MATH::

        {\rm Bez}(f, g) := \frac{f(x) g(y) - f(y) g(x)}{y - x}

    and has size defined by the maximum of the degrees of `f` and `g`.

    INPUT:

    - `f`, `g` -- polynomials in `R[x]`, for some ring `R`

    OUTPUT:

    a quadratic form over `R`

    EXAMPLES::

        sage: R = PolynomialRing(ZZ, 'x')
        sage: f = R([1,2,3])
        sage: g = R([2,5])
        sage: Q = BezoutianQuadraticForm(f, g) ; Q
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 1 -12 ]
        [ * -15 ]

    AUTHORS:

    - Fernando Rodriguez-Villegas, Jonathan Hanke -- added on 11/9/2008

    """
    ## Check that f and g are polynomials with a common base ring
    if not is_Polynomial(f) or not is_Polynomial(g):
        raise TypeError("Oops!  One of your inputs is not a polynomial. =(")
    if f.base_ring() != g.base_ring(
    ):  ## TO DO:  Change this to allow coercion!
        raise TypeError(
            "Oops!  These polynomials are not defined over the same coefficient ring."
        )

    ## Initialize the quadratic form
    R = f.base_ring()
    P = PolynomialRing(R, ['x', 'y'])
    a, b = P.gens()
    n = max(f.degree(), g.degree())
    Q = QuadraticForm(R, n)

    ## Set the coefficients of Bezoutian
    bez_poly = (f(a) * g(b) - f(b) * g(a)) // (
        b - a)  ## Truncated (exact) division here
    for i in range(n):
        for j in range(i, n):
            if i == j:
                Q[i, j] = bez_poly.coefficient({a: i, b: j})
            else:
                Q[i, j] = bez_poly.coefficient({a: i, b: j}) * 2

    return Q
Esempio n. 5
0
 def _norm_form(self):
     r"""
     Return the norm N_{K/QQ} as a integral quadratic form.
     """
     w = self._w()
     wc = w.galois_conjugate()
     wtr = Integer(w + wc)
     return QuadraticForm(Matrix(ZZ, [[2, wtr], [wtr, 2 * w * wc]]))
Esempio n. 6
0
def make_random_instance(n, m):
    solution = VectorSpace(ZZ2, n).random_element()
    results = []
    quad_forms = []
    for _ in range(m):
        e = []
        for _ in range(n * (n + 1) / 2):
            e.append(ZZ2.random_element())
        quad_form = QuadraticForm(ZZ2, n, e)
        quad_forms.append(quad_form)
        results.append(quad_form(solution))
    instance = Instance(n, quad_forms, results)
    prover = Prover(instance, solution)
    return (instance, prover)
Esempio n. 7
0
    def signature(self):
        r"""
        Return the signature of this lattice, which is defined as
        the difference between the number of positive eigenvalues and
        the number of negative eigenvalues in the Gram matrix.

        EXAMPLES::

            sage: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLattice
            sage: U = IntegralLattice(Matrix(ZZ,2,2,[0,1,1,0]))
            sage: U.signature()
            0
        """
        from sage.quadratic_forms.quadratic_form import QuadraticForm
        return QuadraticForm(QQ, self.gram_matrix()).signature()
Esempio n. 8
0
    def signature_pair(self):
        r"""
        Return the signature tuple `(n_+,n_-)` of this lattice.

        Here `n_+` (resp. `n_-`) is the number of positive (resp. negative)
        eigenvalues of the Gram matrix.

        EXAMPLES::


            sage: A2 = IntegralLattice("A2")
            sage: A2.signature_pair()
            (2, 0)
        """
        from sage.quadratic_forms.quadratic_form import QuadraticForm
        return QuadraticForm(QQ, self.gram_matrix()).signature_vector()[:2]
Esempio n. 9
0
    def signature_pair(self):
        r"""
        Return the signature tuple `(n_+,n_-)` of this lattice.

        Here `n_+` (resp. `n_-`) is the number of positive (resp. negative)
        eigenvalues of the Gram matrix.

        EXAMPLES::

            sage: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLattice
            sage: A2 = IntegralLattice(Matrix(ZZ,2,2,[2,-1,-1,2]))
            sage: A2.signature_pair()
            (2, 0)
        """
        from sage.quadratic_forms.quadratic_form import QuadraticForm
        return QuadraticForm(QQ, self.gram_matrix()).signature_vector()[:2]
Esempio n. 10
0
    def signature(self):
        """
        Returns the signature of the link, computed from the Goeritz matrix using
        the algorithm of Gordon and Litherland::

            sage: K = Link('4a1')            
            sage: K.signature()          
            0
            sage: L = Link('9^3_12')
            sage: Lbar = L.mirror()
            sage: L.signature() + Lbar.signature()
            0
        """
        m, G = self.goeritz_matrix(return_graph=True)
        correction = sum([e[2]['sign'] for e in G.edges(sort=False)
                          if e[2]['sign'] == e[2]['crossing'].sign])
        return QuadraticForm(QQ, m).signature() + correction
Esempio n. 11
0
    def orthogonal_group(self, gens=None, is_finite=None):
        """
        Return the orthogonal group of this lattice as a matrix group.

        The elements are isometries of the ambient vector space
        which preserve this lattice. They are represented by
        matrices with respect to the standard basis.

        INPUT:

        - ``gens`` -- a list of matrices (default:``None``)
        - ``is_finite`` -- bool (default: ``None``) If set to ``True``,
          then the group is placed in the category of finite groups. Sage does not check this.

        OUTPUT:

        The matrix group generated by ``gens``.
        If ``gens`` is not specified, then generators of the full
        orthogonal group of this lattice are computed. They are
        continued as the identity on the orthogonal complement of
        the lattice in its ambient space. Currently, we can only
        compute the orthogonal group for positive definite lattices.

        EXAMPLES::

            sage: A4 = IntegralLattice("A4")
            sage: Aut = A4.orthogonal_group()
            sage: Aut
            Group of isometries with 5 generators (
            [-1  0  0  0]  [0 0 0 1]  [-1 -1 -1  0]  [ 1  0  0  0]  [ 1  0  0  0]
            [ 0 -1  0  0]  [0 0 1 0]  [ 0  0  0 -1]  [-1 -1 -1 -1]  [ 0  1  0  0]
            [ 0  0 -1  0]  [0 1 0 0]  [ 0  0  1  1]  [ 0  0  0  1]  [ 0  0  1  1]
            [ 0  0  0 -1], [1 0 0 0], [ 0  1  0  0], [ 0  0  1  0], [ 0  0  0 -1]
            )

        The group acts from the right on the lattice and its discriminant group::

            sage: x = A4.an_element()
            sage: g = Aut.an_element()
            sage: g
            [ 1  1  1  0]
            [ 0  0 -1  0]
            [ 0  0  1  1]
            [ 0 -1 -1 -1]
            sage: x*g
            (1, 1, 1, 0)
            sage: (x*g).parent()==A4
            True
            sage: (g*x).parent()
            Vector space of dimension 4 over Rational Field
            sage: y = A4.discriminant_group().an_element()
            sage: y*g
            (1)

        If the group is finite we can compute the usual things::

            sage: Aut.order()
            240
            sage: conj = Aut.conjugacy_classes_representatives()
            sage: len(conj)
            14
            sage: Aut.structure_description()
            'C2 x S5'

        The lattice can live in a larger ambient space::

            sage: A2 = IntegralLattice(matrix.identity(3),Matrix(ZZ,2,3,[1,-1,0,0,1,-1]))
            sage: A2.orthogonal_group()
            Group of isometries with 3 generators (
            [-1/3  2/3  2/3]  [ 2/3  2/3 -1/3]  [1 0 0]
            [ 2/3 -1/3  2/3]  [ 2/3 -1/3  2/3]  [0 0 1]
            [ 2/3  2/3 -1/3], [-1/3  2/3  2/3], [0 1 0]
            )

        It can be negative definite as well::

            sage: A2m = IntegralLattice(-Matrix(ZZ,2,[2,1,1,2]))
            sage: G = A2m.orthogonal_group()
            sage: G.order()
            12

        If the lattice is indefinite, sage does not know how to compute generators.
        Can you teach it?::

            sage: U = IntegralLattice(Matrix(ZZ,2,[0,1,1,0]))
            sage: U.orthogonal_group()
            Traceback (most recent call last):
            ...
            NotImplementedError: currently, we can only compute generators for orthogonal groups over definite lattices.

        But we can define subgroups::

            sage: S = IntegralLattice(Matrix(ZZ,2,[2, 3, 3, 2]))
            sage: f = Matrix(ZZ,2,[0,1,-1,3])
            sage: S.orthogonal_group([f])
            Group of isometries with 1 generator (
            [ 0  1]
            [-1  3]
            )

        TESTS:

        We can handle the trivial group::

            sage: S = IntegralLattice(Matrix(ZZ,2,[2, 3, 3, 2]))
            sage: S.orthogonal_group([])
            Group of isometries with 1 generator (
            [1 0]
            [0 1]
            )
        """
        from sage.categories.groups import Groups
        from sage.groups.matrix_gps.isometries import GroupOfIsometries
        sig = self.signature_pair()
        if gens is None:
            gens = []
            if sig[1] == 0 or sig[0] == 0:  # definite
                from sage.quadratic_forms.quadratic_form import QuadraticForm
                is_finite = True
                # Compute transformation matrix to the ambient module.
                L = self.overlattice(self.ambient_module().gens())
                Orthogonal = L.orthogonal_complement(self)
                B = self.basis_matrix().stack(Orthogonal.basis_matrix())
                if sig[0] == 0:  # negative definite
                    q = QuadraticForm(ZZ, -2 * self.gram_matrix())
                else:  # positive definite
                    q = QuadraticForm(ZZ, 2 * self.gram_matrix())
                identity = matrix.identity(Orthogonal.rank())
                for g in q.automorphism_group().gens():
                    g = g.matrix().T
                    # We continue g as identity on the orthogonal complement.
                    g = matrix.block_diagonal([g, identity])
                    g = B.inverse() * g * B
                    gens.append(g)
            else:  # indefinite
                raise NotImplementedError(
                    "currently, we can only compute generators "
                    "for orthogonal groups over definite lattices.")
        deg = self.degree()
        base = self.ambient_vector_space().base_ring()
        inv_bil = self.inner_product_matrix()
        if is_finite:
            cat = Groups().Finite()
        else:
            cat = Groups()
        D = self.discriminant_group()
        G = GroupOfIsometries(deg,
                              base,
                              gens,
                              inv_bil,
                              category=cat,
                              invariant_submodule=self,
                              invariant_quotient_module=D)
        return G
Esempio n. 12
0
def random_quadraticform(R, n, rand_arg_list=[]):
    """
    Create a random quadratic form in `n` variables defined over the ring `R`.

    The last (and optional) argument ``rand_arg_list`` is a list of at most 3
    elements which is passed (as at most 3 separate variables) into the method
    ``R.random_element()``.

    INPUT:

    - `R` -- a ring.
    - `n` -- an integer `\ge 0`
    - ``rand_arg_list`` -- a list of at most 3 arguments which can be taken by
      ``R.random_element()``.

    OUTPUT:

    A quadratic form over the ring `R`.

    EXAMPLES::

        sage: random_quadraticform(ZZ, 3, [1,5])    ## RANDOM
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 3 2 3 ]
        [ * 1 4 ]
        [ * * 3 ]

    ::

        sage: random_quadraticform(ZZ, 3, [-5,5])    ## RANDOM
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 3 2 -5 ]
        [ * 2 -2 ]
        [ * * -5 ]

    ::

        sage: random_quadraticform(ZZ, 3, [-50,50])    ## RANDOM
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 1 8 -23 ]
        [ * 0 0 ]
        [ * * 6 ]
    """
    ## Sanity Checks: We have a ring and there are at most 3 parameters for randomness!
    if len(rand_arg_list) > 3:
        raise TypeError(
            "Oops!  The list of randomness arguments can have at most 3 elements."
        )
    if not is_Ring(R):
        raise TypeError("Oops!  The first argument must be a ring.")

    ## Create a list of upper-triangular entries for the quadratic form
    L = len(rand_arg_list)
    nn = int(n * (n + 1) / 2)
    if L == 0:
        rand_list = [R.random_element() for _ in range(nn)]
    elif L == 1:
        rand_list = [R.random_element(rand_arg_list[0]) for _ in range(nn)]
    elif L == 2:
        rand_list = [
            R.random_element(rand_arg_list[0], rand_arg_list[1])
            for _ in range(nn)
        ]
    elif L == 3:
        rand_list = [
            R.random_element(rand_arg_list[0], rand_arg_list[1],
                             rand_arg_list[2]) for _ in range(nn)
        ]

    ## Return  the Quadratic Form
    return QuadraticForm(R, n, rand_list)
def minkowski_reduction(self):
    """
    Find a Minkowski-reduced form equivalent to the given one.
    This means that

    .. MATH::

            Q(v_k) <= Q(s_1 * v_1 + ... + s_n * v_n)

    for all `s_i` where GCD`(s_k, ... s_n) = 1`.

    Note: When Q has dim <= 4 we can take all `s_i` in {1, 0, -1}.

    References:
        Schulze-Pillot's paper on "An algorithm for computing genera
            of ternary and quaternary quadratic forms", p138.
        Donaldson's 1979 paper "Minkowski Reduction of Integral
            Matrices", p203.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ,4,[30, 17, 11, 12, 29, 25, 62, 64, 25, 110])
        sage: Q
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 30 17 11 12 ]
        [ * 29 25 62 ]
        [ * * 64 25 ]
        [ * * * 110 ]
        sage: Q.minkowski_reduction()
        (
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 30 17 11 -5 ]
        [ * 29 25 4 ]
        [ * * 64 0 ]
        [ * * * 77 ]                                                       ,
        <BLANKLINE>
        [ 1  0  0  0]
        [ 0  1  0 -1]
        [ 0  0  1  0]
        [ 0  0  0  1]
        )

    ::

        sage: Q=QuadraticForm(ZZ,4,[1, -2, 0, 0, 2, 0, 0, 2, 0, 2])
        sage: Q
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 1 -2 0 0 ]
        [ * 2 0 0 ]
        [ * * 2 0 ]
        [ * * * 2 ]
        sage: Q.minkowski_reduction()
        (
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 1 0 0 0 ]
        [ * 1 0 0 ]
        [ * * 2 0 ]
        [ * * * 2 ]                                                        ,
        <BLANKLINE>
        [1 1 0 0]
        [0 1 0 0]
        [0 0 1 0]
        [0 0 0 1]
        )

    ::

        sage: Q=QuadraticForm(ZZ,5,[2,2,0,0,0,2,2,0,0,2,2,0,2,2,2])
        sage: Q.Gram_matrix()
        [2 1 0 0 0]
        [1 2 1 0 0]
        [0 1 2 1 0]
        [0 0 1 2 1]
        [0 0 0 1 2]
        sage: Q.minkowski_reduction()
        Traceback (most recent call last):
        ...
        NotImplementedError: This algorithm is only for dimensions less than 5
    """
    from sage.quadratic_forms.quadratic_form import QuadraticForm
    from sage.quadratic_forms.quadratic_form import matrix
    if not self.is_positive_definite():
        raise TypeError(
            "Minkowksi reduction only works for positive definite forms")
    if self.dim() > 4:
        raise NotImplementedError(
            "This algorithm is only for dimensions less than 5")

    R = self.base_ring()
    n = self.dim()
    Q = deepcopy(self)
    M = matrix(R, n, n)
    for i in range(n):
        M[i, i] = 1

    ## Begin the reduction
    done_flag = False
    while not done_flag:

        ## Loop through possible shorted vectors until
        done_flag = True
        #print " j_range = ", range(n-1, -1, -1)
        for j in range(n - 1, -1, -1):
            for a_first in mrange([3 for i in range(j)]):
                y = [x - 1
                     for x in a_first] + [1] + [0 for k in range(n - 1 - j)]
                e_j = [0 for k in range(n)]
                e_j[j] = 1
                #print "j = ", j

                ## Reduce if a shorter vector is found
                #print "y = ", y, "   e_j = ", e_j, "\n"
                if Q(y) < Q(e_j):

                    ## Create the transformation matrix
                    M_new = matrix(R, n, n)
                    for k in range(n):
                        M_new[k, k] = 1
                    for k in range(n):
                        M_new[k, j] = y[k]

                    ## Perform the reduction and restart the loop
                    #print "Q_before = ", Q
                    Q = QuadraticForm(M_new.transpose() * Q.matrix() * M_new)
                    M = M * M_new
                    done_flag = False

                    ## DIAGNOSTIC
                    #print "Q(y) = ", Q(y)
                    #print "Q(e_j) = ", Q(e_j)
                    #print "M_new = ", M_new
                    #print "Q_after = ", Q
                    #print

                if not done_flag:
                    break

            if not done_flag:
                break

    ## Return the results
    return Q, M
    def orthogonal_group(self, gens=None, is_finite=None):
        """
        Return the orthogonal group of this lattice as a matrix group.

        The elements are isometries of the ambient vector space
        which preserve this lattice. They are represented by
        matrices with respect to the standard basis.

        INPUT:

        - ``gens`` -- a list of matrices (default:``None``)
        - ``is_finite`` -- bool (default: ``None``) If set to ``True``,
          then the group is placed in the category of finite groups. Sage does not check this.

        OUTPUT:

        The matrix group generated by ``gens``.
        If ``gens`` is not specified, then generators of the full
        orthogonal group of this lattice are computed. They are
        continued as the identity on the orthogonal complement of
        the lattice in its ambient space. Currently, we can only
        compute the orthogonal group for positive definite lattices.

        EXAMPLES::

            sage: A4 = IntegralLattice("A4")
            sage: Aut = A4.orthogonal_group()
            sage: Aut
            Group of isometries with 5 generators (
            [-1  0  0  0]  [0 0 0 1]  [-1 -1 -1  0]  [ 1  0  0  0]  [ 1  0  0  0]
            [ 0 -1  0  0]  [0 0 1 0]  [ 0  0  0 -1]  [-1 -1 -1 -1]  [ 0  1  0  0]
            [ 0  0 -1  0]  [0 1 0 0]  [ 0  0  1  1]  [ 0  0  0  1]  [ 0  0  1  1]
            [ 0  0  0 -1], [1 0 0 0], [ 0  1  0  0], [ 0  0  1  0], [ 0  0  0 -1]
            )

        The group acts from the right on the lattice and its discriminant group::

            sage: x = A4.an_element()
            sage: g = Aut.an_element()
            sage: g
            [ 1  1  1  0]
            [ 0  0 -1  0]
            [ 0  0  1  1]
            [ 0 -1 -1 -1]
            sage: x*g
            (1, 1, 1, 0)
            sage: (x*g).parent()==A4
            True
            sage: (g*x).parent()
            Vector space of dimension 4 over Rational Field
            sage: y = A4.discriminant_group().an_element()
            sage: y*g
            (1)

        If the group is finite we can compute the usual things::

            sage: Aut.order()
            240
            sage: conj = Aut.conjugacy_classes_representatives()
            sage: len(conj)
            14
            sage: Aut.structure_description()   # optional - database_gap
            'C2 x S5'

        The lattice can live in a larger ambient space::

            sage: A2 = IntegralLattice(matrix.identity(3),Matrix(ZZ,2,3,[1,-1,0,0,1,-1]))
            sage: A2.orthogonal_group()
            Group of isometries with 3 generators (
            [-1/3  2/3  2/3]  [ 2/3  2/3 -1/3]  [1 0 0]
            [ 2/3 -1/3  2/3]  [ 2/3 -1/3  2/3]  [0 0 1]
            [ 2/3  2/3 -1/3], [-1/3  2/3  2/3], [0 1 0]
            )

        It can be negative definite as well::

            sage: A2m = IntegralLattice(-Matrix(ZZ,2,[2,1,1,2]))
            sage: G = A2m.orthogonal_group()
            sage: G.order()
            12

        If the lattice is indefinite, sage does not know how to compute generators.
        Can you teach it?::

            sage: U = IntegralLattice(Matrix(ZZ,2,[0,1,1,0]))
            sage: U.orthogonal_group()
            Traceback (most recent call last):
            ...
            NotImplementedError: currently, we can only compute generators for orthogonal groups over definite lattices.

        But we can define subgroups::

            sage: S = IntegralLattice(Matrix(ZZ,2,[2, 3, 3, 2]))
            sage: f = Matrix(ZZ,2,[0,1,-1,3])
            sage: S.orthogonal_group([f])
            Group of isometries with 1 generator (
            [ 0  1]
            [-1  3]
            )

        TESTS:

        We can handle the trivial group::

            sage: S = IntegralLattice(Matrix(ZZ,2,[2, 3, 3, 2]))
            sage: S.orthogonal_group([])
            Group of isometries with 1 generator (
            [1 0]
            [0 1]
            )
        """
        from sage.categories.groups import Groups
        from sage.groups.matrix_gps.isometries import GroupOfIsometries
        sig = self.signature_pair()
        if gens is None:
            gens = []
            if sig[1]==0 or sig[0]==0: #definite
                from sage.quadratic_forms.quadratic_form import QuadraticForm
                is_finite = True
                # Compute transformation matrix to the ambient module.
                L = self.overlattice(self.ambient_module().gens())
                Orthogonal = L.orthogonal_complement(self)
                B = self.basis_matrix().stack(Orthogonal.basis_matrix())
                if sig[0] == 0: #negative definite
                    q = QuadraticForm(ZZ, -2*self.gram_matrix())
                else:    # positve definite
                    q = QuadraticForm(ZZ, 2*self.gram_matrix())
                identity = matrix.identity(Orthogonal.rank())
                for g in q.automorphism_group().gens():
                    g = g.matrix().T
                    # We continue g as identity on the orthogonal complement.
                    g = matrix.block_diagonal([g, identity])
                    g = B.inverse()*g*B
                    gens.append(g)
            else: #indefinite
                raise NotImplementedError(
                    "currently, we can only compute generators "
                    "for orthogonal groups over definite lattices.")
        deg = self.degree()
        base = self.ambient_vector_space().base_ring()
        inv_bil = self.inner_product_matrix()
        if is_finite:
            cat = Groups().Finite()
        else:
            cat = Groups()
        D = self.discriminant_group()
        G = GroupOfIsometries(deg,
                              base,
                              gens,
                              inv_bil,
                              category=cat,
                              invariant_submodule=self,
                              invariant_quotient_module=D)
        return G
Esempio n. 15
0
def weight_two_basis_from_theta_blocks(N,
                                       prec,
                                       dim,
                                       jacobiforms=None,
                                       verbose=False):
    r"""
    Look for theta blocks of weight two and given index among the infinite families of weight two theta blocks associated to the root systems A_4, B_2+G_2, A_1+B_3, A_1+C_3

    This is not meant to be called directly. Use JacobiForms(N).basis(2) with the optional parameter try_theta_blocks = True.

    INPUT:
    - ``N`` -- the index
    - ``prec`` -- precision
    - ``dim`` -- the dimension of the space of Jacobi forms.
    - ``jacobiforms`` -- a JacobiForms instance for this index. (If none is provided then we construct one now.)
    - ``verbose`` -- boolean (default False); if True then we comment on the computations.

    OUTPUT: a list of JacobiForm's
    """
    if not jacobiforms:
        jacobiforms = JacobiForms(N)
    rank = 0
    #the four families of weight two theta blocks from root lattices
    thetablockQ_1 = QuadraticForm(
        matrix([[4, 3, 2, 1], [3, 6, 4, 2], [2, 4, 6, 3], [1, 2, 3, 4]]))
    thetablockQ_2 = QuadraticForm(
        matrix([[24, 12, 0, 0], [12, 8, 0, 0], [0, 0, 12, 6], [0, 0, 6, 6]]))
    thetablockQ_3 = QuadraticForm(
        matrix([[4, 0, 0, 0], [0, 20, 10, 20], [0, 10, 10, 20],
                [0, 20, 20, 60]]))
    thetablockQ_4 = QuadraticForm(
        matrix([[4, 0, 0, 0], [0, 8, 8, 4], [0, 8, 16, 8], [0, 4, 8, 6]]))
    thetablock_tuple = thetablockQ_1, thetablockQ_2, thetablockQ_3, thetablockQ_4
    from .jacobi_forms_class import theta_block
    thetablock_1 = lambda a, b, c, d, prec: theta_block(
        [a, a + b, a + b + c, a + b + c + d, b, b + c, b + c + d, c, c + d, d],
        -6,
        prec,
        jacobiforms=jacobiforms)
    thetablock_2 = lambda a, b, c, d, prec: theta_block([
        a, 3 * a + b, 3 * a + b + b, a + a + b, a + b, b, c + c, c + c + d, 2 *
        (c + d), d
    ],
                                                        -6,
                                                        prec,
                                                        jacobiforms=jacobiforms
                                                        )
    thetablock_3 = lambda a, b, c, d, prec: theta_block([
        a + a, b + b, b + b + c, 2 * (b + c + d + d), b + b + c + d + d, b + b
        + c + 4 * d, c, c + d + d, c + 4 * d, d + d
    ],
                                                        -6,
                                                        prec,
                                                        jacobiforms=jacobiforms
                                                        )
    thetablock_4 = lambda a, b, c, d, prec: theta_block([
        a + a, b, b + b + c + c + d, b + c, b + c + c + d, b + c + d, c, c + c
        + d, c + d, d
    ],
                                                        -6,
                                                        prec,
                                                        jacobiforms=jacobiforms
                                                        )
    thetablocks = thetablock_1, thetablock_2, thetablock_3, thetablock_4
    basis = []
    basis_vectors = []
    pivots = []
    div_N = divisors(N)
    div_N.reverse()
    for i, Q in enumerate(thetablock_tuple):
        v_list = Q.short_vector_list_up_to_length(N + 1, up_to_sign_flag=True)
        if verbose:
            print(
                'I am looking through the theta block family of the root system %s.'
                % ['A_4', 'B_2+G_2', 'A_1+B_3', 'A_1+C_3'][i])
        for _d in div_N:
            prec_d = prec * (N // _d)
            for v in v_list[_d]:
                a, b, c, d = v
                try:
                    j = thetablocks[i](a, b, c, d, prec_d).hecke_V(N // _d)
                    old_rank = 0 + rank
                    basis, basis_vectors, pivots, rank = update_echelon_form_with(
                        j, basis, basis_vectors, pivots, rank, sage_one_eighth)
                    if verbose and old_rank < rank:
                        if i == 0:
                            L = [
                                abs(x)
                                for x in (a, a + b, a + b + c, a + b + c + d,
                                          b, b + c, b + c + d, c, c + d, d)
                            ]
                        elif i == 1:
                            L = [
                                abs(x) for x in (a, 3 * a + b, 3 * a + b + b,
                                                 a + a + b, a + b, b, c + c,
                                                 c + c + d, 2 * (c + d), d)
                            ]
                        elif i == 2:
                            L = [
                                abs(x)
                                for x in (a + a, b + b, b + b + c,
                                          2 * (b + c + d + d),
                                          b + b + c + d + d, b + b + c + 4 * d,
                                          c, c + d + d, c + 4 * d, d + d)
                            ]
                        else:
                            L = [
                                abs(x)
                                for x in (a + a, b, b + b + c + c + d, b + c,
                                          b + c + c + d, b + c + d, c,
                                          c + c + d, c + d, d)
                            ]
                        L.sort()
                        print('I found the theta block Th_' + str(L) +
                              ' / eta^6.')
                    if rank == dim:
                        return basis
                except ValueError:
                    pass
    if verbose:
        print(
            'I did not find enough theta blocks. Time to try something else.')
    return jacobiforms.basis(2, prec, try_theta_blocks=False, verbose=verbose)
Esempio n. 16
0
def weight_three_basis_from_theta_blocks(N,
                                         prec,
                                         dim,
                                         jacobiforms=None,
                                         verbose=False):
    r"""
    Look for theta blocks of weight three and given index among the infinite families of weight three theta blocks associated to the root systems B_3, C_3, A_2+A_3, 3A_2, 2A_1 + A_2 + B_2, 3A_1 + A_3, A_6, A_1+D_5.

    This is not meant to be called directly. Use JacobiForms(N).basis(3) with the optional parameter try_theta_blocks = True.

    INPUT:
    - ``N`` -- the index
    - ``prec`` -- precision
    - ``dim`` -- the dimension of the space of Jacobi forms.
    - ``jacobiforms`` -- a JacobiForms instance for this index. (If none is provided then we construct one now.)
    - ``verbose`` -- boolean (default False); if True then we comment on the computations.

    OUTPUT: a list of JacobiForm's
    """
    if not jacobiforms:
        jacobiforms = JacobiForms(N)
    rank = 0
    thetablockQ_1 = QuadraticForm(
        matrix([[20, 10, 20], [10, 10, 20], [20, 20, 60]]))  #B3
    thetablockQ_2 = QuadraticForm(matrix([[8, 8, 4], [8, 16, 8], [4, 8,
                                                                  6]]))  #C3
    thetablockQ_3 = QuadraticForm(
        matrix([[2, 1, 0, 0, 0], [1, 2, 0, 0, 0], [0, 0, 12, 4, 4],
                [0, 0, 4, 4, 4], [0, 0, 4, 4, 12]]))  #A2 + A3
    thetablockQ_4 = QuadraticForm(
        matrix([[2, 1, 0, 0, 0, 0], [1, 2, 0, 0, 0, 0], [0, 0, 2, 1, 0, 0],
                [0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 2, 1], [0, 0, 0, 0, 1,
                                                         2]]))  #3 A2
    thetablockQ_5 = QuadraticForm(
        matrix([[4, 0, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0], [0, 0, 2, 1, 0, 0],
                [0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 6, 6], [0, 0, 0, 0, 6,
                                                         12]]))  #2A1 + A2 + B2
    thetablockQ_6 = QuadraticForm(
        matrix([[4, 0, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0], [0, 0, 4, 0, 0, 0],
                [0, 0, 0, 12, 4, 4], [0, 0, 0, 4, 4, 4], [0, 0, 0, 4, 4,
                                                          12]]))  #3A1 + A3
    thetablockQ_7 = QuadraticForm(
        matrix([[6, 5, 4, 3, 2, 1], [5, 10, 8, 6, 4, 2], [4, 8, 12, 9, 6, 3],
                [3, 6, 9, 12, 8, 4], [2, 4, 6, 8, 10, 5], [1, 2, 3, 4, 5,
                                                           6]]))  #A6
    thetablockQ_8 = QuadraticForm(
        matrix([[4, 0, 0, 0, 0, 0], [0, 10, 6, 12, 8, 4], [0, 6, 10, 12, 8, 4],
                [0, 12, 12, 24, 16, 8], [0, 8, 8, 16, 16, 8],
                [0, 4, 4, 8, 8, 8]]))  #A1 + D5
    thetablock_tuple = thetablockQ_1, thetablockQ_2, thetablockQ_3, thetablockQ_4, thetablockQ_5, thetablockQ_6, thetablockQ_7, thetablockQ_8
    args1 = lambda b, c, d: [
        b + b, b + b + c, 2 * (b + c + d + d), b + b + c + d + d, b + b + c + 4
        * d, c, c + d + d, c + 4 * d, d + d
    ]
    args2 = lambda b, c, d: [
        b, b + b + c + c + d, b + c, b + c + c + d, b + c + d, c, c + c + d, c
        + d, d
    ]
    args3 = lambda a, b, d, e, f: [
        a, a + b, b, d + d, e, d + d + e, f + f, e + f + f, d + d + e + f + f
    ]
    args4 = lambda a, b, c, d, e, f: [a, a + b, b, c, c + d, d, e, e + f, f]
    args5 = lambda a, b, c, d, e, f: [
        a + a, b + b, c, c + d, d, e, f + f, e + f + f, e + e + f + f
    ]
    args6 = lambda a, b, c, d, e, f: [
        a + a, b + b, c + c, d + d, e, d + d + e, f + f, e + f + f, d + d + e +
        f + f
    ]
    args7 = lambda a, b, c, d, e, f: [
        a, a + b, a + b + c, a + b + c + d, a + b + c + d + e, a + b + c + d +
        e + f, b, b + c, b + c + d, b + c + d + e, b + c + d + e + f, c, c + d,
        c + d + e, c + d + e + f, d, d + e, d + e + f, e, e + f, f
    ]
    args8 = lambda a, b, c, d, e, f: [
        a + a, b, c, d, b + d, c + d, b + c + d, e, d + e, b + d + e, c + d +
        e, b + c + d + e, b + c + 2 * d + e, f, e + f, d + e + f, b + d + e +
        f, c + d + e + f, b + c + d + e + f, b + c + 2 * d + e + f, b + c + 2 *
        d + 2 * e + f
    ]
    args_tuple = args1, args2, args3, args4, args5, args6, args7, args8
    from .jacobi_forms_class import theta_block
    thetablock_1 = lambda b, c, d, prec: theta_block(
        args1(b, c, d), -3, prec, jacobiforms=jacobiforms)
    thetablock_2 = lambda b, c, d, prec: theta_block(
        args2(b, c, d), -3, prec, jacobiforms=jacobiforms)
    thetablock_3 = lambda a, b, d, e, f, prec: theta_block(
        args3(a, b, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_4 = lambda a, b, c, d, e, f, prec: theta_block(
        args4(a, b, c, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_5 = lambda a, b, c, d, e, f, prec: theta_block(
        args5(a, b, c, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_6 = lambda a, b, c, d, e, f, prec: theta_block(
        args6(a, b, c, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_7 = lambda a, b, c, d, e, f, prec: theta_block(
        args7(a, b, c, d, e, f), -15, prec, jacobiforms=jacobiforms)
    thetablock_8 = lambda a, b, c, d, e, f, prec: theta_block(
        args8(a, b, c, d, e, f), -15, prec, jacobiforms=jacobiforms)
    thetablocks = thetablock_1, thetablock_2, thetablock_3, thetablock_4, thetablock_5, thetablock_6, thetablock_7, thetablock_8
    basis = []
    basis_vectors = []
    pivots = []
    div_N = divisors(N)
    div_N.reverse()
    for i, Q in enumerate(thetablock_tuple):
        v_list = Q.short_vector_list_up_to_length(N + 1, up_to_sign_flag=True)
        if verbose:
            print(
                'I am looking through the theta block family of the root system %s.'
                % [
                    'B_3', 'C_3', 'A_2+A_3', '3A_2', '2A_1+A_2+B_2',
                    '3A_1 + A_3', 'A_6', 'A_1+D_5'
                ][i])
        for _d in div_N:
            prec_d = prec * (N // _d)
            for v in v_list[_d]:
                if all(a for a in args_tuple[i](*v)):
                    try:
                        j = thetablocks[i](*(list(v) + [prec])).hecke_V(N //
                                                                        _d)
                        old_rank = 0 + rank
                        basis, basis_vectors, pivots, rank = update_echelon_form_with(
                            j, basis, basis_vectors, pivots, rank,
                            sage_one_eighth)
                        if verbose and old_rank < rank:
                            L = [abs(x) for x in args_tuple[i](*v)]
                            L.sort()
                            if _d == N:
                                print('I found the theta block Th_' + str(L) +
                                      [' / eta^3.', ' / eta^15.'][i >= 6])
                            else:
                                print(
                                    'I applied the Hecke operator V_%d to the theta block Th_'
                                    % (N // _d) + str(L) +
                                    [' / eta^3.', ' / eta^15.'][i >= 6])
                        if rank == dim:
                            return basis
                    except ValueError:
                        pass
    if verbose:
        print(
            'I did not find enough theta blocks. Time to try something else.')
    return jacobiforms.basis(2, prec, try_theta_blocks=False, verbose=verbose)