Commit 0991640e authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Remaniement du code pour que tous les call soient des yield, introduction de...

Remaniement du code pour que tous les call soient des yield, introduction de getitem pour récupérer les flèches élémentaires
parent 0fd3832c
......@@ -2,22 +2,25 @@ from Categorie import Categorie
from Foncteur import Foncteur
from EnsFinis import EnsFinis,Application
import itertools
from typing import *
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, sources:set, cibles:set) -> set:
result = set()
for a in sources:
for b in cibles:
# a et b sont des catégories, on cherche tous les foncteurs de a vers b
ens_objets = EnsFinis({a.objets,b.objets})
for app_obj in ens_objets({a.objets},{b.objets}):
def identite(self, objet:Categorie) -> Foncteur:
return Foncteur(objet,objet, {o:o for o in objet.objets}, {m:m for m in objet[objet.objets,objet.objets]})
def __call__(self, sources:set, cibles:set) -> Generator[Foncteur,None,None]:
for cat_source in sources:
for cat_cible in cibles:
# cat_source et cat_cible sont des catégories, on cherche tous les foncteurs de cat_source vers cat_cible
ens_objets = EnsFinis({cat_source.objets,cat_cible.objets})
for app_obj in ens_objets({cat_source.objets},{cat_cible.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)}))})
for c,d in itertools.product(cat_source.objets, repeat=2):
ens_fleches = EnsFinis({frozenset(cat_source[{c},{d}]),frozenset(cat_cible({app_obj(c)},{app_obj(d)}))})
app_fleches_c_vers_d = set(ens_fleches({frozenset(cat_source[{c},{d}])},{frozenset(cat_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
......@@ -27,16 +30,15 @@ class CatFinies(Categorie):
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(abs(a),{f.source}):
if app_fleches[f@g] != app_fleches[f]@app_fleches[g]:
for f in cat_source[abs(cat_source),abs(cat_source)]:
for g in cat_source[abs(cat_source),{f.source}]:
if f@g in app_fleches and 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
yield Foncteur(cat_source,cat_cible,app_obj.as_dict(),app_fleches)
def test_CatFinies():
from GrapheDeComposition import GC,MGC
......@@ -57,8 +59,9 @@ def test_CatFinies():
fonct.transformer_graphviz()
fonct.as_diagram().transformer_graphviz()
print(cat.isomorphismes(gc,gc2))
print(next(cat.isomorphismes(gc,gc2)))
list(cat.isomorphismes(gc,gc2))[0][0].transformer_graphviz()
cat.identite(gc).transformer_graphviz()
if __name__ == '__main__':
test_CatFinies()
\ No newline at end of file
......@@ -5,10 +5,73 @@ from collections import defaultdict
import itertools
from Morphisme import Morphisme
from config import *
import typing
from MiseEnCache import call_mis_en_cache
import copy
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
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]:
elem_generes = set()
for elem in func(self,*args):
if elem not in elem_generes:
elem_generes |= {elem}
yield elem
return wrapper
def mise_en_cache_getitem(func):
"""Décorateur qui permer de stocket le résultat de __getitem__ dans un cache et
cherche dans le cache lors de l'appel de __getitem__."""
if func.__name__ != '__getitem__':
raise Exception("Mise en cache d'une fonction differente de __getitem__ : "+str(func))
def wrapper(self, couple_sources_cibles:tuple) -> Generator[Morphisme,None,None]:
sources,cibles = couple_sources_cibles
if not hasattr(self,"__cache__getitem__"):
self.__cache__getitem__ = defaultdict(set)
nouvelles_sources = set()
nouvelles_cibles = set()
for source in sources:
for cible in cibles:
if (source,cible) in self.__cache__getitem__:
for fleche in self.__cache__getitem__[(source,cible)]:
yield fleche
else:
nouvelles_sources |= {source}
nouvelles_cibles |= {cible}
for s in nouvelles_sources:
for c in nouvelles_cibles:
for fleche in func(self,({s},{c})):
yield fleche
self.__cache__getitem__[(s,c)] |= {fleche}
return wrapper
def mise_en_cache_call(func):
"""Décorateur qui permer de stocket le résultat de __call__ dans un cache et
cherche dans le cache lors de l'appel de __call__."""
if func.__name__ != '__call__':
raise Exception("Mise en cache d'une fonction differente de __call__ : "+str(func))
def wrapper(self, sources:set, cibles:set) -> Generator[Morphisme,None,None]:
if not hasattr(self,"__cache__call__"):
self.__cache__call__ = defaultdict(set)
nouvelles_sources = set()
nouvelles_cibles = set()
for source in sources:
for cible in cibles:
if (source,cible) in self.__cache__call__:
for fleche in self.__cache__call__[(source,cible)]:
yield fleche
else:
nouvelles_sources |= {source}
nouvelles_cibles |= {cible}
for s in nouvelles_sources:
for c in nouvelles_cibles:
for fleche in func(self,{s},{c}):
yield fleche
self.__cache__call__[(s,c)] |= {fleche}
return wrapper
class Categorie:
"""Classe abstraite qui définit ce qu'est une catégorie.
......@@ -20,9 +83,10 @@ class Categorie:
On reprend la notation classique C(a,b) pour les flèches de a à b
et abs(C) = |C| pour les objets de C.
Pour récapituler :
- abs(C) opérateur pour obtenir les objets
- C(_,_) opérateur pour obtenir des morphismes
Listes des méthodes importantes :
- abs(C) (ou C.objets) opérateur pour obtenir les objets
- C(_,_) opérateur pour obtenir un itérateur sur tous les morphismes
- C[_,_] opérateur pour obtenir un itérateur sur les morphismes élémentaires (morphismes à partir dequels on peut obtenir tous les morphismes par composition)
- |= opérateur pour ajouter des objets
- -= opérateur pour supprimer des objets
......@@ -38,21 +102,45 @@ class Categorie:
Categorie._id += 1
self._id = Categorie._id
self.nom = "Categorie "+str(self._id) if nom == None else nom
self.objets = frozenset()
self._objets = frozenset() # read-only attribute
self |= objets
def __hash__(self) -> int:
return hash(self.objets)
def __call__(self, sources: any, cibles: any) -> set:
@unique_generator # la méthode call doit renvoyer une seule fois chaque flèche
def __call__(self, sources: set, cibles: set) -> Generator[Morphisme,None,None]:
"""
Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie :
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}.
C({a_i},{b_i}) renvoie un itérateur sur l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}.
"""
raise NotImplementedError("Les categories filles doivent implementer cette methode pour renvoyer les morphismes C(a,b) (fleches de a vers b) et C(a) (identite de a)")
def __getitem__(self, couple_sources_cibles:tuple) -> Generator[Morphisme,None,None]:
"""
C[{a_i},{b_i}] renvoie un itérateur sur l'ensemble des flèches élémentaires d'un élément de {a_i} vers un élément de {b_i}.
Les flèches élémentaires d'une catégorie C sont des flèches telles que toutes les flèches de C s'obtiennent par composition de flèches élémentaires.
Par défaut C[{a_i},{b_i}] renvoie toutes les flèches (équivalent à C({a_i},{b_i})), les classes filles peuvent surcharger cette méthode
pour optimiser les calculs.
Exemple où les flèches élémentaires ont un intérêt : pour construire un foncteur, il suffit de donner une application entre flèches élémentaires.
Surcharger __getitem__ conduit à hériter de CategorieLibre pour s'assurer que toutes les flèches sont bien générées
par les flèches élémentaires.
On peut surcharger la méthode __call__ de CategorieLibre pour optimiser les calculs quand c'est pertinent. Il faut alors
s'assurer que la nouvelle méthode renvoie les mêmes morphismes que l'ancienne méthode aurait renvoyée.
Si on surcharge __getitem__, on peut aussi surcharger __iter__ des morphismes associés à la catégorie pour itérer sur les constituants
élémentaires du morphisme.
"""
if len(couple_sources_cibles) != 2:
raise Exception("On s'attend à avoir un couple : "+str(couple_sources_cibles))
return self(*couple_sources_cibles)
def identite(self,objet: any) -> Morphisme:
def identite(self,objet: Any) -> Morphisme:
"""
Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie,
......@@ -60,12 +148,6 @@ class Categorie:
"""
NotImplementedError("Les categories filles doivent implementer cette methode.")
def __eq__(self,other: 'Categorie') -> bool:
return issubclass(type(other),Categorie) and self.nom == other.nom and self.objets == other.objets and type(self) == type(other)
def __hash__(self) -> int:
return hash((self.nom,self.objets))
def __str__(self) -> str:
return self.nom
......@@ -76,20 +158,32 @@ class Categorie:
"""
return self.objets
@property
def objets(self) -> frozenset:
return self._objets
def __ior__(self,other:set):
"""
Soit C une catégorie.
C |= {a_1,a_2,...} ajoute les objets a_i à la catégorie C.
"""
self.objets |= other
self._objets |= other
if hasattr(self,"__cache__getitem__"):
self.__cache__getitem__ = defaultdict(set)
if hasattr(self,"__cache__call__"):
self.__cache__call__ = defaultdict(set)
return self
def __isub__(self,other:any):
def __isub__(self,other:Any):
"""
Soit C une catégorie.
C -= {a_1,a_2,...} supprime les objets a_i de la catégorie C.
"""
self.objets -= other
self._objets -= other
if hasattr(self,"__cache__getitem__"):
self.__cache__getitem__ = defaultdict(set)
if hasattr(self,"__cache__call__"):
self.__cache__call__ = defaultdict(set)
return self
def verifier_coherence(self):
......@@ -111,18 +205,25 @@ 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 isomorphismes(self, source:any, cible:any) -> set:
def isomorphismes(self, source:Any, cible:Any) -> Generator[tuple,None,None]:
"""Trouve les isomorphismes entre `source` et `cible`.
Renvoie un ensemble de couple (f,g) tels que gof = Id1 et fog = Id2.
Renvoie un générateur de couples {f,g} tels que gof = Id1 et fog = Id2, et f:source->cible.
"""
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 existe_morphisme(self, source:any, cible:any) -> Morphisme:
"""Renvoie True s'il existe un morphisme de `source` à `cible`, renvoie False sinon.
Cette fonction peut-être redéfinie pour accélérer les temps de calcul
(pour ne pas avoir besoin de calculer toutes les flèches de source à cible.)"""
return len(self(source,cible)) > 0
for f in self({source},{cible}):
for g in self({cible},{source}):
if (f@g).is_identite and (g@f).is_identite:
yield (f,g)
def existe_morphisme(self, source:Any, cible:Any) -> Union[Morphisme,None]:
"""Renvoie un morphisme 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."""
return next(self.isomorphismes(source,cible),None)
def table_loi_de_composition(self):
"""
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.
......@@ -135,8 +236,9 @@ class Categorie:
for g in self(B,abs(self)):
table[g][f] = g@f
return table
def loi_de_composition_to_csv(self, destination:str=None, sep: str=','):
def loi_de_composition_to_csv(self, destination:Union[str,None]=None, sep: str=','):
"""
Exporte la loi de composition de la catégorie en csv.
Si destination == None, alors on le print sinon on l'écrit dans un fichier à la destination.
......@@ -161,8 +263,10 @@ class Categorie:
result = result.replace(',','\t,')
print(result)
def transformer_graphviz(self, destination:str=None, afficher_identites:bool=False):
"""Permet de visualiser la catégorie avec graphviz"""
def transformer_graphviz(self, destination:Union[str,None] = None, afficher_identites:bool = True, complet:bool = True, limite_fleches:int = 50):
"""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."""
Categorie.nb_viz += 1
if destination == None:
destination = "graphviz/"+type(self).__name__+ str(Categorie.nb_viz)
......@@ -173,21 +277,32 @@ class Categorie:
for o in self.objets:
graph.node(str(o))
toutes_les_fleches = self(self.objets,self.objets)
for fleche in toutes_les_fleches:
if afficher_identites or not fleche.is_identite:
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")
fleches_elem = set(self[self.objets,self.objets])
if complet:
func = lambda x,y : self({x},{y})
else:
func = lambda x,y : self[{x},{y}]
for source,cible in itertools.product(self.objets,repeat=2):
nb_fleches = 0
for fleche in func(source,cible):
if afficher_identites or not fleche.is_identite:
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", color="grey80" if fleche not in fleches_elem else "black")
nb_fleches += 1
if nb_fleches > limite_fleches:
if WARNING_LIMITE_FLECHES_ATTEINTE:
print("Warning : limite fleches entre "+str(source)+" et "+str(cible)+" atteinte.")
break
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
import os
......@@ -196,8 +311,9 @@ class Categorie:
def test_Categorie():
from GrapheDeComposition import GC,MGC
GC = GC({'A','B','C'})
morphismes = [MGC('A','B','f'),MGC('B','C','g')]
GC |= morphismes
f,g,h = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','A','h')]
MGC.identifier_morphismes(h@h@h,h)
GC |= {f,g,h}
GC.transformer_graphviz()
GC.loi_de_composition_to_csv()
......@@ -208,16 +324,10 @@ class SousCategoriePleine(Categorie):
et de tous les morphismes entre ces objets.
"""
def __init__(self, categorie_originelle:Categorie, objets:set):
"""On construit la sous-catégorie pleine de `categorie_originelle` qui contient l'ensemble des `objets`."""
Categorie.__init__(self,objets,"Sous-catégorie pleine de "+str(categorie_originelle)+ " ayant pour objets "+str(objets))
self.categorie_originelle = categorie_originelle
def __call__(self, sources:set, cibles:set) -> set:
return self.categorie_originelle(sources,cibles)
def identite(self,objet:any) -> Morphisme:
return self.categorie_originelle.identite(objet)
def __new__(cls, categorie_originelle:Categorie, objets:set) -> 'SousCategoriePleine':
copie = copy.copy(categorie_originelle)
copie -= {obj for obj in copie.objets if obj not in objets}
return copie
def test_SousCategoriePleine():
from GrapheDeComposition import GC,MGC
......@@ -237,7 +347,7 @@ class CategorieDiscrete(Categorie):
"""
Tous les morphismes de la catégorie discrète sont des identités.
"""
def __init__(self,objet:any):
def __init__(self,objet:Any):
Morphisme.__init__(self,objet,objet,"Id"+str(objet),True)
def __matmul__(self,other:'CategorieDiscrete.Identite') -> 'CategorieDiscrete.Identite':
......@@ -256,10 +366,10 @@ class CategorieDiscrete(Categorie):
def __hash__(self) -> int:
return hash(self.source)
def __call__(self,sources:set,cibles:set) -> set():
def __call__(self,sources:set,cibles:set) -> Generator[Morphisme,None,None]:
return {CategorieDiscrete.Identite(a) for a in sources for b in cibles if a == b}
def identite(self,objet:any) -> 'CategorieDiscrete.Identite':
def identite(self,objet:Any) -> 'CategorieDiscrete.Identite':
return CategorieDiscrete.Identite(objet)
def test_CategorieDiscrete():
......
from Categorie import Categorie
from Categorie import Categorie, mise_en_cache_call
from Foncteur import Foncteur
from CatFinies import CatFinies
from EnsFinis import CategorieBijections,Bijection
from ChampActif import ChampActif,Cocone
from collections import defaultdict
import itertools
from ProduitGenerateurs import produit_cartesien_generateurs
from typing import *
class FoncteurOubli(Foncteur):
"""Foncteur d'oubli du champ perceptif.
"""Foncteur d'oubli du champ actif.
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))})
{f:f.k 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 bijectifs qui commutent avec le foncteur d'oubli."""
......@@ -26,11 +30,8 @@ class CategorieHomologue(CatFinies):
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()
@mise_en_cache_call
def __call__(self, sources:set, cibles:set) -> Generator[Foncteur,None,None]:
for source in sources:
for cible in cibles:
dict_nadir_cocone_source = defaultdict(frozenset)
......@@ -41,53 +42,43 @@ class CategorieHomologue(CatFinies):
dict_nadir_cocone_cible[Cocone(obj).nadir] |= {obj}
if set(dict_nadir_cocone_source.keys()) != set(dict_nadir_cocone_cible.keys()):
continue
bijections_objets = set() # bijections entre cocônes de même nadir
ens_objets = CategorieBijections()
rate = False
dict_fleches_k_source = defaultdict(lambda: defaultdict(frozenset)) #{(a,b):{k:{flecheComma1(k),flecheComma2(k),...}}}
for a,b in itertools.product(source.objets,repeat=2):
for fleche in source[source.objets,source.objets]:
dict_fleches_k_source[(a,b)][fleche.k] |= {fleche}
dict_fleches_k_cible = defaultdict(lambda: defaultdict(frozenset)) #{(a,b):{k:{flecheComma1(k),flecheComma2(k),...}}}
for a,b in itertools.product(cible.objets,repeat=2):
for fleche in cible[cible.objets,cible.objets]: #on peut viser que les flèches elem puisqu'on doit commuter avec le foncteur d'oubli qui mène des flèches elem à des flèches elem
dict_fleches_k_cible[(a,b)][fleche.k] |= {fleche}
bijections_objets = set() # bijections entre cônes de même nadir
cat_bij = CategorieBijections()
for nadir in dict_nadir_cocone_source:
if len(dict_nadir_cocone_source[nadir]) != len(dict_nadir_cocone_cible[nadir]):
rate = True
break
ens_objets |= {dict_nadir_cocone_source[nadir]}
ens_objets |= {dict_nadir_cocone_cible[nadir]}
bijections_objets |= {frozenset(ens_objets({dict_nadir_cocone_source[nadir]},{dict_nadir_cocone_cible[nadir]}))}
if rate:
continue
print(len(list(itertools.product(*bijections_objets))))
for bijection_objet in itertools.product(*bijections_objets):
cat_bij |= {dict_nadir_cocone_source[nadir]}
cat_bij |= {dict_nadir_cocone_cible[nadir]}
bijections = cat_bij({dict_nadir_cocone_source[nadir]},{dict_nadir_cocone_cible[nadir]})
bijections_objets |= {bijections}
for bijection_objet in produit_cartesien_generateurs(*bijections_objets):
bij_obj = dict([x for bij in bijection_objet for x in bij.as_dict().items()])
bij_fleche_foncteur = dict() # à chaque couple d'objets on associe un ensemble de bijections
rate = False
for c,d in itertools.product(source.objets, repeat=2):
fleches_source_c_vers_d = source({c},{d})
fleches_cibles_c_vers_d = cible({bij_obj[c]},{bij_obj[d]})
if len(fleches_source_c_vers_d) != len(fleches_cibles_c_vers_d):
rate = True
break
ens_fleches = CategorieBijections({frozenset(fleches_source_c_vers_d),frozenset(fleches_cibles_c_vers_d)})
bij_fleches_c_vers_d = ens_fleches({frozenset(source({c},{d}))},{frozenset(cible({bij_obj[c]},{bij_obj[d]}))})
bij_fleche_foncteur[(c,d)] = bij_fleches_c_vers_d
if len(bij_fleches_c_vers_d) == 0:
rate = True
break
if rate:
continue
print(len(list(itertools.product(*bij_fleche_foncteur.values()))))
for bijections_fleches in itertools.product(*bij_fleche_foncteur.values()):
bij_fleches = dict()
for bij in bijections_fleches:
bij_fleches.update(bij.as_dict())
for f in source(abs(source),abs(source)):
for g in source(abs(source),{f.source}):
if bij_fleches[f@g] != bij_fleches[f]@bij_fleches[g]:
break
else:
continue
break
else:
result |= {Foncteur(source,cible,bij_obj,bij_fleches)}
return result
bijections_fleches = set()
for a,b in itertools.product(source.objets,repeat=2):
for k in dict_fleches_k_source[(a,b)]:
cat_bij |= {dict_fleches_k_source[(a,b)][k],dict_fleches_k_cible[(bij_obj[a],bij_obj[b])][k]}
bijections = cat_bij({dict_fleches_k_source[(a,b)][k]},{dict_fleches_k_cible[(bij_obj[a],bij_obj[b])][k]})
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()])
yield Foncteur(source,cible,bij_obj,bij_fleche)
def isomorphismes(self, source:ChampActif, cible:ChampActif) -> Generator[tuple,None,None]:
"""Trouve les isomorphismes entre `source` et `cible`.
Renvoie un générateur de couples {f,g} tels que gof = Id1 et fog = Id2, et f:source->cible.
"""
for f in self({source},{cible}):
g = Foncteur(cible,source,{f(obj):obj for obj in source.objets},{f(morph):morph for morph in source[source.objets,source.objets]})
yield (f,g)
def test_FoncteurOubli():
from GrapheDeComposition import GC,MGC
......@@ -114,7 +105,7 @@ def test_CategorieHomologue():
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')]
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','H','m'),MGC('F','H','n')]
gc |= {f,g,h,i,j,k,l,m,n}
diag_identite = DiagrammeIdentite(gc)
......@@ -122,18 +113,18 @@ def test_CategorieHomologue():
d1 = Fleche(gc,m)
c_p1 = ChampActif(d1)
c_p1.transformer_graphviz()
d2 = Fleche(gc,n)
c_p2 = ChampActif(d2)
c_p1.transformer_graphviz()
c_p2.transformer_graphviz()
cph = CategorieHomologue({c_p1,c_p2})
cph.transformer_graphviz()
f,g = cph.isomorphismes(c_p1,c_p2).pop()
f,g = set(cph.isomorphismes(c_p1,c_p2)).pop()
f.transformer_graphviz()
g.transformer_graphviz()
if __name__ == '__main__':
test_FoncteurOubli()
# test_FoncteurOubli()
test_CategorieHomologue()
\ No newline at end of file
from Categorie import Categorie
from Categorie import Categorie, unique_generator
from Morphisme import Morphisme
from copy import copy
import itertools
import functools
from config import *
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
from typing import *
class CategorieLibre(Categorie):
"""
......@@ -12,10 +14,9 @@ class CategorieLibre(Categorie):
Cette classe surcharge l'opérateur __call__, pour calculer C(a,b),
on fait tous les chemins de a vers b et on renvoie l'ensemble de ces composées.