Commit 96972d7a authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Monoïde et monoïde aléatoire

parent b9a6f07c
......@@ -7,10 +7,10 @@ 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, source:set, cible:set) -> set:
def __call__(self, sources:set, cibles:set) -> set:
result = set()
for a in source:
for b in cible:
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}):
......
......@@ -43,7 +43,7 @@ class Categorie:
def __hash__(self) -> int:
return hash(self.objets)
def __call__(self, source: any, cible: any) -> set:
def __call__(self, sources: any, cibles: any) -> set:
"""
Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie :
......@@ -113,6 +113,12 @@ class Categorie:
"""
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
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.
......@@ -148,6 +154,7 @@ class Categorie:
with open(destination, 'w') as f:
f.write(result)
else:
result = result.replace(',','\t,')
print(result)
def transformer_graphviz(self, destination:str=None, afficher_identites:bool=False):
......@@ -191,8 +198,8 @@ class SousCategoriePleine(Categorie):
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, source:set, cible:set) -> set:
return self.categorie_originelle(source,cible)
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)
......@@ -234,8 +241,8 @@ class CategorieDiscrete(Categorie):
def __hash__(self) -> int:
return hash(self.source)
def __call__(self,source:set,cible:set) -> set():
return {CategorieDiscrete.Identite(a) for a in source for b in cible if a == b}
def __call__(self,sources:set,cibles:set) -> set():
return {CategorieDiscrete.Identite(a) for a in sources for b in cibles if a == b}
def identite(self,objet:any) -> 'CategorieDiscrete.Identite':
return CategorieDiscrete.Identite(objet)
......
from Categorie import Categorie
from Diagramme import DiagrammeIdentite
import itertools
from Morphisme import Morphisme
from copy import copy
class CategorieComposantesConnexes(Categorie):
"""Soit C une catégorie.
La CategorieComposantesConnexes de C que l'on appellera C_cc est telle que deux objets sont isomorphes ssi les deux objets sont reliés par un zig zag dans C."""
def __init__(self,categorie):
Categorie.__init__(self,"Composantes connexes de "+str(categorie))
cat_intermediaire = copy(categorie)
for morph in categorie.morphismes:
inverse = Morphisme(morph.cible,morph.source,morph.representant+'^-1')
cat_intermediaire.ajouter_morphisme(inverse)
self.ajouter_objets(categorie.objets)
for source,cible in itertools.combinations(self.objets,2):
morph = cat_intermediaire.existe_composee(source,cible)
if morph != None:
morphisme = Morphisme(morph.source,morph.cible,morph.representant)
inverse = Morphisme(morph.cible,morph.source,morph.representant+'^-1')
self.ajouter_morphismes([morphisme,inverse])
diag = DiagrammeIdentite(self)
diag.faire_commuter()
def main():
import CategorieAleatoire
import random
random.seed(14896488564964)
cat = CategorieAleatoire.CategorieAleaIsomorph()
cat.transformer_graphviz()
cat2 = CategorieComposantesConnexes(cat)
cat2.transformer_graphviz(complet=False)
if __name__ == '__main__':
main()
\ No newline at end of file
......@@ -55,12 +55,12 @@ class CategorieLibre(Categorie):
# raise Exception("Incoherence CategorieLibre : le(s) morphisme(s) entrant(s) "+str(self.morph_entrants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1})+\
# "n'est pas un morphisme de la catégorie")
def __call__(self, source:set, cible:set) -> set:
def __call__(self, sources:set, cibles:set) -> set:
"""Soit 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}.
Pour la catégorie libre, on doit énumérer tous les chemins et les composer.
"""
return {morph for a in source for b in cible for morph in self.enumerer_composees(a,b)}
return {morph for a in sources for b in cibles for morph in self.enumerer_composees(a,b)}
def enumerer_composees_sans_cycle(self, source:any, cible:any, noeuds_deja_visites:tuple=tuple()) -> frozenset():
"""
......@@ -92,17 +92,16 @@ class CategorieLibre(Categorie):
return chemins_resultat
return frozenset()
def existe_composee(self, source:any, cible:any, noeuds_deja_visites:tuple=tuple()) -> Morphisme:
"""Trouve une composée de `source` à `cible` si elle existe, renvoie None sinon"""
def existe_morphisme(self, source:any, cible:any, objets_deja_visites:tuple=tuple()) -> bool:
"""Renvoie True s'il existe un morphisme de `source` à `cible`, renvoie False sinon"""
if source == cible:
return self.identite(source)
if source not in noeuds_deja_visites:
noeuds_deja_visites = noeuds_deja_visites + (source,)
return True
if source not in objets_deja_visites:
objets_deja_visites = objets_deja_visites + (source,)
for morph in self.morph_sortants(source):
composee = self.existe_composee(morph.cible, cible,noeuds_deja_visites)
if composee != None:
return composee@morph
return None
if self.existe_morphisme(morph.cible, cible, objets_deja_visites):
return True
return False
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)
......
from Categorie import Categorie
from Morphisme import Morphisme
class MorphismePreordre(Morphisme):
"""Un morphisme préordre est un morphisme au sein d'une catégorie préordre,
c'est-à-dire qu'entre deux objets il y a au plus un morphisme.
Deux morphismes préordre sont égaux ssi ils ont la même source et la même cible."""
def __init__(self, source:any, cible:any, nom:str = None):
Morphisme.__init__(self,source,cible,nom,source==cible)
def __eq__(self, other:'MorphismePreordre') -> bool:
return self.source == other.source and self.cible == other.cible
def __hash__(self) -> int:
return hash((self.source,self.cible))
def __matmul__(self, other:'MorphismePreordre') -> 'MorphismePreordre':
return MorphismePreordre(other.source,self.cible,str(self)+'o'+str(other))
class CategoriePreordre(Categorie):
"""Classe abstraite qui définit ce qu'est une catégorie préordre.
Les classes filles doivent implémenter la méthode existe_morphisme(self,source:any,cible:any) -> bool qui détermine
entièrement les flèches de la catégorie.
Une catégorie préordre C est une catégorie telle qu'il n'y a pas plus de deux flèches entre deux objets de C."""
def __init__(self, objets:set = set(), nom:str = "Catégorie préordre"):
Categorie.__init__(self,objets,nom)
def identite(self, objet:any) -> MorphismePreordre:
return MorphismePreordre(objet,objet)
def existe_morphisme(self, source:any, cible:any) -> bool:
"""Cette méthode est virtuelle pure, elle doit renvoyer s'il existe un morphisme entre l'objet `source` et l'objet `cible`."""
NotImplementedError("Les classes filles doivent implementer cette methode qui definit entierement les fleches de la categorie.")
def __call__(self, sources:set, cibles:set) -> set:
return {MorphismePreordre(a,b) for a in sources for b in cibles if self.existe_morphisme(a,b)}
class CategoriePreordreEngendree(CategoriePreordre):
"""La catégorie préordre engendrée par une catégorie C est une sous-catégorie maximale C' de C
telle qu'il n'y a pas plus de deux flèches entre deux objets de C'."""
def __init__(self, categorie:Categorie, nom:str = None):
self._categorie_initiale = categorie
CategoriePreordre.__init__(self,categorie.objets, "Catégorie préordre engendrée par "+str(categorie) if nom == None else nom)
def existe_morphisme(self, source:any, cible:any) -> bool:
return self._categorie_initiale.existe_morphisme(source,cible)
def test_CategoriePreordre():
from GrapheDeComposition import GC,MGC
gc = GC()
gc |= set("ABCDEF")
f,g,h,i,j,k,l = [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')]
gc |= {f,g,h,i,j,k,l}
gc.transformer_graphviz()
c_po = CategoriePreordreEngendree(gc)
c_po.transformer_graphviz()
if __name__ == '__main__':
test_CategoriePreordre()
\ No newline at end of file
from Categorie import Categorie
from CategoriePreordre import CategoriePreordreEngendree
from Morphisme import Morphisme
class CategoriePreordreZigZag(CategoriePreordreEngendree):
"""`CategoriePreordreZigZag` peut être abrégé en `CatPZigZag`
`CategoriePreordreZigZag` est la catégorie préordre des zig-zag d'une catégorie.
Soit C une catégorie et CPZZ la catégorie préordre des zig-zag de C.
Les objets de CPZZ sont les mêmes que ceux de C.
Soient deux objets a et b de CPZZ.
Il y a une flèche entre a et b dans CPZZ s'il existe un zig-zag reliant a et b dans C.
On peut aussi voir cette catégorie de la façon suivante :
Les objets de CPZZ sont les mêmes que ceux de C.
Deux objets sont isomorphes dans CPZZ s'ils sont dans la même composante connexe de C.
"""
def __init__(self, categorie:Categorie, nom:str = None):
CategoriePreordreEngendree.__init__(self,categorie,"Categorie préordre des zig-zag de "+str(categorie) if nom == None else nom)
def existe_morphisme(self, source:any, cible:any) -> bool:
"""Renvoie s'il existe un zig-zag entre source et cible."""
objets_atteints = {source}
objets_a_explorer = {source}
while len(objets_a_explorer) > 0:
explore = objets_a_explorer.pop()
objets_a_explorer |= {morph.cible for morph in self._categorie_initiale(explore,abs(self)) if morph.cible not in objets_atteints}
objets_a_explorer |= {morph.source for morph in self._categorie_initiale(abs(self),explore) if morph.source not in objets_atteints}
objets_atteints |= objets_a_explorer
return cible in objets_atteints
CatPZigZag = CategoriePreordreZigZag
def test_CategoriePreordreZigZag():
from GrapheDeComposition import GC,MGC
from Diagramme import Triangle
gc = GC()
gc |= set("ABCDEFGHIJKL")
f,g,h,i,j,k,l,m,n,o,p = [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('H','G','n'),MGC('I','J','o'),MGC('K','L','p')]
gc |= {f,g,h,i,j,k,l,m,n,o,p}
triangle = Triangle(gc, "GGG", [n@m@n@m,n@m,n@m@n@m])
triangle.faire_commuter()
triangle.transformer_graphviz()
gc.transformer_graphviz()
czz = CatPZigZag(gc)
czz.transformer_graphviz()
if __name__ == '__main__':
test_CategoriePreordreZigZag()
\ No newline at end of file
from Categorie import Categorie
from Morphisme import Morphisme
import itertools
class MorphismeProduit(Morphisme,tuple):
"""Un morphisme d'une catégorie produit est le produit des morphismes des catégories."""
def __new__(cls, *morphismes:Morphisme, nom:str = None):
return super().__new__(cls,morphismes)
def __init__(self, *morphismes:Morphisme, nom:str = None):
Morphisme.__init__(self,tuple(m.source for m in self), tuple(m.cible for m in self),
str(tuple(m.nom for m in self)) if nom == None else nom, all((m.is_identite for m in self)))
def __matmul__(self, other:'MorphismeProduit') -> 'MorphismeProduit':
if len(other) != len(self):
raise Exception("Composition de MorphismeProduit de taille différentes.")
return MorphismeProduit(*(self[i]@other[i] for i in range(len(self))))
__eq__ = tuple.__eq__
__hash__ = tuple.__hash__
__getitem__ = tuple.__getitem__
__iter__ = tuple.__iter__
__len__ = tuple.__len__
class CategorieProduit(Categorie,tuple):
"""Catégorie produit de plusieurs catégories.
Soient C1,C2,...,Cn n catégories.
Un objet de C1xC2x...xCn est un n-uplet (o_1,o_2,...,o_n) avec o_i \in |Ci|.
Un morphisme de C1xC2x...xCn est un n-uplet (m_1,m_2,...,m_n) avec m_i \in Fl(Ci).
On peut itérer sur C1xC2x...xCn pour obtenir les catégories les unes après les autres."""
def __new__(cls, *categories:Categorie):
return super().__new__(cls,categories)
def __init__(self, *categories:Categorie):
Categorie.__init__(self,set(itertools.product(*list(map(abs,categories)))), "x".join(map(str,categories)))
def identite(self, objet:tuple) -> MorphismeProduit:
return MorphismeProduit(*(self[i].identite(objet[i]) for i in range(len(objet))))
def __call__(self, sources:set, cibles:set) -> set:
result = set()
for source in sources:
for cible in cibles:
print(source,cible)
if len(source) != len(cible):
raise Exception("Source et cible de taille différente "+str(source)+" "+str(cible))
for nuplet in itertools.product(*tuple(self[i]({source[i]},{cible[i]}) for i in range(len(source)))):
result |= {MorphismeProduit(*nuplet)}
return result
__eq__ = tuple.__eq__
__hash__ = tuple.__hash__
__getitem__ = tuple.__getitem__
__iter__ = tuple.__iter__
__len__ = tuple.__len__
def test_CategorieProduit():
from GrapheDeComposition import GC,MGC
from Diagramme import Triangle,DiagrammeIdentite
gc = GC()
gc |= set("ABC")
f,g= [MGC('A','B','f'),MGC('B','C','g')]
gc |= {f,g}
gc_2 = CategorieProduit(gc,gc)
gc_2.transformer_graphviz(afficher_identites=True)
if __name__ == '__main__':
test_CategorieProduit()
\ No newline at end of file
......@@ -21,7 +21,7 @@ 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 et à chaque flèche F:a->b la transformation naturelle qui va de Δa à Δb
## on associe à chaque objet son diagramme constant bite et à chaque flèche F:a->b la transformation naturelle qui va de Δa à Δb
self.foncteur_vers_D = DiagrammeObjets(self.categorie_diagrammes,{diagramme})
......
from Interaction import Interaction,CategorieInteractions
from Foncteur import Foncteur
from CommaCategorie import ObjetCommaCategorie,CategorieFSous
from CategoriePreordreZigZag import CatPZigZag
from Interaction import CategorieInteractions
from config import *
class ClusterAbstrait(Morphisme):
nb_viz = 0
def __init__(self,foncteur1, foncteur2, tiges):
self.foncteur1 = foncteur1
self.foncteur2 = foncteur2
self.composantes = list(tiges) #liste de morphismes de la catégorie cible des foncteurs
self.categorie = self.foncteur1.cat_cible
identite = False
if self.foncteur1 == self.foncteur2:
identite = True
Morphisme.__init__(self,self.foncteur1,self.foncteur2,str(self),identite)
def __eq__(self,other):
if issubclass(type(other),ClusterAbstrait):
if len([e for e in self.composantes if e not in other.composantes]) > 0:
return False
if len([e for e in other.composantes if e not in self.composantes]) > 0:
return False
return self.foncteur1 == other.foncteur1 and self.foncteur2 == other.foncteur2
return False
def __le__(self,other):
if issubclass(type(other),ClusterAbstrait):
if len([e for e in self.composantes if e not in other.composantes]) > 0:
return False
return True
raise Exception("On essaie de comparer un Cluster avec un "+type(other))
def __ge__(self,other):
if issubclass(type(other),ClusterAbstrait):
if len([e for e in other.composantes if e not in self.composantes]) > 0:
return False
return True
raise Exception("On essaie de comparer un Cluster avec un "+type(other))
def __hash__(self):
return hash((tuple(self.composantes),self.foncteur1,self.foncteur2))
def __str__(self):
return '('+str(self.foncteur1)+','+'/'.join(map(str,self.composantes))+','+str(self.foncteur2)+')'
def __repr__(self):
return str(self)
class ProtoClusterActif(Interaction):
"""
Un proto-cluster actif ou simplement protocluster est une interaction G entre deux diagrammes D1:I->C et D2:J->C tel que :
1) Pour tout objet i de I, il existe un objet j de J et une flèche f dans C tel que <i,f,j> \in G
2) soit G(i) l'ensemble des <i,f,_>, alors G(i) est inclus dans une composante connexe de la comma-catégorie (D1(i)|D2)
3) Pour toute flèche h:j->j' dans J et <i,g,j> \in G(i), on a <i,h@g,j'> \in G(i)
4) Pour toute flèche h:i'->i dans I et <i,g,j> \in G(i), on a <i',g@h,j> \in G(i')
"""
def verifier_coherence(self):
raise NotImplementedError()
def transformer_graphviz(self, destination=None, afficher_identites=True):
"""Permet de visualiser le cluster avec graphviz
Composante rouge : diagramme 1
Composante verte : diagramme 2
Composante bleue: cluster"""
ClusterAbstrait.nb_viz += 1
image1 = self.foncteur1.categorie_image()
image2 = self.foncteur2.categorie_image()
if destination == None:
destination = "graphviz/cluster"+str(ClusterAbstrait.nb_viz)
graph = Digraph('cluster')
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
graph.attr(label="Cluster entre "+str(self.foncteur1)+" et "+str(self.foncteur2))
for o in self.categorie.objets:
couleur = [0,0,0]
if o in image1.objets:
couleur[0] += 255
if o in image2.objets:
couleur[1] += 255
graph.node(str(o),color="#"+''.join(map(lambda x:(2-len(str(hex(x))[2:]))*"0"+str(hex(x))[2:],couleur)))#"grey60")
morphismes = self.categorie.morphismes+image1.morphismes+image2.morphismes+self.composantes
if afficher_identites:
morphismes += self.categorie.identites.values()
for morph in [morphismes[i] for i in range(len(morphismes)) if i==len(morphismes) or morphismes[i] not in morphismes[i+1:]]:
if not morph.is_identite or afficher_identites:
couleur = [0,0,0]
if morph in image1.morphismes:
couleur[0] += 255
if morph in image2.morphismes:
couleur[1] += 255
if morph in self.composantes:
couleur = [0,0,255]
graph.edge(str(morph.source),str(morph.cible),label=str(morph.representant), color="#"+''.join(map(lambda x:(2-len(str(hex(x))[2:]))*"0"+str(hex(x))[2:],couleur)))
else:
graph.edge(str(morph.source),str(morph.cible),label=str(morph.representant), weight="1000", color="#"+''.join(map(lambda x:(2-len(str(hex(x))[2:]))*"0"+str(hex(x))[2:],couleur)))
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
import os
os.remove(destination)
class ProtoClusterActif(ClusterAbstrait):
"""Seules les contraintes sur les cocônes sont prises en compte pour le cluster actif"""
def __init__(self, foncteur1, foncteur2, composantes):
"""composantes liste de morphisme incluse dans Interact(foncteur1,foncteur2)"""
ClusterAbstrait.__init__(self,foncteur1, foncteur2, composantes)
def __init__(self, D1:Foncteur, D2:Foncteur, composantes:set):
"""composantes est un ensemble d'objets de la comma-catégorie (D1|D2)"""
Interaction.__init__(self,D1, D2, composantes)
if TOUJOURS_VERIFIER_COHERENCE:
ProtoClusterActif.verifier_coherence(self)
def verifier_coherence(self):
if self.foncteur1.cat_cible != self.foncteur2.cat_cible:
raise Exception("Incoherence ProtoClusterActif : les deux foncteurs n'ont pas la meme image.")
impl1, impl2 = [self.foncteur1.categorie_image(),self.foncteur2.categorie_image()]
for composante in self.composantes:
if composante.source not in impl1.objets:
raise Exception("Incoherence ProtoClusterActif : la composante "+str(composante)+" ne prend pas sa source dans l'image de F1")
if composante.cible not in impl2.objets:
raise Exception("Incoherence ProtoClusterActif : la composante "+str(composante)+" ne prend pas sa cible dans l'image de F2")
D1,D2 = self.source,self.cible
Interaction.verifier_coherence(self)
# contrainte 1) au moins une flèche part de chacun des objets de D1
for d in self.foncteur1.cat_source.objets:
for composante in self.composantes:
if composante.source == self.foncteur1(d):
for e in D1.source.objets:
for composante in self:
if composante.e == e:
break
else:
raise Exception("Incoherence ProtoClusterActif : l'objet "+str(self.foncteur1(d))+" de D1 n'a pas de d'image par le cluster")
raise Exception("Incoherence ProtoClusterActif : l'objet "+str(e)+" de l'index de D1 n'a pas de de composante associée.")
# contrainte 2) les composantes qui sortent d'un objet d de D1 mènent à des objets e de D2 reliés par un zig zag dans la comma catégorie (D(d)|E)
for d in self.foncteur1.cat_source.objets:
E_e = [] #les images des composantes
for composante in self.composantes:
if composante.source == self.foncteur1(d):
E_e += [composante]
cat_sous = CategorieFSous(self.foncteur2, self.foncteur1(d))
cat_cc = CategorieComposantesConnexes(cat_sous)
# contrainte 2) les composantes qui sortent d'un objet D1(i) mènent à des objets D2(j_i) reliés par un zig zag dans la comma catégorie (D1(i)|D2)
for i in D1.source.objets:
G_i = {compo for compo in self if compo.e == i}
comma_categorie = CategorieFSous(D2,D1(i))
cat_cc = CatPZigZag(comma_categorie)
# on vérifie que tous les E(e) sont isomorphes dans la catégorie des composantes connexes de la catégorie sous D(d)
for e in E_e[1:]:
for obj1 in cat_cc.objets: #todo corriger tout ça !!!
if obj1.f == e:
for obj2 in cat_cc.objets:
if obj2.f == E_e[0]:
if cat_cc.existe_composee(obj1,obj2) != None:
break
else:
continue
break
else:
raise Exception("Incoherence ProtoClusterActif : l'objet "+str(E_e[0])+" n'est pas dans la même composante connexe que "+str(e))
composante_reference = G_i.pop()
for composante in G_i:
if not cat_cc.existe_morphisme(composante,composante_reference):
raise Exception("Incoherence ProtoClusterActif : la composante"+str(composante)+" n'est pas dans la même composante connexe que "+str(composante_reference))
# contrainte 3) eog = g'
for composante in self.composantes:
for e in self.foncteur2.cat_source.objets:
if self.foncteur2(e) == composante.cible:
for eps in self.foncteur2.cat_source.morph_sortants[e]:
composee = self.categorie.Composee(composante,self.foncteur2(eps))
if composee not in self.composantes:
raise Exception("Incoherence ProtoClusterActif : la composee de la composante "+str(composante)+" avec la fleche "+str(self.foncteur2(eps))+" de D2 n'est pas dans le cluster")
# contrainte 3) hog = g'
for composante in self:
for h in D2.source(abs(D2.source),abs(D2.source)):
if ObjetCommaCategorie(composante.e,D2(h)@composante.f,h.cible) not in self:
raise Exception("Incoherence ProtoClusterActif : la composee de la composante "+str(composante)+" avec la fleche "+str(D2(h))+" de D2 n'est pas dans le cluster.")
# contrainte 4) goh = g'
for composante in self:
for h in D1.source(abs(D1.source),abs(D1.source)):
if ObjetCommaCategorie(h.source,composante.f@D1(h),composante.d) not in self:
raise Exception("Incoherence ProtoClusterActif : la composee de la fleche "+str(D1(h))+" de D1 avec la composante "+str(composante)+" n'est pas dans le cluster")
class Cluster()
class CategorieClusters(CategorieInteractions):
"""
CategorieClusters est une catégorie d'organisations où les intéractions sont des clusters.
"""
def __init__(self, univers:Categorie ,objets:set = set(), nom:str = None):
CategorieInteractions.__init__(self,univers,objets,"Catégorie des diagrammes et clusters sur "+str(univers) if nom == None else nom)
def __identite__(self,objet:Diagramme) -> ProtoClusterActif:
# contrainte 4) god = g'
for composante in self.composantes:
for d in self.foncteur1.cat_source.objets:
if self.foncteur1(d) == composante.source:
for delta in self.foncteur1.cat_source.morph_entrants[d]:
composee = self.categorie.Composee(self.foncteur1(delta),composante)
if composee not in self.composantes:
raise Exception("Incoherence ProtoClusterActif : la composee de la fleche "+str(self.foncteur1(delta))+" de D1 avec la composante "+str(composante)+" n'est pas dans le cluster")
class ProtoClustersActifs:
def __init__(self, foncteur1, foncteur2):
......@@ -263,34 +173,7 @@ class ProtoClustersActifs:
def __len__(self):
return len(self.clusters_actifs)
class ClusterActif(ProtoClusterActif):
def __new__(cls,foncteur1,foncteur2):
proto_clusters = ProtoClustersActifs(foncteur1,foncteur2)