Implémentation complète de la persistance pour FrequencyFilter

Ajout de la fonctionnalité de sauvegarde et de chargement pour FrequencyFilter en utilisant le KmerSetGroup sous-jacent.

- Nouvelle méthode Save() pour enregistrer le filtre dans un répertoire avec formatage des métadonnées
- Nouvelle méthode LoadFrequencyFilter() pour charger un filtre depuis un répertoire
- Initialisation des métadonnées lors de la création du filtre
- Optimisation des méthodes Union() et Intersect() du KmerSetGroup
- Mise à jour du commit hash
This commit is contained in:
Eric Coissac
2026-02-05 16:26:00 +01:00
parent 09ac15a76b
commit 12ca62b06a
3 changed files with 123 additions and 19 deletions

View File

@@ -16,10 +16,24 @@ type FrequencyFilter struct {
// NewFrequencyFilter crée un nouveau filtre par fréquence // NewFrequencyFilter crée un nouveau filtre par fréquence
// minFreq: nombre minimum d'occurrences requises (v) // minFreq: nombre minimum d'occurrences requises (v)
func NewFrequencyFilter(k, minFreq int) *FrequencyFilter { func NewFrequencyFilter(k, minFreq int) *FrequencyFilter {
return &FrequencyFilter{ ff := &FrequencyFilter{
KmerSetGroup: NewKmerSetGroup(k, minFreq), KmerSetGroup: NewKmerSetGroup(k, minFreq),
MinFreq: minFreq, MinFreq: minFreq,
} }
// Initialiser les métadonnées de groupe
ff.SetAttribute("type", "FrequencyFilter")
ff.SetAttribute("min_freq", minFreq)
// Initialiser les métadonnées de chaque niveau
for i := 0; i < minFreq; i++ {
level := ff.Get(i)
level.SetAttribute("level", i)
level.SetAttribute("min_occurrences", i+1)
level.SetId(fmt.Sprintf("level_%d", i))
}
return ff
} }
// AddSequence ajoute tous les k-mers d'une séquence au filtre // AddSequence ajoute tous les k-mers d'une séquence au filtre
@@ -164,17 +178,67 @@ func (ff *FrequencyFilter) AddSequences(sequences *obiseq.BioSequenceSlice) {
// PERSISTANCE // PERSISTANCE
// ================================== // ==================================
// Save sauvegarde le filtre sur disque // Save sauvegarde le FrequencyFilter dans un répertoire
func (ff *FrequencyFilter) Save(path string) error { // Utilise le format de sérialisation du KmerSetGroup sous-jacent
// TODO: implémenter la sérialisation // Les métadonnées incluent le type "FrequencyFilter" et min_freq
// Pour chaque bitmap: bitmap.WriteTo(writer) //
return nil // Format:
// - directory/metadata.{toml,yaml,json} - métadonnées du filtre
// - directory/set_0.roaring - k-mers vus ≥1 fois
// - directory/set_1.roaring - k-mers vus ≥2 fois
// - ...
// - directory/set_{minFreq-1}.roaring - k-mers vus ≥minFreq fois
//
// Parameters:
// - directory: répertoire de destination
// - format: format des métadonnées (FormatTOML, FormatYAML, FormatJSON)
//
// Example:
//
// err := ff.Save("./my_filter", obikmer.FormatTOML)
func (ff *FrequencyFilter) Save(directory string, format MetadataFormat) error {
// Déléguer à KmerSetGroup qui gère déjà tout
return ff.KmerSetGroup.Save(directory, format)
} }
// Load charge le filtre depuis le disque // LoadFrequencyFilter charge un FrequencyFilter depuis un répertoire
func (ff *FrequencyFilter) Load(path string) error { // Vérifie que les métadonnées correspondent à un FrequencyFilter
// TODO: implémenter la désérialisation //
return nil // Parameters:
// - directory: répertoire source
//
// Returns:
// - *FrequencyFilter: le filtre chargé
// - error: erreur si le chargement échoue ou si ce n'est pas un FrequencyFilter
//
// Example:
//
// ff, err := obikmer.LoadFrequencyFilter("./my_filter")
func LoadFrequencyFilter(directory string) (*FrequencyFilter, error) {
// Charger le KmerSetGroup
ksg, err := LoadKmerSetGroup(directory)
if err != nil {
return nil, err
}
// Vérifier que c'est bien un FrequencyFilter
if typeAttr, ok := ksg.GetAttribute("type"); !ok || typeAttr != "FrequencyFilter" {
return nil, fmt.Errorf("loaded data is not a FrequencyFilter (type=%v)", typeAttr)
}
// Récupérer min_freq
minFreqAttr, ok := ksg.GetIntAttribute("min_freq")
if !ok {
return nil, fmt.Errorf("FrequencyFilter missing min_freq attribute")
}
// Créer le FrequencyFilter
ff := &FrequencyFilter{
KmerSetGroup: ksg,
MinFreq: minFreqAttr,
}
return ff, nil
} }
// ================================== // ==================================

View File

@@ -145,29 +145,69 @@ func (ksg *KmerSetGroup) AddSequences(sequences *obiseq.BioSequenceSlice, index
} }
// Union retourne l'union de tous les KmerSet du groupe // Union retourne l'union de tous les KmerSet du groupe
// Optimisation: part du plus grand ensemble pour minimiser les opérations
func (ksg *KmerSetGroup) Union() *KmerSet { func (ksg *KmerSetGroup) Union() *KmerSet {
if len(ksg.sets) == 0 { if len(ksg.sets) == 0 {
return NewKmerSet(ksg.k) return NewKmerSet(ksg.k)
} }
result := ksg.sets[0].Copy() if len(ksg.sets) == 1 {
for i := 1; i < len(ksg.sets); i++ { return ksg.sets[0].Copy()
result = result.Union(ksg.sets[i])
} }
return result
// Trouver l'index du plus grand ensemble (celui avec le plus de k-mers)
maxIdx := 0
maxCard := ksg.sets[0].Len()
for i := 1; i < len(ksg.sets); i++ {
card := ksg.sets[i].Len()
if card > maxCard {
maxCard = card
maxIdx = i
}
}
// Copier le plus grand ensemble et faire les unions in-place
result := ksg.sets[maxIdx].bitmap.Clone()
for i := 0; i < len(ksg.sets); i++ {
if i != maxIdx {
result.Or(ksg.sets[i].bitmap)
}
}
return NewKmerSetFromBitmap(ksg.k, result)
} }
// Intersect retourne l'intersection de tous les KmerSet du groupe // Intersect retourne l'intersection de tous les KmerSet du groupe
// Optimisation: part du plus petit ensemble pour minimiser les opérations
func (ksg *KmerSetGroup) Intersect() *KmerSet { func (ksg *KmerSetGroup) Intersect() *KmerSet {
if len(ksg.sets) == 0 { if len(ksg.sets) == 0 {
return NewKmerSet(ksg.k) return NewKmerSet(ksg.k)
} }
result := ksg.sets[0].Copy() if len(ksg.sets) == 1 {
for i := 1; i < len(ksg.sets); i++ { return ksg.sets[0].Copy()
result = result.Intersect(ksg.sets[i])
} }
return result
// Trouver l'index du plus petit ensemble (celui avec le moins de k-mers)
minIdx := 0
minCard := ksg.sets[0].Len()
for i := 1; i < len(ksg.sets); i++ {
card := ksg.sets[i].Len()
if card < minCard {
minCard = card
minIdx = i
}
}
// Copier le plus petit ensemble et faire les intersections in-place
result := ksg.sets[minIdx].bitmap.Clone()
for i := 0; i < len(ksg.sets); i++ {
if i != minIdx {
result.And(ksg.sets[i].bitmap)
}
}
return NewKmerSetFromBitmap(ksg.k, result)
} }
// Stats retourne des statistiques pour chaque KmerSet du groupe // Stats retourne des statistiques pour chaque KmerSet du groupe

View File

@@ -8,7 +8,7 @@ import (
// corresponds to the last commit, and not the one when the file will be // corresponds to the last commit, and not the one when the file will be
// commited // commited
var _Commit = "16f72e6" var _Commit = "09ac15a"
var _Version = "Release 4.4.0" var _Version = "Release 4.4.0"
// Version returns the version of the obitools package. // Version returns the version of the obitools package.