Commit 1feb2b01 authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Comma catégorie transformée

parent 7a27a1ec
......@@ -147,23 +147,24 @@ class Categorie:
raise Exception("Incoherence Categorie : le morphisme "+str(morphisme)+" est modifie par une identite.")
## On vérifie l'associativité
for m1,m2,m3 in itertools.product(self.__morphismes,repeat=3):
for m1,m2,m3 in itertools.product(sorted(self.__morphismes),repeat=3):
if m1.source == m2.cible and m2.source == m3.cible:
if (m1@m2)@m3 != m1@(m2@m3):
raise Exception("Incoherence Categorie : associativite pas respectee pour "+str(m3)+", "+str(m2)+", "+str(m1)+\
"\n(m1@m2)@m3 = "+str((m1@m2)@m3)+" m1@(m2@m3) = "+str(m1@(m2@m3)))
raise Exception("Incoherence Categorie : associativite pas respectee pour "+str(m1)+", "+str(m2)+", "+str(m3)+\
"\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 table_loi_de_composition(self):
"""
Renvoie un dico de dico tel que table[f][g] = g o f, table[f][g] = None si f et g ne sont pas composables.
Renvoie un dico de dico tel que table[f][g] = f o g, table[f][g] = None si f et g ne sont pas composables.
La table contient les compositions triviales f o Id = f, f o g = f o g, f o (g o h) = (f o g) o h etc.
O(m²)
"""
table = defaultdict(dict())
table = defaultdict(dict)
for A,B in itertools.product(abs(self), repeat=2):
for f in self(A,B):
for g in self(B,abs(self)):
table[f][g] = g@f
table[g][f] = g@f
return table
def loi_de_composition_to_csv(self, destination:str=None, sep: str=','):
......@@ -296,10 +297,8 @@ def test_CategorieDiscrete():
cat_discrete += set("ABCDE")
cat_discrete.transformer_graphviz(afficher_identites=True)
def main():
test_Categorie()
test_CategorieDiscrete()
test_SousCategoriePleine()
if __name__ == '__main__':
main()
\ No newline at end of file
test_Categorie()
test_CategorieDiscrete()
test_SousCategoriePleine()
\ No newline at end of file
......@@ -7,10 +7,12 @@ from config import *
class GrapheCompositionAleatoire(GC):
"""Construit un graphe de composition aléatoire."""
def __init__(self, nb_objets = None, nb_morph = None, proba_branchement_cycle=1/10):
"""nb_morph est le nombre de morphismes élémentaires à ajouter aléatoirement.
proba_branchement_cycle permet de gérer le cas de la composition des différents cycles,
la proba est celle de brancher sur l'arbre de composition."""
def __init__(self, nb_objets = None, nb_morph = None, proba_brancher:float=1/100):
"""`nb_morph` est le nombre de morphismes élémentaires à ajouter aléatoirement, certains seront supprimés après.
`proba_brancher` est la probabilité de brancher lors de la création de l'arbre de composition des cycles,
Plus la probabilité est grande plus il y aura de cycles et plus la loi de composition ne sera pas triviale.
Si la probabilité est 0, tous les cycles seront des isomorphismes
et les flèches de obj vers obj seront idempotents."""
GC.__init__(self,"Graphe de composition aléatoire")
if nb_objets == None:
nb_objets = random.randint(5,20)
......@@ -18,48 +20,76 @@ class GrapheCompositionAleatoire(GC):
nb_morph = random.randint(0,30)
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("Création d'une catégorie avec "+str(nb_objets)+" objets et "+str(nb_morph)+" morphismes")
self += set(range(nb_objets)) #ajout des objets
for i in range(nb_objets): #ajout des objets
self += i
for i in range(nb_morph): # ajout des morphismes
source = random.randint(0,nb_objets-1)
cible = random.randint(0,nb_objets-1)
self += MGC(source,cible)
#on définit la loi de composition pour les cycles
for obj in self.objets:
cycles = self.trouver_cycles_minimaux(obj,inclure_id=False)
## on définit la loi de composition pour les cycles
for obj in sorted(self.objets):
cycles = sorted(self.trouver_cycles_minimaux(obj,inclure_id=False))
arbre_composition = {cycle:"a determiner" for cycle in cycles}
# {noeud_arbre : image_cycle}
profondeur_cycle = {self.identite(obj):0} #cycle: profondeur dans l'arbre de composition
for cycle in cycles:
profondeur_cycle[cycle] = 1
continuer = True
while continuer:
continuer = False
while list(arbre_composition.values()).count("a determiner") > 0:
for cle in copy.copy(arbre_composition):
if arbre_composition[cle] == "a determiner":
continuer=True
if random.random() < proba_branchement_cycle: # ici on branche
if random.random() < proba_brancher**(profondeur_cycle[cle]-(1 if len(cle) == 1 else 0)):
# ici on branche, plus la profondeur est grande, plus la proba de brancher est petite
print("on branche ",proba_brancher**(profondeur_cycle[cle]-(1 if len(cle) == 1 else 0)))
arbre_composition[cle] = None
for cycle in cycles:
arbre_composition[cycle@cle]="a determiner"
profondeur_cycle[cycle@cle] = profondeur_cycle[cle]+1
else: # ici on doit simplifier le cycle
print("on simplifie")
cycles_candidats = [cycle for cycle in profondeur_cycle if profondeur_cycle[cycle] < profondeur_cycle[cle]]
arbre_composition[cle]=random.choice(cycles_candidats)
for cycle in arbre_composition:
if arbre_composition[cycle] != None:
if len(cycle) == 1 and len(arbre_composition[cycle]) == 1: # si on identifie deux cycles élémentaires on en retire un
self -= cycle
else:
MGC.identifier_morphismes(cycle,arbre_composition[cycle])
print(arbre_composition)
assert(len(cycle) != 1)
MGC.identifier_morphismes(cycle,arbre_composition[cycle])
print(obj,cycles)
print("=======")
for cle in arbre_composition:
print(cle," : ",arbre_composition[cle])
## On retire les doubles isomorphismes (fog = fog'= Id et gof = g'of = Id)
for source,cible in itertools.product(self.objets,repeat=2):
suppression_inverses = True
while suppression_inverses:
suppression_inverses = False
for f in sorted(self(source,cible)-{self.identite(source)}): #on retire les identités si source == cible
inverses = {g for g in self(cible,source) if (f@g).is_identite and (g@f).is_identite}
if len(inverses) > 1:
#on doit retirer des inverses
suppression_inverses = True
inverse_a_suppr = sorted(list(inverses),key=len)[0]
if len(inverse_a_suppr) == 1:
print("suppression inverse ",inverse_a_suppr)
self -= inverse_a_suppr
else:
print("suppression inverse ",inverse_a_suppr[0])
self -= inverse_a_suppr[0]
break
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("Fin création")
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
def test_GrapheCompositionAleatoire():
GCA = GrapheCompositionAleatoire(5,10)
random.seed(1)
GCA = GrapheCompositionAleatoire()
GCA.transformer_graphviz()
GCA.loi_de_composition_to_csv('lois de composition/loi_de_compo.csv')
return GCA
# class CategorieAleaIsomorph(Categorie):
# """Construit une catégorie aléatoire où tous les cycles sont des isomorphismes."""
......@@ -148,13 +178,9 @@ def test_GrapheCompositionAleatoire():
# if retirer_objets_isoles and len([obj for obj in cat_source.objets if not(len(cat_source.morph_sortants[obj]) == len(cat_source.morph_entrants[obj]) == 0)]) != 0:
# cat_source.supprimer_objets([obj for obj in cat_source.objets if len(cat_source.morph_sortants[obj]) == len(cat_source.morph_entrants[obj]) == 0])
# Diagramme.__init__(self,cat_source,cat_cible,{o:o for o in cat_source.objets},{m:m for m in cat_source.morphismes})
def main():
random.seed(2579)
test_GrapheCompositionAleatoire()
if __name__ == '__main__':
main()
\ No newline at end of file
random.seed(2579)
test_GrapheCompositionAleatoire()
\ No newline at end of file
......@@ -126,7 +126,7 @@ class CategorieLibre(Categorie):
return cycles|{self.identite(objet)}
return cycles-{self.identite(objet)}
def enumerer_cycles(self, objet:any, limite_profondeur:int = 4) -> frozenset:
def enumerer_cycles(self, objet:any, limite_profondeur:int = 6) -> frozenset:
"""Enumère toutes les compositions de `objet` à `objet`.
Si f et g sont des cycles minimaux, on doit énumérer tous les mots d'alphabet {f,g}.
Pour ça on s'intéresse aux compositions qui se réduisent en composition déjà générées.
......@@ -165,9 +165,8 @@ class CategorieLibre(Categorie):
noeud = chemin[i]
if i < len(chemin)-1:
noeud_suivant = chemin[i+1]
for fleche in self.fleches_elem(noeud,noeud_suivant):
nouvelles_composees_obtenues = {fleche@composee for composee in composees_obtenues}
composees_obtenues = copy(nouvelles_composees_obtenues)
composees_obtenues = {fleche@composee for composee in composees_obtenues for fleche in self.fleches_elem(noeud,noeud_suivant)}
if len(chemin) > 1: #pour que noeud_suivant soit defini
for composee in copy(composees_obtenues):
for cycle in cycles[noeud_suivant]:
......@@ -202,9 +201,10 @@ class CategorieLibre(Categorie):
else:
graph.edge(str(fleche.source), str(fleche.cible), label=str(fleche), color="grey77")
else:
for fleche in self.morphismes:
if afficher_identites or not fleche.is_identite:
graph.edge(str(fleche.source), str(fleche.cible), label=str(fleche))
for source,cible in itertools.product(self.objets,repeat=2):
for fleche in self.fleches_elem(source,cible):
if afficher_identites or not fleche.is_identite:
graph.edge(str(fleche.source), str(fleche.cible), label=str(fleche))
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
import os
......
......@@ -51,9 +51,6 @@ def test_CategoriePreordre():
cat.transformer_graphviz()
cat.transformer_graphviz()
def main():
test_CategoriePreordre()
if __name__ == '__main__':
main()
test_CategoriePreordre()
from Morphisme import Morphisme
from Categorie import Categorie
from Foncteur import Foncteur
from Diagramme import DiagrammeIdentite, DiagrammeObjets
from config import *
class ObjetCommaCategorie:
"""Soit C une catégorie, T et S deux foncteurs de E dans C et D dans C.
Un objet de la comma-categorie (T|S) est un triplet (e,f,d) où e est un objet de E, f une flèche de T(e) vers S(d) et d un objet de D.
(cf. Mac Lane "Categories for the working mathematician" P.45)
On a changé l'ordre par rapport au texte de MacLane qui considérait plutôt les triplets (e,d,f)
"""
def __init__(self,e,f,d):
def __init__(self, e:any, f:any, d:any):
self.e = e
self.f = f
self.d = d
def __eq__(self,other:'ObjetCommaCategorie') -> bool:
if not issubclass(type(other),ObjetCommaCategorie):
raise TypeError("Tentative de comparaison avec un objet de type inconnu "+str(other))
return self.e == other.e and self.f == other.f and self.d == other.d
def __hash__(self) -> int:
return hash((self.e,self.f,self.d))
def __repr__(self):
def __str__(self):
return '('+str(self.e)+','+str(self.f)+','+str(self.d)+')'
def __repr__(self):
str(self)
class FlecheCommaCategorie(Morphisme):
"""Soit C une catégorie, T et S deux foncteurs de E dans C et D dans C.
......@@ -27,21 +42,20 @@ class FlecheCommaCategorie(Morphisme):
commute.
(cf. Mac Lane "Categories for the working mathematician" P.45)
"""
def __init__(self,source,cible,k,h):
def __init__(self, source:any, cible:any, k:Morphisme, h:Morphisme):
self.k = k
self.h = h
Morphisme.__init__(self,source,cible,'('+str(self.k)+','+str(self.h)+')')
def __repr__(self):
return '('+str(self.k)+','+str(self.h)+')'
def verifier_coherence(self,T,S):
if T.cat_cible != S.cat_cible:
Morphisme.__init__(self,source,cible,'('+str(self.k)+','+str(self.h)+')',source == cible and k.is_identite and h.is_identite)
def verifier_coherence(self, T:Foncteur, S:Foncteur):
if T.cible != S.cible:
raise Exception("Incoherence FlecheCommaCategorie : T et S de cibles differentes : "+str(T)+" != "+str(S))
categorie = T.cat_cible
if categorie.Composee(T(self.k),self.cible.f) != categorie.Composee(self.source.f,S(self.h)):
categorie = T.cible
if self.cible.f@T(self.k) != S(self.h)@self.source.f:
raise Exception("Incoherence FlecheCommaCategorie : le carre ne commute pas, "+str(self.cible.f)+" o "+str(T(self.k))+" != "+str(S(self.h))+" o "+str(self.source.f))
def __matmul__(self,other:'FlecheCommaCategorie') -> 'FlecheCommaCategorie':
return FlecheCommaCategorie(other.source,self.cible,self.k@other.k,self.h@other.h)
class CommaCategorie(Categorie):
......@@ -51,9 +65,114 @@ class CommaCategorie(Categorie):
Voir Mac Lane "Categories for the working mathematician" P.45
"""
def __init__(self,T,S):
if T.cat_cible != S.cat_cible:
raise Exception("Incoherence CommaCategorie : T et S de cibles differentes : "+str(T)+" != "+str(S))
self.C = T.cat_cible
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)
if T.cible != S.cible:
raise Exception("Incoherence CommaCategorie : T et S de cibles differentes : "+str(T)+" vs "+str(S))
self.__C = T.cible
self.__T = T
self.__S = S
self += {ObjetCommaCategorie(e,f,d) for e in T.source.objets for d in S.source.objets for f in self.__C(T(e),S(d))} # on ajoute tous les objets
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)))
\ No newline at end of file
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}
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):
if b.f@self.__T(k) == self.__S(h)@a.f:
result |= {FlecheCommaCategorie(a,b,k,h)}
return result
class CategorieFleches(CommaCategorie):
"""Catégorie des flèches d'une catégorie C.
(cf. https://en.wikipedia.org/wiki/Comma_category#Arrow_category)
"""
def __init__(self, C:Categorie, nom:str = None):
CommaCategorie.__init__(self,DiagrammeIdentite(C),DiagrammeIdentite(C),"Categorie des flèches de "+str(C) if nom == None else nom)
class CategorieSous(CommaCategorie):
"""CategorieSous peut aussi être appelée CosliceCategorie.
Soit C une catégorie, et b un objet de C.
CategorieSous est la catégorie en-dessous de b
(cf. Mac Lane "Categories for the working mathematician" P.45)
On construit la catégorie sous b en créant la comma-catégorie (1_b|Id(C)) c'est-à-dire
la comma-catégorie du diagramme qui associe à un objet unique b avec le diagramme identité de C.
"""
def __init__(self, C:Categorie, objet:any, nom:str = None):
CommaCategorie.__init__(self,DiagrammeObjets(C,{objet}),DiagrammeIdentite(C),str(C)+" sous "+str(objet) if nom == None else nom)
CosliceCategorie = CategorieSous
class CategorieSur(CommaCategorie):
"""CategorieSur peut aussi être appelée SliceCategorie.
Soit C une catégorie, et b un objet de C.
CategorieSur est la catégorie au-dessus de b
(cf. Mac Lane "Categories for the working mathematician" P.45)
On construit la catégorie sur b en créant la comma-catégorie (Id(C)|1_b) c'est-à-dire
la comma-catégorie du diagramme identité de C avec le diagramme qui associe à un objet unique b .
"""
def __init__(self, C:Categorie, objet:any, nom:str = None):
CommaCategorie.__init__(self,DiagrammeIdentite(C),DiagrammeObjets(C,{objet}),str(C)+" sur "+str(objet) if nom == None else nom)
SliceCategorie = CategorieSur
def test_CategorieFleches():
from GrapheDeComposition import GC,MGC
from Diagramme import DiagrammeIdentite
cat = GC('C')
cat += {'A','B','C'}
morphismes = [MGC('A','B','f'),MGC('B','C','g')]
cat += morphismes
cat.transformer_graphviz()
cat_fleches = CategorieFleches(cat)
cat_fleches.transformer_graphviz()
def test_CategorieSous():
from GrapheDeComposition import GC,MGC
from Diagramme import DiagrammeIdentite
cat = GC('C')
cat += {'A','B','C','D','E'}
morphismes = [MGC('A','B','f'),MGC('B','C','g'),MGC('C','D','h'),MGC('E','C','i'),MGC('E','A','j')]
cat += morphismes
cat.transformer_graphviz()
cat_sous = CategorieSous(cat,'B')
cat_sous.transformer_graphviz()
def test_CategorieSur():
from GrapheDeComposition import GC,MGC
from Diagramme import DiagrammeIdentite
cat = GC('C')
cat += {'A','B','C','D','E'}
morphismes = [MGC('A','B','f'),MGC('B','C','g'),MGC('C','D','h'),MGC('E','C','i'),MGC('E','A','j')]
cat += morphismes
cat.transformer_graphviz()
cat_sur = CategorieSur(cat,'B')
cat_sur.transformer_graphviz()
if __name__ == '__main__':
test_CategorieFleches()
test_CategorieSous()
test_CategorieSur()
\ No newline at end of file
......@@ -15,12 +15,9 @@ class Diagramme(Foncteur):
nb_viz = 0
def __init__(self, categorie_indexante:Categorie, categorie_indexee:Categorie,
application_objets:dict, application_morphismes:dict, representant:any=None):
Foncteur.__init__(self,categorie_indexante,categorie_indexee, application_objets, application_morphismes,representant=representant)
def __str__(self) -> str:
return "Diag"+Foncteur.__str__(self)
application_objets:dict, application_morphismes:dict, representant:any = None):
Foncteur.__init__(self,categorie_indexante,categorie_indexee, application_objets,
application_morphismes,representant=representant if representant != None else "Diagramme "+str(Morphisme._id))
def faire_commuter(self):
"""
Change la loi de composition de `MorphismeGrapheDeComposition` pour que le diagramme commute.
......@@ -231,7 +228,7 @@ class Diagramme(Foncteur):
destination = "graphviz/diagramme"+str(Diagramme.nb_viz)
graph = Digraph('diagramme')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
graph.attr(label="Diagramme sur "+self.cible.nom)
graph.attr(label=str(self))
image_objets = [self(obj) for obj in self._app_objets]
image_objets_count = {obj : image_objets.count(obj) for obj in image_objets}
......@@ -308,7 +305,7 @@ class Fleche(Diagramme):
_cat += _f
def __init__(self, categorie_indexee:Categorie, fleche_a_selectionner:Morphisme):
Diagramme.__init__(self,Fleche._cat,categorie_indexee,{1:fleche_a_selectionner.source,2:fleche_a_selectionner.cible},{Fleche._f:fleche_a_selectionner},representant="D"+str(fleche_a_selectionner))
Diagramme.__init__(self,Fleche._cat,categorie_indexee,{1:fleche_a_selectionner.source,2:fleche_a_selectionner.cible},{Fleche._f:fleche_a_selectionner},representant="Fleche "+str(fleche_a_selectionner))
def test_Fleche():
cat = GC()
......@@ -327,7 +324,8 @@ class Chemins(Diagramme):
chemins = list(categorie_indexee(source,cible))
morphismes = list(map(lambda x:MorphismeGrapheDeComposition(1,2,x),range(len(chemins))))
cat += morphismes
Diagramme.__init__(self,cat,categorie_indexee,{1:source,2:cible},{morphismes[i]:chemins[i] for i in range(len(chemins))})
Diagramme.__init__(self,cat,categorie_indexee,{1:source,2:cible},
{morphismes[i]:chemins[i] for i in range(len(chemins))}, 'Chemins de '+str(source)+' vers '+str(cible))
def test_Chemins():
cat = GC()
......@@ -353,7 +351,8 @@ class Parallele(Diagramme):
"""
app_objets = {i+1:image_objets[i] for i in range(len(image_objets))}
app_morph = {Parallele.morphismes[i]:image_morphismes[i] for i in range(len(image_morphismes))}
Diagramme.__init__(self,Parallele._cat,categorie_indexee,app_objets,app_morph)
Diagramme.__init__(self,Parallele._cat,categorie_indexee,app_objets,app_morph,
'Paralleles '+str(image_morphismes[0])+' et '+str(image_morphismes[1]))
def test_Parallele():
cat = GC()
......@@ -384,7 +383,7 @@ class Triangle(Diagramme):
"""
app_objets = {i+1:image_objets[i] for i in range(len(image_objets))}
app_morph = {Triangle.morphismes[i]:image_morphismes[i] for i in range(len(image_morphismes))}
Diagramme.__init__(self,Triangle._cat,categorie_indexee,app_objets,app_morph)
Diagramme.__init__(self,Triangle._cat,categorie_indexee,app_objets,app_morph,"Triangle sur "+str(categorie_indexee))
def test_Triangle():
cat = GC()
......@@ -418,7 +417,7 @@ class Carre(Diagramme):
"""
app_objets = {i+1:image_objets[i] for i in range(len(image_objets))}
app_morph = {Carre.morphismes[i]:image_morphismes[i] for i in range(len(image_morphismes))}
Diagramme.__init__(self,Carre._cat,categorie_indexee,app_objets,app_morph)
Diagramme.__init__(self,Carre._cat,categorie_indexee,app_objets,app_morph,'Carre sur '+str(categorie_indexee))
def test_Carre():
cat = GC()
......@@ -432,7 +431,8 @@ class DiagrammeIdentite(Diagramme):
"""Diagramme qui associe à chaque objet le même objet et à chaque morphisme le même morphisme."""
def __init__(self,categorie:Categorie):
Diagramme.__init__(self,categorie,categorie,{e:e for e in categorie.objets},{e:e for e in categorie(categorie.objets,categorie.objets)})
Diagramme.__init__(self,categorie,categorie,{e:e for e in categorie.objets}
,{e:e for e in categorie(categorie.objets,categorie.objets)},"Id("+str(categorie)+')')
def test_DiagrammeIdentite():
cat = GC()
......@@ -446,7 +446,8 @@ def test_DiagrammeIdentite():
diag.transformer_graphviz()
def main():
if __name__ == '__main__':
test_Diagramme()
test_DiagrammeObjets()
test_Fleche()
......@@ -454,7 +455,4 @@ def main():
test_Parallele()
test_Triangle()
test_Carre()
test_DiagrammeIdentite()
if __name__ == '__main__':
main()
\ No newline at end of file
test_DiagrammeIdentite()
\ No newline at end of file
......@@ -25,7 +25,8 @@ class Foncteur(Morphisme):
is_identite = categorie_cible == categorie_source and\
all([obj == application_objets[obj] for obj in application_objets]) and\
all([morph == application_morphismes[morph] for morph in application_morphismes])
Morphisme.__init__(self,categorie_source,categorie_cible,representant,is_identite)
Morphisme.__init__(self,categorie_source,categorie_cible
,representant if representant != None else 'Foncteur '+str(Morphisme._id),is_identite)
self._app_objets = application_objets
self._app_morph = application_morphismes
for obj in self.source.objets:
......@@ -232,10 +233,7 @@ def test_Foncteur():
(fonct2@fonct1).transformer_graphviz()
def main():
test_Foncteur()
if __name__ == '__main__':
main()
test_Foncteur()
\ No newline at end of file
......@@ -31,10 +31,12 @@ class MorphismeGrapheDeComposition(Morphisme):
def __simplifier_chemin(morph1:'MorphismeGrapheDeComposition',morph2:'MorphismeGrapheDeComposition') -> tuple:
"""
On simplifie le chemin défini par la composition des deux morphismes grâce à la loi de composition.
Hypothèses faites sur les données : il n'y a pas d'identité dans `morph1` et `morph2`.
On renvoie un tuple de morphismes.
"""
chemin = morph1.__chemin+morph2.__chemin# on unpack les différents chemins en un seul chemin
chemin = tuple([morph for morph in morph1.__chemin+morph2.__chemin if not morph.is_identite])
if len(chemin) == 0: #s'il n'y avait que des identités
return (morph1.__chemin[0],) #on renvoie une des identités
# on unpack les différents chemins en un seul chemin et on retire les identités
while True: #on continue tant qu'on a simplifié le chemin
for nb_morphismes_consideres in range(len(chemin), 1, -1):
for offset in range(0, len(chemin) - nb_morphismes_consideres + 1):
......@@ -48,7 +50,10 @@ class MorphismeGrapheDeComposition(Morphisme):
break
else:
break #on quitte si on a jamais break avant (si on a jamais simplifié)
return chemin
chemin_resultat = tuple([morph for morph in chemin if not morph.is_identite])# on retire les identités
if len(chemin_resultat) == 0: #s'il n'y avait que des identités
return (chemin[0],) #on renvoie une des identités
return chemin_resultat
def __init__(self, source:any, cible:any, representant:any=None, is_identite:bool = False):
......@@ -186,8 +191,6 @@ def test_GrapheDeComposition():
GC += morphismes
GC.transformer_graphviz()
def main():
test_GrapheDeComposition()
if __name__ == '__main__':
main()
\ No newline at end of file
test_GrapheDeComposition()
\ No newline at end of file
from Morphisme import Morphisme
from Categorie import Categorie
from CommaCategorie import ObjetCommaCategorie
class Interaction(Morphisme):
"""
Soit D1 et D2 deux diagrammes sur C.
Une interaction est une partie de l'ensemble des objets de la comma-category
\ No newline at end of file
......@@ -13,16 +13,16 @@ class Morphisme:
Si un morphisme est défini comme étant la composée de morphismes élémentaires,
le morphisme doit implémenter la méthode __iter__ et renvoyer ses morphismes élémentaires constituants.
"""
__id = 0
_id = 0
def __init__(self, source, cible, representant = None, is_identite = False):
"""Representant est un objet ou un str qui représente le morphisme pour l'affichage."""
self.__id = Morphisme.__id
Morphisme.__id += 1
self._id = Morphisme._id
Morphisme._id += 1
self.source = source
self.cible = cible
self.is_identite = is_identite
if representant == None:
self.representant = str(self.__id)
self.representant = str(self._id)
else:
self.representant = representant
if is_identite and source != cible:
......@@ -42,3 +42,7 @@ class Morphisme: