diff --git a/minimalModel.py b/minimalModel.py index bec0e0a..d3b2043 100644 --- a/minimalModel.py +++ b/minimalModel.py @@ -1,143 +1,96 @@ -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 phi_polinomial(pol, model_morphism, A): -def has_preimage(M, element): + pol_keys = pol.dict().keys() + gens = pol.parent().gens() + m_gens = [g for g,e in zip(gens,sum([vector(k) for k in pol_keys])) if e] + aplkGens = [model_morphism[g.degree()][str(g)] for g in m_gens] + aplkElement = A.null_element() + for exp in pol.dict().keys(): + coef = pol.dict()[exp] + # To-Do: check if exp and aplkGens are sorted in the same way + term = coef*prod([gen**e for gen,e in zip(aplkGens, exp)]) + aplkElement += term + return aplkElement - 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 get_ker_elements(sullivan_model, model_morphism, A, dim): + # Get the basis of Hm + hm_basis = sullivan_model.cohomology(dim).basis() + if hm_basis: + Hc = A.cohomology_group(dim) + apl_correspondence = {} + cochain_correspondence = {} + Hc_correspondence = {} + M = zero_matrix(QQ,len(Hc.basis()),len(hm_basis)) + i = 0 + hm_basis_keys = [g.representative() for g in hm_basis.keys()] + for gen_repr in hm_basis_keys: + # get an element of the basis of Hm + # get a representative of a cohomology class in M + # get the apl representative for each generator of M^p + apl_correspondence[gen_repr] = phi_polinomial(gen_repr, model_morphism, A) + cochain = apl_correspondence[gen_repr].integral() + cochain_correspondence[gen_repr] = cochain + vcochain = A.cochain_to_vector(cochain,dim) + Hc_correspondence[gen_repr] = Hc(vcochain) + M[:,i] = Hc_correspondence[gen_repr] + i+=1 + kernelGens = M.right_kernel().basis() + m_kernel = [sum([a*b for a,b in zip(hm_basis_keys,g)]) for g in kernelGens] + return m_kernel + else: + return [] +def convert_to(element, gca): + d = element.dict() + new_e = {tuple(list(k)+[0]): d[k] for k in d.keys()} + return gca(new_e) + def injectivity_step(M, phi, check_dim, names, - degrees, differentials, index=0, depth=0, max_depth=5): + degrees, differentials, A, 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") + m_kernel = get_ker_elements(M, phi, A, check_dim) + if len(m_kernel)>0: + phi_new_gen = {} + for kerElement in m_kernel: + new_gen = 'y'+str(check_dim-1)+'_'+str(index) + print(" Proceed to kill", kerElement, " 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)]}) + new_degrees = tuple(list(degrees)+[check_dim-1]) 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] + gen = gca.gens_dict()[new_gen] + aplkElement = phi_polinomial(kerElement, phi, A) + print(aplkElement) + print(aplkElement.is_zero()) + print(aplkElement == A.null_element()) + if aplkElement == A.null_element(): + preimage = A.null_element() + else: + preimage = aplkElement.solve_primitive() + phi_new_gen = {str(gen): preimage} + new_differentials = {convert_to(k,gca):convert_to(differentials[k],gca) for k in differentials.keys()} + new_differentials.update({gen: convert_to(kerElement, gca)}) + new_M = gca.cdg_algebra(new_differentials) + new_phi = {k: {s:phi[k][s] for s in phi[k].keys()} for k in phi.keys()} + d = new_phi[check_dim-1] 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] - + new_degrees, new_differentials, A, index=index+1, depth=depth+1, max_depth=max_depth) + else: + 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) + unmmaped_elements = deepcopy(coho_aplk) for gen_key in coho_gens.keys(): gen = coho_gens[gen_key] found = False @@ -167,10 +120,10 @@ def surjective_step(A, M, phi, dim, names, degrees, differentials, cohomology_co 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) + new_names = deepcopy(names) + new_degrees = deepcopy(degrees) + new_phi = {k: {s:phi[k][s] for s in phi[k].keys()} for k in phi.keys()} + new_correspondence = deepcopy(cohomology_correspondence) phi_n_map = {} correspondence_n_map = {} for gen_key in g.keys(): @@ -223,7 +176,7 @@ def minimal_model(S, max_depth=5, max_dim=5): # 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) + degrees, differentials, A, 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] @@ -233,7 +186,7 @@ def minimal_model(S, max_depth=5, max_dim=5): [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) + degrees, differentials, A, index=0, depth=0, max_depth=5) if limit: print("The algorithm has reach the injectivity depth-limit at dimension", dim) break