Categorie.py 37 KB
Newer Older
1
2
3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

4
from Morphisme import Morphisme, Composition
5
from config import *
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
6

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
7
8
if GRAPHVIZ_ENABLED:
    from graphviz import Digraph
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
9
from collections import defaultdict
10
import itertools
11
import Diagramme
12
import copy
13

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
14

15
class Categorie:
16
17
18
19
    """Catégorie libre par défaut, on peut spécifier les contraintes
    de composition en ajoutant des diagrammes commutatifs.
    Les compositions de morphismes ne sont pas ajoutées par défaut,
    elles existent en potentialité."""
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
20
21
22
23

    nb_viz = 0  # nombre de graphes graphviz générés

    def __init__(self, nom="CategorieFinie"):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
24
        self.nom = nom
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
25
26
27
28
29
30
31
32
        self.objets = []  # les objets doivent être hashable
        self.morphismes = []  # liste des morphismes de base (pas les composes)
        self.identites = dict()  # à chaque objet on associe son morphisme identité, ce dernier doit aussi être
        # présent dans morphismes
        self.morph_sortants = defaultdict(list)  # Liste des morphismes sortant non triviaux d'un objet
        self.morph_entrants = defaultdict(list)
        self.diagrammes = []  # liste des diagrammes commutatifs

33
34
35
        class Composee(Composition):
            """La loi de composition est inclus dans la construction de la composee : 
            en construisant la composee de deux morphismes identifiés, le résultat sera le même."""
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
36
37
38
39
40
            loi_de_composition = {}  # dico Composition:Composition

            # /!\ La loi de composition doit être un graphe acyclique simplificateur /!\

            def verifier_coherence(noeuds_visites=tuple()):
41
42
                for noeud in Composee.loi_de_composition:
                    if noeud not in noeuds_visites:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
43
44
                        if not noeud.is_identite and not Composee.loi_de_composition[noeud].is_identite and len(
                                noeud.composee) == 1 and len(Composee.loi_de_composition[noeud]) == 1:
45
                            raise Exception("Loi de composition associe deux fleches elementaires")
46
                        if len(noeud) < len(Composee.loi_de_composition[noeud]):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
47
48
49
                            raise Exception(
                                "Loi de composition pas simplificatrice (image d'une composition est une composition de taille superieure) : " +
                                str(Composition(*noeud)) + " -> " + str(Composee.loi_de_composition[noeud]))
50
                        noeuds_visites += tuple([noeud])
51
                        if Composee.loi_de_composition[noeud] in noeuds_visites:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
52
53
54
                            raise Exception(
                                "Loi de composition n'est pas acyclique : " + str(Composition(*noeud)) + " -> " + str(
                                    Composee.loi_de_composition[noeud]))
55
                        Composee.verifier_coherence(noeuds_visites)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
56

57
            def _simplifier_composee(morphismes):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
58
59
60
61
                morphismes2 = [m for compo in morphismes for m in compo]
                for i in range(len(morphismes2), 1, -1):
                    for j in range(0, len(morphismes2) - i + 1):
                        sous_composee = Composition(*morphismes2[j:j + i])
62
                        if sous_composee in self.Composee.loi_de_composition:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
63
64
65
66
                            morphismes2 = morphismes2[0:j] + [
                                self.Composee.loi_de_composition[sous_composee]] + morphismes2[j + i:]
                            return Composee._simplifier_composee(morphismes2)
                return morphismes2
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
67
68

            def __new__(cls, *morphismes):
69
70
                morphismes = Composee._simplifier_composee(morphismes)
                composition = Composition(*morphismes)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
71
72
                if type(composition) is Composition:  # si la composition n'est pas simplifiée en morphisme
                    instance = super(Composee, cls).__new__(cls, *morphismes)
73
                    return instance
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
74
                else:  # si la composition est simplifiée en morphisme unitaire
75
                    return composition
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
76
77

            def __init__(self, *morphismes):
78
                morphismes = Composee._simplifier_composee(morphismes)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
79
                Composition.__init__(self, *morphismes)
80
81

        self.Composee = Composee
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
82

83
    def __copy__(self):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
84
        newone = Categorie(self.nom)
85
86
87
        newone.objets = copy.copy(self.objets)
        newone.morphismes = copy.copy(self.morphismes)
        newone.identites = copy.copy(self.identites)
88
89
90
91
92
93
        newone.morph_sortants = defaultdict(list)
        newone.morph_entrants = defaultdict(list)
        for cle in self.morph_sortants:
            newone.morph_sortants[cle] = copy.copy(self.morph_sortants[cle])
        for cle in self.morph_entrants:
            newone.morph_entrants[cle] = copy.copy(self.morph_entrants[cle])
94
        newone.diagrammes = copy.copy(self.diagrammes)
95
        newone.Composee.loi_de_composition = copy.copy(self.Composee.loi_de_composition)
96
        return newone
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
97
98

    def __eq__(self, other):
99
100
        if not issubclass(type(other),Categorie):
            return False
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
101
102
103
104
105
        return self.objets == other.objets and self.morphismes == other.morphismes and \
               self.identites == other.identites and self.morph_sortants == other.morph_sortants \
               and self.morph_entrants == other.morph_entrants and self.diagrammes == other.diagrammes \
               and self.Composee.loi_de_composition == other.Composee.loi_de_composition

106
107
    def __hash__(self):
        return super().__hash__()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
108
109
110
        
    def __str__(self):
        return str(self.nom)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
111

112
    def fleches_elem(self, source, cible, inclure_id = True):
Guillaume Sabbagh's avatar
Limite    
Guillaume Sabbagh committed
113
        """Renvoie la liste de flèches élémentaires de source à cible."""
114
        return [morph for morph in self.morph_sortants[source] if morph.cible == cible and (inclure_id or not morph.is_identite)]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
115

116
117
118
119
120
    def verifier_coherence(self):
        """Vérifie la cohérence de la structure (tous les axiomes des catégories sont vérifiés)."""
        m = self.morphismes
        if len(self.objets) == 0:
            ## S'il n'y a aucun objet, on vérifie que tout le reste est vide aussi
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
121
122
            assert (len(m) == len(self.morph_sortants) == len(self.morph_entrants) == len(self.diagrammes) == len(
                self.Composee.loi_de_composition) == 0)
123
124
125
        else:
            ## On vérifie l'existence de l'identité pour tous les objets (O(n))
            for o in self.objets:
126
                if o not in self.identites:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
127
128
                    raise Exception("Pas de morphisme identite pour l'objet " + str(o))

129
130
            ## L'associativité est vérifiée par construction de la composee
            ## On vérifie que les morphismes identifiés ont les mêmes sources et cibles
131
132
            for morph in self.Composee.loi_de_composition:
                if morph.source != self.Composee.loi_de_composition[morph].source:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
133
134
135
                    raise Exception(
                        "Morphismes identifies avec des sources differentes : " + ''.join(map(str, m)) + "," + str(
                            self.Composee.loi_de_composition[m]))
136
                if morph.cible != self.Composee.loi_de_composition[morph].cible:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
137
138
139
140
                    raise Exception(
                        "Morphismes identifies avec des cibles differentes : " + ''.join(map(str, m)) + "," + str(
                            self.Composee.loi_de_composition[m]))

141
142
143
144
            ## L'axiome d'identité est vérifié par construction de la composee
            ## On vérifie qu'il n'y a pas trop d'identités
            ident = [morph for morph in m if morph.is_identite]
            if len(ident) > len(self.objets):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
145
                raise Exception("Trop d'identites : " + ','.join(map(str, ident)))
146

147
148
149
150
151
152
            ## On vérifie que la liste morph_sortants est correcte
            for obj in self.objets:
                for morph in m:
                    if not morph.is_identite:
                        if morph.source == obj:
                            if morph not in self.morph_sortants[obj]:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
153
154
                                # print(' , '.join(map(str,self.morphismes)))
                                print(' , '.join(map(str, m)))
155
                                print(morph)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
156
                                raise Exception("Erreur morph_sortants : morphisme manquant " + str(morph))
157
158
                        if morph.source != obj:
                            if morph in self.morph_sortants[obj]:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
159
160
                                raise Exception("Erreur morph_sortants : morphisme en trop " + str(morph))

161
162
163
164
165
166
            ## On vérifie que la liste morph_entrants est correcte
            for obj in self.objets:
                for morph in m:
                    if not morph.is_identite:
                        if morph.cible == obj:
                            if morph not in self.morph_entrants[obj]:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
167
                                raise Exception("Erreur morph_entrants : morphisme manquant " + str(morph))
168
169
                        if morph.cible != obj:
                            if morph in self.morph_entrants[obj]:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
170
                                raise Exception("Erreur morph_entrants : morphisme en trop " + str(morph))
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
171
172
            if TOUJOURS_VERIFIER_COHERENCE_COMPOSEE:
                self.Composee.verifier_coherence()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
173

174
    def ajouter_objet(self, objet):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
175
        if objet in self.objets:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
176
177
            raise Exception("Objet deja present dans la categorie : tentative d'ajout de " + str(objet) + " echoue")

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
178
        ##on ajoute l'objet et l'identité associée
179
        self.objets += [objet]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
180
        self.identites[objet] = Morphisme(objet, objet, "Id" + str(objet), True)
181
        self.morphismes += [self.identites[objet]]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
182

183
184
        if TOUJOURS_VERIFIER_COHERENCE:
            self.verifier_coherence()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
185

186
    def ajouter_objets(self, objets):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
187
        for i in range(len(objets)):
188
            self.ajouter_objet(objets[i])
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
189
190

    def supprimer_objet(self, objet):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
191
        for morph in copy.copy(self.morph_entrants[objet]):
192
            self.supprimer_morphisme(morph)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
193
        for morph in copy.copy(self.morph_sortants[objet]):
194
195
196
            self.supprimer_morphisme(morph)
        self.supprimer_morphisme(self.identites[objet])
        self.objets.remove(objet)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
197
198
199
200
201
202
203
204
205
206
207
        del self.morph_entrants[objet]
        del self.morph_sortants[objet]
        del self.identites[objet]
        for d in self.diagrammes:
            if objet in d.cat_source.objets:
                if len(d.cat_source.morph_entrants[objet]) == len(d.cat_source.morph_sortants[objet]) == 0:
                    d.cat_source.supprimer_objet(objet)
                else:
                    d.transformer_graphviz()
                    d.cat_source.transformer_graphviz(destination="graphviz/debug",complet=False)
                    raise Exception("Il reste des morphismes sortants ou entrants d'un objet supprime "+str(objet))
208
209
        if TOUJOURS_VERIFIER_COHERENCE:
            self.verifier_coherence()
210
211
212
213
            
    def supprimer_objets(self,objets):
        for obj in copy.copy(objets):
            self.supprimer_objet(obj)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
214
215
216
217

    def supprimer_morphisme(self, morphisme):
        for elem_a_del in [e for e in self.Composee.loi_de_composition if
                           morphisme in self.Composee.loi_de_composition[e]]:
218
219
220
221
222
223
224
225
226
227
228
229
230
231
            del self.Composee.loi_de_composition[elem_a_del]
        if morphisme in self.Composee.loi_de_composition:
            del self.Composee.loi_de_composition[morphisme]
        self.morphismes.remove(morphisme)
        if not morphisme.is_identite:
            self.morph_entrants[morphisme.cible].remove(morphisme)
            self.morph_sortants[morphisme.source].remove(morphisme)
            while len(self.diagrammes) > 0:
                for i in range(len(self.diagrammes)):
                    d = self.diagrammes[i]
                    if morphisme in [e for compo in d.app_morph.values() for e in compo]:
                        self.diagrammes.pop(i)
                        nouveau_sketch = Categorie()
                        nouveau_sketch.ajouter_objets(d.cat_source.objets)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
232
233
234
235
236
237
                        nouveau_sketch.ajouter_morphismes([morph for morph in d.cat_source.morphismes if
                                                           morphisme not in d.app_morph[
                                                               morph] and not morph.is_identite])
                        nouveau_diagramme = Diagramme.Diagramme(nouveau_sketch, self, d.app_objets,
                                                                {cle: d.app_morph[cle] for cle in
                                                                 nouveau_sketch.morphismes if not cle.is_identite})
238
239
240
241
242
243
                        nouveau_diagramme.faire_commuter()
                        break
                else:
                    break
        if TOUJOURS_VERIFIER_COHERENCE:
            self.verifier_coherence()
244
245
246
247
    
    def supprimer_morphismes(self, morphismes):
        for morph in copy.copy(morphismes):
            self.supprimer_morphisme(morph)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
248

249
250
251
252
    def ajouter_morphisme(self, morphisme):
        self.morphismes += [morphisme]
        self.morph_entrants[morphisme.cible] += [morphisme]
        self.morph_sortants[morphisme.source] += [morphisme]
253
254
        if morphisme.is_identite:
            self.remplacer_identite(morphisme)
255
        self.verifier_coherence()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
256

257
258
259
    def ajouter_morphismes(self, morphismes):
        for m in morphismes:
            self.ajouter_morphisme(m)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
260
261

    def remplacer_identite(self, morphisme):
262
263
        """Remplace l'identité d'un objet par le morphisme."""
        if not morphisme.is_identite:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
264
265
            raise Exception(
                "Tentative de remplacer une identite par un morphisme qui n'est pas une identite " + str(morphisme))
266
267
        objet = morphisme.source
        if objet != morphisme.cible:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
268
269
            raise Exception(
                "Tentative de remplacer une identite par un morphisme qui n'est pas une identite." + str(morphisme))
270
271
        self.morphismes.remove(self.identites[objet])
        self.identites[objet] = morphisme
272
273
        self.morph_sortants[objet].remove(morphisme)
        self.morph_entrants[objet].remove(morphisme)
274

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
275
    def enumerer_composees_sans_cycle(self, source, cible, noeuds_deja_visites=tuple()):
276
277
        """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).
        Si la loi de composition est mal définie pour un cycle de morphismes, cette fonction ne boucle pas à l'infini."""
278
        if source == cible:
279
280
            return [self.identites[source]]
        if source not in noeuds_deja_visites:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
281
            noeuds_deja_visites = noeuds_deja_visites + (source,)
282
283
            composees_resultat = []
            for morph in self.morph_sortants[source]:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
284
285
286
                for composition_candidate in self.enumerer_composees_sans_cycle(morph.cible, cible,
                                                                                noeuds_deja_visites):
                    composee = self.Composee(morph, composition_candidate)
287
288
289
290
                    if composee not in composees_resultat:
                        composees_resultat += [composee]
            return composees_resultat
        return []
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
291
292

    def trouver_cycles_elementaires(self, objet):
293
294
        """Renvoie tous les cycles de morphismes élémentaires (qui ne contiennent aucun cycle) 
        de objet à objet qui n'est pas l'identité."""
295
296
297
        cycles = []
        for morph_pred in self.morph_entrants[objet]:
            pred = morph_pred.source
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
298
            cycles_tronques = self.enumerer_composees_sans_cycle(objet, pred)
299
            for cycle_tronque in cycles_tronques:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
300
                cycle = self.Composee(cycle_tronque, morph_pred)
301
302
                if cycle not in cycles:
                    cycles += [cycle]
303
        return cycles
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
304
305

    def enumerer_cycles(self, objet):
306
        """Enumère toutes les compositions de objet à objet qui ne sont pas l'identité.
307
308
309
310
311
        Si f et g sont des cycles élémentaires, 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 avec un nombre de morphismes strictement inférieurs.
        Si f ne se réduit pas, on regarde ff et fg, puis si ceux là non plus fff, ffg, fgf,fgg etc récursivement.
        """
        cycles = self.trouver_cycles_elementaires(objet)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
312
313

        def trouver_reductions(mot=tuple()):
314
            if DEBUG_LOI_DE_COMPOSITION:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
315
316
                print(' , '.join(map(str, mot)))
                print(list(map(lambda x: cycles.index(x), mot)))
317
            if len(mot) > 0:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
318
                # composition = Composition(*mot)
319
                composee = self.Composee(*mot)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
320
                if len(composee) < len(mot) or composee.is_identite:  # l'identité est une composition de 0 morphisme
321
322
323
324
                    return [composee]
                resultat = [composee]
            else:
                resultat = []
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
325

326
            for c in cycles:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
327
                reductions = trouver_reductions(mot + (c,))
328
329
                resultat += [e for e in reductions if e not in resultat]
            return resultat
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
330

331
        return [morph for morph in trouver_reductions() if not morph.is_identite]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
332
333

    def enumerer_composees(self, source, cible):
334
335
336
337
338
        """Renvoie tous les morphismes composés allant de source à cible.
        Si la loi de composition est mal définie pour un cycle de morphismes, cette fonction boucle à l'infini.
        1) On trouve les chemins sans cycle.
        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."""
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
339
        chemins_sans_cycles = self.enumerer_composees_sans_cycle(source, cible)
340
        noeuds = list(set([e for c in chemins_sans_cycles for e in c.objets_traverses()]))
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
341
        cycles = list(map(lambda x: self.enumerer_cycles(x), noeuds))
342
        tous_les_chemins = []
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
343

344
345
346
347
348
349
        for c in chemins_sans_cycles:
            noeuds_impliques = []
            cycles_impliques = []
            for noeud in c.objets_traverses():
                if len(cycles[noeuds.index(noeud)]) > 0:
                    noeuds_impliques += [noeud]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
350
351
                    cycles_impliques += [[self.identites[noeud]] + cycles[
                        noeuds.index(noeud)]]  # on rajoute une identité pour la possibilité de pas rajouter le cycle
352
353
354
355
356
357
            if len(cycles_impliques) > 0:
                for prod in itertools.product(*cycles_impliques):
                    morphismes = []
                    k = 0
                    for i in range(len(noeuds_impliques)):
                        noeud = noeuds_impliques[i]
358
359
                        while k < len(c) and c[k].source != noeud:
                            morphismes += [c[k]]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
360
                            k += 1
361
                        morphismes += [prod[i]]
362
                    while k < len(c):
363
                        morphismes += [c[k]]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
364
                        k += 1
365
366
367
368
                    comp = self.Composee(*morphismes)
                    if comp not in tous_les_chemins:
                        tous_les_chemins += [comp]
            else:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
369
                tous_les_chemins += [c]
370
371
        return tous_les_chemins

372
    def enumerer_composees_sortantes(self, source):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
373
374
        return [morph for cible in self.objets for morph in self.enumerer_composees(source, cible)]

375
376
377
    def enumerer_toutes_composees(self):
        """Renvoie un dictionnaire de la forme (source,cible):[composees]."""
        result = dict()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
378
379
380
381
        cycles_noeuds = {noeud: [self.identites[noeud]] + self.enumerer_cycles(noeud) for noeud in self.objets}
        for couple in itertools.product(self.objets, repeat=2):
            source, cible = couple
            chemins_sans_cycles = self.enumerer_composees_sans_cycle(source, cible)
382
383
            noeuds = list(set([e for c in chemins_sans_cycles for e in c.objets_traverses()]))
            tous_les_chemins = []
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
384

385
386
387
388
389
390
            for c in chemins_sans_cycles:
                noeuds_impliques = []
                cycles_impliques = []
                for noeud in c.objets_traverses():
                    if len(cycles_noeuds[noeud]) > 0:
                        noeuds_impliques += [noeud]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
391
                        cycles_impliques += [cycles_noeuds[noeud]]  # on rajoute une identité pour la possibilité de pas rajouter le cycle
392
393
394
395
396
397
                if len(cycles_impliques) > 0:
                    for prod in itertools.product(*cycles_impliques):
                        morphismes = []
                        k = 0
                        for i in range(len(noeuds_impliques)):
                            noeud = noeuds_impliques[i]
398
399
                            while k < len(c) and c[k].source != noeud:
                                morphismes += [c[k]]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
400
                                k += 1
401
                            morphismes += [prod[i]]
402
                        while k < len(c):
403
                            morphismes += [c[k]]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
404
                            k += 1
405
406
407
408
                        comp = self.Composee(*morphismes)
                        if comp not in tous_les_chemins:
                            tous_les_chemins += [comp]
                else:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
409
                    tous_les_chemins += [c]
410
            result[couple] = tous_les_chemins
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
411
412
        return result

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
413
414
415
416
    def est_cyclique(self):
        """Renvoie un booléen qui indique si le graphe sous-jacent est cyclique"""
        noeuds_suspects = []
        noeuds_safe = []
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
417

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
        def marquer_descendants(noeud):
            """Renvoie true si cycle trouvé, false sinon.
            Ajoute le noeud aux noeuds suspects et étend la liste des noeuds suspects en propageant aux descendants."""
            nonlocal noeuds_suspects
            nonlocal noeuds_safe
            if noeud in noeuds_suspects:
                return True
            if noeud in noeuds_safe:
                return False
            noeuds_suspects += [noeud]
            for morph in self.morph_sortants[noeud]:
                succ = morph.cible
                if marquer_descendants(succ):
                    return True
            noeuds_suspects.remove(noeud)
            noeuds_safe += [noeud]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
434

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
435
436
437
438
439
440
441
442
        noeuds_non_visites = [e for e in self.objets if e not in noeuds_safe]
        while len(noeuds_non_visites) > 0:
            noeud = noeuds_non_visites[0]
            if marquer_descendants(noeud):
                return True
            noeuds_safe += noeuds_suspects
            noeuds_suspects = []
            noeuds_non_visites = [e for e in self.objets if e not in noeuds_safe]
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
443

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
444
        return False
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
445
446

    def nb_composees_borne_sup(self, source, cible, noeuds_deja_visites=tuple()):
447
448
449
450
451
452
453
        """Renvoie une borne sup du nombre de composées entre source et cible.
        Dans le cas acyclique, borne_inf = borne_sup.
        Boucle à l'infini si la loi de composition est mal définie."""
        if source in noeuds_deja_visites:
            return 0
        noeuds_deja_visites += tuple([source])
        if source == cible:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
454
            return 1 + len(self.enumerer_cycles(source))
455
        if len(self.morph_sortants[source]) > 0:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
456
457
            result = sum([self.nb_composees_borne_sup(morph.cible, cible, noeuds_deja_visites) for morph in
                          self.morph_sortants[source]])
458
            for c in self.enumerer_cycles(source):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
459
460
461
                noeuds_deja_visites2 = noeuds_deja_visites + tuple([m.cible for m in c])
                result += sum([self.nb_composees_borne_sup(morph.cible, cible, noeuds_deja_visites2) for morph in
                               self.morph_sortants[source]])
462
463
            return result
        return 0
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
464
465

    def nb_composees_borne_inf(self, source, cible, noeuds_deja_visites=tuple()):
466
467
468
469
470
471
472
473
        """Renvoie une borne inf du nombre de composées entre source et cible.
        Dans le cas acyclique, borne_inf = borne_sup."""
        if source in noeuds_deja_visites:
            return 0
        noeuds_deja_visites += tuple([source])
        if source == cible:
            return 1
        if len(self.morph_sortants[source]) > 0:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
474
475
            return sum([self.nb_composees_borne_inf(morph.cible, cible, noeuds_deja_visites) for morph in
                        self.morph_sortants[source]])
476
        return 0
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
477

478
    def compter_toutes_composees(self):
479
480
481
        """Renvoie un intervalle qui contient le nombre de composées de toute la catégorie.
        Dans le cas acyclique, borne_inf = borne_sup.
        Boucle à l'infini si la loi de composition est mal définie."""
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
482
483
484
        return (sum([self.nb_composees_borne_inf(a, b) for a, b in itertools.product(self.objets, repeat=2)]),
                sum([self.nb_composees_borne_sup(a, b) for a, b in itertools.product(self.objets, repeat=2)]))

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
    def table_loi_de_composition(self):
        """Renvoie un dico de dico tel que table[f][g] = g o f, table[f][g] = None si f et g ne sont pas composables.
        La table contient les compositions triviales f o Id = f, f o g = f o g, f o (g o h) = (f o g) o h etc.
        Appel à  enumerer_toutes_composees, préferer accéder au dico Composee.loi_de_composition si possible."""
        table = defaultdict(defaultdict)
        composees = self.enumerer_toutes_composees()
        fleches = list(set([e for liste in composees.values() for e in liste]))
        print(fleches)
        for f,g in itertools.product(fleches, repeat=2):
            if f.cible == g.source:
                table[f][g] = self.Composee(f,g)
        return table

    def pretty_print_loi_de_composition(self,destination=None):
        """Si destination == None, alors on le print sinon on l'écrit dans un fichier"""
        composees = self.enumerer_toutes_composees()
        fleches = list(set([e for liste in composees.values() for e in liste]))
        fleches.sort(key = str)
        fleches.sort(key = lambda x:len(str(x)))
        result = "\t"+'\t'.join(map(lambda x:str(x).replace(' >> ','>'),fleches))+"\n"
        for f in fleches:
            result += str(f).replace(' >> ','>')+"\t"
            for g in fleches:
                if f.cible == g.source:
                    result += str(self.Composee(f, g)).replace(' >> ','>')+"\t"
                else:
                    result += "X\t"
            result += '\n'
        if destination != None:
            with open(destination, 'w') as f:
                f.write(result)
        else:
            print(result)

519
    def pretty_print(self):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
520
521
522
        result = "Objets de la categorie : \n" + ', '.join(
            map(str, self.objets)) + "\n\nMorphismes de la categorie : \n"
        result += '\n'.join(map(lambda x: x.pretty_print(), self.morphismes)) + "\n\n"
523
        return result
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
524
525

    def transformer_graphviz(self, complet=True, objets=None, destination=None, afficher_identites=False):
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
526
527
        """Permet de visualiser la catégorie avec graphviz"""
        Categorie.nb_viz += 1
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
528
        if destination == None:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
529
            destination = "graphviz/categorie" + str(Categorie.nb_viz)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
530

Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
531
        graph = Digraph('categorie')
532
        graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
533
        graph.attr(label=self.nom)
534
535
        if objets == None:
            objets = self.objets
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
536

537
538
        for o in objets:
            graph.node(str(o))
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
539
        if complet:
540
            fleches = self.enumerer_toutes_composees()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
541
            for couple in itertools.product(objets, objets):
542
543
                composees = fleches[couple]
                for c in composees:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
544
545
                    if afficher_identites or not c.is_identite:
                        if c in self.morphismes:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
546
                            graph.edge(str(couple[0]), str(couple[1]), label=str(c.representant), weight="1000")
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
547
                        else:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
548
                            graph.edge(str(couple[0]), str(couple[1]), label=str(c.representant), color="grey77")
549
550
        else:
            for morphisme in self.morphismes:
551
                if afficher_identites or not morphisme.is_identite:
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
552
                    graph.edge(str(morphisme.source), str(morphisme.cible), label=str(morphisme.representant))
553
        graph.render(destination)
554
555
        if CLEAN_GRAPHVIZ_MODEL:
            import os
556
            os.remove(destination)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
557
558


559
def main():
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
    cat = Categorie("Catégorie acyclique")
    cat.ajouter_objets("ABCDEF")
    f,g,h,i,j,k = [Morphisme('A','B','f'),Morphisme('A','C','g'),Morphisme('B','D','h'),Morphisme('B','E','i'),
                       Morphisme('C','E','j'),Morphisme('C','F','k')]
    cat.ajouter_morphismes([f,g,h,i,j,k])
    cat.transformer_graphviz()
    cat4 = cat.__copy__()

    diag = Diagramme.Carre(cat,"ABCE",[f,i,g,j])
    diag.faire_commuter()
    diag.transformer_graphviz()
    cat.transformer_graphviz()

    m = Morphisme('B', 'C', 'm')
    cat4.ajouter_morphisme(m)
    import CategoriePreordre
    cat4 = CategoriePreordre.CategoriePreordre(cat4)

    a = Morphisme('A','A','a')
    cat.ajouter_morphisme(a)
    cat4.ajouter_morphisme(a)
    diag = Diagramme.Triangle(cat,"AAA",[a,a,a])
    diag2 = Diagramme.Triangle(cat4,"AAA",[a,a,a])
    diag.faire_commuter()
    diag2.faire_commuter()
    cat.nom = "Catégorie"
    cat4.nom = "Catégorie"
    cat.transformer_graphviz()

    b = Morphisme('E','A','b')
    cat.ajouter_morphisme(b)
    diag = Diagramme.Triangle(cat, "AAA", [cat.Composee(f,i,b), cat.Composee(f,i,b), cat.Composee(f,i,b)])
    diag.faire_commuter()
    diag = Diagramme.Triangle(cat, "AAA", [cat.Composee(f,i,b), a, cat.identites['A']])
    diag.faire_commuter()
    cat.transformer_graphviz()

    cat.transformer_graphviz(False)
    import CategoriePreordre
    cat = CategoriePreordre.CategoriePreordre(cat)
    cat.transformer_graphviz()

    cat.diagrammes[-1].transformer_graphviz()

    cat2 = Categorie("1 flèche")
    cat2.ajouter_objets("XY")
    z = Morphisme('X','Y','z')
    cat2.ajouter_morphisme(z)

    import Foncteur
    cat4.transformer_graphviz()
    fonct = Foncteur.Foncteur(cat2,cat4,{'X':'C','Y':'F'},{z:k})
    fonct.transformer_graphviz()

    for obj in cat.objets:
        cones = fonct.enumerer_cones(obj)
        for c in cones:
            c.transformer_graphviz()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
618
619
    from CategorieCones import CategorieCones
    cat3 = CategorieCones(fonct)
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
    cat3.transformer_graphviz()

    cat = Categorie("Test table loi de composition")
    cat.ajouter_objets("ABCDE")
    f, g, h, i, j, k, l, m = [Morphisme('A', 'B', 'f'), Morphisme('A', 'E', 'g'), Morphisme('A', 'E', 'h'),
                              Morphisme('B', 'E', 'i'),
                              Morphisme('C', 'D', 'j'), Morphisme('B', 'E', 'k'), Morphisme('C', 'A', 'l'),
                              Morphisme('D', 'E', 'm')]
    cat.ajouter_morphismes([f, g, h, i, j, k, l, m])
    cat.transformer_graphviz()
    import CategoriePreordre
    cat2 = CategoriePreordre.CategoriePreordre(cat)
    cat2.transformer_graphviz()
    n = Morphisme('A', 'A', 'n')
    cat2.ajouter_morphisme(n)
    diag = Diagramme.Triangle(cat2, "AAA", [Composition(n,n), n, n])
    diag.faire_commuter()
    cat2.transformer_graphviz()


640
641
642
643
644
645
646
647
648
649
650
651
    # cat = Categorie("Acyclique")
    # cat.ajouter_objets("ABCDEF")
    # f,g,h,i,j,k,l = [Morphisme('A','B','f'),Morphisme('A','C','g'),Morphisme('A','E','h'),Morphisme('B','D','i'),Morphisme('B','E','j'),Morphisme('C','E','k'),Morphisme('C','F','l')]
    # cat.ajouter_morphismes([f,g,h,i,j,k,l])
    # cat.transformer_graphviz()
    # t = Diagramme.Triangle(cat,"ABE",[f,j,h])
    # t.faire_commuter()
    # cat.transformer_graphviz()
    # t = Diagramme.Triangle(cat,"ACE",[g,k,h])
    # t.faire_commuter()
    # cat.transformer_graphviz()
    # print("est_cyclique : "+str(cat.est_cyclique()))
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
652

653
654
655
656
657
658
    # cat = Categorie("Test Isomorphisme")
    # cat.ajouter_objets("ABC")
    # f,g,h,i = [Morphisme('A','B','f'),Morphisme('B','A','g'),Morphisme('A','C','h'),Morphisme('C','A','i')]
    # cat.ajouter_morphismes([f,g,h,i])
    # t = Diagramme.Triangle(cat,"ABA",[f,g,cat.identites['A']])
    # t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
659
660
    # t = Diagramme.Triangle(cat,"ACA",[h,i,cat.identites['A']])
    # t.faire_commuter()
661
662
663
664
665
666
667
    # t = Diagramme.Triangle(cat,"BAB",[g,f,cat.identites['B']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"CAC",[cat.Composee(i,h,i),h,cat.Composee(i,h)])
    # t.faire_commuter()
    # cat.transformer_graphviz()
    # t.transformer_graphviz()
    # print("est_cyclique : "+str(cat.est_cyclique()))
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
668

669
    cat = Categorie("Test cycles")
670
    cat.ajouter_objets("ABCDE")
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
671
672
673
674
675
676
    a1, a2, a3 = [Morphisme('A', 'B', 'a1'), Morphisme('B', 'C', 'a2'), Morphisme('C', 'A', 'a3')]
    b1, b2, b3 = [Morphisme('A', 'D', 'b1'), Morphisme('D', 'E', 'b2'), Morphisme('E', 'A', 'b3')]
    cat.ajouter_morphismes([a1, a2, a3, b1, b2, b3])
    a = cat.Composee(a1, a2, a3)
    b = cat.Composee(b1, b2, b3)
    t = Diagramme.Triangle(cat, "AAA", [a, cat.identites['A'], cat.identites['A']])
677
    t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
678
    t = Diagramme.Triangle(cat, "AAA", [b, cat.identites['A'], cat.identites['A']])
679
    t.faire_commuter()
680
681
682
683
    # t = Diagramme.Triangle(cat,"AAA",[c,cat.identites['A'],cat.identites['A']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[a,c,cat.identites['A']])
    # t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
684
    t = Diagramme.Triangle(cat, "AAA", [a, b, cat.identites['A']])
685
    t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
686
    t = Diagramme.Triangle(cat, "AAA", [b, a, cat.identites['A']])
687
    t.faire_commuter()
688
689
690
691
692
693
    # t = Diagramme.Triangle(cat,"AAA",[c,a,cat.identites['A']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[b,c,cat.identites['A']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[c,b,cat.identites['A']])
    # t.faire_commuter()
694
695
    cat.transformer_graphviz()
    print(cat.enumerer_cycles('D')[0])
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
696
697

    print("est_cyclique : " + str(cat.est_cyclique()))
698
    print(cat.compter_toutes_composees())
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
699
700
    print(sum(map(len, cat.enumerer_toutes_composees().values())))

701
702
703
704
705
    # cat = Categorie("Test")
    # cat.ajouter_objets("AB")
    # f,g = [Morphisme('A','B','f'),Morphisme('A','B','g')]
    # cat.ajouter_morphismes([f,g])
    # cat.transformer_graphviz()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
706

707
708
709
710
    # diag = Diagramme.DiagrammeIdentite(cat)
    # diag.faire_commuter()
    # print(cat.morph_sortants)
    # cat.transformer_graphviz()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
711

712
713
    cat = Categorie("Test comptage composees")
    cat.ajouter_objets("ABCDE")
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
714
715
716
717
718
    f, g, h, i, j, k, l, m = [Morphisme('A', 'B', 'f'), Morphisme('A', 'E', 'g'), Morphisme('A', 'E', 'h'),
                              Morphisme('B', 'E', 'i'),
                              Morphisme('C', 'D', 'j'), Morphisme('B', 'E', 'k'), Morphisme('C', 'A', 'l'),
                              Morphisme('D', 'E', 'm')]
    cat.ajouter_morphismes([f, g, h, i, j, k, l, m])
719
    cat.transformer_graphviz()
720
    print(cat.compter_toutes_composees())
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
721
722
    print(sum(map(len, cat.enumerer_toutes_composees().values())))

723
724
    cat = Categorie("Test comptage composees")
    cat.ajouter_objets("ABCD")
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
725
726
727
728
729
    f, g, h, i, j, k = [Morphisme('A', 'B', 'f'), Morphisme('B', 'A', 'g'), Morphisme('A', 'C', 'h'),
                        Morphisme('C', 'B', 'i'),
                        Morphisme('B', 'D', 'j'), Morphisme('C', 'D', 'k')]
    cat.ajouter_morphismes([f, g, h, i, j, k])
    diag = Diagramme.Triangle(cat, "AAA", [cat.Composee(f, g), cat.Composee(f, g), cat.Composee(f, g)])
730
    diag.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
731
    diag = Diagramme.Triangle(cat, "AAA", [cat.Composee(h, i, g), cat.identites['A'], cat.identites['A']])
732
    diag.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
733

734
735
    cat.transformer_graphviz()
    print(cat.compter_toutes_composees())
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
736
737
    print(sum(map(len, cat.enumerer_toutes_composees().values())))

738
739
    cat = Categorie("Test comptage composees borne sup atteinte")
    cat.ajouter_objets("ABC")
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
740
741
742
743
    f, g, h, i, j = [Morphisme('A', 'A', 'f'), Morphisme('A', 'B', 'g'), Morphisme('B', 'B', 'h'),
                     Morphisme('B', 'C', 'i'), Morphisme('C', 'C', 'j')]
    cat.ajouter_morphismes([f, g, h, i, j])
    diag = Diagramme.Triangle(cat, "AAA", [cat.Composee(f), cat.Composee(f), cat.Composee(f)])
744
    diag.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
745
    diag = Diagramme.Triangle(cat, "BBB", [cat.Composee(h), cat.Composee(h), cat.Composee(h)])
746
    diag.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
747
    diag = Diagramme.Triangle(cat, "CCC", [cat.Composee(j), cat.Composee(j), cat.Composee(j)])
748
    diag.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
749

750
751
    cat.transformer_graphviz()
    print(cat.compter_toutes_composees())
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
752
753
    print(sum(map(len, cat.enumerer_toutes_composees().values())))

754
755
    cat.transformer_graphviz()
    print(cat.compter_toutes_composees())
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
756
757
    print(sum(map(len, cat.enumerer_toutes_composees().values())))

758
759
    cat = Categorie("Test cycles")
    cat.ajouter_objets("ABC")
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
760
761
762
763
764
765
    a1, a2 = [Morphisme('A', 'B', 'a1'), Morphisme('B', 'A', 'a2')]
    b1, b2 = [Morphisme('A', 'C', 'b1'), Morphisme('C', 'A', 'b2')]
    cat.ajouter_morphismes([a1, a2, b1, b2])
    a = cat.Composee(a1, a2)
    b = cat.Composee(b1, b2)
    t = Diagramme.Triangle(cat, "AAA", [a, a, cat.identites['A']])
766
    t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
767
    t = Diagramme.Triangle(cat, "AAA", [b, b, cat.identites['A']])
768
769
770
771
772
    t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[c,cat.identites['A'],cat.identites['A']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[a,c,cat.identites['A']])
    # t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
773
    t = Diagramme.Triangle(cat, "AAA", [a, b, cat.identites['A']])
774
    t.faire_commuter()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
775
    t = Diagramme.Triangle(cat, "AAA", [b, a, cat.identites['A']])
776
777
778
779
780
781
782
783
    t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[c,a,cat.identites['A']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[b,c,cat.identites['A']])
    # t.faire_commuter()
    # t = Diagramme.Triangle(cat,"AAA",[c,b,cat.identites['A']])
    # t.faire_commuter()
    cat.transformer_graphviz()
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
784
785
786
    print(cat.nb_composees_borne_sup("A", "A"))
    print(len(cat.enumerer_toutes_composees()[("A", "A")]))

787
if __name__ == '__main__':
Guillaume Sabbagh's avatar
Guillaume Sabbagh committed
788
    main()