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])
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())