use super::{banded::*, test_data::*, *};
use crate::{
alignment::profile::StripedProfile,
data::{mappings::DNA_PROFILE_MAP, matrices::WeightMatrix},
};
macro_rules! test_sw_simd_align {
($profile_seq:expr, $other_seq:expr, $int_type:ty, $uint_type:ty, $lanes:expr) => {{
let weights = WeightMatrix::new(&DNA_PROFILE_MAP, 2, -5, Some(b'N'));
let profile_scalar = ScalarProfile::new($profile_seq, &weights, GAP_OPEN, GAP_EXTEND).unwrap();
let profile_simd =
StripedProfile::<$int_type, $lanes, 5>::new($profile_seq, &weights, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile_simd.sw_score($other_seq);
let aln_scalar = profile_scalar.sw_align(SeqSrc::Reference($other_seq)).unwrap();
let aln_simd = profile_simd.sw_align(SeqSrc::Reference($other_seq)).unwrap();
assert_eq!(score, MaybeAligned::Some(aln_scalar.score));
assert_eq!(aln_scalar, aln_simd);
let profile_simd =
StripedProfile::<$uint_type, $lanes, 5>::new($profile_seq, &weights.to_biased_matrix(), GAP_OPEN, GAP_EXTEND)
.unwrap();
let aln_simd = profile_simd.sw_align(SeqSrc::Reference($other_seq)).unwrap();
assert_eq!(aln_scalar, aln_simd, "SIMD ALIGNMENT");
let ScoreEnds {
score,
ref_end,
query_end,
} = profile_simd.sw_score_ends(SeqSrc::Reference($other_seq)).unwrap();
assert_eq!(score, aln_scalar.score);
assert_eq!(aln_scalar.ref_range.end, ref_end, "REFERENCE END");
assert_eq!(aln_scalar.query_range.end, query_end, "QUERY END");
let band_width = ($profile_seq.len() + $other_seq.len()) / 2;
let banded_alignment = sw_banded_align($other_seq, &profile_scalar, band_width);
if let MaybeAligned::Some(banded_aln) = banded_alignment {
assert!(banded_aln.score > 0, "Banded alignment score should be positive");
println!(
"Scalar: {}, Banded: {} (band_width: {})",
aln_scalar.score, banded_aln.score, band_width
);
}
}};
}
#[test]
#[cfg(feature = "alignment-diagnostics")]
fn sw_verify_score_from_path() {
let reference = b"ATTCCTTTTGCCGGG";
let weights: WeightMatrix<i8, 5> = WeightMatrix::new_dna_matrix(3, -1, Some(b'N'));
let profile = ScalarProfile::new(b"ATTGCGCCCGG", &weights, -4, -1).unwrap();
let Alignment {
score,
ref_range,
query_range: _,
states,
..
} = sw_scalar_align(reference, &profile).unwrap();
assert_eq!(Ok(score), sw_score_from_path(&states, &reference[ref_range], &profile));
}
#[test]
fn sw() {
let weights: WeightMatrix<i8, 5> = WeightMatrix::new_dna_matrix(2, -5, Some(b'N'));
let profile = ScalarProfile::new(H1_HA_SEQ, &weights, GAP_OPEN, GAP_EXTEND).unwrap();
let Alignment { score, ref_range, .. } = sw_scalar_align(H5_HA_SEQ, &profile).unwrap();
assert_eq!((336, 37), (ref_range.start, score));
let score = sw_scalar_score(H5_HA_SEQ, &profile).unwrap();
assert_eq!(37, score);
let v: Vec<_> = std::iter::repeat_n(b'A', 100).collect();
let profile = ScalarProfile::new(&v, &weights, GAP_OPEN, GAP_EXTEND).unwrap();
let score = sw_scalar_score(&v, &profile).unwrap();
assert_eq!(200, score);
}
#[test]
fn sw_t_u_check() {
let profile = ScalarProfile::new(b"ACGTUNacgtun", &WEIGHTS, GAP_OPEN, GAP_EXTEND).unwrap();
let score = sw_scalar_score(b"ACGTTNACGTTN", &profile);
assert_eq!(score, MaybeAligned::Some(20));
let profile = StripedProfile::<u16, 16, 5>::new(b"ACGTUNacgtun", &BIASED_WEIGHTS, GAP_OPEN, GAP_EXTEND).unwrap();
let score = sw_simd_score(b"ACGTTNACGTTN", &profile);
assert_eq!(score, MaybeAligned::Some(20));
let profile = StripedProfile::<i16, 16, 5>::new(b"ACGTUNacgtun", &WEIGHTS, GAP_OPEN, GAP_EXTEND).unwrap();
let score = sw_simd_score(b"ACGTTNACGTTN", &profile);
assert_eq!(score, MaybeAligned::Some(20));
}
#[test]
fn sw_simd() {
let matrix_i = WeightMatrix::new(&DNA_PROFILE_MAP, 2, -5, Some(b'N'));
let matrix_u = matrix_i.to_biased_matrix();
let profile = StripedProfile::<u8, 16, 5>::new(H5_HA_SEQ, &matrix_u, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile.sw_score(H1_HA_SEQ);
assert_eq!(MaybeAligned::Some(37), score);
let profile = StripedProfile::<i16, 16, 5>::new(H5_HA_SEQ, &matrix_i, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile.sw_score(H1_HA_SEQ);
assert_eq!(MaybeAligned::Some(37), score);
}
#[test]
fn sw_simd_aln() {
test_sw_simd_align!(H5_HA_SEQ, H1_HA_SEQ, i16, u16, 8);
}
#[test]
fn sw_simd_aln2() {
let reference = b"AAACTA";
let query = b"TTTAG";
test_sw_simd_align!(query, reference, i8, u8, 2);
}
#[test]
fn sw_simd_aln3() {
let reference = b"AAAAAAAAAA";
let query = b"AAAAAATAAA";
test_sw_simd_align!(query, reference, i8, u8, 4);
}
#[test]
fn sw_simd_aln4() {
let reference = b"TAAAA";
let query = b"CCCCA";
test_sw_simd_align!(query, reference, i8, u8, 4);
}
#[test]
fn sw_simd_aln5() {
let reference = b"TCCCC";
let query = b"CCCCC";
test_sw_simd_align!(query, reference, i8, u8, 4);
}
#[test]
fn sw_simd_aln6() {
let reference = b"GCTTTTC";
let query = b"CCCCT";
test_sw_simd_align!(query, reference, i8, u8, 4);
}
#[test]
fn sw_simd_aln7() {
let reference = b"TTGTTTTTTTTTGTT";
let query = b"TTTTTGTTTTCTTTTTTGTTTA";
test_sw_simd_align!(query, reference, i8, u8, 16);
}
#[test]
fn sw_simd_aln8() {
let reference = b"TTTTTGTTTGGGAAAAATTCTT";
let query = b"TTGTTTTGGGGAAAAA";
test_sw_simd_align!(query, reference, i8, u8, 8);
}
#[test]
fn sw_simd_aln9() {
let reference = b"TTTTTGTTTTCTTGGT";
let query = b"TTTTTTTCTTGTTTTTG";
test_sw_simd_align!(query, reference, i8, u8, 16);
}
#[test]
fn sw_simd_aln10() {
let reference = b"TTTTTTTTTTTTAAAATTTGTAAACGTTTTGTTA";
let query = b"TTTTTTTTACTATTTTTAAATTTATGTTTTGTTA";
test_sw_simd_align!(query, reference, i8, u8, 8);
}
#[test]
fn sw_simd_aln11() {
let reference = b"TTTATTTTTTTTTTTTTTCCCCCCCTTTTTTTTTTTTTTTTTCCCCCCTTT";
let query = b"TTTTTTTTTTTTTTTTTTTCCTTTTTTTTTTTTTTTTTTTTTTTTTTCCCCCCTTTA";
test_sw_simd_align!(query, reference, i8, u8, 8);
}
#[test]
fn sw_simd_aln12() {
let reference = b"TTTTTTTTTTTTTTTCCCCCTTTTTTTTTTCCCCCCCCCTT";
let query = b"TTTTTTTTTTTTTTTCCTTTTTTTTTTTTTTTTTTTCCCCCCCCCTA";
test_sw_simd_align!(query, reference, i8, u8, 8);
}
#[test]
fn sw_simd_locations() {
let reference = b"TTTTTTCCTTTTTTTTCCCCCTTTTT";
let query = b"GGGGGGGCCCCCAAAA";
let weights = WeightMatrix::new(&DNA_PROFILE_MAP, 2, -5, Some(b'N'));
let profile_scalar = ScalarProfile::new(query, &weights, GAP_OPEN, GAP_EXTEND).unwrap();
let profile_simd = StripedProfile::<u8, 8, 5>::new(query, &weights.to_biased_matrix(), GAP_OPEN, GAP_EXTEND).unwrap();
let aln_scalar = profile_scalar.sw_align(SeqSrc::Reference(reference)).unwrap();
let ScoreEnds {
score,
ref_end,
query_end,
} = profile_simd.sw_score_ends(SeqSrc::Reference(reference)).unwrap();
assert_eq!(score, aln_scalar.score);
assert_eq!(aln_scalar.ref_range.end, ref_end, "REFERENCE END");
assert_eq!(aln_scalar.query_range.end, query_end, "QUERY END");
let query_rev: Vec<u8> = query[..query_end].iter().copied().rev().collect();
let profile_simd_rev =
StripedProfile::<u8, 8, 5>::new(&query_rev, &weights.to_biased_matrix(), GAP_OPEN, GAP_EXTEND).unwrap();
let ScoreStarts {
score: score2,
ref_start,
query_start,
} = profile_simd_rev
.sw_score_ends_reverse(SeqSrc::Reference(&reference[..ref_end]))
.unwrap();
assert_eq!(score, score2);
assert_eq!(ref_start..ref_end, aln_scalar.ref_range, "REFERENCE");
assert_eq!(query_start..query_end, aln_scalar.query_range, "QUERY");
let profile_simd_rev2 = profile_simd.reverse_from_forward(query_end).unwrap();
assert_eq!(profile_simd_rev, profile_simd_rev2);
let ScoreAndRanges {
ref_range, query_range, ..
} = profile_simd.sw_score_ranges(SeqSrc::Reference(reference)).unwrap();
assert_eq!(query_range, aln_scalar.query_range, "3pass QUERY RANGE");
assert_eq!(ref_range, aln_scalar.ref_range, "3pass REFERENCE RANGE");
}
#[test]
fn sw_simd_ranges() {
let reference = b"TAAAA";
let query = b"CCCCA";
let weights = WeightMatrix::new(&DNA_PROFILE_MAP, 2, -5, Some(b'N'));
let profile_scalar = ScalarProfile::new(query, &weights, GAP_OPEN, GAP_EXTEND).unwrap();
let profile_simd = StripedProfile::<u8, 8, 5>::new(query, &weights.to_biased_matrix(), GAP_OPEN, GAP_EXTEND).unwrap();
let aln_scalar = profile_scalar.sw_align(SeqSrc::Reference(reference)).unwrap();
let ScoreAndRanges {
score,
ref_range,
query_range,
} = profile_simd.sw_score_ranges(SeqSrc::Reference(reference)).unwrap();
assert_eq!(score, aln_scalar.score);
assert_eq!(ref_range, aln_scalar.ref_range, "REFERENCE");
assert_eq!(query_range, aln_scalar.query_range, "QUERY");
}
#[test]
fn sw_simd_poly_a() {
let v: Vec<_> = std::iter::repeat_n(b'A', 100).collect();
let matrix = WeightMatrix::new_dna_matrix(2, -5, Some(b'N')).to_biased_matrix();
let profile = StripedProfile::<u16, 16, 5>::new(&v, &matrix, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile.sw_score(&v);
assert_eq!(MaybeAligned::Some(200), score);
}
#[test]
fn sw_simd_single() {
let v: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/CY137594.txt"));
let matrix = WeightMatrix::<i8, 5>::new_dna_matrix(2, -5, Some(b'N')).to_biased_matrix();
let profile = StripedProfile::<u16, 16, 5>::new(v, &matrix, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile.sw_score(v);
assert_eq!(MaybeAligned::Some(3372), score);
}
#[test]
fn sw_simd_regression() {
let query = b"AGA";
let reference = b"AA";
let matrix = WeightMatrix::new(&DNA_PROFILE_MAP, 10, -10, Some(b'N')).to_biased_matrix();
let profile = StripedProfile::<u16, 4, 5>::new(query, &matrix, -5, -5).unwrap();
let score = profile.sw_score(reference);
assert_eq!(MaybeAligned::Some(15), score);
}
#[test]
fn sw_simd_overflow_check() {
let query = b"AAAA";
let reference = b"AAAA";
let matrix = WeightMatrix::new(&DNA_PROFILE_MAP, 127, 0, Some(b'N')).to_biased_matrix();
let profile = StripedProfile::<u8, 8, 5>::new(query, &matrix, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile.sw_score(reference);
assert_eq!(score, MaybeAligned::Overflowed);
}
#[test]
fn sw_simd_profile_set() {
let v: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/CY137594.txt"));
let matrix_i = WeightMatrix::<i8, 5>::new_dna_matrix(2, -5, Some(b'N'));
let profile = LocalProfiles::new_with_w128(v, &matrix_i, GAP_OPEN, GAP_EXTEND).unwrap();
let score = profile.sw_score_from_i8(&v);
assert_eq!(MaybeAligned::Some(3372), score);
}
#[test]
fn sw_banded_comparison() {
let reference = b"GGCCACAGGATTGAG";
let query = b"CTCAGATTG";
let weights = WeightMatrix::new_dna_matrix(4, -2, Some(b'N'));
let profile = ScalarProfile::new(query, &weights, -3, -1).unwrap();
let scalar_score = sw_scalar_score(reference, &profile).unwrap();
let scalar_alignment = sw_scalar_align(reference, &profile).unwrap();
let band_width = reference.len().max(query.len());
let banded_alignment = sw_banded_align(reference, &profile, band_width);
assert!(
matches!(banded_alignment, MaybeAligned::Some(_)),
"Banded should find an alignment"
);
if let MaybeAligned::Some(banded_aln) = banded_alignment {
println!("Scalar score: {}, Banded score: {}", scalar_score, banded_aln.score);
println!("Scalar alignment: {scalar_alignment:?}");
println!("Banded alignment: {banded_aln:?}");
assert!(banded_aln.score > 0, "Banded alignment score should be positive");
}
let small_band = 3;
let small_banded_alignment = sw_banded_align(reference, &profile, small_band);
println!("Small band (width={small_band}): {small_banded_alignment:?}");
}
mod banded {
use super::*;
#[test]
fn test_banded_sw_align_simple() {
let reference = b"AAACCCGGG";
let query = b"AACCGG";
let weights = WeightMatrix::new_dna_matrix(2, -1, Some(b'N'));
let profile = ScalarProfile::new(query, &weights, -2, -1).unwrap();
let alignment = sw_banded_align(reference, &profile, 3);
assert!(matches!(alignment, MaybeAligned::Some(_)));
if let MaybeAligned::Some(aln) = alignment {
assert_eq!(aln.score, 10);
assert!(!aln.ref_range.is_empty());
assert!(!aln.query_range.is_empty());
}
}
#[test]
fn test_banded_empty_sequences() {
let weights = WeightMatrix::new_dna_matrix(2, -1, Some(b'N'));
let profile = ScalarProfile::new(b"", &weights, -2, -1);
assert!(profile.is_err());
let profile = ScalarProfile::new(b"ACGT", &weights, -2, -1).unwrap();
let alignment = sw_banded_align(b"", &profile, 3);
assert_eq!(alignment, MaybeAligned::Unmapped);
}
#[test]
fn test_band_bounds() {
let row = 5usize;
let query_len = 10usize;
let band_width = 2usize;
let start = row.saturating_sub(band_width);
let end = (row + band_width + 1).min(query_len);
assert_eq!(start, 3); assert_eq!(end, 8);
let row = 0usize;
let start = row.saturating_sub(band_width);
let end = (row + band_width + 1).min(query_len);
assert_eq!(start, 0); assert_eq!(end, 3); }
}