예제 #1
0
    def __init__(self, Lam):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: Lambda = RootSystem(['A',3,1]).weight_lattice(extended=true).fundamental_weights()
            sage: V = IntegrableRepresentation(Lambda[1]+Lambda[2]+Lambda[3])

        Some methods required by the category are not implemented::

            sage: TestSuite(V).run()  # known bug (#21387)
        """
        CategoryObject.__init__(self, base=ZZ, category=Modules(ZZ))

        self._Lam = Lam
        self._P = Lam.parent()
        self._Q = self._P.root_system.root_lattice()

        # Store some extra simple computations that appear in tight loops
        self._Lam_rho = self._Lam + self._P.rho()

        self._cartan_matrix = self._P.root_system.cartan_matrix()
        self._cartan_type = self._P.root_system.cartan_type()

        self._classical_rank = self._cartan_type.classical().rank()
        self._index_set = self._P.index_set()
        self._index_set_classical = self._cartan_type.classical().index_set()
        self._cminv = self._cartan_type.classical().cartan_matrix().inverse()

        self._ddict = {}
        self._mdict = {tuple(0 for i in self._index_set): 1}
        # Coerce a classical root into the root lattice Q
        from_cl_root = lambda h: self._Q._from_dict(h._monomial_coefficients)
        self._classical_roots = [
            from_cl_root(al) for al in self._Q.classical().roots()
        ]
        self._classical_positive_roots = [
            from_cl_root(al) for al in self._Q.classical().positive_roots()
        ]
        self._a = self._cartan_type.a()  # This is not cached
        self._ac = self._cartan_type.dual().a()  # This is not cached
        self._eps = {i: self._a[i] / self._ac[i] for i in self._index_set}
        E = Matrix.diagonal([self._eps[i] for i in self._index_set_classical])
        self._ip = (self._cartan_type.classical().cartan_matrix() *
                    E).inverse()

        # Extra data for the twisted cases
        if not self._cartan_type.is_untwisted_affine():
            self._classical_short_roots = frozenset(
                al for al in self._classical_roots
                if self._inner_qq(al, al) == 2)
예제 #2
0
    def __init__(self, Lam):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: Lambda = RootSystem(['A',3,1]).weight_lattice(extended=true).fundamental_weights()
            sage: V = IntegrableRepresentation(Lambda[1]+Lambda[2]+Lambda[3])
            sage: TestSuite(V).run()
        """
        CategoryObject.__init__(self, base=ZZ, category=Modules(ZZ))

        if not Lam.parent().cartan_type().is_affine() or not Lam.parent(
        )._extended:
            raise ValueError(
                "the parent of %s must be an extended affine root lattice" %
                Lam)
        self._Lam = Lam
        self._P = Lam.parent()
        self._Q = self._P.root_system.root_lattice()

        self._cartan_matrix = self._P.root_system.cartan_matrix()
        self._cartan_type = self._P.root_system.cartan_type()
        if not self._cartan_type.is_untwisted_affine():
            raise NotImplementedError(
                "integrable representations are only implemented for untwisted affine types"
            )
        self._classical_rank = self._cartan_type.classical().rank()
        self._index_set = self._P.index_set()
        self._index_set_classical = self._cartan_type.classical().index_set()
        self._cminv = self._cartan_type.classical().cartan_matrix().inverse()

        self._ddict = {}
        self._mdict = {tuple(0 for i in self._index_set): 1}
        # Coerce a classical root into the root lattice Q
        from_cl_root = lambda h: self._Q._from_dict(h._monomial_coefficients)
        self._classical_roots = [
            from_cl_root(al) for al in self._Q.classical().roots()
        ]
        self._classical_positive_roots = [
            from_cl_root(al) for al in self._Q.classical().positive_roots()
        ]
        self._a = self._cartan_type.a()  # This is not cached
        self._ac = self._cartan_type.dual().a()  # This is not cached
        self._eps = {i: self._a[i] / self._ac[i] for i in self._index_set}
        self._coxeter_number = sum(self._a)
        self._dual_coxeter_number = sum(self._ac)
        E = Matrix.diagonal([self._eps[i] for i in self._index_set_classical])
        self._ip = (self._cartan_type.classical().cartan_matrix() *
                    E).inverse()
예제 #3
0
def poly_dual_basis(P, poly_basis):
    r"""
    Return a collection of polynomials which are dual under the differential
    bilinear form to a given homogeneous collection

    INPUT:

    - ``P`` -- a polynomial ring
    - ``poly_basis`` -- a collection of polynomials in ``P`` which are
      homogeneous and linearly independent

    OUTPUT:

    - the dual basis of the polynomials in ``poly_basis`` in their span

    EXAMPLES:

        sage: P.<x, y> = PolynomialRing(QQ)
        sage: poly_basis = (1, x, x+y)
        sage: poly_dual_basis(P, poly_basis)
        [1, x - y, y]
        sage: poly_basis = (1, 2*x - y, x^2, x^2 + x*y)
        sage: poly_dual_basis(P, poly_basis)
        [1, 2/5*x - 1/5*y, 1/2*x^2 - x*y, x*y]
    """
    # recast poly_basis to ensure elements are all from P
    poly_basis = [P(p) for p in poly_basis]
    # compute max degree of basis polynomials for linear algebra computations
    deg = max([p.degree() for p in poly_basis])
    # construct polynomial free module for linear algebra computations
    monoms = Monomials(P, (0, deg))
    poly_module = PolynomialFreeModule(P, basis=monoms)
    # compute the values of the bilinear form <m|m> for basis monomials m
    bilinear_form_coeffs = []
    for b in poly_module.basis().keys():
        # each b is a monomial in P of degree at most deg
        b = P(b)
        bilinear_form_coeffs.append(prod(map(factorial, b.degrees())))
    # compute dual basis
    A = Matrix([poly_module(p).to_vector() for p in poly_basis])
    D = Matrix.diagonal(bilinear_form_coeffs, sparse=False)
    B = (A * D * A.transpose()).inverse()
    # reconstruct dual basis polynomials from corresponding vectors
    dual_basis = []
    for col in B.columns():
        q = sum([coeff * p for coeff, p in zip(col, poly_basis)])
        dual_basis.append(q)
    return dual_basis
예제 #4
0
    def __init__(self, Lam):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: Lambda = RootSystem(['A',3,1]).weight_lattice(extended=true).fundamental_weights()
            sage: V = IntegrableRepresentation(Lambda[1]+Lambda[2]+Lambda[3])

        Some methods required by the category are not implemented::

            sage: TestSuite(V).run()  # known bug (#21387)
        """
        CategoryObject.__init__(self, base=ZZ, category=Modules(ZZ))

        self._Lam = Lam
        self._P = Lam.parent()
        self._Q = self._P.root_system.root_lattice()

        # Store some extra simple computations that appear in tight loops
        self._Lam_rho = self._Lam + self._P.rho()

        self._cartan_matrix = self._P.root_system.cartan_matrix()
        self._cartan_type = self._P.root_system.cartan_type()

        self._classical_rank = self._cartan_type.classical().rank()
        self._index_set = self._P.index_set()
        self._index_set_classical = self._cartan_type.classical().index_set()
        self._cminv = self._cartan_type.classical().cartan_matrix().inverse()

        self._ddict = {}
        self._mdict = {tuple(0 for i in self._index_set): 1}
        # Coerce a classical root into the root lattice Q
        from_cl_root = lambda h: self._Q._from_dict(h._monomial_coefficients)
        self._classical_roots = [from_cl_root(al)
                                 for al in self._Q.classical().roots()]
        self._classical_positive_roots = [from_cl_root(al)
                                          for al in self._Q.classical().positive_roots()]
        self._a = self._cartan_type.a() # This is not cached
        self._ac = self._cartan_type.dual().a() # This is not cached
        self._eps = {i: self._a[i] / self._ac[i] for i in self._index_set}
        E = Matrix.diagonal([self._eps[i] for i in self._index_set_classical])
        self._ip = (self._cartan_type.classical().cartan_matrix()*E).inverse()

        # Extra data for the twisted cases
        if not self._cartan_type.is_untwisted_affine():
            self._classical_short_roots = frozenset(al for al in self._classical_roots
                                                    if self._inner_qq(al,al) == 2)
    def __init__(self, Lam):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: Lambda = RootSystem(['A',3,1]).weight_lattice(extended=true).fundamental_weights()
            sage: V = IntegrableRepresentation(Lambda[1]+Lambda[2]+Lambda[3])
            sage: TestSuite(V).run()
        """
        CategoryObject.__init__(self, base=ZZ, category=Modules(ZZ))

        if not Lam.parent().cartan_type().is_affine() or not Lam.parent()._extended:
            raise ValueError("the parent of %s must be an extended affine root lattice"%Lam)
        self._Lam = Lam
        self._P = Lam.parent()
        self._Q = self._P.root_system.root_lattice()

        self._cartan_matrix = self._P.root_system.cartan_matrix()
        self._cartan_type = self._P.root_system.cartan_type()
        if not self._cartan_type.is_untwisted_affine():
            raise NotImplementedError("integrable representations are only implemented for untwisted affine types")
        self._classical_rank = self._cartan_type.classical().rank()
        self._index_set = self._P.index_set()
        self._index_set_classical = self._cartan_type.classical().index_set()
        self._cminv = self._cartan_type.classical().cartan_matrix().inverse()

        self._ddict = {}
        self._mdict = {tuple(0 for i in self._index_set): 1}
        # Coerce a classical root into the root lattice Q
        from_cl_root = lambda h: self._Q._from_dict(h._monomial_coefficients)
        self._classical_roots = [from_cl_root(al)
                                 for al in self._Q.classical().roots()]
        self._classical_positive_roots = [from_cl_root(al)
                                          for al in self._Q.classical().positive_roots()]
        self._a = self._cartan_type.a() # This is not cached
        self._ac = self._cartan_type.dual().a() # This is not cached
        self._eps = {i: self._a[i] / self._ac[i] for i in self._index_set}
        self._coxeter_number = sum(self._a)
        self._dual_coxeter_number = sum(self._ac)
        E = Matrix.diagonal([self._eps[i] for i in self._index_set_classical])
        self._ip = (self._cartan_type.classical().cartan_matrix()*E).inverse()
예제 #6
0
파일: normal_form.py 프로젝트: yazici/sage
def p_adic_normal_form(G, p, precision=None, partial=False, debug=False):
    r"""
    Return the transformation to the `p`-adic normal form of a symmetric matrix.

    Two ``p-adic`` quadratic forms are integrally equivalent if and only if
    their Gram matrices have the same normal form.

    Let `p` be odd and `u` be the smallest non-square modulo `p`.
    The normal form is a block diagonal matrix
    with blocks `p^k G_k` such that `G_k` is either the identity matrix or
    the identity matrix with the last diagonal entry replaced by `u`.

    If `p=2` is even, define the `1` by `1` matrices::

        sage: W1 = Matrix([1]); W1
        [1]
        sage: W3 = Matrix([3]); W3
        [3]
        sage: W5 = Matrix([5]); W5
        [5]
        sage: W7 = Matrix([7]); W7
        [7]

    and the `2` by `2` matrices::

        sage: U = Matrix(2,[0,1,1,0]); U
        [0 1]
        [1 0]
        sage: V = Matrix(2,[2,1,1,2]); V
        [2 1]
        [1 2]

    For `p=2` the partial normal form is a block diagonal matrix with blocks
    `2^k G_k` such that $G_k$ is a block diagonal matrix of the form
    `[U`, ... , `U`, `V`, `Wa`, `Wb]`
    where we allow `V`, `Wa`, `Wb` to be `0 \times 0` matrices.

    Further restrictions to the full normal form apply.
    We refer to [MirMor2009]_ IV Definition 4.6. for the details.

    INPUT:

    - ``G`` -- a symmetric `n` by `n` matrix in `\QQ`
    - ``p`` -- a prime number -- it is not checked whether it is prime
    - ``precision`` -- if not set, the minimal possible is taken
    - ``partial`` --  boolean (default: ``False``) if set, only the
      partial normal form is returned.

    OUTPUT:

    - ``D`` -- the jordan matrix over `\QQ_p`
    - ``B`` -- invertible transformation matrix over `\ZZ_p`,
      i.e, ``D = B * G * B^T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
        sage: D4 = Matrix(ZZ, 4, [2,-1,-1,-1,-1,2,0,0,-1,0,2,0,-1,0,0,2])
        sage: D4
        [ 2 -1 -1 -1]
        [-1  2  0  0]
        [-1  0  2  0]
        [-1  0  0  2]
        sage: D, B = p_adic_normal_form(D4, 2)
        sage: D
        [  2   1   0   0]
        [  1   2   0   0]
        [  0   0 2^2   2]
        [  0   0   2 2^2]
        sage: D == B * D4 * B.T
        True
        sage: A4 = Matrix(ZZ, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -1  2 -1]
        [ 0  0 -1  2]
        sage: D, B = p_adic_normal_form(A4, 2)
        sage: D
        [0 1 0 0]
        [1 0 0 0]
        [0 0 2 1]
        [0 0 1 2]

    We can handle degenerate forms::

        sage: A4_extended = Matrix(ZZ, 5, [2, -1, 0, 0, -1, -1, 2, -1, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 2, -1, -1, 0, 0, -1, 2])
        sage: D, B = p_adic_normal_form(A4_extended, 5)
        sage: D
        [1 0 0 0 0]
        [0 1 0 0 0]
        [0 0 1 0 0]
        [0 0 0 5 0]
        [0 0 0 0 0]

    and denominators::

        sage: A4dual = A4.inverse()
        sage: D, B = p_adic_normal_form(A4dual, 5)
        sage: D
        [5^-1    0    0    0]
        [   0    1    0    0]
        [   0    0    1    0]
        [   0    0    0    1]

    TESTS::

        sage: Z = Matrix(ZZ,0,[])
        sage: p_adic_normal_form(Z, 3)
        ([], [])
        sage: Z = matrix.zero(10)
        sage: p_adic_normal_form(Z, 3)[0] == 0
        True
    """
    p = ZZ(p)
    # input checks!!
    G0, denom = G._clear_denom()
    d = denom.valuation(p)
    r = G0.rank()
    if r != G0.ncols():
        U = G0.hermite_form(transformation=True)[1]
    else:
        U = G0.parent().identity_matrix()
    kernel = U[r:, :]
    nondeg = U[:r, :]

    # continue with the non-degenerate part
    G = nondeg * G * nondeg.T * p**d
    if precision is None:
        # in Zp(2) we have to calculate at least mod 8 for things to make sense.
        precision = G.det().valuation(p) + 4
    R = Zp(p, prec=precision, type='fixed-mod')
    G = G.change_ring(R)
    G.set_immutable()  # is not changed during computation
    D = copy(G)  # is transformed into jordan form
    n = G.ncols()
    # The trivial case
    if n == 0:
        return G.parent().zero(), G.parent().zero()
    # the transformation matrix is called B
    B = Matrix.identity(R, n)
    if p == 2:
        D, B = _jordan_2_adic(G)
    else:
        D, B = _jordan_odd_adic(G)
    D, B1 = _normalize(D)
    B = B1 * B
    # we have reached a normal form for p != 2
    # for p == 2 extra work is necessary
    if p == 2:
        D, B1 = _two_adic_normal_forms(D, partial=partial)
        B = B1 * B
    nondeg = B * nondeg
    B = nondeg.stack(kernel)
    D = Matrix.block_diagonal([D, Matrix.zero(kernel.nrows())])
    if debug:
        assert B.determinant().valuation() == 0  # B is invertible!
        if p == 2:
            assert B * G * B.T == Matrix.block_diagonal(
                collect_small_blocks(D))
        else:
            assert B * G * B.T == Matrix.diagonal(D.diagonal())
    return D / p**d, B
예제 #7
0
def p_adic_normal_form(G, p, precision=None, partial=False, debug=False):
    r"""
    Return the transformation to the `p`-adic normal form of a symmetric matrix.

    Two ``p-adic`` quadratic forms are integrally equivalent if and only if
    their Gram matrices have the same normal form.

    Let `p` be odd and `u` be the smallest non-square modulo `p`.
    The normal form is a block diagonal matrix
    with blocks `p^k G_k` such that `G_k` is either the identity matrix or
    the identity matrix with the last diagonal entry replaced by `u`.

    If `p=2` is even, define the `1` by `1` matrices::

        sage: W1 = Matrix([1]); W1
        [1]
        sage: W3 = Matrix([3]); W3
        [3]
        sage: W5 = Matrix([5]); W5
        [5]
        sage: W7 = Matrix([7]); W7
        [7]

    and the `2` by `2` matrices::

        sage: U = Matrix(2,[0,1,1,0]); U
        [0 1]
        [1 0]
        sage: V = Matrix(2,[2,1,1,2]); V
        [2 1]
        [1 2]

    For `p=2` the partial normal form is a block diagonal matrix with blocks
    `2^k G_k` such that $G_k$ is a block diagonal matrix of the form
    `[U`, ... , `U`, `V`, `Wa`, `Wb]`
    where we allow `V`, `Wa`, `Wb` to be `0 \times 0` matrices.

    Further restrictions to the full normal form apply.
    We refer to [MirMor2009]_ IV Definition 4.6. for the details.

    INPUT:

    - ``G`` -- a symmetric `n` by `n` matrix in `\QQ`
    - ``p`` -- a prime number -- it is not checked whether it is prime
    - ``precision`` -- if not set, the minimal possible is taken
    - ``partial`` --  boolean (default: ``False``) if set, only the
      partial normal form is returned.

    OUTPUT:

    - ``D`` -- the jordan matrix over `\QQ_p`
    - ``B`` -- invertible transformation matrix over `\ZZ_p`,
      i.e, ``D = B * G * B^T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
        sage: D4 = Matrix(ZZ, 4, [2,-1,-1,-1,-1,2,0,0,-1,0,2,0,-1,0,0,2])
        sage: D4
        [ 2 -1 -1 -1]
        [-1  2  0  0]
        [-1  0  2  0]
        [-1  0  0  2]
        sage: D, B = p_adic_normal_form(D4, 2)
        sage: D
        [  2   1   0   0]
        [  1   2   0   0]
        [  0   0 2^2   2]
        [  0   0   2 2^2]
        sage: D == B * D4 * B.T
        True
        sage: A4 = Matrix(ZZ, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -1  2 -1]
        [ 0  0 -1  2]
        sage: D, B = p_adic_normal_form(A4, 2)
        sage: D
        [0 1 0 0]
        [1 0 0 0]
        [0 0 2 1]
        [0 0 1 2]

    We can handle degenerate forms::

        sage: A4_extended = Matrix(ZZ, 5, [2, -1, 0, 0, -1, -1, 2, -1, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 2, -1, -1, 0, 0, -1, 2])
        sage: D, B = p_adic_normal_form(A4_extended, 5)
        sage: D
        [1 0 0 0 0]
        [0 1 0 0 0]
        [0 0 1 0 0]
        [0 0 0 5 0]
        [0 0 0 0 0]

    and denominators::

        sage: A4dual = A4.inverse()
        sage: D, B = p_adic_normal_form(A4dual, 5)
        sage: D
        [5^-1    0    0    0]
        [   0    1    0    0]
        [   0    0    1    0]
        [   0    0    0    1]

    TESTS::

        sage: Z = Matrix(ZZ,0,[])
        sage: p_adic_normal_form(Z, 3)
        ([], [])
        sage: Z = matrix.zero(10)
        sage: p_adic_normal_form(Z, 3)[0] == 0
        True
    """
    p = ZZ(p)
    # input checks!!
    G0, denom = G._clear_denom()
    d = denom.valuation(p)
    r = G0.rank()
    if r != G0.ncols():
        U  = G0.hermite_form(transformation=True)[1]
    else:
        U = G0.parent().identity_matrix()
    kernel = U[r:,:]
    nondeg = U[:r,:]

    # continue with the non-degenerate part
    G = nondeg * G * nondeg.T * p**d
    if precision == None:
        # in Zp(2) we have to calculate at least mod 8 for things to make sense.
        precision = G.det().valuation(p) + 4
    R = Zp(p, prec = precision, type = 'fixed-mod')
    G = G.change_ring(R)
    G.set_immutable() # is not changed during computation
    D = copy(G)    # is transformed into jordan form
    n = G.ncols()
    # The trivial case
    if n == 0:
        return G.parent().zero(), G.parent().zero()
    # the transformation matrix is called B
    B = Matrix.identity(R, n)
    if(p == 2):
        D, B = _jordan_2_adic(G)
    else:
        D, B = _jordan_odd_adic(G)
    D, B1 = _normalize(D)
    B = B1 * B
    # we have reached a normal form for p != 2
    # for p == 2 extra work is necessary
    if p==2:
        D, B1 = _two_adic_normal_forms(D, partial=partial)
        B = B1 * B
    nondeg = B * nondeg
    B = nondeg.stack(kernel)
    D = Matrix.block_diagonal([D, Matrix.zero(kernel.nrows())])
    if debug:
        assert B.determinant().valuation() == 0     # B is invertible!
        if p==2:
            assert B*G*B.T == Matrix.block_diagonal(collect_small_blocks(D))
        else:
            assert B*G*B.T == Matrix.diagonal(D.diagonal())
    return D/p**d, B