Commit 1022c308 authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

EnsFinis fini

parent 9303d85b
......@@ -13,10 +13,7 @@ class Categorie:
"""Classe abstraite qui définit ce qu'est une catégorie.
Toutes les catégories qui héritent de Categorie doivent surcharger :
- l'opérateur __call__ tel que si C est une catégorie :
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}
C({a_i},b) renvoie l'ensemble des flèches d'un élément de {a_i} vers b
C(a,{b_i}) renvoie l'ensemble des flèches de a vers un élément de {b_i}
C(a,b) renvoie l'ensemble des flèches de a vers b.
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}.
- la méthode identite(self,objet) qui renvoie l'identité de l'objet.
On reprend la notation classique C(a,b) pour les flèches de a à b
......@@ -35,9 +32,10 @@ class Categorie:
"""
nb_viz = 0 # nombre de graphes graphviz générés
def __init__(self,nom: str = "Catégorie"):
def __init__(self, objets:set = set(), nom:str = "Catégorie"):
self.nom = nom
self.objets = frozenset()
self |= objets
def __hash__(self) -> int:
return hash(self.objets)
......@@ -46,10 +44,7 @@ class Categorie:
"""
Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie :
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}
C({a_i},b) renvoie l'ensemble des flèches d'un élément de {a_i} vers b
C(a,{b_i}) renvoie l'ensemble des flèches de a vers un élément de {b_i}
C(a,b) renvoie l'ensemble des flèches de a vers b.
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}.
"""
raise NotImplementedError("Les categories filles doivent implementer cette methode pour renvoyer les morphismes C(a,b) (fleches de a vers b) et C(a) (identite de a)")
......@@ -169,8 +164,7 @@ class Categorie:
def test_Categorie():
from GrapheDeComposition import GC,MGC
GC = GC()
GC |= {'A','B','C'}
GC = GC({'A','B','C'})
morphismes = [MGC('A','B','f'),MGC('B','C','g')]
GC |= morphismes
GC.transformer_graphviz()
......@@ -185,11 +179,10 @@ class SousCategoriePleine(Categorie):
def __init__(self, categorie_originelle:Categorie, objets:set):
"""On construit la sous-catégorie pleine de `categorie_originelle` qui contient l'ensemble des `objets`."""
Categorie.__init__(self,"Sous-catégorie pleine de "+str(categorie_originelle)+ " ayant pour objets "+str(objets))
self |= objets
Categorie.__init__(self,objets,"Sous-catégorie pleine de "+str(categorie_originelle)+ " ayant pour objets "+str(objets))
self.categorie_originelle = categorie_originelle
def __call__(self, source:any, cible:any) -> set:
def __call__(self, source:set, cible:set) -> set:
return self.categorie_originelle(source,cible)
def identite(self,objet:any) -> Morphisme:
......@@ -197,8 +190,7 @@ class SousCategoriePleine(Categorie):
def test_SousCategoriePleine():
from GrapheDeComposition import GC,MGC
GC = GC()
GC |= set("ABCDE")
GC = GC(set("ABCDE"))
morphismes = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','D','h'),MGC('D','E','i'),MGC('C','E','j')]
GC |= morphismes
GC.transformer_graphviz()
......@@ -233,22 +225,14 @@ class CategorieDiscrete(Categorie):
def __hash__(self) -> int:
return hash(self.source)
def __call__(self,source:any,cible:any) -> set():
if source in self.objets:
# source est de type a
source = {source}
if cible in self.objets:
# cible est de type b
cible = {cible}
#maintenant tout est de la forme {a_i} vers {b_i}
def __call__(self,source:set,cible:set) -> set():
return {CategorieDiscrete.Identite(a) for a in source for b in cible if a == b}
def identite(self,objet:any) -> 'CategorieDiscrete.Identite':
return CategorieDiscrete.Identite(objet)
def test_CategorieDiscrete():
cat_discrete = CategorieDiscrete()
cat_discrete |= set("ABCDE")
cat_discrete = CategorieDiscrete(set("ABCDE"))
cat_discrete.transformer_graphviz(afficher_identites=True)
......
......@@ -11,8 +11,8 @@ class CategorieDiagrammes(Categorie):
/!\ si on ajoute un objet à la catégorie C après la création de la catégorie des diagrammes, cet objet n'aura pas de diagramme constant associé /!\
"""
def __init__(self, C:Categorie, nom:str = None):
Categorie.__init__(self,"Categorie des diagrammes sur "+str(C) if nom == None else nom)
def __init__(self, C:Categorie, objets:set = set(), nom:str = None):
Categorie.__init__(self,objets,"Categorie des diagrammes sur "+str(C) if nom == None else nom)
self.categorie_initiale = C
self |= {DiagrammeObjets(C,obj) for obj in C.objets }
\ No newline at end of file
......@@ -19,8 +19,8 @@ class CategorieLibre(Categorie):
"""
nb_viz = 0
def __init__(self, nom:str="Categorie libre"):
Categorie.__init__(self,nom)
def __init__(self, objets:set = set(), nom:str="Categorie libre"):
Categorie.__init__(self,objets,nom)
def fleches_elem(self, source:any, cible:any, inclure_id:bool = True) -> set:
"""
......@@ -52,21 +52,11 @@ class CategorieLibre(Categorie):
# raise Exception("Incoherence CategorieLibre : le(s) morphisme(s) entrant(s) "+str(self.morph_entrants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1})+\
# "n'est pas un morphisme de la catégorie")
def __call__(self, source:any, cible:any) -> set:
def __call__(self, source:set, cible:set) -> set:
"""Soit C est une catégorie:
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}
C({a_i},b) renvoie l'ensemble des flèches d'un élément de {a_i} vers b
C(a,{b_i}) renvoie l'ensemble des flèches de a vers un élément de {b_i}
C(a,b) renvoie l'ensemble des flèches de a vers b.
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}.
Pour la catégorie libre, on doit énumérer tous les chemins et les composer.
"""
if source in self.objets:
# source est de type a
source = {source}
if cible in self.objets:
# cible est de type b
cible = {cible}
#maintenant tout est de la forme {a_i} vers {b_i}
return {morph for a in source for b in cible for morph in self.enumerer_composees(a,b)}
def enumerer_composees_sans_cycle(self, source:any, cible:any, noeuds_deja_visites:tuple=tuple()) -> frozenset():
......
......@@ -28,14 +28,7 @@ class CategoriePreordre(Categorie):
"""
Il n'y a qu'un seul morphisme pré-ordre entre deux objets d'une catégorie pré-ordre.
"""
def __call__(self,source:any,cible:any) -> set:
if source in self.objets:
# source est de type a
source = {source}
if cible in self.objets:
# cible est de type b
cible = {cible}
#maintenant tout est de la forme {a_i} vers {b_i}
def __call__(self,source:set,cible:set) -> set:
return {MorphismePreordre(a,b) for a in source for b in cible}
def identite(self,objet:any) -> MorphismePreordre:
......
......@@ -72,7 +72,7 @@ class CommaCategorie(Categorie):
"""
def __init__(self, T:Foncteur, S:Foncteur, nom:str = None):
Categorie.__init__(self,"Comma-catégorie ("+str(T)+'|'+str(S)+')' if nom == None else nom)
Categorie.__init__(self,set(),"Comma-catégorie ("+str(T)+'|'+str(S)+')' if nom == None else nom)
if T.cible != S.cible:
raise Exception("Incoherence CommaCategorie : T et S de cibles differentes : "+str(T)+" vs "+str(S))
self.__C = T.cible
......@@ -90,19 +90,12 @@ class CommaCategorie(Categorie):
'''L'identité pour un objet (e,f,d) est la paire d'identité de T(e) et S(d).'''
return FlecheCommaCategorie(objet,objet,self.__C.identite(self._T(objet.e)),self.__C.identite(self._S(objet.d)))
def __call__(self, source:ObjetCommaCategorie, cible:ObjetCommaCategorie) -> set:
if source in self.objets:
# source est de type a
source = {source}
if cible in self.objets:
# cible est de type b
cible = {cible}
#maintenant tout est de la forme {a_i} vers {b_i}
def __call__(self, source:set, cible:set) -> set:
result = set()
for a in source:
for b in cible:
for k in self._T.source(a.e,b.e):
for h in self._S.source(a.d,b.d):
for k in self._T.source({a.e},{b.e}):
for h in self._S.source({a.d},{b.d}):
if b.f@self._T(k) == self._S(h)@a.f:
result |= {FlecheCommaCategorie(a,b,k,h)}
return result
......
......@@ -278,8 +278,7 @@ class DiagrammeObjets(Diagramme):
def __init__(self, categorie_indexee:Categorie, objets_a_selectionner:set):
"""`objets_a_selectionner` est une listes d'objets à selectionner dans la catégorie."""
cat = CategorieDiscrete("Objets")
cat |= set(range(len(objets_a_selectionner)))
cat = CategorieDiscrete(set(range(len(objets_a_selectionner))),"Objets")
objets_a_selectionner = list(objets_a_selectionner)
Diagramme.__init__(self,cat,categorie_indexee,{i:objets_a_selectionner[i] for i in range(len(objets_a_selectionner))},dict(),representant="O("+','.join(map(str,objets_a_selectionner))+")")
......@@ -293,8 +292,7 @@ def test_DiagrammeObjets():
class Fleche(Diagramme):
"""Permet de selectionner deux objets réliés par une flèche"""
_cat = GrapheDeComposition("1 Flèche")
_cat |= {1,2}
_cat = GrapheDeComposition({1,2},"1 Flèche")
_f = MorphismeGrapheDeComposition(1,2,'f')
_cat |= _f
......@@ -313,8 +311,7 @@ class Chemins(Diagramme):
"""Diagramme des chemins entre deux objets"""
def __init__(self,categorie_indexee:Categorie, source:any, cible:any):
cat = GrapheDeComposition('Chemins')
cat |= {1,2}
cat = GrapheDeComposition({1,2},'Chemins')
chemins = list(categorie_indexee(source,cible))
morphismes = list(map(lambda x:MorphismeGrapheDeComposition(1,2,x),range(len(chemins))))
cat |= morphismes
......@@ -322,16 +319,14 @@ class Chemins(Diagramme):
{morphismes[i]:chemins[i] for i in range(len(chemins))}, 'Chemins de '+str(source)+' vers '+str(cible))
def test_Chemins():
cat = GC()
cat |= set("ABCD")
cat = GC(set("ABCD"))
f,g,h,i = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','D','h'),MGC('D','C','i')]
cat |= {f,g,h,i}
diag = Chemins(cat,'A','C')
diag.transformer_graphviz()
class Parallele(Diagramme):
_cat = GrapheDeComposition("Parallele")
_cat |= {1,2}
_cat = GrapheDeComposition({1,2},"Parallele")
f = MorphismeGrapheDeComposition(1,2,'f')
g = MorphismeGrapheDeComposition(1,2,'g')
morphismes = [f,g]
......@@ -349,16 +344,14 @@ class Parallele(Diagramme):
'Paralleles '+str(image_morphismes[0])+' et '+str(image_morphismes[1]))
def test_Parallele():
cat = GC()
cat |= set("ABCD")
cat = GC(set("ABCD"))
f,g,h,i = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','D','h'),MGC('D','C','i')]
cat |= {f,g,h,i}
diag = Parallele(cat,['A','C'],[g@f,i@h])
diag.transformer_graphviz()
class Triangle(Diagramme):
_cat = GrapheDeComposition("Triangle")
_cat |= {1,2,3}
_cat = GrapheDeComposition({1,2,3},"Triangle")
f = MorphismeGrapheDeComposition(1,2,'f')
g = MorphismeGrapheDeComposition(2,3,'g')
h = MorphismeGrapheDeComposition(1,3,'h')
......@@ -389,8 +382,7 @@ def test_Triangle():
class Carre(Diagramme):
_cat = GrapheDeComposition("Carre")
_cat |= {1,2,3,4}
_cat = GrapheDeComposition({1,2,3,4},"Carre")
f = MorphismeGrapheDeComposition(1,2,'f')
g = MorphismeGrapheDeComposition(2,4,'g')
h = MorphismeGrapheDeComposition(1,3,'h')
......@@ -429,8 +421,7 @@ class DiagrammeIdentite(Diagramme):
,{e:e for e in categorie(categorie.objets,categorie.objets)},"Id("+str(categorie)+')')
def test_DiagrammeIdentite():
cat = GC()
cat |= set("ABCD")
cat = GC(set("ABCD"))
f,g,h,i = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','D','h'),MGC('D','C','i')]
cat |= {f,g,h,i}
diag = DiagrammeIdentite(cat)
......
from Morphisme import Morphisme
from Categorie import Categorie
import itertools
from config import *
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
class Application(Morphisme):
"""Une application A de E vers F (A : E->F) est une relation binaire telle que pour tout x de E il existe un y de F tel que F(x) = y <=> (x,y) \in A.
On représente l'application par un dictionnaire où les clés sont des éléments de E et les valeurs des éléments de F.
"""
nb_viz = 0
def __init__(self, source:frozenset, cible:frozenset, application:dict):
Morphisme.__init__(self,source,cible,None, source==cible and application == {x:x for x in source})
self._app = application
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
def verifier_coherence(self):
if len(frozenset(self._app.keys())) != len(self._app.keys()):
raise Exception("Incoherence Application : un element a plusieurs images "+str(self._app))
if frozenset(self._app.keys()) != self.source:
raise Exception("Incoherence Application : l'application n'a pas suffisament ou a trop d'antecedants\n"+str(self._app)+'\n'+str(self.source))
def __eq__(self,other:'Application') -> bool:
if not issubclass(type(other),Application):
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._app == other._app
def __hash__(self) -> int:
return hash((self.source,self.cible,frozenset(self._app.items())))
def __call__(self, element:any) -> any:
"""Renvoie l'image de l'`element` par l'application."""
return self._app[element]
def __matmul__(self, other:'Application') -> 'Application':
return Application(other.source,self.cible,frozenset({e:self(other(e)) for e in other.source}))
def transformer_graphviz(self, destination:any=None):
Application.nb_viz += 1
if destination == None:
destination = "graphviz/application"+str(Application.nb_viz)
graph = Digraph('application')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
graph.attr(label="Application "+self.representant)
with graph.subgraph(name='cluster_0') as cluster:
cluster.attr(label=str(self.source))
for o in self.source:
cluster.node(str(o))
with graph.subgraph(name='cluster_1') as cluster:
cluster.attr(label=str(self.cible))
for o in self.cible:
cluster.node(str(o)+"'")
for source in self._app:
graph.edge(str(source),str(self(source))+"'")
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
import os
os.remove(destination)
class EnsFinis(Categorie):
"""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"):
Categorie.__init__(self,objets,nom)
def identite(self, ensemble:frozenset()) -> Application:
return Application(ensemble,ensemble,frozenset({e:e for e in ensemble}))
def __call__(self, source:set, cible:set) -> frozenset:
result = frozenset()
for a in source:
for b in cible:
result |= {Application(a,b,dict(zip(a,prod))) for prod in itertools.product(b,repeat=len(a))}
return result
def test_EnsFinis():
cat = EnsFinis(set(map(frozenset,[{1,2},{3,4,5}])))
cat.transformer_graphviz()
for fleche in cat({frozenset({1,2})},{frozenset({3,4,5})}):
fleche.transformer_graphviz()
if __name__ == '__main__':
test_EnsFinis()
\ No newline at end of file
......@@ -124,11 +124,11 @@ class GrapheDeComposition(CategorieLibre):
- faire commuter un diagramme qui a pour cible ce modèle pour identifier des flèches
"""
def __init__(self,nom:str = 'Graphe de composition'):
CategorieLibre.__init__(self,nom)
def __init__(self, objets:set = set(), nom:str = 'Graphe de composition'):
self.__identites = dict()
self.__morph_entrants = defaultdict(frozenset)
self.__morph_sortants = defaultdict(frozenset)
CategorieLibre.__init__(self,objets,nom)
def verifier_coherence(self):
CategorieLibre.verifier_coherence(self)
......
......@@ -93,14 +93,7 @@ class CategorieInteractions(CategorieDiagrammes):
nb_viz = 0
def __call__(self, source:Foncteur, cible:Foncteur) -> set:
if source in self.objets:
# source est de type a
source = {source}
if cible in self.objets:
# cible est de type b
cible = {cible}
#maintenant tout est de la forme {a_i} vers {b_i}
def __call__(self, source:set, cible:set) -> set:
result = set()
for a in source:
for b in cible:
......
......@@ -4,6 +4,8 @@ from config import *
import itertools
from Interaction import Interaction
from CommaCategorie import ObjetCommaCategorie
from Categorie import Categorie
from Diagramme import Diagramme
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
......@@ -19,7 +21,7 @@ class TransformationNaturelle(Morphisme):
def __init__(self, foncteur_source:Foncteur, foncteur_cible:Foncteur, composantes:dict):
"""
`foncteurs1` et `foncteurs2` 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`)."""
self.__C = foncteur_source.source
self.__D = foncteur_source.cible
......@@ -44,7 +46,14 @@ class TransformationNaturelle(Morphisme):
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 __str__(self):
return "TN"+str(self.__id)
......@@ -94,7 +103,27 @@ def test_TransfoNat():
tn = TransfoNat(d1,d2,{1:j,2:k,3:l})
tn.transformer_graphviz()
tn2 = TransfoNat(d1,d2,{2:k,3:l,1:j})
print(hash(tn)==hash(tn2))
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, source:set, cible:set) -> set:
result = set()
for a in source:
for b in cible:
CatTransfoNat = CategorieTransformationsNaturelles
if __name__ == '__main__':
test_TransfoNat()
\ No newline at end of file
......@@ -40,4 +40,4 @@ envoyer lien
documentation (@, return)
type helpers
laisser la possiblité de fournir des objets au constructeur de Catégorie
\ No newline at end of file
Changer les cluster de transformer_graphviz de Application pour des ovales
\ 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