diff --git a/formatted/AplN.py b/formatted/AplN.py new file mode 100644 index 0000000..4d2d0f2 --- /dev/null +++ b/formatted/AplN.py @@ -0,0 +1,426 @@ +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() +#############################################