AplK/notebooks/todo.ipynb

73 KiB

In [1]:
from sage.structure.parent import Parent
from sage.structure.element import Element
#from sage.structure.unique_representation import UniqueRepresentation
#from sage.categories.rings import Rings
from sage.structure.richcmp import rich_to_bool, op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE
#from sage.categories.morphism import Morphism
#from sage.rings.rational_field import QQ
#from sage.algebras.commutative_dga import GradedCommutativeAlgebra
#from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
#from sage.functions.other import factorial
import itertools
import copy
import random

Simplicial Complex

In [2]:
S1 = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[3,4],[3,5],[4,5]],is_mutable=False) 
In [3]:
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) 
In [4]:
def cohomology_gens(S,n):
    gens = {}
    basis = S.cohomology_ring().basis()
    for k in basis.keys():
        if k[0] == n:
            gens[k] = basis[k]
    return gens
In [5]:
gens = cohomology_gens(S2,2)
gens
Out[5]:
{(2, 0): h^{2,0}, (2, 1): h^{2,1}}
In [6]:
v = gens[(2,1)]
c = v.to_cycle()
c
Out[6]:
\chi_(4, 5, 6)
In [7]:
def lift_cohomology_generator(S, g):
    cycle = g.to_cycle()
    cochain = {}
    for n in range(S.dimension()+1):
        for simplex in S._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
In [8]:
lift_cohomology_generator(S2,gens[(2,1)])
Out[8]:
{(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): 0,
 (3, 4, 5): 0,
 (3, 4, 6): 0,
 (3, 5, 6): 0,
 (4, 5, 6): 1}

Apln

In [2]:
class Apln(UniqueRepresentation):

    def __init__(self,n):
        self.n = n
        self.algebras = []
        self.relations = []
        for i in range(n+1):
            [cdga, relations] = self._cdga(i)
            self.algebras.append(cdga)
            self.relations.append(relations)
  
    def _cdga(self,n):
        ts = ['t'+str(t) for t in range(n+1)]
        ys = ['y'+str(t) for t in range(n+1)]
        degrees = tuple((n+1)*[(0,2)])+tuple((n+1)*[(1,2)])
        gca = GradedCommutativeAlgebra(QQ, names=ts+ys, degrees=degrees)
        t_relation = -1
        y_relation = 0
        #t_relation = sum(self.gca.gens()[:n])-1
        #y_relation = sum(self.gca.gens()[n:])
        for i in range(n+1):
            t_relation += gca.gens()[i]
            y_relation += gca.gens()[n+1+i]
        #gca_with_relations = self.gca.quotient(self.gca.ideal(t_relation,y_relation),check=False)
        dfs = {gca.gens_dict()[ts[i]]:gca.gens_dict()[ys[i]] for i in range(n+1)}
        return [gca.cdg_algebra(dfs),[t_relation,y_relation]]
    
    def __repr__(self):
        return "Simplicial algebra of dimension %s" % self.n
    
    def inject_variables(self, n):
        self.algebras[n].inject_variables()
      
    def generators(self, n):
        return self.algebras[n].gens()
    
    def get_multidegree(self, p):
        a3 = p.parent()
        n = len(a3.gens())/2
        multidegrees = {}
        for k in p.dict().keys():
            multideg = vector(QQ,n*[0])
            for i in range(n):
                multideg[i] = k[i]+k[i+n]
            multidegrees[k] = multideg
        return multidegrees
        
    def face(self,i,n):

        if(self.n==0):
            return 0
        domain = self.algebras[n]
        codomain = self.algebras[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 reduce_polynomial(self,p):
        ## Input:
        #  - p: polynomial in variables t_0,..t_n,y_0,...,y_n
        ## Output:
        #  - The polynomial after applying the corresponding relations to reduce the variables t_0, y_0
        apl = p.parent()
        dim = len(apl.gens())//2-1
        return p.reduce(self.relations[dim])
    
    def _convert_integrable_polynomial(self,p):
        ## Input:
        #  - p: polynomial in variables t_1,..t_n,y_1,...,y_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 = p.parent()
        n = len(apl.gens())//2
        pol = self.reduce_polynomial(p)
        relations = []
        for i in range(n): 
            relations.append(apl.gens()[n+i]-1)
        return pol.reduce(relations)
        
    def integral(self,p):
        ## Input:
        #  - p: polynomial in variables t_0,..t_n,y_0,...,y_n
        ## Output:
        #  - the integral of the polynomial after reducing the variables t_0, y_0 by the corresponding relations 
        #
        apl = p.parent()
        n = len(apl.gens())//2-1
        pol = self._convert_integrable_polynomial(p)
        result = 0
        for k in pol.dict().keys(): # k is a monomial
            numerator = 1
            denominator = n
            for e in k: # e is an exponents
                numerator *= factorial(e)
                denominator += e
            coef = pol.dict()[k]
            result += coef*numerator/factorial(denominator)
        return ((-1)**(n*(n-1)/2))*result
        
    def lift(self, value, n): 
        apl = self.algebras[n]
        gens = apl.gens()
        return value*((-1)**(n*(n-1)/2))*factorial(n)*prod(gens[n+2:])
    
    def homogeneous_part(self,pol,degree):
        parts = [pol.homogeneous_parts()[k] for k in pol.homogeneous_parts() if list(k)[0]==degree]
        return sum(parts)
    
    def propagate(self, face_list, n):
        """
         - face_list: indexes of the elements that form the face of the simplex we want to propagate
         - EXAMPLE: p = a.propagate([0,3,4],5)
                    t4*y0*y3 - t3*y0*y4 + t0*y3*y4
        """
        simplex_dim = len(face_list)-1
        indexes = list(range(simplex_dim+1))
        apl = self.algebras[n]
        y_gens = [apl.gens()[n+1+i] for i in face_list]
        t_gens = [apl.gens()[i] for i in face_list]
        result = 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)
            result += ((-1)**i)*t*y_prod#((-1)**(simplex_dim+i))*t*y_prod
        return result
In [3]:
# Funciones añadidas
In [4]:
    def get_multidegree(self, p):
        a3 = p.parent()
        n = len(a3.gens())/2
        multidegrees = {}
        for k in p.dict().keys():
            multideg = vector(QQ,n*[0])
            for i in range(n):
                multideg[i] = k[i]+k[i+n]
            multidegrees[k] = multideg
        return multidegrees
        
In [ ]:
 
In [5]:
a = Apln(5)
a.inject_variables(3)
p = t2*y0*y1-t1*y0*y2+t0*y1*y2
q = a.face(1,3)(p)
ared = a.reduce_polynomial(q)
ared
Defining t0, t1, t2, t3, y0, y1, y2, y3
Out[5]:
0

Empiezan celdas de pruebas

In [6]:
p = 5*t2^2*y0*y1-t1*y0*y2+t0*y1*y2
p.dict()
Out[6]:
{(0, 0, 2, 0, 1, 1, 0, 0): 5,
 (0, 1, 0, 0, 1, 0, 1, 0): -1,
 (1, 0, 0, 0, 0, 1, 1, 0): 1}
In [ ]:
 
In [7]:
apl = Apln(5)
apl.inject_variables(3)
d=1
c=d
b=2*d-1/2
a=2*d-1/2
w1=a*(t0*t1^2*t2*t3*y0)+b*(t0^2*t1*t2*t3*y1)+c*(t0^2*t1^2*t3*y2)+d*(t0^2*t1^2*t2*y3)
d=5
c=d
b=2*d-1/2
a=2*d-1/2
w2=a*(t0*t1^2*t2*t3*y0)+b*(t0^2*t1*t2*t3*y1)+c*(t0^2*t1^2*t3*y2)+d*(t0^2*t1^2*t2*y3)
w1.differential()==w2.differential()
Defining t0, t1, t2, t3, y0, y1, y2, y3
Out[7]:
True
In [8]:
m0 = p.monomials()
m0
Out[8]:
[t2^2*y0*y1, t1*y0*y2, t0*y1*y2]
In [9]:
p = (5*t2^2 + 5)*y0*y1
p
Out[9]:
5*t2^2*y0*y1 + 5*y0*y1
In [10]:
p = 5*t2^2*y0*y1-t1*y0*y2+t0*y1*y2
p.dict()
Out[10]:
{(0, 0, 2, 0, 1, 1, 0, 0): 5,
 (0, 1, 0, 0, 1, 0, 1, 0): -1,
 (1, 0, 0, 0, 0, 1, 1, 0): 1}
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [11]:
def get_term(coef, exp, gens):
    return coef*prod(list(map(lambda exp, gen: gen^exp, exp, gens)))

def get_terms(p):
    a = p.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 = p.dict()
    for exp in pol.keys():
        key =  get_term(1, exp,ys_only_gens)
        if key not in terms.keys():
            coef = get_term(pol[exp],exp,ts_only_gens)
            terms[key] = coef
        else:
            coef = get_term(pol[exp],exp,ts_only_gens)
            terms[key] += coef    
    return terms
    
In [12]:
p = 5*t2^2*y0*y1-t1*y0*y2+t0*y1*y2+3*y0*y1
print(p)
terms = get_terms(p)
terms
5*t2^2*y0*y1 - t1*y0*y2 + t0*y1*y2 + 3*y0*y1
Out[12]:
{y0*y1: 5*t2^2 + 3, y0*y2: -t1, y1*y2: t0}
In [13]:
# comprobamos que la suma de los términos nos da el polinomio original
p2 = sum([terms[key]*key for key in terms.keys()])
p2==p
Out[13]:
True
In [81]:
        
    
Out[81]:
3
In [80]:
lead_term = get_leading_term(p)
symbolic_partial_integral(lead_term, 0)
Out[80]:
{y1: 5*t0*t2^2 + 3*t0}
In [114]:
def get_leading_pol(pol):
    terms = get_terms(pol)
    keys = list(terms.keys())
    keys.sort(reverse=True)
    return terms[keys[0]]*keys[0]

def get_integration_idx(exp):
    # gets the idx 'i' of the first generator 'y_i'  that appears in exp
    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 symbolic_partial_monomial_integral(monomial, idx):
    exp = list(monomial.dict().keys())[0]
    coef = monomial.dict()[exp]
    n = len(exp)/2
    if (exp[n+idx]==0):
        raise IndexError("El término no se puede integrar respecto a esa variable")
    integral = list(copy.deepcopy(exp))
    integral[n+idx] = 0
    integral[idx] += 1
    new_coef = coef/integral[idx]
    a = monomial.parent()
    return get_term(new_coef, integral, a.gens())
    
def symbolic_partial_integral(polynomial):
    # necessary condition: all monomials have apl.gens[n+integration_idx] factor
    exp = list(polynomial.dict().keys())[0]
    integration_idx = get_integration_idx(exp)
    aGens = polynomial.parent().gens()
    integral = 0
    for exp in polynomial.dict().keys():
        coef = polynomial.dict()[exp]
        monomial = get_term(coef, exp, aGens)
        integral += symbolic_partial_monomial_integral(monomial, integration_idx)
    terms = get_terms(integral)
    result = sum([terms[key]*key for key in terms.keys()])
    return result
In [115]:
symbolic_partial_integral(terms[y0*y1]*y0*y1)
Out[115]:
5*t0*t2^2*y1 + 3*t0*y1
In [143]:
def find_preimage_diff(pol):
    if pol.differential()!=0 :
        raise ValueError("This polynomial is not in the kernel of the differential")
    lead_pol = get_leading_pol(pol)
    w = symbolic_partial_integral(lead_pol)
    theta = pol - w.differential()
    if (theta == 0):
        return w
    else:
        return w+find_preimage_diff(theta)
In [144]:
# Ejemplo 1
q = t0*y1+t1*y0
find_preimage_diff(q)
Out[144]:
t0*t1
In [149]:
#Ejemplo 2
q1 = (-1/2)*(t0*t1^2*t2*t3*y0)
q2 =  (-1/2)*(t0^2*t1*t2*t3*y1)
q = q1+q2
dq = q.differential()
w = find_preimage_diff(dq)
w.differential() == dq
Out[149]:
True
In [157]:
show(q)
show(w)
<html></html>
<html></html>

Terminan celdas de pruebas

In [16]:
p = a.propagate([0, 2, 3, 4, 5],5)
q = a.face(1,5)(p)
ared = a.reduce_polynomial(q)
print(q)
print(ared)
t4*y0*y1*y2*y3 - t3*y0*y1*y2*y4 + t2*y0*y1*y3*y4 - t1*y0*y2*y3*y4 + t0*y1*y2*y3*y4
y1*y2*y3*y4
In [ ]:
 
In [17]:
a.algebras[3].gens()[4:-1]
Out[17]:
(y0, y1, y2)
In [18]:
n=3
a = Apln(n)
a.inject_variables(n)
cdga = a.algebras[n]
gens = cdga.gens()[n+1:-1]
gens
Defining t0, t1, t2, t3, y0, y1, y2, y3
Out[18]:
(y0, y1, y2)
In [19]:
N = 6
for n in reversed(range(1,N)):
    print("n = " + str(n))
    for face in range(n+1):
        idx = list(range(N))
        del idx[face]
        r = [perm for perm in itertools.permutations(idx,r=int(n)) if sorted(perm) == list(perm)]
        #print(r)
n = 5
n = 4
n = 3
n = 2
n = 1
In [ ]:
 

Apl(K)

In [20]:
class AplK(UniqueRepresentation, Parent):
    
    def __init__(self,K, category=None):
        self.K = K
        self.n = K.dimension()
        self.apln = Apln(K.dimension())
        Parent.__init__(self,category=category )
        f = MorphismFromQQToAplK(self)
        self.register_coercion(f)
   
    def _repr_(self):
        return "Algebra AplK over %s" % self.K
    
    def _element_constructor_(self,values):
        return self.element_class(self,values)
    
    def dimension(self):
        return self.K.dimension()
    
    def simplicialComplex(self):
        return self.K
    
    def _n_cells_sorted(self,n):
        return self.K._n_cells_sorted(n)
        
    def lift(self, cochain):
        
        # first we find polynomials compatible with the integration value
        values = {}
        max_n = 0
        for k in cochain.keys():
            n = len(k)-1
            if n > max_n:
                max_n = n
            apl = self.apln.algebras[n]
            if n>0:
                values[k] = self.apln.lift(cochain[k],n)
            else:
                values[k]= apl(cochain[k])
        
        # second, we propagate the simplicial information
        simplicial_structure = {}
        for n in reversed(range(max_n+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 AplKElement(self,simplicial_structure)

    def _faces_of_length(self, simplex, length):
        return [tuple(e) for e in powerset(simplex) if len(e)==length]
    
    def _propagate(self, cochain, simplex):
        
        dim = len(simplex)-1
        apl = self.apln.algebras[dim]
        propagation = apl(0)
        for n in reversed(range(dim+1)):
            faces = self._faces_of_length(simplex, n)
            for f in faces:
                if f in cochain.keys():
                    dim_face = len(f)-1
                    value = cochain[f]
                    face_indexes = [simplex.index(i) for i in f]
                    propagation += value*((-1)**(dim_face*(dim_face-1)/2))*factorial(dim_face)*self.apln.propagate(face_indexes, dim)
        return propagation
    
In [22]:
# AplK._propagate TEST
cochain = {(0,): 0, (1,): 0, (2,):0, (3,): 0, (0,1): 0, (0,2): 0, (1,2): 1, (1,3): 0, (2,3): 0, (0,1,2):0}
S = SimplicialComplex([[0,1,2],[1,3],[2,3]],is_mutable=False) 
A = AplK(S)
p = A._propagate(cochain,(0,1,2))
print(p)
a = Apln(5)
q = a.face(0,2)(p)
print(q)
ared = a.reduce_polynomial(q)
print(ared)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/Documents/0-PhD/Code/sage-9.2/local/lib/python3.8/site-packages/sage/misc/cachefunc.pyx in sage.misc.cachefunc.CachedFunction.__call__ (build/cythonized/sage/misc/cachefunc.c:5975)()
    995             try:
--> 996                 return self.cache[k]
    997             except TypeError:  # k is not hashable

~/Documents/0-PhD/Code/sage-9.2/local/lib/python3.8/site-packages/sage/misc/weak_dict.pyx in sage.misc.weak_dict.WeakValueDictionary.__getitem__ (build/cythonized/sage/misc/weak_dict.c:3737)()
    703         if wr == NULL:
--> 704             raise KeyError(k)
    705         out = PyWeakref_GetObject(wr)

KeyError: ((<class '__main__.AplK'>, Simplicial complex with vertex set (0, 1, 2, 3) and facets {(1, 3), (2, 3), (0, 1, 2)}), ())

During handling of the above exception, another exception occurred:

NameError                                 Traceback (most recent call last)
<ipython-input-22-3b31891d9403> in <module>
      2 cochain = {(Integer(0),): Integer(0), (Integer(1),): Integer(0), (Integer(2),):Integer(0), (Integer(3),): Integer(0), (Integer(0),Integer(1)): Integer(0), (Integer(0),Integer(2)): Integer(0), (Integer(1),Integer(2)): Integer(1), (Integer(1),Integer(3)): Integer(0), (Integer(2),Integer(3)): Integer(0), (Integer(0),Integer(1),Integer(2)):Integer(0)}
      3 S = SimplicialComplex([[Integer(0),Integer(1),Integer(2)],[Integer(1),Integer(3)],[Integer(2),Integer(3)]],is_mutable=False)
----> 4 A = AplK(S)
      5 p = A._propagate(cochain,(Integer(0),Integer(1),Integer(2)))
      6 print(p)

~/Documents/0-PhD/Code/sage-9.2/local/lib/python3.8/site-packages/sage/misc/classcall_metaclass.pyx in sage.misc.classcall_metaclass.ClasscallMetaclass.__call__ (build/cythonized/sage/misc/classcall_metaclass.c:1762)()
    320         """
    321         if cls.classcall is not None:
--> 322             return cls.classcall(cls, *args, **kwds)
    323         else:
    324             # Fast version of type.__call__(cls, *args, **kwds)

~/Documents/0-PhD/Code/sage-9.2/local/lib/python3.8/site-packages/sage/misc/cachefunc.pyx in sage.misc.cachefunc.CachedFunction.__call__ (build/cythonized/sage/misc/cachefunc.c:6101)()
    999                 return self.cache[k]
   1000         except KeyError:
-> 1001             w = self.f(*args, **kwds)
   1002             self.cache[k] = w
   1003             return w

~/Documents/0-PhD/Code/sage-9.2/local/lib/python3.8/site-packages/sage/structure/unique_representation.py in __classcall__(cls, *args, **options)
   1006             True
   1007         """
-> 1008         instance = typecall(cls, *args, **options)
   1009         assert isinstance( instance, cls )
   1010         if instance.__class__.__reduce__ == CachedRepresentation.__reduce__:

~/Documents/0-PhD/Code/sage-9.2/local/lib/python3.8/site-packages/sage/misc/classcall_metaclass.pyx in sage.misc.classcall_metaclass.typecall (build/cythonized/sage/misc/classcall_metaclass.c:2224)()
    484             TypeError: Argument 'cls' has incorrect type (expected type, got classobj)
    485     """
--> 486     return (<PyTypeObject*>type).tp_call(cls, args, kwds)
    487 
    488 # Class for timing::

<ipython-input-20-57d0fa9f124b> in __init__(self, K, category)
      6         self.apln = Apln(K.dimension())
      7         Parent.__init__(self,category=category )
----> 8         f = MorphismFromQQToAplK(self)
      9         self.register_coercion(f)
     10 

NameError: name 'MorphismFromQQToAplK' is not defined
In [ ]:
S = SimplicialComplex([[0,1,2],[1,3],[2,3]],is_mutable=False) 
A = AplK(S)
cochain = {(0,): 2, (1,): 3, (2,):0, (3,): 0, (0,1): 1, (0,2): 5, (1,2): 1, (1,3): 0, (2,3): 0, (0,1,2):10}
p = A.lift(cochain)
p
In [ ]:
A.lift(cochain).integral()==cochain
In [ ]:
A.lift(p.integral())==p
In [ ]:
 

Apl(K) Element

In [23]:
class AplKElement(Element):

    def __init__(self, parent, values):
        Element.__init__(self,parent)
        self.apln = Apln(parent.dimension())
        self.elements = self.reduce(values) 
        self.check()
        
    def reduce(self, values):
        reduced_values = {}
        for k in values.keys():
            n = len(k)-1
            apl = self.apln.algebras[n]
            reduced_values[k] = self.apln.reduce_polynomial(apl(values[k]))
        return reduced_values
        
    def _repr_(self):
        return str(self.elements)
    
    def dimension(self):
        return self.parent().dimension()
    
    def nullElement(self):
        C = self.__class__
        values = {}
        for k in self.elements.keys():
            values[k]=0
        return C(self.parent(),values)    
    
    def _add_(self,other):
        C = self.__class__
        if(self.elements.keys()!=other.elements.keys()):
            raise ValueError("error: keys do not match")
        newValues = {}
        for k in self.elements.keys():
            newValues[k] = self.elements[k]+other.elements[k]
        return C(self.parent(),newValues)
      
    def _sub_(self,other):

        C = self.__class__
        if(self.elements.keys()!=other.elements.keys()):
            raise ValueError("error: keys do not match")
        newValues = {}
        for k in self.elements.keys():
            newValues[k] = self.elements[k]-other.elements[k]
        return C(self.parent(),newValues)
      
    def _mul_(self,other):

        C = self.__class__
        if(self.elements.keys()!=other.elements.keys()):
            raise ValueError("error: keys do not match")
        newValues = {}
        for k in self.elements.keys():
            newValues[k] = self.elements[k]*other.elements[k]
        return C(self.parent(),newValues)
    
    def _richcmp_(self, other, op):

        if(op==op_EQ):
            if(self.elements.keys()!=other.elements.keys()):
                return False
            for k in self.elements.keys():
                if(self.elements[k]!=other.elements[k]):
                    return False
            return True
        elif(op==op_NE):
            return not self._richcmp_(other,op_EQ)
        else:
            raise NotImplementedError("operation not implemented for this element class")
      
    def differential(self):
        C = self.__class__
        newValues = {}
        for k in self.elements.keys():
            newValues[k] = self.elements[k].differential()
        return C(self.parent(),newValues)
      
    def check(self):

        S = self.parent().simplicialComplex()
        dim = S.dimension()
        for n in range(1,dim+1):
            #print("Checking dimension " + str(n))
            for simplex in S._n_cells_sorted(n):
                for f in range(n+1):
                    sFace = tuple(S.face(simplex,f))
                    aFace = self.apln.face(f,n)(self.elements[simplex.tuple()])
                    aFace = self.apln.reduce_polynomial(aFace)
                    #print("face: " + str(f),"simplex: " + str(simplex),"sFace: " + str(self.elements[sFace]), "aFace: " + str(aFace))
                    if(self.elements[sFace] != aFace):
                        raise ValueError("the assignation is not valid")
            
    def homogeneous_part(self,degree):
        C = self.__class__
        part = {}
        for k in self.elements.keys():
            part[k] = self.apln.homogeneous_part(self.elements[k],degree)
        return C(self.parent(),part)
    
    def homogeneous_parts(self):
        parts = {}
        for n in range(self.dimension()+1):
            parts[n] = self.homogeneous_part(n)
        return parts
               
    def is_homogeneous(self):
        isHomogeneous = True
        for k in self.elements.keys():
            isHomogeneous = isHomogeneous and self.elements[k].is_homogeneous()
        return isHomogeneous
  
    def is_closed(self):
        diff = self.differential()
        if(diff == self.nullElement()):
            return True
        else:
            return False
      
    def _integrable_component(self):
        C = self.__class__
        component = {}
        for k in self.elements.keys():
            degree = len(k)-1
            component[k] = self.apln.homogeneous_part(self.elements[k],degree)
            #component[k] = self.homogeneous_component(dim).elements[k]
        return component
    
    def integral(self):
        component = self._integrable_component()
        cochain = {}
        #integration of each polynomial
        for k in component.keys():
            n = len(k)-1
            apl = self.apln.algebras[n]
            cochain[k] = self.apln.integral(apl(component[k]))
        return cochain
    
#####################################################################

class MorphismFromQQToAplK(Morphism):

    def __init__(self, codomain):
        self.simplicialComplex= codomain.K
        Morphism.__init__(self, QQ, codomain)

    def _call_(self, x):
        S = self.simplicialComplex
        params1 = {}
        for d in range(S.dimension()+1):
            for c in S._n_cells_sorted(d):
                params1[tuple(c)]=x
        A = AplK(S)
        return AplKElement(A, params1)
In [24]:
S = SimplicialComplex([[0, 1, 2]], is_mutable=False)                                                                                 
apl = Apln(S.dimension())
A = AplK(S)
cochain = {(0,): 0, (1,): 0, (2,):0, (0,1): 0,(0,2): 0, (1,2): 0, (0,1,2): 1}
#A.lift(apl, cochain)
In [25]:
q = A.lift(cochain)
q
Out[25]:
{(0, 1, 2): -2*y1*y2, (0, 1): 0, (0, 2): 0, (1, 2): 0, (0,): 0, (1,): 0, (2,): 0}
In [ ]:
2*q
In [ ]:
 
In [ ]:
 

All together

In [ ]:
S1 = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[3,4],[3,5],[4,5]],is_mutable=False) 
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) 
S3 = SimplicialComplex([[0,1,2],[1,3],[2,3]],is_mutable=False) 
S4 = SimplicialComplex([[0,1,3],[1,2,4],[3,4,5]],is_mutable=False) 
S5 = simplicial_complexes.Sphere(5)
S = S5
apl = Apln(S.dimension())
A = AplK(S)
In [ ]:
n = 5
gens = cohomology_gens(S,n)
cochain = lift_cohomology_generator(S,gens[(n,0)])
In [ ]:
gens
In [ ]:
p = A.lift(cochain)
In [ ]:
p.integral()==cochain

Test lift == integral

In [26]:
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+1):
        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
In [27]:
S = random_simplicial_complex(5,4)
S.facets()
Out[27]:
{(0, 1, 4), (0, 1, 3), (0, 1, 2)}
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [28]:
def test(max_vertices, verbose=False):
    success = True
    data = []
    for i in range(10):
        v = randint(2,max_vertices)
        f = randint(1,v)
        S = random_simplicial_complex(num_vertices=v,max_num_facets=f)
        A = AplK(S)
        cochain = random_cochain(S)
        #print(cochain)
        data = [S, cochain]
        if verbose:
            print("iteration: " + str(i), "num_vertices: " + str(v), "max_num_facets: " + str(f), "facets: " + str(S.facets()))
        p = A.lift(cochain)
        if verbose:
            print(p)
        if not p.integral()==cochain:
            success = False
            print("Error")
            break
    if success:
        print("success: " + str(success))
In [29]:
test(max_vertices=6, verbose=True)
iteration: 0 num_vertices: 5 max_num_facets: 1 facets: {(1, 3, 4), (0, 4)}
{(1, 3, 4): -168*y1*y2 - 65*t2*y1 + 65*t1*y2 - 71*y1 - 94*y2 - 150*t1 + t2 + 87, (0, 4): -80*y1 + 60*t1 + 28, (1, 3): -71*y1 - 150*t1 + 87, (1, 4): -94*y1 + t1 + 87, (3, 4): 42*y1 + 151*t1 - 63, (0,): 28, (1,): 87, (3,): -63, (4,): 88}
iteration: 1 num_vertices: 4 max_num_facets: 1 facets: {(0, 2, 3), (0, 1, 2)}
{(0, 1, 2): -52*y1*y2 - 65*t2*y1 + 65*t1*y2 + 54*y1 + 81*y2 - 64*t1 - 58*t2 + 70, (0, 2, 3): 82*y1*y2 + 45*t2*y1 - 45*t1*y2 + 81*y1 + 71*y2 - 58*t1 - 95*t2 + 70, (0, 1): 54*y1 - 64*t1 + 70, (0, 2): 81*y1 - 58*t1 + 70, (0, 3): 71*y1 - 95*t1 + 70, (1, 2): 92*y1 + 6*t1 + 6, (2, 3): -55*y1 - 37*t1 + 12, (0,): 70, (1,): 6, (2,): 12, (3,): -25}
iteration: 2 num_vertices: 4 max_num_facets: 3 facets: {(0, 1, 3), (0, 2, 3), (1, 2)}
{(0, 1, 3): -12*y1*y2 + 47*t2*y1 - 47*t1*y2 - 59*y1 + 22*y2 - 95*t1 - 132*t2 + 67, (0, 2, 3): -178*y1*y2 - 29*t2*y1 + 29*t1*y2 - 15*y1 + 22*y2 - 7*t1 - 132*t2 + 67, (0, 1): -59*y1 - 95*t1 + 67, (0, 2): -15*y1 - 7*t1 + 67, (0, 3): 22*y1 - 132*t1 + 67, (1, 2): -5*y1 + 88*t1 - 28, (1, 3): 34*y1 - 37*t1 - 28, (2, 3): 66*y1 - 125*t1 + 60, (0,): 67, (1,): -28, (2,): 60, (3,): -65}
iteration: 3 num_vertices: 6 max_num_facets: 2 facets: {(2, 3, 4), (0, 1, 3, 4)}
{(0, 1, 3, 4): -36*y1*y2*y3 + 444*t3*y1*y2 - 444*t2*y1*y3 + 444*t1*y2*y3 - 22*y1*y2 + 120*y1*y3 - 130*y2*y3 - 51*t2*y1 - 129*t3*y1 + 51*t1*y2 - 118*t3*y2 + 129*t1*y3 + 118*t2*y3 + 22*y1 + 67*y2 - 93*y3 - 57*t1 - 8*t2 + 112*t3 - 22, (0, 1, 3): -22*y1*y2 - 51*t2*y1 + 51*t1*y2 + 22*y1 + 67*y2 - 57*t1 - 8*t2 - 22, (0, 1, 4): 120*y1*y2 - 129*t2*y1 + 129*t1*y2 + 22*y1 - 93*y2 - 57*t1 + 112*t2 - 22, (0, 3, 4): -130*y1*y2 - 118*t2*y1 + 118*t1*y2 + 67*y1 - 93*y2 - 8*t1 + 112*t2 - 22, (1, 3, 4): 172*y1*y2 - 40*t2*y1 + 40*t1*y2 + 96*y1 + 14*y2 + 49*t1 + 169*t2 - 79, (2, 3, 4): -96*y1*y2 + 92*t2*y1 - 92*t1*y2 + 35*y1 + 85*y2 - 35*t1 + 85*t2 + 5, (0, 1): 22*y1 - 57*t1 - 22, (0, 3): 67*y1 - 8*t1 - 22, (0, 4): -93*y1 + 112*t1 - 22, (1, 3): 96*y1 + 49*t1 - 79, (1, 4): 14*y1 + 169*t1 - 79, (2, 3): 35*y1 - 35*t1 + 5, (2, 4): 85*y1 + 85*t1 + 5, (3, 4): -42*y1 + 120*t1 - 30, (0,): -22, (1,): -79, (2,): 5, (3,): -30, (4,): 90}
iteration: 4 num_vertices: 2 max_num_facets: 2 facets: {(1,), (0,)}
{(0,): -2, (1,): 97}
iteration: 5 num_vertices: 5 max_num_facets: 5 facets: {(0, 2, 3, 4), (0, 1, 2, 4), (1, 2, 3)}
{(0, 1, 2, 4): -384*y1*y2*y3 + 212*t3*y1*y2 - 212*t2*y1*y3 + 212*t1*y2*y3 - 76*y1*y2 - 44*y1*y3 - 128*y2*y3 + 8*t2*y1 + 28*t3*y1 - 8*t1*y2 - 103*t3*y2 - 28*t1*y3 + 103*t2*y3 - 6*y1 + 51*y2 - 46*y3 - 37*t1 + 119*t2 + 147*t3 - 63, (0, 2, 3, 4): -150*y1*y2*y3 - 254*t3*y1*y2 + 254*t2*y1*y3 - 254*t1*y2*y3 + 142*y1*y2 - 128*y1*y3 + 74*y2*y3 + 61*t2*y1 - 103*t3*y1 - 61*t1*y2 + t3*y2 + 103*t1*y3 - t2*y3 + 51*y1 + 53*y2 - 46*y3 + 119*t1 + 125*t2 + 147*t3 - 63, (0, 1, 2): -76*y1*y2 + 8*t2*y1 - 8*t1*y2 - 6*y1 + 51*y2 - 37*t1 + 119*t2 - 63, (0, 1, 4): -44*y1*y2 + 28*t2*y1 - 28*t1*y2 - 6*y1 - 46*y2 - 37*t1 + 147*t2 - 63, (0, 2, 3): 142*y1*y2 + 61*t2*y1 - 61*t1*y2 + 51*y1 + 53*y2 + 119*t1 + 125*t2 - 63, (0, 2, 4): -128*y1*y2 - 103*t2*y1 + 103*t1*y2 + 51*y1 - 46*y2 + 119*t1 + 147*t2 - 63, (0, 3, 4): 74*y1*y2 + t2*y1 - t1*y2 + 53*y1 - 46*y2 + 125*t1 + 147*t2 - 63, (1, 2, 3): 148*y1*y2 - 55*t2*y1 + 55*t1*y2 + 49*y1 - 65*y2 + 156*t1 + 162*t2 - 100, (1, 2, 4): 52*y1*y2 - 123*t2*y1 + 123*t1*y2 + 49*y1 - 68*y2 + 156*t1 + 184*t2 - 100, (2, 3, 4): 90*y1*y2 + 165*t2*y1 - 165*t1*y2 - 59*y1 + 6*y2 + 6*t1 + 28*t2 + 56, (0, 1): -6*y1 - 37*t1 - 63, (0, 2): 51*y1 + 119*t1 - 63, (0, 3): 53*y1 + 125*t1 - 63, (0, 4): -46*y1 + 147*t1 - 63, (1, 2): 49*y1 + 156*t1 - 100, (1, 3): -65*y1 + 162*t1 - 100, (1, 4): -68*y1 + 184*t1 - 100, (2, 3): -59*y1 + 6*t1 + 56, (2, 4): 6*y1 + 28*t1 + 56, (3, 4): -100*y1 + 22*t1 + 62, (0,): -63, (1,): -100, (2,): 56, (3,): 62, (4,): 84}
iteration: 6 num_vertices: 2 max_num_facets: 1 facets: {(1,), (0,)}
{(0,): -95, (1,): 1}
iteration: 7 num_vertices: 4 max_num_facets: 3 facets: {(1, 3), (0, 2, 3)}
{(0, 2, 3): -182*y1*y2 - 93*t2*y1 + 93*t1*y2 - 53*y1 - 66*y2 - 25*t1 - 26*t2 - 52, (0, 2): -53*y1 - 25*t1 - 52, (0, 3): -66*y1 - 26*t1 - 52, (1, 3): -35*y1 + 16*t1 - 94, (2, 3): 80*y1 - t1 - 77, (0,): -52, (1,): -94, (2,): -77, (3,): -78}
iteration: 8 num_vertices: 5 max_num_facets: 4 facets: {(2, 3, 4), (1, 2), (0, 1, 3, 4)}
{(0, 1, 3, 4): 36*y1*y2*y3 - 74*t3*y1*y2 + 74*t2*y1*y3 - 74*t1*y2*y3 + 190*y1*y2 - 138*y1*y3 - 172*y2*y3 - 6*t3*y1 - 16*t3*y2 + 6*t1*y3 + 16*t2*y3 + 44*y1 - 3*y2 - 13*y3 + 90*t1 - 8*t2 + 136*t3 - 46, (0, 1, 3): 190*y1*y2 + 44*y1 - 3*y2 + 90*t1 - 8*t2 - 46, (0, 1, 4): -138*y1*y2 - 6*t2*y1 + 6*t1*y2 + 44*y1 - 13*y2 + 90*t1 + 136*t2 - 46, (0, 3, 4): -172*y1*y2 - 16*t2*y1 + 16*t1*y2 - 3*y1 - 13*y2 - 8*t1 + 136*t2 - 46, (1, 3, 4): 82*y1*y2 - 10*t2*y1 + 10*t1*y2 - 47*y1 - 51*y2 - 98*t1 + 46*t2 + 44, (2, 3, 4): 6*y1*y2 + 85*t2*y1 - 85*t1*y2 - 72*y1 + 19*y2 + 17*t1 + 161*t2 - 71, (0, 1): 44*y1 + 90*t1 - 46, (0, 3): -3*y1 - 8*t1 - 46, (0, 4): -13*y1 + 136*t1 - 46, (1, 2): -27*y1 - 115*t1 + 44, (1, 3): -47*y1 - 98*t1 + 44, (1, 4): -51*y1 + 46*t1 + 44, (2, 3): -72*y1 + 17*t1 - 71, (2, 4): 19*y1 + 161*t1 - 71, (3, 4): 6*y1 + 144*t1 - 54, (0,): -46, (1,): 44, (2,): -71, (3,): -54, (4,): 90}
iteration: 9 num_vertices: 5 max_num_facets: 3 facets: {(1, 3), (1, 2)}
{(1, 2): 91*y1 - 78*t1 + 81, (1, 3): 100*y1 + 5*t1 + 81, (1,): 81, (2,): 3, (3,): 86}
success: True
In [33]:
S = random_simplicial_complex(5,4)
A = AplK(S)
In [34]:
c1 = random_cochain(S)
p1 = A.lift(c1)
c2 = random_cochain(S)
p2 = A.lift(c2)
In [40]:
(p2*p1).differential
Object `differential` not found.
In [ ]:
a = apl.algebras[2]
In [50]:
 
Out[50]:
Commutative Differential Graded Algebra with generators ('t0', 't1', 't2', 'y0', 'y1', 'y2') in degrees ((0, 2), (0, 2), (0, 2), (1, 2), (1, 2), (1, 2)) over Rational Field with differential:
   t0 --> y0
   t1 --> y1
   t2 --> y2
   y0 --> 0
   y1 --> 0
   y2 --> 0
In [45]:
 

Minimal model $S^2 \wedge S^1$

In [ ]:
 
In [ ]:
S1 = SimplicialComplex([[0,1,2],[0,1,3],[0,2,3],[1,2,3],[3,4],[3,5],[4,5]],is_mutable=False) 
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)
S = S1
apl = Apln(S.dimension())
A = AplK(S)
In [ ]:
# busco el primer grupo de cohomología no trivial
gens = cohomology_gens(S,1)
gens
In [ ]:
c10 = lift_cohomology_generator(S,gens[(1,0)])
a10 = A.lift(c10)
a10
In [ ]:
# compruebo que la diferencial es cero
a10.is_closed()
In [ ]:
# pasamos al sigiente grado
gens = cohomology_gens(S,2)
gens
In [ ]:
c20 = lift_cohomology_generator(S,gens[(2,0)])
a20 = A.lift(c20)
a20
In [ ]:
c20.keys()
In [ ]:
a20.is_closed()
In [ ]:
a10*a20
In [ ]:
 

Minimal model $S^2 \wedge S^2$

In [ ]:
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)
S = S2
apl = Apln(S.dimension())
A = AplK(S)
In [ ]:
# busco el primer grupo de cohomología no trivial
gens = cohomology_gens(S,2)
gens
In [ ]:
c20 = lift_cohomology_generator(S,gens[(2,0)])
a20 = A.lift(c20)
c21 = lift_cohomology_generator(S,gens[(2,1)])
a21 = A.lift(c21)
a20*a21 == a20.nullElement()
In [ ]:
a31 = a20.nullElement()
In [ ]:
for k in gens.keys():
    print(k[0])

Minimal model

In [ ]:
class MinimalModel():

    def __init__(self, S):
        self.K = S
        self.Phi = {}
        self.AplK = AplK(S)
        self.Apln = Apln(S.dimension())
        gens = self.first_non_trivial_cohomology_group()
        [self.cdga, self.cohomology_dict]  = self.createModel(gens)
        
    def first_non_trivial_cohomology_group(self, max_iter=20):
        # busco el primer grupo de cohomología no trivial
        i=1
        gens = self.cohomology_gens(i)
        while(len(gens)==0 and i<max_iter):
            i+=1
            gens = self.cohomology_gens(i)
        return gens
    
    def lift_cohomology_generator(self, g):
        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 cohomology_gens(self, n):
        gens = {}
        basis = self.K.cohomology_ring().basis()
        for k in basis.keys():
            if k[0] == n:
                gens[k] = basis[k]
        return gens
        
    def createModel(self, gens):
        n = len(gens)
        generators = []
        degrees = []
        cohomology_dict = {}
        for k in gens.keys():
            degree = k[0]
            index = k[1]
            genName = 'x'+str(index)+'d'+str(degree)
            generators.append(genName)
            degrees.append(degree)
            cohomology_dict[genName] = gens[k]
        gca = GradedCommutativeAlgebra(QQ, names=generators, degrees=degrees)
        dfs = {gca.gens_dict()[generators[i]]:0 for i in range(n)}
        return [gca.cdg_algebra(dfs), cohomology_dict]
In [ ]:
S3 = simplicial_complexes.Sphere(3)
In [ ]:
M = MinimalModel(S3)
In [ ]:
h = M.cohomology_dict['x0d3']
cochain = M.lift_cohomology_generator(h)
M.AplK.lift(cochain)
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
S = simplicial_complexes.Sphere(8)
M = MinimalModel(S)
In [ ]:
M.cdga
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
list(M.cohomology_dict.values())[0]
In [ ]:
M.cdga.gens_dict()[list(M.cohomology_dict.values())[0]]
In [ ]:
# busco el primer grupo de cohomología no trivial
i=1
gens = cohomology_gens(S,i)
while(len(gens)==0):
    i+=1
    gens = cohomology_gens(S,i)
print(gens)
In [ ]:
 
In [ ]:
c80 = lift_cohomology_generator(S,gens[(4,0)])
a80 = A.lift(c80)
In [ ]:
a80
In [ ]: