def HS_all_minimal_p(p, f, m=None, return_transformation=False): r""" Find a representative in each distinct `SL(2,\ZZ)` orbit with minimal `p`-resultant. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representatives in each distinct `SL(2,\ZZ)` orbit with minimal valuation with respect to the prime ``p`` is returned. The input ``f`` must have minimal resultant in its conjugacy class. INPUT: - ``p`` -- a prime - ``f`` -- dynamical system on the projective line with minimal resultant - ``m`` -- (optional) `2 \times 2` matrix associated with ``f`` - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model OUTPUT: List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^5 - 6^4*y^5, x^2*y^3]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal_p sage: HS_all_minimal_p(2, f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^5 - 1296*y^5 : x^2*y^3), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (4*x^5 - 162*y^5 : x^2*y^3)] sage: cl = HS_all_minimal_p(2, f, return_transformation=True) sage: all(f.conjugate(m) == g for g, m in cl) True """ count = 0 prev = 0 # no exclusions F = copy(f) res = ZZ(F.resultant()) vp = res.valuation(p) MS = MatrixSpace(ZZ, 2) if m is None: m = MS.one() if f.degree() % 2 == 0 or vp == 0: # there is only one orbit for even degree # nothing to do if the prime doesn't divide the resultant if return_transformation: return [[f, m]] else: return [f] to_do = [[F, m, prev]] # repns left to check reps = [[F, m]] # orbit representatives for f while to_do: F, m, prev = to_do.pop() # there are at most two directions preserving the resultant if prev == 0: count = 0 else: count = 1 if prev != 2: # [p,a,0,1] t = MS([1, 0, 0, p]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 == vp: count += 1 # we have a new representative reps.append([F1, m*t]) # need to check if it has any neighbors to_do.append([F1, m*t, 1]) for b in range(p): if not (b == 0 and prev == 1): t = MS([p, b, 0, 1]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 == vp: count += 1 # we have a new representative reps.append([F1, m*t]) # need to check if it has any neighbors to_do.append([F1, m*t, 2]) if count >= 2: # at most two neighbors break if return_transformation: return reps else: return [funct for funct, matr in reps]
################################################################################ from sage.misc.cachefunc import cached_method from sage.misc.misc import prod from congroup_generic import is_CongruenceSubgroup from congroup_gammaH import GammaH_class, is_GammaH, GammaH_constructor #from congroup_gamma0 import Gamma0_constructor -- circular! from arithgroup_element import ArithmeticSubgroupElement from sage.rings.all import ZZ, euler_phi as phi, moebius, divisors from sage.modular.dirichlet import DirichletGroup # Just for now until we make an SL_2 group type. from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.matrix_space import MatrixSpace Mat2Z = MatrixSpace(IntegerModRing(0),2) def is_Gamma1(x): """ Return True if x is a congruence subgroup of type Gamma1. EXAMPLES:: sage: from sage.modular.arithgroup.all import is_Gamma1 sage: is_Gamma1(SL2Z) False sage: is_Gamma1(Gamma1(13)) True sage: is_Gamma1(Gamma0(6)) False sage: is_Gamma1(GammaH(12, [])) # trick question!
def ToricCode(P, F): r""" Let `P` denote a list of lattice points in `\ZZ^d` and let `T` denote the set of all points in `(F^x)^d` (ordered in some fixed way). Put `n=|T|` and let `k` denote the dimension of the vector space of functions `V = \mathrm{Span}\{x^e \ |\ e \in P\}`. The associated toric code `C` is the evaluation code which is the image of the evaluation map .. math:: \mathrm{eval_T} : V \rightarrow F^n, where `x^e` is the multi-index notation (`x=(x_1,...,x_d)`, `e=(e_1,...,e_d)`, and `x^e = x_1^{e_1}...x_d^{e_d}`), where `eval_T (f(x)) = (f(t_1),...,f(t_n))`, and where `T=\{t_1,...,t_n\}`. This function returns the toric codes discussed in [J]_. INPUT: - ``P`` - all the integer lattice points in a polytope defining the toric variety. - ``F`` - a finite field. OUTPUT: Returns toric code with length n = , dimension k over field F. EXAMPLES:: sage: C = ToricCode([[0,0],[1,0],[2,0],[0,1],[1,1]],GF(7)) sage: C Linear code of length 36, dimension 5 over Finite Field of size 7 sage: C.minimum_distance() 24 sage: C = ToricCode([[-2,-2],[-1,-2],[-1,-1],[-1,0],[0,-1],[0,0],[0,1],[1,-1],[1,0]],GF(5)) sage: C Linear code of length 16, dimension 9 over Finite Field of size 5 sage: C.minimum_distance() 6 sage: C = ToricCode([ [0,0],[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[3,1],[3,2],[4,1]],GF(8,"a")) sage: C Linear code of length 49, dimension 11 over Finite Field in a of size 2^3 This is in fact a [49,11,28] code over GF(8). If you type next ``C.minimum_distance()`` and wait overnight (!), you should get 28. AUTHOR: - David Joyner (07-2006) REFERENCES: .. [J] D. Joyner, Toric codes over finite fields, Applicable Algebra in Engineering, Communication and Computing, 15, (2004), p. 63-79. """ from sage.combinat.all import Tuples mset = [x for x in F if x != 0] d = len(P[0]) pts = Tuples(mset, d).list() n = len(pts) # (q-1)^d k = len(P) e = P[0] B = [] for e in P: tmpvar = [prod([t[i]**e[i] for i in range(d)]) for t in pts] B.append(tmpvar) # now B0 *should* be a full rank matrix MS = MatrixSpace(F, k, n) return LinearCode(MS(B))
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite irreducible coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite irreducible coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite irreducible coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite irreducible coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) one = MS.one() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty E = UniversalCyclotomicField().gen if base_ring is UniversalCyclotomicField(): def val(x): if x == -1: return 2 else: return E(2 * x) + ~E(2 * x) elif is_QuadraticField(base_ring): def val(x): if x == -1: return 2 else: return base_ring( (E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x == -1: return 2 else: return base_ring(2 * cos(pi / x)) gens = [ one + MS([ SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]])) for j in range(n) ]) for i in range(n) ] # Make the generators dense matrices for consistency and speed gens = [g.dense_matrix() for g in gens] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() if all(self._matrix._matrix[i, j] == 2 for i in range(n) for j in range(i)): category = category.Commutative() if self._matrix.is_irreducible(): category = category.Irreducible() self._index_set_inverse = { i: ii for ii, i in enumerate(self._matrix.index_set()) } FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty if base_ring is UniversalCyclotomicField(): val = lambda x: base_ring.gen(2 * x) + ~base_ring.gen( 2 * x) if x != -1 else base_ring(2) else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: base_ring(2 * cos(pi / x) ) if x != -1 else base_ring(2) gens = [ MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[index_set[i], index_set[j]]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): r""" Return a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` parameterised by the weights k in ``klist``. The matrix `A_k` is the finite square matrix which occurs on input p, k, N and m in Step 6 of Algorithm 2 in [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices.) INPUT: - ``p`` -- prime at least 5. - ``N`` -- integer at least 2 and not divisible by p (level). - ``klist`` -- list of integers all congruent modulo (p-1) (the weights). - ``m`` -- positive integer. - ``modformsring`` -- ``True`` or ``False``. - ``bound`` -- (even) positive integer. - ``extra_data`` -- (default: ``False``) boolean. OUTPUT: - list of square matrices. If ``extra_data`` is ``True``, return also extra intermediate data, namely the matrix `E` in [Lau2011]_ and the integers ``elldash`` and ``mdash``. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import higher_level_UpGj sage: higher_level_UpGj(5,3,[4],2,true,6) [ [ 1 0 0 0 0 0] [ 0 1 0 0 0 0] [ 0 7 0 0 0 0] [ 0 5 10 20 0 0] [ 0 7 20 0 20 0] [ 0 1 24 0 20 0] ] sage: len(higher_level_UpGj(5,3,[4],2,true,6,extra_data=True)) 4 """ t = cputime() # Step 1 k0 = klist[0] % (p - 1) n = floor(((p + 1) / (p - 1)) * (m + 1)) elldash = compute_elldash(p, N, k0, n) elldashp = elldash * p mdash = m + ceil(n / (p + 1)) verbose("done step 1", t) t = cputime() # Steps 2 and 3 e, Ep1 = higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring, bound) ell = dimension(transpose(e)[0].parent()) S = e[0, 0].parent() verbose("done steps 2+3", t) t = cputime() # Step 4 R = Ep1.parent() G = compute_G(p, Ep1) Alist = [] verbose("done step 4a", t) t = cputime() for k in klist: k = ZZ(k) # convert to sage integer kdiv = k // (p - 1) Gkdiv = G**kdiv T = matrix(S, ell, elldash) for i in range(ell): ei = R(e[i].list()) Gkdivei = Gkdiv * ei # act by G^kdiv for j in range(0, elldash): T[i, j] = Gkdivei[p * j] verbose("done steps 4b and 5", t) t = cputime() # Step 6: solve T = AE using fact E is upper triangular. # Warning: assumes that T = AE (rather than pT = AE) has # a solution over Z/(p^mdash). This has always been the case in # examples computed by the author, see Note 3.1. A = matrix(S, ell, ell) verbose("solving a square matrix problem of dimension %s" % ell) verbose("elldash is %s" % elldash) for i in range(ell): Ti = T[i] for j in range(ell): ej = Ti.parent()([e[j][l] for l in range(elldash)]) ejleadpos = ej.nonzero_positions()[0] lj = ZZ(ej[ejleadpos]) A[i, j] = S(ZZ(Ti[j]) / lj) Ti = Ti - A[i, j] * ej Alist.append(MatrixSpace(Zmod(p**m), ell, ell)(A)) verbose("done step 6", t) if extra_data: return Alist, e, elldash, mdash else: return Alist
def CyclicCodeFromGeneratingPolynomial(n,g,ignore=True): r""" If g is a polynomial over GF(q) which divides `x^n-1` then this constructs the code "generated by g" (ie, the code associated with the principle ideal `gR` in the ring `R = GF(q)[x]/(x^n-1)` in the usual way). The option "ignore" says to ignore the condition that (a) the characteristic of the base field does not divide the length (the usual assumption in the theory of cyclic codes), and (b) `g` must divide `x^n-1`. If ignore=True, instead of returning an error, a code generated by `gcd(x^n-1,g)` is created. EXAMPLES:: sage: P.<x> = PolynomialRing(GF(3),"x") sage: g = x-1 sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C Linear code of length 4, dimension 3 over Finite Field of size 3 sage: P.<x> = PolynomialRing(GF(4,"a"),"x") sage: g = x^3+1 sage: C = codes.CyclicCodeFromGeneratingPolynomial(9,g); C Linear code of length 9, dimension 6 over Finite Field in a of size 2^2 sage: P.<x> = PolynomialRing(GF(2),"x") sage: g = x^3+x+1 sage: C = codes.CyclicCodeFromGeneratingPolynomial(7,g); C Linear code of length 7, dimension 4 over Finite Field of size 2 sage: C.gen_mat() [1 1 0 1 0 0 0] [0 1 1 0 1 0 0] [0 0 1 1 0 1 0] [0 0 0 1 1 0 1] sage: g = x+1 sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C Linear code of length 4, dimension 3 over Finite Field of size 2 sage: C.gen_mat() [1 1 0 0] [0 1 1 0] [0 0 1 1] On the other hand, CyclicCodeFromPolynomial(4,x) will produce a ValueError including a traceback error message: "`x` must divide `x^4 - 1`". You will also get a ValueError if you type :: sage: P.<x> = PolynomialRing(GF(4,"a"),"x") sage: g = x^2+1 followed by CyclicCodeFromGeneratingPolynomial(6,g). You will also get a ValueError if you type :: sage: P.<x> = PolynomialRing(GF(3),"x") sage: g = x^2-1 sage: C = codes.CyclicCodeFromGeneratingPolynomial(5,g); C Linear code of length 5, dimension 4 over Finite Field of size 3 followed by C = CyclicCodeFromGeneratingPolynomial(5,g,False), with a traceback message including "`x^2 + 2` must divide `x^5 - 1`". """ P = g.parent() x = P.gen() F = g.base_ring() p = F.characteristic() if not(ignore) and p.divides(n): raise ValueError('The characteristic %s must not divide %s'%(p,n)) if not(ignore) and not(g.divides(x**n-1)): raise ValueError('%s must divide x^%s - 1'%(g,n)) gn = GCD([g,x**n-1]) d = gn.degree() coeffs = Sequence(gn.list()) r1 = Sequence(coeffs+[0]*(n - d - 1)) Sn = SymmetricGroup(n) s = Sn.gens()[0] # assumes 1st gen of S_n is (1,2,...,n) rows = [permutation_action(s**(-i),r1) for i in range(n-d)] MS = MatrixSpace(F,n-d,n) return LinearCode(MS(rows))
def __call__(self, A, name='', **kwds): r""" Create an element of the homspace ``self`` from `A`. INPUT: - ``A`` -- one of the following: - an element of a Hecke algebra - a Hecke module morphism - a matrix - a list of elements of the codomain specifying the images of the basis elements of the domain. EXAMPLES:: sage: M = ModularForms(Gamma0(7), 4) sage: H = M.Hom(M) sage: H(M.hecke_operator(7)) Hecke module morphism T_7 defined by the matrix [ -7 0 0] [ 0 1 240] [ 0 0 343] Domain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... Codomain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... sage: H(H(M.hecke_operator(7))) Hecke module morphism T_7 defined by the matrix [ -7 0 0] [ 0 1 240] [ 0 0 343] Domain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... Codomain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... sage: H(matrix(QQ, 3, srange(9))) Hecke module morphism defined by the matrix [0 1 2] [3 4 5] [6 7 8] Domain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... Codomain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... TESTS: Make sure that the element is created correctly when the codomain is not the full module (related to :trac:`21497`):: sage: M = ModularSymbols(Gamma0(3),weight=22,sign=1) sage: S = M.cuspidal_subspace() sage: H = S.Hom(S) sage: H(S.gens()) Hecke module morphism defined by the matrix [1 0 0 0 0 0] [0 1 0 0 0 0] [0 0 1 0 0 0] [0 0 0 1 0 0] [0 0 0 0 1 0] [0 0 0 0 0 1] Domain: Modular Symbols subspace of dimension 6 of Modular Symbols space ... Codomain: Modular Symbols subspace of dimension 6 of Modular Symbols space ... sage: H.zero() in H True sage: H.one() in H True """ try: if A.parent() == self: A._set_parent(self) return A A = A.hecke_module_morphism() if A.parent() == self: A._set_parent(self) return A else: raise TypeError("unable to coerce A to self") except AttributeError: pass side = kwds.get("side", "left") if A in self.base_ring(): dim_dom = self.domain().rank() dim_codom = self.codomain().rank() if side == "left": MS = MatrixSpace(self.base_ring(), dim_dom, dim_codom) else: MS = MatrixSpace(self.base_ring(), dim_codom, dim_dom) if self.domain() == self.codomain(): A = A * MS.identity_matrix() elif A == 0: A = MS.zero() else: raise ValueError('scalars do not coerce to this homspace') elif isinstance(A, (list, tuple)): A = matrix([self.codomain().coordinate_vector(f) for f in A]) if side == "right": A = A.transpose() return HeckeModuleMorphism_matrix(self, A, name, side)
def __classcall_private__(cls, data=None, index_set=None, cartan_type=None, cartan_type_check=True): """ Normalize input so we can inherit from sparse integer matrix. .. NOTE:: To disable the Cartan type check, use the optional argument ``cartan_type_check = False``. EXAMPLES:: sage: C = CartanMatrix(['A',1,1]) sage: C2 = CartanMatrix([[2, -2], [-2, 2]]) sage: C3 = CartanMatrix(matrix([[2, -2], [-2, 2]]), [0, 1]) sage: C == C2 and C == C3 True TESTS: Check that :trac:`15740` is fixed:: sage: d = DynkinDiagram() sage: d.add_edge('a', 'b', 2) sage: d.index_set() ('a', 'b') sage: cm = CartanMatrix(d) sage: cm.index_set() ('a', 'b') """ # Special case with 0 args and kwds has Cartan type if cartan_type is not None and data is None: data = CartanType(cartan_type) if data is None: data = [] n = 0 index_set = tuple() cartan_type = None subdivisions = None elif isinstance(data, CartanMatrix): if index_set is not None: d = {a: index_set[i] for i, a in enumerate(data.index_set())} return data.relabel(d) return data else: dynkin_diagram = None subdivisions = None from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class if isinstance(data, DynkinDiagram_class): dynkin_diagram = data cartan_type = data._cartan_type else: try: cartan_type = CartanType(data) dynkin_diagram = cartan_type.dynkin_diagram() except (TypeError, ValueError): pass if dynkin_diagram is not None: n = dynkin_diagram.rank() index_set = dynkin_diagram.index_set() reverse = {a: i for i, a in enumerate(index_set)} data = {(i, i): 2 for i in range(n)} for (i, j, l) in dynkin_diagram.edge_iterator(): data[(reverse[j], reverse[i])] = -l else: M = matrix(data) if not is_generalized_cartan_matrix(M): raise ValueError( "the input matrix is not a generalized Cartan matrix") n = M.ncols() data = M.dict() subdivisions = M._subdivisions if index_set is None: index_set = tuple(range(n)) else: index_set = tuple(index_set) if len(index_set) != n and len(set(index_set)) != n: raise ValueError("the given index set is not valid") # We can do the Cartan type initialization later as this is not # a unqiue representation mat = typecall(cls, MatrixSpace(ZZ, n, sparse=True), data, False, False) # FIXME: We have to initialize the CartanMatrix part separately because # of the __cinit__ of the matrix. We should get rid of this workaround mat._CM_init(cartan_type, index_set, cartan_type_check) mat._subdivisions = subdivisions return mat
def CongruenceSubgroup_constructor(*args): r""" Attempt to create a congruence subgroup from the given data. The allowed inputs are as follows: - A :class:`~sage.groups.matrix_gps.matrix_group.MatrixGroup` object. This must be a group of matrices over `\ZZ / N\ZZ` for some `N`, with determinant 1, in which case the function will return the group of matrices in `SL(2, \ZZ)` whose reduction mod `N` is in the given group. - A list of matrices over `\ZZ / N\ZZ` for some `N`. The function will then compute the subgroup of `SL(2, \ZZ)` generated by these matrices, and proceed as above. - An integer `N` and a list of matrices (over any ring coercible to `\ZZ / N\ZZ`, e.g. over `\ZZ`). The matrices will then be coerced to `\ZZ / N\ZZ`. The function checks that the input G is valid. It then tests to see if `G` is the preimage mod `N` of some group of matrices modulo a proper divisor `M` of `N`, in which case it replaces `G` with this group before continuing. EXAMPLES:: sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroup_constructor as CS sage: CS(2, [[1,1,0,1]]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators ( [1 1] [0 1] ) sage: CS([matrix(Zmod(2), 2, [1,1,0,1])]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators ( [1 1] [0 1] ) sage: CS(MatrixGroup([matrix(Zmod(2), 2, [1,1,0,1])])) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators ( [1 1] [0 1] ) sage: CS(SL(2, 2)) Modular Group SL(2,Z) Some invalid inputs:: sage: CS(SU(2, 7)) Traceback (most recent call last): ... TypeError: Ring of definition must be Z / NZ for some N """ from sage.groups.matrix_gps.matrix_group import is_MatrixGroup if is_MatrixGroup(args[0]): G = args[0] elif isinstance(args[0], list): G = MatrixGroup(args[0]) elif args[0] in ZZ: M = MatrixSpace(Zmod(args[0]), 2) G = MatrixGroup([M(x) for x in args[1]]) R = G.matrix_space().base_ring() if not hasattr(R, "cover_ring") or R.cover_ring() != ZZ: raise TypeError("Ring of definition must be Z / NZ for some N") if not all(x.matrix().det() == 1 for x in G.gens()): raise ValueError("Group must be contained in SL(2, Z / N)") GG = _minimize_level(G) if GG in ZZ: from .all import Gamma return Gamma(GG) else: return CongruenceSubgroupFromGroup(GG)
def get_form(self, connection, cmatrices=None): r""" Return the form representing ``self`` with respect to the given connection ``connection``. INPUT: - ``connection`` -- connection to which the form should be associated to; this can be either a bundle connection as an instance of :class:`~sage.manifolds.differentiable.bundle_connection.BundleConnection` or, in case of the tensor bundle, an affine connection as an instance of :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection` - ``cmatrices`` -- (default: ``None``) a dictionary of curvature matrices with local frames as keys and curvature matrices as items; if ``None``, Sage tries to get the curvature matrices from the connection OUTPUT: - mixed form as an instance of :class:`~sage.manifolds.differentiable.mixed_form.MixedForm` representing the total characteristic class .. NOTE:: Be aware that depending on the characteristic class and complexity of the manifold, computation times may vary a lot. In addition, if not done before, the curvature form is computed from the connection, here. If this behaviour is not wanted and the curvature form is already known, please use the argument ``cmatrices``. EXAMPLES: Again, consider the Chern character on some 2-dimensional spacetime:: sage: M = Manifold(2, 'M', structure='Lorentzian') sage: X.<t,x> = M.chart() sage: E = M.vector_bundle(1, 'E', field='complex'); E Differentiable complex vector bundle E -> M of rank 1 over the base space 2-dimensional Lorentzian manifold M sage: e = E.local_frame('e') And again, we define the connection `\nabla^E` in terms of an electro-magnetic potential `A(t)`:: sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') sage: omega = M.one_form(name='omega') sage: A = function('A') sage: omega[1] = I*A(t) sage: omega.display() omega = I*A(t) dx sage: nab.set_connection_form(0, 0, omega) The Chern character is then given by:: sage: ch = E.characteristic_class('ChernChar'); ch Characteristic class ch of additive type associated to e^x on the Differentiable complex vector bundle E -> M of rank 1 over the base space 2-dimensional Lorentzian manifold M Inserting the connection, the result is a mixed differential form with a priori non-zero components in even degrees:: sage: ch_form = ch.get_form(nab); ch_form Mixed differential form ch(E, nabla^E) on the 2-dimensional Lorentzian manifold M sage: ch_form.display() ch(E, nabla^E) = ch_0(E, nabla^E) + zero + ch_1(E, nabla^E) sage: ch_form.display_expansion() ch(E, nabla^E) = [1] + [0] + [1/2*d(A)/dt/pi dt/\dx] Due to long computation times, the form is saved:: sage: ch_form is ch.get_form(nab) True """ from .bundle_connection import BundleConnection from .affine_connection import AffineConnection if not isinstance(connection, (AffineConnection, BundleConnection)): raise TypeError("argument must be an affine connection on the " "manifold or bundle connection on the vector " "bundle") if connection not in self._mixed_forms: if cmatrices is None: if self._class_type == 'Pfaffian': raise NotImplementedError( "At this stage, Pfaffian forms cannot be derived from " "(metric) connections. Please use the argument " "'cmatrices' to insert a dictionary of skew-symmetric " "curvature matrices by hand, instead.") cmatrices = {} for frame in self._get_min_frames( connection._coefficients.keys()): cmatrix = [[ connection.curvature_form(i, j, frame) for j in self._vbundle.irange() ] for i in self._vbundle.irange()] cmatrices[frame] = cmatrix # Prepare mixed form: name, latex_name = self._name, self._latex_name if name is not None and connection._name is not None: name += "(" + self._vbundle._name + ", " + connection._name + ")" if latex_name is not None and connection._latex_name is not None: latex_name += "(" + self._vbundle._latex_name + ", " + \ connection._latex_name + ")" res = self._base_space.mixed_form(name=name, latex_name=latex_name) # BEGIN computation: from sage.matrix.matrix_space import MatrixSpace for frame, cmatrix in cmatrices.items(): # Define matrix space: dom = frame._domain alg = dom.mixed_form_algebra() mspace = MatrixSpace(alg, self._rank) # Insert "normalized" curvature matrix into polynomial: cmatrix = mspace(cmatrix) # convert curvature matrix ncmatrix = self._normalize_matrix(cmatrix) rmatrix = self._insert_in_polynomial(ncmatrix) # Compute classes: if self._class_type == 'additive': rst = rmatrix.trace() # mixed form elif self._class_type == 'multiplicative': rst = rmatrix.det() # mixed form elif self._class_type == 'Pfaffian': rst = rmatrix.pfaffian() # mixed form # Set restriction: res.set_restriction(rst) # END of computation # # Preparation to name each homogeneous component; only even (or in # the real case, by four divisible) degrees are non-zero: if self._class_type == 'Pfaffian': deg_dist = self._rank elif self._vbundle._field_type == 'real': deg_dist = 4 elif self._vbundle._field_type == 'complex': deg_dist = 2 else: # You never know... deg_dist = 1 # Now, define the name for each form: for k in res.irange(): if k % deg_dist != 0 or (self._class_type == 'Pfaffian' and k == 0): res[k] = 0 # this form is zero anyway else: # String representation: if self._name is not None: name = self._name + "_" + str(k // deg_dist) + \ "(" + self._vbundle._name if connection._name is not None: name += ", " + connection._name name += ")" # LaTeX name: if self._latex_name is not None: latex_name = self._latex_name + \ r"_{" + str(k // deg_dist) + r"}" + \ r"(" + self._vbundle._latex_name if connection._latex_name is not None: latex_name += r", " + connection._latex_name latex_name += r")" # Set name: res[k].set_name(name=name, latex_name=latex_name) # Add the result to the dictionary: self._mixed_forms[connection] = res return self._mixed_forms[connection]
def hecke_matrix(self, L): r""" Return the `L^{\text{th}}` Hecke matrix. INPUT: - ``self`` -- SupersingularModule object - ``L`` -- integer, positive OUTPUT: - matrix -- sparse integer matrix EXAMPLES: This example computes the action of the Hecke operator `T_2` on the module of supersingular points on `X_0(1)/F_{37}`:: sage: S = SupersingularModule(37) sage: M = S.hecke_matrix(2) sage: M [1 1 1] [1 0 2] [1 2 0] This example computes the action of the Hecke operator `T_3` on the module of supersingular points on `X_0(1)/F_{67}`:: sage: S = SupersingularModule(67) sage: M = S.hecke_matrix(3) sage: M [0 0 0 0 2 2] [0 0 1 1 1 1] [0 1 0 2 0 1] [0 1 2 0 1 0] [1 1 0 1 0 1] [1 1 1 0 1 0] .. note:: The first list --- list_j --- returned by the supersingular_points function are the rows *and* column indexes of the above hecke matrices and its ordering should be kept in mind when interpreting these matrices. AUTHORS: - David Kohel -- [email protected] - Iftikhar Burhanuddin -- [email protected] """ if L in self.__hecke_matrices: return self.__hecke_matrices[L] SS, II = self.supersingular_points() if L == 2: # since T_2 gets computed as a side effect of computing the supersingular points return self.__hecke_matrices[2] Fp2 = self.__finite_field h = len(SS) R = self.base_ring() T_L = MatrixSpace(R, h)(0) S, X = Fp2['x'].objgen() for i in range(len(SS)): ss_i = SS[i] phi_L_in_x = Phi_polys(L, X, ss_i) rts = phi_L_in_x.roots() for r in rts: T_L[i, int(II[r[0]])] = r[1] self.__hecke_matrices[L] = T_L return T_L
def matrix(self, base_ring=None, side="left"): r""" Return the matrix of this morphism in the distinguished bases of the domain and codomain. INPUT: - ``base_ring`` -- a ring (default: ``None``, meaning the base ring of the codomain) - ``side`` -- "left" or "right" (default: "left") If ``side`` is "left", this morphism is considered as acting on the left; i.e. each column of the matrix represents the image of an element of the basis of the domain. The order of the rows and columns matches with the order in which the bases are enumerated. .. SEEALSO:: :func:`Modules.WithBasis.ParentMethods.module_morphism` EXAMPLES:: sage: X = CombinatorialFreeModule(ZZ, [1,2]); x = X.basis() sage: Y = CombinatorialFreeModule(ZZ, [3,4]); y = Y.basis() sage: phi = X.module_morphism(on_basis = {1: y[3] + 3*y[4], 2: 2*y[3] + 5*y[4]}.__getitem__, ....: codomain = Y) sage: phi.matrix() [1 2] [3 5] sage: phi.matrix(side="right") [1 3] [2 5] sage: phi.matrix().parent() Full MatrixSpace of 2 by 2 dense matrices over Integer Ring sage: phi.matrix(QQ).parent() Full MatrixSpace of 2 by 2 dense matrices over Rational Field The resulting matrix is immutable:: sage: phi.matrix().is_mutable() False The zero morphism has a zero matrix:: sage: Hom(X,Y).zero().matrix() [0 0] [0 0] .. TODO:: Add support for morphisms where the codomain has a different base ring than the domain:: sage: Y = CombinatorialFreeModule(QQ, [3,4]); y = Y.basis() sage: phi = X.module_morphism(on_basis = {1: y[3] + 3*y[4], 2: 2*y[3] + 5/2*y[4]}.__getitem__, ....: codomain = Y) sage: phi.matrix().parent() # todo: not implemented Full MatrixSpace of 2 by 2 dense matrices over Rational Field This currently does not work because, in this case, the morphism is just in the category of commutative additive groups (i.e. the intersection of the categories of modules over `\ZZ` and over `\QQ`):: sage: phi.parent().homset_category() Category of commutative additive semigroups sage: phi.parent().homset_category() # todo: not implemented Category of finite dimensional modules with basis over Integer Ring TESTS: Check that :trac:`23216` is fixed:: sage: X = CombinatorialFreeModule(QQ, []) sage: Y = CombinatorialFreeModule(QQ, [1,2,3]) sage: Hom(X,Y).zero().matrix() [] sage: Hom(X,Y).zero().matrix().parent() Full MatrixSpace of 3 by 0 dense matrices over Rational Field """ if base_ring is None: base_ring = self.codomain().base_ring() on_basis = self.on_basis() basis_keys = self.domain().basis().keys() from sage.matrix.matrix_space import MatrixSpace if isinstance(basis_keys, list): nrows = len(basis_keys) else: nrows = basis_keys.cardinality() MS = MatrixSpace(base_ring, nrows, self.codomain().dimension()) m = MS([on_basis(x)._vector_() for x in basis_keys]) if side == "left": m = m.transpose() m.set_immutable() return m
def HS_all_minimal(f, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representative in each distinct `SL(2,\ZZ)` orbit is returned. The input ``f`` must have minimal resultant in its conjugacy class. INPUT: - ``f`` -- dynamical system on the projective line with minimal resultant - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: List of pairs ``[f, m]``, where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal sage: HS_all_minimal(f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (9*x^3 - 12*y^3 : 9*x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (4*x^3 - 18*y^3 : 4*x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (36*x^3 - 6*y^3 : 36*x^2*y)] sage: HS_all_minimal(f, D=[3]) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (9*x^3 - 12*y^3 : 9*x^2*y)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal sage: cl = HS_all_minimal(f, return_transformation=True) sage: all(f.conjugate(m) == g for g, m in cl) True """ MS = MatrixSpace(ZZ, 2) m = MS.one() F = copy(f) F.normalize_coordinates() if F.degree() == 1: raise ValueError("function must be degree at least 2") if f.degree() % 2 == 0: #there is only one orbit for even degree if return_transformation: return [[f, m]] else: return [f] if D is None: res = ZZ(F.resultant()) D = res.prime_divisors() M = [[F, m]] for p in D: # get p-orbits Mp = HS_all_minimal_p(p, F, m, return_transformation=True) # combine with previous orbits representatives M = [[g.conjugate(t), t*s] for g,s in M for G,t in Mp] if return_transformation: return M else: return [funct for funct, matr in M]
def QuaternionMatrixGroupGF3(): r""" The quaternion group as a set of `2\times 2` matrices over `GF(3)`. OUTPUT: A matrix group consisting of `2\times 2` matrices with elements from the finite field of order 3. The group is the quaternion group, the nonabelian group of order 8 that is not isomorphic to the group of symmetries of a square (the dihedral group `D_4`). .. note:: This group is most easily available via ``groups.matrix.QuaternionGF3()``. EXAMPLES: The generators are the matrix representations of the elements commonly called `I` and `J`, while `K` is the product of `I` and `J`. :: sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3 sage: Q = QuaternionMatrixGroupGF3() sage: Q.order() 8 sage: aye = Q.gens()[0]; aye [1 1] [1 2] sage: jay = Q.gens()[1]; jay [2 1] [1 1] sage: kay = aye*jay; kay [0 2] [1 0] TESTS:: sage: groups.matrix.QuaternionGF3() Matrix group over Finite Field of size 3 with 2 generators ( [1 1] [2 1] [1 2], [1 1] ) sage: Q = QuaternionMatrixGroupGF3() sage: QP = Q.as_permutation_group() sage: QP.is_isomorphic(QuaternionGroup()) True sage: H = DihedralGroup(4) sage: H.order() 8 sage: QP.is_abelian(), H.is_abelian() (False, False) sage: QP.is_isomorphic(H) False """ from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.matrix.matrix_space import MatrixSpace MS = MatrixSpace(FiniteField(3), 2) aye = MS([1,1,1,2]) jay = MS([2,1,1,1]) return MatrixGroup([aye, jay])
def __init__(self, type="shuffle"): self.type = type MS34 = MatrixSpace(SR,3,4) minimog_modulo11 = MS34([[0,3,infinity,2],[5,9,8,10],[4,1,6,7]]) minimog_shuffle = MS34([[6,3,0,9],[5,2,7,10],[4,1,8,11]]) if type == "shuffle": self.minimog = minimog_shuffle elif type == "modulo11": self.minimog = minimog_modulo11 else: raise ValueError("That Minimog type is not implemented.") # This initializes the variables in the game. MS34 = MatrixSpace(SR,3,4) A = self.minimog MS33 = MatrixSpace(SR,3,3) self.picture00 = MS33([[A[(1,0)],A[(2,3)],A[(0,1)]],[A[(2,2)],A[(1,1)],A[(2,0)]],[A[(0,3)],A[(1,3)],A[(1,2)]]]) ####### self.picture00 is the "picture at 6" self.picture02 = MS33([[A[(1,0)],A[(2,3)],A[(0,1)]],[A[(1,1)],A[(2,0)],A[(2,2)]],[A[(1,2)],A[(0,3)],A[(1,3)]]]) ####### self.picture02 is the "picture at 1" self.picture21 = MS33([[A[(2,2)],A[(1,3)],A[(0,1)]],[A[(0,3)],A[(2,3)],A[(2,0)]],[A[(1,0)],A[(1,1)],A[(1,2)]]]) ####### self.picture21 is the "picture at 0" self.line = [set([]) for i in range(12)] self.line[0] = set([(0,0),(0,1),(0,2)]) self.line[1] = set([(1,0),(1,1),(1,2)]) self.line[2] = set([(2,0),(2,1),(2,2)]) self.line[3] = set([(0,2),(1,2),(2,2)]) self.line[4] = set([(0,1),(1,1),(2,1)]) self.line[5] = set([(0,0),(1,0),(2,0)]) self.line[6] = set([(0,0),(1,1),(2,2)]) self.line[7] = set([(2,0),(0,1),(1,2)]) self.line[8] = set([(0,2),(1,0),(2,1)]) self.line[9] = set([(2,0),(1,1),(0,2)]) self.line[10] = set([(0,0),(1,2),(2,1)]) self.line[11] = set([(1,0),(0,1),(2,2)]) self.cross = [set([]) for i in range(18)] self.cross[0] = set([(0,0),(0,1),(0,2),(1,0),(2,0)]) self.cross[1] = set([(0,0),(0,1),(0,2),(1,2),(2,2)]) self.cross[2] = set([(0,0),(1,0),(2,0),(2,1),(2,2)]) self.cross[3] = set([(2,0),(2,1),(2,2),(0,2),(1,2)]) self.cross[4] = set([(0,0),(0,1),(0,2),(1,1),(2,1)]) self.cross[5] = set([(0,0),(1,0),(2,0),(1,1),(1,2)]) self.cross[6] = set([(1,0),(1,1),(1,2),(0,2),(2,2)]) self.cross[7] = set([(0,1),(1,1),(2,1),(2,0),(2,2)]) self.cross[8] = set([(0,0),(0,1),(1,0),(1,1),(2,2)]) self.cross[9] = set([(0,0),(1,1),(1,2),(2,1),(2,2)]) self.cross[10] = set([(2,0),(2,1),(1,0),(1,1),(0,2)]) self.cross[11] = set([(0,1),(0,2),(1,1),(1,2),(2,0)]) self.cross[12] = set([(0,0),(1,0),(0,2),(1,2),(2,1)]) self.cross[13] = set([(1,0),(0,1),(0,2),(2,1),(2,2)]) self.cross[14] = set([(0,1),(1,0),(1,2),(2,0),(2,2)]) self.cross[15] = set([(0,0),(0,1),(1,2),(2,0),(2,1)]) self.cross[16] = set([(1,0),(1,1),(1,2),(0,1),(2,1)]) self.cross[17] = set([(0,0),(0,2),(1,1),(2,0),(2,2)]) self.box = set([(i,j) for i in range(3) for j in range(3)]) self.square = [set([]) for i in range(18)] for i in range(18): self.square[i] = self.box - self.cross[i] MS34_GF3 = MatrixSpace(GF(3),3,4) self.col1 = MS34_GF3([[1,0,0,0],[1,0,0,0],[1,0,0,0]]) self.col2 = MS34_GF3([[0,1,0,0],[0,1,0,0],[0,1,0,0]]) self.col3 = MS34_GF3([[0,0,1,0],[0,0,1,0],[0,0,1,0]]) self.col4 = MS34_GF3([[0,0,0,1],[0,0,0,1],[0,0,0,1]]) self.tet1 = MS34_GF3([[1,1,1,1],[0,0,0,0],[0,0,0,0]]) self.tet2 = MS34_GF3([[1,0,0,0],[0,1,1,1],[0,0,0,0]]) self.tet3 = MS34_GF3([[1,0,0,0],[0,0,0,0],[0,1,1,1]]) self.tet4 = MS34_GF3([[0,1,0,0],[1,0,1,0],[0,0,0,1]]) self.tet5 = MS34_GF3([[0,0,0,1],[1,1,0,0],[0,0,1,0]]) self.tet6 = MS34_GF3([[0,0,1,0],[1,0,0,1],[0,1,0,0]]) self.tet7 = MS34_GF3([[0,1,0,0],[0,0,0,1],[1,0,1,0]]) self.tet8 = MS34_GF3([[0,0,1,0],[0,1,0,0],[1,0,0,1]]) self.tet9 = MS34_GF3([[0,0,0,1],[0,0,1,0],[1,1,0,0]]) self.col = [ self.col1, self.col2, self.col3, self.col4] self.tet = [ self.tet1, self.tet2, self.tet3, self.tet4, self.tet5, self.tet6, self.tet7, self.tet8, self.tet9]
def maximal_grading(L): r""" Return a maximal grading of a Lie algebra defined over an algebraically closed field. A maximal grading of a Lie algebra `\mathfrak{g}` is the finest possible grading of `\mathfrak{g}` over torsion free abelian groups. If `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^k} \mathfrak{g}_n` is a maximal grading, then there exists no other grading `\mathfrak{g} = \bigoplus_{a\in A} \mathfrak{g}_a` over a torsion free abelian group `A` such that every `\mathfrak{g}_a` is contained in some `\mathfrak{g}_n`. EXAMPLES: A maximal grading of an abelian Lie algebra puts each basis element into an independent layer:: sage: import sys, pathlib sage: sys.path.append(str(pathlib.Path().absolute())) sage: from lie_gradings.gradings.grading import maximal_grading sage: L = LieAlgebra(QQbar, 4, abelian=True) sage: maximal_grading(L) Grading over Additive abelian group isomorphic to Z + Z + Z + Z of Abelian Lie algebra on 4 generators (L[0], L[1], L[2], L[3]) over Algebraic Field with nonzero layers (1, 0, 0, 0) : (L[3],) (0, 1, 0, 0) : (L[2],) (0, 0, 1, 0) : (L[1],) (0, 0, 0, 1) : (L[0],) A maximal grading of a free nilpotent Lie algebra decomposes the Lie algebra based on how many times each generator appears in the defining Lie bracket:: sage: L = LieAlgebra(QQbar, 3, step=3) sage: maximal_grading(L) Grading over Additive abelian group isomorphic to Z + Z + Z of Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Algebraic Field with nonzero layers (1, 0, 0) : (X_1,) (0, 1, 0) : (X_2,) (1, 1, 0) : (X_12,) (1, 2, 0) : (X_122,) (2, 1, 0) : (X_112,) (0, 0, 1) : (X_3,) (0, 1, 1) : (X_23,) (0, 1, 2) : (X_233,) (0, 2, 1) : (X_223,) (1, 0, 1) : (X_13,) (1, 0, 2) : (X_133,) (1, 1, 1) : (X_123, X_132) (2, 0, 1) : (X_113,) """ # define utilities to convert from matrices to vectors and back R = L.base_ring() n = L.dimension() MS = MatrixSpace(R, n, n) def matrix_to_vec(A): return vector(R, sum((list(Ar) for Ar in A.rows()), [])) db = L.derivations_basis() def derivation_lincomb(vec): return sum((vk * dk for vk, dk in zip(vec, db)), MS.zero()) # iteratively construct larger and larger tori of derivations t = [] while True: # compute the centralizer of the torus in the derivation algebra ker = FreeModule(R, len(db)) for der in t: # form the matrix of ad(der) with rows # the images of basis derivations A = matrix([matrix_to_vec(der * X - X * der) for X in db]) ker = ker.intersection(A.left_kernel()) cb = [derivation_lincomb(v) for v in ker.basis()] # check the basis of the centralizer for semisimple parts outside of t gl = FreeModule(R, n * n) t_submodule = gl.submodule([matrix_to_vec(der) for der in t]) for A in cb: As, An = jordan_decomposition(A) if matrix_to_vec(As) not in t_submodule: # extend the torus by As t.append(As) break else: # no new elements found, so the torus is maximal break # compute the eigenspace intersections to get the concrete grading common_eigenspaces = [([], FreeModule(R, n))] for A in t: new_eigenspaces = [] eig = A.right_eigenspaces() for ev, V in common_eigenspaces: for ew, W in eig: VW = V.intersection(W) if VW.dimension() > 0: new_eigenspaces.append((ev + [ew], VW)) common_eigenspaces = new_eigenspaces if not t: # zero dimensional maximal torus # the only grading is the trivial grading magma = AdditiveAbelianGroup([]) layers = {magma.zero(): L.basis().list()} return grading(L, layers, magma=magma) # define a grading with layers indexed by tuples of eigenvalues cm = get_coercion_model() all_eigenvalues = sum((ev for ev, V in common_eigenspaces), []) k = len(common_eigenspaces[0][0]) evR = cm.common_parent(*all_eigenvalues) layers = { tuple(ev): [L.from_vector(v) for v in V.basis()] for ev, V in common_eigenspaces } magma = evR.cartesian_product(*[evR] * (k - 1)) gr = grading(L, layers, magma=magma) # convert to a grading over Z^k return gr.universal_realization()
def rational_diagonal_form(self, return_matrix=False): """ Returns a diagonal form equivalent to Q over the fraction field of its defining ring. If the return_matrix is True, then we return the transformation matrix performing the diagonalization as the second argument. INPUT: none OUTPUT: Q -- the diagonalized form of this quadratic form (optional) T -- matrix which diagonalizes Q (over it's fraction field) EXAMPLES:: sage: Q = QuadraticForm(ZZ, 2, [0,1,-1]) sage: Q Quadratic form in 2 variables over Integer Ring with coefficients: [ 0 1 ] [ * -1 ] sage: Q.rational_diagonal_form() Quadratic form in 2 variables over Rational Field with coefficients: [ -2 0 ] [ * 1/8 ] :: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.rational_diagonal_form(return_matrix=True) ( Quadratic form in 4 variables over Rational Field with coefficients: [ 1 0 0 0 ] [ * 3 0 0 ] [ * * 5 0 ] [ * * * 7 ] , <BLANKLINE> [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] ) :: sage: Q1 = QuadraticForm(ZZ, 4, [1, 1, 0, 0, 1, 0, 0, 1, 0, 18]) sage: Q1 Quadratic form in 4 variables over Integer Ring with coefficients: [ 1 1 0 0 ] [ * 1 0 0 ] [ * * 1 0 ] [ * * * 18 ] sage: Q1.rational_diagonal_form(return_matrix=True) ( Quadratic form in 4 variables over Rational Field with coefficients: [ 1 0 0 0 ] [ * 3/4 0 0 ] [ * * 1 0 ] [ * * * 18 ] , <BLANKLINE> [ 1 -1/2 0 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] ) """ n = self.dim() Q = copy.deepcopy(self) Q.__init__(FractionField(self.base_ring()), self.dim(), self.coefficients()) MS = MatrixSpace(Q.base_ring(), n, n) T = MS(1) ## Clear the entries one row at a time. for i in range(n): ## Deal with rows where the diagonal entry is zero. if Q[i,i] == 0: ## Look for a non-zero entry and use it to make the diagonal non-zero (if it exists) for j in range(i+1, n): if Q[i,j] != 0: temp = MS(1) if Q[i,j] + Q[j,j] == 0: temp[j, i] = -1 else: temp[j, i] = 1 ## Apply the transformation Q = Q(temp) T = T * temp break ## Create a matrix which deals with off-diagonal entries (all at once for each row) temp = MS(1) for j in range(i+1, n): if Q[i,j] != 0: temp[i,j] = -Q[i,j] / (Q[i,i] * 2) ## This should only occur when Q[i,i] != 0, which the above step guarantees. Q = Q(temp) T = T * temp ## Return the appropriate output if return_matrix: return Q, T else: return Q
def level1_UpGj(p, klist, m, extra_data=False): r""" Return a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` parameterised by the weights k in ``klist``. The matrix `A_k` is the finite square matrix which occurs on input p, k and m in Step 6 of Algorithm 1 in [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices. INPUT: - ``p`` -- prime at least 5. - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights). - ``m`` -- positive integer. - ``extra_data`` -- (default: ``False``) boolean OUTPUT: - list of square matrices. If ``extra_data`` is ``True``, return also extra intermediate data, namely the matrix `E` in [Lau2011]_ and the integers ``elldash`` and ``mdash``. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import level1_UpGj sage: level1_UpGj(7,[100],5) [ [ 1 980 4802 0 0] [ 0 13727 14406 0 0] [ 0 13440 7203 0 0] [ 0 1995 4802 0 0] [ 0 9212 14406 0 0] ] sage: len(level1_UpGj(7,[100],5,extra_data=True)) 4 """ # Step 1 t = cputime() k0 = klist[0] % (p - 1) n = floor(((p + 1) / (p - 1)) * (m + 1)) ell = dimension_modular_forms(1, k0 + n * (p - 1)) ellp = ell * p mdash = m + ceil(n / (p + 1)) verbose("done step 1", t) t = cputime() # Steps 2 and 3 e, Ep1 = katz_expansions(k0, p, ellp, mdash, n) verbose("done steps 2+3", t) t = cputime() # Step 4 G = compute_G(p, Ep1) Alist = [] verbose("done step 4a", t) t = cputime() for k in klist: k = ZZ(k) # convert to sage integer kdiv = k // (p - 1) Gkdiv = G**kdiv u = [] for i in range(0, ell): ei = e[i] ui = Gkdiv * ei u.append(ui) verbose("done step 4b", t) t = cputime() # Step 5 and computation of T in Step 6 S = e[0][0].parent() T = matrix(S, ell, ell) for i in range(0, ell): for j in range(0, ell): T[i, j] = u[i][p * j] verbose("done step 5", t) t = cputime() # Step 6: solve T = AE using fact E is upper triangular. # Warning: assumes that T = AE (rather than pT = AE) has # a solution over Z/(p^mdash). This has always been the case in # examples computed by the author, see Note 3.1. A = matrix(S, ell, ell) verbose("solving a square matrix problem of dimension %s" % ell, t) for i in range(0, ell): Ti = T[i] for j in range(0, ell): ej = Ti.parent()([e[j][l] for l in range(0, ell)]) lj = ZZ(ej[j]) A[i, j] = S(ZZ(Ti[j]) / lj) Ti = Ti - A[i, j] * ej Alist.append(MatrixSpace(Zmod(p**m), ell, ell)(A)) verbose("done step 6", t) if extra_data: return Alist, e, ell, mdash else: return Alist
def supersingular_points(self): r""" This function computes the supersingular j-invariants over the finite field associated to self. INPUT: - ``self`` -- SupersingularModule object OUTPUT: list_j, dict_j -- list_j is the list of supersingular j-invariants, dict_j is a dictionary with these j-invariants as keys and their indexes as values. The latter is used to speed up j-invariant look-up. The indexes are based on the order of their *discovery*. EXAMPLES: The following examples calculate supersingular j-invariants over finite fields with characteristic 7, 11 and 37:: sage: S = SupersingularModule(7) sage: S.supersingular_points() ([6], {6: 0}) sage: S = SupersingularModule(11) sage: S.supersingular_points() ([1, 0], {0: 1, 1: 0}) sage: S = SupersingularModule(37) sage: S.supersingular_points() ([8, 27*a + 23, 10*a + 20], {8: 0, 10*a + 20: 2, 27*a + 23: 1}) AUTHORS: - David Kohel -- [email protected] - Iftikhar Burhanuddin -- [email protected] """ try: return (self._ss_points_dic, self._ss_points) except AttributeError: pass Fp2 = self.__finite_field level = self.__level prime = Fp2.characteristic() X = Fp2['x'].gen() jinv = supersingular_j(Fp2) dim = dimension_supersingular_module(prime, level) pos = int(0) #using list to keep track of explored nodes using pos ss_points = [jinv] #using to keep track of index of the previous node ss_points_pre = [-1] #using dictionary for fast j-invariant look-up ss_points_dic = {jinv:pos} T2_matrix = MatrixSpace(rings.Integers(), dim, sparse=True)(0) while pos < len(ss_points): if pos == 0: neighbors = Phi_polys(2,X,ss_points[pos]).roots() else: j_prev = ss_points_pre[pos] # TODO: These are quadratic polynomials -- maybe we should use the # quadratic formula and fast square root finding (??) neighbors = Phi2_quad(X, ss_points[j_prev], ss_points[pos]).roots() for (xj,ej) in neighbors: if xj not in ss_points_dic: j = len(ss_points) ss_points += [xj] ss_points_pre += [pos] ss_points_dic[xj] = j else: j = ss_points_dic[xj] T2_matrix[pos, j] += ej # end for if pos != 0: # also record the root from j_prev T2_matrix[pos, j_prev] += 1 pos += int(1) self.__hecke_matrices[2] = T2_matrix return (ss_points, ss_points_dic)
def __init__(self, n=1, R=0): """ Initialize ``self``. EXAMPLES:: sage: H = groups.matrix.Heisenberg(n=2, R=5) sage: TestSuite(H).run() # long time sage: H.category() Category of finitely generated finite enumerated groups sage: H = groups.matrix.Heisenberg(n=2, R=4) sage: TestSuite(H).run() # long time sage: H = groups.matrix.Heisenberg(n=3) sage: TestSuite(H).run(max_runs=30, skip="_test_elements") # long time sage: H = groups.matrix.Heisenberg(n=2, R=GF(4)) sage: TestSuite(H).run() # long time TESTS:: sage: groups.matrix.Heisenberg(n=2, R=ZZ).category() Category of finitely generated infinite enumerated groups """ def elementary_matrix(i, j, val, MS): elm = copy(MS.one()) elm[i, j] = val elm.set_immutable() return elm self._n = n self._ring = R # We need the generators of the ring as a commutative additive group if self._ring is ZZ: ring_gens = [self._ring.one()] else: if self._ring.cardinality() == self._ring.characteristic(): ring_gens = [self._ring.one()] else: # This is overkill, but is the only way to ensure # we get all of the elements ring_gens = list(self._ring) dim = ZZ(n + 2) MS = MatrixSpace(self._ring, dim) gens_x = [ elementary_matrix(0, j, gen, MS) for j in range(1, dim - 1) for gen in ring_gens ] gens_y = [ elementary_matrix(i, dim - 1, gen, MS) for i in range(1, dim - 1) for gen in ring_gens ] gen_z = [elementary_matrix(0, dim - 1, gen, MS) for gen in ring_gens] gens = gens_x + gens_y + gen_z from sage.libs.gap.libgap import libgap gap_gens = [libgap(single_gen) for single_gen in gens] gap_group = libgap.Group(gap_gens) cat = Groups().FinitelyGenerated() if self._ring in Rings().Finite(): cat = cat.Finite() else: cat = cat.Infinite() FinitelyGeneratedMatrixGroup_gap.__init__(self, ZZ(dim), self._ring, gap_group, category=cat)
def hecke_matrix(self,L): r""" This function returns the `L^{\text{th}}` Hecke matrix. INPUT: - ``self`` -- SupersingularModule object - ``L`` -- integer, positive OUTPUT: matrix -- sparse integer matrix EXAMPLES: This example computes the action of the Hecke operator `T_2` on the module of supersingular points on `X_0(1)/F_{37}`:: sage: S = SupersingularModule(37) sage: M = S.hecke_matrix(2) sage: M [1 1 1] [1 0 2] [1 2 0] This example computes the action of the Hecke operator `T_3` on the module of supersingular points on `X_0(1)/F_{67}`:: sage: S = SupersingularModule(67) sage: M = S.hecke_matrix(3) sage: M [0 0 0 0 2 2] [0 0 1 1 1 1] [0 1 0 2 0 1] [0 1 2 0 1 0] [1 1 0 1 0 1] [1 1 1 0 1 0] .. note:: The first list --- list_j --- returned by the supersingular_points function are the rows *and* column indexes of the above hecke matrices and its ordering should be kept in mind when interpreting these matrices. AUTHORS: - David Kohel -- [email protected] - Iftikhar Burhanuddin -- [email protected] """ if L in self.__hecke_matrices: return self.__hecke_matrices[L] SS, II = self.supersingular_points() if L == 2: # since T_2 gets computed as a side effect of computing the supersingular points return self.__hecke_matrices[2] Fp2 = self.__finite_field h = len(SS) R = self.base_ring() T_L = MatrixSpace(R,h)(0) S, X = Fp2['x'].objgen() if L in [3,5,7,11]: for i in range(len(SS)): ss_i = SS[i] phi_L_in_x = Phi_polys(L, X, ss_i) rts = phi_L_in_x.roots() for r in rts: T_L[i,int(II[r[0]])] = r[1] else: DBMP = ClassicalModularPolynomialDatabase() phi_L = DBMP[L] M, (x,y) =Fp2['x,y'].objgens() phi_L = phi_L(x,y) # As an optimization, we compute the coefficients of y and evaluate # them, since univariate polynomial evaluation is much faster than # multivariate evaluation (in Sage :-( ). uni_coeff = [phi_L(x,0).univariate_polynomial()] + \ [phi_L.coefficient(y**i).univariate_polynomial() for i in range(1,phi_L.degree(y)+1)] for i in range(len(SS)): ss_i = SS[i] ## We would do the eval below, but it is too slow (right now). #phi_L_in_x = phi_L(X, ss_i) phi_L_in_x = S([f(ss_i) for f in uni_coeff]) rts = phi_L_in_x.roots() for r in rts: T_L[i,int(II[r[0]])] = r[1] self.__hecke_matrices[L] = T_L return T_L
def cholesky_decomposition(self, bit_prec=53): """ Give the Cholesky decomposition of this quadratic form `Q` as a real matrix of precision ``bit_prec``. RESTRICTIONS: Q must be given as a QuadraticForm defined over `\ZZ`, `\QQ`, or some real field. If it is over some real field, then an error is raised if the precision given is not less than the defined precision of the real field defining the quadratic form! REFERENCE: From Cohen's "A Course in Computational Algebraic Number Theory" book, p 103. INPUT: ``bit_prec`` -- a natural number (default 53). OUTPUT: an upper triangular real matrix of precision ``bit_prec``. TO DO: If we only care about working over the real double field (RDF), then we can use the ``cholesky()`` method present for square matrices over that. .. note:: There is a note in the original code reading :: ##///////////////////////////////////////////////////////////////////////////////////////////////// ##/// Finds the Cholesky decomposition of a quadratic form -- as an upper-triangular matrix! ##/// (It's assumed to be global, hence twice the form it refers to.) <-- Python revision asks: Is this true?!? =| ##///////////////////////////////////////////////////////////////////////////////////////////////// EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.cholesky_decomposition() [ 1.00000000000000 0.000000000000000 0.000000000000000] [0.000000000000000 1.00000000000000 0.000000000000000] [0.000000000000000 0.000000000000000 1.00000000000000] :: sage: Q = QuadraticForm(QQ, 3, range(1,7)); Q Quadratic form in 3 variables over Rational Field with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] sage: Q.cholesky_decomposition() [ 1.00000000000000 1.00000000000000 1.50000000000000] [0.000000000000000 3.00000000000000 0.333333333333333] [0.000000000000000 0.000000000000000 3.41666666666667] """ ## Check that the precision passed is allowed. if isinstance(self.base_ring(), RealField_class) and (self.base_ring().prec() < bit_prec): raise RuntimeError( "Oops! The precision requested is greater than that of the given quadratic form!" ) ## 1. Initialization n = self.dim() R = RealField(bit_prec) MS = MatrixSpace(R, n, n) Q = MS(R(0.5)) * MS( self.matrix() ) ## Initialize the real symmetric matrix A with the matrix for Q(x) = x^t * A * x ## DIAGNOSTIC #print "After 1: Q is \n" + str(Q) ## 2. Loop on i for i in range(n): for j in range(i + 1, n): Q[j, i] = Q[i, j] ## Is this line redundant? Q[i, j] = Q[i, j] / Q[i, i] ## 3. Main Loop for k in range(i + 1, n): for l in range(k, n): Q[k, l] = Q[k, l] - Q[k, i] * Q[i, l] ## 4. Zero out the strictly lower-triangular entries for i in range(n): for j in range(i): Q[i, j] = 0 return Q
def _rational_diagonal_form_and_transformation(self): """ Return a diagonal form equivalent to the given quadratic from and the corresponding transformation matrix. This is over the fraction field of the base ring of the given quadratic form. OUTPUT: a tuple ``(D,T)`` where - ``D`` -- the diagonalized form of this quadratic form. - ``T`` -- transformation matrix. This is such that ``T.transpose() * self.matrix() * T`` gives ``D.matrix()``. Both ``D`` and ``T`` are defined over the fraction field of the base ring of the given form. EXAMPLES:: sage: Q = QuadraticForm(ZZ, 4, [1, 1, 0, 0, 1, 0, 0, 1, 0, 18]) sage: Q Quadratic form in 4 variables over Integer Ring with coefficients: [ 1 1 0 0 ] [ * 1 0 0 ] [ * * 1 0 ] [ * * * 18 ] sage: Q._rational_diagonal_form_and_transformation() ( Quadratic form in 4 variables over Rational Field with coefficients: [ 1 0 0 0 ] [ * 3/4 0 0 ] [ * * 1 0 ] [ * * * 18 ] , <BLANKLINE> [ 1 -1/2 0 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] ) """ n = self.dim() K = self.base_ring().fraction_field() Q = self.base_change_to(K) MS = MatrixSpace(K, n, n) try: # Try PARI if the type is supported pariself = self._pari_() # Check that conversion back works MS(pariself.sage()) except Exception: pass else: R = pariself.qfgaussred() # Diagonal matrix D = MS() for i in range(n): D[i, i] = R[i, i] Q = Q.parent()(D) # Transformation matrix (inverted) T = MS(R.sage()) for i in range(n): T[i, i] = K.one() try: return Q, ~T except ZeroDivisionError: # Singular case is not fully supported by PARI pass # General case if conversion to/from PARI failed T = MS(1) ## Clear the entries one row at a time. for i in range(n): ## Deal with rows where the diagonal entry is zero. if Q[i, i] == 0: ## Look for a non-zero entry and use it to make the diagonal non-zero (if it exists) for j in range(i + 1, n): if Q[i, j] != 0: temp = MS(1) if Q[i, j] + Q[j, j] == 0: temp[j, i] = -1 else: temp[j, i] = 1 ## Apply the transformation Q = Q(temp) T = T * temp break ## Create a matrix which deals with off-diagonal entries (all at once for each row) temp = MS(1) for j in range(i + 1, n): if Q[i, j] != 0: temp[i, j] = -Q[i, j] / ( Q[i, i] * 2 ) ## This should only occur when Q[i,i] != 0, which the above step guarantees. Q = Q(temp) T = T * temp return Q, T
def __init__(self, diffop, dz, derivatives, nterms_est): deq_Scalars = diffop.base_ring().base_ring() E = dz.parent() if deq_Scalars.characteristic() != 0: raise ValueError("only makes sense for scalar rings of " "characteristic 0") assert deq_Scalars is dz.parent() or deq_Scalars != dz.parent() #### Recurrence operator # Reduce to the case of a number field generated by an algebraic # integer. This is mainly intended to avoid computing gcds (due to # denominators in the representation of number field elements) in the # product tree, but could also be useful to extend the field using Pari # in the future. NF_rec, AlgInts_rec = _number_field_with_integer_gen(deq_Scalars) # ore_algebra currently does not support orders as scalar rings Pols = PolynomialRing(NF_rec, 'n') Rops, Sn = ore_algebra.OreAlgebra(Pols, 'Sn').objgen() recop = diffop.to_S(Rops).primitive_part().numerator() recop = lcm([p.denominator() for p in recop.coefficients()]) * recop # Ensure that ordrec >= orddeq. When the homomorphic image of diffop in # Rops is divisible by Sn, it can happen that the recop (e.g., after # normalization to Sn-valuation 0) has order < orddeq, and our strategy # of using vectors of coefficients of the form [u(n-s'), ..., u(n+r-1)] # with s'=s-r does not work in this case. orddelta = recop.order() - diffop.order() if orddelta < 0: recop = Sn**(-orddelta) * recop #### Choose computation domains if ((isinstance(E, (RealBallField, ComplexBallField)) or E is QQ or utilities.is_QQi(E) or E is RLF or E is CLF) and (deq_Scalars is QQ or utilities.is_QQi(deq_Scalars))): # Special-case QQ and QQ[i] to use arb matrices # (overwrites AlgInts_rec) self.StepMatrix_class = StepMatrix_arb self.binsplit_threshold = 8 # Working precision. We typically want operations on exact balls to # be exact, so that overshooting shouldn't be a problem. # XXX Less clear in the case dz ∈ XBF! # XXX The rough estimate below ignores the height of rec and dz. # prec = nterms_est*(recop.degree()*nterms_est.nbits() # + recop.order().nbits() + 1) prec = 8 + nterms_est * (1 + ZZ(ZZ(recop.order()).nbits()).nbits()) if (E is QQ or isinstance(E, RealBallField)) and deq_Scalars is QQ: AlgInts_rec = AlgInts_pow = RealBallField(prec) else: AlgInts_rec = AlgInts_pow = ComplexBallField(prec) if is_NumberField(E): pow_den = AlgInts_pow(dz.denominator()) else: pow_den = AlgInts_pow.one() else: self.StepMatrix_class = StepMatrix_generic self.binsplit_threshold = 64 if is_NumberField(E): # In fact we should probably do something similar for dz in any # finite-dimensional Q-algebra. (But how?) NF_pow, AlgInts_pow = _number_field_with_integer_gen(E) pow_den = NF_pow(dz).denominator() else: # This includes the case E = ZZ, but dz could live in pretty # much any algebra over deq_Scalars (including matrices, # intervals...). Then the computation of sums_row may take time, # but we still hope to gain something on the computation of the # coefficients and/or limit interval blow-up thanks to the use # of binary splitting. AlgInts_pow = E pow_den = ZZ.one() assert pow_den.parent() is ZZ assert AlgInts_pow is AlgInts_rec or AlgInts_pow != AlgInts_rec #### Recurrence matrix self.recop = recop self.orddeq = diffop.order() self.ordrec = recop.order() self.orddelta = self.ordrec - self.orddeq Pols_rec, n = PolynomialRing(AlgInts_rec, 'n').objgen() self.rec_coeffs = [ -Pols_rec(recop[i])(n - self.orddelta) for i in xrange(self.ordrec) ] self.rec_den = Pols_rec(recop.leading_coefficient())(n - self.orddelta) # Guard against various problems related to number field embeddings and # uniqueness assert Pols_rec.base_ring() is AlgInts_rec assert self.rec_den.base_ring() is AlgInts_rec assert self.rec_den( self.rec_den.base_ring().zero()).parent() is AlgInts_rec # Also store a version of the recurrence operator of the form # b[0](n) + b[1](n) S^(-1) + ··· + b[s](n) S^(-s). # This is convenient to share code with other implementations, or at # least make the implementations easier to compare. # XXX: understand what to do about variable names! self.bwrec = [ recop[self.ordrec - k](Rops.base_ring().gen() - self.ordrec) for k in xrange(self.ordrec + 1) ] #### Power of dz. Note that this part does not depend on n. # If we extend the removal of denominators above to algebras other than # number fields, it would probably make more sense to move this into # the caller. --> support dz in non-com ring (mat)? power series work # only over com rings Series_pow = PolynomialRing(AlgInts_pow, 'delta') self.pow_num = Series_pow([pow_den * dz, pow_den]) self.pow_den = pow_den self.derivatives = derivatives #### Partial sums # We need a parent containing both the coefficients of the operator and # the evaluation point. # XXX: Is this the correct way to get one? Should we use # canonical_coercion()? Something else? # XXX: This is not powerful enough to find a number field containing # two given number fields (both given with embeddings into CC) # Work around #14982 (fixed) + weaknesses of the coercion framework for orders #Series_sums = sage.categories.pushout.pushout(AlgInts_rec, Series_pow) try: AlgInts_sums = sage.categories.pushout.pushout( AlgInts_rec, AlgInts_pow) except sage.structure.coerce_exceptions.CoercionException: AlgInts_sums = sage.categories.pushout.pushout(NF_rec, AlgInts_pow) assert AlgInts_sums is AlgInts_rec or AlgInts_sums != AlgInts_rec assert AlgInts_sums is AlgInts_pow or AlgInts_sums != AlgInts_pow Series_sums = PolynomialRing(AlgInts_sums, 'delta') assert Series_sums.base_ring() is AlgInts_sums # for speed self.Series_sums = Series_sums self.series_class_sums = type(Series_sums.gen()) self.Mat_rec = MatrixSpace(AlgInts_rec, self.ordrec, self.ordrec) self.Mat_sums_row = MatrixSpace(Series_sums, 1, self.ordrec) self.Mat_series_sums = self.Mat_rec.change_ring(Series_sums)
def algebraic_topological_model_delta_complex(K, base_ring=None): r""" Algebraic topological model for cell complex ``K`` with coefficients in the field ``base_ring``. This has the same basic functionality as :func:`algebraic_topological_model`, but it also works for `\Delta`-complexes. For simplicial and cubical complexes it is somewhat slower, though. INPUT: - ``K`` -- a simplicial complex, a cubical complex, or a `\Delta`-complex - ``base_ring`` -- coefficient ring; must be a field OUTPUT: a pair ``(phi, M)`` consisting of - chain contraction ``phi`` - chain complex `M` See :func:`algebraic_topological_model` for the main documentation. The difference in implementation between the two: this uses matrix and vector algebra. The other function does more of the computations "by hand" and uses cells (given as simplices or cubes) to index various dictionaries. Since the cells in `\Delta`-complexes are not as nice, the other function does not work for them, while this function relies almost entirely on the structure of the associated chain complex. EXAMPLES:: sage: from sage.homology.algebraic_topological_model import algebraic_topological_model_delta_complex as AT_model sage: RP2 = simplicial_complexes.RealProjectivePlane() sage: phi, M = AT_model(RP2, GF(2)) sage: M.homology() {0: Vector space of dimension 1 over Finite Field of size 2, 1: Vector space of dimension 1 over Finite Field of size 2, 2: Vector space of dimension 1 over Finite Field of size 2} sage: T = delta_complexes.Torus() sage: phi, M = AT_model(T, QQ) sage: M.homology() {0: Vector space of dimension 1 over Rational Field, 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} If you want to work with cohomology rather than homology, just dualize the outputs of this function:: sage: M.dual().homology() {0: Vector space of dimension 1 over Rational Field, 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} sage: M.dual().degree_of_differential() 1 sage: phi.dual() Chain homotopy between: Chain complex endomorphism of Chain complex with at most 3 nonzero terms over Rational Field and Chain complex morphism: From: Chain complex with at most 3 nonzero terms over Rational Field To: Chain complex with at most 3 nonzero terms over Rational Field In degree 0, the inclusion of the homology `M` into the chain complex `C` sends the homology generator to a single vertex:: sage: K = delta_complexes.Simplex(2) sage: phi, M = AT_model(K, QQ) sage: phi.iota().in_degree(0) [0] [0] [1] In cohomology, though, one needs the dual of every degree 0 cell to detect the degree 0 cohomology generator:: sage: phi.dual().iota().in_degree(0) [1] [1] [1] TESTS:: sage: T = cubical_complexes.Torus() sage: C = T.chain_complex() sage: H, M = AT_model(T, QQ) sage: C.differential(1) * H.iota().in_degree(1).column(0) == 0 True sage: C.differential(1) * H.iota().in_degree(1).column(1) == 0 True sage: coC = T.chain_complex(cochain=True) sage: coC.differential(1) * H.dual().iota().in_degree(1).column(0) == 0 True sage: coC.differential(1) * H.dual().iota().in_degree(1).column(1) == 0 True """ def conditionally_sparse(m): """ Return a sparse matrix if the characteristic is zero. Multiplication of matrices with low density seems to be quicker if the matrices are sparse, when over the rationals. Over finite fields, dense matrices are faster regardless of density. """ if base_ring == QQ: return m.sparse_matrix() else: return m if not base_ring.is_field(): raise ValueError('the coefficient ring must be a field') # The following are all dictionaries indexed by dimension. # For each n, gens[n] is an ordered list of the n-cells generating the complex M. gens = {} pi_data = {} phi_data = {} iota_data = {} for n in range(-1, K.dimension() + 1): gens[n] = [] C = K.chain_complex(base_ring=base_ring) n_cells = [] pi_cols = [] iota_cols = {} for dim in range(K.dimension() + 1): # old_cells: cells one dimension lower. old_cells = n_cells # n_cells: the standard basis for the vector space C.free_module(dim). n_cells = C.free_module(dim).gens() diff = C.differential(dim) # diff is sparse and low density. Dense matrices are faster # over finite fields, but for low density matrices, sparse # matrices are faster over the rationals. if base_ring != QQ: diff = diff.dense_matrix() rank = len(n_cells) old_rank = len(old_cells) # Create some matrix spaces to try to speed up matrix creation. MS_pi_t = MatrixSpace(base_ring, old_rank, len(gens[dim - 1])) pi_old = MS_pi_t.matrix(pi_cols).transpose() iota_cols_old = iota_cols iota_cols = {} pi_cols_old = pi_cols pi_cols = [] phi_old = MatrixSpace(base_ring, rank, old_rank, sparse=(base_ring == QQ)).zero() phi_old_cols = phi_old.columns() phi_old = conditionally_sparse(phi_old) to_be_deleted = [] zero_vector = vector(base_ring, rank) pi_nrows = pi_old.nrows() for c_idx, c in enumerate(n_cells): # c_bar = c - phi(bdry(c)): # Avoid a bug in matrix-vector multiplication (trac 19378): if not diff: c_bar = c pi_bdry_c_bar = False else: if base_ring == QQ: c_bar = c - phi_old * (diff * c) pi_bdry_c_bar = conditionally_sparse(pi_old) * (diff * c_bar) else: c_bar = c - phi_old * diff * c pi_bdry_c_bar = conditionally_sparse(pi_old) * diff * c_bar # One small typo in the published algorithm: it says # "if bdry(c_bar) == 0", but should say # "if pi(bdry(c_bar)) == 0". if not pi_bdry_c_bar: # Append c to list of gens. gens[dim].append(c_idx) # iota(c) = c_bar iota_cols[c_idx] = c_bar # pi(c) = c pi_cols.append(c) else: # Take any u in gens so that lambda_i = <u, pi(bdry(c_bar))> != 0. # u_idx will be the index of the corresponding cell. (u_idx, lambda_i) = pi_bdry_c_bar.leading_item() for (u_idx, lambda_i) in iteritems(pi_bdry_c_bar): if u_idx not in to_be_deleted: break # This element/column needs to be deleted from gens and # iota_old. Do that later. to_be_deleted.append(u_idx) # pi(c) = 0. pi_cols.append(zero_vector) for c_j_idx, c_j in enumerate(old_cells): # eta_ij = <u, pi(c_j)>. # That is, eta_ij is the u_idx entry in the vector pi_old * c_j: eta_ij = c_j.dot_product(pi_old.row(u_idx)) if eta_ij: # Adjust phi(c_j). phi_old_cols[c_j_idx] += eta_ij * lambda_i**( -1) * c_bar # Adjust pi(c_j). pi_cols_old[c_j_idx] -= eta_ij * lambda_i**( -1) * pi_bdry_c_bar # The matrices involved have many zero entries. For # such matrices, using sparse matrices is faster over # the rationals, slower over finite fields. phi_old = matrix(base_ring, phi_old_cols, sparse=(base_ring == QQ)).transpose() keep = vector( base_ring, pi_nrows, {i: 1 for i in range(pi_nrows) if i not in to_be_deleted}) cols = [v.pairwise_product(keep) for v in pi_cols_old] pi_old = MS_pi_t.matrix(cols).transpose() # Here cols is a temporary storage for the columns of iota. cols = [iota_cols_old[i] for i in sorted(iota_cols_old.keys())] for r in sorted(to_be_deleted, reverse=True): del cols[r] del gens[dim - 1][r] iota_data[dim - 1] = matrix(base_ring, len(gens[dim - 1]), old_rank, cols).transpose() # keep: rows to keep in pi_cols_old. Start with all # columns, then delete those in to_be_deleted. keep = sorted(set(range(pi_nrows)).difference(to_be_deleted)) # Now cols is a temporary storage for columns of pi. cols = [v.list_from_positions(keep) for v in pi_cols_old] pi_data[dim - 1] = matrix(base_ring, old_rank, len(gens[dim - 1]), cols).transpose() phi_data[dim - 1] = phi_old V_gens = VectorSpace(base_ring, len(gens[dim])) if pi_cols: cols = [] for v in pi_cols: cols.append(V_gens(v.list_from_positions(gens[dim]))) pi_cols = cols pi_data[dim] = matrix(base_ring, rank, len(gens[dim]), pi_cols).transpose() cols = [iota_cols[i] for i in sorted(iota_cols.keys())] iota_data[dim] = matrix(base_ring, len(gens[dim]), rank, cols).transpose() # M_data will contain (trivial) matrices defining the differential # on M. Keep track of the sizes using "M_rows" and "M_cols", which are # just the ranks of consecutive graded pieces of M. M_data = {} M_rows = 0 for n in range(K.dimension() + 1): M_cols = len(gens[n]) M_data[n] = zero_matrix(base_ring, M_rows, M_cols) M_rows = M_cols M = ChainComplex(M_data, base_ring=base_ring, degree=-1) pi = ChainComplexMorphism(pi_data, C, M) iota = ChainComplexMorphism(iota_data, M, C) phi = ChainContraction(phi_data, pi, iota) return phi, M
def ReedSolomonCode(n, k, F, pts=None): r""" Given a finite field `F` of order `q`, let `n` and `k` be chosen such that `1 \leq k \leq n \leq q`. Pick `n` distinct elements of `F`, denoted `\{ x_1, x_2, ... , x_n \}`. Then, the codewords are obtained by evaluating every polynomial in `F[x]` of degree less than `k` at each `x_i`: .. math:: C = \left\{ \left( f(x_1), f(x_2), ..., f(x_n) \right), f \in F[x], {\rm deg}(f)<k \right\}. `C` is a `[n, k, n-k+1]` code. (In particular, `C` is MDS.) INPUT: n : the length k : the dimension F : the base ring pts : (optional) list of n points in F (if None then Sage picks n of them in the order given to the elements of F) EXAMPLES:: sage: C = ReedSolomonCode(6,4,GF(7)); C Linear code of length 6, dimension 4 over Finite Field of size 7 sage: C.minimum_distance() 3 sage: C = ReedSolomonCode(6,4,GF(8,"a")); C Linear code of length 6, dimension 4 over Finite Field in a of size 2^3 sage: C.minimum_distance() 3 sage: C.minimum_distance(algorithm='gap') # long time, check d=n-k+1 3 sage: F.<a> = GF(3^2,"a") sage: pts = [0,1,a,a^2,2*a,2*a+1] sage: len(Set(pts)) == 6 # to make sure there are no duplicates True sage: C = ReedSolomonCode(6,4,F,pts); C Linear code of length 6, dimension 4 over Finite Field in a of size 3^2 sage: C.minimum_distance() 3 REFERENCES: - [W] http://en.wikipedia.org/wiki/Reed-Solomon """ q = F.order() power = lambda x, n, F: (x == 0 and n == 0) and F(1) or F( x**n) # since 0^0 is undefined if n > q or k > n or k > q: raise ValueError, "RS codes does not exist with the given input." if not (pts == None) and not (len(pts) == n): raise ValueError, "You must provide exactly %s distinct points of %s" % ( n, F) if (pts == None): pts = [] i = 0 for x in F: if i < n: pts.append(x) i = i + 1 MS = MatrixSpace(F, k, n) rowsG = [] rowsG = [[power(x, j, F) for x in pts] for j in range(k)] G = MS(rowsG) return LinearCode(G, d=n - k + 1)
def __init__(self, R, ngens=None, gram_matrix=None, names=None, index_set=None): """ Initialize self. TESTS:: sage: V = lie_conformal_algebras.Weyl(QQ) sage: TestSuite(V).run() """ from sage.matrix.matrix_space import MatrixSpace if ngens: try: from sage.rings.all import ZZ assert ngens in ZZ and ngens % 2 == 0 except AssertionError: raise ValueError("ngens needs to be an even positive " + "Integer, got {}".format(ngens)) if (gram_matrix is not None): if ngens is None: ngens = gram_matrix.dimensions()[0] try: assert (gram_matrix in MatrixSpace(R, ngens, ngens)) except AssertionError: raise ValueError( "The gram_matrix should be a skew-symmetric " + "{0} x {0} matrix, got {1}".format(ngens, gram_matrix)) if (not gram_matrix.is_skew_symmetric()) or \ (gram_matrix.is_singular()): raise ValueError("The gram_matrix should be a non degenerate " + "skew-symmetric {0} x {0} matrix, got {1}"\ .format(ngens,gram_matrix)) elif (gram_matrix is None): if ngens is None: ngens = 2 A = identity_matrix(R, ngens / 2) from sage.matrix.special import block_matrix gram_matrix = block_matrix([[R.zero(), A], [-A, R.zero()]]) latex_names = None if (names is None) and (index_set is None): names = 'alpha' latex_names = tuple(r'\alpha_{%d}' % i \ for i in range (ngens)) + ('K',) names, index_set = standardize_names_index_set(names=names, index_set=index_set, ngens=ngens) weyldict = {(i, j): { 0: { ('K', 0): gram_matrix[index_set.rank(i), index_set.rank(j)] } } for i in index_set for j in index_set} super(WeylLieConformalAlgebra, self).__init__(R, weyldict, names=names, latex_names=latex_names, index_set=index_set, central_elements=('K', )) self._gram_matrix = gram_matrix
def TrivialCode(F, n): MS = MatrixSpace(F, 1, n) return LinearCode(MS(0))
def HS_minimal(f, return_transformation=False, D=None): r""" Compute a minimal model for the given projective dynamical system. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representative with minimal resultant in the conjugacy class of ``f`` returned. INPUT: - ``f`` -- dynamical system on the projective line with minimal resultant - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the `PGL_2` transformation to conjugate this map to the calculated models - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: - a dynamical system - (optional) a `2 \times 2` matrix EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y]) sage: m = matrix(QQ,2,2,[5,1,0,1]) sage: g = f.conjugate(m) sage: g.normalize_coordinates() sage: g.resultant().factor() 2^4 * 3^4 * 5^12 sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_minimal sage: HS_minimal(g).resultant().factor() 2^4 * 3^4 sage: HS_minimal(g, D=[2]).resultant().factor() 2^4 * 3^4 * 5^12 sage: F,m = HS_minimal(g, return_transformation=True) sage: g.conjugate(m) == F True """ F = copy(f) d = F.degree() F.normalize_coordinates() MS = MatrixSpace(ZZ, 2, 2) m = MS.one() res = ZZ(F.resultant()) if D is None: D = res.prime_divisors() # minimize for each prime for p in D: vp = res.valuation(p) minimal = False while not minimal: if (d % 2 == 0 and vp < d) or (d % 2 == 1 and vp < 2 * d): # must be minimal minimal = True break minimal = True t = MS([1, 0, 0, p]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = F1.resultant() vp1 = res1.valuation(p) if vp1 < vp: # check if smaller F = F1 vp = vp1 m = m * t # keep track of conjugation minimal = False else: # still search for smaller for b in range(p): t = matrix(ZZ,2,2,[p, b, 0, 1]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 < vp: # check if smaller F = F1 m = m * t # keep track of transformation minimal = False vp = vp1 break # exit for loop if return_transformation: return F, m return F