Commit 7210ba2b authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

sous_cat_equiv dans Catégorie quotient

parent d1227c28
......@@ -104,6 +104,8 @@ class Categorie:
self.nom = "Categorie "+str(self._id) if nom == None else nom
self._objets = frozenset() # read-only attribute
self |= objets
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
def __hash__(self) -> int:
return hash(self.objets)
......@@ -188,9 +190,10 @@ class Categorie:
def verifier_coherence(self):
"""Vérifie la cohérence de la structure (tous les axiomes des catégories sont vérifiés)."""
morphismes = sorted(self(self.objets,self.objets))
## On vérifie que les identités sont neutres
for morphisme in self(self.objets,self.objets):
for morphisme in morphismes:
if not (morphisme@self.identite(morphisme.source) == self.identite(morphisme.cible)@morphisme == morphisme):
raise Exception("Incoherence Categorie : le morphisme "+str(morphisme)+" est modifie par une identite.\n"\
+repr(morphisme)+" o "+repr(self.identite(morphisme.source))+" = "+repr(morphisme@self.identite(morphisme.source))+"\n"\
......@@ -198,12 +201,18 @@ class Categorie:
repr(morphisme))
## On vérifie l'associativité
for m1,m2,m3 in itertools.product(sorted(self(self.objets,self.objets)),repeat=3):
for m1,m2,m3 in itertools.product(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(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))
## On vérifie que toutes les composées existent
for m1 in morphismes:
for m2 in self({m1.cible},self.objets):
if m2@m1 not in morphismes:
raise Exception("Incoherence Categorie : composee de deux morphismes pas dans la categorie "+str(m2@m1))
def isomorphismes(self, source:Any, cible:Any) -> Generator[tuple,None,None]:
"""Trouve les isomorphismes entre `source` et `cible`.
......@@ -346,6 +355,39 @@ def test_SousCategoriePleine():
sous_cat_pleine = SousCategoriePleine(GC,set("ADE"))
sous_cat_pleine.transformer_graphviz()
class SousCategorie(Categorie):
def __init__(self, categorie_originelle:Categorie, objets_a_conserver:frozenset, morph_a_conserver:frozenset,nom:Union[str,None] = None):
"""`objets_a_conserver` contient tous les objets à conserver.
`morph_a_conserver` contient tous les morphismes à conserver (pas seulement les flèches élémentaires).
Toutes les flèches deviennent élémentaires (on perd la structure de la catégorie initiale).
/!\ Les flèches restantes doivent former une catégorie, si deux flèches sont composables alors la composée doit être dans `morph_a_conserver` /!\ """
self._cat_origin = categorie_originelle
self._morph = morph_a_conserver
Categorie.__init__(self,objets_a_conserver,nom if nom != None else "Sous catégorie de "+str(categorie_originelle))
def identite(self,objet:Any) -> Morphisme:
assert(objet in self.objets)
return self._cat_origin.identite(objet)
def __call__(self, sources: set, cibles: set) -> Generator[Morphisme,None,None]:
assert(len(sources-self.objets) == 0 and len(cibles-self.objets) == 0)
for morph in self._cat_origin(sources,cibles):
if morph in self._morph or morph.is_identite:
yield morph
def __getitem__(self, couple_sources_cibles:tuple) -> Generator[Morphisme,None,None]:
for morph in self(*couple_sources_cibles):
yield morph
def test_SousCategorie():
from GrapheDeComposition import GC,MGC
GC = GC(set("ABCDE"))
morphismes = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','D','h'),MGC('D','E','i'),MGC('C','E','j')]
GC |= morphismes
GC.transformer_graphviz()
sous_cat = SousCategorie(GC,set("ABDE"),{morphismes[0],morphismes[2],morphismes[3],morphismes[3]@morphismes[2]})
sous_cat.transformer_graphviz()
class CategorieDiscrete(Categorie):
"""
......@@ -388,4 +430,5 @@ def test_CategorieDiscrete():
if __name__ == '__main__':
test_Categorie()
test_CategorieDiscrete()
test_SousCategoriePleine()
\ No newline at end of file
test_SousCategoriePleine()
test_SousCategorie()
\ No newline at end of file
......@@ -4,7 +4,8 @@ from Morphisme import Morphisme
import itertools
class CategorieAcyclique(CategorieQuotient):
"""Catégorie quotientée par la relation d'équivalence sur les objets suivante :
"""`CategorieAcyclique` peut être abrégé en `CatAcyclique`.
Catégorie quotientée par la relation d'équivalence sur les objets suivante :
x ~ y ssi il existe f: x -> y et il existe g: y -> x."""
def __init__(self,categorie_a_quotienter:Categorie, nom:str = None):
......@@ -22,20 +23,23 @@ class CategorieAcyclique(CategorieQuotient):
else:
for m in categorie_a_quotienter({obj1},{obj1}):
self.identifier_morphismes(m,categorie_a_quotienter.identite(obj1))
CatAcyclique = CategorieAcyclique
def test_CategorieAcyclique():
from CategorieAleatoire import GrapheCompositionAleatoire
import random
random.seed(3)
for i in range(5):
random.seed(1)
for i in range(1):
c = GrapheCompositionAleatoire()
c.transformer_graphviz()
c_a = CategorieAcyclique(c)
c_a.transformer_graphviz()
c_a.fonct_surjection.transformer_graphviz()
for obj in c_a.objets:
c_a.sous_cat_equiv(obj).transformer_graphviz()
if __name__ == '__main__':
test_CategorieAcyclique()
\ No newline at end of file
......@@ -4,6 +4,8 @@ from Monoide import Monoide,ElementMonoideGC,MonoideGC
from Categorie import Categorie
from CategorieProduit import CategorieProduit
from CategorieLibre import CategorieLibre
from CategorieComposantesConnexes import CatCC
from CategorieAcyclique import CatAcyclique
import random
import copy
import itertools
......@@ -361,15 +363,33 @@ def test_MonoideGC():
mon.loi_de_composition_to_csv(destination="lois de composition/monoide.csv")
#class FoncteurAleatoire(Foncteur):
# """Foncteur aléatoire sur une catégorie."""
# def __new__(cls, categorie_indexante:Categorie = None, categorie_cible:Categorie = None, nom:str = None):
# if nom == None:
# nom = "Foncteur aléatoire sur "+str(categorie_cible)
# cat = CatFinies({categorie_indexante,categorie_cible}, "Catégorie des foncteurs pour génération de foncteur aléatoire")
# foncteur = random.choice(list(cat({categorie_indexante},{categorie_cible})))
# foncteur.nom = nom
# return foncteur
class FoncteurCCAleatoire(Foncteur):
"""Foncteur aléatoire entre deux catégories composantes connexes."""
def __init__(self,categorie_indexante:CatCC, categorie_cible:CatCC, nom:Union[str,None] = None):
Foncteur.__init__(self,categorie_indexante,categorie_cible,{obj:random.choice(list(categorie_cible.objets)) for obj in categorie_indexante.objets},dict(),nom)
def test_FoncteurCCAleatoire():
import random
random.seed(4)
c1,c2 = GrapheCompositionAleatoire(),GrapheCompositionAleatoire()
c1.transformer_graphviz()
c2.transformer_graphviz()
cc1,cc2 = CatCC(c1),CatCC(c2)
cc1.transformer_graphviz()
cc2.transformer_graphviz()
f = FoncteurCCAleatoire(cc1,cc2)
f.as_diagram().transformer_graphviz()
f.transformer_graphviz()
f = FoncteurCCAleatoire(cc1,cc2)
f.as_diagram().transformer_graphviz()
f.transformer_graphviz()
class FoncteurCatAcycliquesAleatoire(Foncteur):
"""Foncteur aléatoire entre deux catégories acycliques."""
def __init__(self,categorie_indexante:CatAcyclique, categorie_cible:CatAcyclique, nom:Union[str,None] = None):
fonct_cc = FoncteurAleatoire(categorie_indexante,categorie_cible)
Foncteur.__init__(self,categorie_indexante,categorie_cible,{obj:random.choice(list(categorie_cible.objets)) for obj in categorie_indexante.objets},dict(),nom)
class FoncteurAleatoire(Foncteur):
"""Foncteur aléatoire sur une catégorie."""
......@@ -381,8 +401,8 @@ class FoncteurAleatoire(Foncteur):
if categorie_cible == None:
categorie_cible = GrapheCompositionAleatoire()
# categorie_indexante.transformer_graphviz()
# categorie_cible.transformer_graphviz()
categorie_indexante.transformer_graphviz()
categorie_cible.transformer_graphviz()
app_obj = dict()
app_morph = dict()
......@@ -462,6 +482,6 @@ def test_FoncteurAleatoire():
if __name__ == '__main__':
test_GrapheCompositionAleatoire()
# test_GrapheCompositionAleatoire()
# test_MonoideGC()
# test_FoncteurAleatoire()
\ No newline at end of file
test_FoncteurCCAleatoire()
\ No newline at end of file
......@@ -8,7 +8,8 @@ import itertools
from typing import *
class CategorieComposantesConnexes(CategorieQuotient):
"""Catégorie quotientée par la relation d'équivalence sur les objets suivante :
"""`CategorieComposantesConnexes` peut être abrégé en `CatCC`
Catégorie quotientée par la relation d'équivalence sur les objets suivante :
x ~ y si il existe f: x -> y ou il existe f: y -> x."""
def __init__(self,categorie_a_quotienter:Categorie, nom:str = None):
......@@ -26,14 +27,14 @@ class CategorieComposantesConnexes(CategorieQuotient):
else:
for m in categorie_a_quotienter({obj1},{obj1}):
self.identifier_morphismes(m,categorie_a_quotienter.identite(obj1))
CatCC = CategorieComposantesConnexes
def test_CategorieComposantesConnexes():
from CategorieAleatoire import GrapheCompositionAleatoire
import random
random.seed(3)
for i in range(5):
for i in range(3):
c = GrapheCompositionAleatoire()
c.transformer_graphviz()
......@@ -41,6 +42,9 @@ def test_CategorieComposantesConnexes():
c_a.transformer_graphviz()
c_a.fonct_surjection.transformer_graphviz()
for obj in c_a.objets:
c_a.sous_cat_equiv(obj).transformer_graphviz()
class CategorieCatComposantesConnexes(Categorie):
"""Catégorie des catégories composantes connexes.
......
from Categorie import Categorie, unique_generator
from Categorie import Categorie, unique_generator, SousCategoriePleine
from CategorieLibre import CategorieLibre
from Foncteur import Foncteur
from Morphisme import Morphisme
......@@ -47,7 +47,7 @@ class MorphismeQuotient(Morphisme):
for m1 in other:
for m2 in self:
if m1.cible == m2.source:
return self._cat_quotient_associee.fonct_surjection(m2@m1)
return self._cat_quotient_associee._morph_vers_classe[m2@m1]
if config.WARNING_CATEGORIE_QUOTIENT:
print("Warning : lors du calcul de la composition "+str(self)+" o "+str(other)+", aucun morphisme composable")
return MorphismeQuotient(other.source,self.cible,set(),self)
......@@ -152,7 +152,11 @@ class CategorieQuotient(CategorieLibre):
yield self._morph_vers_classe[fleche_elem]
def identite(self, objet:ObjetQuotient) -> MorphismeQuotient:
return self.fonct_surjection(self.cat_a_quotienter.identite(next(iter(objet))))
return self._morph_vers_classe[self.cat_a_quotienter.identite(next(iter(objet)))]
def sous_cat_equiv(self, objet:ObjetQuotient) -> SousCategoriePleine:
'''Renvoie la sous-catégorie qui est envoyée sur un objet par le foncteur de surjection.'''
return SousCategoriePleine(self.cat_a_quotienter,{obj for obj in self.cat_a_quotienter.objets if self._obj_vers_classe[obj] == objet})
def test_CategorieQuotient():
......
......@@ -137,6 +137,19 @@ class GrapheDeComposition(CategorieLibre):
self.__morph_sortants = defaultdict(frozenset)
CategorieLibre.__init__(self,objets,"Graphe de Composition "+str(self.__id) if nom == None else nom)
def __copy__(self) -> "GrapheDeComposition":
nouveau_gc = GrapheDeComposition(self.objets)
for f in self[self.objets,self.objets]:
if not f.is_identite:
nouveau_gc |= {f}
else:
nouveau_gc.__morph_entrants[f.source] -= {nouveau_gc.identite(f.source)}
nouveau_gc.__morph_entrants[f.source] |= {f}
nouveau_gc.__morph_sortants[f.source] -= {nouveau_gc.identite(f.source)}
nouveau_gc.__morph_sortants[f.source] |= {f}
nouveau_gc.__identites[f.source] = f
return nouveau_gc
def verifier_coherence(self):
CategorieLibre.verifier_coherence(self)
if self.objets != set(self.__identites.keys()):
......@@ -203,8 +216,8 @@ class GrapheDeComposition(CategorieLibre):
self.__morph_entrants[m] -= {objet_ou_morphisme}
del self.__morph_entrants[objet_ou_morphisme]
del self.__morph_sortants[objet_ou_morphisme]
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
return self
def morph_entrants(self,objet:Any) -> frozenset():
......
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