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

Limites et colimites

parent 11922fac
from Categorie import Categorie
from Foncteur import Foncteur
from EnsFinis import EnsFinis,Application
import itertools
class CatFinies(Categorie):
"""Catégorie des catégories finies, les morphismes sont des foncteurs.
Cette catégorie est infinie, par défaut il n'y a aucune catégorie dedans, il faut rajouter les catégories d'intérêt."""
def __call__(self, source:set, cible:set) -> set:
result = set()
for a in source:
for b in cible:
# a et b sont des catégories, on cherche tous les foncteurs de a vers b
\ No newline at end of file
ens_objets = EnsFinis({a.objets,b.objets})
for app_obj in ens_objets({a.objets},{b.objets}):
app_fleche_foncteur = dict() # à chaque couple d'objets on associe un ensemble d'applications
for c,d in itertools.product(a.objets, repeat=2):
ens_fleches = EnsFinis({frozenset(a(c,d)),frozenset(b(app_obj(c),app_obj(d)))})
app_fleches_c_vers_d = ens_fleches({frozenset(a(c,d))},{frozenset(b(app_obj(c),app_obj(d)))})
app_fleche_foncteur[(c,d)] = app_fleches_c_vers_d
if len(app_fleches_c_vers_d) == 0:
break
if len(app_fleche_foncteur[(c,d)]) == 0:
continue
for applications_fleches in itertools.product(*app_fleche_foncteur.values()):
app_fleches = dict()
for app in applications_fleches:
app_fleches.update(app.as_dict())
for f in a(abs(a),abs(a)):
for g in a({f},abs(a)):
if app_fleches[f@g] != app_fleches[f]@app_fleches[g]:
break
else:
continue
break
else:
result |= {Foncteur(a,b,app_obj.as_dict(),app_fleches)}
return result
def test_CatFinies():
from GrapheDeComposition import GC,MGC
gc = GC()
gc |= set("ABC")
f,g = [MGC('A','B','f'),MGC('B','C','g')]
gc |= {f,g}
gc2 = GC()
gc2 |= set("DEF")
f,g = [MGC('D','E','a'),MGC('E','F','b')]
gc2 |= {f,g}
cat = CatFinies({gc,gc2})
cat.transformer_graphviz()
for fonct in cat({gc},{gc2}):
fonct.transformer_graphviz()
fonct.as_diagram().transformer_graphviz()
print(cat.isomorphismes(gc,gc2))
list(cat.isomorphismes(gc,gc2))[0][0].transformer_graphviz()
if __name__ == '__main__':
test_CatFinies()
\ No newline at end of file
......@@ -30,10 +30,13 @@ class Categorie:
- visualiser son graphe sous-jacent avec la méthode transformer_graphviz
- exporter sa loi de composition en csv avec loi_de_composition_to_csv
"""
__id = 0
nb_viz = 0 # nombre de graphes graphviz générés
def __init__(self, objets:set = set(), nom:str = "Catégorie"):
self.nom = nom
def __init__(self, objets:set = set(), nom:str = None):
Categorie.__id += 1
self.__id = Categorie.__id
self.nom = "Categorie "+str(self.__id) if nom == None else nom
self.objets = frozenset()
self |= objets
......@@ -104,11 +107,11 @@ class Categorie:
"\n(m1@m2)@m3 = "+str((m1@m2)@m3)+" m1@(m2@m3) = "+str(m1@(m2@m3))+
"\nm1@m2 = "+str(m1@m2)+" m2@m3 = "+str(m2@m3))#+"\nm2@m1 = "+str(m2@m1)+" m3@m2 = "+str(m3@m2))
def est_isomorphe_a(self, objet1:any, objet2:any) -> set:
"""Trouve les isomorphismes entre `objet1` et `objet2`.
def isomorphismes(self, source:any, cible:any) -> set:
"""Trouve les isomorphismes entre `source` et `cible`.
Renvoie un ensemble de couple (f,g) tels que gof = Id1 et fog = Id2.
"""
return {(f,g) for f in self({objet1},{objet2}) for g in self({objet2},{objet1}) if (f@g).is_identite and (g@f).is_identite}
return {(f,g) for f in self({source},{cible}) for g in self({cible},{source}) if (f@g).is_identite and (g@f).is_identite}
def table_loi_de_composition(self):
"""
......@@ -151,7 +154,7 @@ class Categorie:
"""Permet de visualiser la catégorie avec graphviz"""
Categorie.nb_viz += 1
if destination == None:
destination = "graphviz/categorie" + str(Categorie.nb_viz)
destination = "graphviz/"+type(self).__name__+ str(Categorie.nb_viz)
graph = Digraph('categorie')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
......@@ -219,8 +222,8 @@ class CategorieDiscrete(Categorie):
"""
On peut composer uniquement une identité avec elle même.
"""
if type(other) != Identite:
raise Exception("Composition d'un morphisme de type inconnu avec une Identite")
if type(other) != CategorieDiscrete.Identite:
raise Exception("Composition d'un morphisme de type inconnu avec une Identite\n"+repr(self)+'\n'+repr(other))
if self.source != other.source:
raise Exception("Composition incoherente d'Identites")
return self
......
......@@ -17,10 +17,13 @@ class CategorieLibre(Categorie):
- morph_entrants(self, cible) : renvoie l'ensemble des morphismes entrants dans la cible
ces deux méthodes définissent le graphe sous-jacent avec les objets de la catégorie
"""
__id = 0
nb_viz = 0
def __init__(self, objets:set = set(), nom:str="Categorie libre"):
Categorie.__init__(self,objets,nom)
def __init__(self, objets:set = set(), nom:str = None):
CategorieLibre.__id += 1
self.__id = CategorieLibre.__id
Categorie.__init__(self,objets,"Categorie libre "+str(self.__id) if nom == None else nom)
def fleches_elem(self, source:any, cible:any, inclure_id:bool = True) -> set:
"""
......@@ -174,7 +177,7 @@ class CategorieLibre(Categorie):
"""
CategorieLibre.nb_viz += 1
if destination == None:
destination = "graphviz/categorie_libre" + str(CategorieLibre.nb_viz)
destination = "graphviz/"+type(self).__name__+ str(CategorieLibre.nb_viz)
graph = Digraph('categorie_libre')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
......
from Categorie import Categorie
from Morphisme import Morphisme
class CategorieMiseEnCache(Categorie):
"""
Lorsqu'il est coûteux de calculer les morphismes entre deux objets, la catégorie mise en cache permet de ne pas recalculer deux fois ces morphismes.
Lors de l'appel de __call__, les morphismes entre deux objets sont mis en cache,
on empêche aussi la modification ultérieure de la catégorie en supprimant les méthodes __iadd__ et __idel__.
"""
def __init__(self, objets:set = set(), nom:str = "Catégorie"):
Categorie.__init__(self,objets,nom)
self._call = dict() # (source,cible) : {Morphismes} dictionnaire qui associe à un couple d'objets un ensemble de morphismes
def __call__(self,source:set,cible:set) -> set:
def methode_supprimee(self):
raise Exception("Cette methode a ete supprimee lors de la mise en cache par la methode __call__.")
self.__iadd__ = methode_supprimee
self.__idel__ = methode_supprimee
for a in source:
for b in cible:
if (a,b) in self._call:
return self._call[(a,b)]
return self
def identite(self,objet:any) -> MorphismePreordre:
return MorphismePreordre(objet,objet,'Id'+str(objet))
def test_CategoriePreordre():
cat = CategoriePreordre()
cat |= set("ABCDEF")
MPO = MorphismePreordre
f,g,h,i,j,k,l,m = [MPO('A','B','f'),MPO('C','D','g'),MPO('D','E','h'),MPO('E','A','i'),
MPO('F','D','j'),MPO('F','E','k'),MPO('D','B','l'),MPO('B','E','m')]
cat |= {f,g,h,i,j,k,l,m}
cat.transformer_graphviz()
cat.transformer_graphviz()
if __name__ == '__main__':
test_CategoriePreordre()
......@@ -30,8 +30,27 @@ class ChampActif(CommaCategorie):
def cocones(self,nadirs:set) -> set:
"""`nadirs` est un ensemble d'objets de Q
Cette fonction renvoie l'ensemble tous les cocônes de nadir un objets d'`nadirs`.
Un cocône est une interaction de D vers le nadir."""
Un cocône est une transformation naturelle de D vers le diagramme constant sur le nadir."""
return {obj.f for obj in self.objets for nadir in nadirs if obj.d == nadir}
def colimites(self) -> set:
"""Renvoie un ensemble de cocônes colimites.
Les cocônes colimites sont des objets initiaux dans le champ actif."""
if len(self.objets) == 0:
return set()
cocones = list(self.objets)
cocone_courant = cocones[0]
cocones_visites = {cocone_courant}
cocones_restants = self.objets-cocones_visites
fleches_accessibles = self(cocones_restants,{cocone_courant})
while len(fleches_accessibles) > 0:
fleche_choisie = list(fleches_accessibles)[0]
cocone_courant = fleche_choisie.source
cocones_visites |= {cocone_courant}
cocones_restants = self.objets-cocones_visites
fleches_accessibles = self({cocone_courant},cocones_restants)
# ici cocone_courant est un objet initial de la catégorie
return {cocone.f for cocone in self.objets if len(self.isomorphismes(cocone_courant,cocone)) > 0}
def test_ChampActif():
from GrapheDeComposition import GC,MGC
......@@ -52,5 +71,12 @@ def test_ChampActif():
cocone.transformer_graphviz()
c_p.transformer_graphviz()
colimites = list(c_p.colimites())
for i in range(len(colimites)):
print("colimite")
colimites[i].nom = "Colimite "+str(i)
print(str(colimites))
colimites[i].transformer_graphviz()
if __name__ == '__main__':
test_ChampActif()
\ No newline at end of file
......@@ -30,8 +30,27 @@ class ChampPerceptif(CommaCategorie):
def cones(self,apices:set) -> set:
"""`apices` est un ensemble d'objets de Q
Cette fonction renvoie l'ensemble tous les cônes d'apex un objets d'`apices`.
Un cône est une interaction de l'apex vers D."""
Un cône est une transformation naturelle du diagramme constant sur l'apex vers D."""
return {obj.f for obj in self.objets for apex in apices if obj.e == apex}
def limites(self) -> set:
"""Renvoie un ensemble de cônes limites.
Les cônes limites sont des objets finaux dans le champ perceptif."""
if len(self.objets) == 0:
return set()
cones = list(self.objets)
cone_courant = cones[0]
cones_visites = {cone_courant}
cones_restants = self.objets-cones_visites
fleches_accessibles = self({cone_courant},cones_restants)
while len(fleches_accessibles) > 0:
fleche_choisie = list(fleches_accessibles)[0]
cone_courant = fleche_choisie.cible
cones_visites |= {cone_courant}
cones_restants = self.objets-cones_visites
fleches_accessibles = self({cone_courant},cones_restants)
# ici cone_courant est un objet final de la catégorie
return {cone.f for cone in self.objets if len(self.isomorphismes(cone_courant,cone)) > 0}
def test_ChampPerceptif():
......@@ -53,5 +72,12 @@ def test_ChampPerceptif():
cone.transformer_graphviz()
c_p.transformer_graphviz()
limites = list(c_p.limites())
for i in range(len(limites)):
print("limite")
limites[i].nom = "Limite "+str(i)
print(str(limites))
limites[i].transformer_graphviz()
if __name__ == '__main__':
test_ChampPerceptif()
\ No newline at end of file
from Morphisme import Morphisme
from Interaction import Interaction,CategorieInteractions
from config import *
import itertools
from copy import copy
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
from CategorieSous import CategorieFSous, ObjetCategorieFSous
from CategorieComposantesConnexes import CategorieComposantesConnexes
class ClusterAbstrait(Morphisme):
nb_viz = 0
......
......@@ -63,166 +63,13 @@ class Diagramme(Foncteur):
result = deepcopy(self.cible)
result.nom = "Implementation du diagramme "+str(self)
result -= {obj for obj in self.cible.objets if obj not in self._app_objets.values()}
return result
def champ_perceptif(self):
import CategorieCones
return CategorieCones.CategorieCones(self)
def champ_actif(self):
import CategorieCocones
return CategorieCocones.CategorieCocones(self)
def est_prohomologue_a(self,other):
"""Renvoie un foncteur isomorphisme entre les champs perceptifs des deux diagrammes s'il existe (les diagrammes sont prohomologues)
None sinon"""
import FoncteurOubli
assert(self.categorie_indexee == other.categorie_indexee)
cat_cones1 = self.champ_perceptif()
# cat_cones1.transformer_graphviz(complet=False)
cat_cones2 = other.champ_perceptif()
# cat_cones2.transformer_graphviz(complet=False)
apices = set([e.apex for e in cat_cones1.objets])
apices2 = set([e.apex for e in cat_cones2.objets])
if apices != apices2:
return None
if len(cat_cones1.morphismes) != len(cat_cones2.morphismes) or len(cat_cones1.objets) != len(cat_cones2.objets):
return None
oubli1 = FoncteurOubli.FoncteurOubli(cat_cones1)
oubli2 = FoncteurOubli.FoncteurOubli(cat_cones2)
# si on note C1_o les cônes d'apex o de D1 et C2_o les cônes d'apex o de D2
# on cherche toutes les applications (C1_o1 ~> C2_o1) x (C1_o2 ~> C2_o2) x ... x (C1_oN ~> C2_oN)
applications_cones = []
for apex in apices:
antecedants = [e for e in cat_cones1.objets if e.apex == apex]
images = [e for e in cat_cones2.objets if e.apex == apex]
if len(antecedants) != len(images):
return None
#on sépare les cônes en fonction de leurs degrés
classes_degre_cone_ante = defaultdict(list)
for ante in antecedants:
classes_degre_cone_ante[(len(cat_cones1.morph_sortants[ante]),len(cat_cones1.morph_entrants[ante]))] += [ante]
classes_degre_cone_im = defaultdict(list)
for im in images:
classes_degre_cone_im[(len(cat_cones2.morph_sortants[im]),len(cat_cones2.morph_entrants[im]))] += [im]
applications_intra_classe = []
for degre in classes_degre_cone_ante:
if len(classes_degre_cone_ante[degre]) != len(classes_degre_cone_im[degre]):
return None
images_internes_possibles = itertools.permutations(classes_degre_cone_im[degre])
applications_intra_classe += [list(map(lambda x:dict(zip(classes_degre_cone_ante[degre],x)),images_internes_possibles))]
# print(list(map(list,applications_intra_classe)))
applications_cones += [[{cle:val for app in app_intra for cle,val in app.items()} for app_intra in itertools.product(*applications_intra_classe)]]
# print(applications_cones)
for application_cones in itertools.product(*applications_cones):
application_cones = {cle:val for app in application_cones for cle,val in app.items()}
applications_fleches = []
for source,cible in itertools.product(cat_cones1.objets,repeat=2):
antecedants = cat_cones1.fleches_elem(source,cible,False)
images = cat_cones2.fleches_elem(application_cones[source],application_cones[cible],False)
if len(antecedants) != len(images):
break
if len(antecedants) > 0:
images_possibles = itertools.permutations(images)
applications_fleches += [list(map(lambda x:dict(zip(antecedants,x)),images_possibles))]
else:
# ici on a toujours pu trouver au moins un isomorphisme entre les flèches elem
for application_fleches in itertools.product(*applications_fleches):
application_fleches = {cle:val for app in application_fleches for cle,val in app.items()}
try:
foncteur = Foncteur.Foncteur(cat_cones1,cat_cones2,application_cones,application_fleches)
foncteur.verifier_coherence()
# on va vérifier la coherence de l'isomorphisme
for obj in cat_cones1.objets:
if oubli1(obj) != oubli2(foncteur(obj)):
raise Exception("L'isomorphisme ne commute pas avec les foncteurs d'oubli pour l'objet "+str(obj))
for morph in cat_cones1.morphismes:
if oubli1(morph) != oubli2(foncteur(morph)):
raise Exception("L'isomorphisme ne commute pas avec les foncteurs d'oubli pour le morphisme "+str(morph.pretty_print()))
return foncteur
except Exception as e:
print("Warning : foncteur incoherent cree, on passe au suivant, "+str(e))
def est_homologue_a(self,other):
"""Renvoie un foncteur isomorphisme entre les champs perceptifs des deux diagrammes s'il existe (les diagrammes sont prohomologues)
None sinon"""
import FoncteurOubli
assert(self.categorie_indexee == other.categorie_indexee)
cat_cocones1 = self.champ_actif()
# cat_cocones1.transformer_graphviz(complet=False)
cat_cocones2 = other.champ_actif()
# cat_cocones2.transformer_graphviz(complet=False)
nadirs = set([e.nadir for e in cat_cocones1.objets])
nadirs2 = set([e.nadir for e in cat_cocones2.objets])
if nadirs != nadirs2:
return None
if len(cat_cocones1.morphismes) != len(cat_cocones2.morphismes) or len(cat_cocones1.objets) != len(cat_cocones2.objets):
return None
oubli1 = FoncteurOubli.FoncteurOubli(cat_cocones1)
oubli2 = FoncteurOubli.FoncteurOubli(cat_cocones2)
# si on note C1_o les cônes de nadir o de D1 et C2_o les cônes de nadir o de D2
# on cherche toutes les applications (C1_o1 ~> C2_o1) x (C1_o2 ~> C2_o2) x ... x (C1_oN ~> C2_oN)
applications_cones = []
for nadir in nadirs:
antecedants = [e for e in cat_cocones1.objets if e.nadir == nadir]
images = [e for e in cat_cocones2.objets if e.nadir == nadir]
if len(antecedants) != len(images):
return None
#on sépare les cônes en fonction de leurs degrés
classes_degre_cone_ante = defaultdict(list)
for ante in antecedants:
classes_degre_cone_ante[(len(cat_cocones1.morph_sortants[ante]),len(cat_cocones1.morph_entrants[ante]))] += [ante]
classes_degre_cone_im = defaultdict(list)
for im in images:
classes_degre_cone_im[(len(cat_cocones2.morph_sortants[im]),len(cat_cocones2.morph_entrants[im]))] += [im]
applications_intra_classe = []
for degre in classes_degre_cone_ante:
if len(classes_degre_cone_ante[degre]) != len(classes_degre_cone_im[degre]):
return None
images_internes_possibles = itertools.permutations(classes_degre_cone_im[degre])
applications_intra_classe += [list(map(lambda x:dict(zip(classes_degre_cone_ante[degre],x)),images_internes_possibles))]
applications_cones += [[{cle:val for app in app_intra for cle,val in app.items()} for app_intra in itertools.product(*applications_intra_classe)]]
for application_cones in itertools.product(*applications_cones):
application_cones = {cle:val for app in application_cones for cle,val in app.items()}
applications_fleches = []
for source,cible in itertools.product(cat_cocones1.objets,repeat=2):
antecedants = cat_cocones1.fleches_elem(source,cible,False)
images = cat_cocones2.fleches_elem(application_cones[source],application_cones[cible],False)
if len(antecedants) != len(images):
break
if len(antecedants) > 0:
images_possibles = itertools.permutations(images)
applications_fleches += [list(map(lambda x:dict(zip(antecedants,x)),images_possibles))]
else:
# ici on a toujours pu trouver au moins un isomorphisme entre les flèches elem
for application_fleches in itertools.product(*applications_fleches):
application_fleches = {cle:val for app in application_fleches for cle,val in app.items()}
try:
foncteur = Foncteur.Foncteur(cat_cocones1,cat_cocones2,application_cones,application_fleches)
foncteur.verifier_coherence()
# on va vérifier la coherence de l'isomorphisme
for obj in cat_cocones1.objets:
if oubli1(obj) != oubli2(foncteur(obj)):
raise Exception("L'isomorphisme ne commute pas avec les foncteurs d'oubli pour l'objet "+str(obj))
for morph in cat_cocones1.morphismes:
if oubli1(morph) != oubli2(foncteur(morph)):
raise Exception("L'isomorphisme ne commute pas avec les foncteurs d'oubli pour le morphisme "+str(morph.pretty_print()))
return foncteur
except Exception as e:
print("Warning : foncteur incoherent cree, on passe au suivant, "+str(e))
return result
def transformer_graphviz(self, destination:any = None,afficher_identite:bool = False):
"""Permet de visualiser la categorie indexee par le diagramme avec graphviz"""
Diagramme.nb_viz += 1
if destination == None:
destination = "graphviz/diagramme"+str(Diagramme.nb_viz)
destination = "graphviz/"+type(self).__name__+str(Diagramme.nb_viz)
graph = Digraph('diagramme')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
graph.attr(label=str(self))
......
......@@ -48,7 +48,7 @@ class Application(Morphisme):
def transformer_graphviz(self, destination:any=None):
Application.nb_viz += 1
if destination == None:
destination = "graphviz/application"+str(Application.nb_viz)
destination = "graphviz/"+type(self).__name__+str(Application.nb_viz)
graph = Digraph('application')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
......@@ -96,10 +96,14 @@ EnsFinis = CategorieEnsemblesFinis
def test_EnsFinis():
cat = EnsFinis(set(map(frozenset,[{1,2},{3,4,5}])))
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()
for fleche in cat({frozenset({1,2})},{frozenset({})}):
print("fleche de {1,2} vers {}")
fleche.transformer_graphviz()
......
......@@ -101,7 +101,7 @@ class Foncteur(Morphisme):
from GrapheDeComposition import MGC
Foncteur.nb_viz += 1
if destination == None:
destination = "graphviz/foncteur"+str(Foncteur.nb_viz)
destination = "graphviz/"+type(self).__name__+str(Foncteur.nb_viz)
suffixe1_objet = "_1"
suffixe1_morph = "-1"
......@@ -136,9 +136,18 @@ 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")
identites_ajoutees = set()
for source in self._app_morph:
if not source.is_identite or afficher_identites:
if self(source).is_identite and self(source) not in identites_ajoutees:
identites_ajoutees |= {self(source)}
with graph.subgraph(name='cluster_1') as cluster:
morph = self(source)
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")
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")
for source in self._app_objets:
......@@ -149,65 +158,6 @@ class Foncteur(Morphisme):
import os
os.remove(destination)
def enumerer_cones(self,apex):
"""Renvoie une liste de cônes.
1) On considère les cônes du diagramme trivial où toutes les flèches de la catégorie source ont été retirées.
2) Pour chaque flèche de la catégorie source, on retire les cônes qui ne commutent pas correctement avec l'image de la flèche.
On a besoin de faire cette suppression uniquement pour les flèches du graphe de composition et pas pour les composées.
Preuve : soit F un foncteur de C1 vers C2, soient f : A->B et g : B->C deux morphismes de C1
soient pA, pB et pC trois jambes d'un cône candidat associées respectivement aux images F(A), F(B) et F(C).
On suppose que la propriété de commutativité du cône est respectée pour les flèches élémentaires f et g :
F(f) o pA = pB et F(g) o pB = pC
on a alors F(g) o F(f) o pA = pC
puis F(g o f) o pA = pC car F est un foncteur
on en déduit que la propriété du cône est respectée pour la composée de f et g cqfd.
"""
import Cone
if apex not in self.cat_cible.objets:
raise Exception("Apex pas dans la categorie indexee.")
composees = [self.cat_cible.enumerer_composees(apex,self(objet)) for objet in self.cat_source.objets]
tous_les_cones = [dict(zip(self.cat_source.objets,liste)) for liste in itertools.product(*composees)]
for morph in self.cat_source.morphismes:
if not morph.is_identite:
tous_les_cones = [cone for cone in tous_les_cones if self.cat_cible.Composee(cone[morph.source],self(morph)) == cone[morph.cible]]
return [Cone.Cone(self,apex,famille) for famille in tous_les_cones]
def enumerer_cocones(self,nadir):
"""Voir enumerer_cones."""
import Cocone
if nadir not in self.cat_cible.objets:
raise Exception("Nadir pas dans la categorie indexee.")
composees = [self.cat_cible.enumerer_composees(self(objet),nadir) for objet in self.cat_source.objets]
tous_les_cocones = [dict(zip(self.cat_source.objets,liste)) for liste in itertools.product(*composees)]
for morph in self.cat_source.morphismes:
if not morph.is_identite:
tous_les_cocones = [cocone for cocone in tous_les_cocones if self.cat_cible.Composee(self(morph),cocone[morph.cible]) == cocone[morph.source]]
return [Cocone.Cocone(self,nadir,famille) for famille in tous_les_cocones]
def trouver_limite(self):
"""Renvoie la limite du foncteur s'il y en a une, None sinon."""
cones = [c for o in self.cat_cible.objets for c in self.enumerer_cones(o)]
for lim_candidate in cones:
for cone in cones:
fleches = self.cat_cible.enumerer_composees(cone.apex,lim_candidate.apex)
for pied in self.cat_source.objets:
fleches = [f for f in fleches if cone.jambes[pied] == self.cat_cible.Composee(f,lim_candidate.jambes[pied])]
if len(fleches) == 1:
return lim_candidate
return None
def trouver_colimite(self):
"""Renvoie la colimite du foncteur s'il y en a une, None sinon."""
cocones = [c for o in self.cat_cible.objets for c in self.enumerer_cocones(o)]
for colim_candidate in cocones:
for cocone in cocones:
fleches = self.cat_cible.enumerer_composees(colim_candidate.nadir,cocone.nadir)
for pied in self.cat_source.objets:
fleches = [f for f in fleches if cocone.jambes[pied] == self.cat_cible.Composee(colim_candidate.jambes[pied],f)]
if len(fleches) == 1:
return colim_candidate
return None
def test_Foncteur():
from GrapheDeComposition import GC,MGC
triangle = GC()
......
......@@ -123,12 +123,15 @@ class GrapheDeComposition(CategorieLibre):
- ajouter un objet ou un morphisme avec |=
- faire commuter un diagramme qui a pour cible ce modèle pour identifier des flèches
"""
__id = 0
def __init__(self, objets:set = set(), nom:str = 'Graphe de composition'):
def __init__(self, objets:set = set(), nom:str = None):
GrapheDeComposition.__id += 1
self.__id = GrapheDeComposition.__id
self.__identites = dict()
self.__morph_entrants = defaultdict(frozenset)
self.__morph_sortants = defaultdict(frozenset)
CategorieLibre.__init__(self,objets,nom)