Refactor KmerSet and FrequencyFilter to use immutable K parameter and consistent Copy/Clone methods

This commit refactors the KmerSet and related structures to use an immutable K parameter and introduces consistent Copy methods instead of Clone. It also adds attribute API support for KmerSet and KmerSetGroup, and updates persistence logic to handle IDs and metadata correctly.
This commit is contained in:
Eric Coissac
2026-02-05 15:32:19 +01:00
parent afcb43b352
commit c5dd477675
6 changed files with 489 additions and 63 deletions

View File

@@ -26,7 +26,7 @@ func NewFrequencyFilter(k, minFreq int) *FrequencyFilter {
// Utilise un itérateur pour éviter l'allocation d'un vecteur intermédiaire
func (ff *FrequencyFilter) AddSequence(seq *obiseq.BioSequence) {
rawSeq := seq.Sequence()
for canonical := range IterNormalizedKmers(rawSeq, ff.K) {
for canonical := range IterNormalizedKmers(rawSeq, ff.K()) {
ff.addKmer(canonical)
}
}
@@ -48,7 +48,7 @@ func (ff *FrequencyFilter) addKmer(kmer uint64) {
// GetFilteredSet retourne un KmerSet des k-mers avec fréquence ≥ minFreq
func (ff *FrequencyFilter) GetFilteredSet() *KmerSet {
// Les k-mers filtrés sont dans le dernier niveau
return ff.Get(ff.MinFreq - 1).Clone()
return ff.Get(ff.MinFreq - 1).Copy()
}
// GetKmersAtLevel retourne un KmerSet des k-mers vus au moins (level+1) fois
@@ -56,9 +56,9 @@ func (ff *FrequencyFilter) GetFilteredSet() *KmerSet {
func (ff *FrequencyFilter) GetKmersAtLevel(level int) *KmerSet {
ks := ff.Get(level)
if ks == nil {
return NewKmerSet(ff.K)
return NewKmerSet(ff.K())
}
return ks.Clone()
return ks.Copy()
}
// Stats retourne des statistiques sur les niveaux de fréquence
@@ -161,14 +161,14 @@ func (ff *FrequencyFilter) Load(path string) error {
// Contains vérifie si un k-mer a atteint la fréquence minimale
func (ff *FrequencyFilter) Contains(kmer uint64) bool {
canonical := NormalizeKmer(kmer, ff.K)
canonical := NormalizeKmer(kmer, ff.K())
return ff.Get(ff.MinFreq - 1).Contains(canonical)
}
// GetFrequency retourne la fréquence approximative d'un k-mer
// Retourne le niveau maximum atteint (freq ≥ niveau)
func (ff *FrequencyFilter) GetFrequency(kmer uint64) int {
canonical := NormalizeKmer(kmer, ff.K)
canonical := NormalizeKmer(kmer, ff.K())
freq := 0
for i := 0; i < ff.MinFreq; i++ {

View File

@@ -10,7 +10,8 @@ import (
// KmerSet encapsule un ensemble de k-mers stockés dans un Roaring Bitmap
// Fournit des méthodes utilitaires pour manipuler des ensembles de k-mers
type KmerSet struct {
K int // Taille des k-mers
id string // Identifiant unique du KmerSet
k int // Taille des k-mers (immutable)
bitmap *roaring64.Bitmap // Bitmap contenant les k-mers
Metadata map[string]interface{} // Métadonnées utilisateur (clé=valeur atomique)
}
@@ -18,7 +19,7 @@ type KmerSet struct {
// NewKmerSet crée un nouveau KmerSet vide
func NewKmerSet(k int) *KmerSet {
return &KmerSet{
K: k,
k: k,
bitmap: roaring64.New(),
Metadata: make(map[string]interface{}),
}
@@ -27,12 +28,17 @@ func NewKmerSet(k int) *KmerSet {
// NewKmerSetFromBitmap crée un KmerSet à partir d'un bitmap existant
func NewKmerSetFromBitmap(k int, bitmap *roaring64.Bitmap) *KmerSet {
return &KmerSet{
K: k,
k: k,
bitmap: bitmap,
Metadata: make(map[string]interface{}),
}
}
// K retourne la taille des k-mers (immutable)
func (ks *KmerSet) K() int {
return ks.k
}
// Add ajoute un k-mer à l'ensemble
func (ks *KmerSet) Add(kmer uint64) {
ks.bitmap.Add(kmer)
@@ -42,7 +48,7 @@ func (ks *KmerSet) Add(kmer uint64) {
// Utilise un itérateur pour éviter l'allocation d'un vecteur intermédiaire
func (ks *KmerSet) AddSequence(seq *obiseq.BioSequence) {
rawSeq := seq.Sequence()
for canonical := range IterNormalizedKmers(rawSeq, ks.K) {
for canonical := range IterNormalizedKmers(rawSeq, ks.k) {
ks.bitmap.Add(canonical)
}
}
@@ -74,8 +80,8 @@ func (ks *KmerSet) Clear() {
ks.bitmap.Clear()
}
// Clone crée une copie de l'ensemble
func (ks *KmerSet) Clone() *KmerSet {
// Copy crée une copie de l'ensemble (cohérent avec BioSequence.Copy)
func (ks *KmerSet) Copy() *KmerSet {
// Copier les métadonnées
metadata := make(map[string]interface{}, len(ks.Metadata))
for k, v := range ks.Metadata {
@@ -83,40 +89,51 @@ func (ks *KmerSet) Clone() *KmerSet {
}
return &KmerSet{
K: ks.K,
id: ks.id,
k: ks.k,
bitmap: ks.bitmap.Clone(),
Metadata: metadata,
}
}
// Id retourne l'identifiant du KmerSet (cohérent avec BioSequence.Id)
func (ks *KmerSet) Id() string {
return ks.id
}
// SetId définit l'identifiant du KmerSet (cohérent avec BioSequence.SetId)
func (ks *KmerSet) SetId(id string) {
ks.id = id
}
// Union retourne l'union de cet ensemble avec un autre
func (ks *KmerSet) Union(other *KmerSet) *KmerSet {
if ks.K != other.K {
panic(fmt.Sprintf("Cannot union KmerSets with different k values: %d vs %d", ks.K, other.K))
if ks.k != other.k {
panic(fmt.Sprintf("Cannot union KmerSets with different k values: %d vs %d", ks.k, other.k))
}
result := ks.bitmap.Clone()
result.Or(other.bitmap)
return NewKmerSetFromBitmap(ks.K, result)
return NewKmerSetFromBitmap(ks.k, result)
}
// Intersect retourne l'intersection de cet ensemble avec un autre
func (ks *KmerSet) Intersect(other *KmerSet) *KmerSet {
if ks.K != other.K {
panic(fmt.Sprintf("Cannot intersect KmerSets with different k values: %d vs %d", ks.K, other.K))
if ks.k != other.k {
panic(fmt.Sprintf("Cannot intersect KmerSets with different k values: %d vs %d", ks.k, other.k))
}
result := ks.bitmap.Clone()
result.And(other.bitmap)
return NewKmerSetFromBitmap(ks.K, result)
return NewKmerSetFromBitmap(ks.k, result)
}
// Difference retourne la différence de cet ensemble avec un autre (this - other)
func (ks *KmerSet) Difference(other *KmerSet) *KmerSet {
if ks.K != other.K {
panic(fmt.Sprintf("Cannot subtract KmerSets with different k values: %d vs %d", ks.K, other.K))
if ks.k != other.k {
panic(fmt.Sprintf("Cannot subtract KmerSets with different k values: %d vs %d", ks.k, other.k))
}
result := ks.bitmap.Clone()
result.AndNot(other.bitmap)
return NewKmerSetFromBitmap(ks.K, result)
return NewKmerSetFromBitmap(ks.k, result)
}
// Iterator retourne un itérateur sur tous les k-mers de l'ensemble

View File

@@ -0,0 +1,362 @@
package obikmer
import (
"fmt"
"strconv"
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiutils"
)
// ==================================
// KMER SET ATTRIBUTE API
// Mimic BioSequence attribute API from obiseq/attributes.go
// ==================================
// HasAttribute vérifie si une clé d'attribut existe
func (ks *KmerSet) HasAttribute(key string) bool {
_, ok := ks.Metadata[key]
return ok
}
// GetAttribute récupère la valeur d'un attribut
// Cas particuliers: "id" utilise Id(), "k" utilise K()
func (ks *KmerSet) GetAttribute(key string) (interface{}, bool) {
switch key {
case "id":
return ks.Id(), true
case "k":
return ks.K(), true
default:
value, ok := ks.Metadata[key]
return value, ok
}
}
// SetAttribute définit la valeur d'un attribut
// Cas particuliers: "id" utilise SetId(), "k" est immutable (panique)
func (ks *KmerSet) SetAttribute(key string, value interface{}) {
switch key {
case "id":
if id, ok := value.(string); ok {
ks.SetId(id)
} else {
panic(fmt.Sprintf("id must be a string, got %T", value))
}
case "k":
panic("k is immutable and cannot be modified via SetAttribute")
default:
ks.Metadata[key] = value
}
}
// DeleteAttribute supprime un attribut
func (ks *KmerSet) DeleteAttribute(key string) {
delete(ks.Metadata, key)
}
// RemoveAttribute supprime un attribut (alias de DeleteAttribute)
func (ks *KmerSet) RemoveAttribute(key string) {
ks.DeleteAttribute(key)
}
// RenameAttribute renomme un attribut
func (ks *KmerSet) RenameAttribute(newName, oldName string) {
if value, ok := ks.Metadata[oldName]; ok {
ks.Metadata[newName] = value
delete(ks.Metadata, oldName)
}
}
// GetIntAttribute récupère un attribut en tant qu'entier
func (ks *KmerSet) GetIntAttribute(key string) (int, bool) {
value, ok := ks.Metadata[key]
if !ok {
return 0, false
}
switch v := value.(type) {
case int:
return v, true
case int64:
return int(v), true
case float64:
return int(v), true
case string:
if i, err := strconv.Atoi(v); err == nil {
return i, true
}
}
return 0, false
}
// GetFloatAttribute récupère un attribut en tant que float64
func (ks *KmerSet) GetFloatAttribute(key string) (float64, bool) {
value, ok := ks.Metadata[key]
if !ok {
return 0, false
}
switch v := value.(type) {
case float64:
return v, true
case float32:
return float64(v), true
case int:
return float64(v), true
case int64:
return float64(v), true
case string:
if f, err := strconv.ParseFloat(v, 64); err == nil {
return f, true
}
}
return 0, false
}
// GetNumericAttribute récupère un attribut numérique (alias de GetFloatAttribute)
func (ks *KmerSet) GetNumericAttribute(key string) (float64, bool) {
return ks.GetFloatAttribute(key)
}
// GetStringAttribute récupère un attribut en tant que chaîne
func (ks *KmerSet) GetStringAttribute(key string) (string, bool) {
value, ok := ks.Metadata[key]
if !ok {
return "", false
}
switch v := value.(type) {
case string:
return v, true
default:
return fmt.Sprintf("%v", v), true
}
}
// GetBoolAttribute récupère un attribut en tant que booléen
func (ks *KmerSet) GetBoolAttribute(key string) (bool, bool) {
value, ok := ks.Metadata[key]
if !ok {
return false, false
}
switch v := value.(type) {
case bool:
return v, true
case int:
return v != 0, true
case string:
if b, err := strconv.ParseBool(v); err == nil {
return b, true
}
}
return false, false
}
// AttributeKeys retourne l'ensemble des clés d'attributs
func (ks *KmerSet) AttributeKeys() obiutils.Set[string] {
keys := obiutils.MakeSet[string]()
for key := range ks.Metadata {
keys.Add(key)
}
return keys
}
// Keys retourne l'ensemble des clés d'attributs (alias de AttributeKeys)
func (ks *KmerSet) Keys() obiutils.Set[string] {
return ks.AttributeKeys()
}
// ==================================
// KMER SET GROUP ATTRIBUTE API
// Métadonnées du groupe + accès via Get() pour les sets individuels
// ==================================
// HasAttribute vérifie si une clé d'attribut existe pour le groupe
func (ksg *KmerSetGroup) HasAttribute(key string) bool {
_, ok := ksg.Metadata[key]
return ok
}
// GetAttribute récupère la valeur d'un attribut du groupe
// Cas particuliers: "id" utilise Id(), "k" utilise K()
func (ksg *KmerSetGroup) GetAttribute(key string) (interface{}, bool) {
switch key {
case "id":
return ksg.Id(), true
case "k":
return ksg.K(), true
default:
value, ok := ksg.Metadata[key]
return value, ok
}
}
// SetAttribute définit la valeur d'un attribut du groupe
// Cas particuliers: "id" utilise SetId(), "k" est immutable (panique)
func (ksg *KmerSetGroup) SetAttribute(key string, value interface{}) {
switch key {
case "id":
if id, ok := value.(string); ok {
ksg.SetId(id)
} else {
panic(fmt.Sprintf("id must be a string, got %T", value))
}
case "k":
panic("k is immutable and cannot be modified via SetAttribute")
default:
ksg.Metadata[key] = value
}
}
// DeleteAttribute supprime un attribut du groupe
func (ksg *KmerSetGroup) DeleteAttribute(key string) {
delete(ksg.Metadata, key)
}
// RemoveAttribute supprime un attribut du groupe (alias)
func (ksg *KmerSetGroup) RemoveAttribute(key string) {
ksg.DeleteAttribute(key)
}
// RenameAttribute renomme un attribut du groupe
func (ksg *KmerSetGroup) RenameAttribute(newName, oldName string) {
if value, ok := ksg.Metadata[oldName]; ok {
ksg.Metadata[newName] = value
delete(ksg.Metadata, oldName)
}
}
// GetIntAttribute récupère un attribut entier du groupe
func (ksg *KmerSetGroup) GetIntAttribute(key string) (int, bool) {
value, ok := ksg.GetAttribute(key)
if !ok {
return 0, false
}
switch v := value.(type) {
case int:
return v, true
case int64:
return int(v), true
case float64:
return int(v), true
case string:
if i, err := strconv.Atoi(v); err == nil {
return i, true
}
}
return 0, false
}
// GetFloatAttribute récupère un attribut float64 du groupe
func (ksg *KmerSetGroup) GetFloatAttribute(key string) (float64, bool) {
value, ok := ksg.GetAttribute(key)
if !ok {
return 0, false
}
switch v := value.(type) {
case float64:
return v, true
case float32:
return float64(v), true
case int:
return float64(v), true
case int64:
return float64(v), true
case string:
if f, err := strconv.ParseFloat(v, 64); err == nil {
return f, true
}
}
return 0, false
}
// GetNumericAttribute récupère un attribut numérique du groupe
func (ksg *KmerSetGroup) GetNumericAttribute(key string) (float64, bool) {
return ksg.GetFloatAttribute(key)
}
// GetStringAttribute récupère un attribut chaîne du groupe
func (ksg *KmerSetGroup) GetStringAttribute(key string) (string, bool) {
value, ok := ksg.GetAttribute(key)
if !ok {
return "", false
}
switch v := value.(type) {
case string:
return v, true
default:
return fmt.Sprintf("%v", v), true
}
}
// GetBoolAttribute récupère un attribut booléen du groupe
func (ksg *KmerSetGroup) GetBoolAttribute(key string) (bool, bool) {
value, ok := ksg.GetAttribute(key)
if !ok {
return false, false
}
switch v := value.(type) {
case bool:
return v, true
case int:
return v != 0, true
case string:
if b, err := strconv.ParseBool(v); err == nil {
return b, true
}
}
return false, false
}
// AttributeKeys retourne l'ensemble des clés d'attributs du groupe
func (ksg *KmerSetGroup) AttributeKeys() obiutils.Set[string] {
keys := obiutils.MakeSet[string]()
for key := range ksg.Metadata {
keys.Add(key)
}
return keys
}
// Keys retourne l'ensemble des clés d'attributs du groupe (alias)
func (ksg *KmerSetGroup) Keys() obiutils.Set[string] {
return ksg.AttributeKeys()
}
// ==================================
// MÉTHODES POUR ACCÉDER AUX ATTRIBUTS DES SETS INDIVIDUELS VIA Get()
// Architecture zero-copy: ksg.Get(i).SetAttribute(...)
// ==================================
// Exemple d'utilisation:
// Pour accéder aux métadonnées d'un KmerSet individuel dans un groupe:
// ks := ksg.Get(0)
// ks.SetAttribute("level", 1)
// hasLevel := ks.HasAttribute("level")
//
// Pour les métadonnées du groupe:
// ksg.SetAttribute("name", "FrequencyFilter")
// name, ok := ksg.GetStringAttribute("name")
// AllAttributeKeys retourne toutes les clés d'attributs uniques du groupe ET de tous ses sets
func (ksg *KmerSetGroup) AllAttributeKeys() obiutils.Set[string] {
keys := obiutils.MakeSet[string]()
// Ajouter les clés du groupe
for key := range ksg.Metadata {
keys.Add(key)
}
// Ajouter les clés de chaque set
for _, ks := range ksg.sets {
for key := range ks.Metadata {
keys.Add(key)
}
}
return keys
}

View File

@@ -9,9 +9,10 @@ import (
// KmerSetGroup représente un vecteur de KmerSet
// Utilisé pour gérer plusieurs ensembles de k-mers (par exemple, par niveau de fréquence)
type KmerSetGroup struct {
K int // Taille des k-mers
id string // Identifiant unique du KmerSetGroup
k int // Taille des k-mers (immutable)
sets []*KmerSet // Vecteur de KmerSet
Metadata []map[string]interface{} // Métadonnées par KmerSet (même longueur que sets)
Metadata map[string]interface{} // Métadonnées du groupe (pas des sets individuels)
}
// NewKmerSetGroup crée un nouveau groupe de n KmerSets
@@ -21,19 +22,22 @@ func NewKmerSetGroup(k int, n int) *KmerSetGroup {
}
sets := make([]*KmerSet, n)
metadata := make([]map[string]interface{}, n)
for i := range sets {
sets[i] = NewKmerSet(k)
metadata[i] = make(map[string]interface{})
}
return &KmerSetGroup{
K: k,
k: k,
sets: sets,
Metadata: metadata,
Metadata: make(map[string]interface{}),
}
}
// K retourne la taille des k-mers (immutable)
func (ksg *KmerSetGroup) K() int {
return ksg.k
}
// Size retourne le nombre de KmerSet dans le groupe
func (ksg *KmerSetGroup) Size() int {
return len(ksg.sets)
@@ -54,8 +58,8 @@ func (ksg *KmerSetGroup) Set(index int, ks *KmerSet) {
if index < 0 || index >= len(ksg.sets) {
panic(fmt.Sprintf("Index out of bounds: %d (size: %d)", index, len(ksg.sets)))
}
if ks.K != ksg.K {
panic(fmt.Sprintf("KmerSet k mismatch: expected %d, got %d", ksg.K, ks.K))
if ks.k != ksg.k {
panic(fmt.Sprintf("KmerSet k mismatch: expected %d, got %d", ksg.k, ks.k))
}
ksg.sets[index] = ks
}
@@ -93,28 +97,37 @@ func (ksg *KmerSetGroup) Clear() {
}
}
// Clone crée une copie complète du groupe
func (ksg *KmerSetGroup) Clone() *KmerSetGroup {
clonedSets := make([]*KmerSet, len(ksg.sets))
clonedMetadata := make([]map[string]interface{}, len(ksg.Metadata))
// Copy crée une copie complète du groupe (cohérent avec BioSequence.Copy)
func (ksg *KmerSetGroup) Copy() *KmerSetGroup {
copiedSets := make([]*KmerSet, len(ksg.sets))
for i, ks := range ksg.sets {
clonedSets[i] = ks.Clone()
copiedSets[i] = ks.Copy() // Copy chaque KmerSet avec ses métadonnées
}
// Copier les métadonnées du groupe
clonedMetadata[i] = make(map[string]interface{}, len(ksg.Metadata[i]))
for k, v := range ksg.Metadata[i] {
clonedMetadata[i][k] = v
}
groupMetadata := make(map[string]interface{}, len(ksg.Metadata))
for k, v := range ksg.Metadata {
groupMetadata[k] = v
}
return &KmerSetGroup{
K: ksg.K,
sets: clonedSets,
Metadata: clonedMetadata,
id: ksg.id,
k: ksg.k,
sets: copiedSets,
Metadata: groupMetadata,
}
}
// Id retourne l'identifiant du KmerSetGroup (cohérent avec BioSequence.Id)
func (ksg *KmerSetGroup) Id() string {
return ksg.id
}
// SetId définit l'identifiant du KmerSetGroup (cohérent avec BioSequence.SetId)
func (ksg *KmerSetGroup) SetId(id string) {
ksg.id = id
}
// AddSequence ajoute tous les k-mers d'une séquence à un KmerSet spécifique
func (ksg *KmerSetGroup) AddSequence(seq *obiseq.BioSequence, index int) {
if index < 0 || index >= len(ksg.sets) {
@@ -134,10 +147,10 @@ func (ksg *KmerSetGroup) AddSequences(sequences *obiseq.BioSequenceSlice, index
// Union retourne l'union de tous les KmerSet du groupe
func (ksg *KmerSetGroup) Union() *KmerSet {
if len(ksg.sets) == 0 {
return NewKmerSet(ksg.K)
return NewKmerSet(ksg.k)
}
result := ksg.sets[0].Clone()
result := ksg.sets[0].Copy()
for i := 1; i < len(ksg.sets); i++ {
result = result.Union(ksg.sets[i])
}
@@ -147,10 +160,10 @@ func (ksg *KmerSetGroup) Union() *KmerSet {
// Intersect retourne l'intersection de tous les KmerSet du groupe
func (ksg *KmerSetGroup) Intersect() *KmerSet {
if len(ksg.sets) == 0 {
return NewKmerSet(ksg.K)
return NewKmerSet(ksg.k)
}
result := ksg.sets[0].Clone()
result := ksg.sets[0].Copy()
for i := 1; i < len(ksg.sets); i++ {
result = result.Intersect(ksg.sets[i])
}
@@ -173,7 +186,7 @@ type KmerSetStats struct {
func (ksg *KmerSetGroup) Stats() KmerSetGroupStats {
stats := KmerSetGroupStats{
K: ksg.K,
K: ksg.k,
Size: len(ksg.sets),
Sets: make([]KmerSetStats, len(ksg.sets)),
}

View File

@@ -36,12 +36,14 @@ func (f MetadataFormat) String() string {
// KmerSetMetadata contient les métadonnées d'un KmerSet ou KmerSetGroup
type KmerSetMetadata struct {
ID string `toml:"id,omitempty" yaml:"id,omitempty" json:"id,omitempty"` // Identifiant unique
K int `toml:"k" yaml:"k" json:"k"` // Taille des k-mers
Type string `toml:"type" yaml:"type" json:"type"` // "KmerSet" ou "KmerSetGroup"
Size int `toml:"size" yaml:"size" json:"size"` // 1 pour KmerSet, n pour KmerSetGroup
Files []string `toml:"files" yaml:"files" json:"files"` // Liste des fichiers .roaring
UserMetadata map[string]interface{} `toml:"user_metadata,omitempty" yaml:"user_metadata,omitempty" json:"user_metadata,omitempty"` // Métadonnées KmerSet unique
SetsMetadata []map[string]interface{} `toml:"sets_metadata,omitempty" yaml:"sets_metadata,omitempty" json:"sets_metadata,omitempty"` // Métadonnées par set (KmerSetGroup)
SetsIDs []string `toml:"sets_ids,omitempty" yaml:"sets_ids,omitempty" json:"sets_ids,omitempty"` // IDs des KmerSet individuels
UserMetadata map[string]interface{} `toml:"user_metadata,omitempty" yaml:"user_metadata,omitempty" json:"user_metadata,omitempty"` // Métadonnées KmerSet ou KmerSetGroup
SetsMetadata []map[string]interface{} `toml:"sets_metadata,omitempty" yaml:"sets_metadata,omitempty" json:"sets_metadata,omitempty"` // Métadonnées des KmerSet individuels dans un KmerSetGroup
}
// SaveKmerSet sauvegarde un KmerSet dans un répertoire
@@ -54,7 +56,8 @@ func (ks *KmerSet) Save(directory string, format MetadataFormat) error {
// Métadonnées
metadata := KmerSetMetadata{
K: ks.K,
ID: ks.id,
K: ks.k,
Type: "KmerSet",
Size: 1,
Files: []string{"set_0.roaring"},
@@ -109,6 +112,9 @@ func LoadKmerSet(directory string) (*KmerSet, error) {
ks := NewKmerSet(metadata.K)
// Charger l'ID
ks.id = metadata.ID
// Charger les métadonnées utilisateur
if metadata.UserMetadata != nil {
ks.Metadata = metadata.UserMetadata
@@ -135,12 +141,23 @@ func (ksg *KmerSetGroup) Save(directory string, format MetadataFormat) error {
files[i] = fmt.Sprintf("set_%d.roaring", i)
}
// Collecter les IDs et métadonnées de chaque KmerSet individuel
setsIDs := make([]string, len(ksg.sets))
setsMetadata := make([]map[string]interface{}, len(ksg.sets))
for i, ks := range ksg.sets {
setsIDs[i] = ks.id
setsMetadata[i] = ks.Metadata
}
metadata := KmerSetMetadata{
K: ksg.K,
ID: ksg.id,
K: ksg.k,
Type: "KmerSetGroup",
Size: len(ksg.sets),
Files: files,
SetsMetadata: ksg.Metadata, // Sauvegarder les métadonnées de chaque set
SetsIDs: setsIDs, // IDs de chaque set
UserMetadata: ksg.Metadata, // Métadonnées du groupe
SetsMetadata: setsMetadata, // Métadonnées de chaque set
}
// Sauvegarder les métadonnées
@@ -187,12 +204,29 @@ func LoadKmerSetGroup(directory string) (*KmerSetGroup, error) {
// Créer le groupe
ksg := NewKmerSetGroup(metadata.K, metadata.Size)
// Charger les métadonnées de chaque set
// Charger l'ID du groupe
ksg.id = metadata.ID
// Charger les métadonnées du groupe
if metadata.UserMetadata != nil {
ksg.Metadata = metadata.UserMetadata
}
// Charger les IDs de chaque KmerSet
if metadata.SetsIDs != nil && len(metadata.SetsIDs) == metadata.Size {
for i := range ksg.sets {
ksg.sets[i].id = metadata.SetsIDs[i]
}
}
// Charger les métadonnées de chaque KmerSet individuel
if metadata.SetsMetadata != nil {
if len(metadata.SetsMetadata) != metadata.Size {
return nil, fmt.Errorf("metadata size mismatch: expected %d, got %d", metadata.Size, len(metadata.SetsMetadata))
return nil, fmt.Errorf("sets metadata size mismatch: expected %d, got %d", metadata.Size, len(metadata.SetsMetadata))
}
for i := range ksg.sets {
ksg.sets[i].Metadata = metadata.SetsMetadata[i]
}
ksg.Metadata = metadata.SetsMetadata
}
// Charger chaque bitmap

View File

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