Commit 529db12a authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Debut algo modification

parent e8789cc8
...@@ -12,9 +12,9 @@ from typing import * ...@@ -12,9 +12,9 @@ from typing import *
def unique_generator(func): def unique_generator(func):
"""Décorateur qui permet de renvoyer les flèches une seule fois par générateur.""" """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() elem_generes = set()
for elem in func(self,*args): for elem in func(self,*args,**kwargs):
if elem not in elem_generes: if elem not in elem_generes:
elem_generes |= {elem} elem_generes |= {elem}
yield elem yield elem
...@@ -218,6 +218,10 @@ class Categorie: ...@@ -218,6 +218,10 @@ class Categorie:
"""Renvoie un morphisme de `source` à `cible` s'il existe, renvoie None sinon.""" """Renvoie un morphisme de `source` à `cible` s'il existe, renvoie None sinon."""
return next(self({source},{cible}),None) 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]: 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 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.""" renvoie None sinon."""
...@@ -263,7 +267,7 @@ class Categorie: ...@@ -263,7 +267,7 @@ class Categorie:
result = result.replace(',','\t,') result = result.replace(',','\t,')
print(result) 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. """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. `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.""" `limite_fleches` est le nombre maximal de flèches entre deux objets qu'on s'autorise à afficher."""
......
...@@ -78,7 +78,6 @@ class CategorieHomologue(CatFinies): ...@@ -78,7 +78,6 @@ class CategorieHomologue(CatFinies):
bijections_fleches |= {bijections} bijections_fleches |= {bijections}
for bijection_fleche in produit_cartesien_generateurs(*bijections_fleches): 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()]) 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) yield Foncteur(source,cible,bij_obj,bij_fleche)
......
from Categorie import Categorie, unique_generator from Categorie import Categorie, unique_generator
from Morphisme import Morphisme from Morphisme import Morphisme
from ProduitGenerateurs import produit_cartesien_generateurs
from copy import copy from copy import copy
import itertools import itertools
import functools import functools
...@@ -57,7 +58,7 @@ class CategorieLibre(Categorie): ...@@ -57,7 +58,7 @@ class CategorieLibre(Categorie):
return composees_resultat return composees_resultat
return frozenset() return frozenset()
for chemin in enumerer_chemin_elem_sans_cycle(morphisme.source,morphisme.cible): 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: if resultat == morphisme:
for morph_elem in chemin: for morph_elem in chemin:
yield morph_elem yield morph_elem
...@@ -77,89 +78,157 @@ class CategorieLibre(Categorie): ...@@ -77,89 +78,157 @@ class CategorieLibre(Categorie):
if morph not in self[self.objets,{morph.cible}]: if morph not in self[self.objets,{morph.cible}]:
raise Exception("Incoherence CategorieLibre : le morphisme "+str(morph)+" n'entre pas dans "+str(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: for chemin in self.enumerer_chemins_sans_cycle(source,cible):
return frozenset({self.identite(source)}) if len(chemin) == 1:
if source not in noeuds_deja_visites: for e in self[{chemin[0]},{chemin[0]}]:
noeuds_deja_visites = noeuds_deja_visites + (source,) yield e
composees_resultat = frozenset() else:
for morph in self[{source},self.objets]: generateurs = [self[{chemin[i]},{chemin[i+1]}] for i in range(len(chemin)-1)]
for composition_candidate in self.enumerer_composees_sans_cycle(morph.cible, cible, noeuds_deja_visites): for prod in produit_cartesien_generateurs(*generateurs):
composees_resultat |= {composition_candidate@morph} yield functools.reduce(lambda x,y : y@x, prod)
return composees_resultat
return frozenset()
def enumerer_chemins_sans_cycle(self, source:Any, cible:Any) -> Generator[tuple,None,None]:
def enumerer_chemins_sans_cycle(self, source:Any, cible:Any, noeuds_deja_visites:tuple=tuple()) -> frozenset:
""" """
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: # on fait un parcours en largeur
return frozenset({(cible,)}) file_chemins = [(source,)]
if source not in noeuds_deja_visites: while len(file_chemins) > 0:
noeuds_deja_visites = noeuds_deja_visites + (source,) chemin = file_chemins.pop(0)
chemins_resultat = frozenset() if chemin[-1] == cible:
for morph in self[{source},self.objets]: yield chemin
for chemin_candidat in self.enumerer_chemins_sans_cycle(morph.cible, cible, noeuds_deja_visites): else:
chemins_resultat |= {(source,)+chemin_candidat} for obj in self.objets:
return chemins_resultat if obj not in chemin:
return frozenset() if self.existe_morphisme_elementaire(chemin[-1],obj):
file_chemins += [chemin+(obj,)]
def trouver_cycles_minimaux(self, objet:Any, inclure_id:bool=True) -> frozenset(): @unique_generator
"""Renvoie tous les cycles minimaux de morphismes élémentaires (qui ne contiennent aucun cycle) def trouver_cycles_minimaux(self, objet:Any) -> Generator[Morphisme,None,None]:
de `objet` à `objet` (à l'exception de l'identité si `inclure_id` = False).""" """Génère tous les cycles minimaux de morphismes élémentaires (qui ne contiennent aucun cycle)
cycles = frozenset() de `objet` à `objet` différent de l'identité."""
for morph_pred in self[self.objets,{objet}]: for morph_pred in self[self.objets,{objet}]:
pred = morph_pred.source if morph_pred.source == objet:
cycles_tronques = self.enumerer_composees_sans_cycle(objet, pred) yield morph_pred
for cycle_tronque in cycles_tronques: else:
cycle = morph_pred@cycle_tronque pred = morph_pred.source
if cycle not in cycles: for cycle_tronque in self.enumerer_composees_sans_cycle(objet, pred):
cycles |= {cycle} cycle = morph_pred@cycle_tronque
if inclure_id: if not cycle.is_identite:
return cycles|{self.identite(objet)} yield cycle
return cycles-{self.identite(objet)}
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`. """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}. 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. 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. 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. 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_a_reduire = copy(cycles)
cycles_minimaux = copy(cycles) cycles_minimaux = copy(cycles)
cycles |= {self.identite(objet)} cycles |= {self.identite(objet)}
for cycle in cycles:
yield cycle
for profondeur in range(limite_profondeur): for profondeur in range(limite_profondeur):
for cycle_a_reduire in copy(cycles_a_reduire): for cycle_a_reduire in copy(cycles_a_reduire):
for cycle_minimal in cycles_minimaux: for cycle_minimal in cycles_minimaux:
if cycle_minimal@cycle_a_reduire not in cycles: if cycle_minimal@cycle_a_reduire not in cycles:
cycles_a_reduire |= {cycle_minimal@cycle_a_reduire} new_cycle = cycle_minimal@cycle_a_reduire
cycles |= {cycle_minimal@cycle_a_reduire} cycles_a_reduire |= {new_cycle}
cycles |= {new_cycle}
yield new_cycle
cycles_a_reduire -= {cycle_a_reduire} cycles_a_reduire -= {cycle_a_reduire}
if len(cycles_a_reduire) > 0: if len(cycles_a_reduire) > 0:
print("Warning CategorieLibre : limite de profondeur atteinte dans l'enumeration des cycles") print("Warning CategorieLibre : limite de profondeur atteinte dans l'enumeration des cycles")
if DEBUG_LOI_DE_COMPOSITION: if DEBUG_LOI_DE_COMPOSITION:
print(cycles_a_reduire) print(cycles_a_reduire)
return cycles
def enumerer_composees(self, source:Any, cible:Any) -> Generator[Morphisme,None,None]: 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. 1) On trouve les chemins sans cycle.
2) Pour tous les noeuds U du chemin on énumère les cycles de U à U. 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.""" 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) chemins_sans_cycles = self.enumerer_chemins_sans_cycle(source, cible)
noeuds = list(frozenset([e for chemin in chemins_sans_cycles for e in chemin])) dict_noeuds_cycles = dict() # {noeud : set(cycles)}
cycles = {x: self.enumerer_cycles(x) for x in noeuds}
for chemin in chemins_sans_cycles: for chemin in chemins_sans_cycles:
cycles_concernes = [cycles[noeud] for noeud in chemin] cycles_concernes = []
fleches_concernees = [set(self[{chemin[i]},{chemin[i+1]}]) for i in range(len(chemin)-1)] for noeud in chemin:
for combinaison_fleches in itertools.product(*fleches_concernees): if noeud not in dict_noeuds_cycles:
for combinaison_cycle in itertools.product(*cycles_concernes): dict_noeuds_cycles[noeud] = set(self.enumerer_cycles(noeud))
composee = combinaison_cycle[0] cycles_concernes += [dict_noeuds_cycles[noeud]]
for i in range(len(combinaison_fleches)): if len(chemin) >= 2:
composee = combinaison_cycle[i+1]@combinaison_fleches[i]@composee fleches_concernees = [self[{chemin[i]},{chemin[i+1]}] for i in range(len(chemin)-1)]
yield composee 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
...@@ -3,6 +3,7 @@ from TransformationNaturelle import CatTransfoNat,TransfoNat ...@@ -3,6 +3,7 @@ from TransformationNaturelle import CatTransfoNat,TransfoNat
from Foncteur import Foncteur from Foncteur import Foncteur
from Diagramme import DiagrammeConstant,DiagrammeObjets from Diagramme import DiagrammeConstant,DiagrammeObjets
from typing import * from typing import *
from Cluster import ClusterActif,ProtoClusterActif
class Cocone(TransfoNat): class Cocone(TransfoNat):
"""Un cocône sur D de nadir N est une transformation naturelle de D vers le foncteur constant Δ_N.""" """Un cocône sur D de nadir N est une transformation naturelle de D vers le foncteur constant Δ_N."""
...@@ -17,6 +18,9 @@ class Cocone(TransfoNat): ...@@ -17,6 +18,9 @@ class Cocone(TransfoNat):
def as_ObjetCommaCategorie(self) -> ObjetCommaCategorie: def as_ObjetCommaCategorie(self) -> ObjetCommaCategorie:
return ObjetCommaCategorie(0,self,self.nadir) 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): class ChampActif(CommaCategorie):
"""Le champ actif d'un diagramme(ou foncteur) D:P->Q est défini comme étant la comma-catégorie (D|Δ) """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 Morphisme import Morphisme
from GrapheDeComposition import GC,MGC from GrapheDeComposition import GC,MGC
from Diagramme import Diagramme, DiagrammeIdentite from Diagramme import Diagramme, DiagrammeIdentite
from ChampActif import ChampActif from ChampActif import ChampActif
from copy import copy from copy import copy
from typing import *
cat = GC(set("ABC")) cat = GC(set("ABC"))
f,g = [MGC('A','B','f'),MGC('A','C','g')] f,g = [MGC('A','B','f'),MGC('A','C','g')]
cat |= {f,g} cat |= {f,g}
diag = DiagrammeIdentite(cat) # diag = DiagrammeIdentite(cat)
c_a = ChampActif(diag) # c_a = ChampActif(diag)
diag.transformer_graphviz() # diag.transformer_graphviz()
c_a.transformer_graphviz() # c_a.transformer_graphviz()
cat2 = copy(cat) # cat2 = copy(cat)
cat2 |= {'L'} # cat2 |= {'L'}
h,i,j = [MGC('A','L','h'),MGC('B','L','i'),MGC('C','L','j')] # h,i,j = [MGC('A','L','h'),MGC('B','L','i'),MGC('C','L','j')]
cat2 |= {h,i,j} # cat2 |= {h,i,j}
diag2 = Diagramme(cat,cat2,{'A':'A','B':'B','C':'C'},{f:f,g:g}) # diag2 = Diagramme(cat,cat2,{'A':'A','B':'B','C':'C'},{f:f,g:g})
MGC.identifier_morphismes(i@f,h) # MGC.identifier_morphismes(i@f,h)
MGC.identifier_morphismes(j@g,h) # MGC.identifier_morphismes(j@g,h)
diag2.transformer_graphviz() # diag2.transformer_graphviz()
c_a2 = ChampActif(diag2) # c_a2 = ChampActif(diag2)
c_a2.transformer_graphviz() # c_a2.transformer_graphviz()
def modification(gc: GC, option_ajout:Tuple[Sequence[Any],Sequence[Morphisme]], option_elimination:Sequence[Diagramme], option_complex_agissante:Sequence[Diagramme]) -> GC:
nouveau_gc = copy(gc)
nouveaux_obj, nouvelles_fleches = option_ajout
nouveau_gc |= set(nouveaux_obj)
nouveau_gc |= set(nouvelles_fleches)
nouveau_gc -= set(option_elimination)
for
return nouveau_gc
cat.transformer_graphviz()
cat2 = modification(cat,({'S','T'},{MGC('S','T','h')}),{'C'},{})
cat2.transformer_graphviz()
...@@ -33,6 +33,21 @@ cat_hom.transformer_graphviz() ...@@ -33,6 +33,21 @@ cat_hom.transformer_graphviz()
from Cluster import CategorieClusters 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}) cat_clust = CategorieClusters(cat,{d1,d2})
SousCategoriePleine(cat_clust,{d1,d2}).transformer_graphviz()
print(cat_clust.existe_isomorphisme(d1,d2)) for cocone_d2 in c_p2.objets:
\ No newline at end of file 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
...@@ -57,4 +57,6 @@ cat[a,b] appelle cat(a,b) par défaut sauf pour le graphe de composition ...@@ -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 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; 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 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
Supports Markdown
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