diff --git a/aplK.py b/aplK.py index 0451b68..4dbf70d 100644 --- a/aplK.py +++ b/aplK.py @@ -1,393 +1,812 @@ -r""" - - - - -EXAMPLES:: - - - -AUTHORS: - -- YOUR NAME (2005-01-03): initial version - -- person (date in ISO year-month-day format): short desc - -""" - -# **************************************************************************** -# Copyright (C) 2021 YOUR NAME -# -# 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 -from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.rings import Rings from sage.structure.richcmp import rich_to_bool, op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE -from sage.categories.morphism import Morphism -from sage.rings.rational_field import QQ -from sage.algebras.commutative_dga import GradedCommutativeAlgebra -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.functions.other import factorial +import copy -##################################################################### -class Apln(UniqueRepresentation): - - def __init__(self,n): - self.n = n - 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)]) - self.gca = GradedCommutativeAlgebra(QQ, names=ts+ys, degrees=degrees) - t_relation = -1 - y_relation = 0 - #t_relation = sum(self.gca.gens()[:n])-1 - #y_relation = sum(self.gca.gens()[n:]) - for i in range(n+1): - t_relation += self.gca.gens()[i] - y_relation += self.gca.gens()[n+1+i] - self.gca_with_relations = self.gca.quotient(self.gca.ideal(t_relation,y_relation),check=False) - dfs = {self.gca.gens_dict()[ts[i]]:self.gca.gens_dict()[ys[i]] for i in range(n+1)} - self.cdga = self.gca.cdg_algebra(dfs) - #self.inject_variables() - - def _repr_(self): - return "Algebra Apln of dimension %s" % self.n - - def inject_variables(self): - self.cdga.inject_variables() +class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): + """ + A differential multi-graded commutative algebra - def generators(self): - return self.cdga.gens() - - def reduce_with_relations(self,pol): - return self.gca_with_relations(pol) - - def homogeneousPart(self,pol,degree): + This class extends the DifferentialGCAlgebra_multigraded class + to create the algebra of polynomial differential forms with coefficients in QQ - parts = [pol.homogeneous_parts()[k] for k in pol.homogeneous_parts() if list(k)[0]==degree] - hompart = 0 - for p in parts: - hompart += p - return hompart + INPUT: - def face(self,i): + - ``A`` -- a multi-graded commutative algebra + - ``differential`` -- the differential rules - if(self.n==0): - return 0 - domain = self.cdga - codomain = self.cdga - tImages = [] - yImages = [] - n = len(domain.gens())//2 - for k in range(n): - if(ki): - tImages.append(codomain.gens()[k-1]) - yImages.append(codomain.gens()[n+k-1]) - images = tImages+yImages - return domain.hom(codomain=codomain,im_gens=images) - - def _reduce_polynomial(self,p,n): + """ - ## Input: - # - p: polynomial in variables t_0,..t_n,y_0,...,y_n - # - n: dimension of the algebra (apl)_n where p is defined - ## Output: - # - The polynomial after applying the corresponding relations to reduce the variables t_n, y_n - # - For practical reasons, we change the nomenclature in this way: t_i ==> xs_i, y_i ==> zs_i - - xs = ['x'+str(x) for x in range(n+1)] - zs = ['z'+str(z) for z in range(n+1)] - R = PolynomialRing(QQ,names=xs+zs) - r_gens = R.gens() - gca_gens = self.gca.gens() - pol = R(0) - for monomial_exponents in list(p.dict().keys()): - monomial = R(1) - coef = p.dict()[monomial_exponents] - for i in range(n+1): - monomial *= r_gens[i]**monomial_exponents[i] - monomial *= r_gens[n+1+i]**monomial_exponents[self.n+1+i] - pol += coef*monomial - xn_relation = 1-sum(r_gens[:n]) - zn_relation = -sum(r_gens[n+1:-1]) - pol = pol.subs({r_gens[n]:xn_relation,r_gens[2*n+1]:zn_relation}) - return pol - - def _convert_integrable_polynomial(self,p,n): - ## Input: - # - p: polynomial in variables t_0,..t_n,y_0,...,y_n - # - n: dimension of the algebra (apl)_n where p is defined - ## Output: - # - A polynomial in the ring Q. - # (After reducing the variables t_{n},y_{n} by the corresponding relations, - # the algorithm removes the variables y_{0},..,y_{n-1}). + def __init__(self, A, differential): + """ + Initialize ``self``. + INPUT: - xs = ['x'+str(x) for x in range(n)] - zs = ['z'+str(z) for z in range(n)] - R = PolynomialRing(QQ,names=xs+zs) - pol = R(self._reduce_polynomial(p,n)) - r_gens = R.gens() - relations = {} - for i in range(n): - relations[r_gens[n+i]]=1 - return pol.subs(relations) + - ``A`` -- a multi-graded commutative algebra + - ``differential`` -- a differential + """ + + self.n = len(A._names)//2-1 + DifferentialGCAlgebra_multigraded.__init__(self, A, differential) + self.t_relation = sum(self.gens()[:self.n+1])-1 + self.y_relation = sum(self.gens()[self.n+1:]) + + def _base_repr(self): + """ + Return the base string representation of ``self``. + """ + + s = DifferentialGCAlgebra._base_repr(self) + old = '{}'.format(self._degrees) + new = '{}'.format([i[0] for i in self._degrees_multi]) + return s.replace(old, new) + + def integrand(self, value): + """ + Returns a polynomial whose definite integral is equal to 'value' + + OUTPUT: + - A polynomial + """ + if (value in QQ) or ((value in A) and (value.degree().is_zero())): + return value*factorial(self.n)*prod(self.gens()[self.n+2:]) + else: + raise ValueError("only field elements can be integrated.") - def integral(self,p,n): - ## Input: - # - p: polynomial in variables t_0,..t_n,y_0,...,y_n - # - n: dimension of the algebra (apl)_n where p is defined - ## Output: - # - the integral of the polynomial after reducing the variables t_n, y_n by the corresponding relations - # - - xs = ['x'+str(x) for x in range(n)] - R = PolynomialRing(QQ,names=xs) - r_gens = R.gens() - pol = R(self._convert_integrable_polynomial(p,n)) - result = pol - for i in range(n): - result = result.integral(r_gens[n-1-i]) - result = result.subs({r_gens[n-1-i]:1-sum(r_gens[:n-1-i])}) - return R(((-1)**(n*(n-1)/2)))*result + class Element(GCAlgebra_multigraded.Element, DifferentialGCAlgebra.Element): + """ + Element class of the commutative differential multi-graded algebra (Apl)n. + """ - def integral2(self,p,n): - ## Input: - # - p: polynomial in variables t_0,..t_n,y_0,...,y_n - # - n: dimension of the algebra (apl)_n where p is defined - ## Output: - # - the integral of the polynomial after reducing the variables t_n, y_n by the corresponding relations - # - - xs = ['x'+str(x) for x in range(n)] - R = PolynomialRing(QQ,names=xs) - r_gens = R.gens() - pol = R(self._convert_integrable_polynomial(p,n)) - result = 0 - for k in pol.dict().keys(): # k is a monomial - numerator = 1 - denominator = n - for e in k: # e is an exponents - numerator *= factorial(e) - denominator += e - coef = pol.dict()[k] - result += coef*numerator/factorial(denominator) - return R(((-1)**(n*(n-1)/2)))*result + def is_homogeneous(self): + """ + Return "True" if "self" is homogeneous and "False" otherwise. + """ -##################################################################### + degrees = set([k[0] for k in self.homogeneous_parts()]) + return len(degrees) == 1 + + def total_degree(self): + """ + Return the total degree of this element. + """ + + degrees = set([k[0] for k in self.homogeneous_parts()]) + return max(degrees) + + def homogeneous_part(self,degree): + """ + Returns the homogeneous part of the element for a given degree. + """ + 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] + return self.parent()(sum(parts)) + + def reduce(self, relations=[]): + """" + The method reduces the polynomial by the Ideal + + INPUT:: + - p: polynomial in variables t_0,..t_n,y_0,...,y_n + - relations (optional): if relations are given, the polynomial is reduced following + this rules. If no relations are given, the polynomial is reduced taking into + account the relations of the parent algebra. + + OUTPUT:: + - The polynomial after applying the corresponding relations + """ + apl = self.parent() + if relations: + return DifferentialGCAlgebra.Element.reduce(self, relations) + else: + 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` + + OUTPUT: + - A polynomial in the ring Q. + (After reducing the variables t_{0},y_{0} by the corresponding relations, + the algorithm removes the variables y_{1},..,y_{n}). + """ + + apl = self.parent() + pol = apl(self.reduce()) #needed in case reduce() returns an integer + relations = [] + for i in range(apl.n+1): + relations.append(apl.gens()[apl.n+1+i]-1) + pol = apl(pol.homogeneous_part(degree=apl.n)) + return apl(pol.reduce(relations=relations)) + + def integral(self): + """ + Returns the definite integral of the term of degree `n` + + OUTPUT: + - The rational number obtained by the definite integral + of the term of degree `n` + + """ + + apl = self.parent() + pol = self._integrable_part() + result = 0 + for k in pol.dict().keys(): # k is a monomial + numerator = 1 + denominator = apl.n + 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 result + + def _get_term(self, coef, exp, gens): + """" + + Returns the term specified by its coefficient, exponents and generators + + INPUT:: + - coef: The coefficient of the term + - exp: A tuple with the exponents of each variable + - gens: The generators + + OUTPUT:: + - A polynomial + """ + return coef*prod(list(map(lambda exp, gen: gen**exp, exp, gens))) + + def _get_terms(self): + """" + + Returns self seen as a collection of terms in the variables y_j with coefficients in QQ + + OUTPUT:: + - A dictionary with all terms + + """ + a = self.parent() + n = len(a.gens())//2 + ts_only_gens = a.gens()[:n]+tuple(n*[1]) + ys_only_gens = tuple(n*[1])+a.gens()[n:] + 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) + if key not in terms.keys(): + terms[key] = coef + else: + terms[key] += coef + return terms + + def _get_leading_pol(self): + """" + + Returns the leading polynomial + + OUTPUT:: + - A polynomial + """ + terms = self._get_terms() + keys = list(terms.keys()) + keys.sort(reverse=True) + return terms[keys[0]]*keys[0] + + def _get_idx(self, exp): + """" + + Returns the idx 'i' of the first generator 'y_i' that appears in exp + + INPUT:: + - exp: A tuple with the exponents + + OUTPUT:: + - A non-negative number lower or equal to 'n' + """ + n = len(exp)//2 + filter_ts = n*[0]+n*[1] + diff_forms = list(map(lambda v, f: v*f, exp, filter_ts)) + return next((i for i, x in enumerate(list(diff_forms)) if x), None)-n + + def _monomial_primitive(self, monomial, idx): + """" + + Returns the primitive of the given monomial + + INPUT:: + - monomial: A monomial + - idx: the index of the variable y_j that will be used to calculate the pseudo-primitive + + OUTPUT:: + - A polynomial + """ + 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.") + primitive = list(copy.deepcopy(exp)) + primitive[n+idx] = 0 + primitive[idx] += 1 + new_coef = coef/primitive[idx] + a = monomial.parent() + return self._get_term(new_coef, primitive, a.gens()) + + def _leading_primitive(self): + """" + Returns the primitive of the leading term of self + + OUTPUT:: + - A polynomial + """ + polynomial = self._get_leading_pol() + # necessary condition: all monomials have apl.gens[n+idx] factor + exp = list(polynomial.dict().keys())[0] + idx = self._get_idx(exp) + aGens = polynomial.parent().gens() + primitive = 0 + for exp in polynomial.dict().keys(): + coef = polynomial.dict()[exp] + monomial = self._get_term(coef, exp, aGens) + primitive += self._monomial_primitive(monomial, idx) + terms = primitive._get_terms() + result = sum([terms[key]*key for key in terms.keys()]) + return result + + def primitive(self): + """" + Retuns a polynomial whose differential is self + + OUTPUT:: + - A polynomial + """ + if self.differential()!=0 : + raise ValueError("This polynomial is not closed.") + w = self._leading_primitive() + theta = self - w.differential() + if (theta == 0): + return w + else: + return w+theta.primitive() + +def AlgebraPL(n): + """ + The graded commutative differential cochain algebra Apl of dimension ``n`` + + INPUT: + + - ``n`` -- A non-negative integer corresponding to the number of generators of degree 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)]) + 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) + +############################################# +# TEST +# print("Test AlgebraPL") +#A = AlgebraPL(5) +#A.inject_variables() +#p = t0**2 + t1 + y3 + y1*y2 +#q = p.differential() +############################################# + +class SimplicialAplN(AplN, UniqueRepresentation): + """ + + The simplicial commutative cochain algebra Apl + + """ + + def _base_repr(self): + """ + Return the base string representation of ``self``. + """ + + s = DifferentialGCAlgebra._base_repr(self) + old = '{}'.format(self._degrees) + new = '{}'.format([i[0] for i in self._degrees_multi]) + return "Simplicial " + s.replace(old, new) + + class Element(AplN.Element): + """ + Element class of the simplicial commutative differential algebra (Apl)n. + """ + + def face_morphism(self,i): + """" + + Returns the morphism face 'i' of self + + INPUT:: + + - i: A non-negative integer lower or equal to 'n'. Represents the face index + + OUTPUT:: + + - A morphism of cochain algebras + """ + n = self.parent().n + if(n==0): + return 0 + domain = self.parent() + codomain = SimplicialAlgebraplN(n-1) + tImages = [] + yImages = [] + n = len(domain.gens())//2 + for k in range(n): + if(ki): + tImages.append(codomain.gens()[k-1]) + yImages.append(codomain.gens()[n+k-2]) + images = tImages+yImages + return domain.hom(codomain=codomain,im_gens=images) + + def face(self,i): + """" + + Returns the face 'i' of self + + INPUT:: + + - i: A non-negative integer lower or equal to 'n'. Represents the face index + + OUTPUT:: + + - A polynomial that is the face 'i' of self + """ + + return self.face_morphism(i)(self) + + def degeneration_morphism(self,j): + """" + + Returns the morphism degeneration 'j' of self + + INPUT:: + - j: A non-negative integer lower or equal to 'n'. Represents the degeneration index + + OUTPUT:: + - A morphism of cochain algebras + """ + + 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)) + return False + elif(j<0): + print("the degeneration index must be non-negative") + return False + else: + tImages = [] + yImages = [] + n = len(domain.gens())//2 + for k in range(n): + if(kj): + 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) + + def degeneration(self,j): + """" + + Returns the degeneration 'j' of self + + INPUT:: + - j: A non-negative integer lower or equal to 'n'. Represents the degeneration index + + OUTPUT:: + - A polynomial that is the degeneration 'j' of self + """ + return self.degeneration_morphism(j)(self) + + def propagate(self, face_list, dest_dim): + """ + Returns a polynomial in dimension 'dest_dim' that has 'self' as a face + + INPUT: + - face_list: indexes of the elements that form the face of the simplex we want to propagate + - dest_dim: Dimension of the codomain cochain algebra + - EXAMPLE: s = SimplicialAlgebraplN(2) + s.inject_variables() + p = (t1^2+t2^2)*y1*y2 + p.propagate2([0,1,2],3) + 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.") + self_dim = self.parent().n + indexes = list(range(self_dim+1)) + apl = SimplicialAlgebraplN(dest_dim) + y_gens = [apl.gens()[dest_dim+1+i] for i in face_list] + t_gens = [apl.gens()[i] for i in face_list] + graded_part = apl(0) + for i in reversed(indexes): + t = t_gens[i] + y_prod = copy.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 + 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))) + return coeff_in_t*graded_part + +def SimplicialAlgebraplN(n): + """ + Returns the graded commutative differential cochain algebra (Apl)_n as part of + the simplicial cochain algebra A_{PL} + + INPUT: + + - ``n`` -- A non-negative integer corresponding to the number of generators of degree 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)]) + 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) + + +############################################# +# TEST +#print("TEST SimplicialAlgebraN") +# s = SimplicialAlgebraplN(5) +# s.inject_variables() +# ## face and degeneration maps +# print("Test face and degeneration maps") +# p = t1+y2 +# p.degeneration(0).face(1)==p +# m1 = p.degeneration(1).face_morphism(1) +# m2 = p.degeneration_morphism(1) +# print((m1*m2).is_identity()) # true +# ## Propagation +# print("Test progatation") +# s = SimplicialAlgebraplN(2) +# s.inject_variables() +# p = (t1**2+t2**2)*y1*y2 +# q = p.propagate([1,5,7],10) +# print(q) +# print(q.face(10).face(9).face(8).face(6).face(4).face(3).face(2).face(0).reduce() == p) +############################################# + +class AlgebraAplK(UniqueRepresentation, Parent): + """ + + The graded commutative differential cochain algebra Apl(K) + + """ + + def __init__(self,simplicial_complex, category=None): + """ + The constructor of Apl(K) + + INPUT: + - simplicial_complex: The simplicial complex used to build the algebra + + """ + self.K = simplicial_complex + self.n = simplicial_complex.dimension() + Parent.__init__(self,category=category) + f = MorphismFromQQToAplK(self) + self.register_coercion(f) + + def _repr_(self): + """ + Return the base string representation of ``self``. + """ + return "The graded commutative differential cochain algebra Apl(K) of %s" % self.K + + def _element_constructor_(self,values): + """ + The constructor of an element of this class + """ + return self.element_class(self,values) + + def dimension(self): + """ + The dimension of the algebra. It is equivalent to the maximum degree possible that a non-null element of this algebra can take. + """ + return self.n + + def simplicialComplex(self): + """ + The simplicial complex of this algebra. + """ + return self.K + + def _n_cells_sorted(self,n): + """ + Returns all simplices of dimension 'n', as a sorted list. + """ + return self.K._n_cells_sorted(n) + + def _faces_of_length(self, simplex, length): + """ + Returns all faces of dimension 'length' for a given simplex + """ + return [tuple(e) for e in Set(simplex).subsets(length)] + + def null_element(self): + """ + Returns the null element of the algebra respect to the addition operation + """ + values = {} + 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) + return self._element_constructor_(values) + + class Element(Element): + """ + Element class of the simplicial commutative differential algebra (Apl)n. + """ + def __init__(self, parent, values): + Element.__init__(self,parent) + self.elements = self._reduce(values) + self.check() + + def _repr_(self): + """ + Return the base string representation of ``self``. + """ + return str(self.elements) + + def dimension(self): + """ + Returns the dimension where 'self' lives + """ + return self.parent().dimension() + + + def _add_(self,other): + """ + Addition operation + """ + C = self.__class__ + 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) + + def _sub_(self,other): + """ + Substration operation + """ + C = self.__class__ + 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) + + def _mul_(self,other): + """ + Multiplication operation + """ + C = self.__class__ + 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) + + def __pow__(self, n): + """ + Return ``self`` raised to the power ``n``. + """ + C = self.__class__ + newValues = {} + for k in self.elements.keys(): + newValues[k] = self.elements[k]**n + 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()): + return False + for k in self.elements.keys(): + if(self.elements[k]!=other.elements[k]): + return False + return True + elif(op==op_NE): + return not self._richcmp_(other,op_EQ) + else: + raise NotImplementedError("operation not implemented for this element class") + + def _reduce(self, values): + """ + Returns the polynomials given as input, after applying the corresponding relations + of the algebra where each polynomial belongs. + + INPUT: + - values: a dictionary where the values are polynomials in (Apl)n + """ + reduced_values = {} + for k in values.keys(): + n = len(k)-1 + reduced_values[k] = values[k].reduce() + return reduced_values + + def check(self): + """ + Checks if the initial assignation respects the simplicial structure. For every simplex S, the algorithm checks + if the polynomial assigned to each face of S is equal to the same face of the polynomial assigned to S. + """ + S = self.parent().simplicialComplex() + dim = S.dimension() + 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)) + 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): + raise ValueError("the assignation is not valid") + + def differential(self): + """ + Returns the differential of 'self' + """ + C = self.__class__ + newValues = {} + for k in self.elements.keys(): + newValues[k] = self.elements[k].differential() + return C(self.parent(),newValues) + + def is_closed(self): + """ + Returns 'True' if 'self' is closed, i.e., its differential is zero. + """ + diff = self.differential() + if(diff == self.parent().null_element()): + return True + else: + return False + + def homogeneous_part(self,degree): + """ + Returns the homogeneous component of grade 'degree' of 'self' + """ + C = self.__class__ + part = {} + 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) + + def homogeneous_parts(self): + """ + Returns all graded components of 'self' + """ + parts = {} + for n in range(self.dimension()+1): + hom_part = self.homogeneous_part(degree=n) + if hom_part != self.parent().null_element(): + parts[n] = hom_part + return parts + + def is_homogeneous(self): + """ + Returns 'True' if self is composed by homogeneous polynomials of the same degree + """ + isHomogeneous = True + S = self.parent().simplicialComplex() + dim = S.dimension() + for n in range(dim+1): + for simplex in S._n_cells_sorted(n): + pol = self.elements[simplex.tuple()] + if pol: + isHomogeneous = isHomogeneous and pol.is_homogeneous() + if not isHomogeneous: + break + if not isHomogeneous: + break + return isHomogeneous + + def degree(self): + if not self.is_homogeneous(): + return -1 + else: + degree = -1 + for e in self.elements.values(): + if e: + degree = e.degree()[0] + break + return degree + + def get_skeleton(self, dim): + + values = {} + facets = self._get_elements_of_dimension(dim) + for i in range(dim+1): + values.update(self._get_elements_of_dimension(i)) + S = SimplicialComplex(list(facets.keys()),is_mutable=False) + A = AlgebraAplK(S) + return A._element_constructor_(values) + class MorphismFromQQToAplK(Morphism): def __init__(self, codomain): - self.simplicialComplex= codomain.K + self.simplicialComplex = codomain.K Morphism.__init__(self, QQ, codomain) def _call_(self, x): S = self.simplicialComplex - params1 = {} + params = {} for d in range(S.dimension()+1): + s = SimplicialAlgebraplN(d) for c in S._n_cells_sorted(d): - params1[tuple(c)]=x - A = AplK(S) - return AplKElement(A,params1) - -##################################################################### - -class AplK(UniqueRepresentation, Parent): - - def __init__(self,K, category=None): - self.K = K - self.n = K.dimension() - Parent.__init__(self,category=category ) - f = MorphismFromQQToAplK(self) - self.register_coercion(f) - - def _repr_(self): - return "Algebra AplK over %s" % self.K - - def _element_constructor_(self,values): - return self.element_class(self,values) - - def dimension(self): - return self.K.dimension() - - def simplicialComplex(self): - return self.K - - def _n_cells_sorted(self,n): - return self.K._n_cells_sorted(n) - -##################################################################### -class AplKElement(Element): - - def __init__(self, parent,values): - self.elements = values - self.apl = Apln(parent.dimension()) - Element.__init__(self,parent) - self.check() - - def _repr_(self): - return str(self.elements) - - def dimension(self): - return self.parent().dimension() - - def nullElement(self): - C = self.__class__ - values = {} - for k in self.elements.keys(): - values[k]=0 - return C(self.parent(),values) - - def _add_(self,other): - r""" - Return sum of the elements - - INPUT: - - - ``other`` -- An AplKEelement - - - OUTPUT: An AplKElement equal to the sum of self and the element passed as argument - - EXAMPLES: - - This example illustrates ... :: - - sage: S = SimplicialComplex([[0,1],[0,2],[1,2]],is_mutable=False) - sage: apl = Apln(S.dimension()) - sage: (t0, t1, y0, y1) = apl.generators() - sage: params1 = {(0,): t0, (1,): t0, (2,):3*t0, (0,1): t0+t1,(0,2): t0+3*t1, (1,2): t0+3*t1} - sage: params2 = {(0,): 3*t0, (1,): 4*t0, (2,):5*t0, (0,1): 3*t0+4*t1,(0,2): 3*t0+5*t1, (1,2): 4*t0+5*t1} - sage: A = AplK(S) - sage: e1 = AplKElement(A,params1) - sage: e2 = AplKElement(A,params2) - sage: e1+e2 - {(0,): 4*t0, (1,): 5*t0, (2,): 8*t0, (0, 1): 4*t0 + 5*t1, (0, 2): 4*t0 + 8*t1, (1, 2): 5*t0 + 8*t1} - - """ - C = self.__class__ - 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) - - def _sub_(self,other): - - C = self.__class__ - 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) - - def _mul_(self,other): - - C = self.__class__ - 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) - - def _richcmp_(self, other, op): - - 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]): - return False - return True - elif(op==op_NE): - return not self._richcmp_(other,op_EQ) - else: - raise NotImplementedError("operation not implemented for this element class") - - def differential(self): - - C = self.__class__ - newValues = {} - for k in self.elements.keys(): - newValues[k] = self.apl.cdga(self.elements[k]).differential() - return C(self.parent(),newValues) - - def check(self): - - S = self.parent().simplicialComplex() - dim = S.dimension() - 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)) - aFace = self.apl.face(f)(self.elements[tuple(simplex)]) - if(self.elements[sFace] != aFace): - raise ValueError("the assignation is not valid") - - def homogeneous_component(self,degree): - - C = self.__class__ - component = {} - for k in self.elements.keys(): - component[k] = self.apl.homogeneousPart(self.elements[k],degree) - return C(self.parent(),component) - - def homogeneous_components(self): - - components = {} - for n in range(self.dimension()+1): - components[n] = self.homogeneous_component(n) - return components - - def is_homogeneous(self): - - isHomogeneous = True - for k in self.elements.keys(): - isHomogeneous = isHomogeneous and self.apl.cdga(self.elements[k]).is_homogeneous() - return isHomogeneous - - def is_in_kernel(self): - - diff = self.differential() - if(diff == self.nullElement()): - return True - else: - return False - - def _integrable_component(self): - C = self.__class__ - component = {} - for k in self.elements.keys(): - degree = len(k)-1 - component[k] = self.apl.homogeneousPart(self.elements[k],degree) - #component[k] = self.homogeneous_component(dim).elements[k] - return component - - def integral(self): - component = self._integrable_component() - cochain = {} - #integration of each polynomial - for k in component.keys(): - print("Integrating simplex " + str(k)) - n = len(k)-1 - pol = self.apl.cdga(component[k]) - cochain[k] = self.apl.integral(pol,n) - return cochain + params[tuple(c)]=s(x) + A = AlgebraAplK(S) + return AlgebraAplK.Element(A, params) +############################################# +# TEST +print("TEST AlgebraAplK") +S = SimplicialComplex([[0,1,2]],is_mutable=False) +A = AlgebraAplK(S) +print("Test check if the assignation is valid") +s0 = SimplicialAlgebraplN(0) +s0.inject_variables() +p0 = s0(2) +p1 = s0(1) +p2 = s0(3) +s1 = SimplicialAlgebraplN(1) +s1.inject_variables() +p01 = 2*t0+t1 +p02 = 2*t0+3*t1 +p12 = t0+3*t1 +s2 = SimplicialAlgebraplN(2) +s2.inject_variables() +p012 = 2-t1+t2 +values = {(0,): p0, (1,): p1, (2,): p2, (0,1): p01, (0,2): p02, (1,2): p12, (0,1,2): p012} +e1 = A._element_constructor_(values) +e1 +#############################################