Add unified matrix builder abstraction and k-mer filtering CLI

Introduces a unified MatrixBuilder abstraction for persistent bit and integer matrices, replacing custom dispatch enums and boolean flags across consumers. Refactors index, merge, and select layers to adopt explicit merge modes and centralized lifecycle management. Adds a complete k-mer filtering implementation with partition-level processing, progress tracking, and a new CLI subcommand supporting configurable predicates and thresholds.
This commit is contained in:
Eric Coissac
2026-08-26 14:19:15 +02:00
parent b7a8b5e6cf
commit 354e6f9bf1
17 changed files with 588 additions and 125 deletions
-25
View File
@@ -1,25 +0,0 @@
use crate::index::error::{OKIError, OKIResult};
use obicompactvec::{PersistentBitVecBuilder, PersistentCompactIntVecBuilder};
// ── ColBuilder ────────────────────────────────────────────────────────────────
pub enum ColBuilder {
Bit(PersistentBitVecBuilder),
Int(PersistentCompactIntVecBuilder),
}
impl ColBuilder {
pub fn set_val(&mut self, slot: usize, value: u32) {
match self {
ColBuilder::Bit(b) => b.set(slot, value > 0),
ColBuilder::Int(b) => b.set(slot, value),
}
}
pub fn close(self) -> OKIResult<()> {
match self {
ColBuilder::Bit(b) => b.close().map_err(OKIError::Io),
ColBuilder::Int(b) => b.close().map_err(OKIError::Io),
}
}
}
-2
View File
@@ -2,12 +2,10 @@ pub mod error;
pub mod meta;
pub mod state;
mod builder;
mod common;
mod kmer_index;
pub use error::{OKIError, OKIResult};
pub use builder::IndexBuilder;
pub use common::ColBuilder;
pub use kmer_index::KmerIndex;
pub use meta::{GenomeInfo, IndexConfig, IndexMeta, META_FILENAME};
pub use state::IndexState;
+47 -2
View File
@@ -1,7 +1,7 @@
// use crate::layer::utils::layer_dir;
use obicompactvec::{
BinaryMatrix, PersistentBitMatrix, PersistentBitMatrixBuilder, PersistentCompactIntMatrix,
PersistentCompactIntMatrixBuilder, PersistentSparseBitMatrix,
BinaryMatrix, ColBuilder, MatrixBuilder, PersistentBitMatrix, PersistentBitMatrixBuilder,
PersistentCompactIntMatrix, PersistentCompactIntMatrixBuilder, PersistentSparseBitMatrix,
};
use obikseq::CanonicalKmer;
use obiskio::{UnitigFileReader, UnitigFileWriter};
@@ -281,6 +281,51 @@ impl TypedLayer<()> {
MphfLayer::build(out_dir, block_bits, mode, &mut |_, _| Ok(()))
}
/// Build MPHF + evidence + a full multi-column matrix in one MPHF pass —
/// unlike `TypedLayer::<PersistentCompactIntMatrix>::build`'s single
/// `count_of` column, this fills `n_cols` columns at once from `row_of`,
/// so the (expensive) MPHF construction only runs once regardless of how
/// many genome columns the destination has. Columns are written via
/// random-access, mmap-backed `ColBuilder::set_val(slot, _)` calls as
/// the MPHF reports each kmer's slot — no ordering assumption, no
/// buffering of the matrix in memory.
///
/// `presence` picks the on-disk matrix kind (bit vs int), same meaning
/// as `obicompactvec::MatrixBuilder::new`. `row_of(kmer)` must return
/// exactly `n_cols` values, one per destination column, in column order.
pub fn build_with_matrix(
out_dir: &Path,
block_bits: u8,
mode: &IndexMode,
presence: bool,
n_cols: usize,
row_of: impl Fn(CanonicalKmer) -> Box<[u32]>,
) -> OKIResult<usize> {
let data_dir = out_dir.join(if presence { PRESENCE_DIR } else { COUNTS_DIR });
fs::create_dir_all(&data_dir).map_err(OKIError::Io)?;
let n = UnitigFileReader::open_sequential(&out_dir.join(UNITIGS_FILE))?.n_kmers();
let mut mb = MatrixBuilder::new(presence, n, &data_dir).map_err(OKIError::Io)?;
let mut cols: Vec<ColBuilder> = (0..n_cols)
.map(|_| mb.add_col())
.collect::<std::io::Result<_>>()
.map_err(OKIError::Io)?;
let n_built = MphfLayer::build(out_dir, block_bits, mode, &mut |slot, kmer| {
let row = row_of(kmer);
for (c, col) in cols.iter_mut().enumerate() {
col.set_val(slot, row[c]);
}
Ok(())
})?;
for col in cols {
col.close().map_err(OKIError::Io)?;
}
mb.close().map_err(OKIError::Io)?;
Ok(n_built)
}
/// Create a presence matrix for a set-membership layer (first merge).
pub fn init_presence_matrix(layer_dir: &Path, n_kmers: usize) -> OKIResult<()> {
let presence_dir = layer_dir.join(PRESENCE_DIR);
-1
View File
@@ -17,6 +17,5 @@ pub use index::{
IndexBuilder, IndexConfig, IndexMeta, IndexState,
KmerIndex, OKIError, OKIResult,
META_FILENAME,
ColBuilder,
};
pub use index::meta;