From ec5c49be307e6b80177c228e64e277a212da05c7 Mon Sep 17 00:00:00 2001 From: carlos alquezar Date: Thu, 31 Mar 2022 16:30:23 +0200 Subject: [PATCH] added minimalModel.py --- minimalModel.py | 257 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 minimalModel.py diff --git a/minimalModel.py b/minimalModel.py new file mode 100644 index 0000000..48e093a --- /dev/null +++ b/minimalModel.py @@ -0,0 +1,257 @@ +def update_cdga(gca, diff_dict={}): + + gens_dict = gca.gens_dict() + differentials = {} + if diff_dict: + for gen_name in diff_dict.keys(): + gen = gens_dict[gen_name] + product_gens = diff_dict[gen_name] + gen0 = gens_dict[product_gens[0]] + if type(product_gens[1]) == int: # is a power + power = product_gens[1] + mult = gen0 + for i in range(1, power): + mult *= gen0 + differentials.update({gen:mult}) + else: + gen1 = gens_dict[product_gens[1]] + differentials.update({gen:gen0*gen1}) +# print("differentials", differentials) + return gca.cdg_algebra(differentials) + + +def has_preimage(M, element): + + d = M.differential() + deg = element.degree()-1 +# print("differential of ", element, "degree: ", deg) + Md = d.differential_matrix(deg) + gens = M.basis(deg) + try: + solution = Md.solve_left(vector(QQ,element.basis_coefficients())) + primitive = sum(map(lambda coef, gen: coef*gen, solution, gens)) + print(element, " has preimage ", primitive) + return True + except ValueError: + return False + +def injectivity_step(M, phi, check_dim, names, + degrees, differentials, index=0, depth=0, max_depth=5): + + print("Injectivity at dimension ", check_dim) + if depth > max_depth: + return [M, phi, names, degrees, differentials, True] + deg = check_dim -1 + gens = [g for g in M.gens() if g.degree() < check_dim] +# gens.sort(reverse=True) + gen_products = [] + for i in range(len(gens)): + e0 = gens[i] + d = e0.degree() + mcd = gcd(d, check_dim) + if d%2 == 0 and mcd == d: + power = True + exp = check_dim / d + mult = e0 + for j in range(1,exp-1): + mult *= e0 + gen_products.append([e0, mult, power, exp]) + for j in range(i+1,len(gens)): + power = False + e1 = gens[j] + if e0.degree() + e1.degree() == check_dim: + gen_products.append([e0, e1, power]) +# print("products at dim ",check_dim,": ", gen_products) + new_M = M + new_phi = phi + new_names = names + new_degrees = degrees + new_differentials = differentials + for pair in gen_products: + # Looking at the minimal model side + gen0 = pair[0] + deg_0 = gen0.degree() + gen1 = pair[1] + deg_1 = gen1.degree() + M_product = gen0*gen1 + # Looking at the AplK side + gen0_name = list(M.gens_dict().keys())[list(M.gens_dict().values()).index(gen0)] + aplk_gen0 = phi[deg_0][gen0_name] + is_power = pair[2] + power = 0 + if is_power: + power = pair[3] + aplk_gen1 = aplk_gen0 + for i in range(1, power): + aplk_gen1 *= aplk_gen0 + else: + gen1_name = list(M.gens_dict().keys())[list(M.gens_dict().values()).index(gen1)] + aplk_gen1 = phi[deg_1][gen1_name] + aplk_product = aplk_gen0*aplk_gen1 + A = aplk_product.parent() + # Check conditions + if (M_product == M(0)) and (aplk_product == A.null_element()): # zero element + pass + elif (M_product == M(0)) and (aplk_product != A.null_element()): #check + if aplk_product.is_closed(): + pass #trivial cohomology class (assuming aplk_product has preimage by differential) + else: + raise ValueError('zero associated to a non-closed element in AplK') + elif (M_product.differential() != 0): # is not closed + print("The element ",M_product, " is not closed") + pass + elif (M_product.differential() == 0) and aplk_product.is_closed() and has_preimage(M, M_product): + pass #trivial cohomology class (assuming aplk_product has preimage by differential) + elif (M_product.differential() == 0) and aplk_product.is_closed(): #key step because M_product doesn't have preimage + new_gen = 'y'+str(deg)+'_'+str(index) + if aplk_product != A.null_element(): + try: + preimage = aplk_product.solve_primitive() + except ValueError: + print(" The element ",M_product, " has non trivial cohomology class. No generator added.") + continue # we don't add a new generator because the element has non trivial cohomology class + phi_new_gen = {new_gen: preimage} + else: + phi_new_gen = {new_gen: A.null_element()} + print(" Proceed to kill", M_product, " because has associated trivial cohomology class") + print("Adding generator "+new_gen) + new_names = names + [new_gen] + new_degrees = tuple(list(degrees)+[deg]) + new_differentials = copy.deepcopy(differentials) + if not is_power: + new_differentials.update({new_gen: [gen0_name, gen1_name]}) + else: + new_differentials.update({new_gen: [gen0_name, int(power)]}) + gca = GradedCommutativeAlgebra(QQ, names=new_names, degrees=new_degrees) + new_M = update_cdga(gca, new_differentials) + new_phi = copy.deepcopy(phi) + d = new_phi[deg] + d.update(phi_new_gen) + return injectivity_step(new_M, new_phi, check_dim, new_names, + new_degrees, new_differentials, index=index+1, depth=depth+1, max_depth=max_depth) + else: + pass + + return [new_M, new_phi, new_names, new_degrees, new_differentials, False] + +def get_unmapped_generators(dim, coho_aplk, coho_gens, correspondence): + + dimensions = list(correspondence.keys()) + unmmaped_elements = copy.deepcopy(coho_aplk) + for gen_key in coho_gens.keys(): + gen = coho_gens[gen_key] + found = False + for i in range(1,dim): + if (not found) and (i in dimensions): + for j in range(i,dim): + if (j in dimensions) and (i+j == dim): + gensi = correspondence[i] + gensj = correspondence[j] + for gi in gensi.values(): + if not found: + for gj in gensj.values(): + product = gi.cup_product(gj) + if product == gen or -product == gen: + found = True + i_name = list(gensi.keys())[list(gensi.values()).index(gi)] + j_name = list(gensj.keys())[list(gensj.values()).index(gj)] + if gen_key in unmmaped_elements.keys(): + print("The element ",i_name+"*"+j_name," is already mapped") + del unmmaped_elements[gen_key] + break + return unmmaped_elements + +def surjective_step(A, M, phi, dim, names, degrees, differentials, cohomology_correspondence): + + print("Surjectivity at dimension ", dim) + coho_aplk = A.get_cohomology_aplk(dim) + cohomology_gens = A.cohomology_gens(dim) + g = get_unmapped_generators(dim, coho_aplk, cohomology_gens, cohomology_correspondence) + new_names = copy.deepcopy(names) + new_degrees = copy.deepcopy(degrees) + new_phi = copy.deepcopy(phi) + new_correspondence = copy.deepcopy(cohomology_correspondence) + phi_n_map = {} + correspondence_n_map = {} + for gen_key in g.keys(): + deg = gen_key[0] + index = gen_key[1] + new_var = 'x'+str(deg)+'_'+str(index) + new_names += [new_var] + new_degrees += [deg] + phi_n_map[new_var] = g[gen_key] + correspondence_n_map[new_var] = cohomology_gens[gen_key] + gca = GradedCommutativeAlgebra(QQ, names=new_names, degrees=new_degrees) + new_M = update_cdga(gca, differentials) + new_phi[dim] = phi_n_map + new_correspondence[dim] = correspondence_n_map + return [new_M, new_phi, new_names, new_degrees, new_correspondence] + +def minimal_model(S, max_depth=5, max_dim=5): + + fundamental_group = S.fundamental_group() + if (fundamental_group.order() != 1): + print("Warning: The space is not simply-connected") + phi = {} + cohomology_correspondence = {} + A = AlgebraAplK(S) + # First step of the algorithm + first_dim = A.first_cohomology_group() + phi_n_map = {} + correspondence_n_map = {} + if first_dim < 0: + return [0,{},{}] # due to the function returns multiple objects + elif first_dim > max_dim: + return [0,{},{}] # due to the function returns multiple objects + g = A.get_cohomology_aplk(first_dim) + cohomology_gens = A.cohomology_gens(first_dim) + names = [] + degrees = [] + for gen_key in g.keys(): + deg = gen_key[0] + index = gen_key[1] + new_var = 'x'+str(deg)+'_'+str(index) + names += [new_var] + degrees += [deg] + phi_n_map[new_var] = g[gen_key] + correspondence_n_map[new_var] = cohomology_gens[gen_key] + differentials={} + gca = GradedCommutativeAlgebra(QQ, names=names, degrees=degrees) + M = gca.cdg_algebra(differentials) + phi[first_dim] = phi_n_map + cohomology_correspondence[first_dim] = correspondence_n_map + # Now we have an isomorphism in the first non-null cohomology group + # we check if phi is a monomorphism + [M, phi, names, degrees, differentials, limit] = injectivity_step(M, phi, first_dim+1, names, + degrees, differentials, index=0, depth=0, max_depth=5) + if limit: + print("The algorithm has reach the injectivity depth-limit at dimension ", first_dim) + return [M, phi] + else: + dim = first_dim + 1 + for i in range(dim, max_dim): + [M, phi, names, degrees, cohomology_correspondence] = surjective_step(A, M, phi, i, names, degrees, + differentials, cohomology_correspondence) + [M, phi, names, degrees, differentials, limit] = injectivity_step(M, phi, i+1, names, + degrees, differentials, index=0, depth=0, max_depth=5) + if limit: + print("The algorithm has reach the injectivity depth-limit at dimension", dim) + break + + return [M, phi, cohomology_correspondence] + +####################################################### +# TESTS +S1 = simplicial_complexes.Sphere(2) +S2 = simplicial_complexes.Sphere(2) +S = S1.wedge(S2, is_mutable=False) +####################################################### +#S = simplicial_complexes.ComplexProjectivePlane() +####################################################### +# S = simplicial_complexes.BrucknerGrunbaumSphere() +####################################################### +# S = simplicial_complexes.ProjectivePlane().wedge(simplicial_complexes.Sphere(2), is_mutable=False) +####################################################### +[sullivan_model, model_morphism, cohomology_correspondence] = minimal_model(S, max_depth=5, max_dim=10) +sullivan_model +