TransformationNaturelle.py 7.94 KB
Newer Older
1
from Foncteur import Foncteur
2
from config import *
3
4
5
import itertools
from Interaction import Interaction
from CommaCategorie import ObjetCommaCategorie
6
from Categorie import Categorie, mise_en_cache_call
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
7
from Morphisme import Morphisme
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
8
from Diagramme import Diagramme
9
from EnsFinis import Application, EnsFinis
10
11
if GRAPHVIZ_ENABLED:
    from graphviz import Digraph
12
from typing import *
13

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
14
class TransformationNaturelle(Interaction):
15
16
17
18
19
20
    """TransformationNaturelle peut être abrégé TransfoNat.
    Une transformation naturelle d'un foncteur à un autre.
    Soit F et G deux foncteurs de C dans D.
    Une transformation naturelle t de F vers G est une fonction des objets c de C vers les flèches D(F(c),G(c)) telle que
    pour deux objets de C, c et c', et pour toute flèche f de c à c', on a G(f) o t(c) = t(c') o F(f)
    (cf. Mac Lane "Categories for the working mathematician" p.16)"""
21
    nb_viz = 0
22
    __id = 0
23
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
24
    def __init__(self, foncteur_source:Foncteur, foncteur_cible:Foncteur, composantes:dict, nom:str = None):
25
        """
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
26
        `foncteur_source` et `foncteur_cible` deux foncteurs de C vers D.
27
28
29
30
31
32
        `composantes` est un dictionnaire {`c`:`morph`} où `c` est un objet de C, `morph` un morphisme de B de foncteur_source(`c`) vers foncteur_cible(`c`)."""
        self.__C = foncteur_source.source
        self.__D = foncteur_source.cible
        self.__F = foncteur_source
        self.__G = foncteur_cible
        
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
33
        self._composantes = composantes
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
34
        Interaction.__init__(self,foncteur_source, foncteur_cible, {ObjetCommaCategorie(c,composantes[c],c) for c in composantes},"TN"+str(self.__id) if nom == None else nom)
35
36
37
38
39
        self.__id = TransfoNat.__id #pour la transformation en str
        TransfoNat.__id += 1
        if TOUJOURS_VERIFIER_COHERENCE:
            self.verifier_coherence()
        
40
    def __call__(self, obj:Any) -> Morphisme:
41
42
        """Renvoie l'image d'un objet de C.
        L'image d'un objet i de C est une composante qui fait la "translation" du premier objet indexé par i vers le second objet indexé par i."""
43
44
        if obj not in self.__C.objets:
            raise Exception("L'objet "+str(obj)+" n'appartient pas à la categorie C = "+str(self.__C))
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
45
        return self._composantes[obj]
46
            
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
47
48
49
50
51
52
    def __eq__(self, other:'TransformationNaturelle') -> bool:
        if not issubclass(type(other),TransformationNaturelle):
            raise TypeError("Tentative de comparaison avec un objet de type inconnu "+str(other))
        return self.source == other.source and self.cible == other.cible and self._composantes == other._composantes
    
    def __hash__(self) -> int:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
53
        return hash((self.source,self.cible,frozenset(self._composantes.items())))
54
55
56
        
    def __matmul__(self, other:'TransformationNaturelle') -> 'TransformationNaturelle':
        """Composition verticale des transformations naturelles."""
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
57
58
        return TransformationNaturelle(other.source,self.cible,{c:self(c)@other(c) for c in self.__C.objets})
       
59
60
61
62
63
    def verifier_coherence(self):
        if self.source.source != self.cible.source:
            raise Exception("Incoherence TransformationNaturelle : foncteur sources et cible n'ont pas la meme categorie source "+str(self.source)+'  '+str(self.cible))
        if self.source.cible != self.cible.cible:
            raise Exception("Incoherence TransformationNaturelle : les foncteurs source et cible n'ont pas la meme categorie cible "++str(self.source)+'  '+str(self.cible))
64
        
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
65
        if set(self._composantes.keys()) != self.__C.objets:
66
67
            raise Exception("Incoherence TransformationNaturelle : les objets de la categorie C ne sont pas les memes que les antecedants des composantes\n"+\
            str(self._composantes)+"\n"+str(self.__C.objets))
68
        
69
        for source,cible in itertools.product(self.__C.objets,repeat=2):
70
            for f in self.__C({source},{cible}):
71
72
                c1,c2 = [f.source,f.cible]
                if self.__G(f)@self(c1) != self(c2)@self.__F(f):
73
                    raise Exception("Incoherence TransformationNaturelle :  la commutativité des diagrammes est pas respectee\n"+\
74
                    "Sf = "+str(self.__F(f))+"\ntC' = "+str(self(c2))+"\ntC = "+str(self(c1))+"\nTf = "+str(self.__G(f))+\
75
                    "\ntC' o Sf != Tf o tC : "+str(self(c2)@self.__F(f)) + " != "+str(self.__G(f)@self(c1)))
76
                    
77
    
78
TransfoNat = TransformationNaturelle
79
    
80
81
def test_TransfoNat():
    from GrapheDeComposition import GC,MGC
82
83
    from Diagramme import Triangle,DiagrammeIdentite
    
84
85
86
87
88
    gc = GC()
    gc |= set("ABCDEF")
    f,g,h,i,j,k,l = [MGC('A','B','f'),MGC('B','C','g'),MGC('D','E','h'),MGC('E','F','i'),MGC('A','D','j'),MGC('B','E','k'),MGC('C','F','l')]
    gc |= {f,g,h,i,j,k,l}
    gc.transformer_graphviz()
89
    
90
91
    diag_identite = DiagrammeIdentite(gc)
    diag_identite.faire_commuter()
92
    
93
94
95
96
    d1 = Triangle(gc,"ABC",[f,g,g@f])
    d2 = Triangle(gc,"DEF",[h,i,i@h])
    d1.transformer_graphviz()
    d2.transformer_graphviz()
97
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
98
    tn = TransfoNat(d1,d2,{1:j,2:k,3:l})
99
    tn.transformer_graphviz()
100
101
    
    d3 = Triangle(gc,"EEF",[gc.identite('E'),i,i])
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
102
    tn2 = TransfoNat(d2,d3,{1:h,2:gc.identite('E'),3:gc.identite('F')})
103
104
105
106
    tn2.transformer_graphviz()
    
    tn3 = tn2@tn
    tn3.transformer_graphviz()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
107
108
109
110
111
112
113
    
class CategorieTransformationsNaturelles(Categorie):
    """CategorieTransformationsNaturelles peut être abrégé en CatTransfoNat.
    Cette catégorie a pour objet des diagrammes D_i de J dans C.
    Tous les diagrammes ont la même catégorie indexante et la même catégorie indexée.
    Une flèche de cette catégorie est une transformation naturelle entre deux diagrammes.
    """
114
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
115
116
    def identite(self, diag:Diagramme) -> TransfoNat:
        return TransfoNat(diag,diag,{o:diag.cible.identite(diag(o)) for o in diag.source.objets})
117
118
                
    def __call__(self, sources:set, cibles:set) -> Generator[TransfoNat,None,None]:
119
120
121
        if len(sources) > 0:
            J = list(sources)[0].source
            C = list(sources)[0].cible
122
123
124
        for source in sources: 
            for cible in cibles:
                # on cherche toutes les transformations naturelles du diagramme source:J->C vers le diagramme cible:J->C
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
125
                obj_index = J.objets
126
                fleches_arrivee = frozenset(C({source(o) for o in set(J.objets)},{cible(o) for o in set(J.objets)}))
127
128
                # flèches de l'image de a vers l'image de b
                
129
130
                ens = EnsFinis({obj_index,fleches_arrivee})
                for app in ens({obj_index},{fleches_arrivee}):
131
132
                    for fleche_index in J[abs(J),abs(J)]:
                        if app(fleche_index.source).cible != cible(fleche_index).source:
133
                            break
134
                        if source(fleche_index).cible != app(fleche_index.cible).source:
135
                            break
136
                        if cible(fleche_index)@app(fleche_index.source) != app(fleche_index.cible)@source(fleche_index):
137
138
                            break
                    else:
139
                        yield TransfoNat(source,cible,app.as_dict())
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
140
141
142

CatTransfoNat = CategorieTransformationsNaturelles

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def test_CatTransfoNat():
    from GrapheDeComposition import GC,MGC
    from Diagramme import Triangle,DiagrammeIdentite
    
    gc = GC()
    gc |= set("ABCDEF")
    f,g,h,i,j,k,l = [MGC('A','B','f'),MGC('B','C','g'),MGC('D','E','h'),MGC('E','F','i'),MGC('A','D','j'),MGC('B','E','k'),MGC('C','F','l')]
    gc |= {f,g,h,i,j,k,l}
    
    diag_identite = DiagrammeIdentite(gc)
    diag_identite.faire_commuter()
    
    d1 = Triangle(gc,"ABC",[f,g,g@f])
    d2 = Triangle(gc,"DEF",[h,i,i@h])
    d1.transformer_graphviz()
    d2.transformer_graphviz()
    
    ctn = CatTransfoNat({d1,d2})
    ctn.transformer_graphviz()
    
    
    d3 = Triangle(gc,"EEF",[gc.identite('E'),i,i])
    ctn |= {d3}
    ctn.transformer_graphviz()
    
    for t_n in ctn(abs(ctn),abs(ctn)):
        t_n.transformer_graphviz()
    
171
if __name__ == '__main__':
172
173
    test_TransfoNat()
    test_CatTransfoNat()