Implement pack CLI command for matrix packing and layer compaction

Adds the `pack` subcommand to `obikmer2` for persisting presence and count matrices in sparse or dense formats. Introduces the `compact_layer` module in `obikrebuild` to merge multiple partition layers into a single layer in-place via atomic operations. Updates dependencies across `obikdump`, `obikmer2`, and `obikrebuild` to resolve new local crate references. Shifts the architecture from multi-layer accumulation to in-place compaction, removing legacy rebuild modules.
This commit is contained in:
Eric Coissac
2026-08-26 14:48:44 +02:00
parent 96dfb5300b
commit 904d85f33b
12 changed files with 252 additions and 382 deletions
+24 -9
View File
@@ -245,14 +245,25 @@ impl KmerIndex {
/// Reduces per-query file-open overhead from O(n_genomes) to O(1) per partition.
/// Column files are kept in place; packed files take priority when opening.
///
/// If `sparse` is set, presence matrices go one step further, from the
/// dense `.pbmx` form into `obicompactvec::PersistentSparseBitMatrix`'s
/// on-disk format (see `DevDocMD/architecture/siblings.md`) — count
/// matrices are unaffected, sparse count matrices aren't implemented
/// (see the sparse-matrix design plan's "Explicitly deferred").
/// TODO: To be moved somewhere else
pub(crate) fn pack_matrices(&self, sparse: bool) -> OKIResult<()> {
use obicompactvec::{pack_bit_matrix, pack_compact_int_matrix, pack_sparse_bit_matrix};
/// If `sparse` is set, both matrix kinds go one step further, from their
/// dense single-file form into `obicompactvec`'s sparse, deduplicated
/// on-disk formats (`PersistentSparseBitMatrix` for presence,
/// `PersistentSparseCompactIntMatrix` for counts) — smaller and faster
/// for single-row access on real, sparse data; column-oriented access
/// (`--metric` distance matrices) is much slower on the sparse format.
///
/// Public — and not moved out of this crate, despite covering the same
/// "maintenance operation on an already-built index" territory as
/// `obikrebuild` — because `finalize_indexed` calls it internally as the
/// shared tail of every index-building algorithm (`select`/`filter`/
/// `merge`), and `obikindex` cannot depend on `obikrebuild` (wrong
/// direction). Standalone re-packing (`obikmer2 pack`) calls this same
/// method directly.
pub fn pack_matrices(&self, sparse: bool) -> OKIResult<()> {
use obicompactvec::{
pack_bit_matrix, pack_compact_int_matrix, pack_sparse_bit_matrix,
pack_sparse_compact_int_matrix,
};
let n = self.n_partitions();
let order: Vec<usize> = (0..n).collect();
@@ -277,7 +288,11 @@ impl KmerIndex {
}
}
if counts_dir.exists() {
pack_compact_int_matrix(&counts_dir).map_err(OKIError::Io)?;
if sparse {
pack_sparse_compact_int_matrix(&counts_dir).map_err(OKIError::Io)?;
} else {
pack_compact_int_matrix(&counts_dir).map_err(OKIError::Io)?;
}
}
}
Ok(())