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 ...@@ -2,22 +2,25 @@ from Categorie import Categorie
from Foncteur import Foncteur from Foncteur import Foncteur
from EnsFinis import EnsFinis,Application from EnsFinis import EnsFinis,Application
import itertools import itertools
from typing import *
class CatFinies(Categorie): class CatFinies(Categorie):
"""Catégorie des catégories finies, les morphismes sont des foncteurs. """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.""" 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: def identite(self, objet:Categorie) -> Foncteur:
result = set() return Foncteur(objet,objet, {o:o for o in objet.objets}, {m:m for m in objet[objet.objets,objet.objets]})
for a in sources:
for b in cibles: def __call__(self, sources:set, cibles:set) -> Generator[Foncteur,None,None]:
# a et b sont des catégories, on cherche tous les foncteurs de a vers b for cat_source in sources:
ens_objets = EnsFinis({a.objets,b.objets}) for cat_cible in cibles:
for app_obj in ens_objets({a.objets},{b.objets}): # 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 app_fleche_foncteur = dict() # à chaque couple d'objets on associe un ensemble d'applications
for c,d in itertools.product(a.objets, repeat=2): for c,d in itertools.product(cat_source.objets, repeat=2):
ens_fleches = EnsFinis({frozenset(a({c},{d})),frozenset(b({app_obj(c)},{app_obj(d)}))}) ens_fleches = EnsFinis({frozenset(cat_source[{c},{d}]),frozenset(cat_cible({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)}))}) 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 app_fleche_foncteur[(c,d)] = app_fleches_c_vers_d
if len(app_fleches_c_vers_d) == 0: if len(app_fleches_c_vers_d) == 0:
break break
...@@ -27,16 +30,15 @@ class CatFinies(Categorie): ...@@ -27,16 +30,15 @@ class CatFinies(Categorie):
app_fleches = dict() app_fleches = dict()
for app in applications_fleches: for app in applications_fleches:
app_fleches.update(app.as_dict()) app_fleches.update(app.as_dict())
for f in a(abs(a),abs(a)): for f in cat_source[abs(cat_source),abs(cat_source)]:
for g in a(abs(a),{f.source}): for g in cat_source[abs(cat_source),{f.source}]:
if app_fleches[f@g] != app_fleches[f]@app_fleches[g]: if f@g in app_fleches and app_fleches[f@g] != app_fleches[f]@app_fleches[g]:
break break
else: else:
continue continue
break break
else: else:
result |= {Foncteur(a,b,app_obj.as_dict(),app_fleches)} yield Foncteur(cat_source,cat_cible,app_obj.as_dict(),app_fleches)
return result
def test_CatFinies(): def test_CatFinies():
from GrapheDeComposition import GC,MGC from GrapheDeComposition import GC,MGC
...@@ -57,8 +59,9 @@ def test_CatFinies(): ...@@ -57,8 +59,9 @@ def test_CatFinies():
fonct.transformer_graphviz() fonct.transformer_graphviz()
fonct.as_diagram().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() list(cat.isomorphismes(gc,gc2))[0][0].transformer_graphviz()
cat.identite(gc).transformer_graphviz()
if __name__ == '__main__': if __name__ == '__main__':
test_CatFinies() test_CatFinies()
\ No newline at end of file
...@@ -5,10 +5,73 @@ from collections import defaultdict ...@@ -5,10 +5,73 @@ from collections import defaultdict
import itertools import itertools
from Morphisme import Morphisme from Morphisme import Morphisme
from config import * from config import *
import typing import copy
from MiseEnCache import call_mis_en_cache
if GRAPHVIZ_ENABLED: if GRAPHVIZ_ENABLED:
from graphviz import Digraph 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: class Categorie:
"""Classe abstraite qui définit ce qu'est une catégorie. """Classe abstraite qui définit ce qu'est une catégorie.
...@@ -20,9 +83,10 @@ class Categorie: ...@@ -20,9 +83,10 @@ class Categorie:
On reprend la notation classique C(a,b) pour les flèches de a à b On reprend la notation classique C(a,b) pour les flèches de a à b
et abs(C) = |C| pour les objets de C. et abs(C) = |C| pour les objets de C.
Pour récapituler : Listes des méthodes importantes :
- abs(C) opérateur pour obtenir les objets - abs(C) (ou C.objets) opérateur pour obtenir les objets
- C(_,_) opérateur pour obtenir des morphismes - 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 ajouter des objets
- -= opérateur pour supprimer des objets - -= opérateur pour supprimer des objets
...@@ -38,21 +102,45 @@ class Categorie: ...@@ -38,21 +102,45 @@ class Categorie:
Categorie._id += 1 Categorie._id += 1
self._id = Categorie._id self._id = Categorie._id
self.nom = "Categorie "+str(self._id) if nom == None else nom self.nom = "Categorie "+str(self._id) if nom == None else nom
self.objets = frozenset() self._objets = frozenset() # read-only attribute
self |= objets self |= objets
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(self.objets) 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. Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie : 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)") 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. Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie, Les classes filles doivent l'implémenter tel que si C est une catégorie,
...@@ -60,12 +148,6 @@ class Categorie: ...@@ -60,12 +148,6 @@ class Categorie:
""" """
NotImplementedError("Les categories filles doivent implementer cette methode.") 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: def __str__(self) -> str:
return self.nom return self.nom
...@@ -76,20 +158,32 @@ class Categorie: ...@@ -76,20 +158,32 @@ class Categorie:
""" """
return self.objets return self.objets
@property
def objets(self) -> frozenset:
return self._objets
def __ior__(self,other:set): def __ior__(self,other:set):
""" """
Soit C une catégorie. Soit C une catégorie.
C |= {a_1,a_2,...} ajoute les objets a_i à la catégorie C. 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 return self
def __isub__(self,other:any): def __isub__(self,other:Any):
""" """
Soit C une catégorie. Soit C une catégorie.
C -= {a_1,a_2,...} supprime les objets a_i de la catégorie C. 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 return self
def verifier_coherence(self): def verifier_coherence(self):
...@@ -111,18 +205,25 @@ class Categorie: ...@@ -111,18 +205,25 @@ class Categorie:
"\n(m1@m2)@m3 = "+str((m1@m2)@m3)+" m1@(m2@m3) = "+str(m1@(m2@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)) "\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`. """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} for f in self({source},{cible}):
for g in self({cible},{source}):
def existe_morphisme(self, source:any, cible:any) -> Morphisme: if (f@g).is_identite and (g@f).is_identite:
"""Renvoie True s'il existe un morphisme de `source` à `cible`, renvoie False sinon. yield (f,g)
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
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): 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. 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: ...@@ -135,8 +236,9 @@ class Categorie:
for g in self(B,abs(self)): for g in self(B,abs(self)):
table[g][f] = g@f table[g][f] = g@f
return table 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. 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. Si destination == None, alors on le print sinon on l'écrit dans un fichier à la destination.
...@@ -161,8 +263,10 @@ class Categorie: ...@@ -161,8 +263,10 @@ class Categorie:
result = result.replace(',','\t,') result = result.replace(',','\t,')
print(result) print(result)
def transformer_graphviz(self, destination:str=None, afficher_identites:bool=False): 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""" """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 Categorie.nb_viz += 1
if destination == None: if destination == None:
destination = "graphviz/"+type(self).__name__+ str(Categorie.nb_viz) destination = "graphviz/"+type(self).__name__+ str(Categorie.nb_viz)
...@@ -173,21 +277,32 @@ class Categorie: ...@@ -173,21 +277,32 @@ class Categorie:
for o in self.objets: for o in self.objets:
graph.node(str(o)) graph.node(str(o))
toutes_les_fleches = self(self.objets,self.objets) fleches_elem = set(self[self.objets,self.objets])
for fleche in toutes_les_fleches: if complet:
if afficher_identites or not fleche.is_identite: func = lambda x,y : self({x},{y})
if fleche.source not in self.objets: else:
raise Exception("Source d'une fleche pas dans les objets de la categorie.") func = lambda x,y : self[{x},{y}]
if fleche.cible not in self.objets: for source,cible in itertools.product(self.objets,repeat=2):
raise Exception("Source d'une fleche pas dans les objets de la categorie.") nb_fleches = 0
if len({obj for obj in self.objets if obj == fleche.source}) > 1: for fleche in func(source,cible):
raise Exception("Plus d'un objet de la catégorie est source de la fleche.") if afficher_identites or not fleche.is_identite:
if len({obj for obj in self.objets if obj == fleche.cible}) > 1: if fleche.source not in self.objets:
raise Exception("Plus d'un objet de la catégorie est cible de la fleche.") raise Exception("Source d'une fleche pas dans les objets de la categorie.")
source = {obj for obj in self.objets if obj == fleche.source}.pop() #permet d'avoir toujours un objet de la catégorie comme source if fleche.cible not in self.objets:
cible = {obj for obj in self.objets if obj == fleche.cible}.pop() #permet d'avoir toujours un objet de la catégorie comme cible raise Exception("Source d'une fleche pas dans les objets de la categorie.")
graph.edge(str(source), str(cible), label=str(fleche), weight="1000") 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) graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL: if CLEAN_GRAPHVIZ_MODEL:
import os import os
...@@ -196,8 +311,9 @@ class Categorie: ...@@ -196,8 +311,9 @@ class Categorie:
def test_Categorie(): def test_Categorie():
from GrapheDeComposition import GC,MGC from GrapheDeComposition import GC,MGC
GC = GC({'A','B','C'}) GC = GC({'A','B','C'})
morphismes = [MGC('A','B','f'),MGC('B','C','g')] f,g,h = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','A','h')]
GC |= morphismes MGC.identifier_morphismes(h@h@h,h)
GC |= {f,g,h}
GC.transformer_graphviz() GC.transformer_graphviz()
GC.loi_de_composition_to_csv() GC.loi_de_composition_to_csv()
...@@ -208,16 +324,10 @@ class SousCategoriePleine(Categorie): ...@@ -208,16 +324,10 @@ class SousCategoriePleine(Categorie):
et de tous les morphismes entre ces objets. et de tous les morphismes entre ces objets.
""" """
def __init__(self, categorie_originelle:Categorie, objets:set): def __new__(cls, categorie_originelle:Categorie, objets:set) -> 'SousCategoriePleine':
"""On construit la sous-catégorie pleine de `categorie_originelle` qui contient l'ensemble des `objets`.""" copie = copy.copy(categorie_originelle)
Categorie.__init__(self,objets,"Sous-catégorie pleine de "+str(categorie_originelle)+ " ayant pour objets "+str(objets)) copie -= {obj for obj in copie.objets if obj not in objets}
self.categorie_originelle = categorie_originelle return copie
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 test_SousCategoriePleine(): def test_SousCategoriePleine():
from GrapheDeComposition import GC,MGC from GrapheDeComposition import GC,MGC
...@@ -237,7 +347,7 @@ class CategorieDiscrete(Categorie): ...@@ -237,7 +347,7 @@ class CategorieDiscrete(Categorie):
""" """
Tous les morphismes de la catégorie discrète sont des identités. 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) Morphisme.__init__(self,objet,objet,"Id"+str(objet),True)
def __matmul__(self,other:'CategorieDiscrete.Identite') -> 'CategorieDiscrete.Identite': def __matmul__(self,other:'CategorieDiscrete.Identite') -> 'CategorieDiscrete.Identite':
...@@ -256,10 +366,10 @@ class CategorieDiscrete(Categorie): ...@@ -256,10 +366,10 @@ class CategorieDiscrete(Categorie):
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(self.source) 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} 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) return CategorieDiscrete.Identite(objet)
def test_CategorieDiscrete(): def test_CategorieDiscrete():
......
from Categorie import Categorie from Categorie import Categorie, mise_en_cache_call
from Foncteur import Foncteur from Foncteur import Foncteur
from CatFinies import CatFinies from CatFinies import CatFinies
from EnsFinis import CategorieBijections,Bijection from EnsFinis import CategorieBijections,Bijection
from ChampActif import ChampActif,Cocone from ChampActif import ChampActif,Cocone
from collections import defaultdict from collections import defaultdict
import itertools import itertools
from ProduitGenerateurs import produit_cartesien_generateurs
from typing import *
class FoncteurOubli(Foncteur): 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.""" 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): def __init__(self, champ_actif:ChampActif):
univers = champ_actif.foncteur_diagonal.source univers = champ_actif.foncteur_diagonal.source
Foncteur.__init__(self, champ_actif, univers, {obj:obj.d for obj in abs(champ_actif)}, 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): 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.""" """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): ...@@ -26,11 +30,8 @@ class CategorieHomologue(CatFinies):
if fonct_oubli_source != fonct_oubli_cible@f: if fonct_oubli_source != fonct_oubli_cible@f:
raise Exception("Incoherence CategorieHomologue : "+str(f)+" ne commute pas avec les foncteurs d'oublis") raise Exception("Incoherence CategorieHomologue : "+str(f)+" ne commute pas avec les foncteurs d'oublis")
def identite(self, objet:ChampActif) -> Foncteur: @mise_en_cache_call
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) -> Generator[Foncteur,None,None]:
def __call__(self, sources:set, cibles:set) -> set:
result = set()
for source in sources: for source in sources:
for cible in cibles: for cible in cibles:
dict_nadir_cocone_source = defaultdict(frozenset) dict_nadir_cocone_source = defaultdict(frozenset)
...@@ -41,53 +42,43 @@ class CategorieHomologue(CatFinies): ...@@ -41,53 +42,43 @@ class CategorieHomologue(CatFinies):
dict_nadir_cocone_cible[Cocone(obj).nadir] |= {obj} dict_nadir_cocone_cible[Cocone(obj).nadir] |= {obj}
if set(dict_nadir_cocone_source.keys()) != set(dict_nadir_cocone_cible.keys()): if set(dict_nadir_cocone_source.keys()) != set(dict_nadir_cocone_cible.keys()):
continue continue
bijections_objets = set() # bijections entre cocônes de même nadir dict_fleches_k_source = defaultdict(lambda: defaultdict(frozenset)) #{(a,b):{k:{flecheComma1(k),flecheComma2(k),...}}}
ens_objets = CategorieBijections() for a,b in itertools.product(source.objets,repeat=2):
rate = False 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: for nadir in dict_nadir_cocone_source:
if len(dict_nadir_cocone_source[nadir]) != len(dict_nadir_cocone_cible[nadir]): cat_bij |= {dict_nadir_cocone_source[nadir]}
rate = True cat_bij |= {dict_nadir_cocone_cible[nadir]}
break bijections = cat_bij({dict_nadir_cocone_source[nadir]},{dict_nadir_cocone_cible[nadir]})
ens_objets |= {dict_nadir_cocone_source[nadir]}