Commit 53bce9a4 authored by Guillaume Sabbagh's avatar Guillaume Sabbagh
Browse files

Correction des Graphes de composition aléatoires pour que les compositions...

Correction des Graphes de composition aléatoires pour que les compositions puissent donner des identités, on utilise maintenant un autographe pour représenter la table de composition
parent c54c6b35
......@@ -12,6 +12,8 @@ from collections import defaultdict
from config import *
from typing import *
from pprint import pprint
if PROGRESS_BAR:
from tqdm import tqdm
......@@ -20,100 +22,78 @@ from graphviz import Digraph
class GrapheCompositionAleatoire(GC):
"""Construit un graphe de composition aléatoire."""
def __init__(self, nb_fleches:Union[int,None] = None, nb_tentatives_complexification_loi_de_compo:Union[int,None] = None,nom:str = "Catégorie Aléatoire"):
def __init__(self, nb_fleches:Union[int,None] = None, nb_tentatives_complexification_loi_de_compo:Union[int,None] = None, nb_tentatives_correction_composition:Union[Callable,int,None] = None, nom:str = "Catégorie Aléatoire"):
"""`nb_fleches` est le nombre de flèches élémentaires dans la catégorie aléatoire.
`nb_tentatives_complexification_loi_de_compo` détermine à quel point la loi de composition sera complexifiée,
si le nombre est faible on obtient une catégorie où les flèches sont isolées."""
si le nombre est faible on obtient une catégorie où les flèches sont isolées.
Par défaut, un nombre est tiré au hasard entre 0 et 10*`nb_fleches`
`nb_tentatives_correction_composition` détermine à quel point on pousse loin la recherche de correction de la loi de composition par
rapport à la contrainte de composition.
Si le nombre est faible, on aura quasiment aucun cycle, si le nombre est grand, la création de cycles et favorisée mais le temps de calcul est plus long.
Par défaut, un nombre est tiré au hasard à chaque fois entre 3 et 6."""
if nb_fleches == None:
nb_fleches = random.randint(1,20)
nb_fleches = 10
if nb_tentatives_complexification_loi_de_compo == None:
nb_tentatives_complexification_loi_de_compo = random.randint(0,10*nb_fleches)
GC.__init__(self,nom=nom)
class LoiDeComposition:
def __init__(self):
self.table = defaultdict(lambda:None)
for i in range(1,2*nb_fleches+1):
for j in range(1,2*nb_fleches+1):
self.table[(-j,i)] = self.table[(i,-j)] = i # les identités sont neutres
self.table[(-j,-j)] = -j
# une classe par source et target de flèches initiales (soit 2*nb_fleches classes initiales)
self.obj_vers_fleche = defaultdict(set) # {numero_objet: {fleche1,fleche2,...}}
self.fleche_vers_obj = dict() # {fleche:numero_obj}
for fleche in self.fleches:
self.obj_vers_fleche[fleche] |= {fleche,self.identite_source_fleche(fleche)}
self.obj_vers_fleche[fleche+nb_fleches] |= {fleche,self.identite_cible_fleche(fleche)}
self.fleche_vers_obj[fleche] = fleche
self.fleche_vers_obj[fleche+nb_fleches] = fleche+nb_fleches
self.fleche_vers_obj[self.identite_source_fleche(fleche)] = fleche
self.fleche_vers_obj[self.identite_cible_fleche(fleche)] = fleche+nb_fleches
print(nb_fleches)
print(self.obj_vers_fleche)
print(self.fleche_vers_obj)
@property
def fleches(self) -> list:
'''Renvoie la liste des flèches.'''
return list(range(1,nb_fleches+1))
def source_fleche(self,i:int) -> int:
'''Renvoie la représentation de la source de la flèche i.'''
assert(i != 0)
return self.fleche_vers_obj[abs(i)]
def cible_fleche(self,i:int) -> int:
'''Renvoie la représentation de la cible de la flèche i.'''
assert(i != 0)
if i > 0:
return self.fleche_vers_obj[i+nb_fleches]
else:
return self.fleche_vers_obj[abs(i)]
"""La loi de composition va suivre une structure d'auto-graphe : chaque morphisme a pour source et pour cible une auto-flèche identité."""
def __init__(self,nb_fleches):
self.table = defaultdict(lambda:None) #){ (i,j) : j o i}
#on initialise les identités
for i in range(nb_fleches):
self.table[(i+nb_fleches,i)] = i
self.table[(i+nb_fleches,i+nb_fleches)] = i+nb_fleches
self.table[(i,i+2*nb_fleches)] = i
self.table[(i+2*nb_fleches,i+2*nb_fleches)] = i+2*nb_fleches
self.source = dict() # dictionnaire qui associe à chaque flèche son identité source
for i in range(nb_fleches):
self.source[i] = i+nb_fleches
self.source[i+nb_fleches] = i+nb_fleches
self.source[i+2*nb_fleches] = i+2*nb_fleches
self.cible = dict() # dictionnaire qui associe à chaque flèche son identité cible
for i in range(nb_fleches):
self.cible[i] = i+2*nb_fleches
self.cible[i+nb_fleches] = i+nb_fleches
self.cible[i+2*nb_fleches] = i+2*nb_fleches
def identite_source_fleche(self,i:int) -> int:
'''Renvoie la représentation de la flèche identité sur la source de la flèche i.'''
return -abs(i)
def identifier_identites(self,id1:int,id2:int):
#on va identifier l'identité 1 à l'identité 2
assert(id1 in self.source.values() or id1 in self.cible.values())
assert(id2 in self.source.values() or id2 in self.cible.values())
if id1 != id2:
# on update les contraintes de composition de id1 à celles de id2
for i in range(nb_fleches):
if self.table[(id1,i)] != None:
assert(self.table[(id2,i)] == None)
self.table[(id2,i)] = i
if self.table[(i,id1)] != None:
assert(self.table[(i,id2)] == None)
self.table[(i,id2)] = i
# on supprime l'auto-flèche id1
del self.source[id1]
del self.cible[id1]
for i in range(nb_fleches):
if (id1,i) in self.table:
del self.table[(id1,i)]
if (i,id1) in self.table:
del self.table[(i,id1)]
# on remplace toutes les occurences de id1 par id2
for i in range(nb_fleches):
for j in range(nb_fleches):
if self.table[(i,j)] == id1:
self.table[(i,j)] = id2
if self.source[i] == id1:
self.source[i] = id2
if self.cible[i] == id1:
self.cible[i] = id2
def identite_cible_fleche(self,i:int) -> int:
'''Renvoie la représentation de la flèche identité sur la cible de la flèche i.'''
return -((abs(i)+nb_fleches-1)%(2*nb_fleches)+1)
def linker_sources_fleches(self,fleche1,fleche2):
classe1,classe2 = self.fleche_vers_obj[self.source_fleche(fleche1)],self.fleche_vers_obj[self.source_fleche(fleche2)]
if classe1 != classe2:
#on fusionne les deux classes dans classe1
for fleche in self.obj_vers_fleche[classe2]:
self.fleche_vers_obj[self.source_fleche(fleche)] = classe1
self.fleche_vers_obj[self.source_fleche(self.identite_source_fleche(fleche))] = classe1
self.fleche_vers_obj[self.cible_fleche(self.identite_source_fleche(fleche))] = classe1
self.obj_vers_fleche[classe1] |= self.obj_vers_fleche[classe2]
del self.obj_vers_fleche[classe2]
def linker_cibles_fleches(self,fleche1,fleche2):
classe1,classe2 = self.fleche_vers_obj[self.source_fleche(fleche1)],self.fleche_vers_obj[self.cible_fleche(fleche2)]
if classe1 != classe2:
#on fusionne les deux classes dans classe1
for fleche in self.obj_vers_fleche[classe2]:
self.fleche_vers_obj[self.cible_fleche(fleche)] = classe1
self.fleche_vers_obj[self.source_fleche(self.identite_cible_fleche(fleche))] = classe1
self.fleche_vers_obj[self.cible_fleche(self.identite_cible_fleche(fleche))] = classe1
self.obj_vers_fleche[classe1] |= self.obj_vers_fleche[classe2]
del self.obj_vers_fleche[classe2]
def linker_cible_source_fleches(self,fleche1,fleche2):
'''link la cible de `fleche1` à la source de `fleche2`'''
classe1,classe2 = self.fleche_vers_obj[self.cible_fleche(fleche1)],self.fleche_vers_obj[self.source_fleche(fleche2)]
if classe1 != classe2:
#on fusionne les deux classes dans classe1
for fleche in self.obj_vers_fleche[classe2]:
self.fleche_vers_obj[fleche] = classe1
self.fleche_vers_obj[self.source_fleche(self.identite_source_fleche(fleche))] = classe1
self.fleche_vers_obj[self.cible_fleche(self.identite_source_fleche(fleche))] = classe1
self.obj_vers_fleche[classe1] |= self.obj_vers_fleche[classe2]
del self.obj_vers_fleche[classe2]
@property
def fleches(self) -> set:
assert(set(self.source.keys()) == set(self.cible.keys()))
return set(self.source.keys())
def tentative_complexification(loi:LoiDeComposition,i:int,j:int,nb_tentatives:int = 3) -> Union[LoiDeComposition,None]:
"""Tente de complexifier la loi de composition en définissant la composition j o i.
......@@ -121,34 +101,33 @@ class GrapheCompositionAleatoire(GC):
nouvelle_loi = copy.deepcopy(loi)
if nouvelle_loi.table[(i,j)] != None:
return None
if nouvelle_loi.fleche_vers_obj[nouvelle_loi.source_fleche(i)] == nouvelle_loi.fleche_vers_obj[nouvelle_loi.cible_fleche(j)]:
# si la source de i et la cible de j sont dans la même classe d'équivalence
# une issue possible est j o i = Id
nouvelle_loi.table[(i,j)] = random.randint(0,nb_fleches)
if nouvelle_loi.table[(i,j)] == 0:
nouvelle_loi.table[(i,j)] = nouvelle_loi.identite_source_fleche(i)
else:
nouvelle_loi.table[(i,j)] = random.randint(1,nb_fleches)
nouvelle_loi.linker_sources_fleches(i,nouvelle_loi.table[(i,j)])
nouvelle_loi.linker_cibles_fleches(j,nouvelle_loi.table[(i,j)])
nouvelle_loi.linker_cible_source_fleches(i,j)
ij = random.choice(list(nouvelle_loi.fleches))
nouvelle_loi.table[(i,j)] = ij
nouvelle_loi.identifier_identites(nouvelle_loi.source[i],nouvelle_loi.source[ij])
nouvelle_loi.identifier_identites(nouvelle_loi.cible[j],nouvelle_loi.cible[ij])
nouvelle_loi.identifier_identites(nouvelle_loi.cible[i],nouvelle_loi.source[j])
while True:
for a,b,c in itertools.product(range(nb_fleches),repeat=3):
if nouvelle_loi.table[(a,b)] != None and nouvelle_loi.table[(b,c)] != None:
if nouvelle_loi.table[(nouvelle_loi.table[(a,b)],c)] != nouvelle_loi.table[(a,nouvelle_loi.table[(b,c)])]:
ab = nouvelle_loi.table[(a,b)]
bc = nouvelle_loi.table[(b,c)]
if ab != None and bc != None:
if nouvelle_loi.table[(ab,c)] != nouvelle_loi.table[(a,bc)]:
# l'associativité n'est pas vérifiée
if nouvelle_loi.table[(nouvelle_loi.table[(a,b)],c)] == None:
if nouvelle_loi.table[(ab,c)] == None:
#on peut corriger la table
nouvelle_loi.table[(nouvelle_loi.table[(a,b)],c)] = nouvelle_loi.table[(a,nouvelle_loi.table[(b,c)])]
nouvelle_loi.linker_sources_fleches(nouvelle_loi.table[(a,b)],nouvelle_loi.table[(a,nouvelle_loi.table[(b,c)])])
nouvelle_loi.linker_cibles_fleches(c,nouvelle_loi.table[(a,nouvelle_loi.table[(b,c)])])
nouvelle_loi.linker_cible_source_fleches(nouvelle_loi.table[(a,b)],c)
abc = nouvelle_loi.table[(a,bc)]
nouvelle_loi.table[(ab,c)] = abc
nouvelle_loi.identifier_identites(nouvelle_loi.source[ab],nouvelle_loi.source[abc])
nouvelle_loi.identifier_identites(nouvelle_loi.cible[c],nouvelle_loi.cible[abc])
nouvelle_loi.identifier_identites(nouvelle_loi.cible[ab],nouvelle_loi.source[c])
if nouvelle_loi.table[(a,nouvelle_loi.table[(b,c)])] == None:
#on peut corriger la table
nouvelle_loi.table[(a,nouvelle_loi.table[(b,c)])] = nouvelle_loi.table[(nouvelle_loi.table[(a,b)],c)]
nouvelle_loi.linker_sources_fleches(a,nouvelle_loi.table[(nouvelle_loi.table[(a,b)],c)])
nouvelle_loi.linker_cibles_fleches(nouvelle_loi.table[(b,c)],nouvelle_loi.table[(nouvelle_loi.table[(a,b)],c)])
nouvelle_loi.linker_cible_source_fleches(a,nouvelle_loi.table[(b,c)])
abc = nouvelle_loi.table[(ab,c)]
nouvelle_loi.table[(a,bc)] = abc
nouvelle_loi.identifier_identites(nouvelle_loi.source[a],nouvelle_loi.source[abc])
nouvelle_loi.identifier_identites(nouvelle_loi.cible[bc],nouvelle_loi.cible[abc])
nouvelle_loi.identifier_identites(nouvelle_loi.cible[a],nouvelle_loi.source[bc])
else:
# on peut pas corriger la table
return None
......@@ -158,69 +137,82 @@ class GrapheCompositionAleatoire(GC):
break
# ici l'associativité est vérifiée
# on doit checker si la composition est respectée
probleme_composition = True
while probleme_composition:
probleme_composition = False
for obj in nouvelle_loi.obj_vers_fleche:
for fleche1 in nouvelle_loi.obj_vers_fleche[obj]:
if fleche1 > nb_fleches:
for fleche2 in nouvelle_loi.obj_vers_fleche[obj]:
if 0 < fleche2 < nb_fleches:
# fleche1 et fleche2 sont censées être composables
if nouvelle_loi.table[(fleche1-nb_fleches,fleche2)] == None:
# problème de composition
probleme_composition = True
for k in range(nb_tentatives):
nouvelle_loi2 = tentative_complexification(nouvelle_loi,fleche1-nb_fleches,fleche2,nb_tentatives-1) #on tente de complexifier
if nouvelle_loi2 != None:
nouvelle_loi = nouvelle_loi2
break
else:
# on a pas réussi à complexifier
return None
break
while True: # tant qu'on a un problème de composition
for f,g in itertools.product(nouvelle_loi.fleches,repeat=2):
if nouvelle_loi.cible[f] == nouvelle_loi.source[g]:
# f et g sont censées être composables
if nouvelle_loi.table[(f,g)] == None:
# problème de composition
for k in range(nb_tentatives):
nouvelle_loi2 = tentative_complexification(nouvelle_loi,f,g,nb_tentatives-1) #on tente de complexifier
if nouvelle_loi2 != None:
nouvelle_loi = nouvelle_loi2
break
else:
continue
# on a pas réussi à complexifier
return None
break
else:
continue
else:
break
return nouvelle_loi
loi = LoiDeComposition()
loi = LoiDeComposition(nb_fleches)
if PROGRESS_BAR:
print("Création d'une catégorie aléatoire à "+str(nb_fleches)+" flèches ("+str(nb_tentatives_complexification_loi_de_compo)+' complexifications de la loi de composition)')
iterator = tqdm(range(nb_tentatives_complexification_loi_de_compo))
else:
iterator = range(nb_tentatives_complexification_loi_de_compo)
for tentative in iterator:
i,j = random.randint(1,nb_fleches),random.randint(1,nb_fleches)
loi2 = tentative_complexification(loi,i,j)
i,j = random.randint(0,nb_fleches-1),random.randint(0,nb_fleches-1)
if nb_tentatives_correction_composition == None:
loi2 = tentative_complexification(loi,i,j,random.randint(3,6))
elif type(nb_tentatives_correction_composition) is int:
loi2 = tentative_complexification(loi,i,j,nb_tentatives_correction_composition)
else:
loi2 = tentative_complexification(loi,i,j,nb_tentatives_correction_composition())
if loi2 != None:
loi = loi2
#on a une table de loi de composition aléatoire
for obj in loi.obj_vers_fleche:
self |= {obj}
#on va retirer les flèches qui peuvent être trouvées par composition de flèches élémentaires
fleches_composites = {loi.table[(i,j)] for i,j in itertools.product(loi.fleches,repeat=2) if loi.table[(i,j)] != None and loi.table[(i,j)] not in [i,j] and loi.table[(i,j)] > 0}
print(loi.fleches)
print(list(map(lambda x:str(loi.source_fleche(x)),loi.fleches)))
fleches = {i:MGC(loi.fleche_vers_obj[loi.source_fleche(i)],loi.fleche_vers_obj[loi.cible_fleche(i)]) for i in loi.fleches}
# on a une table de loi de composition aléatoire
# on ajoute un objet par identité
self |= {loi.source[fleche] for fleche in loi.source} | {loi.cible[fleche] for fleche in loi.cible}
# on créé les flèches
fleches = {i:MGC(loi.source[i],loi.cible[i]) for i in range(nb_fleches)}
# on les ajoute à la catégorie
self |= set(fleches.values())
self._fleches_elem = {fleches[i] for i in set(loi.fleches) - fleches_composites}
for i,j in itertools.product(loi.fleches,repeat=2):
#on fait les commutations
for i,j in itertools.product(range(nb_fleches),repeat=2):
if loi.table[(i,j)] != None:
if loi.table[(i,j)] < 0:
MGC.identifier_morphismes(fleches[j]@fleches[i],self.identite(loi.fleche_vers_obj[loi.source_fleche(i)]))
if loi.table[(i,j)] >= nb_fleches:
MGC.identifier_morphismes(fleches[j]@fleches[i],self.identite(loi.table[(i,j)]))
elif fleches[loi.table[(i,j)]] != fleches[j]@fleches[i]:
MGC.identifier_morphismes(fleches[j]@fleches[i],fleches[loi.table[(i,j)]])
# on définit les flèches élémentaires
self._fleches_elem = set(range(nb_fleches))
# on va retirer les flèches qui peuvent être trouvées par composition de flèches élémentaires
while True:
for k in self._fleches_elem:
for i,j in itertools.product(self._fleches_elem-{k},repeat=2):
if loi.table[(i,j)] == k: # k est composite
self._fleches_elem -= {k}
break
else:
continue
break
else:
break
self._fleches_elem = {fleches[i] for i in self._fleches_elem} # on récupère les flèches et non leur index
with open("out.csv","w") as f:
f.write(","+",".join(map(str,range(-2*nb_fleches,nb_fleches+1)))+"\n")
for a in range(-2*nb_fleches,nb_fleches+1):
f.write(","+",".join(map(str,range(nb_fleches*3)))+"\n")
for a in range(nb_fleches*3):
f.write(str(a))
for b in range(-2*nb_fleches,nb_fleches+1):
for b in range(nb_fleches*3):
f.write(','+str(loi.table[(a,b)]))
f.write('\n')
......@@ -353,7 +345,7 @@ def test_MonoideGC():
class FoncteurAleatoire(Foncteur):
"""Foncteur aléatoire sur une catégorie."""
def __new__(cls, categorie_indexante:Categorie = GrapheCompositionAleatoire(), categorie_cible:Categorie = GrapheCompositionAleatoire(), nom:str = None):
def __new__(cls, categorie_indexante:Categorie = None, categorie_cible:Categorie = None, nom:str = None):
if nom == None:
nom = "Foncteur aléatoire sur "+str(categorie_cible)
cat = CatFinies({categorie_indexante,categorie_cible}, "Catégorie des foncteurs pour génération de foncteur aléatoire")
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment