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 """ self._matrix = coxeter_matrix self._index_set = index_set n = ZZ(coxeter_matrix.nrows()) MS = MatrixSpace(base_ring, n, sparse=True) # 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() + MS({(i, j): val(coxeter_matrix[i, j]) for j in range(n)}) for i in range(n)] FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring, gens, category=CoxeterGroups())
def matId(n): Id = [] n2 = n.quo_rem(2)[0] for j in range(n2): MSn = MatrixSpace(F, n2-j, n2-j) Id.append(MSn.identity_matrix()) return Id
def matA(n): A = [] n2 = n.quo_rem(2)[0] for j in range(n2+2): MS0 = MatrixSpace(F, j, j) I = MS0.identity_matrix() O = MS0(j*j*[1]) A.append(I+O) return A
def __init__(self, modulus, dimension): self.modulus = int(modulus) #The modulus p self.dimension = int(dimension) #The dimension d self.field = FiniteField(modulus) #The underlying field self.space = MatrixSpace(self.field, 1, dimension) #The space of vectors in Z_p^d viewed as matrices self.elements = list(self.space) #An actual list of those vectors self.basis = self.space.basis() #The standard basis for the vector space
def to_matrix(self): r""" Return ``self`` as a matrix. We define a matrix `M_{xy} = \alpha(x, y)` for some element `\alpha \in I_P` in the incidence algebra `I_P` and we order the elements `x,y \in P` by some linear extension of `P`. This defines an algebra (iso)morphism; in particular, multiplication in the incidence algebra goes to matrix multiplication. EXAMPLES:: sage: P = posets.BooleanLattice(2) sage: I = P.incidence_algebra(QQ) sage: I.moebius().to_matrix() [ 1 -1 -1 1] [ 0 1 0 -1] [ 0 0 1 -1] [ 0 0 0 1] sage: I.zeta().to_matrix() [1 1 1 1] [0 1 0 1] [0 0 1 1] [0 0 0 1] TESTS: We check that this is an algebra (iso)morphism:: sage: P = posets.BooleanLattice(4) sage: I = P.incidence_algebra(QQ) sage: mu = I.moebius() sage: (mu*mu).to_matrix() == mu.to_matrix() * mu.to_matrix() True """ P = self.parent() MS = MatrixSpace(P.base_ring(), P._poset.cardinality(), sparse=True) L = P._linear_extension M = copy(MS.zero()) for i, c in self: M[L.index(i[0]), L.index(i[1])] = c M.set_immutable() return M
def __init__(self, n, use_monotone_triangles=True): r""" Initialize ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: TestSuite(A).run() sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) False """ self._n = n self._matrix_space = MatrixSpace(ZZ, n) self._umt = use_monotone_triangles Parent.__init__(self, category=FiniteEnumeratedSets())
def RandomLinearCode(n,k,F): r""" The method used is to first construct a `k \times n` matrix using Sage's random_element method for the MatrixSpace class. The construction is probabilistic but should only fail extremely rarely. INPUT: Integers n,k, with `n>k`, and a finite field F OUTPUT: Returns a "random" linear code with length n, dimension k over field F. EXAMPLES:: sage: C = codes.RandomLinearCode(30,15,GF(2)) sage: C Linear code of length 30, dimension 15 over Finite Field of size 2 sage: C = codes.RandomLinearCode(10,5,GF(4,'a')) sage: C Linear code of length 10, dimension 5 over Finite Field in a of size 2^2 AUTHORS: - David Joyner (2007-05) """ MS = MatrixSpace(F,k,n) for i in range(50): G = MS.random_element() if G.rank() == k: V = span(G.rows(), F) return LinearCodeFromVectorSpace(V) # may not be in standard form MS1 = MatrixSpace(F,k,k) MS2 = MatrixSpace(F,k,n-k) Ik = MS1.identity_matrix() A = MS2.random_element() G = Ik.augment(A) return LinearCode(G) # in standard form
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 __classcall_private__(cls, *args, **kwds): """ Normalize input so we can inherit from spare 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 """ # Special case with 0 args and kwds has cartan type if "cartan_type" in kwds and len(args) == 0: args = (CartanType(kwds["cartan_type"]), ) if len(args) == 0: data = [] n = 0 index_set = tuple() cartan_type = None subdivisions = None elif len(args) == 4 and isinstance(args[0], MatrixSpace): # For pickling return typecall(cls, args[0], args[1], args[2], args[3]) elif isinstance(args[0], CartanMatrix): return args[0] else: cartan_type = None dynkin_diagram = None subdivisions = None from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class if isinstance(args[0], DynkinDiagram_class): dynkin_diagram = args[0] cartan_type = args[0]._cartan_type else: try: cartan_type = CartanType(args[0]) 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 = dict( (index_set[i], i) for i in range(len(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(args[0]) if not is_generalized_cartan_matrix(M): raise ValueError( "The input matrix is not a generalized Cartan matrix.") n = M.ncols() if "cartan_type" in kwds: cartan_type = CartanType(kwds["cartan_type"]) elif n == 1: cartan_type = CartanType(['A', 1]) elif kwds.get("cartan_type_check", True): cartan_type = find_cartan_type_from_matrix(M) data = M.dict() subdivisions = M._subdivisions if len(args) == 1: if cartan_type is not None: index_set = tuple(cartan_type.index_set()) else: index_set = tuple(range(n)) elif len(args) == 2: index_set = tuple(args[1]) if len(index_set) != n and len(set(index_set)) != n: raise ValueError("The given index set is not valid.") else: raise ValueError("Too many arguments.") mat = typecall(cls, MatrixSpace(ZZ, n, sparse=True), data, cartan_type, index_set) mat._subdivisions = subdivisions return mat
################################################################################ from sage.rings.arith import euler_phi, lcm, gcd, divisors, get_inverse_mod, get_gcd, factor from sage.modular.modsym.p1list import lift_to_sl2z from congroup_generic import CongruenceSubgroup from sage.modular.cusps import Cusp from sage.misc.cachefunc import cached_method # Just for now until we make an SL_2 group type. from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.matrix.matrix_space import MatrixSpace from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.matrix.constructor import matrix Mat2Z = MatrixSpace(ZZ,2) _gammaH_cache = {} def GammaH_constructor(level, H): r""" Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of `SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\ c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified subgroup of `(\ZZ/N\ZZ)^\times`. INPUT: - level -- an integer - H -- either 0, 1, or a list * If H is a list, return `\Gamma_H(N)`, where `H`
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 type(args[0]) == type([]): 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 _matrix_space_3x3(self): from sage.matrix.matrix_space import MatrixSpace return MatrixSpace(self._ring, 3)
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 self._index_set = index_set n = ZZ(coxeter_matrix.nrows()) # 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[i, j]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. # This describes the bilinear form corresponding to this # Coxeter system, and might lead us out of our base ring. base_field = base_ring.fraction_field() MS2 = MatrixSpace(base_field, n, sparse=True) MC2 = MS2._get_matrix_class() self._bilinear = MC2( MS2, entries={ (i, j): val(coxeter_matrix[i, j]) / base_field(-2) for i in range(n) for j in range(n) if coxeter_matrix[i, j] != 2 }, coerce=True, copy=True, ) self._bilinear.set_immutable() 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()``. is_finite = self._finite_recognition() if is_finite: category = category.Finite() else: category = category.Infinite() FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring, gens, category=category)
def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False): """ This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: - ``type`` -- one of the following strings - ``'modular'`` (default) -- A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [A96]_. - ``'random'`` -- Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM02]_. - ``'ideal'`` -- Special case of modular. Allows for a more compact representation proposed by [LM06]_. - ``'cyclotomic'`` -- Special case of ideal. Allows for efficient processing proposed by [LM06]_. - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [R05]_. - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: Modular basis:: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] Random basis:: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] Ideal bases with quotient x^n-1, m=2*n are NTRU bases:: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [-3 4 -2 -3 0 1 0 0] [-3 -3 4 -2 0 0 1 0] [-2 -3 -3 4 0 0 0 1] Ideal bases also work with polynomials:: sage: R.<t> = PolynomialRing(ZZ) sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 1 4 -3 1 0 0 0] [-3 4 1 4 0 1 0 0] [ 4 -3 4 1 0 0 1 0] [ 1 4 -3 4 0 0 0 1] Cyclotomic bases with n=2^k are SWIFFT bases:: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [ 3 4 -2 -3 0 1 0 0] [ 3 3 4 -2 0 0 1 0] [ 2 3 3 4 0 0 0 1] Dual modular bases are related to Regev's famous public-key encryption [R05]_:: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] Relation of primal and dual bases:: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True TESTS: Test some bad quotient polynomials:: sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) Traceback (most recent call last): ... TypeError: unable to convert cos(x) to an integer sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) Traceback (most recent call last): ... ValueError: ideal basis requires n = quotient.degree() sage: R.<u,v> = ZZ[] sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=u+v) Traceback (most recent call last): ... TypeError: quotient should be a univariate polynomial We are testing output format choices:: sage: sage.crypto.gen_lattice(m=10, q=11, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, ntl=True) [ [11 0 0 0 0 0 0 0 0 0] [0 11 0 0 0 0 0 0 0 0] [0 0 11 0 0 0 0 0 0 0] [0 0 0 11 0 0 0 0 0 0] [2 4 3 5 1 0 0 0 0 0] [1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] ] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] [-1 1 0 1 0 1 1 0 1 1] [-1 0 0 0 -1 1 1 -2 0 0] [-1 -1 0 1 1 0 0 1 1 -1] [ 1 0 -1 0 0 0 -2 -2 0 0] [ 2 -1 0 0 1 0 1 0 0 -1] [-1 1 -1 0 1 -1 1 0 -1 -2] [ 0 0 -1 3 0 0 0 -1 -1 -1] [ 0 -1 0 -1 2 0 -1 0 0 2] [ 0 1 1 0 1 1 -2 1 -1 -2] REFERENCES: .. [A96] Miklos Ajtai. Generating hard instances of lattice problems (extended abstract). STOC, pp. 99--108, ACM, 1996. .. [GM02] Daniel Goldstein and Andrew Mayer. On the equidistribution of Hecke points. Forum Mathematicum, 15:2, pp. 165--189, De Gruyter, 2003. .. [LM06] Vadim Lyubashevsky and Daniele Micciancio. Generalized compact knapsacks are collision resistant. ICALP, pp. 144--155, Springer, 2006. .. [R05] Oded Regev. On lattices, learning with errors, random linear codes, and cryptography. STOC, pp. 84--93, ACM, 2005. """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.constructor import identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed is not None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m-n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient is None: raise ValueError('ideal bases require a quotient polynomial') try: quotient = quotient.change_ring(ZZ_q) except (AttributeError, TypeError): quotient = quotient.polynomial(base_ring=ZZ_q) P = quotient.parent() # P should be a univariate polynomial ring over ZZ_q if not is_PolynomialRing(P): raise TypeError("quotient should be a univariate polynomial") assert P.base_ring() is ZZ_q if quotient.degree() != n: raise ValueError('ideal basis requires n = quotient.degree()') R = P.quotient(quotient) for i in range(m//n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m//n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a-q) < abs(a): return a-q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()] ], subdivide=False) else: B = block_matrix([[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), ZZ(q)]], subdivide=False) for i in range(m//2): B.swap_rows(i,m-i-1) if ntl and lattice: raise ValueError("Cannot specify ntl=True and lattice=True " "at the same time") if ntl: return B._ntl_() elif lattice: from sage.modules.free_module_integer import IntegerLattice return IntegerLattice(B) else: return B
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 pi_bdry_c_bar.iteritems(): 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 gen_lattice(type='modular', n=4, m=8, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False): r""" This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: - ``type`` -- one of the following strings - ``'modular'`` (default) -- A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [Aj1996]_. - ``'random'`` -- Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM2002]_. - ``'ideal'`` -- Special case of modular. Allows for a more compact representation proposed by [LM2006]_. - ``'cyclotomic'`` -- Special case of ideal. Allows for efficient processing proposed by [LM2006]_. - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [Reg2005]_. - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: Modular basis:: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] Random basis:: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] Ideal bases with quotient x^n-1, m=2*n are NTRU bases:: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [-2 -3 -3 4 1 0 0 0] [ 4 -2 -3 -3 0 1 0 0] [-3 4 -2 -3 0 0 1 0] [-3 -3 4 -2 0 0 0 1] Ideal bases also work with polynomials:: sage: R.<t> = PolynomialRing(ZZ) sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 1 4 -3 3 1 0 0 0] [ 3 1 4 -3 0 1 0 0] [-3 3 1 4 0 0 1 0] [ 4 -3 3 1 0 0 0 1] Cyclotomic bases with n=2^k are SWIFFT bases:: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [-2 -3 -3 4 1 0 0 0] [-4 -2 -3 -3 0 1 0 0] [ 3 -4 -2 -3 0 0 1 0] [ 3 3 -4 -2 0 0 0 1] Dual modular bases are related to Regev's famous public-key encryption [Reg2005]_:: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] Relation of primal and dual bases:: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True TESTS: Test some bad quotient polynomials:: sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) Traceback (most recent call last): ... TypeError: self must be a numeric expression sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) Traceback (most recent call last): ... ValueError: ideal basis requires n = quotient.degree() sage: R.<u,v> = ZZ[] sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=u+v) Traceback (most recent call last): ... TypeError: quotient should be a univariate polynomial We are testing output format choices:: sage: sage.crypto.gen_lattice(m=10, q=11, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, ntl=True) [ [11 0 0 0 0 0 0 0 0 0] [0 11 0 0 0 0 0 0 0 0] [0 0 11 0 0 0 0 0 0 0] [0 0 0 11 0 0 0 0 0 0] [2 4 3 5 1 0 0 0 0 0] [1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] ] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] [-1 1 0 1 0 1 1 0 1 1] [-1 0 0 0 -1 1 1 -2 0 0] [-1 -1 0 1 1 0 0 1 1 -1] [ 1 0 -1 0 0 0 -2 -2 0 0] [ 2 -1 0 0 1 0 1 0 0 -1] [-1 1 -1 0 1 -1 1 0 -1 -2] [ 0 0 -1 3 0 0 0 -1 -1 -1] [ 0 -1 0 -1 2 0 -1 0 0 2] [ 0 1 1 0 1 1 -2 1 -1 -2] """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.constructor import identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed is not None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m - n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient is None: raise ValueError('ideal bases require a quotient polynomial') try: quotient = quotient.change_ring(ZZ_q) except (AttributeError, TypeError): quotient = quotient.polynomial(base_ring=ZZ_q) P = quotient.parent() # P should be a univariate polynomial ring over ZZ_q if not is_PolynomialRing(P): raise TypeError("quotient should be a univariate polynomial") assert P.base_ring() is ZZ_q if quotient.degree() != n: raise ValueError('ideal basis requires n = quotient.degree()') R = P.quotient(quotient) for i in range(m // n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2 * n, n, -1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m // n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a - q) < abs(a): return a - q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()]], subdivide=False) else: B = block_matrix( [[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), ZZ(q)]], subdivide=False) for i in range(m // 2): B.swap_rows(i, m - i - 1) if ntl and lattice: raise ValueError("Cannot specify ntl=True and lattice=True " "at the same time") if ntl: return B._ntl_() elif lattice: from sage.modules.free_module_integer import IntegerLattice return IntegerLattice(B) else: return B
def is_global(M, r, s, return_symbol=False): r""" Test if the FiniteQuadraticModule M can be represented by a Z-lattice of signature ``(r,s)``. INPUT: -``M`` -- FiniteQuadraticModule - ``r`` -- positive integer - ``s`` -- positive integer OUTPUT: - boolean """ J = M.jordan_decomposition() symbols = {} n = r + s sig = r - s for A in J: p = A[1][0] if p not in symbols: symbols[p] = list() sym = list(A[1][1:len(A[1])]) if p == 2: if len(A[1]) == 4: sym.append(0) sym.append(0) else: if sym[3].kronecker(2) == sym[2]: det = sym[3] % 8 else: if sym[2] == -1: det = 3 else: det = 1 sym = [sym[0], sym[1], det, 1, sym[3] % 8] #print sym #if sym[1]==1: # if sym[2].kronecker(2)==sym[4].kronecker(2): # sym[2]=sym[4] # else: # return False #print p, sym symbols[p].append(sym) D = M.order() * (-1)**s for p in symbols.keys(): prank = sum([sym[1] for sym in symbols[p]]) v = sum([sym[0] * sym[1] for sym in symbols[p]]) Dp = D // (p**v) if prank != n: eps = (Dp * Integer(prod([sym[2] for sym in symbols[p]]))).kronecker(p) if p == 2: if eps == -1: eps = 3 symbols[p].append([0, n - prank, eps, 0, 0]) else: if eps == -1: for x in Zmod(p): if not x.is_square(): eps = x break symbols[p].append([0, n - prank, eps]) symbol = GenusSymbol_global_ring(MatrixSpace(ZZ, r + s, r + s).one()) symbol._local_symbols = [ Genus_Symbol_p_adic_ring(p, syms) for p, syms in symbols.items() ] symbol._signature = (r, s) #print r,s, symbol._local_symbols isglob = is_GlobalGenus(symbol) if return_symbol: return symbol, isglob else: return isglob
def search_global_symbols(n, D): rank = 2 + n sign = 2 - n csymbols = list() # a list of canonical symbols to avoid duplicates symbols = list() #print D D = (-1)**n * D fac = Integer(D).factor() symbols = list() for p, v in fac: psymbols = list() parts = partitions(v) Dp = D // (p**v) for vs in parts: #print "partition:", vs l = list() # list of p-symbols corresponding to the partition vs if len(vs) <= rank: exponents = Set(list(vs)) # now we set up a list ll for each vv in the partition vs # that contains an entry for each possibility # and then update l with ll (see below) if p == 2: for vv in exponents: mult = vs.count(vv) ll = list() for t in [0, 1]: # even(0) or odd(1) type for det in [1, 3, 5, 7]: # the possible determinants if mult % 2 == 0 and t == 0: ll.append([vv, mult, det, 0, 0]) if mult == 1: odds = [det] elif mult == 2: if det in [1, 7]: odds = [0, 2, 6] else: odds = [2, 4, 6] else: odds = [ o for o in range(8) if o % 2 == mult % 2 ] for oddity in odds: if t == 1: ll.append([vv, mult, det, 1, oddity]) #else: #ll.append([vv,1,det,0,0]) #if mult % 2 == 0 and mult>2: # for x in range(1,Integer(mult)/Integer(2)): # if mult-2*x==2 and det in [1,7] and oddity not in [0,2,6]: # continue # elif mult-2*x==2 and det in [3,5] and oddity not in [2,4,6]: # continue # ll.append([[vv,2*x,det,0,0],[vv,mult-2*x,det,1,oddity]]) #print "ll:\n",ll if len(l) == 0: for t in ll: if type(t[0]) == list: l.append({p: t}) else: l.append({p: [t]}) else: newl = list() for t in ll: for sym in l: newsym = deepcopy(sym) #print newsym if type(t[0]) == list: newsym[p] = newsym[p] + t else: newsym[p].append(t) #print newsym newl.append(newsym) #print l l = newl #print "l:\n",l else: for vv in exponents: ll = [[vv, vs.count(vv), 1], [vv, vs.count(vv), -1]] if len(l) == 0: for t in ll: l.append({p: [t]}) else: newl = list() for t in ll: for sym in l: sym[p].append(t) newl.append(sym) l = newl #print "l=\n",l #print "psymbols=\n",psymbols #print psymbols+l psymbols = psymbols + l if len(symbols) == 0: symbols = psymbols else: symbols_new = list() for sym in symbols: for psym in psymbols: newsym = deepcopy(sym) newsym.update(psym) symbols_new.append(newsym) symbols = symbols_new global_symbols = [] for sym in symbols: #print sym for p in sym.keys(): prank = sum([s[1] for s in sym[p]]) v = sum([s[0] * s[1] for s in sym[p]]) Dp = D // (p**v) if prank != rank: eps = (Dp * Integer(prod([s[2] for s in sym[p]]))).kronecker(p) if p == 2: if eps == -1: eps = 3 sym[p].insert(0, [0, rank - prank, eps, 0, 0]) else: if eps == -1: for x in Zmod(p): if not x.is_square(): eps = x break sym[p].insert(0, [0, rank - prank, eps]) symbol = GenusSymbol_global_ring(MatrixSpace(ZZ, rank, rank).one()) symbol._local_symbols = [ Genus_Symbol_p_adic_ring(p, syms) for p, syms in sym.items() ] symbol._signature = (2, n) #print symbol._local_symbols isglob = is_GlobalGenus(symbol) if isglob: #print "GLOBAL SYMBOL:" #print symbol._local_symbols #return symbol #for s in symbol._local_symbols: # s = s.canonical_symbol() append = True for j, s in enumerate(symbol._local_symbols): if s._prime == 2: sc = deepcopy(symbol) sc._local_symbols[j] = sc._local_symbols[ j].canonical_symbol() if csymbols.count(sc) > 0: append = False else: csymbols.append(sc) break if append: global_symbols.append(symbol) return global_symbols
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 = 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 = 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 = 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 = 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 = 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 TrivialCode(F,n): MS = MatrixSpace(F,1,n) return LinearCode(MS(0))
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, *args, **kwargs): MatrixSpace.__init__(self, *args, **kwargs) from lazy import LazyApproximation_matrix self._lazy_class = LazyApproximation_matrix self._zero = self(0) self._lazy_zero = self._lazy_class(self,[])
class MatrixRec(object): r""" A matrix recurrence simultaneously generating the coefficients and partial sums of solutions of an ODE, and possibly derivatives of this solution. Note: Mathematically, the recurrence matrix has the structure of a StepMatrix (depending on parameters). However, this class does not derive from StepMatrix as the data structure is different. """ 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() # Using the primitive part here would break the computation of # residuals! (Cf. local_solutions.) # recop = diffop.to_S(Rops).primitive_part().numerator() recop = diffop.to_S(Rops) 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 __call__(self, n): stepmat = self.StepMatrix_class() stepmat.idx_start = n stepmat.idx_end = n + 1 stepmat.rec_den = self.rec_den(n) stepmat.rec_mat = self.Mat_rec.matrix() for i in xrange(self.ordrec - 1): stepmat.rec_mat[i, i + 1] = stepmat.rec_den for i in xrange(self.ordrec): stepmat.rec_mat[self.ordrec - 1, i] = self.rec_coeffs[i](n) stepmat.pow_num = self.pow_num stepmat.pow_den = self.pow_den # TODO: fix redundancy--the rec_den*pow_den probabably doesn't belong # here # XXX: should we give a truncation order? den = stepmat.rec_den * stepmat.pow_den den = self.series_class_sums(self.Series_sums, [den]) stepmat.sums_row = self.Mat_sums_row.matrix() stepmat.sums_row[0, self.orddelta] = den stepmat.ord = self.derivatives stepmat.BigScalars = self.Series_sums # XXX unused in arb case stepmat.Mat_big_scalars = self.Mat_series_sums return stepmat def one(self, n): stepmat = self.StepMatrix_class() stepmat.idx_start = stepmat.idx_end = n stepmat.rec_mat = self.Mat_rec.identity_matrix() stepmat.rec_den = self.rec_den.base_ring().one() stepmat.pow_num = self.pow_num.parent().one() stepmat.pow_den = self.pow_den.parent().one() stepmat.sums_row = self.Mat_sums_row.matrix() stepmat.ord = self.derivatives stepmat.BigScalars = self.Series_sums # XXX unused in arb case stepmat.Mat_big_scalars = self.Mat_series_sums return stepmat def binsplit(self, low, high): if high - low <= self.binsplit_threshold: mat = self.one(low) for n in xrange(low, high): mat.imulleft(self(n)) else: mid = (low + high) // 2 mat = self.binsplit(low, mid) mat.imulleft(self.binsplit(mid, high)) return mat def __repr__(self): return pprint.pformat(self.__dict__) # XXX: needs testing, especially when rop.valuation() > 0 def normalized_residual(self, maj, prod, n, j): r""" Compute the normalized residual associated with the fundamental solution of index j. TESTS:: sage: from ore_algebra import * sage: DOP, t, D = DifferentialOperators() sage: ode = D + 1/4/(t - 1/2) sage: ode.numerical_transition_matrix([0,1+I,1], 1e-100, algorithm='binsplit') [[0.707...2078...] + [0.707...]*I] """ r, s = self.orddeq, self.ordrec IC = bounds.IC # Compute the "missing" coefficients u(n-s), ..., u(n-s'-1) s'=s-r): # indeed, it is convenient to compute the residuals starting from # u(n-s), ..., u(n-1), while our recurrence matrices produce the partial # sum of index n along with the vector [u(n-s'), ..., u(n+r-1)]. last = [IC.zero()] * r # u(n-s), ..., u(n-s'-1) last.extend([ IC(c) / IC(prod.rec_den) # u(n-s'), ..., u(n+r-1) for c in prod.rec_mat.column(s - r + j) ]) # XXX: check column index rop = self.recop v = rop.valuation() for i in xrange(r - 1, -1, -1): # compute u(n-s+i) last[i] = ~(rop[v](n - s + i)) * sum( rop[k](n - s + i) * last[i + k] # u(n-s+i) for k in xrange(v + 1, s + 1)) # Now compute the residual. WARNING: this residual must correspond to # the operator stored in maj.dop, which typically isn't self.diffop (but # an operator in θx equal to x^k·self.diffop for some k). # XXX: do not recompute this every time! bwrnp = [[[pol(n + i)] for pol in self.bwrec] for i in range(s)] altlast = [[c] for c in reversed(last[:s])] return maj.normalized_residual(n, altlast, bwrnp) def normalized_residuals(self, maj, prod, n): return [ self.normalized_residual(maj, prod, n, j) for j in xrange(self.orddeq) ] def term(self, prod, parent, j): r""" Given a prodrix representing a product B(n-1)···B(0) where B is the recurrence matrix associated to some differential operator P, return the term of index n of the fundamental solution of P of the form y[j](z) = z^j + O(z^r), 0 <= j < r = order(P). """ orddelta = self.orddelta num = parent(prod.rec_mat[orddelta + j, orddelta]) * parent( prod.pow_num[0]) den = parent(prod.rec_den) * parent(prod.pow_den) return num / den def partial_sums(self, prod, ring, rows): r""" Return a matrix of partial sums of the series and its derivatives. """ numer = matrix(ring, rows, self.orddeq, lambda i, j: prod.sums_row[0, self.orddelta + j][i]) denom = ring(prod.rec_den) * ring(prod.pow_den) return numer / denom def error_estimate(self, prod): orddelta = self.orddelta num1 = sum( abs(prod.rec_mat[orddelta + j, orddelta]) for j in range(self.orddeq)) num2 = sum(abs(a) for a in prod.pow_num) den = abs(prod.rec_den) * abs(prod.pow_den) return num1 * num2 / den
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() # Using the primitive part here would break the computation of # residuals! (Cf. local_solutions.) # recop = diffop.to_S(Rops).primitive_part().numerator() recop = diffop.to_S(Rops) 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 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 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: nab.set_connection_form(0, 0)[1] = I*A(t) sage: nab.set_immutable() sage: nab[0, 0].display() connection (0,0) of bundle connection nabla^E w.r.t. Local frame (E|_M, (e_0)) = I*A(t) dx .. NOTE:: The characteristic form is strongly linked to the connection which is why we must make the connection unchangeable, i.e. immutable, with the command :meth:`sage.manifolds.differentiable.bundle_connection.BundleConnection.set_immutable` before we can use :meth:`get_form`. 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: base_space = self._base_space 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 base_space._get_min_covering( connection._coefficients): 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 = 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""" 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 self.__hecke_matrices.has_key(L): 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 __classcall_private__(cls, data=None, index_set=None, coxeter_type=None, cartan_type=None, coxeter_type_check=True): r""" A Coxeter matrix can we created via a graph, a Coxeter type, or a matrix. .. NOTE:: To disable the Coxeter type check, use the optional argument ``coxeter_type_check = False``. EXAMPLES:: sage: C = CoxeterMatrix(['A',1,1],['a','b']) sage: C2 = CoxeterMatrix([[1, -1], [-1, 1]]) sage: C3 = CoxeterMatrix(matrix([[1, -1], [-1, 1]]), [0, 1]) sage: C == C2 and C == C3 True Check with `\infty` because of the hack of using `-1` to represent `\infty` in the Coxeter matrix:: sage: G = Graph([(0, 1, 3), (1, 2, oo)]) sage: W1 = CoxeterMatrix([[1, 3, 2], [3, 1, -1], [2, -1, 1]]) sage: W2 = CoxeterMatrix(G) sage: W1 == W2 True sage: CoxeterMatrix(W1.coxeter_graph()) == W1 True The base ring of the matrix depends on the entries given:: sage: CoxeterMatrix([[1,-1],[-1,1]])._matrix.base_ring() Integer Ring sage: CoxeterMatrix([[1,-3/2],[-3/2,1]])._matrix.base_ring() Rational Field sage: CoxeterMatrix([[1,-1.5],[-1.5,1]])._matrix.base_ring() Real Field with 53 bits of precision """ if not data: if coxeter_type: data = CoxeterType(coxeter_type) elif cartan_type: data = CoxeterType(CartanType(cartan_type)) # Special cases with no arguments passed if not data: data = [] n = 0 index_set = tuple() coxeter_type = None base_ring = ZZ mat = typecall(cls, MatrixSpace(base_ring, n, sparse=False), data, coxeter_type, index_set) mat._subdivisions = None return mat if isinstance(data, CoxeterMatrix): # Initiate from itself return data # Initiate from a graph: # TODO: Check if a CoxeterDiagram once implemented if isinstance(data, Graph): return cls._from_graph(data, coxeter_type_check) # Get the Coxeter type coxeter_type = None from sage.combinat.root_system.cartan_type import CartanType_abstract if isinstance(data, CartanType_abstract): coxeter_type = data.coxeter_type() else: try: coxeter_type = CoxeterType(data) except (TypeError, ValueError, NotImplementedError): pass # Initiate from a Coxeter type if coxeter_type: return cls._from_coxetertype(coxeter_type) # TODO:: remove when oo is possible in matrices. n = len(data[0]) data = [x if x != infinity else -1 for r in data for x in r] data = matrix(n, n, data) # until here # Get the index set if index_set: index_set = tuple(index_set) else: index_set = tuple(range(1, n + 1)) if len(set(index_set)) != n: raise ValueError("the given index set is not valid") return cls._from_matrix(data, coxeter_type, index_set, coxeter_type_check)
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 not ss_points_dic.has_key(xj): 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 approx_module(self, M=None): if M is None: M = self.dimension() return MatrixSpace(self._R, M, 1)
O = MS0(j*j*[1]) A.append(I+O) return A def matId(n): Id = [] n2 = n.quo_rem(2)[0] for j in range(n2): MSn = MatrixSpace(F, n2-j, n2-j) Id.append(MSn.identity_matrix()) return Id def MS2(n): n2 = n.quo_rem(2)[0]; return MatrixSpace(F, n2, n2) I2 = lambda n: MS2(n).identity_matrix() # non-diagonal constructions MS7 = MatrixSpace(F, 7, 7) And7 = MS7([[1, 1, 1, 0, 0, 1, 1],\ [1, 1, 1, 0, 1, 0, 1],\ [1, 1, 1, 0, 1, 1, 0],\ [0, 0, 0, 0, 1, 1, 1],\ [0, 1, 1, 1, 0, 0, 0],\ [1, 0, 1, 1, 0, 0, 0],\ [1, 1, 0, 1, 0, 0, 0]]) MS8 = MatrixSpace(ZZ, 8, 8) H8 = MS8([[1, 1, 1, 1, 1, 1, 1, 1],\ [1, -1, 1, -1, 1, -1, 1, -1],\ [1, 1, -1, -1, 1, 1, -1, -1],\ [1, -1, -1, 1, 1, -1, -1, 1],\ [1, 1, 1, 1, -1, -1, -1, -1],\ [1, -1, 1, -1, -1, 1, -1, 1],\ [1, 1, -1, -1, -1, -1, 1, 1],\
def as_permutation_group(self, algorithm=None): r""" Return a permutation group representation for the group. In most cases occurring in practice, this is a permutation group of minimal degree (the degree begin determined from orbits under the group action). When these orbits are hard to compute, the procedure can be time-consuming and the degree may not be minimal. INPUT: - ``algorithm`` -- ``None`` or ``'smaller'``. In the latter case, try harder to find a permutation representation of small degree. OUTPUT: A permutation group isomorphic to ``self``. The ``algorithm='smaller'`` option tries to return an isomorphic group of low degree, but is not guaranteed to find the smallest one. EXAMPLES:: sage: MS = MatrixSpace(GF(2), 5, 5) sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]]) sage: G = MatrixGroup([A]) sage: G.as_permutation_group() Permutation Group with generators [(1,2)] sage: MS = MatrixSpace( GF(7), 12, 12) sage: GG = gap("ImfMatrixGroup( 12, 3 )") sage: GG.GeneratorsOfGroup().Length() 3 sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n",""))) sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n",""))) sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n",""))) sage: G = MatrixGroup([g1, g2, g3]) sage: G.cardinality() 21499084800 sage: set_random_seed(0); current_randstate().set_seed_gap() sage: P = G.as_permutation_group() sage: P.cardinality() 21499084800 sage: P.degree() # random output 144 sage: set_random_seed(3); current_randstate().set_seed_gap() sage: Psmaller = G.as_permutation_group(algorithm="smaller") sage: Psmaller.cardinality() 21499084800 sage: Psmaller.degree() # random output 108 In this case, the "smaller" option returned an isomorphic group of lower degree. The above example used GAP's library of irreducible maximal finite ("imf") integer matrix groups to construct the MatrixGroup G over GF(7). The section "Irreducible Maximal Finite Integral Matrix Groups" in the GAP reference manual has more details. TESTS:: sage: A= matrix(QQ, 2, [0, 1, 1, 0]) sage: B= matrix(QQ, 2, [1, 0, 0, 1]) sage: a, b= MatrixGroup([A, B]).as_permutation_group().gens() sage: a.order(), b.order() (2, 1) """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of # doctests and/or architecture from sage.groups.perm_gps.permgroup import PermutationGroup if not self.is_finite(): raise NotImplementedError("Group must be finite.") n = self.degree() MS = MatrixSpace(self.base_ring(), n, n) mats = [] # initializing list of mats by which the gens act on self for g in self.gens(): p = MS(g.matrix()) m = p.rows() mats.append(m) mats_str = str(gap([[list(r) for r in m] for m in mats])) gap.eval("iso:=IsomorphismPermGroup(Group("+mats_str+"))") if algorithm == "smaller": gap.eval("small:= SmallerDegreePermutationRepresentation( Image( iso ) );") C = gap("Image( small )") else: C = gap("Image( iso )") return PermutationGroup(gap_group=C, canonicalize=False)
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 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
class AlternatingSignMatrices(Parent, UniqueRepresentation): r""" Class of all `n \times n` alternating sign matrices. An alternating sign matrix of size `n` is an `n \times n` matrix of `0`s, `1`s and `-1`s such that the sum of each row and column is `1` and the non-zero entries in each row and column alternate in sign. Alternating sign matrices of size `n` are in bijection with :class:`monotone triangles <MonotoneTriangles>` with `n` rows. INPUT: - `n` -- an integer, the size of the matrices. - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the generation of the matrices uses monotone triangles, else it will use the earlier and now obsolete contre-tableaux implementation; must be ``True`` to generate a lattice (with the ``lattice`` method) EXAMPLES: This will create an instance to manipulate the alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: A Alternating sign matrices of size 3 sage: A.cardinality() 7 Notably, this implementation allows to make a lattice of it:: sage: L = A.lattice() sage: L Finite lattice containing 7 elements sage: L.category() Category of facade finite lattice posets """ def __init__(self, n, use_monotone_triangles=True): r""" Initialize ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: TestSuite(A).run() sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) False """ self._n = n self._matrix_space = MatrixSpace(ZZ, n) self._umt = use_monotone_triangles Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): r""" Return a string representation of ``self``. TESTS:: sage: A = AlternatingSignMatrices(4); A Alternating sign matrices of size 4 """ return "Alternating sign matrices of size %s" % self._n def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A._repr_option('element_ascii_art') True """ return self._matrix_space._repr_option(key) def __contains__(self, asm): """ Check if ``asm`` is in ``self``. TESTS:: sage: A = AlternatingSignMatrices(3) sage: [[0,1,0],[1,0,0],[0,0,1]] in A True sage: [[0,1,0],[1,-1,1],[0,1,0]] in A True sage: [[0, 1],[1,0]] in A False sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A False sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A False """ if isinstance(asm, AlternatingSignMatrix): return asm._matrix.nrows() == self._n try: asm = self._matrix_space(asm) except (TypeError, ValueError): return False for row in asm: pos = False for val in row: if val > 0: if pos: return False else: pos = True elif val < 0: if pos: pos = False else: return False if not pos: return False if any(sum(row) != 1 for row in asm.columns()): return False return True def _element_constructor_(self, asm): """ Construct an element of ``self``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt [1 0 0] [0 1 0] [0 0 1] sage: elt.parent() is A True sage: A([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] """ if isinstance(asm, AlternatingSignMatrix): if asm.parent() is self: return asm raise ValueError("Cannot convert between alternating sign matrices of different sizes") if asm in MonotoneTriangles(self._n): return self.from_monotone_triangle(asm) return self.element_class(self, self._matrix_space(asm)) Element = AlternatingSignMatrix def _an_element_(self): """ Return an element of ``self``. """ return self.element_class(self, self._matrix_space.identity_matrix()) def from_monotone_triangle(self, triangle): r""" Return an alternating sign matrix from a monotone triangle. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) [0 0 1] [0 1 0] [1 0 0] """ n = len(triangle) if n != self._n: raise ValueError("Incorrect size") asm = [] prev = [0]*n for line in reversed(triangle): v = [1 if j+1 in reversed(line) else 0 for j in range(n)] row = [a-b for (a, b) in zip(v, prev)] asm.append(row) prev = v return self.element_class(self, self._matrix_space(asm)) def size(self): r""" Return the size of the matrices in ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: A.size() 4 """ return self._n def cardinality(self): r""" Return the cardinality of ``self``. The number of `n \times n` alternating sign matrices is equal to .. MATH:: \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} EXAMPLES:: sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] """ return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) for k in range(self._n)] )) def matrix_space(self): """ Return the underlying matrix space. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.matrix_space() Full MatrixSpace of 3 by 3 dense matrices over Integer Ring """ return self._matrix_space def __iter__(self): r""" Iterator on the alternating sign matrices of size `n`. If defined using ``use_monotone_triangles``, this iterator will use the iteration on the monotone triangles. Else, it will use the iteration on contre-tableaux. TESTS:: sage: A = AlternatingSignMatrices(4) sage: len(list(A)) 42 """ if self._umt: for t in MonotoneTriangles(self._n): yield self.from_monotone_triangle(t) else: for c in ContreTableaux(self._n): yield from_contre_tableau(c) def _lattice_initializer(self): r""" Return a 2-tuple to use in argument of ``LatticePoset``. For more details about the cover relations, see ``MonotoneTriangles``. Notice that the returned matrices are made immutable to ensure their hashability required by ``LatticePoset``. EXAMPLES: Proof of the lattice property for alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: P = Poset(A._lattice_initializer()) sage: P.is_lattice() True """ assert(self._umt) (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() bij = dict((t, self.from_monotone_triangle(t)) for t in mts) asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] return (asms, rels) def cover_relations(self): r""" Iterate on the cover relations between the alternating sign matrices. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: for (a,b) in A.cover_relations(): ... eval('a, b') ... ( [1 0 0] [0 1 0] [0 1 0] [1 0 0] [0 0 1], [0 0 1] ) ( [1 0 0] [1 0 0] [0 1 0] [0 0 1] [0 0 1], [0 1 0] ) ( [0 1 0] [ 0 1 0] [1 0 0] [ 1 -1 1] [0 0 1], [ 0 1 0] ) ( [1 0 0] [ 0 1 0] [0 0 1] [ 1 -1 1] [0 1 0], [ 0 1 0] ) ( [ 0 1 0] [0 0 1] [ 1 -1 1] [1 0 0] [ 0 1 0], [0 1 0] ) ( [ 0 1 0] [0 1 0] [ 1 -1 1] [0 0 1] [ 0 1 0], [1 0 0] ) ( [0 0 1] [0 0 1] [1 0 0] [0 1 0] [0 1 0], [1 0 0] ) ( [0 1 0] [0 0 1] [0 0 1] [0 1 0] [1 0 0], [1 0 0] ) """ (_, rels) = self._lattice_initializer() return (_ for _ in rels) def lattice(self): r""" Return the lattice of the alternating sign matrices of size `n`, created by ``LatticePoset``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: L = A.lattice() sage: L Finite lattice containing 7 elements """ return LatticePoset(self._lattice_initializer(), cover_relations=True)
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 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 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 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() prev = copy(m) 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
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 conguacy 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 __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 BM_all_minimal(vp, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. This function modifies the Bruin-Molnar algorithm ([BM2012]_) to solve in the inequalities as ``<=`` instead of ``<``. Among the list of solutions is all conjugations that preserve the resultant. From that list the `SL(2,\ZZ)` orbits are identified and one representative from each orbit is returned. This function assumes that the given model is a minimal model. INPUT: - ``vp`` -- a minimal model of a dynamical system on the projective line - ``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 - 13^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: BM_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 - 169*y^3 : x*y^2), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (13*x^3 - y^3 : x*y^2)] :: 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 BM_all_minimal sage: BM_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*y^2), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (3*x^3 - 4*y^3 : x*y^2)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 4^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: cl = BM_all_minimal(f, return_transformation=True) sage: all([f.conjugate(m) == g for g,m in cl]) True """ mp = copy(vp) mp.normalize_coordinates() BR = mp.domain().base_ring() MS = MatrixSpace(QQ, 2) M_Id = MS.one() d = mp.degree() F, G = list(mp) #coordinate polys aff_map = mp.dehomogenize(1) f, g = aff_map[0].numerator(), aff_map[0].denominator() z = aff_map.domain().gen(0) dg = f.parent()(g).degree() Res = mp.resultant() ##### because of how the bound is compute in lemma 3.3 from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine h = f - z*g A = AffineSpace(BR, 1, h.parent().variable_name()) res = DynamicalSystem_affine([h/g], domain=A).homogenize(1).resultant() if D is None: D = ZZ(Res).prime_divisors() # get the conjugations for each prime independently # these are returning (p,k,b) so that the matrix is [p^k,b,0,1] all_pM = [] for p in D: # all_orbits used to scale inequalities to equalities all_pM.append(Min(mp, p, res, M_Id, all_orbits=True)) # need the identity for each prime if [p, 0, 0] not in all_pM[-1]: all_pM[-1].append([p, 0, 0]) #combine conjugations for all primes all_M = [M_Id] for prime_data in all_pM: #these are (p,k,b) so that the matrix is [p^k,b,0,1] new_M = [] if prime_data: p = prime_data[0][0] for m in prime_data: mat = MS([m[0]**m[1], m[2], 0, 1]) new_map = mp.conjugate(mat) new_map.normalize_coordinates() # make sure the resultant didn't change and that it is a different SL(2,ZZ) orbit if (mat == M_Id) or (new_map.resultant().valuation(p) == Res.valuation(p) and mat.det() not in [1,-1]): new_M.append(m) if new_M: all_M = [m1 * MS([m[0]**m[1], m[2], 0, 1]) for m1 in all_M for m in new_M] #get all models with same resultant all_maps = [] for M in all_M: new_map = mp.conjugate(M) new_map.normalize_coordinates() if not [new_map, M] in all_maps: all_maps.append([new_map, M]) #Split into conjugacy classes #We just keep track of the two matrices that come from #the original to get the conjugation that goes between these!! classes = [] for funct, mat in all_maps: if not classes: classes.append([funct, mat]) else: found = False for Func, Mat in classes: #get conjugation M = mat.inverse() * Mat assert funct.conjugate(M) == Func if M.det() in [1,-1]: #same SL(2,Z) orbit found = True break if found is False: classes.append([funct, mat]) if return_transformation: return classes else: return [funct for funct, matr in classes]
def MS2(n): n2 = n.quo_rem(2)[0]; return MatrixSpace(F, n2, n2) I2 = lambda n: MS2(n).identity_matrix()
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 conguacy 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]
def self_dual_codes_binary(n): r""" Returns the dictionary of inequivalent sd codes of length n. For n=4 even, returns the sd codes of a given length, up to (perm) equivalence, the (perm) aut gp, and the type. The number of inequiv "diagonal" sd binary codes in the database of length n is ("diagonal" is defined by the conjecture above) is the same as the restricted partition number of n, where only integers from the set 1,4,6,8,... are allowed. This is the coefficient of `x^n` in the series expansion `(1-x)^{-1}\prod_{2^\infty (1-x^{2j})^{-1}}`. Typing the command f = (1-x)(-1)\*prod([(1-x(2\*j))(-1) for j in range(2,18)]) into Sage, we obtain for the coeffs of `x^4`, `x^6`, ... [1, 1, 2, 2, 3, 3, 5, 5, 7, 7, 11, 11, 15, 15, 22, 22, 30, 30, 42, 42, 56, 56, 77, 77, 101, 101, 135, 135, 176, 176, 231] These numbers grow too slowly to account for all the sd codes (see Huffman+Pless' Table 9.1, referenced above). In fact, in Table 9.10 of [HP], the number B_n of inequivalent sd binary codes of length n is given:: n 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 B_n 1 1 1 2 2 3 4 7 9 16 25 55 103 261 731 According to http://oeis.org/classic/A003179, the next 2 entries are: 3295, 24147. EXAMPLES:: sage: C = self_dual_codes_binary(10) sage: C["10"]["0"]["code"] == C["10"]["0"]["code"].dual_code() True sage: C["10"]["1"]["code"] == C["10"]["1"]["code"].dual_code() True sage: len(C["10"].keys()) # number of inequiv sd codes of length 10 2 sage: C = self_dual_codes_binary(12) sage: C["12"]["0"]["code"] == C["12"]["0"]["code"].dual_code() True sage: C["12"]["1"]["code"] == C["12"]["1"]["code"].dual_code() True sage: C["12"]["2"]["code"] == C["12"]["2"]["code"].dual_code() True """ sd_codes = {} if n == 4: # this code is Type I # [4,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup([ "(2,4)", "(1,2)(3,4)" ]) spectrum = [1, 0, 2, 0, 1] sd_codes_4_0 = {"order autgp":8,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique."} sd_codes["4"] = {"0":sd_codes_4_0} return sd_codes if n == 6: # this is Type I # [6,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( ["(3,6)", "(2,3)(5,6)", "(1,2)(4,5)"] ) spectrum = [1, 0, 3, 0, 3, 0, 1] sd_codes_6_0 = {"order autgp":48,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique"} sd_codes["6"] = {"0":sd_codes_6_0} return sd_codes if n == 8: # the first code is Type I, the second is Type II # the second code is equiv to the extended Hamming [8,4,4] code. # [8,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( ["(4,8)", "(3,4)(7,8)", "(2,3)(6,7)", "(1,2)(5,6)"] ) spectrum = [1, 0, 4, 0, 6, 0, 4, 0, 1] sd_codes_8_0 = {"order autgp":384,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique Type I of this length."} # [8,1]: genmat = I2(n).augment(matA(n)[4]) # G = PermutationGroup( ["(4,5)(6,7)", "(4,6)(5,7)", "(3,4)(7,8)",\ # "(2,3)(6,7)", "(1,2)(5,6)"] ) spectrum = [1, 0, 0, 0, 14, 0, 0, 0, 1] sd_codes_8_1 = {"order autgp":1344,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"II","Comment":"Unique Type II of this length."} sd_codes["8"] = {"0":sd_codes_8_0,"1":sd_codes_8_1} return sd_codes if n == 10: # Both of these are Type I; one has a unique lowest weight codeword # [10,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( ["(5,10)", "(4,5)(9,10)", "(3,4)(8,9)",\ # "(2,3)(7,8)", "(1,2)(6,7)"] ) spectrum = [1, 0, 5, 0, 10, 0, 10, 0, 5, 0, 1] sd_codes_10_0 = {"order autgp":3840,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"No Type II of this length."} # [10,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( ["(5,10)", "(4,6)(7,8)", "(4,7)(6,8)", "(3,4)(8,9)",\ # "(2,3)(7,8)", "(1,2)(6,7)"] ) spectrum = [1, 0, 1, 0, 14, 0, 14, 0, 1, 0, 1] sd_codes_10_1 = {"order autgp":2688,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique lowest weight nonzero codeword."} sd_codes["10"] = {"0":sd_codes_10_0,"1":sd_codes_10_1} return sd_codes if n == 12: # all of these are Type I # [12,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( ["(6,12)", "(5,6)(11,12)", "(4,5)(10,11)", "(3,4)(9,10)",\ # "(2,3)(8,9)", "(1,2)(7,8)"] ) spectrum = [1, 0, 6, 0, 15, 0, 20, 0, 15, 0, 6, 0, 1] sd_codes_12_0 = {"order autgp":48080,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"No Type II of this length."} # [12,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( ["(2,3)(4,7)", "(2,4)(3,7)", "(2,4,9)(3,7,8)", "(2,4,8,10)(3,9)",\ # "(1,2,4,7,8,10)(3,9)", "(2,4,8,10)(3,9)(6,12)", "(2,4,8,10)(3,9)(5,6,11,12)"] ) spectrum = [1, 0, 2, 0, 15, 0, 28, 0, 15, 0, 2, 0, 1] sd_codes_12_1 = {"order autgp":10752,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Smallest automorphism group of these."} # [12,2]: genmat = I2(n).augment(matA(n)[6]) # G = PermutationGroup( ["(5,6)(11,12)", "(5,11)(6,12)", "(4,5)(10,11)", "(3,4)(9,10)",\ # "(2,3)(8,9)", "(1,2)(7,8)"] ) spectrum = [1, 0, 0, 0, 15, 0, 32, 0, 15, 0, 0, 0, 1] sd_codes_12_2 = {"order autgp":23040,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Largest minimum distance of these."} sd_codes["12"] = {"0":sd_codes_12_0,"1":sd_codes_12_1,"2":sd_codes_12_2} return sd_codes if n == 14: # all of these are Type I; one has a unique lowest weight codeword # (there are 4 total inequiv sd codes of n = 14, by Table 9.10 [HP]) # [14,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( ["(7,14)", "(6,7)(13,14)", "(5,6)(12,13)", "(4,5)(11,12)",\ # "(3,4)(10,11)", "(2,3)(9,10)", "(1,2)(8,9)"] ) spectrum = [1, 0, 7, 0, 21, 0, 35, 0, 35, 0, 21, 0, 7, 0, 1] sd_codes_14_0 = {"order autgp":645120,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"No Type II of this length. Huge aut gp."} # [14,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( ["(7,14)", "(6,7)(13,14)", "(5,6)(12,13)", "(4,8)(9,10)",\ # "(4,9)(8,10)", "(3,4)(10,11)", "(2,3)(9,10)", "(1,2)(8,9)"] ) spectrum = [1, 0, 3, 0, 17, 0, 43, 0, 43, 0, 17, 0, 3, 0, 1] sd_codes_14_1 = {"order autgp":64512,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Automorphism group has order 64512."} # [14,2]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matId(n)[6]])) # G = PermutationGroup( ["(7,14)", "(5,6)(12,13)", "(5,12)(6,13)", "(4,5)(11,12)",\ # "(3,4)(10,11)", "(2,3)(9,10)", "(1,2)(8,9)"] ) spectrum = [1, 0, 1, 0, 15, 0, 47, 0, 47, 0, 15, 0, 1, 0, 1] sd_codes_14_2 = {"order autgp":46080,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique codeword of weight 2."} # [14,3]: genmat = I2(n).augment(And7) # G = PermutationGroup( ["(7,11)(12,13)", "(7,12)(11,13)", "(6,9)(10,14)",\ # "(6,10)(9,14)", "(5,6)(8,9)", "(4,5)(9,10), (2,3)(11,12)", "(2,7)(3,13)",\ # "(1,2)(12,13)", "(1,4)(2,5)(3,8)(6,7)(9,13)(10,12)(11,14)"]) spectrum = [1, 0, 0, 0, 14, 0, 49, 0, 49, 0, 14, 0, 0, 0, 1] sd_codes_14_3 = {"order autgp":56448,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Largest minimum distance of these."} sd_codes["14"] = {"0":sd_codes_14_0,"1":sd_codes_14_1,"2":sd_codes_14_2,\ "3":sd_codes_14_3} return sd_codes if n == 16: # 4 of these are Type I, 2 are Type II. The 2 Type II codes # are formally equivalent but with different automorphism groups # [16,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( [ "(8,16)", "(7,8)(15,16)", "(6,7)(14,15)", "(5,6)(13,14)", # "(4,5)(12,13)", "(3,4)(11,12)", "(2,3)(10,11)", "(1,2)(9,10)"] ) spectrum = [1, 0, 8, 0, 28, 0, 56, 0, 70, 0, 56, 0, 28, 0, 8, 0, 1] sd_codes_16_0 = {"order autgp":10321920,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Huge aut gp."} # [16,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( [ "(8,16)", "(7,8)(15,16)", "(6,7)(14,15)", "(5,6)(13,14)",\ # "(4,9)(10,11)", "(4,10)(9,11)", "(3,4)(11,12)", "(2,3)(10,11)", "(1,2)(9,10)"] ) spectrum = [1, 0, 4, 0, 20, 0, 60, 0, 86, 0, 60, 0, 20, 0, 4, 0, 1] sd_codes_16_1 = {"order autgp":516096,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [16,2]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matA(n)[4]])) # G = PermutationGroup( [ "(8,13)(14,15)", "(8,14)(13,15)", "(7,8)(15,16)", "(6,7)(14,15)",\ # "(5,6)(13,14)", "(4,9)(10,11)", "(4,10)(9,11)", "(3,4)(11,12)", "(2,3)(10,11)",\ # "(1,2)(9,10)","(1,5)(2,6)(3,7)(4,8)(9,13)(10,14)(11,15)(12,16)"] ) spectrum = [1, 0, 0, 0, 28, 0, 0, 0, 198, 0, 0, 0, 28, 0, 0, 0, 1] sd_codes_16_2 = {"order autgp":3612672,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"II","Comment":"Same spectrum as the other Type II code."} # [16,3]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matId(n)[6]])) # G = PermutationGroup( [ "(8,16)", "(7,8)(15,16)", "(5,6)(13,14)", "(5,13)(6,14)",\ # "(4,5)(12,13)", "(3,4)(11,12)", "(2,3)(10,11)", "(1,2)(9,10)"] ) spectrum = [1, 0, 2, 0, 16, 0, 62, 0, 94, 0, 62, 0, 16, 0, 2, 0, 1] sd_codes_16_3 = {"order autgp":184320,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [16,4]: genmat = I2(n).augment(matA(n)[8]) # an equivalent form: See also [20,8] using A[10] # [(1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), # (0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1), # (0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0), # (0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0), # (0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0), # (0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0), # (0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0), # (0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1)] # G = PermutationGroup( [ "(7,8)(15,16)", "(7,15)(8,16)", "(6,7)(14,15)",\ # "(5,6)(13,14)","(4,5)(12,13)","(3,4)(11,12)", "(2,3)(10,11)", "(1,2)(9,10)"] ) spectrum = [1, 0, 0, 0, 28, 0, 0, 0, 198, 0, 0, 0, 28, 0, 0, 0, 1] sd_codes_16_4 = {"order autgp":5160960,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"II","Comment":"Same spectrum as the other Type II code. Large aut gp."} # [16,5]: genmat = I2(n).augment(block_diagonal_matrix([And7,matId(n)[7]])) # G = PermutationGroup( [ "(8,16)", "(7,12)(13,14)", "(7,13)(12,14)",\ # "(6,10)(11,15)", "(6,11)(10,15)", "(5,6)(9,10)", "(4,5)(10,11)",\ # "(2,3)(12,13)", "(2,7)(3,14)", "(1,2)(13,14)",\ # "(1,4)(2,5)(3,9)(6,7)(10,14)(11,13)(12,15)" ] ) spectrum = [1, 0, 1, 0, 14, 0, 63, 0, 98, 0, 63, 0, 14, 0, 1, 0, 1] sd_codes_16_5 = {"order autgp":112896,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"'Exceptional' construction."} # [16,6]: J8 = MatrixSpace(ZZ,8,8)(64*[1]) genmat = I2(n).augment(I2(n)+MS2(n)((H8+J8)/2)) # G = PermutationGroup( [ "(7,9)(10,16)", "(7,10)(9,16)", "(6,7)(10,11)",\ # "(4,6)(11,13)", "(3,5)(12,14)", "(3,12)(5,14)", "(2,3)(14,15)",\ # "(1,2)(8,15)", "(1,4)(2,6)(3,7)(5,16)(8,13)(9,12)(10,14)(11,15)" ] ) spectrum = [1, 0, 0, 0, 12, 0, 64, 0, 102, 0, 64, 0, 12, 0, 0, 0, 1] sd_codes_16_6 = {"order autgp":73728,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"'Exceptional' construction. Min dist 4."} sd_codes["16"] = {"0":sd_codes_16_0,"1":sd_codes_16_1,"2":sd_codes_16_2,\ "3":sd_codes_16_3,"4":sd_codes_16_4,"5":sd_codes_16_5,"6":sd_codes_16_6} return sd_codes if n == 18: # all of these are Type I, all are "extensions" of the n=16 codes # [18,3] and [18,4] each has a unique lowest weight codeword. Also, they # are formally equivalent but with different automorphism groups # [18,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( [ "(9,18)", "(8,9)(17,18)", "(7,8)(16,17)", "(6,7)(15,16)",\ # "(5,6)(14,15)", "(4,5)(13,14)", "(3,4)(12,13)", "(2,3)(11,12)", "(1,2)(10,11)" ] ) spectrum = [1, 0, 9, 0, 36, 0, 84, 0, 126, 0, 126, 0, 84, 0, 36, 0, 9, 0, 1] sd_codes_18_0 = {"order autgp":185794560,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Huge aut gp. S_9x(ZZ/2ZZ)^9?"} # [18,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( [ "(9,18)", "(8,9)(17,18)", "(7,8)(16,17)", "(6,7)(15,16)",\ # "(5,6)(14,15)", "(4,10)(11,12)", "(4,11)(10,12)", "(3,4)(12,13)",\ # "(2,3)(11,12)", "(1,2)(10,11)" ] ) spectrum = [1, 0, 5, 0, 24, 0, 80, 0, 146, 0, 146, 0, 80, 0, 24, 0, 5, 0, 1] sd_codes_18_1 = {"order autgp":5160960,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Large aut gp."} # [18,2]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matId(n)[6]])) # G = PermutationGroup( [ "(9,18)", "(8,9)(17,18)", "(7,8)(16,17)", "(5,6)(14,15)",\ # "(5,14)(6,15)","(4,5)(13,14)", "(3,4)(12,13)", "(2,3)(11,12)", "(1,2)(10,11)"] ) spectrum = [1, 0, 3, 0, 18, 0, 78, 0, 156, 0, 156, 0, 78, 0, 18, 0, 3, 0, 1] sd_codes_18_2 = {"order autgp":1105920,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": ""} # [18,3]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matA(n)[4],matId(n)[8]])) # G = PermutationGroup( [ "(9,18)", "(8,14)(15,16)", "(8,15)(14,16)", "(7,8)(16,17)",\ # "(6,7)(15,16)","(5,6)(14,15)", "(4,10)(11,12)", "(4,11)(10,12)",\ # "(3,4)(12,13)", "(2,3)(11,12)","(1,2)(10,11)",\ # "(1,5)(2,6)(3,7)(4,8)(10,14)(11,15)(12,16)(13,17)" ] ) spectrum = [1, 0, 1, 0, 28, 0, 28, 0, 198, 0, 198, 0, 28, 0, 28, 0, 1, 0, 1] sd_codes_18_3 = {"order autgp":7225344,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Large aut gp. Unique codeword of smallest non-zero wt.\ Same spectrum as '[18,4]' sd code."} # [18,4]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[8],matId(n)[8]])) # G = PermutationGroup( [ "(9,18)", "(7,8)(16,17)", "(7,16)(8,17)", "(6,7)(15,16)", \ # "(5,6)(14,15)", "(4,5)(13,14)", "(3,4)(12,13)", "(2,3)(11,12)", "(1,2)(10,11)" ] ) spectrum = [1, 0, 1, 0, 28, 0, 28, 0, 198, 0, 198, 0, 28, 0, 28, 0, 1, 0, 1] sd_codes_18_4 = {"order autgp":10321920,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Huge aut gp. Unique codeword of smallest non-zero wt.\ Same spectrum as '[18,3]' sd code."} # [18,5]: C = self_dual_codes_binary(n-2)["%s"%(n-2)]["5"]["code"] A0 = C.redundancy_matrix() genmat = I2(n).augment(block_diagonal_matrix([A0,matId(n)[8]])) # G = PermutationGroup( [ "(5,10)(6,11)", "(5,11)(6,10)", "(5,11,12)(6,7,10)",\ # "(5,11,10,7,12,6,13)", "(2,15)(3,16)(5,11,10,7,12,6,13)",\ # "(2,16)(3,15)(5,11,10,7,12,6,13)", "(2,16,14)(3,15,4)(5,11,10,7,12,6,13)",\ # "(1,2,16,15,4,3,14)(5,11,10,7,12,6,13)", "(1,5,14,6,16,11,15,7,3,10,4,12,2,13)",\ # "(2,16,14)(3,15,4)(5,11,10,7,12,6,13)(9,18)",\ # "(2,16,14)(3,15,4)(5,11,10,7,12,6,13)(8,9,17,18)" ] ) spectrum = [1, 0, 2, 0, 15, 0, 77, 0, 161, 0, 161, 0, 77, 0, 15, 0, 2, 0, 1] sd_codes_18_5 = {"order autgp":451584,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "'Exceptional' construction."} # [18,6]: C = self_dual_codes_binary(n-2)["%s"%(n-2)]["6"]["code"] A0 = C.redundancy_matrix() genmat = I2(n).augment(block_diagonal_matrix([A0,matId(n)[8]])) G = PermutationGroup( [ "(9,18)", "(7,10)(11,17)", "(7,11)(10,17)", "(6,7)(11,12)",\ "(4,6)(12,14)", "(3,5)(13,15)", "(3,13)(5,15)", "(2,3)(15,16)", "(1,2)(8,16)",\ "(1,4)(2,6)(3,7)(5,17)(8,14)(10,13)(11,15)(12,16)" ] ) spectrum = [1, 0, 1, 0, 12, 0, 76, 0, 166, 0, 166, 0, 76, 0, 12, 0, 1, 0, 1] sd_codes_18_6 = {"order autgp":147456,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "'Exceptional'. Unique codeword of smallest non-zero wt."} # [18,7] (equiv to H18 in [P]) genmat = MS(n)([[1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],\ [0,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1],\ [0,0,1,0,0,0,0,0,0,1,1,1,0,0,1,0,0,1],\ [0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1],\ [0,0,0,0,1,0,0,0,0,1,1,0,0,1,0,1,1,0],\ [0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,1,0],\ [0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0],\ [0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1],\ [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1]]) # G = PermutationGroup( [ "(9,10)(16,18)", "(9,16)(10,18)", "(8,9)(14,16)",\ # "(7,11)(12,17)", "(7,12)(11,17)", "(5,6)(11,12)", "(5,7)(6,17)",\ # "(4,13)(5,8)(6,14)(7,9)(10,12)(11,18)(16,17)", "(3,4)(13,15)",\ # "(1,2)(5,8)(6,14)(7,9)(10,12)(11,18)(16,17)", "(1,3)(2,15)",\ # "(1,5)(2,6)(3,7)(4,11)(10,18)(12,13)(15,17)" ] ) spectrum = [1, 0, 0, 0, 9, 0, 75, 0, 171, 0, 171, 0, 75, 0, 9, 0, 0, 0, 1] sd_codes_18_7 = {"order autgp":82944,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "'Exceptional' construction. Min dist 4."} # [18, 8] (equiv to I18 in [P]) I18 = MS(n)([[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],\ [1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],\ [0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1],\ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]) genmat = MS(n)([[1,0,0,0,0,0,0,0,0, 1, 1, 1, 1, 1, 0, 0, 0, 0],\ [0,1,0,0,0,0,0,0,0, 1, 0, 1, 1, 1, 0, 1, 1, 1],\ [0,0,1,0,0,0,0,0,0, 0, 1, 1, 0, 0, 0, 1, 1, 1],\ [0,0,0,1,0,0,0,0,0, 0, 1, 0, 0, 1, 0, 1, 1, 1],\ [0,0,0,0,1,0,0,0,0, 0, 1, 0, 1, 0, 0, 1, 1, 1],\ [0,0,0,0,0,1,0,0,0, 1, 1, 0, 0, 0, 0, 1, 1, 1],\ [0,0,0,0,0,0,1,0,0, 0, 0, 0, 0, 0, 1, 0, 1, 1],\ [0,0,0,0,0,0,0,1,0, 0, 0, 0, 0, 0, 1, 1, 0, 1],\ [0,0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 1, 1, 1, 0]]) G = PermutationGroup( [ "(9,15)(16,17)", "(9,16)(15,17)", "(8,9)(17,18)",\ "(7,8)(16,17)", "(5,6)(10,13)", "(5,10)(6,13)", "(4,5)(13,14)",\ "(3,4)(12,14)", "(1,2)(6,10)", "(1,3)(2,12)" ] ) spectrum = [1, 0, 0, 0, 17, 0, 51, 0, 187, 0, 187, 0, 51, 0, 17, 0, 0, 0, 1] sd_codes_18_8 = {"order autgp":322560,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "'Exceptional' construction. Min dist 4."} sd_codes["18"] = {"0":sd_codes_18_0,"1":sd_codes_18_1,"2":sd_codes_18_2,\ "3":sd_codes_18_3,"4":sd_codes_18_4,"5":sd_codes_18_5,\ "6":sd_codes_18_6,"7":sd_codes_18_7,"8":sd_codes_18_8} return sd_codes if n == 20: # all of these of these are Type I; 2 of these codes # are formally equivalent but with different automorphism groups; # one of these has a unique codeword of lowest weight A10 = MatrixSpace(F,10,10)([[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],\ [1, 1, 1, 0, 1, 0, 1, 0, 1, 1],\ [1, 0, 0, 1, 0, 1, 0, 1, 0, 1],\ [0, 0, 0, 1, 1, 1, 0, 1, 0, 1],\ [0, 0, 1, 1, 0, 1, 0, 1, 0, 1],\ [0, 0, 0, 1, 0, 1, 1, 1, 0, 1],\ [0, 1, 0, 1, 0, 1, 0, 1, 0, 1],\ [0, 0, 0, 1, 0, 0, 0, 0, 1, 1],\ [0, 0, 0, 0, 0, 1, 0, 0, 1, 1],\ [0, 0, 0, 0, 0, 0, 0, 1, 1, 1]]) # [20,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( ["(10,20)", "(9,10)(19,20)", "(8,9)(18,19)", "(7,8)(17,18)", "(6,7)(16,17)",\ # "(5,6)(15,16)", "(4,5)(14,15)", "(3,4)(13,14)", "(2,3)(12,13)", "(1,2)(11,12)"] ) spectrum = [1, 0, 10, 0, 45, 0, 120, 0, 210, 0, 252, 0, 210, 0, 120, 0, 45, 0, 10, 0, 1] sd_codes_20_0 = {"order autgp":3715891200,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Huge aut gp"} # [20,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( [ "(10,20)", "(9,10)(19,20)", "(8,9)(18,19)", "(7,8)(17,18)", "(6,7)(16,17)",\ # "(5,6)(15,16)", "(4,11)(12,13)", "(4,12)(11,13)", "(3,4)(13,14)",\ # "(2,3)(12,13)", "(1,2)(11,12)"] ) spectrum = [1, 0, 6, 0, 29, 0, 104, 0, 226, 0, 292, 0, 226, 0, 104, 0, 29, 0, 6, 0, 1] sd_codes_20_1 = {"order autgp":61931520,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [20,2]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matId(n)[6]])) # G = PermutationGroup( [ "(10,20)", "(9,10)(19,20)", "(8,9)(18,19)", "(7,8)(17,18)",\ # "(5,6)(15,16)", "(5,15)(6,16)", "(4,5)(14,15)", "(3,4)(13,14)",\ # "(2,3)(12,13)", "(1,2)(11,12)"] ) spectrum = [1, 0, 4, 0, 21, 0, 96, 0, 234, 0, 312, 0, 234, 0, 96, 0, 21, 0, 4, 0, 1] sd_codes_20_2 = {"order autgp":8847360,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [20,3]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matA(n)[4]])) # G = PermutationGroup( [ "(5,6)(15,16)", "(5,15)(6,16)", "(4,5)(14,15)", "(3,4)(13,14)",\ # "(2,3)(12,13)", "(1,2)(11,12)", "(8,17)(9,10)", "(8,10)(9,17)", "(8,10,20)(9,19,17)",\ # "(8,19,20,9,17,10,18)", "(7,8,19,20,9,18)(10,17)"] ) spectrum =[1, 0, 0, 0, 29, 0, 32, 0, 226, 0, 448, 0, 226, 0, 32, 0, 29, 0, 0, 0, 1] sd_codes_20_3 = {"order autgp":30965760,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Min dist 4."} # [20,4]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matA(n)[4],matId(n)[8]])) # G = PermutationGroup( [ "(5,15)(6,16)", "(5,16)(6,15)", "(5,16,7)(6,17,15)", "(5,15,8)(6,17,7)",\ # "(5,17,18)(6,15,8), (3,14)(4,13)(5,17,18)(6,15,8)", "(3,13)(4,14)(5,17,18)(6,15,8)",\ # "(2,3,14)(4,13,11)(5,17,18)(6,15,8)"," (2,3,12)(4,11,14)(5,17,18)(6,15,8)",\ # "(1,2,3,11,14,4,12)(5,17,18)(6,15,8)", "(1,5,13,17,14,8,2,7,3,16,12,6,11,18)(4,15)",\ # "(2,3,12)(4,11,14)(5,17,18)(6,15,8)(10,20)",\ # "(2,3,12)(4,11,14)(5,17,18)(6,15,8)(9,10,19,20)"] ) spectrum =[1, 0, 2, 0, 29, 0, 56, 0, 226, 0, 396, 0, 226, 0, 56, 0, 29, 0, 2, 0, 1] sd_codes_20_4 = {"order autgp":28901376,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [20,5]: genmat = I2(n).augment(block_diagonal_matrix([And7,matId(n)[7]])) # G = PermutationGroup( [ "(10,20)", "(9,10)(19,20)", "(8,9)(18,19)",\ # "(7,11)(12,14)", "(7,12)(11,14)", "(6,7)(12,13)", "(5,6)(11,12)",\ # "(4,15)(16,17)", "(4,16)(15,17)", "(2,3)(16,17)", "(2,4)(3,15)",\ # "(1,2)(15,16)", "(1,5)(2,6)(3,13)(4,7)(11,16)(12,15)(14,17)" ] ) # order 2709504 spectrum = [1, 0, 3, 0, 17, 0, 92, 0, 238, 0, 322, 0, 238, 0, 92, 0, 17, 0, 3, 0, 1] sd_codes_20_5 = {"order autgp":2709504,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "'Exceptional' construction."} # [20,6]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[8],matId(n)[8]])) # G = PermutationGroup( [ "(7,8)(17,18)", "(7,17)(8,18)", "(6,7)(16,17)", "(5,6)(15,16)",\ # "(4,5)(14,15)", "(3,4)(13,14)", "(2,3)(12,13)", "(1,2)(11,12)",\ # "(10,20)", "(9,10,19,20)"] ) spectrum = [1, 0, 2, 0, 29, 0, 56, 0, 226, 0, 396, 0, 226, 0, 56, 0, 29, 0, 2, 0, 1] sd_codes_20_6 = {"order autgp":41287680,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [20,7]: A0 = self_dual_codes_binary(n-4)["16"]["6"]["code"].redundancy_matrix() genmat = I2(n).augment(block_diagonal_matrix([A0,matId(n)[8]])) # G = PermutationGroup( [ "(10,20)", "(9,10)(19,20)", "(7,11)(12,18)",\ # "(7,12)(11,18)", "(6,7)(12,13)", "(4,6)(13,15)", "(3,5)(14,16)",\ # "(3,14)(5,16)", "(2,3)(16,17)", "(1,2)(8,17)",\ # "(1,4)(2,6)(3,7)(5,18)(8,15)(11,14)(12,16)(13,17)" ] ) spectrum = [1,0,2,0,13,0,88,0,242,0,332,0,242,0,88,0,13,0,2,0,1] sd_codes_20_7 = {"order autgp":589824,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"'Exceptional' construction."} # [20,8]: (genmat, J20, and genmat2 are all equiv) genmat = I2(n).augment(matA(n)[10]) J20 = MS(n)([[1,1,1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\ [0,0,1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\ [0,0,0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\ [0,0,0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\ [0,0,0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\ [0,0,0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],\ [0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],\ [0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0],\ [0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\ [1,0,1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]) genmat2 = MS(n)([[1,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],\ [0,1,0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1],\ [0,0,1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],\ [0,0,0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],\ [0,0,0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0],\ [0,0,0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0],\ [0,0,0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0],\ [0,0,0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0],\ [0,0,0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0],\ [0,0,0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]]) # G = PermutationGroup( [ "(9,10)(19,20)", "(9,19)(10,20)", "(8,9)(18,19)", "(7,8)(17,18)",\ # "(6,7)(16,17)", "(5,6)(15,16)", "(4,5)(14,15)", "(3,4)(13,14)",\ # "(2,3)(12,13)", "(1,2)(11,12)"] ) spectrum =[1, 0, 0, 0, 45, 0, 0, 0, 210, 0, 512, 0, 210, 0, 0, 0, 45, 0, 0, 0, 1] sd_codes_20_8 = {"order autgp":1857945600,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Huge aut gp. Min dist 4."} # [20,9]: (genmat, K20 are equiv) genmat = I2(n).augment(A10) K20 = MS(n)([[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],\ [1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,1,0]]) #genmat = K20 # not in standard form # G = PermutationGroup( [ "(4,13)(5,15)", "(4,15)(5,13)", "(3,4,13)(5,11,15)", # "(3,4,6,11,15,17)(5,13)", "(3,5,17,4,12)(6,15,7,11,13)", # "(1,2)(3,5,17,4,7,11,13,6,15,12)", "(1,3,5,17,4,12)(2,11,13,6,15,7)", # "(3,5,17,4,12)(6,15,7,11,13)(10,18)(19,20)", "(3,5,17,4,12)(6,15,7,11,13)(10,19)(18,20)", # "(3,5,17,4,12)(6,15,7,11,13)(9,10)(16,18)", # "(3,5,17,4,12)(6,15,7,11,13)(8,9)(14,16)" ] ) spectrum = [1, 0, 0, 0, 21, 0, 48, 0, 234, 0, 416, 0, 234, 0, 48, 0, 21, 0, 0, 0, 1] sd_codes_20_9 = {"order autgp":4423680,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Min dist 4."} # [20,10] L20 = MS(n)([[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],\ [0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,1,0,0,0,0],\ [0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,0,1,0,1,0]]) genmat = L20 # not in standard form # G = PermutationGroup( [ "(17,18)(19,20)", "(17,19)(18,20)", "(15,16)(19,20)", # "(15,17)(16,18)", "(10,11)(12,13)", "(10,12)(11,13)", "(9,10)(13,14)", # "(8,9)(12,13)", "(3,4)(5,6)", "(3,5)(4,6)", "(2,3)(6,7)", "(1,2)(5,6)", # "(1,8)(2,9)(3,10)(4,11)(5,12)(6,13)(7,14)(19,20)" ] ) # order 1354752 spectrum = [1, 0, 0, 0, 17, 0, 56, 0, 238, 0, 400, 0, 238, 0, 56, 0, 17, 0, 0, 0, 1] sd_codes_20_10 = {"order autgp":1354752,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Min dist 4."} # [20,11] S20 = MS(n)([[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],\ [1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,1,1,0,0],\ [1,1,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,0,0],\ [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,0]] ) genmat = S20 # not in standard form # G = PermutationGroup( [ "(17,18)(19,20)", "(17,19)(18,20)", "(13,14)(15,16)", # "(13,15)(14,16)", "(11,12)(15,16)", "(11,13)(12,14)", "(9,10)(15,16)", # "(9,11)(10,12)", "(5,6)(7,8)", "(5,7)(6,8)", "(3,4)(7,8)", "(3,5)(4,6)", # "(1,2)(7,8)", "(1,3)(2,4)", "(1,9)(2,10)(3,11)(4,12)(5,13)(6,14)(7,15)(8,16)" ] ) # G.order() = 294912 spectrum = [1, 0, 0, 0, 13, 0, 64, 0, 242, 0, 384, 0, 242, 0, 64, 0, 13, 0, 0, 0, 1] sd_codes_20_11 = {"order autgp":294912,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Min dist 4."} # [20,12] R20 = MS(n)([[0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],\ [0,1,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,0],\ [1,1,1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0],\ [1,1,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,1],\ [1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1]]) genmat = R20 # not in standard form # G = PermutationGroup( [ "(17,18)(19,20)", "(17,19)(18,20)", "(15,16)(19,20)", # "(15,17)(16,18)", "(11,12)(13,14)", "(11,13)(12,14)", "(9,10)(13,14)", # "(9,11)(10,12)", "(5,6)(7,8)", "(5,7)(6,8)", "(3,4)(7,8)", "(3,5)(4,6)", # "(3,9,15)(4,10,16)(5,11,17)(6,12,18)(7,14,19)(8,13,20)", # "(1,2)(7,8)(9,15)(10,16)(11,17)(12,18)(13,19)(14,20)" ] ) # order 82944 spectrum = [1, 0, 0, 0, 9, 0, 72, 0, 246, 0, 368, 0, 246, 0, 72, 0, 9, 0, 0, 0, 1] sd_codes_20_12 = {"order autgp":82944,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Min dist 4."} # [20,13] M20 = MS(n)([[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0],\ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],\ [0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0],\ [1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0],\ [0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],\ [0,0,1,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0],\ [0,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,0]]) genmat = M20 # not in standard form # G = PermutationGroup( [ "(17,18)(19,20)", "(17,19)(18,20)", "(13,14)(15,16)", # "(13,15)(14,16)", "(9,10)(11,12)", "(9,11)(10,12)", "(5,6)(7,8)", # "(5,7)(6,8)", "(5,9)(6,11)(7,12)(8,10)(13,17)(14,19)(15,18)(16,20)", # "(5,13)(6,15)(7,14)(8,16)(9,17)(10,20)(11,18)(12,19)", # "(3,4)(6,7)(11,12)(13,17)(14,18)(15,19)(16,20)", # "(2,3)(7,8)(9,13)(10,14)(11,15)(12,16)(19,20)", # "(1,2)(6,7)(11,12)(13,17)(14,18)(15,19)(16,20)", # "(1,5)(2,6)(3,7)(4,8)(9,17)(10,18)(11,19)(12,20)" ] ) spectrum = [1, 0, 0, 0, 5, 0, 80, 0, 250, 0, 352, 0, 250, 0, 80, 0, 5, 0, 0, 0, 1] sd_codes_20_13 = {"order autgp":122880,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "Min dist 4."} # [20,14]: # aut gp of this computed using a program by Robert Miller A0 = self_dual_codes_binary(n-2)["18"]["8"]["code"].redundancy_matrix() genmat = I2(n).augment(block_diagonal_matrix([A0,matId(n)[9]])) # [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], # [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0], # [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0], # [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0], # [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0], # [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0], # [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0], # [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0], # [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0], # [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]] # G = PermutationGroup( [ "(8,19)(16,17)", "(8,16)(17,19)", "(9,18)(16,17)", "(8,9)(18,19)", # "(7,8)(17,18)", "(4,15)(5,14)", "(4,5)(14,15)", "(4,15)(6,11)", "(5,6)(11,14)", # "(3,13)(4,15)", "(3,15)(4,13)", "(1,2)(4,15)", "(1,4)(2,15)(3,5)(13,14)", "(10,20)" ] ) spectrum = [1, 0, 1, 0, 17, 0, 68, 0, 238, 0, 374, 0, 238, 0, 68, 0, 17, 0, 1, 0, 1] sd_codes_20_14 = {"order autgp":645120,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment": "'Exceptional' construction."} # [20,15]: A0 = self_dual_codes_binary(n-2)["18"]["7"]["code"].redundancy_matrix() genmat = I2(n).augment(block_diagonal_matrix([A0,matId(n)[9]])) # [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], # [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0], # [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0], # [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0], # [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0], # [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0], # [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0], # [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0], # [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0], # [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]] # G = PermutationGroup( [ "(10,20)", "(9,11)(17,19)", "(9,17)(11,19)", "(8,9)(15,17)", # "(7,12)(13,18)", "(7,13)(12,18)", "(5,6)(12,13)", "(5,7)(6,18)", # "(4,14)(5,8)(6,15)(7,9)(11,13)(12,19)(17,18)", "(3,4)(14,16)", # "(1,2)(5,8)(6,15)(7,9)(11,13)(12,19)(17,18)", "(1,3)(2,16)", # "(1,5)(2,6)(3,7)(4,12)(11,19)(13,14)(16,18)" ] ) # order 165888 spectrum = [1, 0, 1, 0, 9, 0, 84, 0, 246, 0, 342, 0, 246, 0, 84, 0, 9, 0, 1, 0, 1] sd_codes_20_15 = {"order autgp":165888,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"'Exceptional' construction. Unique lowest wt codeword."} sd_codes["20"] = {"0":sd_codes_20_0,"1":sd_codes_20_1,"2":sd_codes_20_2,\ "3":sd_codes_20_3,"4":sd_codes_20_4,"5":sd_codes_20_5,\ "6":sd_codes_20_6,"7":sd_codes_20_7,"8":sd_codes_20_8,\ "9":sd_codes_20_9,"10":sd_codes_20_10,"11":sd_codes_20_11,\ "12":sd_codes_20_12,"13":sd_codes_20_13,"14":sd_codes_20_14, "15":sd_codes_20_15} return sd_codes if n == 22: # all of these of these are Type I; 2 of these codes # are formally equivalent but with different automorphism groups # *** Incomplete *** (7 out of 25) # [22,0]: genmat = I2(n).augment(I2(n)) # G = PermutationGroup( [ "(11,22)", "(10,11)(21,22)", "(9,10)(20,21)",\ # "(8,9)(19,20)", "(7,8)(18,19)", "(6,7)(17,18)", "(5,6)(16,17)",\ # "(4,5)(15,16)", "(3,4)(14,15)", "(2,3)(13,14)", "(1,2)(12,13)" ] ) # S_11x(ZZ/2ZZ)^11?? spectrum = [1, 0, 11, 0, 55, 0, 165, 0, 330, 0, 462, 0, 462, 0, 330, 0, 165, 0, 55, 0, 11, 0, 1] sd_codes_22_0 = {"order autgp":81749606400,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Huge aut gp."} # [22,1]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matId(n)[4]])) # G = PermutationGroup( [ "(11,22)", "(10,11)(21,22)", "(9,10)(20,21)",\ # "(8,9)(19,20)", "(7,8)(18,19)", "(6,7)(17,18)", "(5,6)(16,17)",\ # "(4,12)(13,14)", "(4,13)(12,14)", "(3,4)(14,15)", "(2,3)(13,14)", "(1,2)(12,13)" ] ) spectrum = [1, 0, 7, 0, 35, 0, 133, 0, 330, 0, 518, 0, 518, 0, 330, 0, 133, 0, 35, 0, 7, 0, 1] sd_codes_22_1 = {"order autgp":867041280,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [22,2]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matId(n)[6]])) # G = PermutationGroup( [ "(11,22)", "(10,11)(21,22)", "(9,10)(20,21)",\ # "(8,9)(19,20)", "(7,8)(18,19)", "(5,6)(16,17)", "(5,16)(6,17)",\ # "(4,5)(15,16)", "(3,4)(14,15)", "(2,3)(13,14)", "(1,2)(12,13)" ] ) spectrum = [1, 0, 5, 0, 25, 0, 117, 0, 330, 0, 546, 0, 546, 0, 330, 0, 117, 0, 25, 0, 5, 0, 1] sd_codes_22_2 = {"order autgp":88473600,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":""} # [22,3]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[8],matId(n)[8]])) # G = PermutationGroup( [ "(11,22)", "(10,11)(21,22)", "(9,10)(20,21)",\ # "(7,8)(18,19)", "(7,18)(8,19)", "(6,7)(17,18)", "(5,6)(16,17)",\ # "(4,5)(15,16)", "(3,4)(14,15)", "(2,3)(13,14)", "(1,2)(12,13)" ] ) spectrum = [1, 0, 3, 0, 31, 0, 85, 0, 282, 0, 622, 0, 622, 0, 282, 0, 85, 0, 31, 0, 3, 0, 1] sd_codes_22_3 = {"order autgp":247726080,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Same spectrum as the '[20,5]' code."} # [22,4]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[10],matId(n)[10]])) # G = PermutationGroup( [ "(11,22)", "(9,10)(20,21)", "(9,20)(10,21)",\ # "(8,9)(19,20)", "(7,8)(18,19)", "(6,7)(17,18)", "(5,6)(16,17)",\ # "(4,5)(15,16)", "(3,4)(14,15)", "(2,3)(13,14)", "(1,2)(12,13)" ] ) spectrum = [1, 0, 1, 0, 45, 0, 45, 0, 210, 0, 722, 0, 722, 0, 210, 0, 45, 0, 45, 0, 1, 0, 1] sd_codes_22_4 = {"order autgp":3715891200,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique lowest weight codeword."} # [22,5]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[4],matA(n)[4],matId(n)[8]])) # G = PermutationGroup( [ "(11,22)", "(10,11)(21,22)", "(9,10)(20,21)",\ # "(8,16)(17,18)", "(8,17)(16,18)", "(7,8)(18,19)", "(6,7)(17,18)",\ # "(5,6)(16,17)", "(4,12)(13,14)", "(4,13)(12,14)", "(3,4)(14,15)",\ # "(2,3)(13,14)", "(1,2)(12,13)", "(1,5)(2,6)(3,7)(4,8)(12,16)(13,17)(14,18)(15,19)" ] ) spectrum = [1, 0, 3, 0, 31, 0, 85, 0, 282, 0, 622, 0, 622, 0, 282, 0, 85, 0, 31, 0, 3, 0, 1] sd_codes_22_5 = {"order autgp":173408256,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Same spectrum as the '[20,3]' code."} # [22,6]: genmat = I2(n).augment(block_diagonal_matrix([matA(n)[6],matA(n)[4],matId(n)[10]])) # G = PermutationGroup( [ "(11,22)", "(10,18)(19,20)", "(10,19)(18,20)",\ # "(9,10)(20,21)", "(8,9)(19,20)", "(7,8)(18,19)", "(5,6)(16,17)",\ # "(5,16)(6,17)", "(4,5)(15,16)", "(3,4)(14,15)", "(2,3)(13,14)", "(1,2)(12,13)" ] ) spectrum = [1, 0, 1, 0, 29, 0, 61, 0, 258, 0, 674, 0, 674, 0, 258, 0, 61, 0, 29, 0, 1, 0, 1] sd_codes_22_6 = {"order autgp":61931520,"code":LinearCode(genmat),"spectrum":spectrum,\ "Type":"I","Comment":"Unique lowest weight codeword."} sd_codes["22"] = {"0":sd_codes_22_0,"1":sd_codes_22_1,"2":sd_codes_22_2,\ "3":sd_codes_22_3,"4":sd_codes_22_4,"5":sd_codes_22_5,\ "6":sd_codes_22_6} return sd_codes
def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, \ quotient=None, dual=False, ntl=False): """ This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: * ``type`` - one of the following strings * ``'modular'`` (default). A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [A96]_. * ``'random'`` - Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM02]_. * ``'ideal'`` - Special case of modular. Allows for a more compact representation proposed by [LM06]_. * ``'cyclotomic'`` - Special case of ideal. Allows for efficient processing proposed by [LM06]_. * ``n`` - Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. * ``m`` - Lattice dimension, `L \subseteq Z^m`. * ``q`` - Coefficent size, `q*Z^m \subseteq L`. * ``seed`` - Randomness seed. * ``quotient`` - For the type ideal, this determines the quotient polynomial. Ignored for all other types. * ``dual`` - Set this flag if you want a basis for `q*dual(L)`, for example for Regev's LWE bases [R05]_. * ``ntl`` - Set this flag if you want the lattice basis in NTL readable format. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: * Modular basis :: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] * Random basis :: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] * Ideal bases with quotient x^n-1, m=2*n are NTRU bases :: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [-3 4 -2 -3 0 1 0 0] [-3 -3 4 -2 0 0 1 0] [-2 -3 -3 4 0 0 0 1] * Cyclotomic bases with n=2^k are SWIFFT bases :: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [ 3 4 -2 -3 0 1 0 0] [ 3 3 4 -2 0 0 1 0] [ 2 3 3 4 0 0 0 1] * Dual modular bases are related to Regev's famous public-key encryption [R05]_ :: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] * Relation of primal and dual bases :: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True REFERENCES: .. [A96] Miklos Ajtai. Generating hard instances of lattice problems (extended abstract). STOC, pp. 99--108, ACM, 1996. .. [GM02] Daniel Goldstein and Andrew Mayer. On the equidistribution of Hecke points. Forum Mathematicum, 15:2, pp. 165--189, De Gruyter, 2003. .. [LM06] Vadim Lyubashevsky and Daniele Micciancio. Generalized compact knapsacks are collision resistant. ICALP, pp. 144--155, Springer, 2006. .. [R05] Oded Regev. On lattices, learning with errors, random linear codes, and cryptography. STOC, pp. 84--93, ACM, 2005. """ from sage.rings.finite_rings.integer_mod_ring \ import IntegerModRing from sage.matrix.constructor import matrix, \ identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed != None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m-n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient == None: raise \ ValueError('ideal bases require a quotient polynomial') x = quotient.default_variable() if n != quotient.degree(x): raise \ ValueError('ideal bases require n = quotient.degree()') R = ZZ_q[x].quotient(quotient, x) for i in range(m//n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.rings.arith import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise \ ValueError('cyclotomic bases require that n is an image of' + \ 'Euler\'s totient function') R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m//n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a-q) < abs(a): return a-q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()] ], \ subdivide=False) else: B = block_matrix([[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), \ ZZ(q)]], subdivide=False) for i in range(m//2): B.swap_rows(i,m-i-1) if not ntl: return B else: return B._ntl_()
def MS(n): n2 = ZZ(n)/2; return MatrixSpace(F, n2, n) def matA(n):
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 pi_bdry_c_bar.iteritems(): 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 __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 __call__(self, A, name=''): 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 if A in self.base_ring(): dim_dom = self.domain().rank() dim_codom = self.codomain().rank() MS = MatrixSpace(self.base_ring(), dim_dom, dim_codom) 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]) return HeckeModuleMorphism_matrix(self, A, name)
class SubsetPairs: """The collection of all pairs (E,B) where E and B are subsets of Z_p^d.""" def __init__(self, modulus, dimension): self.modulus = int(modulus) #The modulus p self.dimension = int(dimension) #The dimension d self.field = FiniteField(modulus) #The underlying field self.space = MatrixSpace(self.field, 1, dimension) #The space of vectors in Z_p^d viewed as matrices self.elements = list(self.space) #An actual list of those vectors self.basis = self.space.basis() #The standard basis for the vector space # Preliminary construction of the possible subsets of size "size", implemented as a python generator. # This is a huge speed bottleneck. Right now I just have it throwing out subsets if their vectors are not "in order" so # permutations of the same set are eliminated. There are faster ways to do this, but I haven't found one that is elegant yet. def subsets(self, size): for elem in MatrixSpace(self.field, self.dimension, size): passing = True for i in range(size - 1): if self.elements.index(Matrix(elem.transpose()[i])) >= self.elements.index(Matrix(elem.transpose()[i+1])): passing = False break if passing == True: yield(elem) # Create the sets of the first type which contain the zero vector and the standard basis vectors. # Note that these are not the only type of vectors which need to be checked in general, # so this can only find certain types of counterexamples. def firstsets(self, size): for elem in self.subsets(size): if elem.columns()[0] == 0 * elem.columns()[0] and elem.rref() == elem and elem.rank() == min(size, self.dimension): yield(elem) # Create the sets of the second type which contain the zero vector. def secondsets(self, size): for elem in self.subsets(size): if elem.columns()[0] == 0 * elem.columns()[0]: yield(elem) # Create the log-Hadamard matrix for a given pair of subsets. def loghadamard(self, E, B): return E.transpose() * B # Check if the difference of two rows is balanced. def row_difference(self, row0, row1): difference_vector = list(row0 - row1) counters = {} for i in range(self.modulus): counters[i] = 0 for entry in difference_vector: value = difference_vector[entry] counters[value] = counters[value] + 1 for i in range(self.modulus - 1): if counters[i] != counters[i+1]: return False return True # Perform the appropriate tests on all subsets not already eliminated. # At the moment this still tests (B,E) even after (E,B) has been tested. def runtest(self, size): for elem0 in S.firstsets(size): for elem1 in S.secondsets(size): H = self.loghadamard(elem0, elem1) passing = True for i,j in [(i,j) for i in range(size) for j in range(size)]: if i != j and self.row_difference(H[i], H[j]) == False: print(H[i], H[j], H[i]-H[j]) #Comment if you don't want to watch pairs of vectors and their differences pour down the screen. passing = False break if passing == True: print(elem0, elem1, H) print("")