from Foncteur import Foncteur from config import * import itertools from Interaction import Interaction from CommaCategorie import ObjetCommaCategorie from Categorie import Categorie, mise_en_cache_call from Morphisme import Morphisme from Diagramme import Diagramme from EnsFinis import Application, EnsFinis if GRAPHVIZ_ENABLED: from graphviz import Digraph from typing import * class TransformationNaturelle(Interaction): """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)""" nb_viz = 0 __id = 0 def __init__(self, foncteur_source:Foncteur, foncteur_cible:Foncteur, composantes:dict, nom:str = None): """ `foncteur_source` et `foncteur_cible` deux foncteurs de C vers D. `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 self._composantes = composantes 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) self.__id = TransfoNat.__id #pour la transformation en str TransfoNat.__id += 1 if TOUJOURS_VERIFIER_COHERENCE: self.verifier_coherence() def __call__(self, obj:Any) -> Morphisme: """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.""" if obj not in self.__C.objets: raise Exception("L'objet "+str(obj)+" n'appartient pas à la categorie C = "+str(self.__C)) return self._composantes[obj] 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: return hash((self.source,self.cible,frozenset(self._composantes.items()))) def __matmul__(self, other:'TransformationNaturelle') -> 'TransformationNaturelle': """Composition verticale des transformations naturelles.""" return TransformationNaturelle(other.source,self.cible,{c:self(c)@other(c) for c in self.__C.objets}) 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)) if set(self._composantes.keys()) != self.__C.objets: 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)) for source,cible in itertools.product(self.__C.objets,repeat=2): for f in self.__C({source},{cible}): c1,c2 = [f.source,f.cible] if self.__G(f)@self(c1) != self(c2)@self.__F(f): raise Exception("Incoherence TransformationNaturelle : la commutativité des diagrammes est pas respectee\n"+\ "Sf = "+str(self.__F(f))+"\ntC' = "+str(self(c2))+"\ntC = "+str(self(c1))+"\nTf = "+str(self.__G(f))+\ "\ntC' o Sf != Tf o tC : "+str(self(c2)@self.__F(f)) + " != "+str(self.__G(f)@self(c1))) TransfoNat = TransformationNaturelle def test_TransfoNat(): 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} gc.transformer_graphviz() 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() tn = TransfoNat(d1,d2,{1:j,2:k,3:l}) tn.transformer_graphviz() d3 = Triangle(gc,"EEF",[gc.identite('E'),i,i]) tn2 = TransfoNat(d2,d3,{1:h,2:gc.identite('E'),3:gc.identite('F')}) tn2.transformer_graphviz() tn3 = tn2@tn tn3.transformer_graphviz() 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. """ def identite(self, diag:Diagramme) -> TransfoNat: return TransfoNat(diag,diag,{o:diag.cible.identite(diag(o)) for o in diag.source.objets}) def __call__(self, sources:set, cibles:set) -> Generator[TransfoNat,None,None]: if len(sources) > 0: J = list(sources)[0].source C = list(sources)[0].cible 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 obj_index = J.objets fleches_arrivee = frozenset(C({source(o) for o in set(J.objets)},{cible(o) for o in set(J.objets)})) # flèches de l'image de a vers l'image de b ens = EnsFinis({obj_index,fleches_arrivee}) for app in ens({obj_index},{fleches_arrivee}): for fleche_index in J[abs(J),abs(J)]: if app(fleche_index.source).cible != cible(fleche_index).source: break if source(fleche_index).cible != app(fleche_index.cible).source: break if cible(fleche_index)@app(fleche_index.source) != app(fleche_index.cible)@source(fleche_index): break else: yield TransfoNat(source,cible,app.as_dict()) CatTransfoNat = CategorieTransformationsNaturelles 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() if __name__ == '__main__': test_TransfoNat() test_CatTransfoNat()