diff --git a/tp_2_miso_mphf.py b/tp_2_miso_mphf.py index 9dc47e7d7ae921ee78e4194856a535b3f9ed6271..c876bb7f1382c92b487dd017e7ffe05ef4e43dbf 100644 --- a/tp_2_miso_mphf.py +++ b/tp_2_miso_mphf.py @@ -27,7 +27,7 @@ def construction_mphf(set_kmer, n, gamma=2, nb_niveaux=3): >>> mphf = construction_mphf(set_kmer, n) >>> len(mphf) == n True - >>> all(0 <= mphf[i] < n for i in range(n)) + >>> all(0 <= pair[1] < n for pair in mphf) True >>> len(mphf) == n True @@ -39,64 +39,58 @@ def construction_mphf(set_kmer, n, gamma=2, nb_niveaux=3): # Parcours des niveaux pour placer les k-mers for _ in range(nb_niveaux): - if set_kmer_courant: + if len(set_kmer_courant) > 0: l = len(set_kmer_courant) - # On initialise le tableau principal avec -1 (case vide) tableau_principal = [-1] * (gamma * l) for kmer in set_kmer_courant: # Hacher le k-mer pour obtenir un entier positif h = abs(hash(kmer)) - # Récupérer l'adresse dans le tableau (taille = gamma * l) + # Récupérer l'adresse adresse = h % (gamma * l) # Si la case est déjà occupée, on ajoute le k-mer aux collisions if tableau_principal[adresse] != -1: collision.add(kmer) else: - # Sinon, on écrit le k-mer dans la case - tableau_principal[adresse] = kmer - tableaux.append(tableau_principal) # On sauvegarde le tableau de ce niveau - # Les k-mers en collision seront traités au niveau suivant - set_kmer_courant = collision.copy() - collision = set() + # Sinon, écrire le hash à l'adresse dans le tableau principal + tableau_principal[adresse] = h + + tableaux.append(tableau_principal) # On ajoute le tableau principal à la liste des tableaux + set_kmer_courant = collision.copy() # Les k-mers en collision seront traités au niveau suivant + collision = set() # Réinitialiser l'ensemble des collisions pour le prochain niveau # Construction de la MPHF - # On concatène tous les tableaux pour former le grand_tableau + + mphf = [] grand_tableau = [] for tableau in tableaux: - grand_tableau.extend(tableau) + grand_tableau.extend( + tableau) # On concatène tous les tableaux pour obtenir un grand tableau regroupant tous les indices calculés - # Création d'un mapping temporaire kmer -> indice (non normalisé) - mp_dict = {} - # Pour chaque k-mer de l'ensemble initial : + rangs = [] + max_rang = 0 + i = 0 for kmer in set_kmer: - if kmer in grand_tableau: - # On récupère l'indice de la première occurrence du k-mer dans grand_tableau - pos = grand_tableau.index(kmer) - # Le "rang" est le nombre d'apparitions du k-mer jusqu'à cette position (on commence à 0) - rang = grand_tableau[:pos + 1].count(kmer) - 1 - mp_dict[kmer] = rang - # Pour les k-mers qui n'ont pas été placés (collision persistante), on leur affecte des indices uniques - max_rang = max(mp_dict.values()) if mp_dict else -1 - for kmer in set_kmer: - if kmer not in mp_dict: - max_rang += 1 - mp_dict[kmer] = max_rang + # Hacher le k-mer pour obtenir un entier positif + h = abs(hash(kmer)) + # Si le hash est présent dans le grand_tableau, c'est qu'il a été placé sans collision + if h in grand_tableau: + # Récupérer l'index de la première occurrence de h dans grand_tableau + index = grand_tableau.index(h) + # Récupérer le rang en comptant le nombre d'occurrences de h jusqu'à cet index + rang = grand_tableau[:index + 1].count(h) + # Ajouter à la MPHF la paire [h, rang] + mphf.append([h, rang]) + # Mettre à jour le rang maximum rencontré + if rang > max_rang: + max_rang = rang + + for kmer in set_kmer_courant: # gestion des collisions: expliquer les 3 lignes du dessous + max_rang += 1 + h = abs(hash(kmer)) + mphf.append([h, max_rang]) - # Normalisation de la MPHF : - # On souhaite obtenir une bijection de set_kmer sur {0, ..., n-1}. - # Pour cela, on trie les k-mers (ordre arbitraire mais fixe) et on leur affecte des indices séquentiels. - sorted_kmers = sorted(list(set_kmer)) - normalized = {} - for idx, kmer in enumerate(sorted_kmers): - normalized[kmer] = idx - # On construit la MPHF sous forme de liste d'entiers correspondant aux indices normalisés, - # dans l'ordre du tri (ainsi, la i-ème case correspond au k-mer sorted_kmers[i]). - mphf = [normalized[kmer] for kmer in sorted_kmers] return mphf - - - def get_hash_mphf(mphf, kmer): """ Calcule le hash d'un k-mer à l'aide d'une fonction de hachage minimale parfaite (MPHF). @@ -117,7 +111,14 @@ def get_hash_mphf(mphf, kmer): >>> 0 <= hash_mphf < n True """ - return int(kmer) + # Calculer le hash positif du k-mer + h = abs(hash(kmer)) + # Parcourir la MPHF pour trouver le k-mer correspondant et retourner son indice (rang) + for entry in mphf: + if entry[0] == h: + return entry[1] + # Si aucun k-mer correspondant n'est trouvé, lever une exception + raise ValueError("K-mer non trouvé dans la MPHF") def create_hash_table(set_kmer, n): @@ -145,12 +146,12 @@ def create_hash_table(set_kmer, n): mphf = construction_mphf(set_kmer, n) # Initialiser une table de taille n (liste de n cases) table = [None] * n - # Ici, nous utilisons l'ordre trié pour affecter l'adresse : le perfect hash d'un k-mer - # est sa position dans l'ordre trié. + # écrire les kmers aux adresses du tableau données par la mphf sorted_kmers = sorted(list(set_kmer)) for kmer in set_kmer: index = sorted_kmers.index(kmer) table[index] = kmer + # retourner le tableau et la mphf return table, mphf