예제 #1
0
    def ker_im(self):
        T0 = walltime()
        S = diagonal_matrix([self.pq, self.pq, 1])
        Si = ~S
        AUT = [S * M * Si for M in self.Delta_symmetry]

        pqDelta = self.pq * self.Delta
        pqDelta_fundam = self.pq * self.Delta_fundam
        fundam = fundamental_points(pqDelta_fundam, AUT)
        self.bidegrees = []
        self.numsymm = {}
        for o in orbits(self.tensor_points(pqDelta), AUT):
            bideg = unique_intersection(o, fundam)
            if self.fd[bideg]:
                # Do not handle bidegrees where the domain is
                # 0-dimensional. These do not contribute to the kernel
                # or image.
                self.bidegrees.append(bideg)
            self.numsymm[bideg] = len(o)

        self.TM = 0
        self.TR = 0
        self.done = 0
        self.results = {}

        # Bidegrees to handle in the computation, sorted according to
        # dimension of codomain (=> roughly by increasing difficulty)
        self._bidegrees_todo = sorted(self.bidegrees, key=lambda bideg: self.fc[bideg])

        self.lock = Lock()
        T1 = walltime()
        if not self.threads:
            self._run()
        else:
            T = [Thread(target=self._run, args=(i,)) for i in range(self.threads)]
            for t in T:
                t.start()
            for t in T:
                t.join()
        TT = walltime() - T0

        dimdomain = ZZ(sum(self.fd[bideg] * self.numsymm[bideg] for bideg in self.bidegrees))
        rank = ZZ(sum(self.results[bideg][1] * self.numsymm[bideg] for bideg in self.bidegrees))
        ker = dimdomain - rank
        if self.verbose:
            sys.stderr.flush()
            print("%-8s[%5i]:%10s   (t = %.2fs + %.2fs + %.2fs = %.2fs)" %
                  (str(self), len(self.bidegrees), ker, T1 - T0, self.TM, self.TR, TT))
            sys.stdout.flush()
        return ker, rank
예제 #2
0
    def _compute_echelon(self):
        A = Matrix(self.parent(),
                   self.A.rows())  # we create a copy of the matrix
        U = identity_matrix(self.parent(), A.nrows())

        if (self.have_ideal):  # we do simplifications
            A = self.simplify(A)

        ## Step 1: initialize
        r = 0
        c = 0  # we look from the position (r,c)
        while (r < A.nrows() and c < A.ncols()):
            ir = self.__find_pivot(A, r, c)
            A = self.simplify(A)
            U = self.simplify(U)  # we simplify in case relations pop up

            if (ir != None):  # we found a pivot
                # We do the swapping (if needed)
                if (ir != r):
                    A.swap_rows(r, ir)
                    U.swap_rows(r, ir)

                # We do the bareiss step
                Arc = A[r][c]
                Arows = A.rows()
                Urows = U.rows()
                for i in range(r):  # we create zeros on top of the pivot
                    Aic = A[i][c]
                    A.set_row(i, Arc * Arows[i] - Aic * Arows[r])
                    U.set_row(i, Arc * Urows[i] - Aic * Urows[r])

                # We then leave the row r without change
                for i in range(r + 1,
                               A.nrows()):  # we create zeros below the pivot
                    Aic = A[i][c]
                    A.set_row(i, Aic * Arows[r] - Arc * Arows[i])
                    U.set_row(i, Aic * Urows[r] - Arc * Urows[i])

                r += 1
                c += 1

            else:  # no pivot then only advance in column
                c += 1

        # We finish simplifying the gcds in each row
        gcds = [gcd(row) for row in A]
        T = diagonal_matrix([1 / el if el != 0 else 1 for el in gcds])
        A = (T * A).change_ring(self.parent())
        U = T * U
        return A, U
예제 #3
0
def __contains__(self, x):
    """
    Test whether ``x`` is an element of this subgroup.

    EXAMPLES::

        sage: G.<a,b> = AbelianGroup(2)
        sage: A = G.subgroup([a])
        sage: a in G
        True
        sage: a in A
        True

    TESTS:

    Check that :trac:`32910` is fixed::

        sage: G.<a,b> = AbelianGroup(2, [4, 576])
        sage: Hgens = [a^2, a*b^2]
        sage: H = G.subgroup(Hgens)
        sage: [g in H for g in (a^3, b^2, b^3, a^3*b^2, "junk")]
        [False, False, False, True, False]

    Check that :trac:`31507` is fixed::

        sage: G = AbelianGroup(2, gens_orders=[16, 16])
        sage: f0, f1 = G.gens()
        sage: H = G.subgroup([f0*f1^3])
        sage: [g in H for g in (f0, f0*f1^2, f0*f1^3, f0*f1^4)]
        [False, False, True, False]

        sage: G.<a,b> = AbelianGroup(2)
        sage: Hgens =  [a*b, a*b^-1]
        sage: H = G.subgroup(Hgens)
        sage: b^2 in H
        True
    """

    if not isinstance(x, AbelianGroupElement):
        return False
    if x.parent() is self:
        return True
    elif x in self.ambient_group():
        amb_inv = self.ambient_group().gens_orders()
        inv_basis = diagonal_matrix(ZZ, amb_inv)
        gens_basis = matrix(ZZ, len(self._gens), len(amb_inv),
                            [g.list() for g in self._gens])
        return vector(ZZ, x.list()) in inv_basis.stack(gens_basis).row_module()
    return False
def conf_model(n, X, ring=ZZ, output_file_name=None, verbose=False, parallelize=True, display_degree=7):

    # Set up our graphs
    vertices = range(1, n + 1)
    edges = [(i, j) for i, j in Combinations(vertices, 2)]
    graphs = list(Subsets(edges))

    # Define the poset G(n) as a category
    def G_one(x):
        return '*'
    def G_hom(x, y):
        if x.issubset(y):
            return ['*']
        return []
    def G_comp(x, f, y, g, z):
        return '*'
    G = FiniteCategory(graphs, G_one, G_hom, G_comp)
    Gop = G.op()

    # Print out all the homsets
    if verbose:
        for x in G.objects:
            for y in G.objects:
                print 'Hom(' + str(x) + ', ' + str(y) + ') = ' + str(G.hom(x, y))

    # Build the vertices of X^n

    # Given a tuple of dimensions, this function builds all integer matrices with first column zero,
    # last column dim_tuple, all columns distinct, and where consecutive entries in a row have difference 0 or 1.
    gen_prod_mat = {}


    def generic_prod_mat(dim_tuple):
        if dim_tuple in gen_prod_mat:
            return gen_prod_mat[dim_tuple]
        k = len(dim_tuple)
        first_column = zero_matrix(ZZ, k, 1)
        last_column = matrix(ZZ, k, 1, [d for d in dim_tuple])
        # If dim_list is all zeros, then return a single matrix of zeros.
        if first_column == last_column:
            return [first_column]
        current_batch = [first_column]
        next_batch = []
        gpms = []
        while len(current_batch) != 0:
            for m in current_batch:
                l = m.ncols()
                next_column_options = [range(m[r, l - 1], min(m[r, l - 1] + 2, dim_tuple[r] + 1)) for r in range(k)]
                new_column_iterator = itertools.product(*next_column_options)
                # we don't want the same column again.
                drop = next(new_column_iterator)
                for next_column_tuple in new_column_iterator:
                    next_column = matrix(ZZ, k, 1, next_column_tuple)
                    mm = block_matrix([[m, matrix(ZZ, k, 1, next_column)]], subdivide=False)
                    if next_column == last_column:
                        gpms += [mm]
                    else:
                        next_batch += [mm]
            current_batch = next_batch
            next_batch = []
        gen_prod_mat[dim_tuple] = gpms
        return gpms


    # This set will contain a list of all the Gamma-conf matrices
    # where Gamma is the empty graph on n nodes.
    prod_mats = set()

    nonempty_faces = X.face_iterator(increasing=True)
    # This line pops off the empty face
    next(nonempty_faces)

    for simplex_tuple in itertools.product(nonempty_faces, repeat=n):
        dim_tuple = tuple(s.dimension() for s in simplex_tuple)
        for gpm in generic_prod_mat(dim_tuple):
            l = gpm.ncols()
            m = matrix(ZZ, n, l, [simplex_tuple[r][gpm[r, c]] for r in range(n) for c in range(l)])
            m.set_immutable()
            prod_mats.add(m)

    # Each prod matrix gets assigned a number.  Build the translation dicts both ways.
    # While we're looping, we also compute the row-distinctness graph for each prod matrix
    pm_to_nn = {}
    nn_to_pm = {}
    gammas = {}
    nns_by_gamma = {graph: [] for graph in graphs}
    for nn, pm in enumerate(prod_mats):
        pm_to_nn[pm] = nn
        nn_to_pm[nn] = pm
        graph = Set((i, j) for i, j in edges if pm.row(vertices.index(i)) != pm.row(vertices.index(j)))
        gammas[nn] = graph
        nns_by_gamma[graph] += [nn]

    # The matrix cmb is the combination table.  The (i, j)-entry gives the index of the smallest prod mat that
    # contains the columns of the ith and jth prod mat.  If no such prod mat exists, the table gives -1.
    pmt = len(prod_mats)
    if verbose:
        print 'Preparing combination table'
        print 'Total number of rows: ' + str(pmt)


    # def prep_cmb_row(i):
    #     row = zero_matrix(ZZ, 1, pmt)
    #     p_cols = nn_to_pm[i].columns()
    #     for j in range(i + 1, pmt):
    #         q = nn_to_pm[j]
    #         both_cols = list(set(p_cols + q.columns()))
    #         both_cols.sort()
    #         combined = block_matrix([[matrix(ZZ, n, 1, list(v)) for v in both_cols]], subdivide=False)
    #         combined.set_immutable()
    #         if combined in prod_mats:
    #             row[0, j] = pm_to_nn[combined]
    #         else:
    #             row[0, j] = -1
    #     if verbose:
    #         if i % 100 == 0:
    #             print 'Finished row ' + str(i)
    #     return [row]

    if parallelize:
        pool = Pool()

        def arg_gen(zed):
            num = 0
            while num < zed:
                yield (num, n, pmt, nn_to_pm, prod_mats, pm_to_nn)
                num += 1

        cmb_row_list = pool.map(prep_cmb_row, arg_gen(pmt))
        pool.close()
        pool.join()
    else:
        cmb_row_list = [prep_cmb_row(i) for i in range(pmt)]
    cmb = block_matrix(cmb_row_list)
    cmb = cmb + cmb.transpose() + diagonal_matrix(range(pmt))


    # Check if a list of prod matrices can have their columns assembled into a single prod matrix
    def index_compatible(list_of_indices):
        if len(list_of_indices) <= 1:
            return True
        cur = list_of_indices[0]
        for i in list_of_indices[1:]:
            cur = cmb[cur, i]
            if cur == -1:
                return False
        return True


    # For each graph Gamma, compute the minimal prod matrices of type Gamma.
    min_prod_mat_indices = []
    min_prod_mat_indices_by_gamma = {}
    for gamma in graphs:
        def leq(i, j):
            return cmb[i, j] == j
        poset = Poset((nns_by_gamma[gamma], leq))
        min_prod_mat_indices_by_gamma[gamma] = poset.minimal_elements()
        min_prod_mat_indices += min_prod_mat_indices_by_gamma[gamma]

    # Build the resulting simplicial complex model for the product
    if verbose:
        print
        print 'Building the souped-up model for the product'
        print

    # My copy of sagemath has a verbose flag for SimplicialComplex, but this is not standard yet
    prod_model = SimplicialComplex(from_characteristic_function=(index_compatible, min_prod_mat_indices))
    dim = prod_model.dimension()


    # for graph in graphs:
    #     Y = SimplicialComplex(from_characteristic_function=(index_compatible,
    #                      [i for gamma in graphs if graph.issubset(gamma) for i in min_prod_mat_indices_by_gamma[gamma]]))
    #     print 'Graph ' + str(graph)
    #     for d in range(Y.dimension() + 1):
    #         print 'Faces of dimension ' + str(d) + ': ' + str(len(Y.n_faces(d)))
    #     print Y.homology()
    #     print
    #
    #
    # sys.exit(0)

    if verbose:
        print 'Computing generator degrees'
    z = 1
    zz = sum(len(prod_model.n_faces(d)) for d in range(dim + 1))
    sorted_basis = {}
    for d in range(dim + 1):
        sorted_basis[d] = {graph: [] for graph in graphs}
        for f in prod_model.n_faces(d):
            if verbose:
                print '\r' + str(z) + '/' + str(zz),
                sys.stdout.flush()
                z += 1
            gamma = Set(set(edges).intersection(*[set(gammas[m]) for m in f]))
            sorted_basis[d][gamma] += [f]
    print

    # In degree d, this dict holds a list of faces of prod_model
    basis = {}
    # labels[d] has the same length as basis[d] and tells you which graph
    labels = {}
    for d in range(-1, dim + 2):
        basis[d] = []
        labels[d] = []
        if d in range(dim + 1):
            for graph in graphs:
                batch = sorted_basis[d][graph]
                basis[d] += batch
                labels[d] += [graph] * len(batch)


    def f_law((d,), x, f, y):
        if d in labels:
            return CatMat.identity_matrix(ring, Gop, labels[d])
        else:
            return CatMat.identity_matrix(ring, Gop, [])
예제 #5
0
    def bidegree_tables(self, short=False, flip=False):
        """
        Return a 5-tuple of matrices representing, for each bidegree:

        - [0] whether or not it is in the support
        - [1] the dimension of the domain
        - [2] the dimension of the codomain
        - [3] the dimension of the kernel
        - [4] the dimension of the image

        If ``short=True``, return only a 3-tuple with the first 3
        entries from the above list.

        If ``flip=True``, return the tables for the dual.
        """

        S = diagonal_matrix([self.pq, self.pq, 1])
        Si = ~S
        AUT = [S * M * Si for M in self.Delta_symmetry]

        pqDelta = self.pq * self.Delta
        if flip:
            dx, dy = sum(self.Delta.integral_points())
            xmax = dx
            ymax = dy
        else:
            xmax = max(pt[0] for pt in pqDelta.vertices())
            ymax = max(pt[1] for pt in pqDelta.vertices())
        Msupp = Matrix(ZZ, ymax+1, xmax+1)
        Md = Matrix(ZZ, ymax+1, xmax+1)
        Mc = Matrix(ZZ, ymax+1, xmax+1)

        for bideg in self.tensor_points(pqDelta):
            x, y = bideg
            if flip:
                x = dx - x
                y = dy - y
                if x < 0 or y < 0:
                    continue
            Md[y,x] = self.fd[bideg]
            Mc[y,x] = self.fc[bideg]
            Msupp[y,x] = 1

        if short:
            return Msupp, Md, Mc

        self.ker_im()  # Compute results
        Mker = Matrix(ZZ, ymax+1, xmax+1)
        Mim = Matrix(ZZ, ymax+1, xmax+1)

        for bideg in self.bidegrees:
            v = vector([bideg[0], bideg[1], 1])
            for g in AUT:
                x, y, _ = g * v
                if flip:
                    x = dx - x
                    y = dy - y
                    if x < 0 or y < 0:
                        continue
                Mker[y,x] = self.results[bideg][0]
                Mim[y,x] = self.results[bideg][1]

        return Msupp, Md, Mc, Mker, Mim
예제 #6
0
def _splitting_classes_gens_(K, m, d):
    r"""
    Given a number field `K` of conductor `m` and degree `d`,
    this returns a set of multiplicative generators of the
    subgroup of `(\mathbb{Z}/m\mathbb{Z})^{\times}/(\mathbb{Z}/m\mathbb{Z})^{\times d}`
    containing exactly the classes that contain the primes splitting
    completely in `K`.

    EXAMPLES::

        sage: from sage.rings.number_field.number_field import _splitting_classes_gens_
        sage: K = CyclotomicField(101)
        sage: L = K.subfields(20)[0][0]
        sage: L.conductor()
        101
        sage: _splitting_classes_gens_(L,101,20)
        [95]

        sage: K = CyclotomicField(44)
        sage: L = K.subfields(4)[0][0]
        sage: _splitting_classes_gens_(L,44,4)
        [37]

        sage: K = CyclotomicField(44)
        sage: L = K.subfields(5)[0][0]
        sage: K.degree()
        20
        sage: L
        Number Field in zeta44_0 with defining polynomial x^5 - 2*x^4 - 16*x^3 + 24*x^2 + 48*x - 32 with zeta44_0 = 3.837971894457990?
        sage: L.conductor()
        11
        sage: _splitting_classes_gens_(L,11,5)
        [10]

    """

    R = K.ring_of_integers()
    Zm = IntegerModRing(m)
    unit_gens = Zm.unit_gens()
    ZZunits = ZZ**len(unit_gens)
    unit_relations = [gcd(d, x.multiplicative_order()) for x in unit_gens]
    # sparse=False can be removed if the code below doesn't raise the following
    # AttributeError: 'Matrix_integer_sparse' object has no attribute '_clear_denom'
    D = diagonal_matrix(unit_relations, sparse=False)
    Zmstar = ZZunits / D.row_module()

    def map_Zmstar_to_Zm(h):
        li = h.lift().list()
        return prod(unit_gens[i]**li[i] for i in range(len(unit_gens)))

    Hgens = []
    H = Zmstar.submodule([])
    Horder = Zmstar.cardinality() / d

    for g in Zmstar:
        if H.cardinality() == Horder:
            break
        if g not in H:
            u = map_Zmstar_to_Zm(g)
            p = u.lift()
            while not p.is_prime():
                p += m
            f = R.ideal(p).prime_factors()[0].residue_class_degree()
            h = g * f
            if h not in H:
                Hgens += [h]
                H = Zmstar.submodule(Hgens)

    return [map_Zmstar_to_Zm(h) for h in Hgens]
예제 #7
0
 def test_pullback_4_1(self):
     self.assert_pullback_m_1(8, diagonal_matrix([1, 1, 1, 1]), prec=10)
     self.assert_pullback_m_1(8, diagonal_matrix([2, 1, 1, 1]), prec=8)
예제 #8
0
 def test_pullback_3_1(self):
     self.assert_pullback_m_1(6, diagonal_matrix([1, 1, 1]))
     self.assert_pullback_m_1(6, matrix([[1, 1, 0],
                                         [1, 1, 0],
                                         [0, 0, 2]]))
예제 #9
0
 def assert_pullback_m_1(self, k, A1, prec=10):
     f0 = eisenstein_pullback_coeff(k, A1, diagonal_matrix([0]))
     f = eisenstein_series_qexp(k, prec=prec, normalization='constant') * f0
     for a in range(prec):
         self.assertEqual(f[a],
                          eisenstein_pullback_coeff(k, A1, diagonal_matrix([a])))
예제 #10
0
    def __call__(self, hmult, vmult):
        r"""
        INPUT:

        - ``hmult`` -- multiplicities of the horizontal twists

        - ``vmult`` -- multiplicities of the vertical twists
        """
        if len(hmult) != self._num_hcyls or len(vmult) != self._num_vcyls:
            raise ValueError("invalid input lengths")

        E = self._E
        H = E * diagonal_matrix(vmult)
        V = E.transpose() * diagonal_matrix(hmult)

        if self._num_hcyls < self._num_vcyls:
            F = H * V
        else:
            F = V * H
        p = F.charpoly()
        assert F.nrows() == F.ncols() == min(
            [self._num_hcyls, self._num_vcyls])

        pf = max(p.roots(AA, False))
        mp = pf.minpoly()
        if mp.degree() == 1:
            K = QQ
            pf = QQ(pf)
        else:
            fwd, bck, q = do_polred(pf.minpoly())
            im_gen = fwd(pf)
            K = NumberField(q, 'a', embedding=im_gen)
            pf = bck(K.gen())

        # Compute widths of the cylinders via Perron-Frobenius
        if self._num_hcyls < self._num_vcyls:
            hcirc = (F - pf).right_kernel_matrix()
            assert hcirc.nrows() == 1
            assert hcirc.ncols() == self._num_hcyls
            hcirc = hcirc[0]
            assert all(x > 0 for x in hcirc)
            vcirc = V * hcirc
            c = 1
            d = pf
        else:
            vcirc = (F - pf).right_kernel_matrix()
            assert vcirc.nrows() == 1
            assert vcirc.ncols() == self._num_vcyls
            vcirc = vcirc[0]
            assert all(x > 0 for x in vcirc)
            hcirc = H * vcirc
            d = 1
            c = pf

        # Solve linear systems to get heights
        h = [hcirc[i] * hmult[i] / c for i in range(self._num_hcyls)]
        v = [vcirc[i] * vmult[i] / d for i in range(self._num_vcyls)]

        C = ConvexPolygons(K)
        P = []
        for i in range(self._o.nb_squares()):
            hi = h[self._hcycles[i]]
            vi = v[self._vcycles[i]]
            P.append(C(edges=[(vi, 0), (0, hi), (-vi, 0), (0, -hi)]))

        surface = Surface_list(base_ring=K)
        for p in P:
            surface.add_polygon(p)
        r = self._o.r_tuple()
        u = self._o.u_tuple()
        for i in range(self._o.nb_squares()):
            surface.set_edge_pairing(i, 1, r[i], 3)
            surface.set_edge_pairing(i, 0, u[i], 2)
        surface.set_immutable()
        return TranslationSurface(surface)