#[allow(unused_imports)]
use log::Level::{Debug, Info, Trace};
use log::*;
use ::std::process;
use clap::{Arg, ArgAction, Command};
use std::io::prelude::*;
use std::path::Path;
use std::time::*;
use kmerutils::base::kmergenerator::*;
use kmerutils::sketching::seqblocksketch::{BlockSeqSketcher, BlockSketched, DistBlockSketched};
use kmerutils::sketching::*;
use needletail::FastxReader;
use hnsw_rs::api::AnnT;
use hnsw_rs::prelude::*;
fn init_log() -> u64 {
env_logger::Builder::from_default_env().init();
println!("\n ************** initializing logger *****************\n");
1
}
fn main() {
let _ = init_log();
debug!("entering data sketcher, checking log trace");
let matches = Box::new(
Command::new("datasketcher")
.arg(
Arg::new("file")
.long("file")
.short('f')
.required(true)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(String))
.help("expecting a fastq file"),
)
.arg(
Arg::new("sketch_size")
.long("sketch")
.short('s')
.required(true)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(usize))
.help("expecting sketch size as usize"),
)
.arg(
Arg::new("kmer_size")
.long("kmer")
.short('k')
.required(true)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(usize))
.help("expecting a kmer size"),
)
.arg(
Arg::new("dumpfile")
.long("dumpfile")
.short('d')
.required(true)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(String))
.help("expecting name of dumpfile for signature"),
)
.arg(
Arg::new("block_size")
.long("block_size")
.short('b')
.action(ArgAction::Set)
.value_parser(clap::value_parser!(usize))
.help("-b for blocksize if sketching by block"),
)
.subcommand(
Command::new("ann").about("ann parameters").arg(
Arg::new("nbng")
.long("nb")
.short('n')
.required(true)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(usize))
.help("expecting number of neighbours"),
),
),
)
.get_matches();
let mut do_ann = false;
let mut block_size: usize = 0;
let mut sketch_block = false;
let mut nbng = 0; let fname = matches.get_one::<String>("file").unwrap();
println!("got filename , {}", fname);
let sketch_size = *matches.get_one::<usize>("sketch_size").unwrap();
println!("got sketch_size , {}", sketch_size);
if matches.contains_id("block_size") {
sketch_block = true;
block_size = *matches
.get_one::<usize>("block_size")
.expect("expecting block size");
println!("got block_size , {}", block_size);
}
let kmer_size = *matches
.get_one::<usize>("kmer_size")
.expect("expecting kmer size");
println!("got kmer_size , {}", kmer_size);
let dumpfname = matches.get_one::<String>("dumpfile").unwrap();
println!("got dumpfile , {}", dumpfname);
if let Some(ann_match) = matches.subcommand_matches("ann") {
println!("got ann command");
do_ann = true;
let nbng_decoded = *ann_match.get_one::<usize>("nbng").unwrap();
nbng = nbng_decoded as u8;
println!("got nbng {}", nbng);
}
if !matches.args_present() {
println!(" got no subcommand!");
log::error!(" got no subcommand!");
}
let mut hnsw_opt_seq: Option<Hnsw<u32, DistHamming>> = None;
let mut hnsw_opt_seqblock: Option<Hnsw<BlockSketched, DistBlockSketched>> = None;
if do_ann {
println!("initializing hnsw");
let max_nb_conn = 48.min(3 * nbng as usize);
let ef_search = 200;
log::info!("setting max nb conn to : {:?}", max_nb_conn);
log::info!("setting ef_search to : {:?}", ef_search);
if !sketch_block {
hnsw_opt_seq = Some(Hnsw::<u32, DistHamming>::new(
max_nb_conn,
700000,
16,
ef_search,
DistHamming {},
));
} else {
hnsw_opt_seqblock = Some(Hnsw::<BlockSketched, DistBlockSketched>::new(
max_nb_conn,
700000,
16,
ef_search,
DistBlockSketched {},
));
}
}
let path = Path::new(&fname);
let f_info_res = path.metadata();
match f_info_res {
Ok(meta) => {
let filesize = meta.len();
info!("sketching file {} size : {}", fname, filesize);
}
Err(_e) => {
error!("file does not exist: {:?}", fname);
process::exit(1);
}
}
let start_t = Instant::now();
let mut reader = needletail::parse_fastx_file(path).expect("expecting valid filename");
let sequence_pack = if sketch_block { 5000 } else { 10000 };
log::info!("sketching sequences by pack size {:?}", sequence_pack);
if sketch_block {
log::info!("sketching sequences by blocks of size {:?}", block_size);
} else {
log::info!("sketching sequences in one block each");
}
let kmer_revcomp_hash_fn = |kmer: &Kmer32bit| -> u32 {
let canonical = kmer.reverse_complement().min(*kmer);
probminhash::invhash::int32_hash(canonical.0)
};
let mut sigbuf;
if !sketch_block {
log::info!("allocating whole sequence SeqSketcher");
let sketcher = seqsketchjaccard::SeqSketcher::new(kmer_size, sketch_size);
sigbuf = sketcher.create_signature_dump(dumpfname);
} else {
let sketcher = BlockSeqSketcher::new(block_size, kmer_size, sketch_size);
sigbuf = sketcher.create_signature_dump(dumpfname);
}
let mut nbseq = 0;
loop {
let sequencegroup = readblockseq(&mut reader, sequence_pack);
if sequencegroup.is_empty() {
break;
}
if !sketch_block {
log::info!("sketching entire sequences with probminhash3a algorithm");
let sketcher = seqsketchjaccard::SeqSketcher::new(kmer_size, sketch_size);
let sequencegroup_ref: Vec<&Sequence> = sequencegroup.iter().collect();
let signatures =
sketcher.sketch_probminhash3a(&sequencegroup_ref, kmer_revcomp_hash_fn);
trace!("got nb signatures vector {} ", signatures.len());
let resd = seqsketchjaccard::dump_signatures_block_u32(&signatures, &mut sigbuf);
if resd.is_err() {
println!("\n error occurred dumping signatures");
}
if do_ann {
let mut data_for_hnsw = Vec::<(&Vec<u32>, usize)>::with_capacity(signatures.len());
for i in 0..signatures.len() {
data_for_hnsw.push((&signatures[i], nbseq + i));
}
hnsw_opt_seq
.as_mut()
.unwrap()
.parallel_insert(&data_for_hnsw);
}
} else {
let blocksketcher = BlockSeqSketcher::new(block_size, kmer_size, sketch_size);
let mut tosketch = Vec::<(u32, &Sequence)>::with_capacity(sequencegroup.len());
for i in 0..sequencegroup.len() {
tosketch.push(((nbseq + i) as u32, &sequencegroup[i]));
}
let signatures = blocksketcher.blocksketch_sequences(&tosketch, &kmer_revcomp_hash_fn);
log::trace!("got nb signatures blocks {} ", signatures.len());
blocksketcher.dump_blocks(&mut sigbuf, &signatures);
if do_ann {
let nb_blocks_by_seq = 10; let mut block_rank: usize = 0;
let mut data_for_hnsw = Vec::<(&Vec<BlockSketched>, usize)>::with_capacity(
nb_blocks_by_seq * signatures.len(),
);
for i in 0..signatures.len() {
for j in 0..signatures[i].sketch.len() {
data_for_hnsw.push((&signatures[i].sketch[j], block_rank));
block_rank += 1;
}
}
log::debug!(
"sending (nb seq , nb blocks) in hnsw {:?} , {:?}",
signatures.len(),
block_rank
);
hnsw_opt_seqblock
.as_mut()
.unwrap()
.parallel_insert(&data_for_hnsw);
} }
nbseq += sequencegroup.len();
if nbseq % 1000 == 0 {
println!(" nbseq loaded : {} ", nbseq);
}
} sigbuf.flush().unwrap();
let elapsed_t = start_t.elapsed().as_secs();
println!(" number of sequences loaded {} ", nbseq);
println!(
" elapsed time (s) in sketching [inserting in hnsw] data file {} ",
elapsed_t
);
if do_ann {
let res_dump;
if !sketch_block {
hnsw_opt_seq.as_ref().unwrap().dump_layer_info();
let mut hnswname = dumpfname.clone();
hnswname.push_str("-ann");
println!(" dumping sketch hnsw in {:?} files", hnswname);
let cwd = std::path::PathBuf::from(".");
res_dump = hnsw_opt_seq.as_mut().unwrap().file_dump(&cwd, &hnswname);
} else {
hnsw_opt_seqblock.as_ref().unwrap().dump_layer_info();
let mut hnswname = dumpfname.clone();
hnswname.push_str("-ann");
println!(" dumping block sketch hnsw in {:?} files", hnswname);
let cwd = std::path::PathBuf::from(".");
res_dump = hnsw_opt_seqblock
.as_mut()
.unwrap()
.file_dump(&cwd, &hnswname);
}
if res_dump.is_ok() {
println!(" hnsw dump suceeded");
} else {
println!(" hnsw dump failed");
}
} }
fn readblockseq(reader: &mut Box<dyn FastxReader>, nbseq: usize) -> Vec<Sequence> {
trace!("entering in readblockseq");
let mut veqseq = Vec::<Sequence>::with_capacity(nbseq);
let mut nb_bad_sequence = 0;
while let Some(record) = reader.next() {
let seqrec = record.expect("invalid record");
let nb_bad = count_non_acgt(&seqrec.seq());
if nb_bad > 0 {
nb_bad_sequence += 1;
continue;
}
let newseq = Sequence::new(&seqrec.seq(), 2);
if veqseq.len() < veqseq.capacity() {
veqseq.push(newseq.clone());
}
if veqseq.len() == nbseq {
break;
}
} if nb_bad_sequence > 0 && log_enabled!(Info) {
info!(" number of non acgt sequences {} ", nb_bad_sequence);
}
trace!("returning from readblockseq , nb seq : {} ", veqseq.len());
veqseq
}