diff --git a/src/dense.rs b/src/dense.rs index d9769bed5812b451fcb9e49071c88517ecd022b1..71f7fb0a8560df94ece9cf6392ca433072c25ec4 100644 --- a/src/dense.rs +++ b/src/dense.rs @@ -3,31 +3,19 @@ use std::ops::IndexMut; /// An aligned dense matrix of with a constant number of columns. #[derive(Debug, Clone)] -pub struct DenseMatrix<T: Default, const C: usize = 32> { +pub struct DenseMatrix<T: Default + Copy, const C: usize = 32> { data: Vec<T>, indices: Vec<usize>, } -impl<T: Default, const C: usize> DenseMatrix<T, C> { - /// Create a new matrix +impl<T: Default + Copy, const C: usize> DenseMatrix<T, C> { + /// Create a new matrix with the given number of rows. pub fn new(rows: usize) -> Self { - // alway over-allocate columns to avoid alignment issues. - let c = C.next_power_of_two(); - // allocate data block - let mut data = Vec::with_capacity( (rows+1) * c ); // FIXME - data.resize_with(data.capacity(), T::default); - // compute offset to have proper alignment - let mut offset = 0; - while data[offset..].as_ptr() as usize % c > 0 { - offset += 1 - } - // record row coordinates (only really useful when aligned) - let mut indices = Vec::with_capacity(rows); - for i in 0..rows { - indices.push(offset + i * c) - } - // finish matrix - Self { data, indices } + let data = Vec::new(); + let indices = Vec::new(); + let mut matrix = Self { data, indices }; + matrix.resize(rows); + matrix } /// The number of columns of the matrix. @@ -39,9 +27,43 @@ impl<T: Default, const C: usize> DenseMatrix<T, C> { pub fn rows(&self) -> usize { self.indices.len() } + + /// Change the number of rows of the matrix. + pub fn resize(&mut self, rows: usize) { + + // alway over-allocate columns to avoid alignment issues. + let c = C.next_power_of_two(); + + // + let previous_rows = self.rows(); + let previous_offset = if self.rows() == 0 { 0 } else { self.indices[0] }; + + // allocate data block + self.data.resize_with((rows + 1) * c, T::default); + + // compute offset to aligned memory + let mut offset = 0; + while self.data[offset..].as_ptr() as usize % c > 0 { + offset += 1 + } + + // copy data in case alignment offset changed + if previous_offset != offset { + self.data.as_mut_slice().copy_within( + previous_offset..previous_offset+(previous_rows*c), + offset + ); + } + + // record row coordinates + self.indices.resize(rows, 0); + for i in 0..rows { + self.indices[i] = offset + i * c; + } + } } -impl<T: Default, const C: usize> Index<usize> for DenseMatrix<T, C> { +impl<T: Default + Copy, const C: usize> Index<usize> for DenseMatrix<T, C> { type Output = [T]; fn index(&self, index: usize) -> &Self::Output { let row = self.indices[index]; @@ -49,9 +71,42 @@ impl<T: Default, const C: usize> Index<usize> for DenseMatrix<T, C> { } } -impl<T: Default, const C: usize> IndexMut<usize> for DenseMatrix<T, C> { +impl<T: Default + Copy, const C: usize> IndexMut<usize> for DenseMatrix<T, C> { fn index_mut(&mut self, index: usize) -> &mut Self::Output { let row = self.indices[index]; &mut self.data[row..row + C] } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_resize() { + let mut dense = DenseMatrix::<u64, 32>::new(4); + + for i in 0..4 { + dense[i][0] = (i + 1) as u64; + } + assert_eq!(dense[0][0], 1); + assert_eq!(dense[1][0], 2); + assert_eq!(dense[2][0], 3); + assert_eq!(dense[3][0], 4); + assert_eq!(dense[0].as_ptr() as usize % 4, 0); + + dense.resize(256); + assert_eq!(dense[0][0], 1); + assert_eq!(dense[1][0], 2); + assert_eq!(dense[2][0], 3); + assert_eq!(dense[3][0], 4); + assert_eq!(dense[0].as_ptr() as usize % 4, 0); + + dense.resize(512); + assert_eq!(dense[0][0], 1); + assert_eq!(dense[1][0], 2); + assert_eq!(dense[2][0], 3); + assert_eq!(dense[3][0], 4); + assert_eq!(dense[0].as_ptr() as usize % 4, 0); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3f4d18870094f1f158333499933d090eedf33ccc..dd7d4708431bb167f4c986d2cfc682d5c4447d6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub use pli::Pipeline; pub use pwm::Background; pub use pwm::CountMatrix; pub use pwm::ProbabilityMatrix; -pub use pwm::WeightMatrix; pub use pwm::StripedScores; +pub use pwm::WeightMatrix; pub use seq::EncodedSequence; pub use seq::StripedSequence; diff --git a/src/pli.rs b/src/pli.rs index 29a8ba13dbed7941dbf6f5d7eb88ee8aae3f3e1a..74f6804763e4461a8d3a5218a23506933e00b576 100644 --- a/src/pli.rs +++ b/src/pli.rs @@ -124,7 +124,7 @@ impl Pipeline<DnaAlphabet, __m256> { let mut s3 = _mm256_setzero_ps(); let mut s4 = _mm256_setzero_ps(); for j in 0..pwm.data.rows() { - let x = _mm256_load_si256(seq.data[i+j].as_ptr() as *const __m256i); + let x = _mm256_load_si256(seq.data[i + j].as_ptr() as *const __m256i); let row = pwm.data[j].as_ptr(); // compute probabilities using an external lookup table let p1 = _mm256_i32gather_ps(row, _mm256_shuffle_epi8(x, m1), S); diff --git a/src/pwm.rs b/src/pwm.rs index 8d2cdba2441d146f8abe570ea1a7f83569558add..1ffe66fcb85762886fdfbc4c16c7b3ea1b69934f 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -182,4 +182,4 @@ impl<const C: usize> StripedScores<C> { } vec } -} \ No newline at end of file +} diff --git a/tests/dna.rs b/tests/dna.rs index 8559c1422c5ea7b41d7cb6090e5777c3347e08e6..cd81acd0ac7f78eb19e146ecaaafc97172eeb145 100644 --- a/tests/dna.rs +++ b/tests/dna.rs @@ -34,7 +34,9 @@ fn test_score_generic() { let cm = CountMatrix::<DnaAlphabet, { DnaAlphabet::K }>::from_sequences( "MX000001", - PATTERNS.iter().map(|x| EncodedSequence::from_text(x).unwrap()), + PATTERNS + .iter() + .map(|x| EncodedSequence::from_text(x).unwrap()), ) .unwrap(); let pbm = cm.to_probability([0.1, 0.1, 0.1, 0.1, 0.0]); @@ -47,10 +49,10 @@ fn test_score_generic() { assert_eq!(scores.len(), EXPECTED.len()); for i in 0..scores.len() { assert!( - (scores[i] - EXPECTED[i]).abs() < 1e-5, - "{} != {} at position {}", - scores[i], - EXPECTED[i], + (scores[i] - EXPECTED[i]).abs() < 1e-5, + "{} != {} at position {}", + scores[i], + EXPECTED[i], i ); } @@ -66,7 +68,9 @@ fn test_score_avx2() { let cm = CountMatrix::<DnaAlphabet, { DnaAlphabet::K }>::from_sequences( "MX000001", - PATTERNS.iter().map(|x| EncodedSequence::from_text(x).unwrap()), + PATTERNS + .iter() + .map(|x| EncodedSequence::from_text(x).unwrap()), ) .unwrap(); let pbm = cm.to_probability([0.1, 0.1, 0.1, 0.1, 0.0]); @@ -81,10 +85,10 @@ fn test_score_avx2() { // assert_eq!(scores[0], -23.07094); // -23.07094 for i in 0..EXPECTED.len() { assert!( - (scores[i] - EXPECTED[i]).abs() < 1e-5, - "{} != {} at position {}", - scores[i], - EXPECTED[i], + (scores[i] - EXPECTED[i]).abs() < 1e-5, + "{} != {} at position {}", + scores[i], + EXPECTED[i], i ); }