Cluster.py 11.4 KB
Newer Older
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
1
from Categorie import Categorie
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
2
from Interaction import Interaction,CategorieInteractions
3
4
5
6
from Foncteur import Foncteur
from CommaCategorie import ObjetCommaCategorie,CategorieFSous
from CategoriePreordreZigZag import CatPZigZag
from Interaction import CategorieInteractions
7
from config import *
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
8
from copy import copy
9

10
11
12
13
14
15
16
17
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')
    """
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
18
19
20
21
    
    def verifier_struct(interaction:Interaction) -> tuple:
        '''Renvoie (True,None) si la structure de cluster est vérifiée par l'`interaction`, (False,Exception) sinon.'''
        D1,D2 = interaction.source,interaction.cible
22
        
23
        # contrainte 1) au moins une flèche part de chacun des objets de D1
24
        for e in D1.source.objets:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
25
            for composante in interaction:
26
                if composante.e == e:
27
28
                    break
            else:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
29
                return False,Exception("Incoherence ProtoClusterActif : l'objet "+str(e)+" de l'index de D1 n'a pas de de composante associée.")
30
        
31
32
        # 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:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
33
            G_i = {compo for compo in interaction if compo.e == i}
34
35
            comma_categorie = CategorieFSous(D2,D1(i))
            cat_cc = CatPZigZag(comma_categorie)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
36
            # on vérifie que tous les E(e) sont isomorphes dans la catégorie des composantes connexes de la catégorie sous D(d)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
37
38
            temp = G_i.pop()
            composante_reference = ObjetCommaCategorie(0,temp.f,temp.d) #on met un 0 dans e puisque c'est un objet à gauche de la comma catégorie (D1(i)|D2)
39
            for composante in G_i:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
40
                composante = ObjetCommaCategorie(0,composante.f,composante.d) #on met un 0 dans e puisque c'est un objet à gauche de la comma catégorie (D1(i)|D2)
41
                if not cat_cc.existe_morphisme(composante,composante_reference):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
42
                    return False,Exception("Incoherence ProtoClusterActif : la composante"+str(composante)+" n'est pas dans la même composante connexe que "+str(composante_reference))
43
                    
44
        # contrainte 3) hog = g'
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
45
46
47
48
        for composante in interaction:
            for h in D2.source({composante.d},abs(D2.source)):
                if ObjetCommaCategorie(composante.e,D2(h)@composante.f,h.cible) not in interaction:
                    return False,Exception("Incoherence ProtoClusterActif : la composee de la composante "+str(composante)+" avec la fleche "+str(D2(h))+" de D2 n'est pas dans le cluster.")
49
50
        
        # contrainte 4) goh = g'
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
51
52
53
54
55
        for composante in interaction:
            for h in D1.source(abs(D1.source),{composante.e}):
                if ObjetCommaCategorie(h.source,composante.f@D1(h),composante.d) not in interaction:
                    return False,Exception("Incoherence ProtoClusterActif : la composee de la fleche "+str(D1(h))+" de D1 avec la composante "+str(composante)+" n'est pas dans le cluster")
        return True,None
56

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
57
    def __init__(self, D1:Foncteur, D2:Foncteur, composantes:set, nom:str = None):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
58
        """composantes est un ensemble d'objets de la comma-catégorie (D1|D2)"""
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
59
60
        Interaction.__init__(self,D1, D2, composantes,nom)
        self.__categorie_cluster = None
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
61
62
63
64
65
66
67
68
        if TOUJOURS_VERIFIER_COHERENCE:
            ProtoClusterActif.verifier_coherence(self)

    def verifier_coherence(self):
        Interaction.verifier_coherence(self)
        coherence,exception_levee = ProtoClusterActif.verifier_struct(self)
        if not coherence:
            raise exception_levee
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
            
    def __matmul__(self,other:'ProtoClusterActif') -> 'ProtoClusterActif':
        """La composition de deux clusters G1 et G2 est le cluster engendré par la composition de G1 et G2 vus comme des interactions."""
        import config
        
        if self.__categorie_cluster == None:
            backup = config.TOUJOURS_VERIFIER_COHERENCE
            config.TOUJOURS_VERIFIER_COHERENCE = 0 # on empêche la vérification de la cohérence de CategorieClusters qui ferait une boucle infinie
            self.__categorie_cluster = CategorieClusters(self.source.cible,{self.cible},"Catégorie cluster de "+str(self))
            config.TOUJOURS_VERIFIER_COHERENCE = backup
        composition_interaction = Interaction.__matmul__(self,other) # on calcule la composition des interactions
        #on doit trouver le cluster engendré par la composition_interaction
        self.__categorie_cluster |= {other.source}
        clusters_candidats = self.__categorie_cluster({other.source},{self.cible})
        clusters_finaux = set()
        for cluster in clusters_candidats:
            for obj_comma in composition_interaction:
                if obj_comma not in cluster:
                    break
            else:
                clusters_finaux |= {cluster}
        if len(clusters_finaux) > 1:
            raise Exception("Plus d'une composition possible "+str(self)+"o"+str(other)+"\n"+str(clusters_finaux))
        if len(clusters_finaux) == 0:
            raise Exception("Aucune composition trouvee pour "+str(self)+"o"+str(other)+"\n"+str(clusters_candidats))
        return ProtoClusterActif(other.source,self.cible,{composante for composante in clusters_finaux.pop()},str(self)+'o'+str(other))
    
    def __lt__(self, other:'ProtoClusterActif') -> bool:
        '''G1 < G2 si G1 est inclus strictement dans G2'''
        for composante in self:
            if composante not in other:
                return False
        return self != other
        
    def __gt__(self, other:'ProtoClusterActif') -> bool:
        '''G1 > G2 si G2 est inclus strictement dans G1'''
        for composante in other:
            if composante not in self:
                return False
        return self != other
        
    def __lte__(self, other:'ProtoClusterActif') -> bool:
        '''G1 <= G2 si G1 est inclus dans G2'''
        for composante in self:
            if composante not in other:
                return False
        return True
        
    def __gte__(self, other:'ProtoClusterActif') -> bool:
        '''G1 >= G2 si G2 est inclus dans G1'''
        for composante in other:
            if composante not in self:
                return False
        return True
123
124
125
126
127
128

class CategorieClusters(CategorieInteractions):
    """
    CategorieClusters est une catégorie d'organisations où les intéractions sont des clusters. 
    """
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
129
130
131
132
133
134
135
136
    def __init__(self, univers:Categorie ,diagrammes:set = set(), nom:str = None):
        """
        CategorieClusters est la catégorie dont les objets sont des diagrammes de cible `univers` et les morphismes sont des clusters.
        Par défaut, on considère pour chaque objet O de l'`univers` le diagramme 1_O qui associe à un objet l'objet O.
        On retrouve ainsi l'`univers` dans la catégorie des clusters.
        `diagrammes` est un ensemble de diagrammes d'intérêts à ajouter à la catégorie.
        """
        CategorieInteractions.__init__(self,univers,diagrammes,"Catégorie des diagrammes et clusters sur "+str(univers) if nom == None else nom)
137

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
138
    def identite(self,objet:Foncteur) -> ProtoClusterActif:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
        clusters_candidats = self({objet},{objet})
        clusters_finaux = set()
        for cluster in clusters_candidats:
            for obj_indexant in objet.source.objets:
                if ObjetCommaCategorie(obj_indexant,objet.cible.identite(objet(obj_indexant)),obj_indexant) not in cluster:
                    break
            else:
                clusters_finaux |= {cluster} # on a tous les clusters qui contiennent toutes les identités dans clusters_finaux
        if len(clusters_finaux) > 1:
            raise Exception("Plus d'une identite possible pour l'objet "+str(objet)+"\n"+str(clusters_finaux))
        if len(clusters_finaux) == 0:
            raise Exception("Aucune identite trouvee pour l'objet "+str(objet)+"\n"+str(clusters_candidats))
        return clusters_finaux.pop()
        
        
154
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
155
156
157
158
    def __call__(self, sources:set, cibles:set) -> set:
        result = set()
        for source in sources:
            for cible in cibles:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
                interactions = CategorieInteractions.__call__(self,{source},{cible})
                proto_cluster_maximaux = set()
                while len(interactions) > 0:
                    interact = interactions.pop()
                    if not ProtoClusterActif.verifier_struct(interact)[0]:
                        continue
                    else:
                        proto_cluster_candidat = ProtoClusterActif(source,cible,{composante for composante in interact})
                        for proto_cluster_maximal in proto_cluster_maximaux:
                            assert(proto_cluster_maximal != proto_cluster_candidat)
                            if proto_cluster_maximal < proto_cluster_candidat:
                                proto_cluster_maximaux -= {proto_cluster_maximal}
                                proto_cluster_maximaux |= {proto_cluster_candidat}
                                break
                            if proto_cluster_maximal > proto_cluster_candidat:
                                break
                        else:
                            # ici on n'a jamais break, ce qui signifie que le proto_cluster_candidat était incomparable avec tous les proto_cluster_maximaux
                            proto_cluster_maximaux |= {proto_cluster_candidat}
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
178
179
180
                result |= set(proto_cluster_maximaux)
        return result
   
181

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
182

183
184
185
    
            
def main():
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
186
187
188
189
190
    from GrapheDeComposition import GC,MGC
    from Diagramme import DiagrammeObjets,Fleche
    
    gc = GC()
    gc |= {'A','B','C'}
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
191
192
    f,g,h = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','C','h')]
    gc |= {f,g,h}
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
193
    gc.transformer_graphviz()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
194
195
    ProtoClusterActif(Fleche(gc,f),Fleche(gc,f),{ObjetCommaCategorie(1,f,2),ObjetCommaCategorie(1,gc.identite('A'),1),
    ObjetCommaCategorie(2,gc.identite('B'),2)})
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
196
197
198
199
200
201
202
    
    c_i = CategorieClusters(gc)
    c_i.transformer_graphviz(afficher_identites=True)
    
    
    c_i |= {DiagrammeObjets(gc,{'A','B'})}
    c_i.transformer_graphviz(afficher_identites=True)
203
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
204
    c_i |= {Fleche(gc,f)}
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
205
    c_i.transformer_graphviz(afficher_identites=True)
206
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
207
208
    # c_i.identite(Fleche(gc,f)).transformer_graphviz(afficher_identites=True)
    # c_i.identite(DiagrammeObjets(gc,{'A','B'})).transformer_graphviz(afficher_identites=True)
209
    
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
210
211
212
213
214
215
216
217
218
219
220
221
    a = c_i({Fleche(gc,f)},{DiagrammeObjets(gc,{'A','B'})}).pop()
    a.transformer_graphviz()
    b = c_i({DiagrammeObjets(gc,{'A','B'})},{Fleche(gc,f)}).pop()
    b.transformer_graphviz()
    
    (a@b).transformer_graphviz()
    for cluster in c_i({DiagrammeObjets(gc,{'A','B'})},{DiagrammeObjets(gc,{'A','B'})}):
        cluster.nom="AB vers AB"
        cluster.transformer_graphviz(afficher_identites=True)
    (b@a).transformer_graphviz()
    for cluster in c_i({Fleche(gc,f)},{Fleche(gc,f)}):
        cluster.nom="f vers f"
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
222
        cluster.transformer_graphviz(afficher_identites=True)
223
224
225

if __name__ == '__main__':
    main()