def __init__(self, B): r""" Initialize the ComputeMinimalPolynomials class. INPUT: - ``B`` -- a square matrix TESTS:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: ComputeMinimalPolynomials(matrix([[1, 2]])) Traceback (most recent call last): ... TypeError: square matrix required """ from sage.rings.polynomial.polynomial_ring import polygen super(ComputeMinimalPolynomials, self).__init__() if not B.is_square(): raise TypeError("square matrix required") self._B = B self._D = B.base_ring() X = polygen(self._D) adjoint = (X - B).adjoint() d = B.nrows()**2 b = matrix(d, 1, adjoint.list()) self.chi_B = B.charpoly(X) self.mu_B = B.minimal_polynomial() self._A = matrix.block([[b , -self.chi_B*matrix.identity(d)]]) self._DX = X.parent() self._cache = {}
def __init__(self, B): r""" Initialize the ComputeMinimalPolynomials class. INPUT: - ``B`` -- a square matrix TESTS:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: ComputeMinimalPolynomials(matrix([[1, 2]])) Traceback (most recent call last): ... TypeError: square matrix required """ from sage.rings.polynomial.polynomial_ring import polygen super(ComputeMinimalPolynomials, self).__init__() if not B.is_square(): raise TypeError("square matrix required") self._B = B self._D = B.base_ring() X = polygen(self._D) adjoint = (X - B).adjoint() d = B.nrows()**2 b = matrix(d, 1, adjoint.list()) self.chi_B = B.charpoly(X) self.mu_B = B.minimal_polynomial() self._A = matrix.block([[b, -self.chi_B * matrix.identity(d)]]) self._DX = X.parent() self._cache = {}
def _symmetric_form_matrix(self): r""" Return the matrix for the symmetric form `( | )` in the weight lattice basis. Let `A` be a symmetrizable Cartan matrix with symmetrizer `D`,. This returns the matrix `M^t DA M`, where `M` is dependent upon the type given below. In finite types, `M` is the inverse of the Cartan matrix. In affine types, `M` takes the basis `(\Lambda_0, \Lambda_1, \ldots, \Lambda_r, \delta)` to `(\alpha_0, \ldots, \alpha_r, \Lambda_0)` where `r` is the rank of ``self``. This is used in computing the symmetric form for affine root systems. EXAMPLES:: sage: P = RootSystem(['C',2]).weight_lattice() sage: P._symmetric_form_matrix [1 1] [1 2] sage: P = RootSystem(['C',2,1]).weight_lattice() sage: P._symmetric_form_matrix [0 0 0 1] [0 1 1 1] [0 1 2 1] [1 1 1 0] sage: P = RootSystem(['A',4,2]).weight_lattice() sage: P._symmetric_form_matrix [ 0 0 0 1/2] [ 0 2 2 1] [ 0 2 4 1] [1/2 1 1 0] """ from sage.matrix.constructor import matrix ct = self.cartan_type() cm = ct.cartan_matrix() if cm.det() != 0: cm_inv = cm.inverse() diag = cm.is_symmetrizable(True) return cm_inv.transpose() * matrix.diagonal(diag) if not ct.is_affine(): raise ValueError("only implemented for affine types when the" " Cartan matrix is singular") r = ct.rank() a = ct.a() # Determine the change of basis matrix # La[0], ..., La[r], delta -> al[0], ..., al[r], La[0] M = cm.stack( matrix([1] + [0]*(r-1)) ) M = matrix.block([[ M, matrix([[1]] + [[0]]*r) ]]) M = M.inverse() if a[0] != 1: from sage.rings.all import QQ S = matrix([~a[0]]+[0]*(r-1)) A = cm.symmetrized_matrix().change_ring(QQ).stack(S) else: A = cm.symmetrized_matrix().stack(matrix([1]+[0]*(r-1))) A = matrix.block([[A, matrix([[~a[0]]] + [[0]]*r)]]) return M.transpose() * A * M
def p_minimal_polynomials(self, p, s_max=None): r""" Compute `(p^s)`-minimal polynomials `\nu_s` of `B`. Compute a finite subset `\mathcal{S}` of the positive integers and `(p^s)`-minimal polynomials `\nu_s` for `s \in \mathcal{S}`. For `0 < t \le \max \mathcal{S}`, a `(p^t)`-minimal polynomial is given by `\nu_s` where `s = \min\{ r \in \mathcal{S} \mid r\ge t \}`. For `t > \max \mathcal{S}`, the minimal polynomial of `B` is also a `(p^t)`-minimal polynomial. INPUT: - ``p`` -- a prime in `D` - ``s_max`` -- a positive integer (default: ``None``); if set, only `(p^s)`-minimal polynomials for ``s <= s_max`` are computed (see below for details) OUTPUT: A dictionary. Keys are the finite set `\mathcal{S}`, the values are the associated `(p^s)`-minimal polynomials `\nu_s`, `s \in \mathcal{S}`. Setting ``s_max`` only affects the output if ``s_max`` is at most `\max\mathcal{S}` where `\mathcal{S}` denotes the full set. In that case, only those `\nu_s` with ``s <= s_max`` are returned where ``s_max`` is always included even if it is not included in the full set `\mathcal{S}`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]]) sage: C = ComputeMinimalPolynomials(B) sage: C.p_minimal_polynomials(2) {2: x^2 + 3*x + 2} sage: set_verbose(1) sage: C = ComputeMinimalPolynomials(B) sage: C.p_minimal_polynomials(2) verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 1: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + x] [ x] [ 0] [ 1] [ 1] [ x + 1] [ 1] [ 0] [ 0] [ x + 1] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (x^2 + x) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^2 + x] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^2 + x verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) corresponding columns for G verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + x] [ x + 2] [ 0] [ 1] [ 1] [ x - 1] [ -1] [ 10] [ 0] [ x + 1] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 2: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [ 2*x^2 + 2*x x^2 + 3*x + 2] [ 2*x x + 4] [ 0 0] [ 2 1] [ 2 1] [ 2*x + 2 x + 1] [ 2 -1] [ 0 10] [ 0 0] [ 2*x + 2 x + 3] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (2*x^2 + 2*x, x^2 + 3*x + 2) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^2 + 3*x + 2] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^2 + 3*x + 2 verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) corresponding columns for G verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + 3*x + 2] [ x + 4] [ 0] [ 1] [ 1] [ x + 1] [ -1] [ 10] [ 0] [ x + 3] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 3: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^3 + 7*x^2 + 6*x x^3 + 3*x^2 + 2*x] [ x^2 + 8*x x^2 + 4*x] [ 0 0] [ x x + 4] [ x + 4 x] [ x^2 + 5*x + 4 x^2 + x] [ -x + 4 -x] [ 10*x 10*x] [ 0 0] [ x^2 + 7*x x^2 + 3*x + 4] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x] verbose 1 (...: compute_J_ideal.py, current_nu) [x^3 + 3*x^2 + 2*x] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^3 + 3*x^2 + 2*x {2: x^2 + 3*x + 2} sage: set_verbose(0) sage: C.p_minimal_polynomials(2, s_max=1) {1: x^2 + x} sage: C.p_minimal_polynomials(2, s_max=2) {2: x^2 + 3*x + 2} sage: C.p_minimal_polynomials(2, s_max=3) {2: x^2 + 3*x + 2} ALGORITHM: [HR2016]_, Algorithm 5. """ from sage.misc.misc import verbose from sage.rings.infinity import Infinity deg_mu = self.mu_B.degree() if s_max is None: s_max = Infinity if p in self._cache: (t, G, p_min_polys) = self._cache[p] if t < Infinity: nu = G[0][0] else: t = 0 p_min_polys = {} nu = self._DX(1) d = self._A.ncols() G = matrix(self._DX, d, 0) while t < s_max: deg_prev_nu = nu.degree() t += 1 verbose("------------------------------------------") verbose("p = %s, t = %s:" % (p, t)) verbose("Result of lifting:") verbose("F =") F = lifting(p, t, self._A, G) verbose(F) nu = self.current_nu(p, t, F[0], nu) verbose("nu = %s" % nu) if nu.degree() >= deg_mu: t = Infinity break if nu.degree() == deg_prev_nu: G = G.delete_columns([G.ncols() - 1]) del p_min_polys[t - 1] column = self.mccoy_column(p, t, nu) verbose("corresponding columns for G") verbose(column) G = matrix.block([[p * G, column]]) p_min_polys[t] = nu self._cache[p] = (t, G, p_min_polys) if s_max < t: result = { r: polynomial for r, polynomial in iteritems(p_min_polys) if r < s_max } next_t_candidates = list(r for r in p_min_polys if r >= s_max) if next_t_candidates: next_t = min(next_t_candidates) result.update({s_max: p_min_polys[next_t] % p**s_max}) return result return p_min_polys
def lifting(p, t, A, G): r""" Compute generators of `\{f \in D[X]^d \mid Af \equiv 0 \pmod{p^{t}}\}` given generators of `\{f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`. INPUT: - ``p`` -- a prime element of some principal ideal domain `D` - ``t`` -- a non-negative integer - ``A`` -- a `c\times d` matrix over `D[X]` - ``G`` -- a matrix over `D[X]`. The columns of `\begin{pmatrix}p^{t-1}I& G\end{pmatrix}` are generators of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`; can be set to ``None`` if ``t`` is zero OUTPUT: A matrix `F` over `D[X]` such that the columns of `\begin{pmatrix}p^tI&F&pG\end{pmatrix}` are generators of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^t}\}`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import lifting sage: X = polygen(ZZ, 'X') sage: A = matrix([[1, X], [2*X, X^2]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 [] sage: (A*G1 % 5).is_zero() True sage: A = matrix([[1, X, X^2], [2*X, X^2, 3*X^3]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 [3*X^2] [ X] [ 1] sage: (A*G1 % 5).is_zero() True sage: G2 = lifting(5, 2, A, G1); G2 [15*X^2 23*X^2] [ 5*X X] [ 5 1] sage: (A*G2 % 25).is_zero() True sage: lifting(5, 10, A, G1) Traceback (most recent call last): ... ValueError: A*G not zero mod 5^9 ALGORITHM: [HR2016]_, Algorithm 1. TESTS:: sage: A = matrix([[1, X], [X, X^2]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 Traceback (most recent call last): ... ValueError: [ 1 X|] [ X X^2|] does not have full rank. """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing DX = A.parent().base() (X, ) = DX.variable_names() D = DX.base_ring() d = A.ncols() c = A.nrows() if t == 0: return matrix(DX, d, 0) if not (A * G % p**(t - 1)).is_zero(): raise ValueError("A*G not zero mod %s^%s" % (p, t - 1)) R = A * G / p**(t - 1) R.change_ring(DX) AR = matrix.block([[A, R]]) Fp = D.quotient(p * D) FpX = PolynomialRing(Fp, name=X) ARb = AR.change_ring(FpX) (Db, Sb, Tb) = ARb.smith_form() #assert Sb * ARb * Tb == Db #assert all(i == j or Db[i, j].is_zero() # for i in range(Db.nrows()) # for j in range(Db.ncols())) r = Db.rank() if r != c: raise ValueError("{} does not have full rank.".format(ARb)) T = Tb.change_ring(DX) F1 = matrix.block([[p**(t - 1) * matrix.identity(d), G]]) * T F = F1.matrix_from_columns(range(r, F1.ncols())) assert (A * F % (p**t)).is_zero(), "A*F=%s" % (A * F) return F
def _symmetric_form_matrix(self): r""" Return the matrix for the symmetric form `( | )` in the weight lattice basis. Let `A` be a symmetrizable Cartan matrix with symmetrizer `D`,. This returns the matrix `M^t DA M`, where `M` is dependent upon the type given below. In finite types, `M` is the inverse of the Cartan matrix. In affine types, `M` takes the basis `(\Lambda_0, \Lambda_1, \ldots, \Lambda_r, \delta)` to `(\alpha_0, \ldots, \alpha_r, \Lambda_0)` where `r` is the rank of ``self``. This is used in computing the symmetric form for affine root systems. EXAMPLES:: sage: P = RootSystem(['C',2]).weight_lattice() sage: P._symmetric_form_matrix [1 1] [1 2] sage: P = RootSystem(['C',2,1]).weight_lattice() sage: P._symmetric_form_matrix [0 0 0 1] [0 1 1 1] [0 1 2 1] [1 1 1 0] sage: P = RootSystem(['A',4,2]).weight_lattice() sage: P._symmetric_form_matrix [ 0 0 0 1/2] [ 0 2 2 1] [ 0 2 4 1] [1/2 1 1 0] """ from sage.matrix.constructor import matrix ct = self.cartan_type() cm = ct.cartan_matrix() if cm.det() != 0: cm_inv = cm.inverse() diag = cm.is_symmetrizable(True) return cm_inv.transpose() * matrix.diagonal(diag) if not ct.is_affine(): raise ValueError("only implemented for affine types when the" " Cartan matrix is singular") r = ct.rank() a = ct.a() # Determine the change of basis matrix # La[0], ..., La[r], delta -> al[0], ..., al[r], La[0] M = cm.stack(matrix([1] + [0] * (r - 1))) M = matrix.block([[M, matrix([[1]] + [[0]] * r)]]) M = M.inverse() if a[0] != 1: from sage.rings.all import QQ S = matrix([~a[0]] + [0] * (r - 1)) A = cm.symmetrized_matrix().change_ring(QQ).stack(S) else: A = cm.symmetrized_matrix().stack(matrix([1] + [0] * (r - 1))) A = matrix.block([[A, matrix([[~a[0]]] + [[0]] * r)]]) return M.transpose() * A * M
def IntegralLatticeDirectSum(Lattices, return_embeddings=False): r""" Return the direct sum of the lattices contained in the list ``Lattices``. INPUT: - ``Lattices`` -- a list of lattices ``[L_1,...,L_n]`` - ``return_embeddings`` -- (default: ``False``) a boolean OUTPUT: The direct sum of the `L_i` if ``return_embeddings`` is ``False`` or the tuple ``[L, phi]`` where `L` is the direct sum of `L_i` and ``phi`` is the list of embeddings from `L_i` to `L`. EXAMPLES:: sage: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLatticeDirectSum sage: L1 = IntegralLattice("D4") sage: L2 = IntegralLattice("A3", [[1, 1, 2]]) sage: L3 = IntegralLattice("A4", [[0, 1, 1, 2], [1, 2, 3, 1]]) sage: Lattices = [L1, L2, L3] sage: IntegralLatticeDirectSum([L1, L2, L3]) Lattice of degree 11 and rank 7 over Integer Ring Basis matrix: [1 0 0 0 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0 0 0 0] [0 0 1 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0 0 0] [0 0 0 0 1 1 2 0 0 0 0] [0 0 0 0 0 0 0 0 1 1 2] [0 0 0 0 0 0 0 1 2 3 1] Inner product matrix: [ 2 -1 0 0 0 0 0 0 0 0 0] [-1 2 -1 -1 0 0 0 0 0 0 0] [ 0 -1 2 0 0 0 0 0 0 0 0] [ 0 -1 0 2 0 0 0 0 0 0 0] [ 0 0 0 0 2 -1 0 0 0 0 0] [ 0 0 0 0 -1 2 -1 0 0 0 0] [ 0 0 0 0 0 -1 2 0 0 0 0] [ 0 0 0 0 0 0 0 2 -1 0 0] [ 0 0 0 0 0 0 0 -1 2 -1 0] [ 0 0 0 0 0 0 0 0 -1 2 -1] [ 0 0 0 0 0 0 0 0 0 -1 2] sage: [L, phi] = IntegralLatticeDirectSum([L1, L2, L3], True) sage: LL3 = L.sublattice(phi[2].image().basis_matrix()) sage: L3.discriminant() == LL3.discriminant() True sage: x = L3([1, 2, 3, 1]) sage: phi[2](x).inner_product(phi[2](x)) == x.inner_product(x) True TESTS:: sage: IntegralLatticeDirectSum([IntegralLattice("D4")]) Lattice of degree 4 and rank 4 over Integer Ring Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] Inner product matrix: [ 2 -1 0 0] [-1 2 -1 -1] [ 0 -1 2 0] [ 0 -1 0 2] sage: L1 = IntegralLattice(2 * matrix.identity(2), [[1/2, 1/2]]) sage: L2 = IntegralLattice("A3", [[1, 1, 2]]) sage: [L, phi] = IntegralLatticeDirectSum([L1, L2], True) sage: L Lattice of degree 5 and rank 2 over Integer Ring Basis matrix: [1/2 1/2 0 0 0] [ 0 0 1 1 2] Inner product matrix: [ 2 0 0 0 0] [ 0 2 0 0 0] [ 0 0 2 -1 0] [ 0 0 -1 2 -1] [ 0 0 0 -1 2] """ for L in Lattices: if not isinstance(L, FreeQuadraticModule_integer_symmetric): raise ValueError("Lattices must be a list of lattices") N = len(Lattices) dims = [L_i.dimension() for L_i in Lattices] degrees = [L_i.degree() for L_i in Lattices] degree_tot = sum(degrees) sum_degree = [sum(degrees[:i]) for i in range(N + 1)] inner_product_list = [copy(L_i.inner_product_matrix()) for L_i in Lattices] IM = matrix.block_diagonal(inner_product_list) ambient = FreeQuadraticModule(ZZ, degree_tot, inner_product_matrix=IM) basis = [ matrix.block(1, 3, [ matrix.zero(dims[i], sum_degree[i]), Lattices[i].basis_matrix(), matrix.zero(dims[i], sum_degree[-1] - sum_degree[i + 1]) ]) for i in range(N) ] basis_matrix = matrix.block(N, 1, basis) ipm = ambient.inner_product_matrix() direct_sum = FreeQuadraticModule_integer_symmetric( ambient=ambient, basis=basis_matrix, inner_product_matrix=ipm, already_echelonized=False) if not return_embeddings: return direct_sum sum_dims = [sum(dims[:i]) for i in range(N + 1)] phi = [ Lattices[i].hom(direct_sum.basis()[sum_dims[i]:sum_dims[i + 1]]) for i in range(N) ] return [direct_sum, phi]
def abnormal_factor_system(L, Q, r, m, s, homogeneous=True): r""" Return a matrix for the linear system to find `Q` as a common factor in the abnormal polynomials of layer `m` in rank `r`. INPUT: - ``L`` -- the Lie algebra to do the computations in - ``Q`` -- the polynomial to look for as a common factor - ``r`` -- the rank of the Lie algebra - ``m`` -- the layer of abnormal polynomials to search - ``s`` -- the step to search in - ``homogeneous`` -- a boolean; if ``True`` only the highest order component of the matrix is computed. Otherwise the full system is computed. OUTPUT: A pair (L,A), where `L` is the ambient free Lie algebra quotient and `A` is a block matrix of coefficients of the linear system. The variables are ordered so that the first block has `\dim f_r^{s}` columns describing the covector and the rest are the auxiliary multiplier polynomial variables. """ PR = Q.parent() weights = PR.term_order().weights() # list all relevant monomials up to degree s-m-deg(Q) def monomial_list(deg): return reversed(list(WeightedIntegerVectors(deg, weights))) if homogeneous: mons = [tuple(mon) for mon in monomial_list(s - m)] smons = [PR.monomial(*mon) for mon in monomial_list(s - m - Q.degree())] # extract the maximal degree monomials of the polynomial Q qmons = [qm for qm in Q.monomials() if qm.degree() == Q.degree()] P = abnormal_polynomials(L, r, m, s) supp_basis = [X.leading_support() for X in L.graded_basis(s)] else: mons = [tuple(mon) for k in range(s - m + 1) for mon in monomial_list(k)] smons = [PR.monomial(*mon) for k in range(s - m - Q.degree() + 1) for mon in monomial_list(k)] qmons = Q.monomials() P = None for k in range(m, s + 1): Pk = abnormal_polynomials(L, r, m, k) if not P: # make a copy to not ruin cache P = deepcopy(Pk) continue for X, PX in Pk.items(): for Y, c in PX.items(): P[X][Y] = c supp_basis = [X.leading_support() for k in range(m, s + 1) for X in L.graded_basis(k)] def lie_elem_to_dict(X): mc = X.monomial_coefficients() return {supp_basis.index(m): c for m, c in mc.items()} # form the linear system as a sparse matrix typestr = "homogeneous" if homogeneous else "full" verbose("forming %s linear system in step %d" % (typestr, s)) covec_block = {} aux_blocks = {X: {} for X in P} i = 0 for mon in mons: pmon = PR.monomial(*mon) # S parameters are all coefficients of monomials sc = {} for qmon in qmons: q, r = pmon.quo_rem(qmon) if r.is_zero() and q.degree() <= s - m - Q.degree(): c = Q.monomial_coefficient(qmon) if q in smons: sc[smons.index(q)] = -c for X, PX in P.items(): # compute the coefficient of mon in P_X - S_X * Q # as a row vector of the coefficients of the P and S params # P parameters are the covector pc = PX.get(tuple(mon), L.zero()) for j, c in lie_elem_to_dict(pc).items(): covec_block[(i, j)] = c for j, c in sc.items(): aux_blocks[X][(i, j)] = c i += 1 numrows = i verbose("%d equations, %d covector vars, %d x %d auxiliary vars" % (numrows, len(supp_basis), len(P), len(smons))) R = PR.base_ring() covecmat = matrix(R, numrows, len(supp_basis), covec_block) auxmats = [matrix(R, numrows, len(smons), d) for d in aux_blocks.values()] return matrix.block(R, [covecmat] + auxmats, ncols=1 + len(auxmats))
def p_minimal_polynomials(self, p, s_max=None): r""" Compute `(p^s)`-minimal polynomials `\nu_s` of `B`. Compute a finite subset `\mathcal{S}` of the positive integers and `(p^s)`-minimal polynomials `\nu_s` for `s \in \mathcal{S}`. For `0 < t \le \max \mathcal{S}`, a `(p^t)`-minimal polynomial is given by `\nu_s` where `s = \min\{ r \in \mathcal{S} \mid r\ge t \}`. For `t > \max \mathcal{S}`, the minimal polynomial of `B` is also a `(p^t)`-minimal polynomial. INPUT: - ``p`` -- a prime in `D` - ``s_max`` -- a positive integer (default: ``None``); if set, only `(p^s)`-minimal polynomials for ``s <= s_max`` are computed (see below for details) OUTPUT: A dictionary. Keys are the finite set `\mathcal{S}`, the values are the associated `(p^s)`-minimal polynomials `\nu_s`, `s \in \mathcal{S}`. Setting ``s_max`` only affects the output if ``s_max`` is at most `\max\mathcal{S}` where `\mathcal{S}` denotes the full set. In that case, only those `\nu_s` with ``s <= s_max`` are returned where ``s_max`` is always included even if it is not included in the full set `\mathcal{S}`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]]) sage: C = ComputeMinimalPolynomials(B) sage: C.p_minimal_polynomials(2) {2: x^2 + 3*x + 2} sage: set_verbose(1) sage: C = ComputeMinimalPolynomials(B) sage: C.p_minimal_polynomials(2) verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 1: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + x] [ x] [ 0] [ 1] [ 1] [ x + 1] [ 1] [ 0] [ 0] [ x + 1] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (x^2 + x) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^2 + x] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^2 + x verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) corresponding columns for G verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + x] [ x + 2] [ 0] [ 1] [ 1] [ x - 1] [ -1] [ 10] [ 0] [ x + 1] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 2: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [ 2*x^2 + 2*x x^2 + 3*x + 2] [ 2*x x + 4] [ 0 0] [ 2 1] [ 2 1] [ 2*x + 2 x + 1] [ 2 -1] [ 0 10] [ 0 0] [ 2*x + 2 x + 3] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (2*x^2 + 2*x, x^2 + 3*x + 2) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^2 + 3*x + 2] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^2 + 3*x + 2 verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) corresponding columns for G verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^2 + 3*x + 2] [ x + 4] [ 0] [ 1] [ 1] [ x + 1] [ -1] [ 10] [ 0] [ x + 3] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) p = 2, t = 3: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) Result of lifting: verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) F = verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) [x^3 + 7*x^2 + 6*x x^3 + 3*x^2 + 2*x] [ x^2 + 8*x x^2 + 4*x] [ 0 0] [ x x + 4] [ x + 4 x] [ x^2 + 5*x + 4 x^2 + x] [ -x + 4 -x] [ 10*x 10*x] [ 0 0] [ x^2 + 7*x x^2 + 3*x + 4] verbose 1 (...: compute_J_ideal.py, current_nu) ------------------------------------------ verbose 1 (...: compute_J_ideal.py, current_nu) (x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x) verbose 1 (...: compute_J_ideal.py, current_nu) Generators with (p^t)-generating property: verbose 1 (...: compute_J_ideal.py, current_nu) [x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x] verbose 1 (...: compute_J_ideal.py, current_nu) [x^3 + 3*x^2 + 2*x] verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials) nu = x^3 + 3*x^2 + 2*x {2: x^2 + 3*x + 2} sage: set_verbose(0) sage: C.p_minimal_polynomials(2, s_max=1) {1: x^2 + x} sage: C.p_minimal_polynomials(2, s_max=2) {2: x^2 + 3*x + 2} sage: C.p_minimal_polynomials(2, s_max=3) {2: x^2 + 3*x + 2} ALGORITHM: [HR2016]_, Algorithm 5. """ from sage.misc.misc import verbose from sage.rings.infinity import Infinity deg_mu = self.mu_B.degree() if s_max is None: s_max = Infinity if p in self._cache: (t, G, p_min_polys) = self._cache[p] if t < Infinity: nu = G[0][0] else: t = 0 p_min_polys = {} nu = self._DX(1) d = self._A.ncols() G = matrix(self._DX, d, 0) while t < s_max: deg_prev_nu = nu.degree() t += 1 verbose("------------------------------------------") verbose("p = %s, t = %s:" % (p, t)) verbose("Result of lifting:") verbose("F =") F = lifting(p, t, self._A, G) verbose(F) nu = self.current_nu(p, t, F[0], nu) verbose("nu = %s" % nu) if nu.degree() >= deg_mu: t = Infinity break if nu.degree() == deg_prev_nu: G = G.delete_columns([G.ncols() - 1]) del p_min_polys[t-1] column = self.mccoy_column(p, t, nu) verbose("corresponding columns for G") verbose(column) G = matrix.block([[p * G, column]]) p_min_polys[t] = nu self._cache[p] = (t, G, p_min_polys) if s_max < t: result = {r: polynomial for r, polynomial in iteritems(p_min_polys) if r < s_max} next_t_candidates = list(r for r in p_min_polys if r >= s_max) if next_t_candidates: next_t = min(next_t_candidates) result.update({s_max: p_min_polys[next_t] % p**s_max}) return result return p_min_polys
def lifting(p, t, A, G): r""" Compute generators of `\{f \in D[X]^d \mid Af \equiv 0 \pmod{p^{t}}\}` given generators of `\{f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`. INPUT: - ``p`` -- a prime element of some principal ideal domain `D` - ``t`` -- a non-negative integer - ``A`` -- a `c\times d` matrix over `D[X]` - ``G`` -- a matrix over `D[X]`. The columns of `\begin{pmatrix}p^{t-1}I& G\end{pmatrix}` are generators of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`; can be set to ``None`` if ``t`` is zero OUTPUT: A matrix `F` over `D[X]` such that the columns of `\begin{pmatrix}p^tI&F&pG\end{pmatrix}` are generators of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^t}\}`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import lifting sage: X = polygen(ZZ, 'X') sage: A = matrix([[1, X], [2*X, X^2]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 [] sage: (A*G1 % 5).is_zero() True sage: A = matrix([[1, X, X^2], [2*X, X^2, 3*X^3]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 [3*X^2] [ X] [ 1] sage: (A*G1 % 5).is_zero() True sage: G2 = lifting(5, 2, A, G1); G2 [15*X^2 23*X^2] [ 5*X X] [ 5 1] sage: (A*G2 % 25).is_zero() True sage: lifting(5, 10, A, G1) Traceback (most recent call last): ... ValueError: A*G not zero mod 5^9 ALGORITHM: [HR2016]_, Algorithm 1. TESTS:: sage: A = matrix([[1, X], [X, X^2]]) sage: G0 = lifting(5, 0, A, None) sage: G1 = lifting(5, 1, A, G0); G1 Traceback (most recent call last): ... ValueError: [ 1 X|] [ X X^2|] does not have full rank. """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing DX = A.parent().base() (X,) = DX.gens() D = DX.base_ring() d = A.ncols() c = A.nrows() if t == 0: return matrix(DX, d, 0) if not (A*G % p**(t-1)).is_zero(): raise ValueError("A*G not zero mod %s^%s" % (p, t-1)) R = A*G/p**(t-1) R.change_ring(DX) AR = matrix.block([[A, R]]) Fp = D.quotient(p*D) FpX = PolynomialRing(Fp, name=X) ARb = AR.change_ring(FpX) (Db, Sb, Tb) = ARb.smith_form() #assert Sb * ARb * Tb == Db #assert all(i == j or Db[i, j].is_zero() # for i in range(Db.nrows()) # for j in range(Db.ncols())) r = Db.rank() if r != c: raise ValueError("{} does not have full rank.".format(ARb)) T = Tb.change_ring(DX) F1 = matrix.block([[p**(t-1) * matrix.identity(d), G]])*T F = F1.matrix_from_columns(range(r, F1.ncols())) assert (A*F % (p**t)).is_zero(), "A*F=%s" % (A*F) return F
def IntegralLatticeDirectSum(Lattices, return_embeddings=False): r""" Return the direct sum of the lattices contained in the list ``Lattices``. INPUT: - ``Lattices`` -- a list of lattices ``[L_1,...,L_n]`` - ``return_embeddings`` -- (default: ``False``) a boolean OUTPUT: The direct sum of the `L_i` if ``return_embeddings`` is ``False`` or the tuple ``[L, phi]`` where `L` is the direct sum of `L_i` and ``phi`` is the list of embeddings from `L_i` to `L`. EXAMPLES:: sage: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLatticeDirectSum sage: L1 = IntegralLattice("D4") sage: L2 = IntegralLattice("A3", [[1, 1, 2]]) sage: L3 = IntegralLattice("A4", [[0, 1, 1, 2], [1, 2, 3, 1]]) sage: Lattices = [L1, L2, L3] sage: IntegralLatticeDirectSum([L1, L2, L3]) Lattice of degree 11 and rank 7 over Integer Ring Basis matrix: [1 0 0 0 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0 0 0 0] [0 0 1 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0 0 0] [0 0 0 0 1 1 2 0 0 0 0] [0 0 0 0 0 0 0 0 1 1 2] [0 0 0 0 0 0 0 1 2 3 1] Inner product matrix: [ 2 -1 0 0 0 0 0 0 0 0 0] [-1 2 -1 -1 0 0 0 0 0 0 0] [ 0 -1 2 0 0 0 0 0 0 0 0] [ 0 -1 0 2 0 0 0 0 0 0 0] [ 0 0 0 0 2 -1 0 0 0 0 0] [ 0 0 0 0 -1 2 -1 0 0 0 0] [ 0 0 0 0 0 -1 2 0 0 0 0] [ 0 0 0 0 0 0 0 2 -1 0 0] [ 0 0 0 0 0 0 0 -1 2 -1 0] [ 0 0 0 0 0 0 0 0 -1 2 -1] [ 0 0 0 0 0 0 0 0 0 -1 2] sage: [L, phi] = IntegralLatticeDirectSum([L1, L2, L3], True) sage: LL3 = L.sublattice(phi[2].image().basis_matrix()) sage: L3.discriminant() == LL3.discriminant() True sage: x = L3([1, 2, 3, 1]) sage: phi[2](x).inner_product(phi[2](x)) == x.inner_product(x) True TESTS:: sage: IntegralLatticeDirectSum([IntegralLattice("D4")]) Lattice of degree 4 and rank 4 over Integer Ring Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] Inner product matrix: [ 2 -1 0 0] [-1 2 -1 -1] [ 0 -1 2 0] [ 0 -1 0 2] sage: L1 = IntegralLattice(2 * matrix.identity(2), [[1/2, 1/2]]) sage: L2 = IntegralLattice("A3", [[1, 1, 2]]) sage: [L, phi] = IntegralLatticeDirectSum([L1, L2], True) sage: L Lattice of degree 5 and rank 2 over Integer Ring Basis matrix: [1/2 1/2 0 0 0] [ 0 0 1 1 2] Inner product matrix: [ 2 0 0 0 0] [ 0 2 0 0 0] [ 0 0 2 -1 0] [ 0 0 -1 2 -1] [ 0 0 0 -1 2] """ for L in Lattices: if not isinstance(L, FreeQuadraticModule_integer_symmetric): raise ValueError("Lattices must be a list of lattices") N = len(Lattices) dims = [L_i.dimension() for L_i in Lattices] degrees = [L_i.degree() for L_i in Lattices] dim_tot = sum(dims) degree_tot = sum(degrees) sum_degree = [sum(degrees[:i]) for i in range(N+1)] inner_product_list = [copy(L_i.inner_product_matrix()) for L_i in Lattices] IM = matrix.block_diagonal(inner_product_list) ambient = FreeQuadraticModule(ZZ, degree_tot, inner_product_matrix=IM) basis = [matrix.block(1, 3, [matrix.zero(dims[i], sum_degree[i]), Lattices[i].basis_matrix(), matrix.zero(dims[i], sum_degree[-1] - sum_degree[i+1]) ]) for i in range(N)] basis_matrix = matrix.block(N, 1, basis) ipm = ambient.inner_product_matrix() direct_sum = FreeQuadraticModule_integer_symmetric(ambient=ambient, basis=basis_matrix, inner_product_matrix=ipm, already_echelonized=False) if not return_embeddings: return direct_sum sum_dims = [sum(dims[:i]) for i in range(N+1)] phi = [Lattices[i].hom(direct_sum.basis()[sum_dims[i]:sum_dims[i+1]]) for i in range(N)] return [direct_sum, phi]