258 lines
11 KiB
Python
258 lines
11 KiB
Python
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
|
|
|