예제 #1
0
 def morGradingSet():
     """Find the grading set of the new type D structure."""
     lr_domains = [(d1, d2.opp())
                   for d1, d2 in self.gr_set.periodic_domains]
     self.lr_set = SimpleDbGradingSet(
         self.gr_set.gr_group1, ACTION_LEFT,
         self.gr_set.gr_group2.opp(), ACTION_RIGHT, lr_domains)
     return GeneralGradingSet([self.lr_set.inverse(), other.gr_set])
예제 #2
0
class SimpleDDStructure(DDStructure):
    """Represents a type DD structure with a finite number of generators, and
    explicitly stored generating set and delta operation.

    """
    def __init__(self, ring, algebra1, algebra2,
                 side1 = ACTION_LEFT, side2 = ACTION_LEFT):
        """Specifies the algebras and sides of the type D action."""
        assert side1 == ACTION_LEFT and side2 == ACTION_LEFT, \
            "Right action not implemented."
        DDStructure.__init__(self, ring, algebra1, algebra2, side1, side2)
        self.generators = set()
        self.delta_map = dict()

    def __len__(self):
        return len(self.generators)

    def delta(self, generator):
        return self.delta_map[generator]

    def getGenerators(self):
        return list(self.generators)

    def addGenerator(self, generator):
        """Add a generator. No effect if the generator already exists."""
        assert generator.parent == self
        assert isinstance(generator, DDGenerator)
        self.generators.add(generator)
        if not self.delta_map.has_key(generator):
            self.delta_map[generator] = E0

    def addDelta(self, gen_from, gen_to, alg_coeff1, alg_coeff2, ring_coeff):
        """Add ring_coeff * (alg_coeff1, alg_coeff2) * gen_to to the delta of
        gen_from. The first four arguments should be generators.

        """
        assert gen_from.parent == self and gen_to.parent == self
        assert alg_coeff1.getLeftIdem() == gen_from.idem1
        assert alg_coeff1.getRightIdem() == gen_to.idem1
        assert alg_coeff2.getLeftIdem() == gen_from.idem2
        assert alg_coeff2.getRightIdem() == gen_to.idem2
        target_gen = TensorGenerator((alg_coeff1, alg_coeff2, gen_to),
                                     self.AAtensorM)
        self.delta_map[gen_from] += target_gen.elt(ring_coeff)

    def deltaCoeff(self, gen_from, gen_to):
        """Return the coefficient (as bialgebra element) of gen_to in delta of
        gen_from.

        """
        if self.delta_map[gen_from] == 0:
            return E0
        else:
            # No need for algebra structure on the tensor product
            return self.delta_map[gen_from].fixLast(gen_to)

    def reindex(self):
        """Replace the generators by simple generators indexed by integers."""
        gen_list = list(self.generators)
        new_gen_list = []
        translate_dict = dict()
        for i in range(len(gen_list)):
            new_gen = gen_list[i].toSimpleDDGenerator("g%d"%(i+1))
            new_gen_list.append(new_gen)
            translate_dict[gen_list[i]] = new_gen
        self.generators = set(new_gen_list)
        new_delta = dict()
        for k, v in self.delta_map.items():
            new_v = E0
            for (AGen1, AGen2, MGen), coeff in v.items():
                target_gen = TensorGenerator(
                    (AGen1, AGen2, translate_dict[MGen]), self.AAtensorM)
                new_v += target_gen.elt(coeff)
            new_delta[translate_dict[k]] = new_v
        self.delta_map = new_delta
        if hasattr(self, "grading"):
            new_grading = dict()
            for gen, gr in self.grading.items():
                if gen in translate_dict: # gen is still in ddstr
                    new_grading[translate_dict[gen]] = gr
            self.grading = new_grading

    def testDelta(self):
        """Verify d^2 = 0 for this structure."""
        for gen in self.generators:
            if gen.delta().diff() != 0:
                # Print the offending terms in d^2 for one generator.
                print gen, "==>"
                for k, v in gen.delta().diff().items():
                    print v, "*", k
                return False
        return True

    def getReverseDelta(self):
        """Returns the reverse of delta map. Return value is a dictionary with
        generators as keys, and list of ((a1, a2, gen), coeff) as values.

        Every ((b1, b2, p), coeff) in the list for q means (b1*b2)*q occurs in
        delta(p) with ring coefficient coeff.

        """
        rev_delta = dict()
        for x in self.generators:
            rev_delta[x] = []
        for p in self.generators:
            for (b1, b2, q), coeff in p.delta().items():
                rev_delta[q].append(((b1, b2, p), coeff))
        return rev_delta

    def __str__(self):
        result = "Type DD Structure.\n"
        for k, v in self.delta_map.items():
            result += "d(%s) = %s\n" % (k, v)
        return result

    def morToD(self, other):
        """Compute the type D structure of morphisms from self to other. Note
        ``other`` must be a type D structure.

        """
        assert self.algebra1 == other.algebra
        alg_gens = self.algebra1.getGenerators()
        xlist = self.getGenerators()
        ylist = other.getGenerators()
        gens = list()
        dstr = SimpleDStructure(F2, self.algebra2.opp())
        genType = MorDDtoDGenerator

        def morGradingSet():
            """Find the grading set of the new type D structure."""
            lr_domains = [(d1, d2.opp())
                          for d1, d2 in self.gr_set.periodic_domains]
            self.lr_set = SimpleDbGradingSet(
                self.gr_set.gr_group1, ACTION_LEFT,
                self.gr_set.gr_group2.opp(), ACTION_RIGHT, lr_domains)
            return GeneralGradingSet([self.lr_set.inverse(), other.gr_set])

        def morGrading(gr_set, x, a, y):
            """Find the grading of the generator x -> ay in the morphism
            type D structure. The grading set need to be provided as gr_set.

            """
            gr_x1, gr_x2 = self.grading[x].data
            gr_x_lr = SimpleDbGradingSetElement(self.lr_set,
                                                (gr_x1, gr_x2.opp()))
            gr = [gr_x_lr.inverse(), other.grading[y] * a.getGrading()]
            return GeneralGradingSetElement(gr_set, gr)

        # Prepare rev_delta for the last step in computing differentials
        rev_delta = self.getReverseDelta()

        # Get the list of generators
        for x in xlist:
            for a in alg_gens:
                for y in ylist:
                    if x.idem1 == a.getLeftIdem() and \
                       y.idem == a.getRightIdem():
                        gens.append(genType(dstr, x, a, y))
        for gen in gens:
            dstr.addGenerator(gen)

        # Get the type D structure maps
        for gen in gens:
            # Differential of y in (x -> ay)
            x, a, y = gen.source, gen.coeff, gen.target
            ady = a * y.delta()
            for (b, q), coeff in ady.items():
                dstr.addDelta(gen, genType(dstr, x, b, q), None, coeff)
            # Differential of a
            for da_gen, coeff in a.diff().items():
                dstr.addDelta(gen, genType(dstr, x, da_gen, y), None, coeff)
            # For each p such that (b1,b2)*x is in dp, add opp(b2)*(p->(b1*a)y)
            for (b1, b2, p), coeff1 in rev_delta[x]:
                for b1a_gen, coeff2 in (b1*a).items():
                    dstr.addDelta(gen, genType(dstr, p, b1a_gen, y),
                                  b2.opp(), coeff1*coeff2)

        # Find grading set and grading of elements
        if hasattr(self, "gr_set") and hasattr(other, "gr_set"):
            dstr.gr_set = morGradingSet()
            dstr.grading = dict()
            for gen in gens:
                dstr.grading[gen] = morGrading(
                    dstr.gr_set, gen.source, gen.coeff, gen.target)
        return dstr

    def morToDD(self, other):
        """Compute the chain complex of type DD structure morphisms from self to
        other. Note ``other`` must be a type DD structure over the same two
        PMC's in the same order.

        Currently does not keep track of gradings.

        """
        assert self.algebra1 == other.algebra1
        assert self.algebra2 == other.algebra2
        alg1_gens = self.algebra1.getGenerators()
        alg2_gens = self.algebra2.getGenerators()
        # Type of coefficients of the morphism
        tensor_alg = TensorDGAlgebra((self.algebra1, self.algebra2))
        xlist = self.getGenerators()
        ylist = other.getGenerators()
        gens = list()
        cx = SimpleChainComplex(F2)
        genType = MorDDtoDDGenerator

        # For computing differentials only
        mor_cx = MorDDtoDDComplex(F2, self, other)

        # Prepare rev_delta for the last step in computing differentials
        rev_delta = self.getReverseDelta()

        # Get the list of generators
        for x in xlist:
            for a1 in alg1_gens:
                for a2 in alg2_gens:
                    for y in ylist:
                        if x.idem1 == a1.getLeftIdem() and \
                           y.idem1 == a1.getRightIdem() and \
                           x.idem2 == a2.getLeftIdem() and \
                           y.idem2 == a2.getRightIdem():
                            a = TensorGenerator((a1, a2), tensor_alg)
                            gens.append(genType(cx, x, a, y))
        for gen in gens:
            cx.addGenerator(gen)

        # Get the differentials of type DD structure maps
        for gen in gens:
            for term, coeff in mor_cx.diff(gen).items():
                cx_term = genType(cx, term.source, term.coeff, term.target)
                cx.addDifferential(gen, cx_term, coeff)
        return cx

    def hochschildCochains(self):
        """Returns the Hochschild cochain complex of self, i.e., the morphisms
        from the DD identity to self.
        
        """
        dd_id = identityDD(self.algebra1.pmc, self.algebra1.idem_size)
        return dd_id.morToDD(self)

    def simplify(self, cancellation_constraint = None):
        """Simplify a type DD structure using cancellation lemma.

        cancellation_constraint is a function from two generators to boolean,
        stating whether they can be cancelled.

        """
        # Simplification is best done in terms of coefficients
        # Build dictionary of coefficients
        arrows = dict()
        for gen in self.generators:
            arrows[gen] = dict()
        bialgebra = TensorDGAlgebra((self.algebra1, self.algebra2))
        for gen in self.generators:
            for (AGen1, AGen2, MGen), coeff in self.delta_map[gen].items():
                if MGen not in arrows[gen]:
                    arrows[gen][MGen] = E0
                arrows[gen][MGen] += TensorGenerator(
                    (AGen1, AGen2), bialgebra) * coeff

        arrows = simplifyComplex(
            arrows, default_coeff = E0,
            cancellation_constraint = cancellation_constraint)

        # Now rebuild the type DD structure
        self.generators = set()
        self.delta_map = dict()
        for x in arrows:
            self.generators.add(x)
            self.delta_map[x] = E0
            for y, coeff in arrows[x].items():
                for (a1, a2), ring_coeff in coeff.items():
                    target_gen = TensorGenerator((a1, a2, y), self.AAtensorM)
                    self.delta_map[x] += ring_coeff * target_gen

    def toDStructure(self):
        """Convert this type DD structure into a type D structure over the
        tensor product of two algebras.

        """
        bialgebra = TensorDGAlgebra((self.algebra1, self.algebra2))
        dstr = SimpleDStructure(self.ring, bialgebra, ACTION_LEFT)
        gen_map = dict()
        for gen in self.generators:
            new_gen = gen.toDGenerator(dstr)
            gen_map[gen] = new_gen
            dstr.addGenerator(new_gen)
        for gen_from in self.generators:
            for (a1, a2, gen_to), coeff in self.delta_map[gen_from].items():
                dstr.addDelta(gen_map[gen_from], gen_map[gen_to],
                              TensorGenerator((a1, a2), bialgebra), coeff)
        return dstr

    def registerHDiagram(self, diagram, base_gen, base_gr = None):
        """Associate the given diagram as the Heegaard diagram from which this
        type DD structure can be derived. We will attempt to match generators
        of the type DD structure to generators of the Heegaard diagram.
        Currently this is possible only if no two generators have the same
        idempotents (so the match can be made by comparing idempotents).

        As a result, computes grading of each generator from the Heegaard
        diagram and checks it against type DD operations. Attributes added are:
        *. self.hdiagram - the Heegaard diagram.
        *. self.hdiagram_gen_map - dictionary mapping generators in the type DD
        structure to generators in Heegaard diagram.
        *. self.gr_set - the grading set (of type SimpleDbGradingSet).
        *. self.grading - dictionary mapping generators in the type DD
        structure to their gradings.

        """
        self.hdiagram = diagram
        # Get PMC's and check that they make sense
        hd_pmc1, hd_pmc2 = self.hdiagram.pmc_list
        dds_pmc1, dds_pmc2 = self.algebra1.pmc, self.algebra2.pmc
        assert hd_pmc1.opp() == dds_pmc1
        assert hd_pmc2.opp() == dds_pmc2
        # Now attempt to match generators
        self.hdiagram_gen_map = dict()
        idem_size = 2 * dds_pmc1.genus - len(base_gen.idem1)
        gens = self.getGenerators()
        hgens = diagram.getHFGenerators(idem_size = idem_size)
        for gen in gens:
            for hgen in hgens:
                hgen_idem1, hgen_idem2 = hgen.getDIdem()
                if (gen.idem1, gen.idem2) == (hgen_idem1, hgen_idem2):
                    self.hdiagram_gen_map[gen] = hgen
                    break
            assert gen in self.hdiagram_gen_map
        # Compute grading and check consistency with algebra actions
        base_hgen = self.hdiagram_gen_map[base_gen]
        self.gr_set, gr = self.hdiagram.computeDDGrading(base_hgen, base_gr)
        self.grading = dict()
        for gen in gens:
            self.grading[gen] = gr[self.hdiagram_gen_map[gen]]
        if ASSERT_LEVEL > 0:
            self.checkGrading()

    @memorize
    def dual(self):
        """Returns the dual of this type DD structure, which is the type DD
        invariant of the orientation reversed bordered 3-manifold. If self has
        left action over A1 and A2, then the new DD structure has left action
        over A2.opp() and A1.opp() (in that order).

        """
        dual_str = SimpleDDStructure(
            self.ring, self.algebra2.opp(), self.algebra1.opp(),
            self.side2, self.side1)
        # Map from generators in self to generators in dual_str
        gen_map = dict()
        for x in self.generators:
            # As in the type D case, simple generators only
            assert isinstance(x, SimpleDDGenerator)
            new_x = SimpleDDGenerator(dual_str, x.idem2.opp(), x.idem1.opp(),
                                      x.name)
            dual_str.addGenerator(new_x)
            gen_map[x] = new_x
        for x in self.generators:
            for (a, b, y), coeff in x.delta().items():
                dual_str.addDelta(gen_map[y], gen_map[x], b.opp(), a.opp(),
                                  coeff)
        return dual_str

    def checkGrading(self):
        for x in self.generators:
            for (a1, a2, y), coeff in x.delta().items():
                gr_x = self.grading[x]
                gr_y = self.grading[y]
                assert gr_x - 1 == gr_y * [a1.getGrading(), a2.getGrading()]

    def compareDDStructures(self, other):
        """Compare two type DD structures, print out any differences."""
        return self.toDStructure().compareDStructures(other.toDStructure())