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 class AplN(DifferentialGCAlgebra_multigraded, UniqueRepresentation): """ A differential multi-graded commutative algebra This class extends the DifferentialGCAlgebra_multigraded class to create the algebra of polynomial differential forms with coefficients in QQ INPUT: - ``A`` -- a multi-graded commutative algebra - ``differential`` -- the differential rules TESTS:: sage: 2 2 """ def __init__(self, A, differential): """ Initialize ``self``. INPUT: - ``A`` -- a multi-graded commutative algebra - ``differential`` -- a differential TESTS:: sage: 2 2 """ 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``. TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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.") class Element(GCAlgebra_multigraded.Element, DifferentialGCAlgebra.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ def is_homogeneous(self): """ Return "True" if "self" is homogeneous and "False" otherwise. TESTS:: sage: 2 2 """ 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. TESTS:: sage: 2 2 """ 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. TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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}). TESTS:: sage: 2 2 """ apl = self.parent() # 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) 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` TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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' TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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(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 TESTS:: sage: 2 2 """ 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 TESTS:: sage: 2 2 """ 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. TESTS:: sage: 2 2 """ 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() #############################################