diff --git a/aplK.py b/aplK.py index e80939a..fb702e5 100644 --- a/aplK.py +++ b/aplK.py @@ -1,3 +1,30 @@ +r""" + + + + +EXAMPLES:: + + + +AUTHORS: + +- Carlos Alquézar Baeta (2023-02-07): initial version +- Miguel Ángel Marco (2023-02-07): initial version +- Jorge Martín Morales (2023-02-07): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2023 Carlos Alquézar Baeta +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from sage.algebras.commutative_dga import * from sage.structure.parent import Parent from sage.structure.element import Element @@ -5,6 +32,7 @@ from sage.structure.richcmp import rich_to_bool, op_EQ, op_NE, op_LT, op_LE, op_ from sage.rings.polynomial.polydict import ETuple from copy import deepcopy + class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): """ A differential multi-graded commutative algebra @@ -77,14 +105,15 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): degrees = set([k[0] for k in self.homogeneous_parts()]) return max(degrees) - def homogeneous_part(self,degree): + def homogeneous_part(self, degree): """ Returns the homogeneous part of the element for a given degree. """ - if (degree<0): + if (degree < 0): raise ValueError('`degree` must be a non-negative integer') - parts = [self.homogeneous_parts()[k] for k in self.homogeneous_parts() if list(k)[0]==degree] + parts = [self.homogeneous_parts()[k] + for k in self.homogeneous_parts() if list(k)[0] == degree] return self.parent()(sum(parts)) def reduce(self, relations=[]): @@ -107,7 +136,6 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): return DifferentialGCAlgebra.Element.reduce(self, [apl.t_relation, apl.y_relation]) def _integrable_part(self): - """ The method returns the coefficient in Q[t_1,...,t_n] of the term of degree `n` @@ -119,7 +147,8 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): """ apl = self.parent() - pol = apl(self.reduce()) #needed in case reduce() returns an integer + # needed in case reduce() returns an integer + pol = apl(self.reduce()) relations = [] for i in range(apl.n+1): relations.append(apl.gens()[apl.n+1+i]-1) @@ -139,15 +168,15 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): apl = self.parent() pol = self._integrable_part() result = 0 - for k in pol.dict().keys(): # k is a monomial + for k in pol.dict().keys(): # k is a monomial numerator = 1 denominator = apl.n - for e in k: # e is an exponents + for e in k: # e is an exponents numerator *= factorial(e) denominator += e coef = pol.dict()[k] result += coef*numerator/factorial(denominator) - #return ((-1) ** ((apl.n*(apl.n-1)) // 2)) * result + # return ((-1) ** ((apl.n*(apl.n-1)) // 2)) * result return result def _get_term(self, coef, exp, gens): @@ -181,8 +210,8 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): terms = {} pol = self.dict() for exp in pol.keys(): - key = self._get_term(1, exp,ys_only_gens) - coef = self._get_term(pol[exp],exp,ts_only_gens) + key = self._get_term(1, exp, ys_only_gens) + coef = self._get_term(pol[exp], exp, ts_only_gens) if key not in terms.keys(): terms[key] = coef else: @@ -233,8 +262,9 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): exp = list(monomial.dict().keys())[0] coef = monomial.dict()[exp] n = len(exp)//2 - if (exp[n+idx]==0): - raise IndexError("It is not possible to get a primitive using the given idx.") + if (exp[n+idx] == 0): + raise IndexError( + "It is not possible to get a primitive using the given idx.") primitive = list(deepcopy(exp)) primitive[n+idx] = 0 primitive[idx] += 1 @@ -270,7 +300,7 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): OUTPUT:: - A polynomial """ - if self.differential()!=0 : + if self.differential() != 0: raise ValueError("This polynomial is not closed.") w = self._leading_primitive() theta = self - w.differential() @@ -279,6 +309,7 @@ class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): else: return w+theta.primitive() + def AlgebraPL(n): """ The graded commutative differential cochain algebra Apl of dimension ``n`` @@ -288,24 +319,26 @@ def AlgebraPL(n): - ``n`` -- A non-negative integer corresponding to the number of generators of degree 0. """ - if (n<0): + if (n < 0): raise ValueError('`n` must be a non-negative integer') ts = ['t'+str(t) for t in range(n+1)] ys = ['y'+str(t) for t in range(n+1)] - degrees = tuple((n+1)*[(0,2)])+tuple((n+1)*[(1,2)]) + degrees = tuple((n+1)*[(0, 2)])+tuple((n+1)*[(1, 2)]) gca = GradedCommutativeAlgebra(QQ, names=ts+ys, degrees=degrees) - differentials = {gca.gens_dict()[ts[i]]:gca.gens_dict()[ys[i]] for i in range(n+1)} - return AplN(gca,differentials) + differentials = {gca.gens_dict()[ts[i]]: gca.gens_dict()[ + ys[i]] for i in range(n+1)} + return AplN(gca, differentials) ############################################# # TEST # print("Test AlgebraPL") #A = AlgebraPL(5) -#A.inject_variables() +# A.inject_variables() #p = t0**2 + t1 + y3 + y1*y2 #q = p.differential() ############################################# + class SimplicialAplN(AplN, UniqueRepresentation): """ @@ -328,7 +361,7 @@ class SimplicialAplN(AplN, UniqueRepresentation): Element class of the simplicial commutative differential algebra (Apl)n. """ @cached_method - def face_morphism(self,i): + def face_morphism(self, i): """" Returns the morphism face 'i' of self @@ -342,7 +375,7 @@ class SimplicialAplN(AplN, UniqueRepresentation): - A morphism of cochain algebras """ n = self.parent().n - if(n==0): + if(n == 0): return 0 domain = self.parent() codomain = SimplicialAlgebraplN(n-1) @@ -350,19 +383,19 @@ class SimplicialAplN(AplN, UniqueRepresentation): yImages = [] n = len(domain.gens())//2 for k in range(n): - if(ki): + elif(k > i): tImages.append(codomain.gens()[k-1]) yImages.append(codomain.gens()[n+k-2]) images = tImages+yImages - return domain.hom(codomain=codomain,im_gens=images) + return domain.hom(codomain=codomain, im_gens=images) - def face(self,i): + def face(self, i): """" Returns the face 'i' of self @@ -379,7 +412,7 @@ class SimplicialAplN(AplN, UniqueRepresentation): return self.face_morphism(i)(self) @cached_method - def degeneration_morphism(self,j): + def degeneration_morphism(self, j): """" Returns the morphism degeneration 'j' of self @@ -394,10 +427,11 @@ class SimplicialAplN(AplN, UniqueRepresentation): n = self.parent().n domain = self.parent() codomain = SimplicialAlgebraplN(n+1) - if(j>n): - print("the degeneration index must be a non-negative number lower or equal to "+str(n)) + if(j > n): + print( + "the degeneration index must be a non-negative number lower or equal to "+str(n)) return False - elif(j<0): + elif(j < 0): print("the degeneration index must be non-negative") return False else: @@ -405,19 +439,20 @@ class SimplicialAplN(AplN, UniqueRepresentation): yImages = [] n = len(domain.gens())//2 for k in range(n): - if(kj): + yImages.append(codomain.gens()[ + n+1+k]+codomain.gens()[n+1+k+1]) + elif (k > j): tImages.append(codomain.gens()[k+1]) yImages.append(codomain.gens()[n+1+k+1]) images = tImages+yImages - return domain.hom(codomain=codomain,im_gens=images) + return domain.hom(codomain=codomain, im_gens=images) - def degeneration(self,j): + def degeneration(self, j): """" Returns the degeneration 'j' of self @@ -444,8 +479,9 @@ class SimplicialAplN(AplN, UniqueRepresentation): t1^2*t2*y0*y1 + t2^3*y0*y1 - t1^3*y0*y2 - t1*t2^2*y0*y2 + t0*t1^2*y1*y2 + t0*t2^2*y1*y2 """ - if not (self.is_homogeneous() and self.total_degree()==self.parent().n): - raise ValueError("The polynomial is not homogeneous of maximum degree.") + if not (self.is_homogeneous() and self.total_degree() == self.parent().n): + raise ValueError( + "The polynomial is not homogeneous of maximum degree.") self_dim = self.parent().n indexes = list(range(self_dim+1)) apl = SimplicialAlgebraplN(dest_dim) @@ -457,13 +493,15 @@ class SimplicialAplN(AplN, UniqueRepresentation): y_prod = deepcopy(y_gens) del y_prod[i] y_prod = prod(y_prod) - graded_part += ((-1)**i)*t*y_prod#((-1)**(simplex_dim+i))*t*y_prod + # ((-1)**(simplex_dim+i))*t*y_prod + graded_part += ((-1)**i)*t*y_prod terms = self.dict() coeff_in_t = apl(0) for exp in terms.keys(): coef = terms[exp] ts_exp = exp[:self_dim+1] - coeff_in_t += coef*prod(list(map(lambda t, e: t**e, t_gens, ts_exp))) + coeff_in_t += coef * \ + prod(list(map(lambda t, e: t**e, t_gens, ts_exp))) return coeff_in_t*graded_part def _get_primitive_basis_of_a_monomial(self, monomial): @@ -519,9 +557,10 @@ class SimplicialAplN(AplN, UniqueRepresentation): differential_basis = [] for b in primitive_basis: diff = b.differential().reduce() - if diff: # as we want a basis, we don't consider the coeffient of the differential + if diff: # as we want a basis, we don't consider the coeffient of the differential for monomial in diff.dict().keys(): - term = self._get_term(1, monomial, self.parent().gens()) + term = self._get_term( + 1, monomial, self.parent().gens()) differential_basis.append(term) differential_basis = list(set(differential_basis)) differential_basis.sort(reverse=False) @@ -536,10 +575,10 @@ class SimplicialAplN(AplN, UniqueRepresentation): #dic = p.dict() #terms = [p_reduced.parent()({t: dic[t]}) for t in dic.keys()] monomials = list(p_reduced.dict().keys()) - v = vector(QQ,len(diff_basis)*[0]) + v = vector(QQ, len(diff_basis)*[0]) for i in range(len(diff_basis)): t = diff_basis[i] - term = list(t.dict().keys())[0] # there is only one monomial + term = list(t.dict().keys())[0] # there is only one monomial if term in monomials: v[i] = p_reduced.dict()[term] return v @@ -560,9 +599,10 @@ class SimplicialAplN(AplN, UniqueRepresentation): monomials = list(diff_primitive.dict().keys()) for i in range(nrows): diff_basis_element = differential_basis[i] - term = list(diff_basis_element.dict().keys())[0] # there is only one monomial + term = list(diff_basis_element.dict().keys())[ + 0] # there is only one monomial if term in monomials: - M[i,j] = diff_primitive.dict()[term] + M[i, j] = diff_primitive.dict()[term] return M def solve_primitive(self): @@ -572,7 +612,8 @@ class SimplicialAplN(AplN, UniqueRepresentation): v = p._get_coordinate_vector_differential(diff_basis) Mdif = p._differential_matrix(primitive_basis, diff_basis) sol = Mdif.solve_right(v) - return sum(map(lambda x,y: x*y, sol, primitive_basis)) + return sum(map(lambda x, y: x*y, sol, primitive_basis)) + def SimplicialAlgebraplN(n): """ @@ -584,14 +625,15 @@ def SimplicialAlgebraplN(n): - ``n`` -- A non-negative integer corresponding to the number of generators of degree 0. """ - if (n<0): + if (n < 0): raise ValueError('`n` must be a non-negative integer') ts = ['t'+str(t) for t in range(n+1)] ys = ['y'+str(t) for t in range(n+1)] - degrees = tuple((n+1)*[(0,2)])+tuple((n+1)*[(1,2)]) + degrees = tuple((n+1)*[(0, 2)])+tuple((n+1)*[(1, 2)]) gca = GradedCommutativeAlgebra(QQ, names=ts+ys, degrees=degrees) - differentials = {gca.gens_dict()[ts[i]]:gca.gens_dict()[ys[i]] for i in range(n+1)} - return SimplicialAplN(gca,differentials) + differentials = {gca.gens_dict()[ts[i]]: gca.gens_dict()[ + ys[i]] for i in range(n+1)} + return SimplicialAplN(gca, differentials) ############################################# @@ -623,7 +665,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ - def __init__(self,simplicial_complex, category=None): + def __init__(self, simplicial_complex, category=None): """ The constructor of Apl(K) @@ -633,7 +675,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ self.K = simplicial_complex self.n = simplicial_complex.dimension() - Parent.__init__(self,category=category) + Parent.__init__(self, category=category) f = MorphismFromQQToAplK(self) self.register_coercion(f) @@ -643,11 +685,11 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ return "The graded commutative differential cochain algebra Apl(K) of %s" % self.K - def _element_constructor_(self,values): + def _element_constructor_(self, values): """ The constructor of an element of this class """ - return self.element_class(self,values) + return self.element_class(self, values) def dimension(self): """ @@ -661,7 +703,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ return self.K - def _n_cells_sorted(self,n): + def _n_cells_sorted(self, n): """ Returns all simplices of dimension 'n', as a sorted list. """ @@ -681,9 +723,9 @@ class AlgebraAplK(UniqueRepresentation, Parent): for i in range(self.K.dimension()+1): s = SimplicialAlgebraplN(i) for f in self.K._n_cells_sorted(i): - values[tuple(f)]=s(0) + values[tuple(f)] = s(0) return self._element_constructor_(values) - + def one(self): """ Returns the one element of the algebra @@ -692,9 +734,9 @@ class AlgebraAplK(UniqueRepresentation, Parent): for i in range(self.K.dimension()+1): s = SimplicialAlgebraplN(i) for f in self.K._n_cells_sorted(i): - values[tuple(f)]=s(1) + values[tuple(f)] = s(1) return self._element_constructor_(values) - + null_element = zero def lift_cochain(self, cochain): @@ -709,19 +751,22 @@ class AlgebraAplK(UniqueRepresentation, Parent): if dim > max_dim: max_dim = dim apl = SimplicialAlgebraplN(dim) - if dim>0: - values[k] = ((-1) ** ((dim * (dim-1)) // 2)) * apl.integrand(cochain[k]) + if dim > 0: + values[k] = ((-1) ** ((dim * (dim-1)) // 2)) * \ + apl.integrand(cochain[k]) else: - values[k]= apl(cochain[k]) + values[k] = apl(cochain[k]) # second, we propagate the simplicial information simplicial_structure = {} for n in reversed(range(max_dim+1)): - keys = [k for k in values.keys() if len(k) == n+1] # keys of the n-simplices + keys = [k for k in values.keys() if len( + k) == n+1] # keys of the n-simplices for k in keys: - simplicial_structure[k] = values[k] + self._propagate(cochain, k) - #print(simplicial_structure) - return AlgebraAplK.Element(self,simplicial_structure) + simplicial_structure[k] = values[k] + \ + self._propagate(cochain, k) + # print(simplicial_structure) + return AlgebraAplK.Element(self, simplicial_structure) def _propagate(self, cochain, simplex): """ @@ -734,16 +779,19 @@ class AlgebraAplK(UniqueRepresentation, Parent): for n in reversed(range(dim)): faces = self._faces_of_length(simplex, n+1) apl = SimplicialAlgebraplN(n) - pol = apl(prod(apl.gens()[n+2:])) # we propagate the polynomial (y_1...y_n) + # we propagate the polynomial (y_1...y_n) + pol = apl(prod(apl.gens()[n+2:])) for f in faces: if f in cochain.keys(): value = cochain[f] face_indexes = [simplex.index(i) for i in f] #print("dim: ",n," pol: ",pol," face_idx: ",face_indexes," dim: ", n) - propagation += value*((-1) ** ((n * (n-1)) // 2)) * factorial(n) * pol.propagate(face_indexes, dim) + propagation += value * \ + ((-1) ** ((n * (n-1)) // 2)) * factorial(n) * \ + pol.propagate(face_indexes, dim) return propagation - def cohomology_gens(self,degree): + def cohomology_gens(self, degree): """ Returns the cohomolosy generators of a given degree """ @@ -757,7 +805,8 @@ class AlgebraAplK(UniqueRepresentation, Parent): def cohomology_group(self, n): chainComplex = self.K.chain_complex(base_ring=QQ) cochainComplex = chainComplex.dual() - Hn = cochainComplex.differential(n).right_kernel()/cochainComplex.differential(n-1).column_space() + Hn = cochainComplex.differential(n).right_kernel( + )/cochainComplex.differential(n-1).column_space() return Hn def cochain_to_vector(self, cochain, dim): @@ -765,8 +814,8 @@ class AlgebraAplK(UniqueRepresentation, Parent): Converts a cochain, given as a dictionary, to a vector """ basis = self.K.n_chains(dim, base_ring=QQ, cochains=True).basis() - v = vector(QQ,len(basis)*[0]) - i=0 + v = vector(QQ, len(basis)*[0]) + i = 0 for simplex in basis.keys(): v[i] = cochain[tuple(simplex)] i = i+1 @@ -779,11 +828,11 @@ class AlgebraAplK(UniqueRepresentation, Parent): cochain = {} for d in range(self.K.dimension()+1): basis = self.K.n_chains(d, base_ring=QQ, cochains=True).basis() - if d!=dim: + if d != dim: for simplex in basis.keys(): cochain[tuple(simplex)] = 0 else: - i=0 + i = 0 for simplex in basis.keys(): cochain[tuple(simplex)] = v[i] i = i+1 @@ -818,11 +867,11 @@ class AlgebraAplK(UniqueRepresentation, Parent): for simplex in self.K._n_cells_sorted(n): cochain[simplex.tuple()] = 0 for i in cycle: - #print(type(i[0].tuple())) + # print(type(i[0].tuple())) cochain[i[0].tuple()] = i[1] return cochain - def get_cohomology_aplk(self,degree): + def get_cohomology_aplk(self, degree): """ Returns a set of representative elements of Apl(K), one for each cohomology generator of a given degree """ @@ -842,7 +891,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): indexes = list(basis.keys()) indexes.sort(reverse=False) # print(indexes) - for n in range(1,len(indexes)): + for n in range(1, len(indexes)): if indexes[n][0] > 0: return indexes[n][0] return -1 @@ -851,17 +900,18 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ Element class of the simplicial commutative differential algebra (Apl)n. """ + def __init__(self, parent, values): - Element.__init__(self,parent) + Element.__init__(self, parent) self.elements = self._reduce(values) - #self.check() + # self.check() def _repr_(self): """ Return the base string representation of ``self``. """ - return str({a:b for a,b in self.elements.items() if not b.is_zero()}) - #return str(self.elements) + return str({a: b for a, b in self.elements.items() if not b.is_zero()}) + # return str(self.elements) def dimension(self): """ @@ -869,42 +919,41 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ return self.parent().dimension() - - def _add_(self,other): + def _add_(self, other): """ Addition operation """ C = self.__class__ - if(self.elements.keys()!=other.elements.keys()): + if(self.elements.keys() != other.elements.keys()): raise ValueError("error: keys do not match") newValues = {} for k in self.elements.keys(): newValues[k] = self.elements[k]+other.elements[k] - return C(self.parent(),newValues) + return C(self.parent(), newValues) - def _sub_(self,other): + def _sub_(self, other): """ Substration operation """ C = self.__class__ - if(self.elements.keys()!=other.elements.keys()): + if(self.elements.keys() != other.elements.keys()): raise ValueError("error: keys do not match") newValues = {} for k in self.elements.keys(): newValues[k] = self.elements[k]-other.elements[k] - return C(self.parent(),newValues) + return C(self.parent(), newValues) - def _mul_(self,other): + def _mul_(self, other): """ Multiplication operation """ C = self.__class__ - if(self.elements.keys()!=other.elements.keys()): + if(self.elements.keys() != other.elements.keys()): raise ValueError("error: keys do not match") newValues = {} for k in self.elements.keys(): newValues[k] = self.elements[k]*other.elements[k] - return C(self.parent(),newValues) + return C(self.parent(), newValues) def __pow__(self, n): """ @@ -914,24 +963,24 @@ class AlgebraAplK(UniqueRepresentation, Parent): newValues = {} for k in self.elements.keys(): newValues[k] = self.elements[k]**n - return C(self.parent(),newValues) - + return C(self.parent(), newValues) def _richcmp_(self, other, op): """ Comparison operations. Only implemented equality operations (EQ and NE) """ - if(op==op_EQ): - if(self.elements.keys()!=other.elements.keys()): + if(op == op_EQ): + if(self.elements.keys() != other.elements.keys()): return False for k in self.elements.keys(): - if(self.elements[k]!=other.elements[k]): + if(self.elements[k] != other.elements[k]): return False return True - elif(op==op_NE): - return not self._richcmp_(other,op_EQ) + elif(op == op_NE): + return not self._richcmp_(other, op_EQ) else: - raise NotImplementedError("operation not implemented for this element class") + raise NotImplementedError( + "operation not implemented for this element class") def _reduce(self, values): """ @@ -954,11 +1003,11 @@ class AlgebraAplK(UniqueRepresentation, Parent): """ S = self.parent().simplicialComplex() dim = S.dimension() - for n in range(1,dim+1): + for n in range(1, dim+1): #print("Checking dimension " + str(n)) for simplex in S._n_cells_sorted(n): for f in range(n+1): - sFace = tuple(S.face(simplex,f)) + sFace = tuple(S.face(simplex, f)) aFace = self.elements[simplex.tuple()].face(f).reduce() #print("face: " + str(f),"simplex: " + str(simplex),"sFace: " + str(self.elements[sFace]), "aFace: " + str(aFace)) if(self.elements[sFace] != aFace): @@ -972,7 +1021,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): newValues = {} for k in self.elements.keys(): newValues[k] = self.elements[k].differential() - return C(self.parent(),newValues) + return C(self.parent(), newValues) def is_closed(self): """ @@ -984,7 +1033,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): else: return False - def homogeneous_part(self,degree): + def homogeneous_part(self, degree): """ Returns the homogeneous component of grade 'degree' of 'self' """ @@ -993,7 +1042,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): for k in self.elements.keys(): s = SimplicialAlgebraplN(len(k)-1) part[k] = s(self.elements[k].homogeneous_part(degree=degree)) - return C(self.parent(),part) + return C(self.parent(), part) def homogeneous_parts(self): """ @@ -1040,11 +1089,12 @@ class AlgebraAplK(UniqueRepresentation, Parent): Returns the cochain obtained as integration of 'self' """ cochain = {} - #integration of each polynomial + # integration of each polynomial for k in self.elements.keys(): dim = len(k)-1 # the sign is due to stoke's theorem - cochain[k] = ((-1) ** ((dim * (dim-1)) // 2)) * self.elements[k].integral() + cochain[k] = ((-1) ** ((dim * (dim-1)) // 2)) * \ + self.elements[k].integral() return cochain def _face_basis(self, basis, face_idx): @@ -1053,8 +1103,9 @@ class AlgebraAplK(UniqueRepresentation, Parent): face_pol = b.face(face_idx).reduce() if face_pol: for monomial in face_pol.dict().keys(): - #remove coefficients - term = face_pol._get_term(1, monomial, face_pol.parent().gens()) + # remove coefficients + term = face_pol._get_term( + 1, monomial, face_pol.parent().gens()) face_basis.append(term) face_basis = list(set(face_basis)) face_basis.sort(reverse=False) @@ -1066,22 +1117,26 @@ class AlgebraAplK(UniqueRepresentation, Parent): S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() - for n in reversed(range(min_dim,dim+1)): + for n in reversed(range(min_dim, dim+1)): for simplex in S._n_cells_sorted(n): if simplex.tuple() not in primitive_basis.keys(): primitive_basis[simplex.tuple()] = [] # add primitive elements related to the assigned polynomial pol = self.elements[simplex.tuple()] - primitive_basis[simplex.tuple()] += pol._get_primitive_basis() - primitive_basis[simplex.tuple()] = list(set(primitive_basis[simplex.tuple()])) + primitive_basis[simplex.tuple( + )] += pol._get_primitive_basis() + primitive_basis[simplex.tuple()] = list( + set(primitive_basis[simplex.tuple()])) primitive_basis[simplex.tuple()].sort(reverse=False) # propagate the primitive basis through its faces for f in range(n+1): - simplex_face = S.face(simplex,f).tuple() + simplex_face = S.face(simplex, f).tuple() if simplex_face not in primitive_basis.keys(): primitive_basis[simplex_face] = [] - primitive_basis[simplex_face] += self._face_basis(primitive_basis[simplex.tuple()], f) - primitive_basis[simplex_face] = list(set(primitive_basis[simplex_face])) + primitive_basis[simplex_face] += self._face_basis( + primitive_basis[simplex.tuple()], f) + primitive_basis[simplex_face] = list( + set(primitive_basis[simplex_face])) primitive_basis[simplex_face].sort(reverse=False) return primitive_basis @@ -1091,8 +1146,9 @@ class AlgebraAplK(UniqueRepresentation, Parent): b_diff = b.differential().reduce() if b_diff: for monomial in b_diff.dict().keys(): - #remove coefficients - term = b_diff._get_term(1, monomial, b_diff.parent().gens()) + # remove coefficients + term = b_diff._get_term( + 1, monomial, b_diff.parent().gens()) diff_basis.append(term) diff_basis = list(set(diff_basis)) diff_basis.sort(reverse=False) @@ -1103,10 +1159,11 @@ class AlgebraAplK(UniqueRepresentation, Parent): S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() - for n in reversed(range(min_dim,dim+1)): + for n in reversed(range(min_dim, dim+1)): for simplex in S._n_cells_sorted(n): prim_diff_basis = primitive_basis[simplex.tuple()] - differential_basis[simplex.tuple()] = self._diff_basis(prim_diff_basis) + differential_basis[simplex.tuple()] = self._diff_basis( + prim_diff_basis) return differential_basis def _get_differential_matrix(self, p_basis, d_basis): @@ -1118,9 +1175,9 @@ class AlgebraAplK(UniqueRepresentation, Parent): M = zero_matrix(QQ, nrows, ncols) row_offset = 0 col_index = 0 - for n in reversed(range(min_dim-1,dim+1)): + for n in reversed(range(min_dim-1, dim+1)): for simplex in S._n_cells_sorted(n): -# print(simplex.tuple(), row_offset, col_index) + # print(simplex.tuple(), row_offset, col_index) if simplex.tuple() in p_basis.keys(): primitives = p_basis[simplex.tuple()] if simplex.tuple() in d_basis.keys(): @@ -1130,10 +1187,13 @@ class AlgebraAplK(UniqueRepresentation, Parent): dif_p = p.differential().reduce() dic = dif_p.dict() for monomial in dic.keys(): - term = dif_p._get_term(1, monomial, dif_p.parent().gens()) + term = dif_p._get_term( + 1, monomial, dif_p.parent().gens()) if term in differentials: - row_index = differentials.index(term) - M[row_offset + row_index, col_index] = dic[monomial] + row_index = differentials.index( + term) + M[row_offset + row_index, + col_index] = dic[monomial] col_index += 1 row_offset += len(differentials) elif primitives: @@ -1150,7 +1210,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() - for n in reversed(range(min_dim,dim+1)): + for n in reversed(range(min_dim, dim+1)): for s in S._n_cells_sorted(n): simplex = s.tuple() if simplex in d_basis.keys(): @@ -1159,7 +1219,8 @@ class AlgebraAplK(UniqueRepresentation, Parent): if pol and differentials: dic = pol.dict() for monomial in dic.keys(): - term = pol._get_term(1, monomial, pol.parent().gens()) + term = pol._get_term( + 1, monomial, pol.parent().gens()) if term in differentials: v_index = differentials.index(term) v[offset + v_index] = dic[monomial] @@ -1173,7 +1234,7 @@ class AlgebraAplK(UniqueRepresentation, Parent): min_dim = self.degree() offset = 0 first_simplex = True - for n in reversed(range(min_dim-1,dim+1)): + for n in reversed(range(min_dim-1, dim+1)): for simplex in S._n_cells_sorted(n): if simplex.tuple() in p_basis.keys(): primitives = p_basis[simplex.tuple()] @@ -1187,43 +1248,43 @@ class AlgebraAplK(UniqueRepresentation, Parent): def _get_row_face_matrix(self, primitives, face_idx, face_basis, offset_simplex, offset_face, len_row): - M = zero_matrix(QQ,1, len_row) + M = zero_matrix(QQ, 1, len_row) for fb in face_basis: fb_idx = face_basis.index(fb) - row = zero_matrix(QQ,1, len_row) - row[0,offset_face+fb_idx] = -1 + row = zero_matrix(QQ, 1, len_row) + row[0, offset_face+fb_idx] = -1 for term in primitives: face_term = term.face(face_idx).reduce() dic = face_term.dict() i = primitives.index(term) for monomial in dic: - t = face_term._get_term(1, monomial, face_term.parent().gens()) + t = face_term._get_term( + 1, monomial, face_term.parent().gens()) if t == fb: coef = dic[monomial] - row[0,offset_simplex+i] = coef + row[0, offset_simplex+i] = coef M = M.stack(row) return M[1:] - def _get_face_matrix(self, p_basis): offsets = self._get_offsets_matrix(p_basis) len_row = sum([len(l) for l in p_basis.values()]) - M = zero_matrix(QQ,1, len_row) + M = zero_matrix(QQ, 1, len_row) S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() - for n in reversed(range(min_dim,dim+1)): + for n in reversed(range(min_dim, dim+1)): for simplex in S._n_cells_sorted(n): offset_simplex = offsets[simplex.tuple()] primitives = p_basis[simplex.tuple()] for f in range(n+1): - simplex_face = S.face(simplex,f).tuple() + simplex_face = S.face(simplex, f).tuple() face_primitives = p_basis[simplex_face] offset_face = offsets[simplex_face] rows = self._get_row_face_matrix(primitives, f, face_primitives, - offset_simplex, offset_face, - len_row) + offset_simplex, offset_face, + len_row) M = M.stack(rows) return M[1:] @@ -1253,14 +1314,14 @@ class AlgebraAplK(UniqueRepresentation, Parent): def solve_primitive(self): p_basis = self._get_primitive_basis() - d_basis = self._get_differential_basis(p_basis) + d_basis = self._get_differential_basis(p_basis) diff_matrix = self._get_differential_matrix(p_basis, d_basis) v_dif = self._get_vector_diff_coordinates(d_basis) face_matrix = self._get_face_matrix(p_basis) nrows = face_matrix.nrows() v = vector(QQ, list(v_dif)+nrows*[0]) M = diff_matrix.stack(face_matrix) - print("Matrix size: ",M.nrows(),M.ncols()) + print("Matrix size: ", M.nrows(), M.ncols()) sol = M.solve_right(v) return self._get_solution(sol, p_basis) @@ -1277,6 +1338,6 @@ class MorphismFromQQToAplK(Morphism): for d in range(S.dimension()+1): s = SimplicialAlgebraplN(d) for c in S._n_cells_sorted(d): - params[tuple(c)]=s(x) + params[tuple(c)] = s(x) A = AlgebraAplK(S) return AlgebraAplK.Element(A, params) diff --git a/minimalModel.py b/minimalModel.py index 9f77db3..f406187 100644 --- a/minimalModel.py +++ b/minimalModel.py @@ -1,8 +1,35 @@ +r""" + + + + +EXAMPLES:: + + + +AUTHORS: + +- Carlos Alquézar Baeta (2023-02-07): initial version +- Miguel Ángel Marco (2023-02-07): initial version +- Jorge Martín Morales (2023-02-07): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2023 Carlos Alquézar Baeta +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + def phi_polinomial(pol, model_morphism, A): #pol_keys = pol.dict().keys() m_gens = pol.parent().gens() #m_gens = [g for g,e in zip(gens,sum([vector(k) for k in pol_keys])) if e] - aplkGens = {g:model_morphism[g.degree()][str(g)] for g in m_gens} + aplkGens = {g: 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] @@ -12,11 +39,12 @@ def phi_polinomial(pol, model_morphism, A): e = exp[i] if e: aGen = aplkGens[m_gens[i]] - term *= aGen**e + term *= aGen**e #term = coef*prod([gen^e for (gen,e) in zip(aplkGens, exp)]) aplkElement += term return aplkElement + def get_ker_elements(sullivan_model, model_morphism, A, dim): # Get the basis of Hm hm_basis = sullivan_model.cohomology(dim).basis() @@ -25,34 +53,38 @@ def get_ker_elements(sullivan_model, model_morphism, A, dim): apl_correspondence = {} cochain_correspondence = {} Hc_correspondence = {} - M = zero_matrix(QQ,len(Hc.basis()),len(hm_basis)) + 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) + 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) + vcochain = A.cochain_to_vector(cochain, dim) Hc_correspondence[gen_repr] = Hc(vcochain) - M[:,i] = Hc_correspondence[gen_repr] - i+=1 + 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] + 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, + + +def injectivity_step(M, phi, check_dim, names, 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] @@ -62,15 +94,17 @@ def injectivity_step(M, phi, check_dim, names, new_degrees = degrees new_differentials = differentials m_kernel = get_ker_elements(M, phi, A, check_dim) - if len(m_kernel)>0: + 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(" 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)+[check_dim-1]) - gca = GradedCommutativeAlgebra(QQ, names=new_names, degrees=new_degrees) + gca = GradedCommutativeAlgebra( + QQ, names=new_names, degrees=new_degrees) gen = gca.gens_dict()[new_gen] aplkElement = phi_polinomial(kerElement, phi, A) if aplkElement == A.null_element(): @@ -78,16 +112,20 @@ def injectivity_step(M, phi, check_dim, names, 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 = {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()} + 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, A, index=index+1, depth=depth+1, max_depth=max_depth) + return injectivity_step(new_M, new_phi, check_dim, new_names, + 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()) @@ -95,35 +133,40 @@ def get_unmapped_generators(dim, coho_aplk, coho_gens, correspondence): for gen_key in coho_gens.keys(): gen = coho_gens[gen_key] found = False - for i in range(1,dim): + for i in range(1, dim): if (not found) and (i in dimensions): - for j in range(i,dim): + 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) + 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)] + 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") + 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) + g = get_unmapped_generators( + dim, coho_aplk, cohomology_gens, 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_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 = {} @@ -136,13 +179,14 @@ def surjective_step(A, M, phi, dim, names, degrees, differentials, cohomology_co 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 = gca.cdg_algebra(differentials) + new_M = gca.cdg_algebra(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") @@ -154,9 +198,9 @@ def minimal_model(S, max_depth=5, max_dim=5): phi_n_map = {} correspondence_n_map = {} if first_dim < 0: - return [0,{},{}] # due to the function returns multiple objects + return [0, {}, {}] # due to the function returns multiple objects elif first_dim > max_dim: - return [0,{},{}] # due to the function returns multiple objects + return [0, {}, {}] # due to the function returns multiple objects g = A.get_cohomology_aplk(first_dim) cohomology_gens = A.cohomology_gens(first_dim) names = [] @@ -169,29 +213,31 @@ def minimal_model(S, max_depth=5, max_dim=5): degrees += [deg] phi_n_map[new_var] = g[gen_key] correspondence_n_map[new_var] = cohomology_gens[gen_key] - differentials={} + 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, A, index=0, depth=0, max_depth=5) + [M, phi, names, degrees, differentials, limit] = injectivity_step(M, phi, first_dim+1, names, + 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) + 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): print("phi") print(phi) - [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, A, index=0, depth=0, max_depth=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, A, index=0, depth=0, max_depth=5) if limit: - print("The algorithm has reach the injectivity depth-limit at dimension", dim) - break - + print( + "The algorithm has reach the injectivity depth-limit at dimension", dim) + break + return [M, phi, cohomology_correspondence]