Commit 0bb248cb authored by Francois Mares's avatar Francois Mares
Browse files
parents abd60667 22f5f907
......@@ -12,9 +12,9 @@ from typing import *
def unique_generator(func):
"""Décorateur qui permet de renvoyer les flèches une seule fois par générateur."""
def wrapper(self, *args) -> Generator[any,None,None]:
def wrapper(self, *args, **kwargs) -> Generator[any,None,None]:
elem_generes = set()
for elem in func(self,*args):
for elem in func(self,*args,**kwargs):
if elem not in elem_generes:
elem_generes |= {elem}
yield elem
......@@ -218,6 +218,10 @@ class Categorie:
"""Renvoie un morphisme de `source` à `cible` s'il existe, renvoie None sinon."""
return next(self({source},{cible}),None)
def existe_morphisme_elementaire(self, source:Any, cible:Any) -> Union[Morphisme,None]:
"""Renvoie un morphisme élémentaire de `source` à `cible` s'il existe, renvoie None sinon."""
return next(self[{source},{cible}],None)
def existe_isomorphisme(self, source:Any, cible:Any) -> Union[tuple,None]:
"""Renvoie un isomorphisme de `source` à `cible` s'il existe sous la forme d'un couple (f,g) avec f:source->cible, et g:cible->source
renvoie None sinon."""
......@@ -263,7 +267,7 @@ class Categorie:
result = result.replace(',','\t,')
print(result)
def transformer_graphviz(self, destination:Union[str,None] = None, afficher_identites:bool = True, complet:bool = True, limite_fleches:int = 50):
def transformer_graphviz(self, destination:Union[str,None] = None, afficher_identites:bool = True, complet:bool = True, limite_fleches:int = 30):
"""Permet de visualiser la catégorie avec graphviz.
`complet` indique s'il faut afficher toutes les flèches ou seulement les flèches élémentaires.
`limite_fleches` est le nombre maximal de flèches entre deux objets qu'on s'autorise à afficher."""
......
......@@ -78,7 +78,6 @@ class CategorieHomologue(CatFinies):
bijections_fleches |= {bijections}
for bijection_fleche in produit_cartesien_generateurs(*bijections_fleches):
bij_fleche = dict([x for bij in bijection_fleche for x in bij.as_dict().items()])
print("yield")
yield Foncteur(source,cible,bij_obj,bij_fleche)
......
from Categorie import Categorie, unique_generator
from Morphisme import Morphisme
from ProduitGenerateurs import produit_cartesien_generateurs
from copy import copy
import itertools
import functools
......@@ -57,7 +58,7 @@ class CategorieLibre(Categorie):
return composees_resultat
return frozenset()
for chemin in enumerer_chemin_elem_sans_cycle(morphisme.source,morphisme.cible):
resultat = functools.reduce(lambda x,y:x@y,chemin)
resultat = functools.reduce(lambda x,y:y@x,chemin)
if resultat == morphisme:
for morph_elem in chemin:
yield morph_elem
......@@ -77,89 +78,157 @@ class CategorieLibre(Categorie):
if morph not in self[self.objets,{morph.cible}]:
raise Exception("Incoherence CategorieLibre : le morphisme "+str(morph)+" n'entre pas dans "+str(morph.cible))
def enumerer_composees_sans_cycle(self, source:Any, cible:Any, noeuds_deja_visites:tuple=tuple()) -> frozenset():
def enumerer_composees_sans_cycle(self, source:Any, cible:Any) -> Generator[Morphisme,None,None]:
"""
Renvoie tous les morphismes composés allant de `source` à `cible` ne contenant aucun cycle (on ne passe jamais deux fois par le même noeud).
Génère tous les morphismes composés allant de `source` à `cible` ne contenant aucun cycle (on ne passe jamais deux fois par le même noeud).
"""
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 self.enumerer_composees_sans_cycle(morph.cible, cible, noeuds_deja_visites):
composees_resultat |= {composition_candidate@morph}
return composees_resultat
return frozenset()
def enumerer_chemins_sans_cycle(self, source:Any, cible:Any, noeuds_deja_visites:tuple=tuple()) -> frozenset:
for chemin in self.enumerer_chemins_sans_cycle(source,cible):
if len(chemin) == 1:
for e in self[{chemin[0]},{chemin[0]}]:
yield e
else:
generateurs = [self[{chemin[i]},{chemin[i+1]}] for i in range(len(chemin)-1)]
for prod in produit_cartesien_generateurs(*generateurs):
yield functools.reduce(lambda x,y : y@x, prod)
def enumerer_chemins_sans_cycle(self, source:Any, cible:Any) -> Generator[tuple,None,None]:
"""
Renvoie tous les chemins (liste de noeuds) allant de `source` à `cible` ne contenant aucun cycle (on ne passe jamais deux fois par le même noeud).
Génère tous les chemins (liste de noeuds) allant de `source` à `cible` ne contenant aucun cycle (on ne passe jamais deux fois par le même noeud).
"""
if source == cible:
return frozenset({(cible,)})
if source not in noeuds_deja_visites:
noeuds_deja_visites = noeuds_deja_visites + (source,)
chemins_resultat = frozenset()
for morph in self[{source},self.objets]:
for chemin_candidat in self.enumerer_chemins_sans_cycle(morph.cible, cible, noeuds_deja_visites):
chemins_resultat |= {(source,)+chemin_candidat}
return chemins_resultat
return frozenset()
# on fait un parcours en largeur
file_chemins = [(source,)]
while len(file_chemins) > 0:
chemin = file_chemins.pop(0)
if chemin[-1] == cible:
yield chemin
else:
for obj in self.objets:
if obj not in chemin:
if self.existe_morphisme_elementaire(chemin[-1],obj):
file_chemins += [chemin+(obj,)]
def trouver_cycles_minimaux(self, objet:Any, inclure_id:bool=True) -> frozenset():
"""Renvoie tous les cycles minimaux de morphismes élémentaires (qui ne contiennent aucun cycle)
de `objet` à `objet` (à l'exception de l'identité si `inclure_id` = False)."""
cycles = frozenset()
@unique_generator
def trouver_cycles_minimaux(self, objet:Any) -> Generator[Morphisme,None,None]:
"""Génère tous les cycles minimaux de morphismes élémentaires (qui ne contiennent aucun cycle)
de `objet` à `objet` différent de l'identité."""
for morph_pred in self[self.objets,{objet}]:
pred = morph_pred.source
cycles_tronques = self.enumerer_composees_sans_cycle(objet, pred)
for cycle_tronque in cycles_tronques:
cycle = morph_pred@cycle_tronque
if cycle not in cycles:
cycles |= {cycle}
if inclure_id:
return cycles|{self.identite(objet)}
return cycles-{self.identite(objet)}
if morph_pred.source == objet:
yield morph_pred
else:
pred = morph_pred.source
for cycle_tronque in self.enumerer_composees_sans_cycle(objet, pred):
cycle = morph_pred@cycle_tronque
if not cycle.is_identite:
yield cycle
def enumerer_cycles(self, objet:Any, limite_profondeur:int = 27) -> frozenset:
def enumerer_cycles(self, objet:Any, limite_profondeur:int = 10) -> Generator[Morphisme,None,None]:
"""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.
Si f ne se réduit pas, on regarde ff et fg, puis si ceux là non plus fff, ffg, fgf,fgg etc.
On s'arrête à la limite de profondeur spécifiée.
"""
cycles = self.trouver_cycles_minimaux(objet,inclure_id=False)
cycles = set(self.trouver_cycles_minimaux(objet))
cycles_a_reduire = copy(cycles)
cycles_minimaux = copy(cycles)
cycles |= {self.identite(objet)}
for cycle in cycles:
yield cycle
for profondeur in range(limite_profondeur):
for cycle_a_reduire in copy(cycles_a_reduire):
for cycle_minimal in cycles_minimaux:
if cycle_minimal@cycle_a_reduire not in cycles:
cycles_a_reduire |= {cycle_minimal@cycle_a_reduire}
cycles |= {cycle_minimal@cycle_a_reduire}
new_cycle = cycle_minimal@cycle_a_reduire
cycles_a_reduire |= {new_cycle}
cycles |= {new_cycle}
yield new_cycle
cycles_a_reduire -= {cycle_a_reduire}
if len(cycles_a_reduire) > 0:
print("Warning CategorieLibre : limite de profondeur atteinte dans l'enumeration des cycles")
if DEBUG_LOI_DE_COMPOSITION:
print(cycles_a_reduire)
return cycles
def enumerer_composees(self, source:Any, cible:Any) -> Generator[Morphisme,None,None]:
"""Renvoie tous les morphismes composés allant de `source` à `cible`.
"""Génère tous les morphismes composés allant de `source` à `cible`.
1) On trouve les chemins sans cycle.
2) Pour tous les noeuds U du chemin on énumère les cycles de U à U.
3) Pour chaque chemin sans cycle, pour chaque noeud qui a au moins un cycle, on duplique le chemin d'autant de cycles que nécessaires."""
chemins_sans_cycles = self.enumerer_chemins_sans_cycle(source, cible)
noeuds = list(frozenset([e for chemin in chemins_sans_cycles for e in chemin]))
cycles = {x: self.enumerer_cycles(x) for x in noeuds}
dict_noeuds_cycles = dict() # {noeud : set(cycles)}
for chemin in chemins_sans_cycles:
cycles_concernes = [cycles[noeud] for noeud in chemin]
fleches_concernees = [set(self[{chemin[i]},{chemin[i+1]}]) for i in range(len(chemin)-1)]
for combinaison_fleches in itertools.product(*fleches_concernees):
for combinaison_cycle in itertools.product(*cycles_concernes):
composee = combinaison_cycle[0]
for i in range(len(combinaison_fleches)):
composee = combinaison_cycle[i+1]@combinaison_fleches[i]@composee
yield composee
cycles_concernes = []
for noeud in chemin:
if noeud not in dict_noeuds_cycles:
dict_noeuds_cycles[noeud] = set(self.enumerer_cycles(noeud))
cycles_concernes += [dict_noeuds_cycles[noeud]]
if len(chemin) >= 2:
fleches_concernees = [self[{chemin[i]},{chemin[i+1]}] for i in range(len(chemin)-1)]
for combinaison_fleches in produit_cartesien_generateurs(*fleches_concernees):
for combinaison_cycle in itertools.product(*cycles_concernes):
composee = combinaison_cycle[0]
for i in range(len(combinaison_fleches)):
composee = combinaison_cycle[i+1]@combinaison_fleches[i]@composee
yield composee
else:
# taille du chemin == 1
for e in cycles_concernes[0]:
yield e
def test_enumerer_chemins_sans_cycle():
from GrapheDeComposition import GC,MGC
GC = GC({'A','B','C','D','E','F'})
f,g,h,i,j,k,l,m,n = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','A','h'),MGC('C','D','i'),
MGC('C','E','j'),MGC('E','F','k'),MGC('D','F','l'),MGC('D','D','m'),MGC('C','D','n')]
MGC.identifier_morphismes(h@h@h,h)
MGC.identifier_morphismes(m@m,m)
GC |= {f,g,h,i,j,k,l,m,n}
GC.transformer_graphviz()
for composee in GC.enumerer_chemins_sans_cycle('A','F'):
print(composee)
print()
def test_enumerer_composees_sans_cycle():
from GrapheDeComposition import GC,MGC
GC = GC({'A','B','C','D','E','F'})
f,g,h,i,j,k,l,m,n = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','A','h'),MGC('C','D','i'),
MGC('C','E','j'),MGC('E','F','k'),MGC('D','F','l'),MGC('D','D','m'),MGC('C','D','n')]
MGC.identifier_morphismes(h@h@h,h)
MGC.identifier_morphismes(m@m,m)
GC |= {f,g,h,i,j,k,l,m,n}
GC.transformer_graphviz()
for composee in GC.enumerer_composees_sans_cycle('A','F'):
print(composee)
print()
def test_trouver_cycles_minimaux():
from GrapheDeComposition import GC,MGC
GC = GC({'A','B','C','D','E','F'})
f,g,h,i,j,k,l,m,n,o = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','A','h'),MGC('C','D','i'),
MGC('C','E','j'),MGC('E','F','k'),MGC('D','F','l'),MGC('D','D','m'),MGC('C','D','n'),MGC('D','A','o')]
MGC.identifier_morphismes(h@h@h,h)
MGC.identifier_morphismes(m@m,m)
MGC.identifier_morphismes(o@i@g@f@o@i@g@f,o@i@g@f)
MGC.identifier_morphismes(o@i@g@f@o@n@g@f,o@i@g@f)
MGC.identifier_morphismes(o@n@g@f@o@i@g@f,o@n@g@f)
MGC.identifier_morphismes(o@n@g@f@o@n@g@f,o@n@g@f)
GC |= {f,g,h,i,j,k,l,m,n,o}
GC.transformer_graphviz(complet=False)
for composee in GC.trouver_cycles_minimaux('D'):
print(composee)
print()
def test_enumerer_composees():
from EnsFinis import EnsFinis
cat = EnsFinis({frozenset("ABCD"),frozenset("ABC")})
print(len(list(cat({frozenset("ABCD")},{frozenset("ABC")}))))
cat.transformer_graphviz()
if __name__ == '__main__':
test_enumerer_chemins_sans_cycle()
test_enumerer_composees_sans_cycle()
test_trouver_cycles_minimaux()
test_enumerer_composees()
\ No newline at end of file
......@@ -2,6 +2,7 @@ from CommaCategorie import CommaCategorie, ObjetCommaCategorie
from TransformationNaturelle import CatTransfoNat,TransfoNat
from Foncteur import Foncteur
from Diagramme import DiagrammeConstant,DiagrammeObjets
from Cluster import ClusterActif,ProtoClusterActif
from typing import *
class Cocone(TransfoNat):
......@@ -17,6 +18,9 @@ class Cocone(TransfoNat):
def as_ObjetCommaCategorie(self) -> ObjetCommaCategorie:
return ObjetCommaCategorie(0,self,self.nadir)
def as_Cluster(self) -> ClusterActif:
return ClusterActif(ProtoClusterActif(self.source,self.cible,self._composantes,"Cluster issu du cocône "+str(self)))
class ChampActif(CommaCategorie):
"""Le champ actif d'un diagramme(ou foncteur) D:P->Q est défini comme étant la comma-catégorie (D|Δ)
......
from Categorie import Categorie
from CommaCategorie import CommaCategorie, ObjetCommaCategorie
from TransformationNaturelle import CatTransfoNat,TransfoNat
from Foncteur import Foncteur
from Diagramme import DiagrammeConstant,DiagrammeObjets
# from Cluster import ClusterActif,ProtoClusterActif
from typing import *
class Cone(TransfoNat):
"""Un cône sur D d'apex A est une transformation naturelle du foncteur constant Δ_A vers D."""
......@@ -36,11 +37,11 @@ class ChampPerceptif(CommaCategorie):
self.foncteur_diagonal = Foncteur(Q,self.categorie_diagrammes,{o:DiagrammeConstant(P,Q,o) for o in Q.objets},
{f: TransfoNat(DiagrammeConstant(P,Q,f.source),DiagrammeConstant(P,Q,f.cible),{p:f for p in P.objets}) for f in Q(abs(Q),abs(Q))})
## on associe à chaque objet son diagramme constant bite et à chaque flèche F:a->b la transformation naturelle qui va de Δa à Δb
## on associe à chaque objet son diagramme constant et à chaque flèche F:a->b la transformation naturelle qui va de Δa à Δb
self.foncteur_vers_D = DiagrammeObjets(self.categorie_diagrammes,{diagramme})
CommaCategorie.__init__(self,self.foncteur_diagonal,self.foncteur_vers_D, "Champ perceptif de "+str(diagramme)+" ("+str(Categorie._id)+")" if nom == None else nom)
CommaCategorie.__init__(self,self.foncteur_diagonal,self.foncteur_vers_D, "Champ perceptif de "+str(diagramme) if nom == None else nom)
def objets_cones(self,apices:set) -> set:
"""`apices` est un ensemble d'objets de Q
......@@ -59,12 +60,16 @@ class ChampPerceptif(CommaCategorie):
cone_courant = cones[0]
cones_visites = {cone_courant}
cones_restants = self.objets-cones_visites
fleche_accessible = next(self({cone_courant},cones_restants),None)
fleche_accessible = next(self[{cone_courant},cones_restants],None)
if fleche_accessible == None:
fleche_accessible = next(self({cone_courant},cones_restants),None)
while fleche_accessible != None:
cone_courant = fleche_accessible.cible
cones_visites |= {cone_courant}
cones_restants = self.objets-cones_visites
fleche_accessible = next(self({cone_courant},cones_restants),None)
fleche_accessible = next(self[{cone_courant},cones_restants],None)
if fleche_accessible == None:
fleche_accessible = next(self({cone_courant},cones_restants),None)
# ici cone_courant est un objet final de la catégorie
return {cone for cone in self.objets if self.existe_isomorphisme(cone_courant,cone) != None}
......
......@@ -130,7 +130,6 @@ class CommaCategorie(CategorieLibre):
'''L'identité pour un objet (e,f,d) est la paire d'identité de T(e) et S(d).'''
return FlecheCommaCategorie(objet,objet,self._T.source.identite(objet.e),self._S.source.identite(objet.d))
@mise_en_cache_getitem
def __getitem__(self, couple_sources_cibles:tuple) -> Generator[FlecheCommaCategorie, None, None]:
sources,cibles = couple_sources_cibles
for obj_comma_source in sources:
......@@ -140,7 +139,6 @@ class CommaCategorie(CategorieLibre):
if obj_comma_cible.f@self._T(k) == self._S(h)@obj_comma_source.f:
yield FlecheCommaCategorie(obj_comma_source,obj_comma_cible,k,h)
@mise_en_cache_call
def __call__(self, sources:set, cibles:set) -> Generator[FlecheCommaCategorie, None, None]:
for obj_comma_source in sources:
for obj_comma_cible in cibles:
......
from Categorie import Categorie
from Foncteur import Foncteur
from Morphisme import Morphisme
from GrapheDeComposition import GC,MGC
from Diagramme import Diagramme, DiagrammeIdentite
from ChampActif import ChampActif
from Diagramme import Diagramme, DiagrammeIdentite, Fleche
from ChampActif import ChampActif,Cocone
from ChampPerceptif import ChampPerceptif,Cone
from copy import copy
from TransformationNaturelle import TransfoNat
from typing import *
cat = GC(set("ABC"))
f,g = [MGC('A','B','f'),MGC('A','C','g')]
f,g = [MGC('A','B','f'),MGC('C','B','g')]
cat |= {f,g}
diag = DiagrammeIdentite(cat)
c_a = ChampActif(diag)
diag.transformer_graphviz()
c_a.transformer_graphviz()
cat2 = copy(cat)
cat2 |= {'L'}
h,i,j = [MGC('A','L','h'),MGC('B','L','i'),MGC('C','L','j')]
cat2 |= {h,i,j}
def modification(gc: GC, option_ajout:Tuple[Sequence[Any],Sequence[Morphisme]] = (set(),set()), option_elimination:Sequence[Diagramme] = set(), option_complex_agissante:Sequence[Diagramme] = set(), option_complex_classifiante:Sequence[Diagramme] = set()) -> Tuple[GC,Foncteur]:
nouveau_gc = copy(gc)
fonct = Foncteur(gc,nouveau_gc,{o:o for o in gc.objets},{m:m for m in gc[gc.objets,gc.objets]})
nouveaux_obj, nouvelles_fleches = option_ajout
nouveau_gc |= set(nouveaux_obj)
nouveau_gc |= set(nouvelles_fleches)
nouveau_gc -= set(option_elimination)
while True:
for diag in option_complex_agissante:
c_a = ChampActif(fonct@diag)
colimites = c_a.objets_colimites()
if len(colimites) == 0:
# S'il n'y avait pas de colimite, on la créé
nouvel_objet = "Colimite de "+str(diag)
while nouvel_objet in nouveau_gc.objets:
nouvel_objet += "'"
nouveau_gc |= {nouvel_objet}
dico_index_jambe = dict() # {i:jambe}
for i in diag.source.objets:
jambe = MGC(diag(i),nouvel_objet)
dico_index_jambe[i] = jambe
nouveau_gc |= {jambe}
for fleche_i in diag.source[diag.source.objets, diag.source.objets]:
if dico_index_jambe[fleche_i.source] != dico_index_jambe[fleche_i.cible]@diag(fleche_i):
MGC.identifier_morphismes(dico_index_jambe[fleche_i.cible]@diag(fleche_i),dico_index_jambe[fleche_i.source])
#ajouter les flèches vers les autres cocônes
for cocone in c_a.objets:
cocone = Cocone(cocone)
nouvelle_fleche = MGC(nouvel_objet,cocone.nadir)
for fleche_i in diag.source[diag.source.objets, diag.source.objets]:
if dico_index_jambe[fleche_i.source] != cocone(fleche_i.cible)@diag(fleche_i):
MGC.identifier_morphismes(cocone(fleche_i.cible)@diag(fleche_i),dico_index_jambe[fleche_i.source])
break
else:
for diag in option_complex_classifiante:
c_p = ChampPerceptif(fonct@diag)
limites = c_p.objets_limites()
if len(limites) == 0:
# S'il n'y avait pas de limite, on la créé
nouvel_objet = "Limite de "+str(diag)
while nouvel_objet in nouveau_gc.objets:
nouvel_objet += "'"
nouveau_gc |= {nouvel_objet}
dico_index_jambe = dict() # {i:jambe}
for i in diag.source.objets:
jambe = MGC(nouvel_objet,diag(i))
dico_index_jambe[i] = jambe
nouveau_gc |= {jambe}
for fleche_i in diag.source[diag.source.objets, diag.source.objets]:
if dico_index_jambe[fleche_i.cible] != diag(fleche_i)@dico_index_jambe[fleche_i.source]:
MGC.identifier_morphismes(diag(fleche_i)@dico_index_jambe[fleche_i.source],dico_index_jambe[fleche_i.cible])
#ajouter les flèches depuis les autres cônes
for cone in c_p.objets:
cone = Cone(cone)
nouvelle_fleche = MGC(cocone.apex,nouvel_objet)
for fleche_i in diag.source[diag.source.objets, diag.source.objets]:
if cone(fleche_i.source) != dico_index_jambe[fleche_i.cible]@diag(fleche_i):
MGC.identifier_morphismes(dico_index_jambe[fleche_i.cible]@diag(fleche_i),cone(fleche_i.source))
break
else:
break # si on a rien ajouté on peut enfin sortir
return nouveau_gc,fonct
cat.transformer_graphviz()
h = MGC('S','T','h')
diag = DiagrammeIdentite(cat)
cat2,fonct = modification(cat,({'S','T'},{h}),{},{diag},{diag})
cat2.transformer_graphviz()
fonct.transformer_graphviz()
diag2 = Diagramme(cat,cat2,{'A':'A','B':'B','C':'C'},{f:f,g:g})
MGC.identifier_morphismes(i@f,h)
MGC.identifier_morphismes(j@g,h)
diag = fonct@diag
diag.transformer_graphviz()
ChampPerceptif(diag).transformer_graphviz()
for cone in ChampPerceptif(diag).objets:
Cone(cone).transformer_graphviz()
diag2.transformer_graphviz()
c_a2 = ChampActif(diag2)
c_a2.transformer_graphviz()
\ No newline at end of file
......@@ -73,7 +73,8 @@ class Application(Morphisme):
if CLEAN_GRAPHVIZ_MODEL:
import os
os.remove(destination)
class CategorieEnsemblesFinis(Categorie):
"""CategorieEnsFinis peut être abrégé en EnsFinis
Catégorie des ensembles finis, cette catégorie est infinie, on ajoute uniquement les ensembles dont on a besoin.
......@@ -194,9 +195,9 @@ class CategorieBijections(EnsFinis):
yield Bijection(source,cible,{source_liste[j]:rotation[j] for j in range(len(source_liste))})
def test_EnsFinis():
cat = EnsFinis(set(map(frozenset,[{},{1,2},{3,4,5},{6,7,8,9,10}])))
cat.transformer_graphviz(limite_fleches=243,complet=False)
cat = EnsFinis(set(map(frozenset,[{},{1,2},{3,4,5}])))
cat.transformer_graphviz(limite_fleches=243,complet=False)
cat.transformer_graphviz(limite_fleches=243,complet=True)
# for source,cible in itertools.product(cat.objets,repeat=2):
# print(source,cible)
# print(len(list(cat({source},{cible}))))
......@@ -222,6 +223,6 @@ def test_CategorieBijections():
cat.transformer_graphviz(limite_fleches=27)
if __name__ == '__main__':
# test_EnsFinis()
test_EnsParties()
test_EnsFinis()
# test_EnsParties()
# test_CategorieBijections()
\ No newline at end of file
......@@ -33,6 +33,21 @@ cat_hom.transformer_graphviz()
from Cluster import CategorieClusters
cocone_limite_d1 = Cocone(list(sous_c_p1.objets)[0])
cocone_limite_d1.transformer_graphviz()
cat_clust = CategorieClusters(cat,{d1,d2})
SousCategoriePleine(cat_clust,{d1,d2}).transformer_graphviz()
print(cat_clust.existe_isomorphisme(d1,d2))
\ No newline at end of file
for cocone_d2 in c_p2.objets:
if Cocone(cocone_d2).nadir == cocone_limite_d1.nadir:
Cocone(cocone_d2).transformer_graphviz()
for cluster in cat_clust({d1},{d2}):
print(type(Cocone(cocone_d2).as_Cluster()))
print(type(cluster))
print(cocone_limite_d1 == Cocone(cocone_d2).as_Cluster()@cluster)
cluster.transformer_graphviz()
(Cocone(cocone_d2).as_Cluster()@cluster).transformer_graphviz()
\ No newline at end of file
......@@ -17,7 +17,8 @@ class MorphismeGrapheDeComposition(Morphisme):
#{tuple de morphismes : tuple de morphismes}
def identifier_morphismes(morph1:'MorphismeGrapheDeComposition',morph2:'MorphismeGrapheDeComposition'):
"""Identifie le `morph1` au `morph2`.
"""Identifie le `morph1` au `morph2` /!\ dans cet ordre (c-a-d morph1 devient morph2) /!\.
`morph1` ne doit pas être élémentaire.
Cette fonction statique modifie la loi de composition de la classe `MorphismeGrapheDeComposition`.
Les morphismes seront identifiés indépendamment des catégories auxquelles ils appartiennent."""
MorphismeGrapheDeComposition.loi_de_composition[morph1.__chemin] = morph2.__chemin
......
......@@ -38,7 +38,8 @@ class TransformationNaturelle(Interaction):
self.verifier_coherence()
def __call__(self, obj:Any) -> Morphisme:
"""Renvoie l'image d'un objet de C."""
"""Renvoie l'image d'un objet de C.
L'image d'un objet i de C est une composante qui fait la "translation" du premier objet indexé par i vers le second objet indexé par i."""
if obj not in self.__C.objets:
raise Exception("L'objet "+str(obj)+" n'appartient pas à la categorie C = "+str(self.__C))
return self._composantes[obj]
......@@ -69,9 +70,9 @@ class TransformationNaturelle(Interaction):
for f in self.__C({source},{cible}):
c1,c2 = [f.source,f.cible]
if self.__G(f)@self(c1) != self(c2)@self.__F(f):
raise Exception("Incoherence TransformationNaturelle : la commutativité des diagrammes est pas respectee"+\
raise Exception("Incoherence TransformationNaturelle : la commutativité des diagrammes est pas respectee\n"+\
"Sf = "+str(self.__F(f))+"\ntC' = "+str(self(c2))+"\ntC = "+str(self(c1))+"\nTf = "+str(self.__G(f))+\
"tC' o Sf != Tf o tC : "+str(self(c2)@self.__F(f)) + " != "+str(self.__G(f)@self(c1)))
"\ntC' o Sf != Tf o tC : "+str(self(c2)@self.__F(f)) + " != "+str(self.__G(f)@self(c1)))
TransfoNat = TransformationNaturelle
......@@ -114,7 +115,6 @@ 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})
@mise_en_cache_call
def __call__(self, sources:set, cibles:set) -> Generator[TransfoNat,None,None]:
if len(sources) > 0:
J = list(sources)[0].source
......
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
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
DEBUG_LOI_DE_COMPOSITION = False
DEBUG_MONOIDE_ALEATOIRE = True
GRAPHVIZ_ENABLED = True # booléen qui indique s'il faut charger la bibliothèque graphviz
......
......@@ -57,4 +57,6 @@ cat[a,b] appelle cat(a,b) par défaut sauf pour le graphe de composition
utiliser existe_morphisme plutôt que self({cocone_courant},cocones_restants) dans la recherche de limites
faire une seule classe catégorie homologue en créant une méthode sommet dans cone et cocone;
\ No newline at end of file
faire une seule classe catégorie homologue en créant une méthode sommet dans cone et cocone;
faire un test avec la catégorie de foncteurs avec pour objets CatégorieBij(1,2,3) et CatégorieBij(a,b,c)
\ No newline at end of file
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