Commit 9f611e88 authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

La transformation de Diagramme est terminée

parent e680a974
......@@ -38,8 +38,8 @@ class Categorie:
def __init__(self,nom = "Catégorie"):
assert(type(nom) is str)
self.nom = nom
self.objets = set()
self.__morphismes = set()
self.objets = frozenset()
self.__morphismes = frozenset()
def __call__(self,*args):
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)")
......@@ -47,6 +47,12 @@ class Categorie:
def identite(self,objet):
NotImplementedError("Les categories filles doivent implementer cette methode.")
def __eq__(self,other):
return self.nom == other.nom and self.objets == other.objets and self.__morphismes == other.__morphismes
def __hash__(self):
return hash((self.nom,self.objets,self.__morphismes))
def __str__(self):
return self.nom
......@@ -67,7 +73,7 @@ class Categorie:
self.verifier_coherence()
if issubclass(type(other),Morphisme):
self.ajouter_morphisme(other)
elif type(other) in {set,list,tuple}:
elif type(other) in {frozenset,set,list,tuple}:
for elem in other:
self += elem
else:
......@@ -88,7 +94,7 @@ class Categorie:
self.supprimer_objet(other)
elif other in self.__morphismes:
self.supprimer_morphisme(other)
elif type(other) in {set,list,tuple}:
elif type(other) in {frozenset,set,list,tuple}:
for elem in other:
self -= elem
else:
......@@ -115,10 +121,6 @@ class Categorie:
def verifier_coherence(self):
"""Vérifie la cohérence de la structure (tous les axiomes des catégories sont vérifiés)."""
## 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):
raise Exception("Incoherence Categorie : Trop d'identites : " + ','.join(map(str, ident)))
## On vérifie que les objets et les morphismes sont des ensembles disjoints (un objet ne peut pas être un morphisme en même temps)
if len(self.objets&self.__morphismes) > 0:
......@@ -138,8 +140,9 @@ class Categorie:
## On vérifie l'associativité
for m1,m2,m3 in itertools.product(self.__morphismes,repeat=3):
if (m1@m2)@m3 != m1@(m2@m3):
raise Exception("Incoherence Categorie : associativite pas respectee pour "+str(m3)+", "+str(m2)+", "+str(m1))
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))
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.
......@@ -202,5 +205,44 @@ class SousCategoriePleine(Categorie):
self += objets
self += categorie(objets,objets)
class CategorieDiscrete(Categorie):
"""
Catégorie discrète : catégorie dont les seuls morphismes sont des identités.
"""
class Identite(Morphisme):
def __init__(self,objet):
Morphisme.__init__(self,objet,objet,"Id"+str(objet),True)
def __matmul__(self,other):
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):
return type(other) == CategorieDiscrete.Identite and self.source == other.source
def __hash__(self):
return hash(self.source)
def __call__(self,*args):
if len(args) == 1:
# on doit renvoyer l'identité de a
return CategorieDiscrete.Identite(args[0])
if len(args) > 2:
raise Exception("Categorie __call__ : plus de deux arguments passés")
source,cible = args
if source in self.objets:
# source est de type a
source = {source}
if cible in self.objets:
# cible est de type b
cible = {cible}
#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):
return CategorieDiscrete.Identite(objet)
\ No newline at end of file
......@@ -121,7 +121,22 @@ class CategorieAleaPreOrdreAcyclique(Categorie):
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})
def main():
random.seed(2579)
# c = CategorieAlea()
......
......@@ -26,12 +26,14 @@ class CategorieLibre(Categorie):
Si inclure_id=False, alors on exclut les identités.
"""
if not inclure_id:
return self.morph_sortants(source)&self.morph_entrants(cible)-{self.identites[source]}
return self.morph_sortants(source)&self.morph_entrants(cible)-{self.identite(source)}
return self.morph_sortants(source)&self.morph_entrants(cible)
def verifier_coherence(self):
"""Vérifie la cohérence de la structure du graphe de composition sous-jacent."""
# on vérifie que tous les morphismes de la catégorie sont dans les morph_entrants et morph_sortants
Categorie.verifier_coherence(self)
for morph in self(self.objets,self.objets):
if len(morph) == 1:
if morph not in self.morph_sortants(morph.source):
......@@ -40,13 +42,13 @@ class CategorieLibre(Categorie):
raise Exception("Incoherence CategorieLibre : le morphisme "+str(morph)+" n'entre pas dans "+str(morph.cible))
# on vérifie que tous les morphismes entrants et sortants sont des morphismes
for obj in self.objets:
if len(self.morph_sortants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1}) > 0:
raise Exception("Incoherence CategorieLibre : le(s) morphisme(s) sortant(s) "+str(self.morph_sortants(obj)-self.morphismes)+\
"n'est pas un morphisme de la catégorie")
if len(self.morph_entrants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1}) > 0:
raise Exception("Incoherence CategorieLibre : le(s) morphisme(s) entrant(s) "+str(self.morph_entrants(obj)-self.morphismes)+\
"n'est pas un morphisme de la catégorie")
# for obj in self.objets:
# if len(self.morph_sortants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1}) > 0:
# raise Exception("Incoherence CategorieLibre : le(s) morphisme(s) sortant(s) "+str(self.morph_sortants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1})+\
# "n'est pas un morphisme de la catégorie")
# if len(self.morph_entrants(obj)-{m for m in self(self.objets,self.objets) if len(m) == 1}) > 0:
# 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,*args):
"""Soit C est une catégorie:
......@@ -79,15 +81,15 @@ class CategorieLibre(Categorie):
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).
"""
if source == cible:
return {self.identite(source)}
return frozenset({self.identite(source)})
if source not in noeuds_deja_visites:
noeuds_deja_visites = noeuds_deja_visites + (source,)
composees_resultat = set()
composees_resultat = frozenset()
for morph in self.morph_sortants(source):
for composition_candidate in self.enumerer_composees_sans_cycle(morph.cible, cible, noeuds_deja_visites):
composees_resultat |= {composition_candidate@morph}
return composees_resultat
return set()
return frozenset()
def enumerer_chemins_sans_cycle(self, source, cible, noeuds_deja_visites=tuple()):
"""
......@@ -97,12 +99,12 @@ class CategorieLibre(Categorie):
return {(cible,)}
if source not in noeuds_deja_visites:
noeuds_deja_visites = noeuds_deja_visites + (source,)
chemins_resultat = set()
chemins_resultat = frozenset()
for morph in self.morph_sortants(source):
for chemin_candidat in self.enumerer_chemins_sans_cycle(morph.cible, cible, noeuds_deja_visites):
chemins_resultat |= {(source,)+chemin_candidat}
return chemins_resultat
return set()
return frozenset()
def existe_composee(self,source,cible, noeuds_deja_visites=tuple()):
"""Trouve une composée de source à cible si elle existe, renvoie None sinon"""
......@@ -119,7 +121,7 @@ class CategorieLibre(Categorie):
def trouver_cycles_minimaux(self, objet, inclure_id=True):
"""Renvoie tous les cycles minimaux de morphismes élémentaires (qui ne contiennent aucun cycle)
de objet à objet (à l'exception de l'identité si iclure_id = False)."""
cycles = set()
cycles = frozenset()
for morph_pred in self.morph_entrants(objet):
pred = morph_pred.source
cycles_tronques = self.enumerer_composees_sans_cycle(objet, pred)
......@@ -161,18 +163,18 @@ class CategorieLibre(Categorie):
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."""
chemins_sans_cycles = self.enumerer_chemins_sans_cycle(source, cible)
noeuds = list(set([e for chemin in chemins_sans_cycles for e in chemin]))
noeuds = list(frozenset([e for chemin in chemins_sans_cycles for e in chemin]))
cycles = {x: self.enumerer_cycles(x) for x in noeuds}
tous_les_chemins = set()
tous_les_chemins = frozenset()
for chemin in chemins_sans_cycles:
composees_obtenues = set(cycles[chemin[0]]) #on commence avec les cycles de la source du chemin
composees_obtenues = frozenset(cycles[chemin[0]]) #on commence avec les cycles de la source du chemin
for i in range(len(chemin)):
noeud = chemin[i]
if i < len(chemin)-1:
noeud_suivant = chemin[i+1]
for fleche in self.fleches_elem(noeud,noeud_suivant):
composees_obtenues = {fleche@composee for composee in composees_obtenues}
nouvelles_composees_obtenues = {fleche@composee for composee in composees_obtenues}
composees_obtenues = copy(nouvelles_composees_obtenues)
if len(chemin) > 1: #pour que noeud_suivant soit defini
for composee in copy(composees_obtenues):
for cycle in cycles[noeud_suivant]:
......
This diff is collapsed.
......@@ -5,6 +5,7 @@ from config import *
if GRAPHVIZ_ENABLED:
from graphviz import Digraph
import itertools
import functools
import Morphisme
import copy
from collections import defaultdict
......@@ -14,83 +15,76 @@ class Foncteur(Morphisme.Morphisme):
nb_viz = 0
def __init__(self, categorie_source, categorie_cible, application_objets, application_morphismes, representant = None, is_identite = False):
"""application_objets doit être complète, application_morphismes doit spécifier les images de chaque morphisme du squelette.
L'image des composées est par construction la composée des images"""
def __init__(self, categorie_source, categorie_cible, application_objets, application_morphismes, representant = None):
"""
application_objets doit être complète,
application_morphismes doit spécifier les images de chaque morphisme générateur (pas besoin de spécifier les images des identités).
L'image des composées est par construction la composée des images
"""
is_identite = categorie_cible == categorie_source and\
all([obj == application_objets[obj] for obj in application_objets]) and\
all([morph == application_morphismes[morph] for morph in application_morphismes])
Morphisme.Morphisme.__init__(self,categorie_source,categorie_cible,representant,is_identite)
self.cat_source = categorie_source
self.cat_cible = categorie_cible
self.app_objets = application_objets
self.app_morph = application_morphismes
for obj in self.cat_source.objets:
self.app_morph[self.cat_source.identites[obj]] = self.cat_cible.identites[self.app_objets[obj]]
self.verifier_coherence()
self._app_objets = application_objets
self._app_morph = application_morphismes
for obj in self.source.objets:
self._app_morph[self.source.identite(obj)] = self.cible.identite(self._app_objets[obj])
if TOUJOURS_VERIFIER_COHERENCE:
self.verifier_coherence()
def __eq__(self,other):
if not issubclass(type(other),type(self)):
return False
return self.cat_source == other.cat_source and self.cat_cible == other.cat_cible\
and self.app_objets == other.app_objets and self.app_morph == other.app_morph
return self.source == other.source and self.cible == other.cible\
and self._app_objets == other._app_objets and self._app_morph == other._app_morph
def __hash__(self):
return hash((self.source,self.cible,frozenset(self._app_objets.items()),frozenset(self._app_morph.items())))
__hash__ = Morphisme.Morphisme.__hash__
def __matmul__(self,other):
"""
self @ other
"""
if not issubclass(type(other),Foncteur):
raise Exception("Composition d'un foncteur avec un morphisme de type different : "+str(self)+" o "+str(other))
return Foncteur(other.source,self.cible,{obj:self(other(obj)) for obj in other.source.objets},\
{morph:self(other(morph)) for morph in other._app_morph},representant=str(self)+'o'+str(other))
def as_diagram(self):
import Diagramme
return Diagramme.Diagramme(self.cat_source,self.cat_cible,self.app_objets,self.app_morph)
return Diagramme.Diagramme(self.source,self.cible,self._app_objets,self._app_morph)
def verifier_coherence(self):
# Foncteur.transformer_graphviz(self,"graphviz/ErreurFoncteur")
for obj in self.app_objets:
if obj in self.app_morph:
for obj in self._app_objets:
if obj in self._app_morph:
raise Exception("Incoherence foncteur : "+str(obj)+" est a la fois un objet et un morphisme de la categorie source")
for o in self.cat_source.objets:
if o not in self.app_objets:
for o in self.source.objets:
if o not in self._app_objets:
raise Exception("Incoherence foncteur : l'application d'objet n'est pas une application, "+str(o)+" n'a pas d'image")
for m in self.cat_source.morphismes:
if m not in self.app_morph:
raise Exception("Incoherence foncteur : l'application de morphismes n'est pas une application, "+str(m)+" n'a pas d'image")
##respect de l'identite
for objet in self.cat_source.objets:
if self.app_morph[self.cat_source.identites[objet]] != self.cat_cible.identites[self.app_objets[objet]]:
raise Exception("Incoherence foncteur : l'image de l'identite de "+str(objet)+" ("+str(self.cat_source.identites[objet])+\
") n'est pas l'identite de l'image de l'objet ("+str(self.cat_cible.identites[self.app_objets[objet]])+")")
for objet in self.source.objets:
if self._app_morph[self.source.identite(objet)] != self.cible.identite(self._app_objets[objet]):
raise Exception("Incoherence foncteur : l'image de l'identite de "+str(objet)+" ("+str(self.source.identite(objet))+\
") n'est pas l'identite de l'image de l'objet ("+str(self.cible.identite(self._app_objets[objet]))+")")
##Images de deux flèches composables sont composables
for couple in itertools.combinations(self.app_morph,2):
if couple[0].cible == couple[1].source:
if self.app_morph[couple[0]].cible != self.app_morph[couple[1]].source:
raise Exception("Incoherence foncteur : deux fleches composables n'ont pas leur image composables : "+str(couple[0])+
" composable avec "+str(couple[1])+" mais "+str(self.app_morph[couple[0]])+" pas composable avec "+str(self.app_morph[couple[1]]))
if couple[1].cible == couple[0].source:
if self.app_morph[couple[1]].cible != self.app_morph[couple[0]].source:
print(self.pretty_print())
raise Exception("Incoherence foncteur : deux fleches composables n'ont pas leur image composables : "+str(couple[1])+
" composable avec "+str(couple[0])+" mais "+str(self.app_morph[couple[1]])+" pas composable avec "+str(self.app_morph[couple[0]]))
for m1,m2 in itertools.product(self._app_morph,repeat=2):
if m1.cible == m2.source:
if self._app_morph[m1].cible != self._app_morph[m2].source:
raise Exception("Incoherence foncteur : deux fleches composables n'ont pas leur image composables : "+str(m1)+
" composable avec "+str(m2)+" mais "+str(self._app_morph[m1])+" pas composable avec "+str(self._app_morph[m2]))
def __call__(self,param):
if param in self.cat_source.objets:
return self.app_objets[param]
if param in self.cat_source.morphismes:
return self.app_morph[param]
return self.cat_cible.Composee(*[self.app_morph[morph] for morph in param])
def pretty_print(self):
result = "Categorie source\n ===========\n"+self.cat_source.pretty_print()+"Categorie cible\n ============ \n"+self.cat_cible.pretty_print()
result += "Application objets\n===========\n"
for obj in self.app_objets:
result += str(obj)+" => "+str(self.app_objets[obj])+"\n"
result += "Application morphismes\n===========\n"
for morph in self.app_morph:
result += str(morph)+" => "+str(self.app_morph[morph])+"\n"
return result
if param in self._app_objets:
return self._app_objets[param]
if param in self._app_morph:
return self._app_morph[param]
return functools.reduce(lambda x,y:x@y,[self._app_morph[morph] for morph in param])
def transformer_graphviz(self, destination=None, afficher_identites = False):
"""Permet de visualiser la catégorie avec graphviz"""
Foncteur.nb_viz += 1
if destination == None:
destination = "graphviz/foncteur"+str(Foncteur.nb_viz)
......@@ -102,64 +96,42 @@ class Foncteur(Morphisme.Morphisme):
graph.attr(concentrate="true" if GRAPHVIZ_CONCENTRATE_GRAPHS else "false")
graph.attr(label="Foncteur "+self.representant)
with graph.subgraph(name='cluster_0') as cluster:
cluster.attr(label=self.cat_source.nom)
cluster.attr(label=self.source.nom)
nodes = []
for o in self.cat_source.objets:
for o in self.source.objets:
cluster.node(str(o)+suffixe1_objet)
for morph in self.cat_source.morphismes:
for morph in self.source(self.source.objets,self.source.objets):
if not morph.is_identite or afficher_identites:
cluster.node(str(morph.representant)+suffixe1_morph,style="invis",shape="point")
graph.edge(str(morph.source)+suffixe1_objet,str(morph.representant)+suffixe1_morph,label=str(morph.representant)+suffixe1_morph,headclip="False",arrowhead="none")
graph.edge(str(morph.representant)+suffixe1_morph,str(morph.cible)+suffixe1_objet,tailclip="false")
cluster.node(str(morph)+suffixe1_morph,style="invis",shape="point")
graph.edge(str(morph.source)+suffixe1_objet,str(morph)+suffixe1_morph,label=str(morph)+suffixe1_morph,headclip="False",arrowhead="none")
graph.edge(str(morph)+suffixe1_morph,str(morph.cible)+suffixe1_objet,tailclip="false")
suffixe2_objet = "_2"
suffixe2_morph = "-2"
with graph.subgraph(name='cluster_1') as cluster:
cluster.attr(label=self.cat_cible.nom)
cluster.attr(label=self.cible.nom)
nodes = []
for o in self.cat_cible.objets:
for o in self.cible.objets:
cluster.node(str(o)+suffixe2_objet)
for morph in self.cat_cible.morphismes:
for morph in self.cible(self.cible.objets,self.cible.objets):
if not morph.is_identite or afficher_identites:
cluster.node(str(morph.representant)+suffixe2_morph,style="invis",shape="point")
graph.edge(str(morph.source)+suffixe2_objet,str(morph.representant)+suffixe2_morph,label=str(morph.representant)+suffixe2_morph,headclip="False",arrowhead="none")
graph.edge(str(morph.representant)+suffixe2_morph,str(morph.cible)+suffixe2_objet,tailclip="false")
cluster.node(str(morph)+suffixe2_morph,style="invis",shape="point")
graph.edge(str(morph.source)+suffixe2_objet,str(morph)+suffixe2_morph,label=str(morph)+suffixe2_morph,headclip="False",arrowhead="none")
graph.edge(str(morph)+suffixe2_morph,str(morph.cible)+suffixe2_objet,tailclip="false")
composees_ajoutees = []
for source in self.app_morph:
for source in self._app_morph:
if not source.is_identite or afficher_identites:
if self.app_morph[source] in self.cat_cible.morphismes:
graph.edge(str(source.representant)+suffixe1_morph,str(self.app_morph[source].representant)+suffixe2_morph,color="cyan")
elif self.app_morph[source] not in composees_ajoutees:
cluster.node(str(self.app_morph[source].representant)+suffixe2_morph,style="invis",shape="point")
composees_ajoutees += [self.app_morph[source]]
graph.edge(str(self.app_morph[source].source)+suffixe2_morph,str(self.app_morph[source].representant)+suffixe2_morph,label=str(self.app_morph[source].representant)+suffixe2_morph,headclip="False",arrowhead="none",color="grey77")
graph.edge(str(self.app_morph[source].representant)+suffixe2_morph,str(self.app_morph[source].cible)+suffixe2_morph,tailclip="false",color="grey77")
graph.edge(str(source.representant)+suffixe1_morph,str(self.app_morph[source].representant)+suffixe2_morph,color="cyan")
for source in self.app_objets:
graph.edge(str(source)+suffixe1_objet,str(self.app_objets[source])+suffixe2_objet,color="blue")
graph.edge(str(source)+suffixe1_morph,str(self._app_morph[source])+suffixe2_morph,color="cyan")
for source in self._app_objets:
graph.edge(str(source)+suffixe1_objet,str(self._app_objets[source])+suffixe2_objet,color="blue")
graph.render(destination)
if CLEAN_GRAPHVIZ_MODEL:
import os
os.remove(destination)
def categorie_image(self):
"""Renvoie la sous-catégorie image du foncteur."""
c = copy.copy(self.cat_cible)
for o in copy.copy(c.objets):
if o not in self.app_objets.values():
c.supprimer_objet(o)
for m in copy.copy(c.morphismes):
if m not in self.app_morph.values():
c.supprimer_morphisme(m)
return c
os.remove(destination)
def enumerer_cones(self,apex):
"""Renvoie une liste de cônes.
......@@ -219,47 +191,44 @@ class Foncteur(Morphisme.Morphisme):
if len(fleches) == 1:
return colim_candidate
return None
class Composition(Foncteur):
"""Le foncteur composition vu comme un foncteur et non comme un morphisme."""
def __init__(self,*foncteurs):
if len(foncteurs) == 0:
raise Exception("Composition de 0 foncteurs.")
compo_morph = Morphisme.Composition(*foncteurs)
s,c = compo_morph.source,compo_morph.cible
Foncteur.__init__(self,s,c,{e:compo_morph(e) for e in s.objets},{e:compo_morph(e) for e in s.morphismes})
def main():
from Categorie import Categorie
triangle = Categorie()
triangle.ajouter_objets(['A','B','C'])
def test_Foncteur():
from GrapheDeComposition import GC,MGC
triangle = GC()
triangle+= {'A','B','C'}
f1,g1= [Morphisme.Morphisme('A','B','f'),Morphisme.Morphisme('B','C','g')]
morphismes_triangle = f1,g1
triangle.ajouter_morphismes(morphismes_triangle)
f1,g1= [MGC('A','B','f'),MGC('B','C','g')]
triangle += {f1,g1}
carre = Categorie()
carre.ajouter_objets([1,2,3,4])
carre = GC()
carre += {1,2,3,4}
f,g,h,i = [Morphisme.Morphisme(1,2,'f'),Morphisme.Morphisme(2,4,'g'),Morphisme.Morphisme(1,3,'h'),Morphisme.Morphisme(3,4,'i')]
morphismes_carre = [f,g,h,i]
carre.ajouter_morphismes(morphismes_carre)
f,g,h,i = [MGC(1,2,'f'),MGC(2,4,'g'),MGC(1,3,'h'),MGC(3,4,'i')]
morphismes_carre = {f,g,h,i}
carre += morphismes_carre
pentagone = GC()
pentagone += set("VWXYZ")
a,b,c,d,e = [MGC('X','Y','a'),MGC('Y','Z','b'),MGC('W','V','c'),MGC('W','X','d'),MGC('V','Z','e')]
pentagone += {a,b,c,d,e}
pentagone.transformer_graphviz()
app_obj = {'A':1,'B':2,'C':4}
app_morph = {f1:f,g1:g}
diagramme = Foncteur(triangle, carre, app_obj, app_morph)
fonct1 = Foncteur(triangle, carre, app_obj, app_morph)
fonct1.transformer_graphviz()
print("F(A) = "+str(diagramme('A')))
print("F(B) = "+str(diagramme('B')))
print("F(C) = "+str(diagramme('C')))
print("F(f1) = "+str(diagramme(f1)))
print("F(g1of1) = "+str(diagramme(triangle.Composee(f1,g1))))
app_obj = {1:'W',2:'V',3:'Y',4:'Z'}
app_morph = {f:c,g:e,h:a@d,i:b}
fonct2 = Foncteur(carre,pentagone,app_obj,app_morph)
fonct2.transformer_graphviz()
print(diagramme.pretty_print())
diagramme.transformer_graphviz()
(fonct2@fonct1).transformer_graphviz()
def main():
test_Foncteur()
if __name__ == '__main__':
main()
\ No newline at end of file
......@@ -2,9 +2,11 @@ from Categorie import Categorie
from CategorieLibre import CategorieLibre
from Morphisme import Morphisme
from collections import defaultdict
from config import *
class MorphismeGrapheComposition(Morphisme):
class MorphismeGrapheDeComposition(Morphisme):
"""
MorphismeGrapheDeComposition peut être abrégé en MGC
Un morphisme dans un graphe de composition est un chemin dans ce graphe.
Deux morphismes peuvent être identifiés dans le graphe de composition.
Le chemin est représenté par un tuple de morphismes de graphe de composition.
......@@ -13,6 +15,18 @@ class MorphismeGrapheComposition(Morphisme):
loi_de_composition = dict() # on associe à certains chemins un autre chemin aussi long ou plus court
#{tuple de morphismes : tuple de morphismes}
def identifier_morphismes(morph1,morph2):
"""Identifie le morphisme1 au morphisme2."""
MorphismeGrapheDeComposition.loi_de_composition[morph1.__chemin] = morph2.__chemin
MorphismeGrapheDeComposition.verifier_coherence()
def verifier_coherence():
for chemin in MorphismeGrapheDeComposition.loi_de_composition:
if len(chemin) < len(MorphismeGrapheDeComposition.loi_de_composition[chemin]):
raise Exception("Loi de composition pas simplificatrice "+str(chemin)+" : "+str(MorphismeGrapheDeComposition.loi_de_composition[chemin]))
if chemin == MorphismeGrapheDeComposition.loi_de_composition[chemin]:
raise Exception("Entree de la loi de composition inutile "+str(chemin))
def __simplifier_chemin(morph1,morph2):
"""
On simplifie le chemin défini par la composition des deux morphismes grâce à la loi de composition.
......@@ -24,8 +38,8 @@ class MorphismeGrapheComposition(Morphisme):
for nb_morphismes_consideres in range(len(chemin), 1, -1):
for offset in range(0, len(chemin) - nb_morphismes_consideres + 1):
sous_composee = chemin[offset:offset + nb_morphismes_consideres]
if sous_composee in MorphismeGrapheComposition.loi_de_composition: # si il y a une simplification dans la loi de composition
chemin = chemin[0:offset] + (MorphismeGrapheComposition.loi_de_composition[sous_composee],)\
if sous_composee in MorphismeGrapheDeComposition.loi_de_composition: # si il y a une simplification dans la loi de composition
chemin = chemin[0:offset] + MorphismeGrapheDeComposition.loi_de_composition[sous_composee]\
+ chemin[offset + nb_morphismes_consideres:]
break
else:
......@@ -39,6 +53,8 @@ class MorphismeGrapheComposition(Morphisme):