Commit 95862442 authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Catégorie transformations naturelles finie

parent 1022c308
...@@ -57,7 +57,7 @@ class Categorie: ...@@ -57,7 +57,7 @@ class Categorie:
NotImplementedError("Les categories filles doivent implementer cette methode.") NotImplementedError("Les categories filles doivent implementer cette methode.")
def __eq__(self,other: 'Categorie') -> bool: def __eq__(self,other: 'Categorie') -> bool:
return self.nom == other.nom and self.objets == other.objets and type(self) == type(other) return issubclass(type(other),Categorie) and self.nom == other.nom and self.objets == other.objets and type(self) == type(other)
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((self.nom,self.objets)) return hash((self.nom,self.objets))
...@@ -85,7 +85,7 @@ class Categorie: ...@@ -85,7 +85,7 @@ class Categorie:
Soit C une catégorie. Soit C une catégorie.
C -= {a_1,a_2,...} supprime les objets a_i de la catégorie C. C -= {a_1,a_2,...} supprime les objets a_i de la catégorie C.
""" """
self -= other self.objets -= other
return self return self
def verifier_coherence(self): def verifier_coherence(self):
......
...@@ -4,6 +4,7 @@ from GrapheDeComposition import GrapheDeComposition, MorphismeGrapheDeCompositio ...@@ -4,6 +4,7 @@ from GrapheDeComposition import GrapheDeComposition, MorphismeGrapheDeCompositio
from Categorie import CategorieDiscrete, Categorie from Categorie import CategorieDiscrete, Categorie
import itertools import itertools
from config import * from config import *
from copy import deepcopy
if GRAPHVIZ_ENABLED: if GRAPHVIZ_ENABLED:
from graphviz import Digraph from graphviz import Digraph
...@@ -17,7 +18,7 @@ class Diagramme(Foncteur): ...@@ -17,7 +18,7 @@ class Diagramme(Foncteur):
def __init__(self, categorie_indexante:Categorie, categorie_indexee:Categorie, def __init__(self, categorie_indexante:Categorie, categorie_indexee:Categorie,
application_objets:dict, application_morphismes:dict, representant:any = None): application_objets:dict, application_morphismes:dict, representant:any = None):
Foncteur.__init__(self,categorie_indexante,categorie_indexee, application_objets, Foncteur.__init__(self,categorie_indexante,categorie_indexee, application_objets,
application_morphismes,representant=representant if representant != None else "Diagramme "+str(Morphisme._id)) application_morphismes,representant=representant+' '+str(self._id) if representant != None else "Diagramme "+str(self._id))
def faire_commuter(self): def faire_commuter(self):
""" """
...@@ -55,16 +56,13 @@ class Diagramme(Foncteur): ...@@ -55,16 +56,13 @@ class Diagramme(Foncteur):
else: else:
MorphismeGrapheDeComposition.identifier_morphismes(composee_depart, composee_arrivee) MorphismeGrapheDeComposition.identifier_morphismes(composee_depart, composee_arrivee)
def implementation(self) -> GrapheDeComposition: def implementation(self) -> Categorie:
""" """
Renvoie la sous-catégorie atteinte par le diagramme. Renvoie la sous-catégorie atteinte par le diagramme.
Le diagramme doit cibler un `GrapheDeComposition`.
""" """
if type(self.cible) != GrapheDeComposition: result = deepcopy(self.cible)
raise Exception("Appel d'implementation sur un diagramme dont la cible n'est pas un graphe de composition.") result.nom = "Implementation du diagramme "+str(self)
result = GrapheDeComposition("Implementation de "+str(self)) result -= {obj for obj in self.cible.objets if obj not in self._app_objets.values()}
for obj in self._app_objets:
result |= self(obj)
return result return result
...@@ -379,6 +377,9 @@ def test_Triangle(): ...@@ -379,6 +377,9 @@ def test_Triangle():
cat |= {f,g,h,i} cat |= {f,g,h,i}
diag = Triangle(cat,['A','B','C'],[f,g,g@f]) diag = Triangle(cat,['A','B','C'],[f,g,g@f])
diag.transformer_graphviz() diag.transformer_graphviz()
impl = diag.implementation()
impl.transformer_graphviz()
class Carre(Diagramme): class Carre(Diagramme):
......
...@@ -64,8 +64,9 @@ class Application(Morphisme): ...@@ -64,8 +64,9 @@ class Application(Morphisme):
import os import os
os.remove(destination) os.remove(destination)
class EnsFinis(Categorie): class CategorieEnsemblesFinis(Categorie):
"""Catégorie des ensembles finis, cette catégorie est infinie, on ajoute uniquement les ensembles dont on a besoin.""" """CategorieEnsFinis peut être abrégé en EnsFinis
Catégorie des ensembles finis, cette catégorie est infinie, on ajoute uniquement les ensembles dont on a besoin."""
def __init__(self, objets:set = None, nom:str = "Catégorie d'ensembles finis"): def __init__(self, objets:set = None, nom:str = "Catégorie d'ensembles finis"):
Categorie.__init__(self,objets,nom) Categorie.__init__(self,objets,nom)
...@@ -77,9 +78,14 @@ class EnsFinis(Categorie): ...@@ -77,9 +78,14 @@ class EnsFinis(Categorie):
result = frozenset() result = frozenset()
for a in source: for a in source:
for b in cible: for b in cible:
if not issubclass(type(a),frozenset):
raise Exception("On s'attend à avoir des frozenset dans l'ensemble source, pas un "+str(type(a))+'\n'+str(source))
if not issubclass(type(b),frozenset):
raise Exception("On s'attend à avoir des frozenset dans l'ensemble cible, pas un "+str(type(b))+'\n'+str(cible))
result |= {Application(a,b,dict(zip(a,prod))) for prod in itertools.product(b,repeat=len(a))} result |= {Application(a,b,dict(zip(a,prod))) for prod in itertools.product(b,repeat=len(a))}
return result return result
EnsFinis = CategorieEnsemblesFinis
def test_EnsFinis(): def test_EnsFinis():
......
...@@ -145,8 +145,7 @@ class GrapheDeComposition(CategorieLibre): ...@@ -145,8 +145,7 @@ class GrapheDeComposition(CategorieLibre):
def identite(self,objet:any) -> MorphismeGrapheDeComposition: def identite(self,objet:any) -> MorphismeGrapheDeComposition:
if objet not in self.__identites: if objet not in self.__identites:
print(objet) raise Exception("Tentative d'acceder a l'identite d'un objet qui n'est pas dans la categorie, "+str(objet)+" n'est pas dans "+str(self.__identites))
print(self.__identites)
return self.__identites[objet] return self.__identites[objet]
def __ior__(self,ensemble_objet_ou_morphisme:set): def __ior__(self,ensemble_objet_ou_morphisme:set):
...@@ -183,8 +182,14 @@ class GrapheDeComposition(CategorieLibre): ...@@ -183,8 +182,14 @@ class GrapheDeComposition(CategorieLibre):
self.__morph_entrants[objet_ou_morphisme.cible] -= {objet_ou_morphisme} self.__morph_entrants[objet_ou_morphisme.cible] -= {objet_ou_morphisme}
self.__morph_sortants[objet_ou_morphisme.source] -= {objet_ou_morphisme} self.__morph_sortants[objet_ou_morphisme.source] -= {objet_ou_morphisme}
else: else:
self.objets -= {objet} CategorieLibre.__isub__(self,{objet_ou_morphisme})
del self.__identites[objet] del self.__identites[objet_ou_morphisme]
for m in self.__morph_entrants[objet_ou_morphisme]:
self.__morph_sortants[m] -= {objet_ou_morphisme}
for m in self.__morph_sortants[objet_ou_morphisme]:
self.__morph_entrants[m] -= {objet_ou_morphisme}
del self.__morph_entrants[objet_ou_morphisme]
del self.__morph_sortants[objet_ou_morphisme]
if TOUJOURS_VERIFIER_COHERENCE: if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence() self.verifier_coherence()
return self return self
......
...@@ -6,6 +6,7 @@ from Interaction import Interaction ...@@ -6,6 +6,7 @@ from Interaction import Interaction
from CommaCategorie import ObjetCommaCategorie from CommaCategorie import ObjetCommaCategorie
from Categorie import Categorie from Categorie import Categorie
from Diagramme import Diagramme from Diagramme import Diagramme
from EnsFinis import Application, EnsFinis
if GRAPHVIZ_ENABLED: if GRAPHVIZ_ENABLED:
from graphviz import Digraph from graphviz import Digraph
...@@ -19,7 +20,7 @@ class TransformationNaturelle(Morphisme): ...@@ -19,7 +20,7 @@ class TransformationNaturelle(Morphisme):
nb_viz = 0 nb_viz = 0
__id = 0 __id = 0
def __init__(self, foncteur_source:Foncteur, foncteur_cible:Foncteur, composantes:dict): def __init__(self, foncteur_source:Foncteur, foncteur_cible:Foncteur, composantes:Application):
""" """
`foncteur_source` et `foncteur_cible` deux foncteurs de C vers D. `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`).""" `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`)."""
...@@ -28,7 +29,7 @@ class TransformationNaturelle(Morphisme): ...@@ -28,7 +29,7 @@ class TransformationNaturelle(Morphisme):
self.__F = foncteur_source self.__F = foncteur_source
self.__G = foncteur_cible self.__G = foncteur_cible
if self.__F == self.__G and all([composantes[e]==self__D.identite(self__F(e)) for e in composantes]): if self.__F == self.__G and all([composantes(e)==self.__D.identite(self.__F(e)) for e in composantes.source]):
is_identite = True is_identite = True
else: else:
is_identite = False is_identite = False
...@@ -44,7 +45,7 @@ class TransformationNaturelle(Morphisme): ...@@ -44,7 +45,7 @@ class TransformationNaturelle(Morphisme):
"""Renvoie l'image d'un objet de C.""" """Renvoie l'image d'un objet de C."""
if obj not in self.__C.objets: if obj not in self.__C.objets:
raise Exception("L'objet "+str(obj)+" n'appartient pas à la categorie C = "+str(self.__C)) raise Exception("L'objet "+str(obj)+" n'appartient pas à la categorie C = "+str(self.__C))
return self._composantes[obj] return self._composantes(obj)
def __eq__(self, other:'TransformationNaturelle') -> bool: def __eq__(self, other:'TransformationNaturelle') -> bool:
if not issubclass(type(other),TransformationNaturelle): if not issubclass(type(other),TransformationNaturelle):
...@@ -52,13 +53,18 @@ class TransformationNaturelle(Morphisme): ...@@ -52,13 +53,18 @@ class TransformationNaturelle(Morphisme):
return self.source == other.source and self.cible == other.cible and self._composantes == other._composantes return self.source == other.source and self.cible == other.cible and self._composantes == other._composantes
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((self.source,self.cible,frozenset(self._composantes.items()))) return hash((self.source,self.cible,self._composantes))
def __str__(self): def __str__(self):
return "TN"+str(self.__id) return "TN"+str(self.__id)
def __matmul__(self, other:'TransformationNaturelle') -> 'TransformationNaturelle':
"""Composition verticale des transformations naturelles."""
return TransformationNaturelle(other.source,self.cible,
Application(other._composantes.source,self._composantes.cible,{c:self(c)@other(c) for c in self.__C.objets}))
def as_interaction(self) -> Interaction: def as_interaction(self) -> Interaction:
return Interaction(self.__F,self.__G,{ObjetCommaCategorie(c,self(c),c) for c in self._composantes}) return Interaction(self.__F,self.__G,{ObjetCommaCategorie(c,self(c),c) for c in self._composantes.source})
def verifier_coherence(self): def verifier_coherence(self):
if self.source.source != self.cible.source: if self.source.source != self.cible.source:
...@@ -66,12 +72,12 @@ class TransformationNaturelle(Morphisme): ...@@ -66,12 +72,12 @@ class TransformationNaturelle(Morphisme):
if self.source.cible != self.cible.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)) 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: if set(self._composantes.source) != self.__C.objets:
raise Exception("Incoherence TransformationNaturelle : les objets de la categorie C ne sont pas les memes que les antecedants des composantes\n"+\ 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)) str(self._composantes)+"\n"+str(self.__C.objets))
for source,cible in itertools.product(self.__C.objets,repeat=2): for source,cible in itertools.product(self.__C.objets,repeat=2):
for f in self.__C(source,cible): for f in self.__C({source},{cible}):
c1,c2 = [f.source,f.cible] c1,c2 = [f.source,f.cible]
if self.__G(f)@self(c1) != self(c2)@self.__F(f): if self.__G(f)@self(c1) != self(c2)@self.__F(f):
raise Exception("Incoherence TransformationNaturelle : la commutativité des diagrammes est pas respectee"+\ raise Exception("Incoherence TransformationNaturelle : la commutativité des diagrammes est pas respectee"+\
...@@ -101,10 +107,15 @@ def test_TransfoNat(): ...@@ -101,10 +107,15 @@ def test_TransfoNat():
d1.transformer_graphviz() d1.transformer_graphviz()
d2.transformer_graphviz() d2.transformer_graphviz()
tn = TransfoNat(d1,d2,{1:j,2:k,3:l}) tn = TransfoNat(d1,d2,Application(d1.source.objets,gc(abs(gc),abs(gc)),{1:j,2:k,3:l}))
tn.transformer_graphviz() tn.transformer_graphviz()
tn2 = TransfoNat(d1,d2,{2:k,3:l,1:j})
print(hash(tn)==hash(tn2)) d3 = Triangle(gc,"EEF",[gc.identite('E'),i,i])
tn2 = TransfoNat(d2,d3,Application(d1.source.objets,gc(abs(gc),abs(gc)),{1:h,2:gc.identite('E'),3:gc.identite('F')}))
tn2.transformer_graphviz()
tn3 = tn2@tn
tn3.transformer_graphviz()
class CategorieTransformationsNaturelles(Categorie): class CategorieTransformationsNaturelles(Categorie):
"""CategorieTransformationsNaturelles peut être abrégé en CatTransfoNat. """CategorieTransformationsNaturelles peut être abrégé en CatTransfoNat.
...@@ -118,12 +129,57 @@ class CategorieTransformationsNaturelles(Categorie): ...@@ -118,12 +129,57 @@ class CategorieTransformationsNaturelles(Categorie):
def __call__(self, source:set, cible:set) -> set: def __call__(self, source:set, cible:set) -> set:
result = set() result = set()
for a in source: if len(source) > 0:
C = list(source)[0].source
D = list(source)[0].cible
for a in source:
for b in cible: for b in cible:
# on cherche toutes les transformations naturelles du diagramme a:C->D vers le diagramme b:C->D
obj_index = C.objets
fleches_arrivee = frozenset(D(abs(D),abs(D)))
ens = EnsFinis({obj_index,fleches_arrivee})
for app in ens({obj_index},{fleches_arrivee}):
for fleche_index in C(abs(C),abs(C)):
if app(fleche_index.source).cible != b(fleche_index).source:
break
if a(fleche_index).cible != app(fleche_index.cible).source:
break
if b(fleche_index)@app(fleche_index.source) != app(fleche_index.cible)@a(fleche_index):
break
else:
result |= {TransfoNat(a,b,app)}
return result
CatTransfoNat = CategorieTransformationsNaturelles 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__': if __name__ == '__main__':
test_TransfoNat() test_TransfoNat()
\ No newline at end of file test_CatTransfoNat()
\ No newline at end of file
...@@ -40,4 +40,7 @@ envoyer lien ...@@ -40,4 +40,7 @@ envoyer lien
documentation (@, return) documentation (@, return)
type helpers type helpers
Changer les cluster de transformer_graphviz de Application pour des ovales Changer les cluster de transformer_graphviz de Application pour des ovales
\ No newline at end of file
Corriger le cas où on supprime un morphisme alors qu'une composée était mappé dessus.
(par exemple gof = ioh, on supprime h, ioh n'existe plus, il faut que gof soit delinké)
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment