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

Catégorie squelettique

parent 78add5ea
......@@ -228,6 +228,9 @@ class Categorie:
def existe_morphisme(self, source:Any, cible:Any) -> Union[Morphisme,None]:
"""Renvoie un morphisme de `source` à `cible` s'il existe, renvoie None sinon."""
elem = next(self[{source},{cible}],None)
if elem != None:
return elem
return next(self({source},{cible}),None)
def existe_morphisme_elementaire(self, source:Any, cible:Any) -> Union[Morphisme,None]:
......
......@@ -21,7 +21,8 @@ from graphviz import Digraph
class GrapheCompositionAleatoire(GC):
"""Construit un graphe de composition aléatoire."""
"""Construit un graphe de composition aléatoire.
GrapheCompositionAleatoire peut être abrégé en GCA"""
def __init__(self, nb_fleches:Union[int,None] = None, nb_tentatives_complexification_loi_de_compo:Union[int,None] = None, nb_tentatives_correction_composition:Union[Callable,int,None] = None, nom:str = "Catégorie Aléatoire"):
"""`nb_fleches` est le nombre de flèches élémentaires dans la catégorie aléatoire.
`nb_tentatives_complexification_loi_de_compo` détermine à quel point la loi de composition sera complexifiée,
......@@ -214,6 +215,7 @@ class GrapheCompositionAleatoire(GC):
if e.is_identite or e in self._fleches_elem:
yield e
GCA = GrapheCompositionAleatoire
def test_GrapheCompositionAleatoire():
random.seed(1)
......
......@@ -51,17 +51,16 @@ class CategorieLibre(Categorie):
if morphisme in self[{morphisme.source},{morphisme.cible}]:
yield morphisme
else:
def enumerer_chemin_elem_sans_cycle(source:Any, cible:Any, noeuds_deja_visites:tuple=tuple()) -> frozenset():
def enumerer_chemin_elem_sans_cycle(source:Any, cible:Any, morph_deja_visites:frozenset=frozenset()) -> frozenset:
if source == cible:
return frozenset({(self.identite(source),)})
if source not in noeuds_deja_visites:
noeuds_deja_visites = noeuds_deja_visites + (source,)
composees_resultat = frozenset()
for morph in self[{source},self.objets]:
for composition_candidate in enumerer_chemin_elem_sans_cycle(morph.cible, cible, noeuds_deja_visites):
composees_resultat = frozenset()
for morph in self[{source},self.objets]:
if morph not in morph_deja_visites:
for composition_candidate in enumerer_chemin_elem_sans_cycle(morph.cible, cible, morph_deja_visites|{morph}):
composees_resultat |= {composition_candidate+(morph,)}
return composees_resultat
return frozenset()
return composees_resultat
for chemin in enumerer_chemin_elem_sans_cycle(morphisme.source,morphisme.cible):
resultat = functools.reduce(lambda x,y:x@y,chemin)
if resultat == morphisme:
......
from Categorie import Categorie
from Foncteur import Foncteur
from Morphisme import Morphisme
from typing import *
......@@ -48,19 +49,20 @@ class CategoriePreordre(Categorie):
if self.existe_morphisme(source,cible) != None:
yield MorphismePreordre(source,cible)
CatFine = CategoriePreordre
def __getitem__(self, couple_sources_cibles:tuple) -> Generator[MorphismePreordre,None,None]:
sources,cibles = couple_sources_cibles
for source in sources:
for cible in cibles:
if self.existe_morphisme_elementaire(source,cible) != None:
yield MorphismePreordre(source,cible)
def test_CategoriePreordre():
cat = CategoriePreordre()
cat |= set("ABCDEF")
f,g,h,i,j,k,l = [MPO('A','B','f'),MPO('B','C','g'),MPO('D','E','h'),MPO('E','F','i'),MPO('A','D','j'),MPO('B','E','k'),MPO('C','F','l')]
cat |= {f,g,h,i,j,k,l}
cat.transformer_graphviz()
CatFine = CategoriePreordre
class CategoriePreordreEngendree(CategoriePreordre):
"""La catégorie préordre engendrée par une catégorie C est une sous-catégorie maximale C' de C
telle qu'il n'y a pas plus de deux flèches entre deux objets de C'."""
telle qu'il n'y a pas plus de deux flèches entre deux objets de C'.
L'attribut `foncteur_engendre` est le foncteur de la catégorie initiale vers la catégorie préordre engendrée."""
def __init__(self, categorie:Categorie, nom:str = None):
self._categorie_initiale = categorie
......@@ -69,6 +71,14 @@ class CategoriePreordreEngendree(CategoriePreordre):
def existe_morphisme(self, source:Any, cible:Any) -> Union[Morphisme,None]:
return self._categorie_initiale.existe_morphisme(source,cible)
def existe_morphisme_elementaire(self, source:Any, cible:Any) -> Union[Morphisme,None]:
return self._categorie_initiale.existe_morphisme_elementaire(source,cible)
@property
def foncteur_engendre(self):
c_i = self._categorie_initiale
return Foncteur(c_i,self,{o:o for o in self.objets},{m:next(self({m.source},{m.cible})) for m in c_i[c_i.objets,c_i.objets]})
def test_CategoriePreordreEngendree():
from GrapheDeComposition import GC,MGC
......@@ -82,5 +92,7 @@ def test_CategoriePreordreEngendree():
c_po = CategoriePreordreEngendree(gc)
c_po.transformer_graphviz()
c_po.foncteur_engendre.transformer_graphviz()
if __name__ == '__main__':
test_CategoriePreordre()
\ No newline at end of file
test_CategoriePreordreEngendree()
\ No newline at end of file
from Categorie import Categorie
from CategoriePreordre import CatFine,CategoriePreordreEngendree
from Foncteur import Foncteur
from CategoriePreordre import CatFine,MPO
from Diagramme import DiagrammeIdentite
from config import *
from typing import *
import copy
......@@ -27,7 +29,7 @@ class CategorieSquelettique(CatFine):
tels qu'un objet en précède un autre dans la liste uniquement s'il n'y a pas de morphisme du deuxième vers le premier.
Renvoie une liste vide si la catégorie n'a pas d'objet."""
if len(self.objets) > 0:
copie = copy.deepcopy(self)
copie = copy.copy(self)
result = []
while len(copie.objets) > 0:
objets_sans_predecesseurs = [obj for obj in copie.objets if len({e for e in set(copie(copie.objets,{obj})) if not e.is_identite}) == 0]
......@@ -42,29 +44,77 @@ class CategorieSquelettique(CatFine):
CatFine.verifier_coherence(self)
self.tri_topologique()
class CategorieSquelettiqueEngendree(CategoriePreordreEngendree,CategorieSquelettique):
class CategorieSquelettiqueEngendree(CategorieSquelettique):
"""La catégorie squelettique engendrée par une catégorie C est une sous-catégorie maximale C' de C
telle qu'il n'y a pas plus de deux flèches entre deux objets de C'.
Si la catégorie initiale n'a pas un graphe sous-jacent acyclique, une Exception est levée."""
Si la catégorie initiale n'a pas un graphe sous-jacent acyclique, on créé un objet par classe d'équivalence ou la relation
d'équivalence est a ~ b ssi il existe un morphisme de a vers b et un morphisme de b vers a.
L'attribut `foncteur_engendre` est le foncteur de la catégorie initiale vers la catégorie squelettique engendrée."""
def __init__(self, categorie:Categorie, nom:str = None):
CategorieSquelettique.__init__(self,set(), "Catégorie squelettique engendrée par "+str(categorie) if nom == None else nom)
CategoriePreordreEngendree.__init__(self,categorie, "Catégorie squelettique engendrée par "+str(categorie) if nom == None else nom)
CategorieSquelettique.verifier_coherence(self)
self._categorie_initiale = categorie
self.classes_equiv_vers_obj = dict() # associe à chaque classe d'équivalence un ensemble d'objets {classe : {obj1,obj2,...}}
self.obj_vers_classes_equiv = dict() # associe à chaque objet sa classe d'équivalence {obj:classe}
for obj1 in categorie.objets:
for obj2 in self.obj_vers_classes_equiv:
if categorie.existe_morphisme(obj1,obj2) and categorie.existe_morphisme(obj2,obj1):
self.classes_equiv_vers_obj[self.obj_vers_classes_equiv[obj2]] |= {obj1}
self.obj_vers_classes_equiv[obj1] = self.obj_vers_classes_equiv[obj2]
break
else:
self.obj_vers_classes_equiv[obj1] = len(self.obj_vers_classes_equiv)+1
self.classes_equiv_vers_obj[self.obj_vers_classes_equiv[obj1]] = {obj1}
CategorieSquelettique.__init__(self,set(self.classes_equiv_vers_obj.keys()), "Catégorie squelettique engendrée par "+str(categorie) if nom == None else nom)
def existe_morphisme(self, source:Any, cible:Any) -> Union[MPO,None]:
for s in self.classes_equiv_vers_obj[source]:
for c in self.classes_equiv_vers_obj[cible]:
morph = self._categorie_initiale.existe_morphisme(s,c)
if morph != None:
return morph
def existe_morphisme_elementaire(self, source:Any, cible:Any) -> Union[MPO,None]:
for s in self.classes_equiv_vers_obj[source]:
for c in self.classes_equiv_vers_obj[cible]:
morph = self._categorie_initiale.existe_morphisme_elementaire(s,c)
if morph != None:
return morph
@property
def foncteur_engendre(self):
c_i = self._categorie_initiale
return Foncteur(c_i,self,{o:self.obj_vers_classes_equiv[o] for o in c_i.objets},{m:next(self({self.obj_vers_classes_equiv[m.source]},{self.obj_vers_classes_equiv[m.cible]})) for m in c_i[c_i.objets,c_i.objets]})
def test_CategorieSquelettiqueEgendree():
from GrapheDeComposition import GC,MGC
gc = GC()
gc |= set("ABCDEF")
f,g,h,i,j,k,l,m,n = [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'),MGC('B','D','m'),MGC('C','E','n')]
f,g,h,i,j,k,l,m,n = [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('B','A','l'),MGC('B','D','m'),MGC('C','E','n')]
gc |= {f,g,h,i,j,k,l,m,n}
MGC.identifier_morphismes(l@f,gc.identite('A'))
gc.transformer_graphviz()
c_po = CategorieSquelettiqueEngendree(gc)
c_po.transformer_graphviz()
print(c_po.tri_topologique())
squelette = CategorieSquelettiqueEngendree(gc)
squelette.transformer_graphviz()
print(squelette.tri_topologique())
squelette.foncteur_engendre.transformer_graphviz()
def test_CategorieSquelettiqueEgendree2():
from CategorieAleatoire import GCA
import random
random.seed(1334442)
for i in range(20):
gc = GCA()
gc.transformer_graphviz()
squelette = CategorieSquelettiqueEngendree(gc)
squelette.transformer_graphviz()
print(squelette.tri_topologique())
squelette.foncteur_engendre.transformer_graphviz()
if __name__ == '__main__':
test_CategorieSquelettiqueEgendree()
test_CategorieSquelettiqueEgendree2()
\ No newline at end of file
......@@ -102,7 +102,6 @@ class Foncteur(Morphisme):
if param in self._app_morph:
return self._app_morph[param]
decompo = self.source.decomposition_morphisme(param)
decompo = self.source.decomposition_morphisme(param)
return functools.reduce(lambda x,y:x@y,[self._app_morph[morph] for morph in decompo])
def transformer_graphviz(self, destination:Union[str,None]=None, afficher_identites:bool = False):
......@@ -138,8 +137,9 @@ class Foncteur(Morphisme):
nodes = []
for o in self.cible.objets:
cluster.node(str(o)+suffixe2_objet)
for morph in set(self.cible[self.cible.objets,self.cible.objets])|set(self._app_morph.values()):
if not morph.is_identite or afficher_identites:
fleches2 = set(self.cible[self.cible.objets,self.cible.objets])|set(self._app_morph.values())
for morph in fleches2:
if not morph.is_identite or afficher_identites or morph in self._app_morph.values():
couleur = 'black' if not issubclass(type(morph),MGC) or len(morph) == 1 else 'grey70'
cluster.node(str(morph)+suffixe2_morph,style="invis",shape="point")
graph.edge(str(morph.source)+suffixe2_objet,str(morph)+suffixe2_morph,color=couleur,label=str(morph)+suffixe2_morph,headclip="False",arrowhead="none")
......@@ -156,10 +156,13 @@ class Foncteur(Morphisme):
cluster.node(str(morph)+suffixe2_morph,style="invis",shape="point")
graph.edge(str(morph.source)+suffixe2_objet,str(morph)+suffixe2_morph,color=couleur,label=str(morph)+suffixe2_morph,headclip="False",arrowhead="none")
graph.edge(str(morph)+suffixe2_morph,str(morph.cible)+suffixe2_objet,color=couleur,tailclip="false")
graph.edge(str(source)+suffixe1_morph,str(self(source))+suffixe2_morph,color="cyan")
fleche2 = {f for f in fleches2 if f == self(source)}.pop()
graph.edge(str(source)+suffixe1_morph,str(fleche2)+suffixe2_morph,color="cyan")
for source in self._app_objets:
graph.edge(str(source)+suffixe1_objet,str(self(source))+suffixe2_objet,color="blue")
obj1 = {e for e in self.source.objets if e == source}.pop()
obj2 = {e for e in self.cible.objets if e == self(source)}.pop()
graph.edge(str(obj1)+suffixe1_objet,str(obj2)+suffixe2_objet,color="blue")
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
......
Markdown is supported
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