Beispiel #1
0
    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())
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
 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
Beispiel #5
0
        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
Beispiel #8
0
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
Beispiel #9
0
    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
Beispiel #10
0
################################################################################

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`
Beispiel #11
0
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)
Beispiel #12
0
 def _matrix_space_3x3(self):
     from sage.matrix.matrix_space import MatrixSpace
     return MatrixSpace(self._ring, 3)
Beispiel #13
0
    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)
Beispiel #14
0
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
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
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
Beispiel #19
0
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))
Beispiel #20
0
def TrivialCode(F,n):
    MS = MatrixSpace(F,1,n)
    return LinearCode(MS(0))
Beispiel #21
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))
Beispiel #22
0
 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,[])
Beispiel #23
0
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
Beispiel #24
0
    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
Beispiel #26
0
    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]
Beispiel #27
0
    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
Beispiel #28
0
    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)
Beispiel #29
0
    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)
Beispiel #30
0
 def approx_module(self, M=None):
     if M is None:
         M = self.dimension()
     return MatrixSpace(self._R, M, 1)
Beispiel #31
0
        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],\
Beispiel #32
0
    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)
Beispiel #33
0
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])
Beispiel #34
0
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
Beispiel #35
0
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)
Beispiel #36
0
    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]
Beispiel #37
0
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
Beispiel #39
0
def HS_minimal(f, return_transformation=False, D=None):
    r"""
    Compute a minimal model for the given projective dynamical system.

    This function implements the algorithm in Hutz-Stoll [HS2018]_.
    A representative with minimal resultant in the conjugacy class
    of ``f`` returned.

    INPUT:

    - ``f`` -- dynamical system on the projective line with minimal resultant

    - ``return_transformation`` -- (default: ``False``) boolean; this
      signals a return of the `PGL_2` transformation to conjugate
      this map to the calculated models

    - ``D`` -- a list of primes, in case one only wants to check minimality
      at those specific primes

    OUTPUT:

    - a dynamical system
    - (optional) a `2 \times 2` matrix

    EXAMPLES::

        sage: P.<x,y> = ProjectiveSpace(QQ,1)
        sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y])
        sage: m = matrix(QQ,2,2,[5,1,0,1])
        sage: g = f.conjugate(m)
        sage: g.normalize_coordinates()
        sage: g.resultant().factor()
        2^4 * 3^4 * 5^12
        sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_minimal
        sage: HS_minimal(g).resultant().factor()
        2^4 * 3^4
        sage: HS_minimal(g, D=[2]).resultant().factor()
        2^4 * 3^4 * 5^12
        sage: F,m = HS_minimal(g, return_transformation=True)
        sage: g.conjugate(m) == F
        True
    """
    F = copy(f)
    d = F.degree()
    F.normalize_coordinates()
    MS = MatrixSpace(ZZ, 2, 2)
    m = MS.one()
    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
Beispiel #40
0
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]
Beispiel #41
0
    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)
Beispiel #42
0
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]
Beispiel #43
0
def MS2(n): n2 = n.quo_rem(2)[0]; return MatrixSpace(F, n2, n2)
I2 = lambda n: MS2(n).identity_matrix()
Beispiel #44
0
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]
Beispiel #45
0
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
Beispiel #46
0
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_()
Beispiel #47
0
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
Beispiel #49
0
    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)
Beispiel #50
0
    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)
Beispiel #51
0
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("")