416 lines
13 KiB
Python
416 lines
13 KiB
Python
r"""
|
|
<Short one-line summary that ends with no period>
|
|
|
|
<Paragraph description>
|
|
|
|
EXAMPLES::
|
|
|
|
<Lots and lots of 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 <alquezar@unizar.es>
|
|
#
|
|
# 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
|
|
|
|
"""
|
|
|
|
def __init__(self, A, differential):
|
|
"""
|
|
Initialize ``self``.
|
|
|
|
INPUT:
|
|
|
|
- ``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 equals to 'value'
|
|
|
|
OUTPUT:
|
|
- An element of the algebra Apl(n)
|
|
|
|
TESTS::
|
|
|
|
sage: apl = AlgebraPL(2)
|
|
sage: value = 5
|
|
sage: p = apl.integrand(value)
|
|
sage: p.integral()==value
|
|
True
|
|
"""
|
|
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 <sum(t_i)-1, sum(y_j)>
|
|
|
|
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<t_1,..t_n>.
|
|
(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<t_1,..,t_n>
|
|
|
|
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()
|
|
#############################################
|