#![allow(clippy::unnecessary_unwrap)]
use log::*;
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use std::hash::{BuildHasher, BuildHasherDefault, Hash, Hasher};
use fnv::{FnvBuildHasher, FnvHashMap};
use std::io;
use std::io::{ErrorKind, Read, Write};
use std::fs;
use std::fs::OpenOptions;
use crate::nohasher::*;
use crate::base::kmergenerator::*;
use rayon::prelude::*;
use hnsw_rs::prelude::*;
use probminhash::probminhasher::*;
const MAGIC_BLOCKSIG_DUMP: u32 = 0xceabbadd;
#[derive(Clone, Serialize, Deserialize)]
pub struct BlockSketched {
numseq: u32,
numblock: u32,
sketch: Vec<u32>,
}
impl BlockSketched {
pub fn new(numseq: u32, numblock: u32, sketch_size: u32) -> BlockSketched {
let sketch = Vec::<u32>::with_capacity(sketch_size as usize);
BlockSketched {
numseq,
numblock,
sketch,
}
}
pub fn get_skech_slice(&self) -> &[u32] {
&self.sketch
}
fn dump(&self, out: &mut dyn Write) {
out.write_all(&self.numseq.to_le_bytes()).unwrap();
out.write_all(&self.numblock.to_le_bytes()).unwrap();
for i in 0..self.sketch.len() {
out.write_all(&self.sketch[i].to_le_bytes()).unwrap();
}
} }
pub struct BlockSketchedSeq {
numseq: usize,
pub sketch: Vec<Vec<BlockSketched>>,
}
pub struct BlockSeqSketcher {
sig_size: u8,
block_size: usize,
kmer_size: usize,
sketch_size: usize,
}
impl BlockSeqSketcher {
pub fn new(block_size: usize, kmer_size: usize, sketch_size: usize) -> BlockSeqSketcher {
BlockSeqSketcher {
sig_size: 4_u8,
block_size,
kmer_size,
sketch_size,
}
}
pub fn blocksketch_sequence<F>(
&self,
numseq: usize,
seq: &Sequence,
fhash: &F,
) -> BlockSketchedSeq
where
F: Fn(&Kmer32bit) -> u32 + Sync + Send,
{
assert!(seq.size() > 0);
let nb_blocks = if seq.size() % self.block_size == 0 {
seq.size() / self.block_size
} else {
1 + seq.size() / self.block_size
};
assert!(nb_blocks > 0);
let mut sketch = Vec::<Vec<BlockSketched>>::with_capacity(nb_blocks);
let mut kmergen = KmerSeqIterator::<Kmer32bit>::new(self.kmer_size as u8, seq);
kmergen.set_range(0, seq.size()).unwrap();
let mut wa: FnvHashMap<u32, f64> =
FnvHashMap::with_capacity_and_hasher(self.block_size, FnvBuildHasher::default());
for numblock in 0..nb_blocks {
let mut pminhasha = ProbMinHash3a::<u32, NoHashHasher>::new(self.sketch_size, 0);
let mut kmer_pos = 0;
while let Some(kmer) = kmergen.next() {
let hashval = fhash(&kmer);
*wa.entry(hashval).or_insert(0.) += 1.;
if kmer_pos == self.block_size - 1 {
break;
} else {
kmer_pos += 1;
}
} pminhasha.hash_weigthed_hashmap(&wa);
let siga = pminhasha.get_signature();
let current_block = vec![BlockSketched {
numseq: numseq as u32,
numblock: numblock as u32,
sketch: siga.clone(),
}];
sketch.push(current_block);
wa.clear();
} BlockSketchedSeq { numseq, sketch }
}
pub fn blocksketch_sequences<F>(
&self,
pack_seq: &[(u32, &Sequence)],
fhash: &F,
) -> Vec<BlockSketchedSeq>
where
F: Fn(&Kmer32bit) -> u32 + Sync + Send,
{
let block_sketched: Vec<BlockSketchedSeq> = pack_seq
.into_par_iter()
.map(|(i, seq)| self.blocksketch_sequence(*i as usize, seq, fhash))
.collect();
block_sketched
}
pub fn dump_blocks(&self, out: &mut dyn Write, seqblocks: &[BlockSketchedSeq]) {
for seqblock in seqblocks {
let seqnum = seqblock.numseq as u32;
out.write_all(&seqnum.to_le_bytes()).unwrap();
let nbblock_u32 = seqblock.sketch.len() as u32;
assert!(nbblock_u32 > 0);
out.write_all(&nbblock_u32.to_le_bytes()).unwrap();
for j in 0..seqblock.sketch.len() {
let block = &(seqblock.sketch)[j][0];
block.dump(out);
}
}
}
pub fn create_signature_dump(&self, dumpfname: &String) -> io::BufWriter<fs::File> {
let dumpfile_res = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(dumpfname);
let dumpfile = if dumpfile_res.is_ok() {
dumpfile_res.unwrap()
} else {
println!("cannot open {}", dumpfname);
std::process::exit(1);
};
let sketch_size_u32 = self.sketch_size as u32;
let kmer_size_u32 = self.kmer_size as u32;
let blocksize_u32 = self.block_size as u32;
let mut sigbuf: io::BufWriter<fs::File> =
io::BufWriter::with_capacity(1_000_000_000, dumpfile);
sigbuf
.write_all(&MAGIC_BLOCKSIG_DUMP.to_le_bytes())
.unwrap();
sigbuf.write_all(&self.sig_size.to_le_bytes()).unwrap();
sigbuf.write_all(&sketch_size_u32.to_le_bytes()).unwrap();
sigbuf.write_all(&kmer_size_u32.to_le_bytes()).unwrap();
sigbuf.write_all(&blocksize_u32.to_le_bytes()).unwrap();
sigbuf
} }
pub struct SigBlockSketchFileReader {
_fname: String,
sig_size: u8,
sketch_size: usize,
kmer_size: u8,
block_size: u32,
signature_buf: io::BufReader<fs::File>,
}
impl SigBlockSketchFileReader {
pub fn new(fname: &String) -> Result<SigBlockSketchFileReader, String> {
let dumpfile_res = OpenOptions::new().read(true).open(fname);
let dumpfile = if dumpfile_res.is_ok() {
dumpfile_res.unwrap()
} else {
println!("cannot open {}", fname);
std::process::exit(1);
};
let mut signature_buf: io::BufReader<fs::File> =
io::BufReader::with_capacity(1_000_000_000, dumpfile);
let mut buf_u32 = [0u8; 4];
let mut io_res;
io_res = signature_buf.read_exact(&mut buf_u32);
if io_res.is_err() {
println!("SigBlockSketchFileReader could no read magic");
return Err(String::from("SigBlockSketchFileReader could no read magic"));
}
let magic = u32::from_le_bytes(buf_u32);
if magic != MAGIC_BLOCKSIG_DUMP {
println!("file {} is not a dump of signature", fname);
return Err(String::from("file is not a dump of signature"));
}
io_res = signature_buf.read_exact(&mut buf_u32);
if io_res.is_err() {
println!("SigBlockSketchFileReader could no read sketch_size");
return Err(String::from(
"SigBlockSketchFileReader could no read sketch_size",
));
}
let sig_size = u32::from_le_bytes(buf_u32);
if sig_size != 4 {
println!("SigBlockSketchFileReader could no read sketch_size");
return Err(String::from(
"SigBlockSketchFileReader , sig_size != 4 not yet implemented",
));
}
io_res = signature_buf.read_exact(&mut buf_u32);
if io_res.is_err() {
println!("SigBlockSketchFileReader could no read sketch_size");
return Err(String::from(
"SigBlockSketchFileReader could no read sketch_size",
));
}
let sketch_size = u32::from_le_bytes(buf_u32);
trace!("read sketch size {}", sketch_size);
io_res = signature_buf.read_exact(&mut buf_u32);
if io_res.is_err() {
println!("SigBlockSketchFileReader could no read kmer_size");
return Err(String::from(
"SigBlockSketchFileReader could no read kmer_size",
));
}
let kmer_size = u32::from_le_bytes(buf_u32);
trace!("read kmer_size {}", kmer_size);
io_res = signature_buf.read_exact(&mut buf_u32);
if io_res.is_err() {
println!("SigBlockSketchFileReader could no read block_size");
return Err(String::from(
"SigBlockSketchFileReader could no read block_size",
));
}
let block_size = u32::from_le_bytes(buf_u32);
trace!("read block_size {}", block_size);
Ok(SigBlockSketchFileReader {
_fname: fname.clone(),
sig_size: sig_size as u8,
sketch_size: sketch_size as usize,
kmer_size: kmer_size as u8,
block_size,
signature_buf,
})
}
pub fn get_kmer_size(&self) -> u8 {
self.kmer_size
}
pub fn get_signature_length(&self) -> usize {
self.sketch_size
}
pub fn get_signature_size(&self) -> usize {
self.sig_size as usize
}
pub fn get_block_size(&self) -> usize {
self.block_size as usize
}
pub fn next(&mut self) -> Option<Vec<Vec<u32>>> {
let numseq: u32;
let mut buf_u32 = [0u8; 4];
let io_res = self.signature_buf.read_exact(&mut buf_u32);
if io_res.is_err() {
println!("cannot read sequence num");
match io_res.err().unwrap().kind() {
ErrorKind::UnexpectedEof => return None,
_ => {
println!("an unexpected error occurred reading signature buffer");
std::process::exit(1);
}
}
} else {
numseq = u32::from_le_bytes(buf_u32);
}
let io_res = self.signature_buf.read_exact(&mut buf_u32);
let nbblock: u32 = if io_res.is_err() {
println!("cannot read number of blocks for sequence {} ", numseq);
std::process::exit(1);
} else {
u32::from_le_bytes(buf_u32)
};
let nb_bytes = self.sketch_size * std::mem::size_of::<u32>();
let mut buf: Vec<u8> = (0..nb_bytes).map(|_| 0u8).collect();
let mut sig = Vec::<Vec<u32>>::with_capacity(nbblock as usize);
for _ in 0..nbblock as usize {
let io_res = self.signature_buf.read_exact(buf.as_mut_slice());
if io_res.is_err() {
println!("an unexpected error occurred reading signature buffer");
std::process::exit(1);
} else {
let mut sigblock = Vec::<u32>::with_capacity(self.sketch_size);
for j in 0..self.sketch_size {
buf_u32.copy_from_slice(&buf[j * 4..4 * (j + 1)]);
sigblock.push(u32::from_le_bytes(buf_u32));
}
sig.push(sigblock);
}
}
Some(sig)
} }
#[derive(Default)]
pub struct DistBlockSketched {}
impl Distance<BlockSketched> for DistBlockSketched {
fn eval(&self, va: &[BlockSketched], vb: &[BlockSketched]) -> f32 {
assert!(va.len() == 1 && vb.len() == 1);
if va[0].numseq == vb[0].numseq {
return 1.;
}
let nb_diff = distance_jaccard_serial(&va[0].sketch, &vb[0].sketch);
nb_diff as f32 / va[0].sketch.len() as f32
} }
#[inline]
fn distance_jaccard_serial(va: &[u32], vb: &[u32]) -> u32 {
assert_eq!(va.len(), vb.len());
let dist = va.iter().zip(vb.iter()).filter(|t| t.0 != t.1).count();
dist as u32
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use rand::distr::{Distribution, Uniform};
fn log_init_test() {
let mut builder = env_logger::Builder::from_default_env();
let _ = builder.is_test(true).try_init();
}
#[test]
fn test_block_32bit_sketch() {
log_init_test();
let kmer_revcomp_hash_fn = |kmer: &Kmer32bit| -> u32 {
let canonical = kmer.reverse_complement().min(*kmer);
probminhash::invhash::int32_hash(canonical.0)
};
let seqstra = String::from("TCAAAGGGAAACATTCAAAATCAGTATGCGCCCGTTCAGTTACGTATTGCTCTCGCCGTAGGCCTAATGAGATGGGCTGGGTACAGAG");
let seqa = Sequence::new(seqstra.as_bytes(), 2);
let seqstrb = String::from("TCAAAGGGAAATTTTTTTCATTCAAAATCAGTATGCGCCCGTTCAGTTACGTATTGCTCTCGCCGTAGGCCTAATGATTTTTTTGATGGGCTGGGTACAGAG");
let seqb = Sequence::new(seqstrb.as_bytes(), 2);
let block_size = 10;
let kmer_size = 3;
let sketch_size = 6;
let sketcher = BlockSeqSketcher::new(block_size, kmer_size, sketch_size);
let sketcha = sketcher.blocksketch_sequence(1, &seqa, &kmer_revcomp_hash_fn);
let sketchb = sketcher.blocksketch_sequence(2, &seqb, &kmer_revcomp_hash_fn);
println!("sketcha has number of blocks = {:?}", sketcha.sketch.len());
println!("sketchb has number of blocks = {:?}", sketchb.sketch.len());
let mydist = DistBlockSketched {};
assert_eq!(mydist.eval(&sketcha.sketch[0], &sketcha.sketch[0]), 1.);
let dist_1 = mydist.eval(&sketcha.sketch[0], &sketchb.sketch[0]);
println!("dist_1 = {:?}", dist_1);
log::info!("dist_1 = {:?}", dist_1);
let dist_2 = mydist.eval(&sketcha.sketch[1], &sketchb.sketch[1]);
println!("dist_2 = {:?}", dist_2);
log::info!("dist_2 = {:?}", dist_2);
} }