178 KiB
About the organization of the notebook¶
In this document, the implementation of different mathematical objects is explained. For each one, the information is structured in the following way:
- First section: Definition. The object is defined using mathematical language.
- Second section: Implementation.
- Ideas and decisions made
- Initial declaration of the main class
- Implementation of a new method and examples
Each class is declared iteratively, i.e., each method is added in a different cell in order to explain it properly and to show examples. This is done in that way for a didactic purpose. For that reason, the subsection 2.c) is repeated for each new method that is added to the main class.
The cochain algebra $(A_{PL})_{n}$¶
In this section it is implemented the graded commutative differential cochain algebra $(A_{PL})_{n}$. For more information, see [Rational homotopy theory, Felix, Halperin, Thomas, chapter 10.c)].
Definition¶
First, consider the free graded commutative algebra $\Lambda(t_0,\dots,t_n,y_0,\dots,y_n)$, where the generators $t_i$ have degree $0$, and the generators $y_j$ have degree $1$. The differential, $d$, is given by
- $d(t_i)=y_i$
- $d(y_j)=0$
The cochain algebra $(A_{PL})_n$ is defined as the quotient of the previous algebra by the relations:
$$(A_{PL})_n=\frac{\Lambda(t_0,\dots,t_n,y_0,\dots,y_n)}{<\sum {t_i}-1,\sum{y_j}>}$$
Implementation¶
$(A_{PL})_{n}$ is a graded commutatite differential algebra, GCDA. In order to work with GCDA's that are finitely generated by elements of $degree \geq 1$, there is defined in SAGE a class called "DifferentialGCAlgebra". Our first problem is that our GCDA has generators of $degree=0$ (the generators $t_i$) and this class doesn't allow us to create such object.
As a first approximation, in order to solve this problem, we will define a new class of GCDA's that internally will work with multigraded generators, but this feature will be transparent for the user. For that reason, we will extend the class "DifferentialGCAlgebra_multigraded" in order to add new methods that will be useful later.
Initial declaration of the main class¶
from sage.algebras.commutative_dga import * import copy 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) class Element(GCAlgebra_multigraded.Element, DifferentialGCAlgebra.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ pass 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)
Example: creating an element of $(Apl)_{n}$¶
apl = AlgebraPL(1) print(apl) apl.inject_variables()
Commutative Differential Graded Algebra with generators ('t0', 't1', 'y0', 'y1') in degrees [0, 0, 1, 1] over Rational Field with differential:
t0 --> y0
t1 --> y1
y0 --> 0
y1 --> 0
Defining t0, t1, y0, y1
p = t0 + y0 + y1 p
y0 + y1 + t0
Adding new methods: homogeneity and total degree of a polynomial¶
As we mentioned before, we use a multigraded algebra internally. Specifically, we use a bigraded algebra, where the generators have either degree $(0,2)$ or degree $(1,2)$ The first component of the bigrade represents the actual degree we want to use, and the second component is ignored (it is only needed to be an even number $>0$). In particular, the generators $t_i$ have internally a degree $(0,2)$ and the generators $y_j$ have degree $(1,2)$. In order to make it transparent for the user, we implement a several methods to get, for example, the homogeneous part of a polynomial, ignoring the mentioned bigrade, and taking into account only the first compoment of the multigrade.
class AplN(AplN,UniqueRepresentation): class Element(AplN.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. """ 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 sum(parts)
Example: checking the homogeneity of a given polynomial¶
apl = AlgebraPL(1) apl.inject_variables()
Defining t0, t1, y0, y1
p = t0 + y0 + y1 p.is_homogeneous()
False
Example: getting the homogeneous part of a polynomial for a given degree¶
p = t0 + y0 + y1 deg = 1 print("Original polynomial:", p) print("Homogeneous part of degree", deg, "of p: ", p.homogeneous_part(degree=deg))
Original polynomial: y0 + y1 + t0 Homogeneous part of degree 1 of p: y0 + y1
Example: getting the total degree of a polynomial¶
q = y0*y1 + t0*y1 + 1 q.total_degree()
2
Adding new method: reduce a polynomial by relations¶
As seen in the definition, the algebra $(A_{PL})_{n}$ is the quiotient algebra
$$(A_{PL})_n=\frac{\Lambda(t_0,\dots,t_n,y_0,\dots,y_n)}{<\sum {t_i}-1,\sum{y_j}>}$$
Notice that there is an isomorphism between this algebra and the free algebra:
$$ (A_{PL})_n \cong \Lambda(t_1,\dots,t_n,y_1,\dots,y_n) $$
In order to obtain a representative of a particular element on the free algebra, a method called "reduce" is added to the elements of our class. This method reduces the variables $t_0,y_0$ applying the following rules:
$$t_0 = 1 - \sum_{i=1}^{n} t_i$$ $$y_0 = -\sum_{j=1}^{n} y_j$$
Thus, after applying this method, an element of $(A_{PL})_{n}$ will be represented as an element of the algebra $\Lambda(t_1,\dots,t_n,y_1,\dots,y_n)$.
class AplN(AplN,UniqueRepresentation): class Element(AplN.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ 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 """ apl = self.parent() dim = len(apl.gens())//2-1 if relations: return DifferentialGCAlgebra.Element.reduce(self, relations) else: return DifferentialGCAlgebra.Element.reduce(self, [apl.t_relation, apl.y_relation])
Example: getting the reduced polynomial of a given element¶
apl = AlgebraPL(1) apl.inject_variables()
Defining t0, t1, y0, y1
p=t0+y0+y1 print("unreduced polynomial: ", p) print("reduced polynomial: ", p.reduce())
unreduced polynomial: y0 + y1 + t0 reduced polynomial: -t1 + 1
Adding new methods: The definite integral map¶
We define a linear map:
$$ \int_{n}: (A_{PL})_{n}^{n} \rightarrow \mathbb{Q} $$
by setting
$$ \int_{n}t_1^{k_1}t_2^{k_2}\dots t_n^{k_n}dt_1\wedge \dots \wedge dt_n = \int_{0}^{1} dt_1 \int_{0}^{1-t_1}dt_2 \dots \int_{0}^{1-\sum_{1}^{n-1}t_i}t_1^{k_1}t_2^{k_2}\dots t_n^{k_n}dt_n = \frac{k_1!k_2! \dots k_n!}{(k_1+ \dots +k_n+n)!} $$
class AplN(AplN,UniqueRepresentation): class Element(AplN.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ 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}). """ 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
Example¶
apl = AlgebraPL(1) apl.inject_variables()
Defining t0, t1, y0, y1
p=t0+y1*(t0+1) print("Integrable part: ",p._integrable_part()) print("Definite integral: ", p.integral())
Integrable part: -t1 + 2 Definite integral: 3/2
Adding new method: find an integrand for a given definite integral¶
The linear map we have defined above is not injective, but we can consider one way to find a polynomial whose image by the definite integral map is given. We call "integrand" to this particular element of the preimage set of the given element by the linear map.
$$ \int_{n}^{-1} c = c (n!) dt_1 \wedge \dots \wedge dt_n$$ $$ c \in \mathbb{Q} $$
It is easy to see that $ \int_{n}\int_{n}^{-1} c = c $ although the inverse is not always true.
We define a class method, called "integrand", that returns a polynomial whose definite integral image is equal to a rational number given as input.
class AplN(AplN,UniqueRepresentation): def integrand(self, value): """ Returns a polynomial whose definite integral is equal to 'value' OUTPUT: - A polynomial """ return value*factorial(self.n)*prod(self.gens()[self.n+2:])
Example: getting a polynomial for whose definite integral is given¶
apl = AlgebraPL(1) apl.inject_variables()
Defining t0, t1, y0, y1
value = 5 apl.integrand(value)
5*y1
#checking that the composition of the integral and the lift is the identity map p = apl.integrand(value) p.integral()==value
True
Adding new methods: find a primitive of the coboundary operator¶
As we have mentioned before, there is an isomorphism between the cochain algebra $(A_{PL})_n$ and the algebra $\Lambda (t_1, \dots, t_n,y_1, \dots, y_n)$, where the variables $t_i$ have degree $0$, and the variables $y_j$ have degree $1$. This algebra is the tensor product of the polynomial ring in the variables $t_i$ with the exterior algebra in the variables $y_j$:
$$(A_{PL})_n \cong \mathbb{Q}[t_1, \dots, t_n]\otimes_{\mathbb{Q}} \Lambda ^{\mathbb{Q}}(y_1, \dots, y_n)$$
In this algebra, we define the lexicographic monomial order in the variables $y_i$ as
$$y_i > y_j, \: if \: i<j$$
A polynomial $p \in \Lambda (t_1, \dots, t_n,y_1, \dots, y_n)$ has the form:
$$ p = p^0 y_{1}^{k_{1}^{0}} \dots y_{n}^{k_{n}^{0}} + p^1 y_{1}^{k_{1}^{1}} \dots y_{n}^{k_{n}^{1}} + \dots $$
where
- $k_{j}^{i} \in \mathbb{Z}_2$
- $p^i \in \mathbb{Q}[t_1, \dots, t_n]$
- $(k_{1}^{i}, \dots , k_{n}^{i}) \neq (k_{1}^{j}, \dots, k_{n}^{j})$ if $i \neq j$
It is easy to see that $p$ will have as much as $2^n-1$ non-null terms in the variables $y_j$.
As we have set the lexicographic monomial ordering, for every two consecutive non-null terms in a polynomial $p$
$$ p = \dots + p^i y_{1}^{k_{1}^{i}} \dots y_{n}^{k_{n}^{i}} + p^{i+1} y_{1}^{k_{1}^{i+1}} \dots y_{n}^{k_{n}^{i+1}} + \dots$$
occurs one of the following situations:
- $k_{1}^{i} > k_{1}^{i+1}$
- $\exists m \in \mathbb{N}, 1 < m \leq n $ so that $k_{l}^{i} = k_{l}^{i+1}$ for $1 \leq l < m$, and $k_{l}^{i} > k_{l}^{i+1}$ for $l= m$.
Our algebra $(A_{PL})_n$, as a cochain algebra, has a coboundary operator, $d$, and by definition, we know that $d^2=0$. Also, we know that $H^p((A_{PL})_n)=0$ for $p \geq 1$, meaning that, for every element $\theta \in (A_{PL})_n$ so that $d\theta=0$ (i.e., $\theta$ is closed), there exists an element $\omega \in (A_{PL})_n$ so that $\theta = d\omega$.
Our problem here is to find a primitive $\omega$ whose differential $\theta$ is known, $\theta = d\omega$. As we know that $\theta$ is the differential of some element, for now on we will consider polynomials where all terms have at least one exponent $k_{j}^{i} \neq 0$ (i.e., our polynomial $\theta$ has no terms of degree $0$).
Definition. (pseudo-primitive of a monomial) Let $\theta \in (A_{PL})_n$ be a polynomial formed by only one term of degree $p$ of the form:
$$\theta = ct_{1}^{l_{1}^{0}} \dots t_{n}^{l_{n}^{0}} y_{1}^{k_{1}^{0}} \dots y_{n}^{k_{n}^{0}}$$
satisfying:
- $c \in \mathbb{Q}$
- $k_{j}^{i} \in \mathbb{Z}_2$
- $\sum k_{j}^{i} = p$
- $l_{j}^{i} \in \mathbb{N} \cup \{0\}$
We define the pseudo-primitive of $\theta$ (in relation to the coboundary operator) as the homogeneous polynomial $\omega$, of degree $p-1$, whose differential is the polynomial $\theta$ plus some homogeneous polynomial $\epsilon$ of degree $p$:
$$d\omega = \theta+\epsilon$$
This polynomial is calculated in the following way:
Step 1: Choose the lowest $i$ for which $k_{i}^{0}=1$
Step 2: Set $k_{i}^{0}=0$ and increase $l_{i}^{0}$ by 1
$$ \omega = ct_{1}^{l_{1}^{0}} \dots t_{i}^{l_{i}^{0}+1} \dots t_{n}^{l_{n}^{0}} y_{1}^{0} \dots y_{i}^{0} \dots y_{n}^{k_{n}^{0}}$$
Step 3: Divide the coefficient $c$ by $l_{i}^{0}+1$
$$ \omega = \frac{c}{l_{i}^{0}+1}t_{1}^{l_{1}^{0}} \dots t_{i}^{l_{i}^{0}+1} \dots t_{n}^{l_{n}^{0}} y_{1}^{0} \dots y_{i}^{0} \dots y_{n}^{k_{n}^{0}}$$
Lemma: $d\omega= \theta + \epsilon$
Proof: TODO
The algorithm¶
The input of our algorithm is a polynomial $\theta \in (A_{PL})_n$ that has the form:
$$ \theta = p^0 y_{1}^{k_{1}^{0}} \dots y_{n}^{k_{n}^{0}} + p^1 y_{1}^{k_{1}^{1}} \dots y_{n}^{k_{n}^{1}} + \dots $$
$$p^i \in \mathbb{Q}[t_1, \dots, t_n]$$
Step 1: Find a pseudo-primitive of the leading term of $\theta$.
First, we define the variables $\omega, \theta_0$, and we initialize them as
$$\omega=0$$ $$\theta_0=\theta$$
The leading term of $\theta_0$, $LT(\theta_0)$, has the form:
$$LT(\theta_0) = c_0t_{1}^{l_{1}^{0}} \dots t_{n}^{l_{n}^{0}} y_{1}^{k_{1}^{0}} \dots y_{n}^{k_{n}^{0}}$$ $$ c_0\in \mathbb{Q}$$ $$ l_{i}^{0} \geq 0$$
What we want to do in this step is to find a polynomial $\omega_0$ whose differential contains the leading term of the element $\theta_0$ plus some extra terms $\epsilon_0$:
$$d\omega_0 = LT(\theta_0)+\epsilon_0$$
Knowing that the coboundary operator increases the degree in 1 unit, and the degree of every term comes from the variables $y_j$, one approach is the following:
- Assign to $\omega_0$ the leading term of $\theta_0$,
- Choose the lowest $i$ for which $k_{i}^{0}=1$
- Set $k_{i}^{0}=0$ and increase $l_{i}^{0}$ by 1
- Divide the coefficient $c_0$ by $l_{i}^{0}+1$
$$ \omega_0 = \frac{c_0}{l_{i}^{0}+1}t_{1}^{l_{1}^{0}} \dots t_{i}^{l_{i}^{0}+1} \dots t_{n}^{l_{n}^{0}} y_{1}^{0} \dots y_{i}^{0} \dots y_{n}^{k_{n}^{0}}$$
TODO: Prueba de que esto funciona:
$$\omega_0 = c_0a(t)*b(y)$$ $$d\omega_0 = d(c_0a(t))*b(y)) = c_0d(a(t))b(y)$$
Step 2: Calculate the new $\theta_i$:
Once we have found the element $\omega_0$, we update the variable $\omega$ as:
$$ \omega = \omega + \omega_0$$
and we define the new variable $\theta_{1}$ as:
$$ \theta_{1} = \theta-d\omega $$
If $\theta_{1}=0$ we have finished and $\omega$ is the element we were looking for. If $\theta_{1} \neq 0$, continue with the following steps.
Step 3:
Get the leading term of $\theta_{1}$, and obtain its pseudo-primitive $\omega_1$.
Step 4:
Update the variable $\omega$ as:
$$ \omega = \omega + \omega_1$$
and define the new variable $\theta_{2}$ as:
$$ \theta_{2} = \theta-d\omega $$
Step 5: If $\theta_{2}=0$ we have finished and $\omega$ is the element we were looking for. If $\theta_{2} \neq 0$, go back to step 3 and repeat the steps 3 and 4 several times until we get that $\theta_{i+1}=0$.
class AplN(AplN,UniqueRepresentation): class Element(AplN.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ 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<t_1,..,t_n> 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()
Example¶
apl = AlgebraPL(5) apl.inject_variables()
Defining t0, t1, t2, t3, t4, t5, y0, y1, y2, y3, y4, y5
omega_orig = t0+5*y1*y2+y3*y4*t1*y5 theta = omega_orig.differential() print(theta.primitive().differential()==theta) print(theta.primitive()==omega_orig)
True False
theta = (t0+t1^2+1)*y0*y1*y3 print(theta.differential()) print(theta.primitive().differential()==theta) print(theta.primitive())
0 True t0*t1^2*y1*y3 + 1/2*t0^2*y1*y3 + t0*y1*y3
The simplicial cochain algebra $A_{PL}$¶
This class implements the simplicial cochain algebra $A_{PL} = \{(A_{PL})_{n}\}_{n\geq0}$. For more information, see [Rational homotopy theory, Felix, Halperin, Thomas, chapter 10.c)].
Definition¶
A simplicial object $K$ with values in a category $\mathfrak{C}$ is a sequence $\{K_n\}_{n \geq 0}$ of objects in $\mathfrak{C}$, together with $\mathfrak{C}$-morphisms:
$$\delta_i: K_{n} \rightarrow K_{n-1}, \; 0 \leq i \leq n $$
$$s_j: K_{n} \rightarrow K_{n+1}, \; 0 \leq j \leq n$$
that satisfy the identities:
$$\delta_i \delta_j = \delta_{j-1} \delta_i,\; i<j$$ $$s_i s_j = s_{j+1} s_i,\; i \leq j$$ $$\delta_i s_j = \left\{\begin{matrix} s_{j-1}\delta_i,& i<j\\ id, & i=j,j+1\\ s_j\delta_{i-1}, & i> j+1 \end{matrix}\right.$$
We define the simplicial cochain algebra $A_{PL} = \{(A_{PL})_n\}_{n \geq 0}$ by:
The cochain algebra $(A_{PL})_n$ is given by: $$(A_{PL})_n = \frac{\wedge(t_0, \dots, t_n,y_0,\dots, y_n)}{<\sum{t_i}-1, \sum{y_j}>}$$
The face and degeneracy morphisms are the unique cochain algebra morphisms:
$$\delta_i: (A_{PL})_{n} \rightarrow (A_{PL})_{n-1}, \; 0 \leq i \leq n$$
$$s_j: (A_{PL})_{n} \rightarrow (A_{PL})_{n+1}, \; 0 \leq j \leq n$$
satisfying
$$\delta_i = t_k \rightarrow \left\{\begin{matrix} t_k,& k<i\\ 0, & k=i\\ t_{k-1}, & k>i \end{matrix}\right. \; \; \; \;\; s_j = t_k \rightarrow \left\{\begin{matrix} t_k,& k<j\\ t_k+t_{k+1}, & k=j\\ t_{k+1}, & k>j \end{matrix}\right.$$
Implementation¶
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(k<i): tImages.append(codomain.gens()[k]) yImages.append(codomain.gens()[n-1+k]) elif(k==i): tImages.append(0) yImages.append(0) 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) 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(k<j): tImages.append(codomain.gens()[k]) yImages.append(codomain.gens()[n+1+k]) elif (k==j): tImages.append(codomain.gens()[k]+codomain.gens()[k+1]) 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) 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 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)
s = SimplicialAlgebraplN(5) s.inject_variables()
Defining t0, t1, t2, t3, t4, t5, y0, y1, y2, y3, y4, y5
p = t1+y2 p.degeneration(0).face(1)==p
True
m1 = p.degeneration(1).face_morphism(1) m2 = p.degeneration_morphism(1) (m1*m2).is_identity()
True
Adding new methods: propagate a simplex through the simplicial structure¶
In this part, we want to obtain an homogeneous $(n+k)$-simplex of degree $n$, $p_{n+k} \in (A_{PL})_{n+k}^n$, for $k>0$, where a given homogeneous $n$-simplex of degree $n$, $p_n \in (A_{PL})_n^n$ is a face of $p_{n+k}$ of dimension $n$, and the rest of faces of dimension lower or equal to $n$ are $0$. We call this map propagation,denoted by $\Gamma$ :
$$\Gamma_{n+k}^n: (A_{PL})_n^n \rightarrow (A_{PL})_{n+k}^n$$
Without lost of generality, we asume that our polynomial of degree $n$ is expressed as a polynomial in the variables $y_1,\dots,y_n$. Let $p_n \in (A_{PL})_n^n$ be the polynomial:
$$p_n = q_t y_{1} \dots y_{n}$$
$$q_t \in \mathbb{Q}[t_0,\dots,t_n]$$
Let $I$ be the index set $\{0,\dots, n+k\}$. For some subsets $(i_1, \dots, i_k) \subset I$ we know by definition that
$$p_n = \delta_{i_1} \dots \delta_{i_k} \Gamma_{n+k}^n(p_n)$$
and due to the relations between face morphisms, we know that we can chose a unique subset $(i_1, \dots, i_k)$ in a way that $i_1 < \dots < i_k$.
Just to point the definition, notice that any other combination of faces $\delta_{j_1} \dots \delta_{j_k}$ not equivalent to $\delta_{i_1} \dots \delta_{i_k}$ (i.e., applying the relations between face morphisms), makes the result:
$$\delta_{j_1} \dots \delta_{j_k}\Gamma_{n+k}^n(p_n) = 0$$
Let $J=\{j_0,\dots,j_n\} \subset I$ be the subset $\{i_1, \dots, i_k\}^C$, i.e., those elements of $I$ $\notin \{i_1, \dots, i_k\}$, and where $j_l < j_m$, for $l<m$.
Just before defining the propagation morphism, we define the morphism $\Upsilon$ as:
\begin{align} \Upsilon: \mathbb{Q}[t_0,\dots, t_n] & \longrightarrow \mathbb{Q}[t_0, \dots, t_{n+k}] \\ t_i & \rightarrow t_{j_i} \end{align}
$$j_i \in J$$
Now we can define the propagation map $\Gamma_{n+k}^n$ as:
$$\Gamma_{n+k}^n(p_n) = \Upsilon(q_t)\sum_{k=0}^{n}{(-1)^{n-k} t_{j_{n-k}}(y_{j_0}\dots \widehat{y_{j_{n-k}}} \dots y_{j_n})}$$
where $\widehat{y_{j}}$ means that we have removed the variable $y_{j}$.
Implementation¶
class SimplicialAplN(SimplicialAplN,UniqueRepresentation): class Element(SimplicialAplN.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ 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
Example¶
s = SimplicialAlgebraplN(2) s.inject_variables()
Defining t0, t1, t2, y0, y1, y2
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)
t5^2*t7*y1*y5 + t7^3*y1*y5 - t5^3*y1*y7 - t5*t7^2*y1*y7 + t1*t5^2*y5*y7 + t1*t7^2*y5*y7 True
q1 = p.propagate([1,5,7],10) q2 = p.propagate([0,4,6],9) print(q1.face(0).reduce() == q2.reduce())
True
# tests torus (remove cell) s = SimplicialAlgebraplN(1) s.inject_variables() p = y1 F1a = p.propagate([0,1],2) F2a = p.propagate([1,2],2) F1c = p.propagate([0,2],2) F2c = p.propagate([0,2],2) print((F2a).reduce()) C1F1 = F1a+F1c C1F2 = F2a+F2c C1F2.reduce()
Defining t0, t1, y0, y1 -t2*y1 + t1*y2
y2
# tests torus (remove cell) s = SimplicialAlgebraplN(1) s.inject_variables() p = y1 F1b = p.propagate([1,2],2) F2b = p.propagate([0,1],2) F1c = p.propagate([0,2],2) F2c = p.propagate([0,2],2) print((F2b+F2c)) C2F1 = F1b+F1c C2F2 = F2b+F2c C2F1.reduce()
Defining t0, t1, y0, y1 -t1*y0 - t2*y0 + t0*y1 + t0*y2
y2
The cochain algebra $A_{PL}(K)$¶
This class implements the cochain algebra $A_{PL}(K)$. For more information, see [Rational homotopy theory, Felix, Halperin, Thomas, chapter 10.c)].
Definition¶
Let $K$ be a simplicial set, and let $A_{PL} = \{(A_{PL})_n\}_{n \geq 0}$ be the simplicial cochain algebra defined earlier. Then
$$ A_{PL}(K) = \{A_{PL}^p(K)\}_{p \geq 0}$$
is the cochain algebra defined as follows:
- $A_{PL}^p(K)$ is the set of simplicial set morphisms from $K$ to $A_{PL}^p$. Thus, an element $\Phi \in A_{PL}^p(K)$ is a mapping that assings to each $n$-simplex $\sigma \in K_n$ an element $\Phi_\sigma \in (A_{PL}^p)_n$, such that
$$ \Phi_{\delta_i(\sigma)}=\delta_i(\Phi_\sigma)$$
$$ \Phi_{s_j(\sigma)}=s_j(\Phi_\sigma)$$
- Addition, scalar multiplication and the differential are given by $$ (\Phi+\Psi)_\sigma = \Phi_\sigma + \Psi_\sigma$$
$$ (\lambda \Phi)_\sigma = \lambda \Phi_\sigma$$
$$ (d\Phi)_\sigma = d(\Phi_\sigma)$$
- As $A_{PL}$ is a simplicial cochain algebra, multiplication in $A_{PL}(K)$ is given by
$$ (\Phi \Psi)_\sigma = \Phi_\sigma \Psi_\sigma$$
Implementation¶
from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import rich_to_bool, op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE 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 = values 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") class MorphismFromQQToAplK(Morphism): def __init__(self, codomain): self.simplicialComplex = codomain.K Morphism.__init__(self, QQ, codomain) def _call_(self, x): S = self.simplicialComplex params = {} for d in range(S.dimension()+1): s = SimplicialAlgebraplN(d) for c in S._n_cells_sorted(d): params[tuple(c)]=s(x) A = AlgebraAplK(S) return AlgebraAplK.Element(A, params)
Example¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) A
The graded commutative differential cochain algebra Apl(K) of Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
values = {(0,): 0, (1,): 0, (2,):0, (0,1): 0, (0,2): 0, (1,2): 1, (0,1,2):0} A._element_constructor_(values)
{(0,): 0, (1,): 0, (2,): 0, (0, 1): 0, (0, 2): 0, (1, 2): 1, (0, 1, 2): 0}
values1 = {(0,): 0, (1,): 0, (2,):0, (0,1): 0, (0,2): 0, (1,2): 1, (0,1,2):0} e1 = A._element_constructor_(values1) values2 = {(0,): 0, (1,): 0, (2,):0, (0,1): 0, (0,2): 0, (1,2): 1, (0,1,2):0} e2 = A._element_constructor_(values2)
e1-e2
{(0,): 0, (1,): 0, (2,): 0, (0, 1): 0, (0, 2): 0, (1, 2): 0, (0, 1, 2): 0}
(e1-e2)==A.null_element()
True
3*e1
{(0,): 0, (1,): 0, (2,): 0, (0, 1): 0, (0, 2): 0, (1, 2): 3, (0, 1, 2): 0}
simplex = S._n_cells_sorted(2)[0] A._faces_of_length(simplex,2)
[(0, 1), (0, 2), (1, 2)]
Adding methods: reduce polynomials by relations¶
As the algebra $(A_{PL})_n$ is not freely generated, it could be that two apparently different elements of $A_{PL}(K)$ correspond actually to the same element in the algebra. In order to compare properly elements of the algebra, it is added a new method called "reduce" that, $\forall n \geq 0$, applies the relations of $(A_{PL})_n$ to every polynomial in $ \Phi \in A_{PL}(K)$ in a particular way that makes the representation of every class of elements unique.
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ def __init__(self, parent, values): Element.__init__(self,parent) self.elements = values self.elements = self._reduce(values) 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
Example¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) A
The graded commutative differential cochain algebra Apl(K) over Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
s = SimplicialAlgebraplN(3) s.inject_variables() values = {(0,): y0*y1*y2*y3, (1,): s(0), (2,): s(0), (0,1): t1, (0,2): y0, (1,2): t0, (0,1,2): s(0)} values
Defining t0, t1, t2, t3, y0, y1, y2, y3
{(0,): y0*y1*y2*y3,
(1,): 0,
(2,): 0,
(0, 1): t1,
(0, 2): y0,
(1, 2): t0,
(0, 1, 2): 0}
e1 = A._element_constructor_(values) e1
{(0,): 0, (1,): 0, (2,): 0, (0, 1): t1, (0, 2): -y1 - y2 - y3, (1, 2): -t1 - t2 - t3 + 1, (0, 1, 2): 0}
Adding new methods: Check if a given assignation respects the simplicial structure¶
During the construction of an element of $A_{PL}(K)$, it is important to be sure that a given assignation of polynomials to our simplicial complex respects the simplicial structure. For that reason, a method called "check" is added. The method verifies the following condition:
$$\Phi (\delta_i \sigma) = \delta_i \Phi (\sigma)$$
$$\forall \sigma \in K_{n}, n \geq 1$$ $$0 \leq i \leq n$$
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ def __init__(self, parent, values): Element.__init__(self,parent) self.elements = values self.elements = self._reduce(values) self.check() 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")
Example¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) A
The graded commutative differential cochain algebra Apl(K) over Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
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
Defining t0, y0 Defining t0, t1, y0, y1 Defining t0, t1, t2, y0, y1, y2
{(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1 + t2 + 2}
simplex = S._n_cells_sorted(2)[0] list(powerset(simplex))
[[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2]]
[(0, 1), (0, 2), (1, 2)]
Adding new methods: Differential¶
As mentioned in the definition of $A_{PL}(K)$, the differential of $\Phi \in A_{PL}(K)$ is defined as
$$(d\Phi) (\sigma) = d(\Phi (\sigma))$$
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ 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
Example¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) A
The graded commutative differential cochain algebra Apl(K) over Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
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
Defining t0, y0 Defining t0, t1, y0, y1 Defining t0, t1, t2, y0, y1, y2
{(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1 + t2 + 2}
e1.differential()
{(0,): 0, (1,): 0, (2,): 0, (0, 1): -y1, (0, 2): y1, (1, 2): 2*y1, (0, 1, 2): -y1 + y2}
e1.is_closed()
False
e1.differential().is_closed()
True
Adding new methods: Homogeneity¶
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ 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
Examples¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) A
The graded commutative differential cochain algebra Apl(K) over Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
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
Defining t0, y0 Defining t0, t1, y0, y1 Defining t0, t1, t2, y0, y1, y2
{(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1 + t2 + 2}
e1.is_homogeneous()
True
e1.homogeneous_parts()
{0: {(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1 + t2 + 2}}
values = {(0,): p0, (1,): p1, (2,): p2, (0,1): p01, (0,2): p02, (1,2): p12, (0,1,2): p012+t0*y1*y2+t0*t1*y2} e2 = A._element_constructor_(values) e2
{(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1*y1*y2 - t2*y1*y2 - t1^2*y2 - t1*t2*y2 + y1*y2 + t1*y2 - t1 + t2 + 2}
e2.is_homogeneous()
False
e2.homogeneous_parts()
{0: {(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1 + t2 + 2},
1: {(0,): 0, (1,): 0, (2,): 0, (0, 1): 0, (0, 2): 0, (1, 2): 0, (0, 1, 2): -t1^2*y2 - t1*t2*y2 + t1*y2},
2: {(0,): 0, (1,): 0, (2,): 0, (0, 1): 0, (0, 2): 0, (1, 2): 0, (0, 1, 2): -t1*y1*y2 - t2*y1*y2 + y1*y2}}
sum(e2.homogeneous_parts().values())==e2
True
Adding new methods: Skeleton¶
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ 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)
Example¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) 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+t0*y1*y2+t0*t1*y2} e2 = A._element_constructor_(values) e2
Defining t0, y0 Defining t0, t1, y0, y1 Defining t0, t1, t2, y0, y1, y2
{(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1*y1*y2 - t2*y1*y2 - t1^2*y2 - t1*t2*y2 + y1*y2 + t1*y2 - t1 + t2 + 2}
Adding new methods: Integration map¶
For $(A_{PL})_n$ we have defined the linear map:
$$ \int_{n}: (A_{PL})_{n}^{n} \rightarrow \mathbb{Q} $$
Now we want to extend this map to $A_{PL}(K)$ in order to obtain a natural quasi-isomorphism of cochain complexes:
$$\oint : A_{PL}(K) \rightarrow C^*(K)$$
Remember that $\Phi^n_n \in A_{PL}(K)^n_n$ is a morphism $\Phi^n_n: K_n \rightarrow (A_{PL})^n_n$
We define the integration map $ \oint = \begin{Bmatrix}\oint_n\end{Bmatrix}_{n \geq 0}$ as
$$ \oint_n: A_{PL}(K)^n_n \rightarrow C^n(K)$$
$$ (\oint_n(\Phi^n_n))(\sigma) = (-1)^{\frac{n(n-1)}{2}}\int_{n}(\Phi^n_n(\sigma))$$ $$ \sigma \in K_n$$
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ def integral(self): """ Returns the cochain obtained as integration of 'self' """ cochain = {} #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() return cochain
Examples¶
S = SimplicialComplex([[0,1,2]],is_mutable=False) A = AlgebraAplK(S) A
The graded commutative differential cochain algebra Apl(K) over Simplicial complex with vertex set (0, 1, 2) and facets {(0, 1, 2)}
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
Defining t0, y0 Defining t0, t1, y0, y1 Defining t0, t1, t2, y0, y1, y2
{(0,): 2, (1,): 1, (2,): 3, (0, 1): -t1 + 2, (0, 2): t1 + 2, (1, 2): 2*t1 + 1, (0, 1, 2): -t1 + t2 + 2}
e1.integral()
{(0,): 2, (1,): 1, (2,): 3, (0, 1): 0, (0, 2): 0, (1, 2): 0, (0, 1, 2): 0}
e1.differential()
{(0,): 0, (1,): 0, (2,): 0, (0, 1): -y1, (0, 2): y1, (1, 2): 2*y1, (0, 1, 2): -y1 + y2}
e1.differential().integral()
{(0,): 0, (1,): 0, (2,): 0, (0, 1): -1, (0, 2): 1, (1, 2): 2, (0, 1, 2): 0}
Adding new methods: Lift cochain¶
Here, we define a map
$$\oint^{-1} : C^*(K) \rightarrow A_{PL}(K) $$
that, for a given a cochain $\chi$, we obtain an element $\oint^{-1}\chi \in A_{PL}(K)$ for which
$$\oint\oint^{-1} = id$$
As the map $\oint$ is not injective, the oposite is almost never true. In general, $\oint^{-1}\oint \Phi \neq \Phi$, for any $\Phi \in A_{PL}(K)$
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ def lift_cochain(self, cochain): """ Returns an element of the algebra Apl(K) whose integral is the cochain given as input. """ # first we find polynomials compatible with the integration value values = {} max_dim = 0 for k in cochain.keys(): dim = len(k)-1 if dim > max_dim: max_dim = dim apl = SimplicialAlgebraplN(dim) if dim>0: values[k] = ((-1)**(dim*(dim-1)/2))*apl.integrand(cochain[k]) else: 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 for k in keys: simplicial_structure[k] = values[k] + self._propagate(cochain, k) #print(simplicial_structure) return AlgebraAplK.Element(self,simplicial_structure) def _propagate(self, cochain, simplex): """ Returns the part of the polynomial assigned to 'simplex' related to the propagation of the simplicial information of its faces """ dim = len(simplex)-1 apl = SimplicialAlgebraplN(dim) propagation = apl(0) 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) 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) return propagation
Example¶
S = SimplicialComplex([[0,1,2,3]],is_mutable=False) A = AlgebraAplK(S) cochain = {(0,): 0, (1,): 0, (2,):0, (3,): 0, (0,1): 0, (0,2): 0, (0,3): 0,(1,2): 0, (1,3): 1, (2,3): 0, (0,1,2):0, (0,1,3):0, (0,2,3):0, (1,2,3):1, (0,1,2,3):1} p = A.lift_cochain(cochain) p
{(0, 1, 2, 3): -6*y1*y2*y3 - 2*t3*y1*y2 + 2*t2*y1*y3 - 2*t1*y2*y3 - t3*y1 + t1*y3, (0, 1, 2): 0, (0, 1, 3): -t2*y1 + t1*y2, (0, 2, 3): 0, (1, 2, 3): -2*y1*y2 + t2*y1 - t1*y2 + y2, (0, 1): 0, (0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): y1, (2, 3): 0, (0,): 0, (1,): 0, (2,): 0, (3,): 0}
print(p.integral()==cochain) print(A.lift_cochain(p.integral())==p)
True True
Adding new methods: Managing simplicial-cohomology generators¶
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ def cohomology_gens(self,degree): """ Returns the cohomolosy generators of a given degree """ gens = {} basis = self.K.cohomology_ring(QQ).basis() for k in basis.keys(): if k[0] == degree: gens[k] = basis[k] return gens 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() return Hn def cochain_to_vector(self, cochain, dim): """ 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 for simplex in basis.keys(): v[i] = cochain[tuple(simplex)] i = i+1 return v def vector_to_cochain(self, v, dim): """ Converts a cochain, given as a vector, to a dictionary """ cochain = {} for d in range(self.K.dimension()+1): basis = self.K.n_chains(d, base_ring=QQ, cochains=True).basis() if d!=dim: for simplex in basis.keys(): cochain[tuple(simplex)] = 0 else: i=0 for simplex in basis.keys(): cochain[tuple(simplex)] = v[i] i = i+1 return cochain def lift_cochain_complex(self, cochain, dim): """ (REVISAR) Returns an element of the preimage by the differential of a given cochain INPUT: - cochain: a cochain as a vector - dim: dimension of the cochain """ # S = the Simplicial Complex # cochain = the cochain you want to find a preimage by the differential # dim = the dimension of the cochain cochain_vector = self.cochain_to_vector(cochain, dim) cochainComplex = self.K.chain_complex(base_ring=QQ).dual() diff = cochainComplex.differential(dim-1) show(diff.echelon_form()) result = diff.solve_right(cochain_vector) return self.vector_to_cochain(result) def lift_cohomology_generator(self, g): """ Returns a representative cochain associated to a given cohomology generator. """ cycle = g.to_cycle() cochain = {} for n in range(self.K.dimension()+1): for simplex in self.K._n_cells_sorted(n): cochain[simplex.tuple()] = 0 for i in cycle: #print(type(i[0].tuple())) cochain[i[0].tuple()] = i[1] return cochain def get_cohomology_aplk(self,degree): """ Returns a set of representative elements of Apl(K), one for each cohomology generator of a given degree """ representative_aplk_elements = {} dic_gens = self.cohomology_gens(degree) for key in dic_gens.keys(): gen = dic_gens[key] cochain = self.lift_cohomology_generator(gen) representative_aplk_elements[key] = self.lift_cochain(cochain) return representative_aplk_elements def first_cohomology_group(self): """ Returns the degree >= 1 of the first non-null cohomology generator. """ basis = self.K.cohomology_ring(base_ring=QQ).basis() indexes = list(basis.keys()) indexes.sort(reverse=False) # print(indexes) for n in range(1,len(indexes)): if indexes[n][0] > 0: return indexes[n][0] return -1
Examples¶
S = SimplicialComplex([[0,1,2],[1,3],[2,3]],is_mutable=False) A = AlgebraAplK(S) g = A.cohomology_gens(1) g
{(1, 0): h^{1,0}}
H1 = A.cohomology_group(1) l = [] for v in H1.basis(): l.append(H1.lift_map()(v)) show(l[0]) A.vector_to_cochain(l[0],1)
{(0,): 0,
(1,): 0,
(2,): 0,
(3,): 0,
(0, 1): 1,
(0, 2): 0,
(1, 2): -1,
(1, 3): 0,
(2, 3): 0,
(0, 1, 2): 0}
gen = list(g.values())[0] cochain = A.lift_cohomology_generator(gen) cochain
{(0,): 0,
(1,): 0,
(2,): 0,
(3,): 0,
(0, 1): 0,
(0, 2): 0,
(1, 2): 0,
(1, 3): 0,
(2, 3): 1,
(0, 1, 2): 0}
A.get_cohomology_aplk(1)
{(1,
0): {(0, 1, 2): 0, (0, 1): 0, (0, 2): 0, (1, 2): 0, (1, 3): 0, (2, 3): y1, (0,): 0, (1,): 0, (2,): 0, (3,): 0}}
# get the cohomology generators of a given degree S2 = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[3,4,5],[3,4,6],[3,5,6],[4,5,6]],is_mutable=False) A2 = AlgebraAplK(S2) g = A2.cohomology_gens(2) g
{(2, 0): h^{2,0}, (2, 1): h^{2,1}}
# get a representative cochain for a given cohomology generator gen = list(g.values())[0] cochain = A2.lift_cohomology_generator(gen) cochain
{(0,): 0,
(1,): 0,
(2,): 0,
(3,): 0,
(4,): 0,
(5,): 0,
(6,): 0,
(0, 1): 0,
(0, 2): 0,
(0, 3): 0,
(1, 2): 0,
(1, 3): 0,
(2, 3): 0,
(3, 4): 0,
(3, 5): 0,
(3, 6): 0,
(4, 5): 0,
(4, 6): 0,
(5, 6): 0,
(0, 1, 2): 0,
(0, 1, 3): 0,
(0, 2, 3): 0,
(1, 2, 3): 1,
(3, 4, 5): 0,
(3, 4, 6): 0,
(3, 5, 6): 0,
(4, 5, 6): 0}
phi = A2.get_cohomology_aplk(2) phi
{(2,
0): {(0, 1, 2): 0, (0, 1, 3): 0, (0, 2, 3): 0, (1, 2, 3): -2*y1*y2, (3, 4, 5): 0, (3, 4, 6): 0, (3, 5, 6): 0, (4, 5, 6): 0, (0, 1): 0, (0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): 0, (2, 3): 0, (3, 4): 0, (3, 5): 0, (3, 6): 0, (4, 5): 0, (4, 6): 0, (5, 6): 0, (0,): 0, (1,): 0, (2,): 0, (3,): 0, (4,): 0, (5,): 0, (6,): 0},
(2,
1): {(0, 1, 2): 0, (0, 1, 3): 0, (0, 2, 3): 0, (1, 2, 3): 0, (3, 4, 5): 0, (3, 4, 6): 0, (3, 5, 6): 0, (4, 5, 6): -2*y1*y2, (0, 1): 0, (0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): 0, (2, 3): 0, (3, 4): 0, (3, 5): 0, (3, 6): 0, (4, 5): 0, (4, 6): 0, (5, 6): 0, (0,): 0, (1,): 0, (2,): 0, (3,): 0, (4,): 0, (5,): 0, (6,): 0}}
Adding new methods: find a primitive of a closed element¶
methods related to find a polynomial where the differential is given¶
from sage.rings.polynomial.polydict import ETuple class SimplicialAplN(SimplicialAplN,UniqueRepresentation): class Element(SimplicialAplN.Element): """ Element class of the commutative differential multi-graded algebra (Apl)n. """ def _get_primitive_basis_of_a_monomial(self, monomial): """ Returns a basis of the primitive of a given monomial Example: """ n_vars = len(monomial)/2 combinations = [] for i in range(n_vars): mono = list(copy.deepcopy(monomial)) if mono[n_vars+i]: mono[i] += 1 mono[n_vars+i] = 0 combinations.append(tuple(mono)) return list(set(combinations)) def _add_kernel_terms_to_primitive_basis(self, basis): new_elements = [] for e in basis: monomial = list(copy.deepcopy(e)) n = len(e)/2 monomial[:n] = n*[0] new_elements.append(tuple(monomial)) return new_elements def _get_primitive_basis(self): """ Returns a basis of the primitive of 'self' """ basis = [] p = self.reduce() for monomial in p.dict().keys(): basis += p._get_primitive_basis_of_a_monomial(monomial) basis = list(set(basis)) basis += p._add_kernel_terms_to_primitive_basis(basis) basis = list(set(basis)) terms = [] apl = self.parent() for b in basis: dim = len(monomial)/2-1 terms.append(self._get_term(1, b, apl.gens())) terms.sort(reverse=False) return terms def _get_differential_basis(self): """ Returns a basis of the differential of 'self' """ primitive_basis = self._get_primitive_basis() 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 for monomial in diff.dict().keys(): term = self._get_term(1, monomial, self.parent().gens()) differential_basis.append(term) differential_basis = list(set(differential_basis)) differential_basis.sort(reverse=False) return differential_basis def _get_coordinate_vector_differential(self, diff_basis): """ Returns the coordinates of 'self' in terms of the differential basis """ p_reduced = self.reduce() #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]) for i in range(len(diff_basis)): t = diff_basis[i] term = list(t.dict().keys())[0] # there is only one monomial if term in monomials: v[i] = p_reduced.dict()[term] return v def _differential_matrix(self, primitive_basis, differential_basis): """ Returns the matrix of the differential map """ #primitive_basis = get_primitive_basis_of_a_polynomial(p.reduce()) #differential_basis = get_differential_basis_of_a_polynomial(p.reduce()) nrows = len(differential_basis) ncols = len(primitive_basis) M = matrix(QQ, nrows, ncols, nrows*ncols*[0]) for j in range(ncols): primitive = primitive_basis[j] #diff_dict = self._tuple_differential(primitive) diff_primitive = primitive.differential().reduce() 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 if term in monomials: M[i,j] = diff_primitive.dict()[term] return M def solve_primitive(self): p = self.reduce() primitive_basis = p._get_primitive_basis() diff_basis = p._get_differential_basis() 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))
dim = 5 s = SimplicialAlgebraplN(dim) s.inject_variables() p = -3*t1^2 +10* y2*t3*t4 p = p.differential() primitive_basis = p._get_primitive_basis() primitive_basis
Defining t0, t1, t2, t3, t4, t5, y0, y1, y2, y3, y4, y5
[1, y4, y3, y2, t1^2, t2*t3*y4, t2*t4*y3, t3*t4*y2]
diff_basis = p._get_differential_basis() diff_basis
[t1*y1, t2*y3*y4, t3*y2*y4, t4*y2*y3]
v = p._get_coordinate_vector_differential(diff_basis) v
(-6, 0, -10, -10)
Mdif = p._differential_matrix(primitive_basis, diff_basis) Mdif
[ 0 0 0 0 2 0 0 0] [ 0 0 0 0 0 1 -1 0] [ 0 0 0 0 0 1 0 -1] [ 0 0 0 0 0 0 1 -1]
p.solve_primitive()
-10*t2*t4*y3 - 10*t2*t3*y4 - 3*t1^2
p.solve_primitive().differential() == p
True
find an element of the preimage by the differential of an element of Alp(K)¶
class AlgebraAplK(AlgebraAplK, UniqueRepresentation): """ The graded commutative differential cochain algebra Apl(K) """ class Element(AlgebraAplK.Element): """ Element class of the simplicial commutative differential algebra (Apl)n. """ def _face_basis(self, basis, face_idx): face_basis = [] for b in basis: 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()) face_basis.append(term) face_basis = list(set(face_basis)) face_basis.sort(reverse=False) return face_basis def _get_primitive_basis(self): primitive_basis = {} S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() 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()].sort(reverse=False) # propagate the primitive basis through its faces for f in range(n+1): 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].sort(reverse=False) return primitive_basis def _diff_basis(self, basis): diff_basis = [] for b in basis: 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()) diff_basis.append(term) diff_basis = list(set(diff_basis)) diff_basis.sort(reverse=False) return diff_basis def _get_differential_basis(self, primitive_basis): differential_basis = {} S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() 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) return differential_basis def _get_differential_matrix(self, p_basis, d_basis): S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() ncols = sum([len(l) for l in p_basis.values()]) nrows = sum([len(l) for l in d_basis.values()]) M = zero_matrix(QQ, nrows, ncols) row_offset = 0 col_index = 0 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) if simplex.tuple() in p_basis.keys(): primitives = p_basis[simplex.tuple()] if simplex.tuple() in d_basis.keys(): differentials = d_basis[simplex.tuple()] if primitives and differentials: for p in primitives: 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()) if term in differentials: row_index = differentials.index(term) M[row_offset + row_index, col_index] = dic[monomial] col_index += 1 row_offset += len(differentials) elif primitives: col_index += len(primitives) elif primitives: col_index += len(primitives) return M def _get_vector_diff_coordinates(self, d_basis): len_v = sum([len(l) for l in d_basis.values()]) v = zero_vector(QQ, len_v) offset = 0 S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() 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(): differentials = d_basis[simplex] pol = self.elements[simplex].reduce() if pol and differentials: dic = pol.dict() for monomial in dic.keys(): term = pol._get_term(1, monomial, pol.parent().gens()) if term in differentials: v_index = differentials.index(term) v[offset + v_index] = dic[monomial] offset += len(differentials) return v def _get_offsets_matrix(self, p_basis): offsets = {} S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree() offset = 0 first_simplex = True 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()] if first_simplex: offsets[simplex.tuple()] = 0 first_simplex = False else: offsets[simplex.tuple()] = offset offset += len(primitives) return offsets def _get_row_face_matrix(self, primitives, face_idx, face_basis, offset_simplex, offset_face, 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 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()) if t == fb: coef = dic[monomial] 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) min_dim = self.degree() 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() 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) M = M.stack(rows) return M[1:] def _get_solution(self, sol, p_basis): S = self.parent().simplicialComplex() dim = S.dimension() min_dim = self.degree()-1 offset = 0 values = {} for n in reversed(range(dim+1)): apl = SimplicialAlgebraplN(n) for s in S._n_cells_sorted(n): simplex = s.tuple() pol = apl(0) if simplex in p_basis.keys(): primitives = p_basis[simplex] if primitives: for p in primitives: i = primitives.index(p) coef = sol[offset+i] pol += coef*p offset += len(primitives) values[simplex] = pol A = self.parent() # return values return A._element_constructor_(values) def solve_primitive(self): p_basis = self._get_primitive_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) sol = M.solve_right(v) return self._get_solution(sol, p_basis)
Example¶
S = SimplicialComplex([[0,1,2,3,4]],is_mutable=False) A = AlgebraAplK(S) s0 = SimplicialAlgebraplN(0) s1 = SimplicialAlgebraplN(1) s2 = SimplicialAlgebraplN(2) s3 = SimplicialAlgebraplN(3) s4 = SimplicialAlgebraplN(4) s2.inject_variables() p012 = y1*y2 p013 = y1*y2 p023 = y1*y2 p024 = y1*y2 p034 = y1*y2 p123 = y1*y2 p124 = y1*y2 p134 = y1*y2 p234 = y1*y2 p0123 = p012.propagate([0,1,2],3) + p123.propagate([1,2,3],3)+p013.propagate([0,1,3],3) p0134 = p013.propagate([0,1,2],3) p0124 = p012.propagate([0,1,2],3) p1234 = p123.propagate([0,1,2],3) p01234 = p012.propagate([0,1,2],4) + p123.propagate([1,2,3],4) + p013.propagate([0,1,3],4) values = {(0,): s0(0), (1,): s0(0), (2,): s0(0), (3,): s0(0), (4,): s0(0), (0,1): s1(0), (0,2): s1(0), (0,3): s1(0), (0,4): s1(0), (1,2): s1(0), (1,3): s1(0), (1,4): s1(0), (2,3): s1(0), (2,4): s1(0), (3,4): s1(0), (0,1,2): p012, (0,1,3): p013, (0,1,4): s2(0), (0,2,3): s2(0), (0,2,4): s2(0), (0,3,4): s2(0), (1,2,3): p123, (1,2,4): s2(0), (1,3,4): s2(0), (2,3,4): s2(0), (0,1,2,3): p0123,(0,1,2,4):p0124,(0,1,3,4): p0134, (0,2,3,4): s3(0),(1,2,3,4): p1234, (0,1,2,3,4): p01234} e1 = A._element_constructor_(values) dife1 = e1.differential() dife1
Defining t0, t1, t2, y0, y1, y2
{(0,): 0, (1,): 0, (2,): 0, (3,): 0, (4,): 0, (0, 1): 0, (0, 2): 0, (0, 3): 0, (0, 4): 0, (1, 2): 0, (1, 3): 0, (1, 4): 0, (2, 3): 0, (2, 4): 0, (3, 4): 0, (0, 1, 2): 0, (0, 1, 3): 0, (0, 1, 4): 0, (0, 2, 3): 0, (0, 2, 4): 0, (0, 3, 4): 0, (1, 2, 3): 0, (1, 2, 4): 0, (1, 3, 4): 0, (2, 3, 4): 0, (0, 1, 2, 3): 3*y1*y2*y3, (0, 1, 2, 4): -3*y1*y2*y3, (0, 1, 3, 4): -3*y1*y2*y3, (0, 2, 3, 4): 0, (1, 2, 3, 4): -3*y1*y2*y3, (0, 1, 2, 3, 4): 3*y1*y2*y3 - 3*y1*y2*y4 - 3*y1*y3*y4}
primitive = dife1.solve_primitive() primitive
{(0, 1, 2, 3, 4): t3*y1*y2 - t4*y1*y2 - t2*y1*y3 - t4*y1*y3 + t1*y2*y3 + t2*y1*y4 + t3*y1*y4 - t1*y2*y4 - t1*y3*y4 + y1*y2 + y1*y3, (0, 1, 2, 3): t3*y1*y2 - t2*y1*y3 + t1*y2*y3 + y1*y2 + y1*y3, (0, 1, 2, 4): -t3*y1*y2 + t2*y1*y3 - t1*y2*y3 + y1*y2, (0, 1, 3, 4): -t3*y1*y2 + t2*y1*y3 - t1*y2*y3 + y1*y2, (0, 2, 3, 4): 0, (1, 2, 3, 4): -t3*y1*y2 + t2*y1*y3 - t1*y2*y3 + y1*y2, (0, 1, 2): y1*y2, (0, 1, 3): y1*y2, (0, 1, 4): 0, (0, 2, 3): 0, (0, 2, 4): 0, (0, 3, 4): 0, (1, 2, 3): y1*y2, (1, 2, 4): 0, (1, 3, 4): 0, (2, 3, 4): 0, (0, 1): 0, (0, 2): 0, (0, 3): 0, (0, 4): 0, (1, 2): 0, (1, 3): 0, (1, 4): 0, (2, 3): 0, (2, 4): 0, (3, 4): 0, (0,): 0, (1,): 0, (2,): 0, (3,): 0, (4,): 0}
primitive.differential() == dife1
True
primitive == e1
True
Example Torus¶
# S = simplicial_complexes.Torus() # A = AlgebraAplK(S) # s0 = SimplicialAlgebraplN(0) # s1 = SimplicialAlgebraplN(1) # s2 = SimplicialAlgebraplN(2) # s2.inject_variables() # values = {(0, 1, 2): s2(0), (0, 1, 5): s2(0), (0, 2, 6): s2(0), (0, 3, 4): s2(0), (0, 3, 5): s2(0), # (0, 4, 6): s2(0), (1, 2, 4): s2(0), (1, 3, 4): s2(0), (1, 3, 6): -y1*y2, (1, 5, 6): s2(0), # (2, 3, 5): s2(0), (2, 3, 6): s2(0), (2, 4, 5): y1*y2, (4, 5, 6): s2(0), # (0, 1): s1(0), (0, 2): s1(0), (0, 3): s1(0), (0, 4): s1(0), (0, 5): s1(0), (0, 6): s1(0), # (1, 2): s1(0), (1, 3): s1(0), (1, 4): s1(0), (1, 5): s1(0), (1, 6): s1(0), (2, 3): s1(0), (2, 4): s1(0), # (2, 5): s1(0), (2, 6): s1(0), (3, 4): s1(0), (3, 5): s1(0), (3, 6): s1(0), (4, 5): s1(0), (4, 6): s1(0), (5, 6): s1(0), # (0,): s0(0), (1,): s0(0), (2,): s0(0), (3,): s0(0), (4,): s0(0), (5,): s0(0), (6,): s0(0)} # e = A._element_constructor_(values) # preimage = e.solve_primitive()
Test with random complexes and random elements of Apl(K)¶
def sublist(lst1, lst2): return set(lst1) <= set(lst2) def sublist_of_lists(list1,list_of_lists): is_sublist = False for list2 in list_of_lists: if sublist(list1,list2): is_sublist = True break return is_sublist def random_simplicial_complex(num_vertices,max_num_facets): vertices = list(range(num_vertices)) simplices = [] partitions = [] for length in range(num_vertices): partitions += [tuple(e) for e in powerset(vertices) if len(e)==length and e] for i in range(max_num_facets): r = randint(0,len(partitions)-1) candidate = partitions[r] if not sublist_of_lists(candidate,simplices): simplices.append(candidate) return SimplicialComplex(simplices,is_mutable=False) def random_cochain(S): dim = S.dimension() cochain = {} for n in range(dim+1): for s in S._n_cells_sorted(n): cochain[s.tuple()] = randint(-100,100) return cochain
S = random_simplicial_complex(6,4) # S = SimplicialComplex([5],is_mutable=False) cochain = random_cochain(S) S
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5) and facets {(0, 4, 5), (1, 2, 5), (0, 1, 2, 3)}
A = AlgebraAplK(S) e = A.lift_cochain(cochain) dife = e.differential()
hom_parts = dife.homogeneous_parts() recover = A.null_element() for deg in hom_parts.keys(): print("deg: ", deg) recover += hom_parts[deg].solve_primitive() print("dif(recover) == dif(e): ", recover.differential() == dife) # print("recover == e: ", recover == e) # Although their differentials are equal, this is not always True
deg: 1 deg: 2 deg: 3 dif(recover) == dif(e): True
Example¶
S = SimplicialComplex([[0,1,2,3]],is_mutable=False) A = AlgebraAplK(S) s0 = SimplicialAlgebraplN(0) s1 = SimplicialAlgebraplN(1) s2 = SimplicialAlgebraplN(2) s3 = SimplicialAlgebraplN(3) s3.inject_variables() p0123 = y1*y2*y3 values = {(0,): s0(0), (1,): s0(0), (2,): s0(0), (3,): s0(0), (0,1): s1(0), (0,2): s1(0), (0,3): s1(0), (1,2): s1(0), (1,3): s1(0), (2,3): s1(0), (0,1,2): s2(0), (0,1,3): s2(0), (0,2,3): s2(0), (1,2,3): s2(0), (0,1,2,3): p0123} e1 = A._element_constructor_(values) print("") print("e1:") print(e1) print("") e2 = e1.solve_primitive() print("e2") print(e2) print("") print("d(e2) == e1: ", e2.differential() == e1)
Defining t0, t1, t2, t3, y0, y1, y2, y3
e1:
{(0,): 0, (1,): 0, (2,): 0, (3,): 0, (0, 1): 0, (0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): 0, (2, 3): 0, (0, 1, 2): 0, (0, 1, 3): 0, (0, 2, 3): 0, (1, 2, 3): 0, (0, 1, 2, 3): y1*y2*y3}
e2
{(0, 1, 2, 3): 1/3*t3*y1*y2 - 1/3*t2*y1*y3 + 1/3*t1*y2*y3 - 1/3*y1*y2, (0, 1, 2): -1/3*y1*y2, (0, 1, 3): 0, (0, 2, 3): 0, (1, 2, 3): 0, (0, 1): 0, (0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): 0, (2, 3): 0, (0,): 0, (1,): 0, (2,): 0, (3,): 0}
d(e2) == e1: True
Example of degree 0¶
S = SimplicialComplex([[0,1,2], [0,1,3]],is_mutable=False) A = AlgebraAplK(S) s0 = SimplicialAlgebraplN(0) s2 = SimplicialAlgebraplN(2) s2.inject_variables() p012 = y0 p013 = y0 values = {(0,): s0(0), (1,): s0(0), (2,): s0(0), (3,): s0(0), (0,1): p012.face(2), (0,2): p012.face(1), (0,3): p013.face(1), (1,2): p012.face(0), (1,3): p013.face(0), (0,1,2): p012, (0,1,3): p013} e1 = A._element_constructor_(values) e2 = e1.solve_primitive() e2.differential() == e1
Defining t0, t1, t2, y0, y1, y2
True
Example¶
S = SimplicialComplex([[0,1,2,3]],is_mutable=False) A = AlgebraAplK(S) s0 = SimplicialAlgebraplN(0) s1 = SimplicialAlgebraplN(1) s2 = SimplicialAlgebraplN(2) s3 = SimplicialAlgebraplN(3) s3.inject_variables() p0123 = y1*y2*y3 values = {(0,): s0(0), (1,): s0(0), (2,): s0(0), (3,): s0(0), (0,1): s1(0), (0,2): s1(0), (0,3): s1(0), (1,2): s1(0), (1,3): s1(0), (2,3): s1(0), (0,1,2): s2(0), (0,1,3): s2(0), (0,2,3): s2(0), (1,2,3): s2(0), (0,1,2,3): p0123} e1 = A._element_constructor_(values) primitive = e1.solve_primitive() primitive.differential() == e1
Defining t0, t1, t2, t3, y0, y1, y2, y3
True
Another example¶
S = SimplicialComplex([[0,1,2,3,4]],is_mutable=False) A = AlgebraAplK(S) s0 = SimplicialAlgebraplN(0) s1 = SimplicialAlgebraplN(1) s2 = SimplicialAlgebraplN(2) s3 = SimplicialAlgebraplN(3) s4 = SimplicialAlgebraplN(4) s2.inject_variables() p012 = y1*y2 p013 = y1*y2 p023 = y1*y2 p024 = y1*y2 p034 = y1*y2 p123 = y1*y2 p124 = y1*y2 p134 = y1*y2 p234 = y1*y2 p0123 = p012.propagate([0,1,2],3) + p123.propagate([1,2,3],3)+p013.propagate([0,1,3],3) p0134 = p013.propagate([0,1,2],3) p0124 = p012.propagate([0,1,2],3) p1234 = p123.propagate([0,1,2],3) p01234 = p012.propagate([0,1,2],4) + p123.propagate([1,2,3],4) + p013.propagate([0,1,3],4) values = {(0,): s0(0), (1,): s0(0), (2,): s0(0), (3,): s0(0), (4,): s0(0), (0,1): s1(0), (0,2): s1(0), (0,3): s1(0), (0,4): s1(0), (1,2): s1(0), (1,3): s1(0), (1,4): s1(0), (2,3): s1(0), (2,4): s1(0), (3,4): s1(0), (0,1,2): p012, (0,1,3): p013, (0,1,4): s2(0), (0,2,3): s2(0), (0,2,4): s2(0), (0,3,4): s2(0), (1,2,3): p123, (1,2,4): s2(0), (1,3,4): s2(0), (2,3,4): s2(0), (0,1,2,3): p0123,(0,1,2,4):p0124,(0,1,3,4): p0134, (0,2,3,4): s3(0),(1,2,3,4): p1234, (0,1,2,3,4): p01234} e1 = A._element_constructor_(values) e1
Defining t0, t1, t2, y0, y1, y2
{(0,): 0, (1,): 0, (2,): 0, (3,): 0, (4,): 0, (0, 1): 0, (0, 2): 0, (0, 3): 0, (0, 4): 0, (1, 2): 0, (1, 3): 0, (1, 4): 0, (2, 3): 0, (2, 4): 0, (3, 4): 0, (0, 1, 2): y1*y2, (0, 1, 3): y1*y2, (0, 1, 4): 0, (0, 2, 3): 0, (0, 2, 4): 0, (0, 3, 4): 0, (1, 2, 3): y1*y2, (1, 2, 4): 0, (1, 3, 4): 0, (2, 3, 4): 0, (0, 1, 2, 3): t3*y1*y2 - t2*y1*y3 + t1*y2*y3 + y1*y2 + y1*y3, (0, 1, 2, 4): -t3*y1*y2 + t2*y1*y3 - t1*y2*y3 + y1*y2, (0, 1, 3, 4): -t3*y1*y2 + t2*y1*y3 - t1*y2*y3 + y1*y2, (0, 2, 3, 4): 0, (1, 2, 3, 4): -t3*y1*y2 + t2*y1*y3 - t1*y2*y3 + y1*y2, (0, 1, 2, 3, 4): t3*y1*y2 - t4*y1*y2 - t2*y1*y3 - t4*y1*y3 + t1*y2*y3 + t2*y1*y4 + t3*y1*y4 - t1*y2*y4 - t1*y3*y4 + y1*y2 + y1*y3}
dife1 = e1.differential() #dife1
recovery = dife1.solve_primitive() print("dif(recovery) == diff(e1): ",recovery.differential() == dife1) #recovery
dif(recovery) == diff(e1): True
Minimal model¶
def update_cdga(gca, diff_dict={}): gens_dict = gca.gens_dict() differentials = {} if diff_dict: for gen_name in diff_dict.keys(): gen = gens_dict[gen_name] product_gens = diff_dict[gen_name] gen0 = gens_dict[product_gens[0]] if type(product_gens[1]) == int: # is a power power = product_gens[1] mult = gen0 for i in range(1, power): mult *= gen0 differentials.update({gen:mult}) else: gen1 = gens_dict[product_gens[1]] differentials.update({gen:gen0*gen1}) # print("differentials", differentials) return gca.cdg_algebra(differentials) def has_preimage(M, element): d = M.differential() deg = element.degree()-1 # print("differential of ", element, "degree: ", deg) Md = d.differential_matrix(deg) gens = M.basis(deg) try: solution = Md.solve_left(vector(QQ,element.basis_coefficients())) primitive = sum(map(lambda coef, gen: coef*gen, solution, gens)) print(element, " has preimage ", primitive) return True except ValueError: return False def injectivity_step(M, phi, check_dim, names, degrees, differentials, index=0, depth=0, max_depth=5): print("Injectivity at dimension ", check_dim) if depth > max_depth: return [M, phi, names, degrees, differentials, True] deg = check_dim -1 gens = [g for g in M.gens() if g.degree() < check_dim] # gens.sort(reverse=True) gen_products = [] for i in range(len(gens)): e0 = gens[i] d = e0.degree() mcd = gcd(d, check_dim) if d%2 == 0 and mcd == d: power = True exp = check_dim / d mult = e0 for j in range(1,exp-1): mult *= e0 gen_products.append([e0, mult, power, exp]) for j in range(i+1,len(gens)): power = False e1 = gens[j] if e0.degree() + e1.degree() == check_dim: gen_products.append([e0, e1, power]) # print("products at dim ",check_dim,": ", gen_products) new_M = M new_phi = phi new_names = names new_degrees = degrees new_differentials = differentials for pair in gen_products: # Looking at the minimal model side gen0 = pair[0] deg_0 = gen0.degree() gen1 = pair[1] deg_1 = gen1.degree() M_product = gen0*gen1 # Looking at the AplK side gen0_name = list(M.gens_dict().keys())[list(M.gens_dict().values()).index(gen0)] aplk_gen0 = phi[deg_0][gen0_name] is_power = pair[2] power = 0 if is_power: power = pair[3] aplk_gen1 = aplk_gen0 for i in range(1, power): aplk_gen1 *= aplk_gen0 else: gen1_name = list(M.gens_dict().keys())[list(M.gens_dict().values()).index(gen1)] aplk_gen1 = phi[deg_1][gen1_name] aplk_product = aplk_gen0*aplk_gen1 A = aplk_product.parent() # Check conditions if (M_product == M(0)) and (aplk_product == A.null_element()): # zero element pass elif (M_product == M(0)) and (aplk_product != A.null_element()): #check if aplk_product.is_closed(): pass #trivial cohomology class (assuming aplk_product has preimage by differential) else: raise ValueError('zero associated to a non-closed element in AplK') elif (M_product.differential() != 0): # is not closed print("The element ",M_product, " is not closed") pass elif (M_product.differential() == 0) and aplk_product.is_closed() and has_preimage(M, M_product): pass #trivial cohomology class (assuming aplk_product has preimage by differential) elif (M_product.differential() == 0) and aplk_product.is_closed(): #key step because M_product doesn't have preimage new_gen = 'y'+str(deg)+'_'+str(index) if aplk_product != A.null_element(): try: preimage = aplk_product.solve_primitive() except ValueError: print(" The element ",M_product, " has non trivial cohomology class. No generator added.") continue # we don't add a new generator because the element has non trivial cohomology class phi_new_gen = {new_gen: preimage} else: phi_new_gen = {new_gen: A.null_element()} print(" Proceed to kill", M_product, " because has associated trivial cohomology class") print("Adding generator "+new_gen) new_names = names + [new_gen] new_degrees = tuple(list(degrees)+[deg]) new_differentials = copy.deepcopy(differentials) if not is_power: new_differentials.update({new_gen: [gen0_name, gen1_name]}) else: new_differentials.update({new_gen: [gen0_name, int(power)]}) gca = GradedCommutativeAlgebra(QQ, names=new_names, degrees=new_degrees) new_M = update_cdga(gca, new_differentials) new_phi = copy.deepcopy(phi) d = new_phi[deg] d.update(phi_new_gen) return injectivity_step(new_M, new_phi, check_dim, new_names, new_degrees, new_differentials, index=index+1, depth=depth+1, max_depth=max_depth) else: pass 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()) unmmaped_elements = copy.deepcopy(coho_aplk) for gen_key in coho_gens.keys(): gen = coho_gens[gen_key] found = False for i in range(1,dim): if (not found) and (i in dimensions): 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) 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)] if gen_key in unmmaped_elements.keys(): 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) new_names = copy.deepcopy(names) new_degrees = copy.deepcopy(degrees) new_phi = copy.deepcopy(phi) new_correspondence = copy.deepcopy(cohomology_correspondence) phi_n_map = {} correspondence_n_map = {} for gen_key in g.keys(): deg = gen_key[0] index = gen_key[1] new_var = 'x'+str(deg)+'_'+str(index) new_names += [new_var] new_degrees += [deg] 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 = update_cdga(gca, 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") phi = {} cohomology_correspondence = {} A = AlgebraAplK(S) # First step of the algorithm first_dim = A.first_cohomology_group() phi_n_map = {} correspondence_n_map = {} if first_dim < 0: return [0,{},{}] # due to the function returns multiple objects elif first_dim > max_dim: return [0,{},{}] # due to the function returns multiple objects g = A.get_cohomology_aplk(first_dim) cohomology_gens = A.cohomology_gens(first_dim) names = [] degrees = [] for gen_key in g.keys(): deg = gen_key[0] index = gen_key[1] new_var = 'x'+str(deg)+'_'+str(index) names += [new_var] degrees += [deg] phi_n_map[new_var] = g[gen_key] correspondence_n_map[new_var] = cohomology_gens[gen_key] 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, index=0, depth=0, max_depth=5) if limit: 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): [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, index=0, depth=0, max_depth=5) if limit: print("The algorithm has reach the injectivity depth-limit at dimension", dim) break return [M, phi, cohomology_correspondence] # def minimal_model(): # names = ['x1_0','x1_1'] # degrees = (1,1) # differentials={} # gca = GradedCommutativeAlgebra(QQ, names=names, degrees=degrees) # cdga = gca.cdg_algebra(differentials) # print(cdga) # # añadimos el generador de grado uno con differencial producto de los anteriores # new_gen = 'y1_0' # names = names + [new_gen] # deg = 1 # degrees = tuple(list(degrees)+[deg]) # differentials.update({'y1_0':['x1_0','x1_1']}) # gca = GradedCommutativeAlgebra(QQ, names=names, degrees=degrees) # new_cdga = update_cdga(gca, differentials) # print(new_cdga)
# S = SimplicialComplex([[0,1],[0,2],[1,2]],is_mutable=False) # S^1 # S = SimplicialComplex([[0,1],[0,2],[1,2],[1,3],[2,3]],is_mutable=False) # S^1 wedge S^1 # S = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3]],is_mutable=False) # S^2 # S = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[3,4],[3,5],[4,5]],is_mutable=False) # S^1 wedge S^2 # S = SimplicialComplex([[0,1,2],[0,3,4],[1,3,5]],is_mutable=False) # S^1 surrounded by 2-cells # S = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[3,4,5],[3,4,6],[3,5,6],[4,5,6]],is_mutable=False) # S^2 wedge S^2 # S = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[0,2,4],[0,3,4],[2,3,4]],is_mutable=False) # S^2 wedge S^2 # S = SimplicialComplex([[0,2,3],[0,2,4,5],[0,1,2,5],[1,2,3,6],[2,3,4,6],[0,1,3,7],[0,3,4,7]],is_mutable=False) # S^2 wedge S^2 # S = simplicial_complexes.Torus() ####################################################### # S = simplicial_complexes.Sphere(2) ####################################################### # S = simplicial_complexes.K3Surface() ####################################################### S1 = simplicial_complexes.Sphere(2) S2 = simplicial_complexes.Sphere(2) S = S1.wedge(S2, is_mutable=False) ####################################################### #S = simplicial_complexes.ComplexProjectivePlane() ####################################################### # S = simplicial_complexes.BrucknerGrunbaumSphere() ####################################################### # S = simplicial_complexes.ProjectivePlane().wedge(simplicial_complexes.Sphere(2), is_mutable=False) ####################################################### [sullivan_model, model_morphism, cohomology_correspondence] = minimal_model(S, max_depth=5, max_dim=10) sullivan_model
Injectivity at dimension 3 Surjectivity at dimension 3 Injectivity at dimension 4 Proceed to kill x2_0^2 because has associated trivial cohomology class Adding generator y3_0 Injectivity at dimension 4 x2_0^2 has preimage y3_0 Proceed to kill x2_0*x2_1 because has associated trivial cohomology class Adding generator y3_1 Injectivity at dimension 4 x2_0^2 has preimage y3_0 x2_0*x2_1 has preimage y3_1 Proceed to kill x2_1^2 because has associated trivial cohomology class Adding generator y3_2 Injectivity at dimension 4 x2_0^2 has preimage y3_0 x2_0*x2_1 has preimage y3_1 x2_1^2 has preimage y3_2 Surjectivity at dimension 4 Injectivity at dimension 5 The element x2_0*y3_0 is not closed The element x2_0*y3_1 is not closed The element x2_0*y3_2 is not closed The element x2_1*y3_0 is not closed The element x2_1*y3_1 is not closed The element x2_1*y3_2 is not closed Surjectivity at dimension 5 Injectivity at dimension 6 x2_0^3 has preimage x2_0*y3_0 x2_1^3 has preimage x2_1*y3_2 The element y3_0*y3_1 is not closed The element y3_0*y3_2 is not closed The element y3_1*y3_2 is not closed Surjectivity at dimension 6 Injectivity at dimension 7 Surjectivity at dimension 7 Injectivity at dimension 8 x2_0^4 has preimage x2_0^2*y3_0 x2_1^4 has preimage x2_1^2*y3_2 Surjectivity at dimension 8 Injectivity at dimension 9 Surjectivity at dimension 9 Injectivity at dimension 10 x2_0^5 has preimage x2_0^3*y3_0 x2_1^5 has preimage x2_1^3*y3_2
Commutative Differential Graded Algebra with generators ('x2_0', 'x2_1', 'y3_0', 'y3_1', 'y3_2') in degrees (2, 2, 3, 3, 3) over Rational Field with differential:
x2_0 --> 0
x2_1 --> 0
y3_0 --> x2_0^2
y3_1 --> x2_0*x2_1
y3_2 --> x2_1^2
sullivan_model.basis(6)
[x2_0^3, x2_0^2*x2_1, x2_0*x2_1^2, x2_1^3, y3_0*y3_1, y3_0*y3_2, y3_1*y3_2]
sullivan_model.cohomology_generators(10)
{2: [x2_0, x2_1], 5: [x2_1*y3_0 - x2_0*y3_1, x2_1*y3_1 - x2_0*y3_2]}
a = model_morphism[2]['x2_0']^2 b = model_morphism[3]['y3_0'] # no deberia salir 0
a.solve_primitive()
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-109-21949880a51d> in <module> ----> 1 a.solve_primitive() <ipython-input-88-ffaf432dae2a> in solve_primitive(self) 214 215 def solve_primitive(self): --> 216 p_basis = self._get_primitive_basis() 217 d_basis = self._get_differential_basis(p_basis) 218 diff_matrix = self._get_differential_matrix(p_basis, d_basis) <ipython-input-88-ffaf432dae2a> in _get_primitive_basis(self) 35 primitive_basis[simplex.tuple()] = [] 36 # add primitive elements related to the assigned polynomial ---> 37 pol = self.elements[simplex.tuple()] 38 primitive_basis[simplex.tuple()] += pol._get_primitive_basis() 39 primitive_basis[simplex.tuple()] = list(set(primitive_basis[simplex.tuple()])) KeyError: ()
S1 = simplicial_complexes.Sphere(2) S2 = simplicial_complexes.Sphere(2) S = S1.wedge(S2, is_mutable=False) A = AlgebraAplK(S)
S.cohomology_ring().basis()
dim = 5 # Get the basis of Hm hm_basis = sullivan_model.cohomology(dim).basis() Hc = A.cohomology_group(dim) apl_correspondence = {} cochain_correspondence = {} Hc_correspondence = {} M = zero_matrix(QQ,len(Hc.basis()),len(hm_basis)) i = 0 for gen in list(hm_basis.keys()): # get an element of the basis of Hm # get a representative of a cohomology class in M gen_repr = gen.representative() # get the apl representative for each generator of M^p m_gens = gen_repr.parent().gens() aplgens = [model_morphism[g.degree()][str(g)] for g in m_gens] apl_correspondence[gen] = A.null_element() for exp in gen_repr.dict().keys(): coef = gen_repr.dict()[exp] term = coef*prod([gen^e for gen,e in zip(aplgens, exp)]) apl_correspondence[gen] += term cochain = apl_correspondence[gen].integral() cochain_correspondence[gen] = cochain vcochain = A.cochain_to_vector(cochain,dim) Hc_correspondence[gen] = Hc(vcochain) M[:,i] = Hc_correspondence[gen] i+=1 Hc_correspondence # falta crear la matriz
show(M)
gdca_basis = gen_repr.parent().basis(gen_repr.degree()) gdca_basis
print(gen_repr.dict()) gen_repr.parent().gens()
Hc_2 = A.cohomology_group(2).basis() #A.lift_cochain(A.vector_to_cochain(Hc_2.lift((0,1)),2)) Hc_2
element = Hm_basis[2][0] + Hm_basis[2][1] def integralPhiCohomology(element): aplelement = model_morphism[2][str(element)] cochain = aplelement.integral() vcochain = A.cochain_to_vector(cochain,2) Hc_2 = A.cohomology_group(2) return Hc_2(vcochain) integralPhiCohomology(element)
Hc_basis = A.cohomology_gens(2) cochain0 = A.lift_cohomology_generator(list(Hc_basis.values())[0]) cochain1 = A.lift_cohomology_generator(list(Hc_basis.values())[1]) vcochain0 = A.cochain_to_vector(cochain0,2) vcochain1 = A.cochain_to_vector(cochain1,2) vcochain = vcochain0-3*vcochain1
Hc_2 = A.cohomology_group(2) Hc_2(vcochain)
Example step by step¶
K = simplicial_complexes.ComplexProjectivePlane() A = AlgebraAplK(K)
# get the lowest dim for which H^{dim} is not trivial first_dim = A.first_cohomology_group() first_dim
g = A.get_cohomology_aplk(first_dim)
g = A.get_cohomology_aplk(first_dim) cohomology_gens = A.cohomology_gens(first_dim)
cohomology_gens
names = [] degrees = [] phi = {} phi_n_map = {} cohomology_correspondence = {} correspondence_n_map = {} for gen_key in g.keys(): deg = gen_key[0] index = gen_key[1] new_var = 'x'+str(deg)+'_'+str(index) names += [new_var] degrees += [deg] phi_n_map[new_var] = g[gen_key] correspondence_n_map[new_var] = cohomology_gens[gen_key] 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
r = phi[2]['x2_0']
count = 0 for s in r.elements.keys(): if len(s)==5: v = r.elements[s] if v != 0: count += 1 print(s, " ==> ", v) print(count)
r = (phi[2]['x2_0']^2)
count = 0 for s in r.elements.keys(): if len(s)==5: v = r.elements[s] if v != 0: count += 1 print(s, " ==> ", v) print(count)
M.cohomology(4)
A.cohomology_gens(4)
c = M.cohomology(4) c
K.cohomology(4,base_ring=QQ)
M
dim = 2 # Get the basis of Hm hm_basis = M.cohomology(dim).basis() Hc = A.cohomology_group(dim) apl_correspondence = {} cochain_correspondence = {} Hc_correspondence = {} Mat = zero_matrix(QQ,len(Hc.basis()),len(hm_basis)) i = 0 for gen in list(hm_basis.keys()): # get an element of the basis of Hm # get a representative of a cohomology class in M gen_repr = gen.representative() # get the apl representative for each generator of M^p m_gens = gen_repr.parent().gens() aplgens = [phi[g.degree()][str(g)] for g in m_gens] apl_correspondence[gen] = A.null_element() for exp in gen_repr.dict().keys(): coef = gen_repr.dict()[exp] term = coef*prod([gen^e for gen,e in zip(aplgens, exp)]) apl_correspondence[gen] += term cochain = apl_correspondence[gen].integral() cochain_correspondence[gen] = cochain vcochain = A.cochain_to_vector(cochain,dim) Hc_correspondence[gen] = Hc(vcochain) Mat[:,i] = Hc_correspondence[gen] i+=1 Hc_correspondence
M.cohomology(2)
K.cohomology(5,base_ring=QQ)
gca = GradedCommutativeAlgebra(QQ, names=['x2_0','y5_0'], degrees=(2,5)) differentials = {gca.gens()[1]:gca.gens()[0]^3} M = gca.cdg_algebra(differentials)