Commit 5b87dfc9 authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Correction __eq__ de FlecheCommaCategorie

parent 6f200014
__pycache__/
graphviz/
.vs/
*.pdf
*.png
.idea/
......
......@@ -6,6 +6,7 @@ import itertools
from Morphisme import Morphisme
from config import *
import typing
from MiseEnCache import call_mis_en_cache
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
......@@ -172,9 +173,20 @@ class Categorie:
for o in self.objets:
graph.node(str(o))
for fleche in self(self.objets,self.objets):
toutes_les_fleches = self(self.objets,self.objets)
for fleche in toutes_les_fleches:
if afficher_identites or not fleche.is_identite:
graph.edge(str(fleche.source), str(fleche.cible), label=str(fleche), weight="1000")
if fleche.source not in self.objets:
raise Exception("Source d'une fleche pas dans les objets de la categorie.")
if fleche.cible not in self.objets:
raise Exception("Source d'une fleche pas dans les objets de la categorie.")
if len({obj for obj in self.objets if obj == fleche.source}) > 1:
raise Exception("Plus d'un objet de la catégorie est source de la fleche.")
if len({obj for obj in self.objets if obj == fleche.cible}) > 1:
raise Exception("Plus d'un objet de la catégorie est cible de la fleche.")
source = {obj for obj in self.objets if obj == fleche.source}.pop() #permet d'avoir toujours un objet de la catégorie comme source
cible = {obj for obj in self.objets if obj == fleche.cible}.pop() #permet d'avoir toujours un objet de la catégorie comme cible
graph.edge(str(source), str(cible), label=str(fleche), weight="1000")
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
......
from Categorie import Categorie
from Foncteur import Foncteur
from CatFinies import CatFinies
from EnsFinis import EnsFinis,Application
from ChampActif import ChampActif
from collections import defaultdict
import itertools
class FoncteurOubli(Foncteur):
"""Foncteur d'oubli du champ perceptif.
Associe à chaque cône sont nadir et à chaque flèche entre cône la flèche d'où elle vient."""
def __init__(self, champ_actif:ChampActif):
univers = champ_actif.foncteur_diagonal.source
Foncteur.__init__(self, champ_actif, univers, {obj:obj.d for obj in abs(champ_actif)},
{f:f.h for f in champ_actif(abs(champ_actif),abs(champ_actif))})
class CategorieHomologue(CatFinies):
"""Catégorie dont les objets sont des champs actifs et les morphismes sont des foncteurs qui commutent avec le foncteur d'oubli."""
def verifier_coherence(self):
CatFinies.verifier_coherence(self)
for f in self(abs(self),abs(self)):
fonct_oubli_source = FoncteurOubli(f.source)
fonct_oubli_cible = FoncteurOubli(f.cible)
if fonct_oubli_source != fonct_oubli_cible@f:
raise Exception("Incoherence CategorieHomologue : "+str(f)+" ne commute pas avec les foncteurs d'oublis")
def identite(self, objet:ChampActif) -> Foncteur:
return Foncteur(objet,objet,{o:o for o in abs(objet)},{f:f for f in objet(abs(objet),abs(objet))})
def __call__(self, sources:set, cibles:set) -> set:
result = set()
for source in sources:
for cible in cibles:
dict_nadir_cocone_source = defaultdict(frozenset)
for obj in abs(source):
dict_nadir_cocone_source[obj.d] |= {obj}
dict_nadir_cocone_cible = defaultdict(frozenset)
for obj in abs(cible):
dict_nadir_cocone_cible[obj.d] |= {obj}
if set(dict_nadir_cocone_source.keys()) != set(dict_nadir_cocone_cible.keys()):
continue
applications_objets = set() # applications entre cocônes de même nadir
ens_objets = EnsFinis()
for nadir in dict_nadir_cocone_source:
ens_objets |= {dict_nadir_cocone_source[nadir]}
ens_objets |= {dict_nadir_cocone_cible[nadir]}
applications_objets |= {ens_objets({dict_nadir_cocone_source[nadir]},{dict_nadir_cocone_cible[nadir]})}
for application_objet in itertools.product(*applications_objets):
app_obj = dict([x for app in application_objet for x in app.as_dict().items()])
app_fleche_foncteur = dict() # à chaque couple d'objets on associe un ensemble d'applications
for c,d in itertools.product(source.objets, repeat=2):
ens_fleches = EnsFinis({frozenset(source({c},{d})),frozenset(cible({app_obj[c]},{app_obj[d]}))})
app_fleches_c_vers_d = ens_fleches({frozenset(source({c},{d}))},{frozenset(cible({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: #la dernière recherche d'applications à échouée, on passe à l'application objet suivante
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 source(abs(source),abs(source)):
for g in source(abs(source),{f.source}):
if app_fleches[f@g] != app_fleches[f]@app_fleches[g]:
break
else:
continue
break
else:
result |= {Foncteur(source,cible,app_obj,app_fleches)}
return result
def test_FoncteurOubli():
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,"DEF",[h,i,i@h])
c_p = ChampActif(d1)
foncteur_oubli = FoncteurOubli(c_p)
foncteur_oubli.transformer_graphviz()
def test_CategorieHomologue():
from GrapheDeComposition import GC,MGC
from Diagramme import Fleche,DiagrammeIdentite
gc = GC()
gc |= set("ABCDEFGH")
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('G','A','m'),MGC('H','A','n')]
gc |= {f,g,h,i,j,k,l,m,n}
diag_identite = DiagrammeIdentite(gc)
diag_identite.faire_commuter()
d1 = Fleche(gc,m)
c_p1 = ChampActif(d1)
d2 = Fleche(gc,n)
c_p2 = ChampActif(d2)
cph = CategorieHomologue({c_p1,c_p2})
cph.transformer_graphviz()
f,g = cph.isomorphismes(c_p1,c_p2).pop()
f.transformer_graphviz()
g.transformer_graphviz()
if __name__ == '__main__':
test_FoncteurOubli()
test_CategorieHomologue()
\ No newline at end of file
from CommaCategorie import CommaCategorie
from CommaCategorie import CommaCategorie, ObjetCommaCategorie
from TransformationNaturelle import CatTransfoNat,TransfoNat
from Foncteur import Foncteur
from Diagramme import DiagrammeConstant,DiagrammeObjets
class Cocone(TransfoNat):
"""Un cocône sur D de nadir N est une transformation naturelle de D vers le foncteur constant Δ_N."""
def __init__(self, objet_champ_actif:ObjetCommaCategorie):
self.__nadir = objet_champ_actif.d # attribut read-only
f = objet_champ_actif.f
TransfoNat.__init__(self,f.source,f.cible,f._composantes,f.nom)
@property
def nadir(self):
return self.__nadir
def as_ObjetCommaCategorie(self) -> ObjetCommaCategorie:
return ObjetCommaCategorie(0,self,self.nadir)
class ChampActif(CommaCategorie):
"""Le champ actif d'un diagramme(ou foncteur) D:P->Q est défini comme étant la comma-catégorie (D|Δ)
(cf. https://en.wikipedia.org/wiki/Cone_(category_theory)#Equivalent_formulations)
......@@ -27,15 +41,17 @@ class ChampActif(CommaCategorie):
CommaCategorie.__init__(self,self.foncteur_vers_D,self.foncteur_diagonal, "Champ actif de "+str(diagramme) if nom == None else nom)
def cocones(self,nadirs:set) -> set:
def objets_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 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}
Un cocône est une transformation naturelle de D vers le diagramme constant sur le nadir.
Cette fonction renvoie les objets de la comma-catégorie qui contiennent ces cocônes."""
return {obj for obj in self.objets for nadir in nadirs if obj.d == nadir}
def colimites(self) -> set:
def objets_colimites(self) -> set:
"""Renvoie un ensemble de cocônes colimites.
Les cocônes colimites sont des objets initiaux dans le champ actif."""
Les cocônes colimites sont des objets initiaux dans le champ actif.
Cette fonction renvoie les objets de la comma-catégorie qui contiennent ces cocônes."""
if len(self.objets) == 0:
return set()
cocones = list(self.objets)
......@@ -43,14 +59,14 @@ class ChampActif(CommaCategorie):
cocones_visites = {cocone_courant}
cocones_restants = self.objets-cocones_visites
fleches_accessibles = self(cocones_restants,{cocone_courant})
while len(fleches_accessibles) > 0:
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)
fleches_accessibles = self(cocones_restants,{cocone_courant})
# 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}
return {cocone for cocone in self.objets if len(self.isomorphismes(cocone_courant,cocone)) > 0}
def test_ChampActif():
from GrapheDeComposition import GC,MGC
......@@ -64,7 +80,7 @@ def test_ChampActif():
diag_identite = DiagrammeIdentite(gc)
diag_identite.faire_commuter()
d1 = Triangle(gc,"DEF",[h,i,i@h])
d1 = Triangle(gc,"ABC",[f,g,g@f])
c_p = ChampActif(d1)
for cocone in c_p.cocones(gc.objets):
......@@ -77,6 +93,6 @@ def test_ChampActif():
colimites[i].nom = "Colimite "+str(i)
print(str(colimites))
colimites[i].transformer_graphviz()
if __name__ == '__main__':
test_ChampActif()
\ No newline at end of file
from Categorie import Categorie
from CommaCategorie import CommaCategorie, CategorieSur
from CommaCategorie import CommaCategorie, ObjetCommaCategorie
from TransformationNaturelle import CatTransfoNat,TransfoNat
from Foncteur import Foncteur
from Diagramme import DiagrammeConstant,DiagrammeObjets
class Cone(TransfoNat):
"""Un cône sur D d'apex A est une transformation naturelle du foncteur constant Δ_A vers D."""
def __init__(self, objet_champ_perceptif:ObjetCommaCategorie):
self.__apex = objet_champ_perceptif.e # attribut read-only
f = objet_champ_perceptif.f
TransfoNat.__init__(self,f.source,f.cible,f._composantes,f.nom)
@property
def apex(self):
return self.__apex
def as_ObjetCommaCategorie(self) -> ObjetCommaCategorie:
return ObjetCommaCategorie(self.apex,self,0)
class ChampPerceptif(CommaCategorie):
"""Le champ perceptif d'un diagramme(ou foncteur) D:P->Q est défini comme étant la comma-catégorie (Δ|D)
(cf. https://en.wikipedia.org/wiki/Cone_(category_theory)#Equivalent_formulations)
......@@ -28,15 +42,17 @@ class ChampPerceptif(CommaCategorie):
CommaCategorie.__init__(self,self.foncteur_diagonal,self.foncteur_vers_D, "Champ perceptif de "+str(diagramme)+" ("+str(Categorie._id)+")" if nom == None else nom)
def cones(self,apices:set) -> set:
def objets_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 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}
Un cône est une transformation naturelle du diagramme constant sur l'apex vers D.
Cette fonction renvoie les objets de la comma-catégorie qui contiennent ces cônes."""
return {obj for obj in self.objets for apex in apices if obj.e == apex}
def limites(self) -> set:
def objets_limites(self) -> set:
"""Renvoie un ensemble de cônes limites.
Les cônes limites sont des objets finaux dans le champ perceptif."""
Les cônes limites sont des objets finaux dans le champ perceptif.
Cette fonction renvoie les objets de la comma-catégorie qui contiennent ces cônes."""
if len(self.objets) == 0:
return set()
cones = list(self.objets)
......@@ -51,7 +67,7 @@ class ChampPerceptif(CommaCategorie):
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}
return {cone for cone in self.objets if len(self.isomorphismes(cone_courant,cone)) > 0}
def test_ChampPerceptif():
......@@ -66,7 +82,10 @@ def test_ChampPerceptif():
diag_identite = DiagrammeIdentite(gc)
diag_identite.faire_commuter()
gc.transformer_graphviz()
d1 = Triangle(gc,"DEF",[h,i,i@h])
d1.transformer_graphviz()
c_p = ChampPerceptif(d1)
for cone in c_p.cones(gc.objets):
......
......@@ -3,6 +3,7 @@ from Categorie import Categorie
from Foncteur import Foncteur
from Diagramme import DiagrammeIdentite, DiagrammeObjets
from config import *
from MiseEnCache import call_mis_en_cache
class ObjetCommaCategorie(Morphisme):
"""Soit C une catégorie, T et S deux foncteurs de E dans C et D dans C.
......@@ -91,7 +92,7 @@ class FlecheCommaCategorie(Morphisme):
return FlecheCommaCategorie(other.source,self.cible,self.k@other.k,self.h@other.h)
def __eq__(self,other:'FlecheCommaCategorie') -> bool:
return self.k == other.k and self.h == other.h
return self.source == other.source and self.cible == other.cible and self.k == other.k and self.h == other.h
def __hash__(self) -> int:
return hash((self.k,self.h))
......@@ -122,7 +123,7 @@ class CommaCategorie(Categorie):
def identite(self, objet:ObjetCommaCategorie) -> FlecheCommaCategorie:
'''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, sources:set, cibles:set) -> set:
result = set()
for a in sources:
......
......@@ -39,7 +39,7 @@ class Application(Morphisme):
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}))
return Application(other.source,self.cible,{e:self(other(e)) for e in other.source})
def __repr__(self):
return "\n".join(map(lambda x:str(x[0])+':'+str(x[1]),self._app.items()))+'\n'
......@@ -78,8 +78,8 @@ class CategorieEnsemblesFinis(Categorie):
def __init__(self, objets:set = set(), 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 identite(self, ensemble:frozenset) -> Application:
return Application(ensemble,ensemble,{e:e for e in ensemble})
def __call__(self, sources:set, cibles:set) -> frozenset:
result = frozenset()
......@@ -93,6 +93,15 @@ class CategorieEnsemblesFinis(Categorie):
return result
EnsFinis = CategorieEnsemblesFinis
class CategorieEnsembleParties(EnsFinis):
"""CategorieEnsembleParties peut être abrégé en EnsParties
Catégorie de l'ensemble des parties d'un ensemble, les morphismes sont des applications."""
def __init__(self, ensemble:set, nom:str = None):
EnsFinis.__init__(self,{frozenset(ens) for i in range(len(ensemble)+1) for ens in itertools.combinations(ensemble,i)},"Catégorie des parties de "+str(ensemble) if nom == None else nom)
EnsParties = CategorieEnsembleParties
def test_EnsFinis():
......@@ -105,7 +114,10 @@ def test_EnsFinis():
print("fleche de {1,2} vers {}")
fleche.transformer_graphviz()
def test_EnsParties():
cat = EnsParties({1,2,3})
cat.transformer_graphviz()
if __name__ == '__main__':
test_EnsFinis()
\ No newline at end of file
test_EnsFinis()
test_EnsParties()
\ No newline at end of file
from GrapheDeComposition import GC,MGC
from Diagramme import Triangle,DiagrammeIdentite
gc1 = GC()
gc1 |= 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')]
gc1 |= {f,g,h,i,j,k,l}
diag_identite = DiagrammeIdentite(gc1)
diag_identite.faire_commuter()
\ No newline at end of file
from EnsFinis import EnsParties
from Diagramme import DiagrammeObjets
from ChampActif import ChampActif,Cocone
from CommaCategorie import ObjetCommaCategorie
from Categorie import SousCategoriePleine
from CategorieHomologue import CategorieHomologue
cat = EnsParties({1,2,3})
cat.transformer_graphviz()
d1 = DiagrammeObjets(cat,{frozenset({1,2}),frozenset({3})})
d2 = DiagrammeObjets(cat,{frozenset({3,2}),frozenset({1})})
d1.transformer_graphviz()
#d2.transformer_graphviz()
c_p1 = ChampActif(d1)
c_p2 = ChampActif(d2)
sous_c_p1 = SousCategoriePleine(c_p1,c_p1.objets_colimites())
sous_c_p1.transformer_graphviz()
sous_c_p2 = SousCategoriePleine(c_p2,c_p2.objets_colimites())
sous_c_p2.transformer_graphviz()
cat_hom = CategorieHomologue({sous_c_p1,sous_c_p2}) #on cherche simplement un isomoprhisme entre les colimites pour dire qu'ils ont les mêmes colimites
print("cat_hom")
cat_hom.transformer_graphviz()
\ No newline at end of file
from collections import defaultdict
# Décorateurs qui permettent de mettre en cache le résultat de certaines fonctions
def call_mis_en_cache(func):
"""Stocke le résultat de __call__ dans un cache et cherche dans le cache lors de l'appel de __call__.
/!\ Attention si la catégorie à changée entre temps, les couples d'objets mis en cache renverront les même morphismes."""
if func.__name__ != '__call__':
raise Exception("Mise en cache d'une fonction differente de __call__ : "+str(func))
def wrapper(self, sources:set, cibles:set) -> set:
if not hasattr(self,"__cache__call__"):
self.__cache__call__ = defaultdict(set)
nouvelles_sources = set()
nouvelles_cibles = set()
result = set()
for source in sources:
for cible in cibles:
if (source,cible) in self.__cache__call__:
result |= self.__cache__call__[(source,cible)]
else:
nouvelles_sources |= {source}
nouvelles_cibles |= {cible}
nouveaux_resultats = set()
for s in nouvelles_sources:
for c in nouvelles_cibles:
nouveau_r = func(self,{s},{c})
nouveaux_resultats |= nouveau_r
self.__cache__call__[(s,c)] |= nouveau_r
return nouveaux_resultats|result
return wrapper
\ No newline at end of file
......@@ -35,7 +35,7 @@ class Morphisme:
return str(self.nom)+' : '+str(self.source)+" -> "+str(self.cible)
def __repr__(self) -> str:
return self.pretty_print()#+","+super().__repr__().split('object at ')[-1].split('>')[0]
return self.pretty_print()+","+super().__repr__().split('object at ')[-1].split('>')[0]
def __matmul__(self,other:'Morphisme') -> 'Morphisme':
raise NotImplementedError("Les classes filles doivent implémenter la composition des morphismes.")
......
......@@ -7,6 +7,7 @@ from Categorie import Categorie
from Morphisme import Morphisme
from Diagramme import Diagramme
from EnsFinis import Application, EnsFinis
from MiseEnCache import call_mis_en_cache
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
......@@ -115,6 +116,7 @@ class CategorieTransformationsNaturelles(Categorie):
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) -> set:
result = set()
if len(sources) > 0:
......
TOUJOURS_VERIFIER_COHERENCE = 1 # booléen qui indique si on doit toujours vérifier la cohérence des structures qu'on construit
TOUJOURS_VERIFIER_COHERENCE_COMPOSEE = 1
TOUJOURS_VERIFIER_COHERENCE = 0 # booléen qui indique si on doit toujours vérifier la cohérence des structures qu'on construit
TOUJOURS_VERIFIER_COHERENCE_COMPOSEE = 0
DEBUG_LOI_DE_COMPOSITION = False
DEBUG_MONOIDE_ALEATOIRE = True
GRAPHVIZ_ENABLED = True # booléen qui indique s'il faut charger la bibliothèque graphviz
......
......@@ -55,7 +55,4 @@ Limite et colimite methode de champ perceptif et champt actif
ajouter la convention cat[a,b] pour obtenir les flèches élementaires de a vers b
cat[a,b] appelle cat(a,b) par défaut sauf pour le graphe de composition
Identité de CatégorieCluster pas correcte
Il faudrait le cluster engendré par le proto-cluster contenant IdA,IdB
faire le matmul de cluster
en fait il faudrait pouvoir construire le cluster egendré par un proto-cluster
utiliser existe_morphisme plutôt que self({cocone_courant},cocones_restants) dans la recherche de limites
\ 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