コード例 #1
0
ファイル: chomp.py プロジェクト: timgates42/sage
def process_generators_simplicial(gen_string, dim, complex):
    r"""
    Process CHomP generator information for simplicial complexes.

    :param gen_string: generator output from CHomP
    :type gen_string: string
    :param dim: dimension in which to find generators
    :type dim: integer
    :param complex: simplicial complex under consideration
    :return: list of generators in each dimension, as described below

    ``gen_string`` has the form ::

        The 2 generators of H_1 follow:
        generator 1
        -1 * (1,6)
        1 * (1,4)
        ...
        generator 2
        -1 * (1,6)
        1 * (1,4)
        ...

    where each line contains a coefficient and a simplex.  Each line
    is converted, using regular expressions, from a string to a pair
    ``(coefficient, Simplex)``, like ::

        (-1, (1,6))

    representing an element in the free abelian group with basis given
    by simplices.  Each generator is a list of such pairs,
    representing the sum of such elements.  These are reassembled in
    :meth:`CHomP.__call__` to actual elements in the free module
    generated by the simplices of the simplicial complex in the
    appropriate dimension.

    Therefore the return value is a list of lists of pairs, one list
    of pairs for each generator.

    EXAMPLES::

        sage: from sage.interfaces.chomp import process_generators_simplicial
        sage: s = "The 2 generators of H_1 follow:\ngenerator 1:\n-1 * (1,6)\n1 * (1,4)"
        sage: process_generators_simplicial(s, 1, simplicial_complexes.Torus())
        [[(-1, (1, 6)), (1, (1, 4))]]
    """
    from sage.homology.all import Simplex
    # each dim in gen_string starts with "The generator for H_3
    # follows:".  So search for "T" to find the end of the current
    # list of generators.
    g_srch = re.compile(r'H_%s\sfollow[s]?:\n([^T]*)(?:T|$)' % dim)
    g = g_srch.search(gen_string)
    output = []
    simplex_list = []
    if g:
        g = g.group(1)
    if g:
        lines = g.splitlines()
        for l in lines:
            simplex = re.search(r'([+-]?)\s?([0-9]+)?\s?[*]?\s?(\([0-9,]*\))',
                                l)
            if simplex:
                if simplex.group(1) and re.search("-", simplex.group(1)):
                    sign = -1
                else:
                    sign = 1
                if simplex.group(2) and len(simplex.group(2)) > 0:
                    coeff = sign * int(simplex.group(2))
                else:
                    coeff = sign * 1
                simplex = Simplex(
                    [int(a) for a in simplex.group(3).strip('()').split(',')])
                simplex_list.append((coeff, simplex))
            else:
                if simplex_list:
                    output.append(simplex_list)
                    simplex_list = []
        if simplex_list:
            output.append(simplex_list)
        return output
    else:
        return None
コード例 #2
0
ファイル: chomp.py プロジェクト: mcognetta/sage
def process_generators_simplicial(gen_string, dim, complex):
    r"""
    Process CHomP generator information for simplicial complexes.

    :param gen_string: generator output from CHomP
    :type gen_string: string
    :param dim: dimension in which to find generators
    :type dim: integer
    :param complex: simplicial complex under consideration
    :return: list of generators in each dimension, as described below

    ``gen_string`` has the form ::

        The 2 generators of H_1 follow:
        generator 1
        -1 * (1,6)
        1 * (1,4)
        ...
        generator 2
        -1 * (1,6)
        1 * (1,4)
        ...

    where each line contains a coefficient and a simplex.  Each line
    is converted, using regular expressions, from a string to a pair
    ``(coefficient, Simplex)``, like ::

        (-1, (1,6))

    representing an element in the free abelian group with basis given
    by simplices.  Each generator is a list of such pairs,
    representing the sum of such elements.  These are reassembled in
    :meth:`CHomP.__call__` to actual elements in the free module
    generated by the simplices of the simplicial complex in the
    appropriate dimension.

    Therefore the return value is a list of lists of pairs, one list
    of pairs for each generator.

    EXAMPLES::

        sage: from sage.interfaces.chomp import process_generators_simplicial
        sage: s = "The 2 generators of H_1 follow:\ngenerator 1:\n-1 * (1,6)\n1 * (1,4)"
        sage: process_generators_simplicial(s, 1, simplicial_complexes.Torus())
        [[(-1, (1, 6)), (1, (1, 4))]]
    """
    from sage.homology.all import Simplex
    # each dim in gen_string starts with "The generator for H_3
    # follows:".  So search for "T" to find the end of the current
    # list of generators.
    g_srch = re.compile(r'H_%s\sfollow[s]?:\n([^T]*)(?:T|$)' % dim)
    g = g_srch.search(gen_string)
    output = []
    simplex_list = []
    if g:
        g = g.group(1)
    if g:
        lines = g.splitlines()
        for l in lines:
            simplex = re.search(r'([+-]?)\s?([0-9]+)?\s?[*]?\s?(\([0-9,]*\))', l)
            if simplex:
                if simplex.group(1) and re.search("-", simplex.group(1)):
                    sign = -1
                else:
                    sign = 1
                if simplex.group(2) and len(simplex.group(2)) > 0:
                    coeff = sign * int(simplex.group(2))
                else:
                    coeff = sign * 1
                simplex = Simplex([int(a) for a in simplex.group(3).strip('()').split(',')])
                simplex_list.append((coeff, simplex))
            else:
                if simplex_list:
                    output.append(simplex_list)
                    simplex_list = []
        if simplex_list:
            output.append(simplex_list)
        return output
    else:
        return None
コード例 #3
0
ファイル: chomp.py プロジェクト: timgates42/sage
    def __call__(self, program, complex, subcomplex=None, **kwds):
        """
        Call a CHomP program to compute the homology of a chain
        complex, simplicial complex, or cubical complex.

        See :class:`CHomP` for full documentation.

        EXAMPLES::

            sage: from sage.interfaces.chomp import CHomP
            sage: T = cubical_complexes.Torus()
            sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP
            {0: 0, 1: Z x Z, 2: Z}
        """
        from sage.misc.temporary_file import tmp_filename
        from sage.homology.all import CubicalComplex, cubical_complexes
        from sage.homology.all import SimplicialComplex, Simplex
        from sage.homology.chain_complex import HomologyGroup
        from subprocess import Popen, PIPE
        from sage.rings.all import QQ, ZZ
        from sage.modules.all import VectorSpace, vector
        from sage.combinat.free_module import CombinatorialFreeModule

        if not have_chomp(program):
            raise OSError("Program %s not found" % program)

        verbose = kwds.get('verbose', False)
        generators = kwds.get('generators', False)
        extra_opts = kwds.get('extra_opts', '')
        base_ring = kwds.get('base_ring', ZZ)

        if extra_opts:
            extra_opts = extra_opts.split()
        else:
            extra_opts = []

        # type of complex:
        cubical = False
        simplicial = False
        chain = False
        # CHomP seems to have problems with cubical complexes if the
        # first interval in the first cube defining the complex is
        # degenerate.  So replace the complex X with [0,1] x X.
        if isinstance(complex, CubicalComplex):
            cubical = True
            edge = cubical_complexes.Cube(1)
            original_complex = complex
            complex = edge.product(complex)
            if verbose:
                print("Cubical complex")
        elif isinstance(complex, SimplicialComplex):
            simplicial = True
            if verbose:
                print("Simplicial complex")
        else:
            chain = True
            base_ring = kwds.get('base_ring', complex.base_ring())
            if verbose:
                print("Chain complex over %s" % base_ring)

        if base_ring == QQ:
            raise ValueError(
                "CHomP doesn't compute over the rationals, only over Z or F_p."
            )
        if base_ring.is_prime_field():
            p = base_ring.characteristic()
            extra_opts.append('-p%s' % p)
            mod_p = True
        else:
            mod_p = False

        #
        #    complex
        #
        try:
            data = complex._chomp_repr_()
        except AttributeError:
            raise AttributeError(
                "Complex cannot be converted to use with CHomP.")

        datafile = tmp_filename()
        with open(datafile, 'w') as f:
            f.write(data)

        #
        #    subcomplex
        #
        if subcomplex is None:
            if cubical:
                subcomplex = CubicalComplex([complex.n_cells(0)[0]])
            elif simplicial:
                m = re.search(r'\(([^,]*),', data)
                v = int(m.group(1))
                subcomplex = SimplicialComplex([[v]])
        else:
            # replace subcomplex with [0,1] x subcomplex.
            if cubical:
                subcomplex = edge.product(subcomplex)
        #
        #    generators
        #
        if generators:
            genfile = tmp_filename()
            extra_opts.append('-g%s' % genfile)

        #
        #    call program
        #
        if subcomplex is not None:
            try:
                sub = subcomplex._chomp_repr_()
            except AttributeError:
                raise AttributeError(
                    "Subcomplex cannot be converted to use with CHomP.")
            subfile = tmp_filename()
            with open(subfile, 'w') as f:
                f.write(sub)
        else:
            subfile = ''
        if verbose:
            print("Popen called with arguments", end="")
            print([program, datafile, subfile] + extra_opts)
            print("")
            print("CHomP output:")
            print("")
        # output = Popen([program, datafile, subfile, extra_opts],
        cmd = [program, datafile]
        if subfile:
            cmd.append(subfile)
        if extra_opts:
            cmd.extend(extra_opts)
        output = Popen(cmd, stdout=PIPE).communicate()[0]
        if verbose:
            print(output)
            print("End of CHomP output")
            print("")
        if generators:
            with open(genfile, 'r') as f:
                gens = f.read()
            if verbose:
                print("Generators:")
                print(gens)
        #
        #    process output
        #
        if output.find('ERROR') != -1:
            raise RuntimeError('error inside CHomP')
        # output contains substrings of one of the forms
        # "H_1 = Z", "H_1 = Z_2 + Z", "H_1 = Z_2 + Z^2",
        # "H_1 = Z + Z_2 + Z"
        if output.find('trivial') != -1:
            if mod_p:
                return {0: VectorSpace(base_ring, 0)}
            else:
                return {0: HomologyGroup(0, ZZ)}
        d = {}
        h = re.compile("^H_([0-9]*) = (.*)$", re.M)
        tors = re.compile("Z_([0-9]*)")
        #
        #    homology groups
        #
        for m in h.finditer(output):
            if verbose:
                print(m.groups())
            # dim is the dimension of the homology group
            dim = int(m.group(1))
            # hom_str is the right side of the equation "H_n = Z^r + Z_k + ..."
            hom_str = m.group(2)
            # need to read off number of summands and their invariants
            if hom_str.find("0") == 0:
                if mod_p:
                    hom = VectorSpace(base_ring, 0)
                else:
                    hom = HomologyGroup(0, ZZ)
            else:
                rk = 0
                if hom_str.find("^") != -1:
                    rk_srch = re.search(r'\^([0-9]*)\s?', hom_str)
                    rk = int(rk_srch.group(1))
                rk += len(re.findall(r"(Z$)|(Z\s)", hom_str))
                if mod_p:
                    rk = rk if rk != 0 else 1
                    if verbose:
                        print("dimension = %s, rank of homology = %s" %
                              (dim, rk))
                    hom = VectorSpace(base_ring, rk)
                else:
                    n = rk
                    invts = []
                    for t in tors.finditer(hom_str):
                        n += 1
                        invts.append(int(t.group(1)))
                    for i in range(rk):
                        invts.append(0)
                    if verbose:
                        print(
                            "dimension = %s, number of factors = %s, invariants = %s"
                            % (dim, n, invts))
                    hom = HomologyGroup(n, ZZ, invts)

            #
            #    generators
            #
            if generators:
                if cubical:
                    g = process_generators_cubical(gens, dim)
                    if verbose:
                        print("raw generators: %s" % g)
                    if g:
                        module = CombinatorialFreeModule(
                            base_ring,
                            original_complex.n_cells(dim),
                            prefix="",
                            bracket=True)
                        basis = module.basis()
                        output = []
                        for x in g:
                            v = module(0)
                            for term in x:
                                v += term[0] * basis[term[1]]
                            output.append(v)
                        g = output
                elif simplicial:
                    g = process_generators_simplicial(gens, dim, complex)
                    if verbose:
                        print("raw generators: %s" % gens)
                    if g:
                        module = CombinatorialFreeModule(base_ring,
                                                         complex.n_cells(dim),
                                                         prefix="",
                                                         bracket=False)
                        basis = module.basis()
                        output = []
                        for x in g:
                            v = module(0)
                            for term in x:
                                if complex._is_numeric():
                                    v += term[0] * basis[term[1]]
                                else:
                                    translate = complex._translation_from_numeric(
                                    )
                                    simplex = Simplex(
                                        [translate[a] for a in term[1]])
                                    v += term[0] * basis[simplex]
                            output.append(v)
                        g = output
                elif chain:
                    g = process_generators_chain(gens, dim, base_ring)
                    if verbose:
                        print("raw generators: %s" % gens)
                if g:
                    if not mod_p:
                        # sort generators to match up with corresponding invariant
                        g = [
                            _[1]
                            for _ in sorted(zip(invts, g), key=lambda x: x[0])
                        ]
                    d[dim] = (hom, g)
                else:
                    d[dim] = hom
            else:
                d[dim] = hom

        if chain:
            new_d = {}
            diff = complex.differential()
            if len(diff) == 0:
                return {}
            bottom = min(diff)
            top = max(diff)
            for dim in d:
                if complex._degree_of_differential == -1:  # chain complex
                    new_dim = bottom + dim
                else:  # cochain complex
                    new_dim = top - dim
                if isinstance(d[dim], tuple):
                    # generators included.
                    group = d[dim][0]
                    gens = d[dim][1]
                    new_gens = []
                    dimension = complex.differential(new_dim).ncols()
                    # make sure that each vector is embedded in the
                    # correct ambient space: pad with a zero if
                    # necessary.
                    for v in gens:
                        v_dict = v.dict()
                        if dimension - 1 not in v.dict():
                            v_dict[dimension - 1] = 0
                            new_gens.append(vector(base_ring, v_dict))
                        else:
                            new_gens.append(v)
                    new_d[new_dim] = (group, new_gens)
                else:
                    new_d[new_dim] = d[dim]
            d = new_d
        return d