refactor: rename obikindex to obikindexer

This commit is contained in:
Eric Coissac
2026-08-21 05:29:34 +02:00
parent 5c1584967f
commit 96b6517541
22 changed files with 132 additions and 50 deletions
@@ -16,6 +16,22 @@ tier / Index tier, each depending only downward) are unchanged; only the
crate-vs-module packaging changed. See [obikindex::layer](layer_tier.md) crate-vs-module packaging changed. See [obikindex::layer](layer_tier.md)
for the current module doc. for the current module doc.
**Superseded, second event, same day (2026-08-21):** `obikpartitionner`
and `obikderep`, the two algorithm crates, are also gone — but unlike
`obikpartition`/`obilayeredmap` above, they were **not** folded into
`obikindex`. They were first (mistakenly) merged into `obikindex` as an
`algorithms` submodule, then corrected into a new sibling crate,
**`obikindexer`**, holding `obikindexer::algorithms::{partitionner,
dereplicator}` and depending on `obikindex` — never the reverse, same
dependency direction `obikpartitionner`/`obikderep` already had. Read
`obikpartitionner::X` as `obikindexer::algorithms::partitionner::X` and
`obikderep::X` as `obikindexer::algorithms::dereplicator::X` throughout
what follows. The distinction the mistake surfaced, worth keeping: data
crates (`obikindex`, holding the `index`/`partition`/`layer` model) merge
naturally into one crate as submodules; algorithm crates that operate on
that model from outside stay separate, so the dependency only ever runs
one way.
Status (2026-08-20, latest pass): (1) done — `obilayeredmap::Layer` Status (2026-08-20, latest pass): (1) done — `obilayeredmap::Layer`
exists, `Mat` is gone. (1b) done — `Layer::Empty`, the first non-ready exists, `Mat` is gone. (1b) done — `Layer::Empty`, the first non-ready
state, added (panics on every read method). (2a) done — the state, added (panics on every read method). (2a) done — the
+28 -3
View File
@@ -1539,11 +1539,9 @@ dependencies = [
"obicompactvec", "obicompactvec",
"obidebruinj", "obidebruinj",
"obikentropy", "obikentropy",
"obikrope",
"obikseq", "obikseq",
"obipipeline", "obipipeline",
"obiread", "obiread",
"obiskbuilder",
"obiskio", "obiskio",
"obisys", "obisys",
"obitaxonomy", "obitaxonomy",
@@ -1551,12 +1549,37 @@ dependencies = [
"rayon", "rayon",
"serde", "serde",
"serde_json", "serde_json",
"sysinfo",
"tempfile", "tempfile",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "obikindexer"
version = "0.1.0"
dependencies = [
"cacheline-ef",
"epserde",
"memmap2",
"niffler",
"obicompactvec",
"obikindex",
"obikrope",
"obikseq",
"obipipeline",
"obiread",
"obiskbuilder",
"obiskio",
"obisys",
"ptr_hash",
"rayon",
"serde",
"serde_json",
"sysinfo",
"tempfile",
"tracing",
]
[[package]] [[package]]
name = "obikmer" name = "obikmer"
version = "1.2.2" version = "1.2.2"
@@ -1568,6 +1591,7 @@ dependencies = [
"obidebruinj", "obidebruinj",
"obifastwrite", "obifastwrite",
"obikindex", "obikindex",
"obikindexer",
"obikphylo", "obikphylo",
"obikrope", "obikrope",
"obikseq", "obikseq",
@@ -1595,6 +1619,7 @@ dependencies = [
"ndarray", "ndarray",
"obicompactvec", "obicompactvec",
"obikindex", "obikindex",
"obikindexer",
"obikseq", "obikseq",
"obipipeline", "obipipeline",
"obiread", "obiread",
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["obikseq", "obiread", "obiskbuilder", "obifastwrite", "obikmer","obikrope","obipipeline", "obiskio","obidebruinj", "obicompactvec", "obisys", "obikindex", "obitaxonomy", "obikentropy", "obikphylo"] members = ["obikseq", "obiread", "obiskbuilder", "obifastwrite", "obikmer","obikrope","obipipeline", "obiskio","obidebruinj", "obicompactvec", "obisys", "obikindex", "obikindexer", "obitaxonomy", "obikentropy", "obikphylo"]
[profile.release] [profile.release]
debug = 1 debug = 1
+1 -4
View File
@@ -12,8 +12,6 @@ obicompactvec = { path = "../obicompactvec" }
obidebruinj = { path = "../obidebruinj" } obidebruinj = { path = "../obidebruinj" }
obipipeline = { path = "../obipipeline" } obipipeline = { path = "../obipipeline" }
obikentropy = { path = "../obikentropy" } obikentropy = { path = "../obikentropy" }
obiread = { path = "../obiread" }
obiskbuilder = { path = "../obiskbuilder" }
cacheline-ef = "1.1" cacheline-ef = "1.1"
epserde = "0.8" epserde = "0.8"
ptr_hash = "1.1" ptr_hash = "1.1"
@@ -27,12 +25,11 @@ serde_json = "1"
indicatif = "0.18" indicatif = "0.18"
tracing = "0.1.44" tracing = "0.1.44"
bitvec = "1" bitvec = "1"
sysinfo = "0.39"
hwlocality = { version = "1.0.0-alpha.11", features = ["vendored"], optional = true } hwlocality = { version = "1.0.0-alpha.11", features = ["vendored"], optional = true }
[dev-dependencies] [dev-dependencies]
obiread = { path = "../obiread" }
obikseq = { path = "../obikseq", features = ["test-utils"] } obikseq = { path = "../obikseq", features = ["test-utils"] }
obikrope = { path = "../obikrope" }
tempfile = "3" tempfile = "3"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
anyhow = "1" anyhow = "1"
-9
View File
@@ -1,9 +0,0 @@
//! Indexing-pipeline algorithms: code that operates on a
//! [`crate::index::KmerIndex`] to build or transform its content, as
//! opposed to the `index`/`partition`/`layer` modules, which are the data
//! model itself. Each algorithm is its own submodule: [`partitionner`]
//! (routing raw super-kmers into partitions, then counting), [`dereplicator`]
//! (deduplicating a partition's raw super-kmers before counting).
pub mod dereplicator;
pub mod partitionner;
+5 -5
View File
@@ -159,10 +159,10 @@ impl KmerIndex {
/// Path of partition `i`'s raw directory (`partitions/part_{i:05}`) — /// Path of partition `i`'s raw directory (`partitions/part_{i:05}`) —
/// delegates to `crate::partition`, the Partition tier's own naming /// delegates to `crate::partition`, the Partition tier's own naming
/// primitive (mirrors `layer_dir` delegating to `crate::layer`). /// primitive (mirrors `layer_dir` delegating to `crate::layer`).
/// `crate::algorithms::partitionner::PartitionRouter` reaches this same /// `obikindexer::algorithms::partitionner::PartitionRouter` reaches this
/// directory only indirectly, through this method — it depends on /// same directory only indirectly, through this method — `obikindexer`
/// `KmerIndex`, not the other way around, even though both now live in /// depends on `KmerIndex`, not the other way around — see
/// this crate — see `DevDocMD/implementation/partition_layer_cache.md`. /// `DevDocMD/implementation/partition_layer_cache.md`.
pub fn partition_dir(&self, i: usize) -> PathBuf { pub fn partition_dir(&self, i: usize) -> PathBuf {
crate::partition::partition_dir(&self.root_path, i) crate::partition::partition_dir(&self.root_path, i)
} }
@@ -232,7 +232,7 @@ impl KmerIndex {
/// Write `spectrums/{label}.json` from an already-computed kmer /// Write `spectrums/{label}.json` from an already-computed kmer
/// spectrum (`f0`/`f1`/abundance histogram). Takes plain values rather /// spectrum (`f0`/`f1`/abundance histogram). Takes plain values rather
/// than `crate::algorithms::partitionner::KmerSpectrum` — `KmerIndex` /// than `obikindexer::algorithms::partitionner::KmerSpectrum` — `KmerIndex`
/// is the data model, `PartitionRouter` the algorithm that depends on /// is the data model, `PartitionRouter` the algorithm that depends on
/// it (see `DevDocMD/implementation/partition_layer_cache.md`), not /// it (see `DevDocMD/implementation/partition_layer_cache.md`), not
/// the other way around, and this is the only field of that type it /// the other way around, and this is the only field of that type it
+4 -4
View File
@@ -46,9 +46,9 @@ const SK_EXT: &str = "skmer.zst";
/// Path of a layer's raw, not-yet-dereplicated superkmer file — written by /// Path of a layer's raw, not-yet-dereplicated superkmer file — written by
/// whichever algorithm routes superkmers into this layer (today: /// whichever algorithm routes superkmers into this layer (today:
/// `crate::algorithms::partitionner::PartitionRouter`), read by whichever /// `obikindexer::algorithms::partitionner::PartitionRouter`), read by whichever
/// algorithm dereplicates it (today: /// algorithm dereplicates it (today:
/// `crate::algorithms::dereplicator::Dereplicator`). Naming this once /// `obikindexer::algorithms::dereplicator::Dereplicator`). Naming this once
/// here, rather than in either algorithm submodule, is what lets the two /// here, rather than in either algorithm submodule, is what lets the two
/// agree on the filename without depending on each other directly — see /// agree on the filename without depending on each other directly — see
/// `DevDocMD/implementation/partition_layer_cache.md`. /// `DevDocMD/implementation/partition_layer_cache.md`.
@@ -57,9 +57,9 @@ pub fn raw_superkmers_path(layer_dir: &Path) -> PathBuf {
} }
/// Path of a layer's dereplicated superkmer file — written by /// Path of a layer's dereplicated superkmer file — written by
/// `crate::algorithms::dereplicator::Dereplicator`, read by whichever /// `obikindexer::algorithms::dereplicator::Dereplicator`, read by whichever
/// algorithm counts kmer abundances from it (today: /// algorithm counts kmer abundances from it (today:
/// `crate::algorithms::partitionner::PartitionRouter::count_kmer`) and, /// `obikindexer::algorithms::partitionner::PartitionRouter::count_kmer`) and,
/// later, by `obikindex::build_index_layer` to build the real layer. /// later, by `obikindex::build_index_layer` to build the real layer.
pub fn dereplicated_superkmers_path(layer_dir: &Path) -> PathBuf { pub fn dereplicated_superkmers_path(layer_dir: &Path) -> PathBuf {
layer_dir.join(format!("dereplicated.{SK_EXT}")) layer_dir.join(format!("dereplicated.{SK_EXT}"))
+4 -4
View File
@@ -3,11 +3,11 @@
//! section for the authoritative naming/scope discussion. Each tier is a //! section for the authoritative naming/scope discussion. Each tier is a
//! submodule: [`layer`] (was the `obilayeredmap` crate), [`partition`] //! submodule: [`layer`] (was the `obilayeredmap` crate), [`partition`]
//! (was the `obikpartition` crate), [`index`] (this crate's original //! (was the `obikpartition` crate), [`index`] (this crate's original
//! content). [`algorithms`] (was the `obikpartitionner` and `obikderep` //! content). Indexing-pipeline algorithms that build or transform an
//! crates) sits alongside the model: code that builds or transforms an //! index's content (the former `obikpartitionner`/`obikderep` crates) live
//! index's content rather than defining its shape. //! in the separate `obikindexer` crate, which depends on this one, not the
//! other way around.
pub mod algorithms;
pub mod index; pub mod index;
pub mod layer; pub mod layer;
pub mod partition; pub mod partition;
+29
View File
@@ -0,0 +1,29 @@
[package]
name = "obikindexer"
version = "0.1.0"
edition = "2024"
[dependencies]
obikindex = { path = "../obikindex" }
obikseq = { path = "../obikseq" }
obiskio = { path = "../obiskio" }
obisys = { path = "../obisys" }
obicompactvec = { path = "../obicompactvec" }
obipipeline = { path = "../obipipeline" }
obiread = { path = "../obiread" }
obiskbuilder = { path = "../obiskbuilder" }
cacheline-ef = "1.1"
epserde = "0.8"
ptr_hash = "1.1"
niffler = "3.0.0"
memmap2 = "0.9.10"
rayon = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1.44"
sysinfo = "0.39"
[dev-dependencies]
obikseq = { path = "../obikseq", features = ["test-utils"] }
obikrope = { path = "../obikrope" }
tempfile = "3"
@@ -14,12 +14,12 @@ use niffler::send::compression::Format;
use niffler::Level; use niffler::Level;
use obikseq::superkmer::SuperKmer; use obikseq::superkmer::SuperKmer;
use obikseq::Sequence; use obikseq::Sequence;
use crate::layer::{dereplicated_superkmers_path, raw_superkmers_path}; use obikindex::layer::{dereplicated_superkmers_path, raw_superkmers_path};
use obiskio::{SKFileMeta, SKFileReader, SKFileWriter, SKResult}; use obiskio::{SKFileMeta, SKFileReader, SKFileWriter, SKResult};
/// Scratch-file extension for this algorithm's own intermediate split /// Scratch-file extension for this algorithm's own intermediate split
/// buckets — never read by anything outside [`dereplicate_partition`], /// buckets — never read by anything outside [`dereplicate_partition`],
/// unlike `raw`/`dereplicated` (see `crate::layer::{raw_superkmers_path, /// unlike `raw`/`dereplicated` (see `obikindex::layer::{raw_superkmers_path,
/// dereplicated_superkmers_path}`, the actual cross-module contract). /// dereplicated_superkmers_path}`, the actual cross-module contract).
const TEMP_EXT: &str = "skmer.zst"; const TEMP_EXT: &str = "skmer.zst";
@@ -15,7 +15,7 @@ use obisys::Progress;
use rayon::prelude::*; use rayon::prelude::*;
use sysinfo::System; use sysinfo::System;
use crate::index::KmerIndex; use obikindex::KmerIndex;
use dereplicate::{dereplicate_partition, optimal_buckets}; use dereplicate::{dereplicate_partition, optimal_buckets};
@@ -84,7 +84,7 @@ impl<'a> Dereplicator<'a> {
.map(|i| { .map(|i| {
let dir = self.index.layer_dir(i, 0); let dir = self.index.layer_dir(i, 0);
let result = if dir.exists() { let result = if dir.exists() {
let raw_path = crate::layer::raw_superkmers_path(&dir); let raw_path = obikindex::layer::raw_superkmers_path(&dir);
let n_buckets = optimal_buckets(&raw_path, available_per_thread); let n_buckets = optimal_buckets(&raw_path, available_per_thread);
dereplicate_partition(&dir, level, n_buckets) dereplicate_partition(&dir, level, n_buckets)
} else { } else {
+9
View File
@@ -0,0 +1,9 @@
//! Indexing-pipeline algorithms: code that operates on an
//! [`obikindex::KmerIndex`] to build or transform its content — `obikindex`
//! itself is the `index`/`partition`/`layer` data model, not this. Each
//! algorithm is its own submodule: [`partitionner`] (routing raw super-kmers
//! into partitions, then counting), [`dereplicator`] (deduplicating a
//! partition's raw super-kmers before counting).
pub mod dereplicator;
pub mod partitionner;
@@ -6,7 +6,7 @@
//! //!
//! Submodules: [`router`] (`PartitionRouter`, `KmerSpectrum`, the //! Submodules: [`router`] (`PartitionRouter`, `KmerSpectrum`, the
//! routing/counting lifecycle API — partition/layer path naming itself //! routing/counting lifecycle API — partition/layer path naming itself
//! lives on `crate::index::KmerIndex`, not here), [`count`] (unique-kmer //! lives on `obikindex::KmerIndex`, not here), [`count`] (unique-kmer
//! enumeration, MPHF, abundance counting), `kmer_sort` (external sort //! enumeration, MPHF, abundance counting), `kmer_sort` (external sort
//! support for `count`). //! support for `count`).
@@ -6,9 +6,9 @@ use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use crate::index::KmerIndex; use obikindex::KmerIndex;
use obikseq::RoutableSuperKmer; use obikseq::RoutableSuperKmer;
use crate::layer::Layer; use obikindex::layer::Layer;
use obiskio::SKResult; use obiskio::SKResult;
use obisys::{progress_bar, Progress}; use obisys::{progress_bar, Progress};
use rayon::prelude::*; use rayon::prelude::*;
@@ -80,7 +80,7 @@ impl Drop for GuardedIter {
/// ///
/// Holds `&mut KmerIndex` — this is an algorithm operating on an index, not /// Holds `&mut KmerIndex` — this is an algorithm operating on an index, not
/// a data structure of its own; it owns no path-naming knowledge (every /// a data structure of its own; it owns no path-naming knowledge (every
/// path comes from `index.index_dir`/`crate::layer::layer_dir`/ /// path comes from `index.index_dir`/`obikindex::layer::layer_dir`/
/// `Layer::create`), only the transient routing/dereplication/counting /// `Layer::create`), only the transient routing/dereplication/counting
/// state a run needs. /// state a run needs.
/// ///
@@ -291,7 +291,7 @@ impl<'a> PartitionRouter<'a> {
.into_par_iter() .into_par_iter()
.map(|i| { .map(|i| {
let dir = self.layer0_dir(i); let dir = self.layer0_dir(i);
let dedup_path = crate::layer::dereplicated_superkmers_path(&dir); let dedup_path = obikindex::layer::dereplicated_superkmers_path(&dir);
if !dedup_path.exists() { if !dedup_path.exists() {
pb.inc(1); pb.inc(1);
return Ok(()); return Ok(());
@@ -343,9 +343,9 @@ impl<'a> PartitionRouter<'a> {
/// Directory of partition `i`'s layer 0 — every raw/dereplicated /// Directory of partition `i`'s layer 0 — every raw/dereplicated
/// superkmer file and provisional `mphf1.bin`/`counts1.bin` this router /// superkmer file and provisional `mphf1.bin`/`counts1.bin` this router
/// produces lives here, alongside where `build_index_layer` /// produces lives here, alongside where `build_index_layer`
/// (`crate::index`) will later turn it into the real layer 0. /// (`obikindex`) will later turn it into the real layer 0.
fn layer0_dir(&self, i: usize) -> PathBuf { fn layer0_dir(&self, i: usize) -> PathBuf {
crate::layer::layer_dir(&self.index.index_dir(i), 0) obikindex::layer::layer_dir(&self.index.index_dir(i), 0)
} }
fn check_not_closed(&self) -> SKResult<()> { fn check_not_closed(&self) -> SKResult<()> {
@@ -360,7 +360,7 @@ impl<'a> PartitionRouter<'a> {
if self.writers[partition].is_none() { if self.writers[partition].is_none() {
let dir = self.layer0_dir(partition); let dir = self.layer0_dir(partition);
Layer::create(&dir).map_err(|e| io::Error::other(e.to_string()))?; Layer::create(&dir).map_err(|e| io::Error::other(e.to_string()))?;
let file_path = crate::layer::raw_superkmers_path(&dir); let file_path = obikindex::layer::raw_superkmers_path(&dir);
let writer = SKFileWriter::create_with(file_path, Format::Zstd, self.level)?; let writer = SKFileWriter::create_with(file_path, Format::Zstd, self.level)?;
self.writers[partition] = Some(writer); self.writers[partition] = Some(writer);
} }
@@ -2,8 +2,8 @@ use std::collections::HashMap;
use std::fs; use std::fs;
use crate::algorithms::dereplicator::Dereplicator; use crate::algorithms::dereplicator::Dereplicator;
use crate::{IndexConfig, KmerIndex}; use obikindex::{IndexConfig, KmerIndex};
use crate::layer::IndexMode; use obikindex::layer::IndexMode;
use obikrope::Rope; use obikrope::Rope;
use obikseq::SuperKmer; use obikseq::SuperKmer;
use obiskbuilder::build_superkmers; use obiskbuilder::build_superkmers;
@@ -69,7 +69,7 @@ fn pipeline_counts(seqs: &[&[u8]]) -> (u64, u64) {
Dereplicator::new(&index).run(None::<fn(obisys::Progress)>).unwrap(); Dereplicator::new(&index).run(None::<fn(obisys::Progress)>).unwrap();
let part_dir = index.layer_dir(0, 0); let part_dir = index.layer_dir(0, 0);
let dedup_path = crate::layer::dereplicated_superkmers_path(&part_dir); let dedup_path = obikindex::layer::dereplicated_superkmers_path(&part_dir);
if !dedup_path.exists() { if !dedup_path.exists() {
return (0, 0); return (0, 0);
} }
+13
View File
@@ -0,0 +1,13 @@
//! Indexing-pipeline algorithms: code that builds or transforms an
//! `obikindex::KmerIndex`'s content, as opposed to `obikindex` itself,
//! which is the `Index { Partition { Layer } }` data model. Kept as a
//! separate crate — not a module of `obikindex` — so the dependency runs
//! one way only (algorithms depend on the data model, never the reverse)
//! and `obikindex` doesn't grow every algorithm's own dependencies.
//!
//! [`algorithms`] holds each algorithm as its own submodule: `partitionner`
//! (routing raw super-kmers into partitions, then counting), `dereplicator`
//! (deduplicating a partition's raw super-kmers before counting). A future
//! `extensions` module will sit alongside it.
pub mod algorithms;
+1
View File
@@ -18,6 +18,7 @@ obikrope = { path = "../obikrope" }
obisys = { path = "../obisys" } obisys = { path = "../obisys" }
obiskio = { path = "../obiskio" } obiskio = { path = "../obiskio" }
obikindex = { path = "../obikindex", default-features = false } obikindex = { path = "../obikindex", default-features = false }
obikindexer = { path = "../obikindexer" }
obikphylo = { path = "../obikphylo" } obikphylo = { path = "../obikphylo" }
obitaxonomy = { path = "../obitaxonomy" } obitaxonomy = { path = "../obitaxonomy" }
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
+2 -2
View File
@@ -2,8 +2,8 @@ use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use clap::Args; use clap::Args;
use obikindex::algorithms::dereplicator::Dereplicator; use obikindexer::algorithms::dereplicator::Dereplicator;
use obikindex::algorithms::partitionner::PartitionRouter; use obikindexer::algorithms::partitionner::PartitionRouter;
use obikindex::{validate_label, GenomeInfo, IndexConfig, IndexState, KmerIndex}; use obikindex::{validate_label, GenomeInfo, IndexConfig, IndexState, KmerIndex};
use obikindex::layer::IndexMode; use obikindex::layer::IndexMode;
+2 -1
View File
@@ -18,6 +18,7 @@ rayon = "1"
tracing = "0.1.44" tracing = "0.1.44"
[dev-dependencies] [dev-dependencies]
obiread = { path = "../obiread" } obiread = { path = "../obiread" }
obikindexer = { path = "../obikindexer" }
tempfile = "3" tempfile = "3"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
+2 -2
View File
@@ -6,8 +6,8 @@ use obikindex::layer::MphfLayer;
use obisys::Reporter; use obisys::Reporter;
use tempfile::tempdir; use tempfile::tempdir;
use obikindex::algorithms::dereplicator::Dereplicator; use obikindexer::algorithms::dereplicator::Dereplicator;
use obikindex::algorithms::partitionner::PartitionRouter; use obikindexer::algorithms::partitionner::PartitionRouter;
use obikindex::{GenomeInfo, IndexConfig, KmerIndex, MergeMode}; use obikindex::{GenomeInfo, IndexConfig, KmerIndex, MergeMode};
use super::alignment::SnpAlignmentExt; use super::alignment::SnpAlignmentExt;