From 1d880fdc5ff25f8e2f53d8351db97f1a4bb30da7 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Tue, 26 May 2026 09:53:31 +0200 Subject: [PATCH] refactor: optimize MPHF construction and update legacy guidelines Replaces parallel random-access unitig iteration with a sequential mmap-based iterator for MPHF construction, eliminating the build-time `.idx` dependency by deferring index generation until after persistence. Updates `CLAUDE.md` to treat existing code as a hypothesis, mandating proactive removal of obsolete legacy constructs rather than preserving them out of inertia. --- CLAUDE.md | 3 +++ src/obilayeredmap/src/mphf_layer.rs | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5d78c77..6fa8412 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,6 +8,9 @@ Ne modifier aucun fichier à moins d'une demande explicite de modification. En p **Règle absolue : ne jamais substituer une dépendance ou une bibliothèque sans validation explicite.** Si une dépendance demandée pose problème (erreur de compilation, bug, API manquante), exposer le problème et proposer des alternatives — ne jamais switcher silencieusement vers une autre bibliothèque. Le choix des dépendances est une décision d'architecture qui appartient au développeur. +**Règle absolue : le code existant est une hypothèse, pas une vérité.** +Quand une nouvelle construction (type, itérateur, abstraction) rend du code historique injustifié, le signaler immédiatement et proposer de le supprimer — ne pas conserver les deux en parallèle par inertie. Le développeur demande explicitement de remettre en cause le code base : ne pas attendre qu'il insiste. + Tu maintiens en **anglais**, dense et sans remplissage, les documents suivants : - `docmd/index.md` — document de discussion de base, enrichi progressivement au fil de nos échanges ; il reflète l'état courant de la réflexion sur le projet - les autres fichiers Markdown dans `docmd/` selon leur thème respectif diff --git a/src/obilayeredmap/src/mphf_layer.rs b/src/obilayeredmap/src/mphf_layer.rs index 0e1e0f8..c94ce69 100644 --- a/src/obilayeredmap/src/mphf_layer.rs +++ b/src/obilayeredmap/src/mphf_layer.rs @@ -218,11 +218,15 @@ impl MphfLayer { match evidence_kind { // ── Exact path ──────────────────────────────────────────────────── + // .idx is built LAST, once evidence.bin is written, so it is never + // present during construction — only at query time. EvidenceKind::Exact => { - build_unitig_idx(&unitig_path, block_bits)?; - - let unitigs = UnitigFileReader::open(&unitig_path)?; - let n = unitigs.n_kmers(); + let n = UnitigFileReader::open_sequential(&unitig_path)?.n_kmers(); + let keys = CanonicalKmerIter::new(&unitig_path) + .map_err(|e| match e { + obiskio::SKError::Io(io) => OLMError::Io(io), + e => OLMError::InvalidLayer(e.to_string()), + })?; if n == 0 { fs::File::create(dir.join(EVIDENCE_FILE))?; @@ -232,15 +236,13 @@ impl MphfLayer { mphf.store(&dir.join(MPHF_FILE)) .map_err(|e| OLMError::InvalidLayer(e.to_string()))?; LayerMeta::exact().save(dir)?; + build_unitig_idx(&unitig_path, block_bits)?; return Ok(0); } - // Pass 1 — parallel MPHF via random access (.idx required) - let keys = (0..unitigs.len()) - .into_par_iter() - .flat_map_iter(|ci| unitigs.unitig(ci).into_canonical_kmers().map(|km| km.raw())); + // Pass 1 — MPHF construction via clonable mmap iterator let mphf: Mphf = - Mphf::new_from_par_iter(n, keys, PtrHashParams::::default()); + Mphf::new_from_par_iter(n, keys.map(|k| k.raw()).par_bridge(), PtrHashParams::::default()); mphf.store(&dir.join(MPHF_FILE)) .map_err(|e| OLMError::InvalidLayer(e.to_string()))?; @@ -266,6 +268,8 @@ impl MphfLayer { ev.write(&dir.join(EVIDENCE_FILE))?; LayerMeta::exact().save(dir)?; + // .idx built last: strictly for query-time kmer verification + build_unitig_idx(&unitig_path, block_bits)?; Ok(n) }