Commit 7a27a1ec authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Ajout des type helpers

parent 972dde8c
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from config import *
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
import types
from collections import defaultdict
import copy
import itertools
from Morphisme import Morphisme
from config import *
import typing
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
class Categorie:
"""Classe abstraite qui définit ce qu'est une catégorie.
......@@ -35,31 +35,47 @@ class Categorie:
"""
nb_viz = 0 # nombre de graphes graphviz générés
def __init__(self,nom = "Catégorie"):
assert(type(nom) is str)
def __init__(self,nom: str = "Catégorie"):
self.nom = nom
self.objets = frozenset()
self.__morphismes = frozenset()
def __call__(self,*args):
def __call__(self, source: any, cible: any) -> set:
"""
Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie :
C({a_i},{b_i}) renvoie l'ensemble des flèches d'un élément de {a_i} vers un élément de {b_i}
C({a_i},b) renvoie l'ensemble des flèches d'un élément de {a_i} vers b
C(a,{b_i}) renvoie l'ensemble des flèches de a vers un élément de {b_i}
C(a,b) renvoie l'ensemble des flèches de a vers b.
"""
raise NotImplementedError("Les categories filles doivent implementer cette methode pour renvoyer les morphismes C(a,b) (fleches de a vers b) et C(a) (identite de a)")
def identite(self,objet):
def identite(self,objet: any) -> Morphisme:
"""
Cette méthode est virtuelle pure.
Les classes filles doivent l'implémenter tel que si C est une catégorie,
C.identite(objet) renvoie l'identité de l'`objet`.
"""
NotImplementedError("Les categories filles doivent implementer cette methode.")
def __eq__(self,other):
def __eq__(self,other: 'Categorie') -> bool:
return self.nom == other.nom and self.objets == other.objets and self.__morphismes == other.__morphismes
def __hash__(self):
def __hash__(self) -> int:
return hash((self.nom,self.objets,self.__morphismes))
def __str__(self):
def __str__(self) -> str:
return self.nom
def __abs__(self):
def __abs__(self) -> frozenset:
"""
Renvoie les objets de la catégorie.
Cette syntaxe suit la convention mathématiques |C| = objets de C.
"""
return self.objets
def __iadd__(self,other):
def __iadd__(self,other:any):
"""
Soit C une catégorie.
C += a_i ajoute l'objet a_i à la catégorie C
......@@ -69,8 +85,6 @@ class Categorie:
Si on doit ajouter un objet a_i qui hérite de Morphisme, alors on va l'ajouter en tant que morphisme.
Si on veut ajouter une objet descendant d'un Morphisme en tant qu'objet, il faut utiliser la méthode ajouter_objet.
"""
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
if issubclass(type(other),Morphisme):
self.ajouter_morphisme(other)
elif type(other) in {frozenset,set,list,tuple}:
......@@ -80,7 +94,7 @@ class Categorie:
self.ajouter_objet(other)
return self
def __isub__(self,other):
def __isub__(self,other:any):
"""
Soit C une catégorie.
C -= a_i supprime l'objet a_i de la catégorie C
......@@ -88,8 +102,6 @@ class Categorie:
C -= f_i supprime le morphisme f_i de la catégorie C
C -= {f_1,f_2,...} supprime les morphismes f_i de la catégorie C
"""
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
if other in self.objets:
self.supprimer_objet(other)
elif other in self.__morphismes:
......@@ -101,23 +113,19 @@ class Categorie:
raise Exception("Erreur : tentative de retirer "+str(other)+" qui n'appartient pas a la categorie")
return self
def ajouter_objet(self,objet):
def ajouter_objet(self,objet:any):
self.objets |= {objet}
def ajouter_morphisme(self,morphisme):
def ajouter_morphisme(self,morphisme: Morphisme):
self.__morphismes |= {morphisme}
def supprimer_objet(self, objet):
def supprimer_objet(self, objet: any):
self -= {morph for morph in self(objet,self.objets)}
self -= {morph for morph in self(self.objets,objet)}
self.objets -= {objet} # on supprime l'objet
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
def supprimer_morphisme(self, morphisme):
def supprimer_morphisme(self, morphisme: Morphisme):
self.__morphismes -= {morphisme} # on supprime le morphisme
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
def verifier_coherence(self):
"""Vérifie la cohérence de la structure (tous les axiomes des catégories sont vérifiés)."""
......@@ -142,12 +150,15 @@ class Categorie:
for m1,m2,m3 in itertools.product(self.__morphismes,repeat=3):
if m1.source == m2.cible and m2.source == m3.cible:
if (m1@m2)@m3 != m1@(m2@m3):
raise Exception("Incoherence Categorie : associativite pas respectee pour "+str(m3)+", "+str(m2)+", "+str(m1))
raise Exception("Incoherence Categorie : associativite pas respectee pour "+str(m3)+", "+str(m2)+", "+str(m1)+\
"\n(m1@m2)@m3 = "+str((m1@m2)@m3)+" m1@(m2@m3) = "+str(m1@(m2@m3)))
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.
"""
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.
O(m²)"""
O(m²)
"""
table = defaultdict(dict())
for A,B in itertools.product(abs(self), repeat=2):
for f in self(A,B):
......@@ -155,8 +166,12 @@ class Categorie:
table[f][g] = g@f
return table
def loi_de_composition_to_csv(self,destination=None,sep=','):
"""Si destination == None, alors on le print sinon on l'écrit dans un fichier"""
def loi_de_composition_to_csv(self, destination:str=None, sep: str=','):
"""
Exporte la loi de composition de la catégorie en csv.
Si destination == None, alors on le print sinon on l'écrit dans un fichier à la destination.
`sep` est le séparateur.
"""
fleches = list(self(self.objets,self.objets))
fleches.sort(key = str)
fleches.sort(key = lambda x:len(str(x)))
......@@ -175,7 +190,7 @@ class Categorie:
else:
print(result)
def transformer_graphviz(self, destination=None, afficher_identites=False):
def transformer_graphviz(self, destination:str=None, afficher_identites:bool=False):
"""Permet de visualiser la catégorie avec graphviz"""
Categorie.nb_viz += 1
if destination == None:
......@@ -196,37 +211,74 @@ class Categorie:
import os
os.remove(destination)
def test_Categorie():
from GrapheDeComposition import GC,MGC
GC = GC()
GC += {'A','B','C'}
morphismes = [MGC('A','B','f'),MGC('B','C','g')]
GC += morphismes
GC.transformer_graphviz()
GC.loi_de_composition_to_csv()
class SousCategoriePleine(Categorie):
"""Sous-catégorie pleine d'une catégorie."""
"""
Sous-catégorie pleine d'une catégorie.
Une sous-catégorie pleine est constituée d'un sous-ensemble d'objets de la catégorie initiale
et de tous les morphismes entre ces objets.
"""
def __init__(self,categorie,objets):
"""On construit la sous-catégorie pleine de categorie qui contient l'ensemble des objets."""
Categorie.__init__(self,"Sous-catégorie pleine de "+str(categorie)+ "ayant pour objets "+str(objets))
def __init__(self, categorie_originelle:Categorie, objets:set):
"""On construit la sous-catégorie pleine de `categorie_originelle` qui contient l'ensemble des `objets`."""
Categorie.__init__(self,"Sous-catégorie pleine de "+str(categorie_originelle)+ " ayant pour objets "+str(objets))
self += objets
self += categorie(objets,objets)
self += categorie_originelle(objets,objets)
self.categorie_originelle = categorie_originelle
def __call__(self, source:any, cible:any) -> set:
return self.categorie_originelle(source,cible)
def identite(self,objet:any) -> Morphisme:
return self.categorie_originelle.identite(objet)
def test_SousCategoriePleine():
from GrapheDeComposition import GC,MGC
GC = GC()
GC += set("ABCDE")
morphismes = [MGC('A','B','f'),MGC('B','C','g'),MGC('A','D','h'),MGC('D','E','i'),MGC('C','E','j')]
GC += morphismes
GC.transformer_graphviz()
sous_cat_pleine = SousCategoriePleine(GC,set("ADE"))
sous_cat_pleine.transformer_graphviz()
class CategorieDiscrete(Categorie):
"""
Catégorie discrète : catégorie dont les seuls morphismes sont des identités.
"""
class Identite(Morphisme):
def __init__(self,objet):
"""
Tous les morphismes de la catégorie discrète sont des identités.
"""
def __init__(self,objet:any):
Morphisme.__init__(self,objet,objet,"Id"+str(objet),True)
def __matmul__(self,other):
def __matmul__(self,other:'CategorieDiscrete.Identite') -> 'CategorieDiscrete.Identite':
"""
On peut composer uniquement une identité avec elle même.
"""
if type(other) != Identite:
raise Exception("Composition d'un morphisme de type inconnu avec une Identite")
if self.source != other.source:
raise Exception("Composition incoherente d'Identites")
return self
def __eq__(self,other):
def __eq__(self,other:'CategorieDiscrete.Identite') -> bool:
return type(other) == CategorieDiscrete.Identite and self.source == other.source
def __hash__(self):
def __hash__(self) -> int:
return hash(self.source)
def __call__(self,source,cible):
def __call__(self,source:any,cible:any) -> set():
if source in self.objets:
# source est de type a
source = {source}
......@@ -236,7 +288,18 @@ class CategorieDiscrete(Categorie):
#maintenant tout est de la forme {a_i} vers {b_i}
return {CategorieDiscrete.Identite(a) for a in source for b in cible if a == b}
def identite(self,objet):
def identite(self,objet:any) -> 'CategorieDiscrete.Identite':
return CategorieDiscrete.Identite(objet)
\ No newline at end of file
def test_CategorieDiscrete():
cat_discrete = CategorieDiscrete()
cat_discrete += set("ABCDE")
cat_discrete.transformer_graphviz(afficher_identites=True)
def main():
test_Categorie()
test_CategorieDiscrete()
test_SousCategoriePleine()
if __name__ == '__main__':
main()
\ No newline at end of file
from Categorie import Categorie
from Morphisme import Morphisme
from Diagramme import Parallele,DiagrammeIdentite
from GrapheDeComposition import GC,MGC
import random
import copy
import itertools
from config import *
class CategorieAlea(Categorie):
"""Construit une catégorie aléatoire."""
class GrapheCompositionAleatoire(GC):
"""Construit un graphe de composition aléatoire."""
def __init__(self, nb_objets = None, nb_morph = None, fonct_masse_nb_branchements_cycles = lambda:int(random.expovariate(2))):
def __init__(self, nb_objets = None, nb_morph = None, proba_branchement_cycle=1/10):
"""nb_morph est le nombre de morphismes élémentaires à ajouter aléatoirement.
La fonction de masse permet de gérer le cas de la composition des différents cycles,
le nombre donne le nombre de fois qu'on s'autorise à brancher sur l'arbre de composition."""
Categorie.__init__(self,"Catégorie aléatoire")
proba_branchement_cycle permet de gérer le cas de la composition des différents cycles,
la proba est celle de brancher sur l'arbre de composition."""
GC.__init__(self,"Graphe de composition aléatoire")
if nb_objets == None:
nb_objets = random.randint(5,20)
if nb_morph == None:
nb_morph = random.randint(0,30)
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("Création d'une catégorie avec "+str(nb_objets)+" objets et "+str(nb_morph)+" morphismes")
self.ajouter_objets(list(range(nb_objets))) #ajout des objets
self += set(range(nb_objets)) #ajout des objets
for i in range(nb_morph): # ajout des morphismes
source = random.randint(0,nb_objets-1)
cible = random.randint(0,nb_objets-1)
self.ajouter_morphisme(Morphisme(source,cible,str(i)))
self += MGC(source,cible)
#on définit la loi de composition pour les cycles
for obj in self.objets:
cycles = self.trouver_cycles_elementaires(obj)
arbre_complet = cycles + [self.identites[obj]]
feuilles = [self.Composee(*couple) for couple in itertools.product(cycles,repeat=2)]
arbre_complet += feuilles
if len(cycles) > 0:
nb_branchement = fonct_masse_nb_branchements_cycles()
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("nombre de branchement pour l'objet "+str(obj)+" : "+str(nb_branchement))
print("nombre de cycles : "+str(len(cycles)))
for i in range(nb_branchement):
c = random.choice(feuilles)
feuilles.remove(c)
feuilles += [self.Composee(c,e) for e in cycles]
arbre_complet += [self.Composee(c,e) for e in cycles]
for c in feuilles:
image = random.choice([e for e in arbre_complet if len(e) < len(c)])
diag = Parallele(self,[obj]*2,[c,image])
diag.faire_commuter()
cycles = self.trouver_cycles_minimaux(obj,inclure_id=False)
arbre_composition = {cycle:"a determiner" for cycle in cycles}
# {noeud_arbre : image_cycle}
profondeur_cycle = {self.identite(obj):0} #cycle: profondeur dans l'arbre de composition
for cycle in cycles:
profondeur_cycle[cycle] = 1
continuer = True
while continuer:
continuer = False
for cle in copy.copy(arbre_composition):
if arbre_composition[cle] == "a determiner":
continuer=True
if random.random() < proba_branchement_cycle: # ici on branche
arbre_composition[cle] = None
for cycle in cycles:
arbre_composition[cycle@cle]="a determiner"
profondeur_cycle[cycle@cle] = profondeur_cycle[cle]+1
else: # ici on doit simplifier le cycle
cycles_candidats = [cycle for cycle in profondeur_cycle if profondeur_cycle[cycle] < profondeur_cycle[cle]]
arbre_composition[cle]=random.choice(cycles_candidats)
for cycle in arbre_composition:
if arbre_composition[cycle] != None:
if len(cycle) == 1 and len(arbre_composition[cycle]) == 1: # si on identifie deux cycles élémentaires on en retire un
self -= cycle
else:
MGC.identifier_morphismes(cycle,arbre_composition[cycle])
print(arbre_composition)
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("Fin création")
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
class CategorieAleaIsomorph(Categorie):
"""Construit une catégorie aléatoire où tous les cycles sont des isomorphismes."""
def test_GrapheCompositionAleatoire():
GCA = GrapheCompositionAleatoire(5,10)
GCA.transformer_graphviz()
# class CategorieAleaIsomorph(Categorie):
# """Construit une catégorie aléatoire où tous les cycles sont des isomorphismes."""
def __init__(self, nb_objets = None, nb_morph = None):
"""nb_morph est le nombre de morphismes élémentaires à ajouter aléatoirement."""
Categorie.__init__(self,"Catégorie aléatoire")
if nb_objets == None:
nb_objets = random.randint(5,20)
if nb_morph == None:
nb_morph = random.randint(0,30)
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("Création d'une catégorie avec "+str(nb_objets)+" objets et "+str(nb_morph)+" morphismes")
self.ajouter_objets(list(range(nb_objets))) # ajout des objets
for i in range(nb_morph): # ajout des morphismes
pool = [(source,cible) for source,cible in itertools.product(range(nb_objets),repeat=2) if source != cible and len(self.enumerer_composees_sans_cycle(source,cible)) == 0 or len(self.enumerer_composees_sans_cycle(cible,source)) == 0]
if len(pool) == 0:
break
source,cible = random.choice(pool)
self.ajouter_morphisme(Morphisme(source,cible,str(i)))
#on définit la loi de composition pour les cycles
for obj in self.objets:
cycles = self.trouver_cycles_elementaires(obj)
for c in cycles:
diag = Parallele(self,[obj]*2,[c,self.identites[obj]])
diag.faire_commuter()
if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
print("Fin création")
# def __init__(self, nb_objets = None, nb_morph = None):
# """nb_morph est le nombre de morphismes élémentaires à ajouter aléatoirement."""
# Categorie.__init__(self,"Catégorie aléatoire")
# if nb_objets == None:
# nb_objets = random.randint(5,20)
# if nb_morph == None:
# nb_morph = random.randint(0,30)
# if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
# print("Création d'une catégorie avec "+str(nb_objets)+" objets et "+str(nb_morph)+" morphismes")
# self.ajouter_objets(list(range(nb_objets))) # ajout des objets
# for i in range(nb_morph): # ajout des morphismes
# pool = [(source,cible) for source,cible in itertools.product(range(nb_objets),repeat=2) if source != cible and len(self.enumerer_composees_sans_cycle(source,cible)) == 0 or len(self.enumerer_composees_sans_cycle(cible,source)) == 0]
# if len(pool) == 0:
# break
# source,cible = random.choice(pool)
# self.ajouter_morphisme(Morphisme(source,cible,str(i)))
# on définit la loi de composition pour les cycles
# for obj in self.objets:
# cycles = self.trouver_cycles_elementaires(obj)
# for c in cycles:
# diag = Parallele(self,[obj]*2,[c,self.identites[obj]])
# diag.faire_commuter()
# if PRINT_AVANCEMENT_CREATION_CAT_ALEA:
# print("Fin création")
class CategorieAleaPreOrdre(Categorie):
"""Construit une catégorie pré-ordre aléatoire."""
def __init__(self,nb_objets = None, proportion_morph = None, retirer_obj_isoles = True):
"""proportion morph est la proportion de morphismes de 0 (graphe sans flèche) à 1 (graphe complet)"""
if nb_objets == None:
nb_objets = random.randint(5,20)
if proportion_morph == None:
proportion_morph = min(random.expovariate(10),1)
nb_morph = max(int(proportion_morph*(nb_objets)*(nb_objets-1)/2),5) # min 5 morphismes
Categorie.__init__(self,"Catégorie pré-ordre aléatoire")
self.ajouter_objets(list(range(nb_objets))) #ajout des objets
i = 1
for source,cible in random.sample(list(itertools.product(range(nb_objets),repeat=2)),nb_morph): # ajout des morphismes
self.ajouter_morphisme(Morphisme(source,cible,str(i)))
i += 1
if retirer_obj_isoles:
self.retirer_noeuds_isoles()
diag = DiagrammeIdentite(self)
diag.faire_commuter()
# class CategorieAleaPreOrdre(Categorie):
# """Construit une catégorie pré-ordre aléatoire."""
# def __init__(self,nb_objets = None, proportion_morph = None, retirer_obj_isoles = True):
# """proportion morph est la proportion de morphismes de 0 (graphe sans flèche) à 1 (graphe complet)"""
# if nb_objets == None:
# nb_objets = random.randint(5,20)
# if proportion_morph == None:
# proportion_morph = min(random.expovariate(10),1)
# nb_morph = max(int(proportion_morph*(nb_objets)*(nb_objets-1)/2),5) # min 5 morphismes
# Categorie.__init__(self,"Catégorie pré-ordre aléatoire")
# self.ajouter_objets(list(range(nb_objets))) #ajout des objets
# i = 1
# for source,cible in random.sample(list(itertools.product(range(nb_objets),repeat=2)),nb_morph): # ajout des morphismes
# self.ajouter_morphisme(Morphisme(source,cible,str(i)))
# i += 1
# if retirer_obj_isoles:
# self.retirer_noeuds_isoles()
# diag = DiagrammeIdentite(self)
# diag.faire_commuter()
def retirer_noeuds_isoles(self):
self.supprimer_objets([obj for obj in self.objets if len(self.morph_sortants[obj]) == len(self.morph_entrants[obj]) == 0])
# def retirer_noeuds_isoles(self):
# self.supprimer_objets([obj for obj in self.objets if len(self.morph_sortants[obj]) == len(self.morph_entrants[obj]) == 0])
class CategorieAleaPreOrdreAcyclique(Categorie):
"""Construit une catégorie pré-ordre aléatoire."""
def __init__(self,nb_objets = None, proportion_morph = None, retirer_obj_isoles = True):
"""proportion morph est la proportion de morphismes de 0 (graphe sans flèche) à 1 (graphe complet)"""
if nb_objets == None:
nb_objets = random.randint(5,20)
if proportion_morph == None:
proportion_morph = min(random.expovariate(10),1)
nb_morph = max(int(proportion_morph*(nb_objets)*(nb_objets-1)/2),5) # min 5 morphismes
Categorie.__init__(self,"Catégorie pré-ordre aléatoire")
self.ajouter_objets(list(range(nb_objets))) #ajout des objets
i = 1
for source,cible in random.sample(list(itertools.combinations(range(nb_objets),2)),nb_morph): # ajout des morphismes
self.ajouter_morphisme(Morphisme(source,cible,str(i)))
i += 1
if retirer_obj_isoles:
self.retirer_noeuds_isoles()
diag = DiagrammeIdentite(self)
diag.faire_commuter()
# class CategorieAleaPreOrdreAcyclique(Categorie):
# """Construit une catégorie pré-ordre aléatoire."""
# def __init__(self,nb_objets = None, proportion_morph = None, retirer_obj_isoles = True):
# """proportion morph est la proportion de morphismes de 0 (graphe sans flèche) à 1 (graphe complet)"""
# if nb_objets == None:
# nb_objets = random.randint(5,20)
# if proportion_morph == None:
# proportion_morph = min(random.expovariate(10),1)
# nb_morph = max(int(proportion_morph*(nb_objets)*(nb_objets-1)/2),5) # min 5 morphismes
# Categorie.__init__(self,"Catégorie pré-ordre aléatoire")
# self.ajouter_objets(list(range(nb_objets))) #ajout des objets
# i = 1
# for source,cible in random.sample(list(itertools.combinations(range(nb_objets),2)),nb_morph): # ajout des morphismes
# self.ajouter_morphisme(Morphisme(source,cible,str(i)))
# i += 1
# if retirer_obj_isoles:
# self.retirer_noeuds_isoles()
# diag = DiagrammeIdentite(self)
# diag.faire_commuter()
def retirer_noeuds_isoles(self):
self.supprimer_objets([obj for obj in self.objets if len(self.morph_sortants[obj]) == len(self.morph_entrants[obj]) == 0])
# def retirer_noeuds_isoles(self):
# self.supprimer_objets([obj for obj in self.objets if len(self.morph_sortants[obj]) == len(self.morph_entrants[obj]) == 0])
class DiagrammeAlea(Diagramme):
def __init__(self, cat_cible, nb_objets_restants=None, retirer_objets_isoles=True):
"""nb_objets_restants entre 0 et len(cat_cible.objets)
retirer_objets_isoles n'est pas considéré s'il n'y a que des objets isolés"""
if nb_objets_restants == None:
nb_objets_restants = random.randint(1,len(cat_cible.objets))
cat_source = copy.copy(cat_cible)
obj_a_suppr = random.sample(cat_cible.objets,len(cat_cible.objets)-nb_objets_restants)
print(obj_a_suppr)
cat_source.supprimer_objets(obj_a_suppr)
if retirer_objets_isoles and len([obj for obj in cat_source.objets if not(len(cat_source.morph_sortants[obj]) == len(cat_source.morph_entrants[obj]) == 0)]) != 0:
cat_source.supprimer_objets([obj for obj in cat_source.objets if len(cat_source.morph_sortants[obj]) == len(cat_source.morph_entrants[obj]) == 0])
Diagramme.__init__(self,cat_source,cat_cible,{o:o for o in cat_source.objets},{m:m for m in cat_source.morphismes})
# class DiagrammeAlea(Diagramme):
# def __init__(self, cat_cible, nb_objets_restants=None, retirer_objets_isoles=True):
# """nb_objets_restants entre 0 et len(cat_cible.objets)
# retirer_objets_isoles n'est pas considéré s'il n'y a que des objets isolés"""
# if nb_objets_restants == None:
# nb_objets_restants = random.randint(1,len(cat_cible.objets))
# cat_source = copy.copy(cat_cible)
# obj_a_suppr = random.sample(cat_cible.objets,len(cat_cible.objets)-nb_objets_restants)
# print(obj_a_suppr)
# cat_source.supprimer_objets(obj_a_suppr)
# if retirer_objets_isoles and len([obj for obj in cat_source.objets if not(len(cat_source.morph_sortants[obj]) == len(cat_source.morph_entrants[obj]) == 0)]) != 0:
# cat_source.supprimer_objets([obj for obj in cat_source.objets if len(cat_source.morph_sortants[obj]) == len(cat_source.morph_entrants[obj]) == 0])
# Diagramme.__init__(self,cat_source,cat_cible,{o:o for o in cat_source.objets},{m:m for m in cat_source.morphismes})
def main():
random.seed(2579)
# c = CategorieAlea()
# c.transformer_graphviz(complet=False)
# from CategoriePreordre import CategoriePreordre
# c2 = CategoriePreordre(c)
# c2.transformer_graphviz()
# c.pretty_print_loi_de_composition(destination="graphviz/loi_de_comp.txt")
# c.verifier_coherence()
# c2.verifier_coherence()
for i in range(10):
print("debut creation")
c = CategorieAleaPreOrdreAcyclique(20,0.2)
print("fin creation")
c.transformer_graphviz(complet=False)
c.transformer_graphviz()
test_GrapheCompositionAleatoire()
......
from Categorie import Categorie
from Morphisme import Morphisme
from copy import copy
import itertools
from config import *
......@@ -16,14 +17,15 @@ class CategorieLibre(Categorie):
- morph_entrants(self, cible) : renvoie l'ensemble des morphismes entrants dans la cible
ces deux méthodes définissent le graphe sous-jacent avec les objets de la catégorie
"""
def __init__(self, nom="Categorie libre"):
nb_viz = 0
def __init__(self, nom:str="Categorie libre"):
Categorie.__init__(self,nom)
def fleches_elem(self, source, cible, inclure_id = True):
def fleches_elem(self, source:any, cible:any, inclure_id:bool = True) -> set:
"""
Renvoie la liste de flèches élémentaires (flèches du graphe sous-jacent de source à cible).
Si inclure_id=False, alors on exclut les identités.
Renvoie la liste de flèches élémentaires (flèches du graphe sous-jacent de `source` à `cible`).
Si `inclure_id`=False, alors on exclut les identités.
"""
if not inclure_id:
return self.morph_sortants(source)&self.morph_entrants(cible)-{self.identite(source)}
......@@ -50,7 +52,7 @@ 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,cible):
def __call__(self, source:any, cible:any) -> 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}
C({a_i},b) renvoie l'ensemble des flèches d'un élément de {a_i} vers b
......@@ -67,9 +69,9 @@ class CategorieLibre(Categorie):
#maintenant tout est de la forme {a_i} vers {b_i}
return {morph for a in source for b in cible for morph in self.enumerer_composees(a,b)}
def enumerer_composees_sans_cycle(self, source, cible, noeuds_deja_visites=tuple()):
def enumerer_composees_sans_cycle(self, source