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()
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
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()
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
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