Add configurable sparsity flag to index finalization and merge CLI

Extends `KmerIndex::finalize_indexed` to accept a `sparse` boolean parameter, enabling control over whether output presence matrices are packed in a compact sparse format or stored densely. The merge command now exposes a `--dense` flag to invert this setting, defaulting to the existing sparse representation. Call sites in select and rebuild modules pass `false` to preserve current behavior. A benchmark lock file is also added for dependency tracking.
This commit is contained in:
Eric Coissac
2026-08-22 17:36:50 +02:00
parent bb380d0c7d
commit cbf6893f38
6 changed files with 23 additions and 8 deletions
+6 -5
View File
@@ -52,9 +52,10 @@ pub trait IndexBuilder: Sized {
/// Mark `output` as fully indexed, pack its column matrices, and reopen it. /// Mark `output` as fully indexed, pack its column matrices, and reopen it.
/// ///
/// Shared tail of the `select`/`rebuild` construction paths, once their /// Shared tail of the `select`/`rebuild`/`merge` construction paths,
/// partitions have been written. /// once their partitions have been written. `sparse` selects the
fn finalize_indexed<P: AsRef<Path>>(output: P, rep: &mut Reporter) -> OKIResult<Self>; /// on-disk presence-matrix format (see `pack_matrices`).
fn finalize_indexed<P: AsRef<Path>>(output: P, rep: &mut Reporter, sparse: bool) -> OKIResult<Self>;
/// Current construction state, read fresh from `index.meta` (see /// Current construction state, read fresh from `index.meta` (see
/// `DevDocMD/implementation/partition_layer_cache.md`, "(11)" — no /// `DevDocMD/implementation/partition_layer_cache.md`, "(11)" — no
@@ -99,12 +100,12 @@ impl IndexBuilder for KmerIndex {
Ok(idx) Ok(idx)
} }
fn finalize_indexed<P: AsRef<Path>>(output: P, rep: &mut Reporter) -> OKIResult<Self> { fn finalize_indexed<P: AsRef<Path>>(output: P, rep: &mut Reporter, sparse: bool) -> OKIResult<Self> {
let output = output.as_ref(); let output = output.as_ref();
let idx = KmerIndex::open(output)?; let idx = KmerIndex::open(output)?;
idx.meta.mark_indexed().map_err(OKIError::Io)?; idx.meta.mark_indexed().map_err(OKIError::Io)?;
let t_pack = Stage::start("pack"); let t_pack = Stage::start("pack");
idx.pack_matrices(false)?; idx.pack_matrices(sparse)?;
rep.push(t_pack.stop()); rep.push(t_pack.stop());
Ok(idx) Ok(idx)
} }
+5
View File
@@ -28,6 +28,10 @@ pub struct MergeArgs {
/// Disambiguate duplicate genome labels by appending .1, .2, … instead of erroring /// Disambiguate duplicate genome labels by appending .1, .2, … instead of erroring
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
pub rename_duplicates: bool, pub rename_duplicates: bool,
/// Pack the output's presence matrices in the dense format instead of the default sparse one
#[arg(long, default_value_t = false)]
pub dense: bool,
} }
pub fn run(args: MergeArgs) { pub fn run(args: MergeArgs) {
@@ -89,6 +93,7 @@ pub fn run(args: MergeArgs) {
let mut merge = Merge::new(&source_refs, &args.output, mode) let mut merge = Merge::new(&source_refs, &args.output, mode)
.force(args.force) .force(args.force)
.rename_duplicates(args.rename_duplicates) .rename_duplicates(args.rename_duplicates)
.sparse(!args.dense)
.on_progress(|_: Progress| pb.inc(1)); .on_progress(|_: Progress| pb.inc(1));
let dst = merge.run().unwrap_or_else(|e| { let dst = merge.run().unwrap_or_else(|e| {
+10 -1
View File
@@ -45,6 +45,7 @@ pub struct Merge<'a> {
mode: MergeMode, mode: MergeMode,
force: bool, force: bool,
rename_duplicates: bool, rename_duplicates: bool,
sparse: bool,
reporter: Reporter, reporter: Reporter,
on_progress: Option<Box<dyn FnMut(Progress) + Send + 'a>>, on_progress: Option<Box<dyn FnMut(Progress) + Send + 'a>>,
} }
@@ -57,6 +58,7 @@ impl<'a> Merge<'a> {
mode, mode,
force: false, force: false,
rename_duplicates: false, rename_duplicates: false,
sparse: true,
reporter: Reporter::new(), reporter: Reporter::new(),
on_progress: None, on_progress: None,
} }
@@ -76,6 +78,13 @@ impl<'a> Merge<'a> {
self self
} }
/// Pack the merged output's presence matrices in the compact sparse
/// format rather than dense (default: `true`).
pub fn sparse(mut self, v: bool) -> Self {
self.sparse = v;
self
}
/// Progress callback, called once per completed partition. /// Progress callback, called once per completed partition.
pub fn on_progress(mut self, cb: impl FnMut(Progress) + Send + 'a) -> Self { pub fn on_progress(mut self, cb: impl FnMut(Progress) + Send + 'a) -> Self {
self.on_progress = Some(Box::new(cb)); self.on_progress = Some(Box::new(cb));
@@ -322,7 +331,7 @@ impl Algorithm for Merge<'_> {
// ── Finalize: pack matrices, mark indexed, reopen ────────────────────── // ── Finalize: pack matrices, mark indexed, reopen ──────────────────────
let t = Stage::start("finalize"); let t = Stage::start("finalize");
let dst = KmerIndex::finalize_indexed(&output, &mut self.reporter)?; let dst = KmerIndex::finalize_indexed(&output, &mut self.reporter, self.sparse)?;
self.reporter.push(t.stop()); self.reporter.push(t.stop());
Ok(dst) Ok(dst)
+1 -1
View File
@@ -74,6 +74,6 @@ impl KmerIndex {
rep.push(t.stop()); rep.push(t.stop());
KmerIndex::finalize_indexed(output, rep) KmerIndex::finalize_indexed(output, rep, false)
} }
} }
+1 -1
View File
@@ -82,7 +82,7 @@ impl KmerIndex {
pb.finish_and_clear(); pb.finish_and_clear();
rep.push(t.stop()); rep.push(t.stop());
KmerIndex::finalize_indexed(output, rep) KmerIndex::finalize_indexed(output, rep, false)
} }
/// Rewrite the genome columns of this index in-place according to `specs`. /// Rewrite the genome columns of this index in-place according to `specs`.