use super::backtrack::{backtrack_affine, backtrack_convex, backtrack_linear};
use super::{Alignment, AlignmentEngine, AlignmentType, GapMode, Scoring};
use crate::graph::{EdgeId, Graph};
pub(crate) const NEG_INF: i32 = i32::MIN + 1024;
pub struct SisdEngine {
alignment_type: AlignmentType,
gap_mode: GapMode,
scoring: Scoring,
node_id_to_rank: Vec<u32>,
sequence_profile: Vec<i32>,
h: Vec<i32>,
f: Vec<i32>,
e: Vec<i32>,
o: Vec<i32>,
q: Vec<i32>,
}
impl SisdEngine {
pub fn new(alignment_type: AlignmentType, scoring: Scoring) -> SisdEngine {
SisdEngine {
alignment_type,
gap_mode: scoring.gap_mode(),
scoring,
node_id_to_rank: Vec::new(),
sequence_profile: Vec::new(),
h: Vec::new(),
f: Vec::new(),
e: Vec::new(),
o: Vec::new(),
q: Vec::new(),
}
}
fn realloc(&mut self, matrix_width: usize, matrix_height: usize, num_codes: usize) {
if self.node_id_to_rank.len() < matrix_height - 1 {
self.node_id_to_rank.resize(matrix_height - 1, 0);
}
if self.sequence_profile.len() < num_codes * matrix_width {
self.sequence_profile.resize(num_codes * matrix_width, 0);
}
let cells = matrix_width * matrix_height;
match self.gap_mode {
GapMode::Linear => {
if self.h.len() < cells {
self.h.resize(cells, 0);
}
}
GapMode::Affine => {
if self.h.len() < cells {
self.h.resize(cells, 0);
self.f.resize(cells, 0);
self.e.resize(cells, 0);
}
}
GapMode::Convex => {
if self.h.len() < cells {
self.h.resize(cells, 0);
self.f.resize(cells, 0);
self.e.resize(cells, 0);
self.o.resize(cells, 0);
self.q.resize(cells, 0);
}
}
}
}
fn initialize(&mut self, seq: &[u8], graph: &Graph) {
let matrix_width = seq.len() + 1;
let matrix_height = graph.nodes.len() + 1;
let num_codes = graph.num_codes as usize;
self.realloc(matrix_width, matrix_height, num_codes);
for code in 0..num_codes {
let decoded = graph.decoder[code];
self.sequence_profile[code * matrix_width] = 0;
for (j, &base) in seq.iter().enumerate() {
let score = if decoded == base as i32 {
self.scoring.m
} else {
self.scoring.n
};
self.sequence_profile[code * matrix_width + (j + 1)] = i32::from(score);
}
}
for (rank, &node_id) in graph.rank_to_node.iter().enumerate() {
self.node_id_to_rank[node_id.0 as usize] = rank as u32;
}
if self.gap_mode == GapMode::Convex {
self.initialize_convex_boundary(graph, matrix_width, matrix_height);
}
if self.gap_mode == GapMode::Convex || self.gap_mode == GapMode::Affine {
self.initialize_affine_boundary(graph, matrix_width, matrix_height);
}
self.h[0] = 0;
match self.alignment_type {
AlignmentType::Local => self.initialize_h_boundary_local(matrix_width, matrix_height),
AlignmentType::Global => {
self.initialize_h_boundary_global(graph, matrix_width, matrix_height)
}
AlignmentType::Overlap => {
self.initialize_h_boundary_overlap(graph, matrix_width, matrix_height)
}
}
}
fn initialize_convex_boundary(
&mut self,
graph: &Graph,
matrix_width: usize,
matrix_height: usize,
) {
self.o[0] = 0;
self.q[0] = 0;
let (q_open, c_extend) = (i32::from(self.scoring.q), i32::from(self.scoring.c));
for j in 1..matrix_width {
self.o[j] = NEG_INF;
self.q[j] = q_open + (j as i32 - 1) * c_extend;
}
for i in 1..matrix_height {
let value = boundary_column_value(
graph,
&self.node_id_to_rank,
&self.o,
matrix_width,
i - 1,
q_open - c_extend,
) + c_extend;
self.o[i * matrix_width] = value;
self.q[i * matrix_width] = NEG_INF;
}
}
fn initialize_affine_boundary(
&mut self,
graph: &Graph,
matrix_width: usize,
matrix_height: usize,
) {
self.f[0] = 0;
self.e[0] = 0;
let (g_open, e_extend) = (i32::from(self.scoring.g), i32::from(self.scoring.e));
for j in 1..matrix_width {
self.f[j] = NEG_INF;
self.e[j] = g_open + (j as i32 - 1) * e_extend;
}
for i in 1..matrix_height {
let value = boundary_column_value(
graph,
&self.node_id_to_rank,
&self.f,
matrix_width,
i - 1,
g_open - e_extend,
) + e_extend;
self.f[i * matrix_width] = value;
self.e[i * matrix_width] = NEG_INF;
}
}
fn initialize_h_boundary_local(&mut self, matrix_width: usize, matrix_height: usize) {
for j in 1..matrix_width {
self.h[j] = 0;
}
for i in 1..matrix_height {
self.h[i * matrix_width] = 0;
}
}
fn initialize_h_boundary_global(
&mut self,
graph: &Graph,
matrix_width: usize,
matrix_height: usize,
) {
match self.gap_mode {
GapMode::Convex => {
for j in 1..matrix_width {
self.h[j] = self.q[j].max(self.e[j]);
}
for i in 1..matrix_height {
let idx = i * matrix_width;
self.h[idx] = self.o[idx].max(self.f[idx]);
}
}
GapMode::Affine => {
for j in 1..matrix_width {
self.h[j] = self.e[j];
}
for i in 1..matrix_height {
self.h[i * matrix_width] = self.f[i * matrix_width];
}
}
GapMode::Linear => {
let g_open = i32::from(self.scoring.g);
for j in 1..matrix_width {
self.h[j] = j as i32 * g_open;
}
for i in 1..matrix_height {
let value = boundary_column_value(
graph,
&self.node_id_to_rank,
&self.h,
matrix_width,
i - 1,
0,
) + g_open;
self.h[i * matrix_width] = value;
}
}
}
}
fn initialize_h_boundary_overlap(
&mut self,
_graph: &Graph,
matrix_width: usize,
matrix_height: usize,
) {
match self.gap_mode {
GapMode::Convex => {
for j in 1..matrix_width {
self.h[j] = self.q[j].max(self.e[j]);
}
}
GapMode::Affine => {
for j in 1..matrix_width {
self.h[j] = self.e[j];
}
}
GapMode::Linear => {
let g_open = i32::from(self.scoring.g);
for j in 1..matrix_width {
self.h[j] = j as i32 * g_open;
}
}
}
for i in 1..matrix_height {
self.h[i * matrix_width] = 0;
}
}
fn linear(&mut self, seq_len: usize, graph: &Graph) -> (Alignment, i32) {
let matrix_width = seq_len + 1;
let g = i32::from(self.scoring.g);
let align_type = self.alignment_type;
let node_id_to_rank = &self.node_id_to_rank;
let sequence_profile = &self.sequence_profile;
let h = &mut self.h;
let pred_row = |edge_id: EdgeId| -> usize {
let tail = graph.edges[edge_id.0 as usize].tail;
node_id_to_rank[tail.0 as usize] as usize + 1
};
let mut max_score = match align_type {
AlignmentType::Local => 0,
AlignmentType::Global | AlignmentType::Overlap => NEG_INF,
};
let mut max_i = 0usize;
let mut max_j = 0usize;
for &node_id in &graph.rank_to_node {
let node = &graph.nodes[node_id.0 as usize];
let profile_base = node.code as usize * matrix_width;
let i = node_id_to_rank[node_id.0 as usize] as usize + 1;
let mut pred_i = if node.inedges.is_empty() {
0
} else {
pred_row(node.inedges[0])
};
for j in 1..matrix_width {
let m = h[pred_i * matrix_width + (j - 1)] + sequence_profile[profile_base + j];
let del = h[pred_i * matrix_width + j] + g;
h[i * matrix_width + j] = m.max(del);
}
for p in 1..node.inedges.len() {
pred_i = pred_row(node.inedges[p]);
for j in 1..matrix_width {
let m = h[pred_i * matrix_width + (j - 1)] + sequence_profile[profile_base + j];
let cur = h[i * matrix_width + j];
let del = h[pred_i * matrix_width + j] + g;
h[i * matrix_width + j] = m.max(cur.max(del));
}
}
for j in 1..matrix_width {
let mut value = (h[i * matrix_width + (j - 1)] + g).max(h[i * matrix_width + j]);
match align_type {
AlignmentType::Local => {
value = value.max(0);
h[i * matrix_width + j] = value;
if max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
AlignmentType::Global => {
h[i * matrix_width + j] = value;
if node.outedges.is_empty() && j == matrix_width - 1 && max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
AlignmentType::Overlap => {
h[i * matrix_width + j] = value;
if node.outedges.is_empty() && max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
}
}
}
let score = max_score;
let alignment = backtrack_linear(
graph,
node_id_to_rank,
sequence_profile,
&h[..],
matrix_width,
align_type,
&self.scoring,
max_i,
max_j,
max_score,
);
(alignment, score)
}
fn affine(&mut self, seq_len: usize, graph: &Graph) -> (Alignment, i32) {
let matrix_width = seq_len + 1;
let g = i32::from(self.scoring.g);
let e = i32::from(self.scoring.e);
let align_type = self.alignment_type;
let node_id_to_rank = &self.node_id_to_rank;
let sequence_profile = &self.sequence_profile;
let h = &mut self.h;
let f = &mut self.f;
let e_buf = &mut self.e;
let pred_row = |edge_id: EdgeId| -> usize {
let tail = graph.edges[edge_id.0 as usize].tail;
node_id_to_rank[tail.0 as usize] as usize + 1
};
let mut max_score = match align_type {
AlignmentType::Local => 0,
AlignmentType::Global | AlignmentType::Overlap => NEG_INF,
};
let mut max_i = 0usize;
let mut max_j = 0usize;
for &node_id in &graph.rank_to_node {
let node = &graph.nodes[node_id.0 as usize];
let profile_base = node.code as usize * matrix_width;
let i = node_id_to_rank[node_id.0 as usize] as usize + 1;
let mut pred_i = if node.inedges.is_empty() {
0
} else {
pred_row(node.inedges[0])
};
for j in 1..matrix_width {
f[i * matrix_width + j] =
(h[pred_i * matrix_width + j] + g).max(f[pred_i * matrix_width + j] + e);
h[i * matrix_width + j] =
h[pred_i * matrix_width + (j - 1)] + sequence_profile[profile_base + j];
}
for p in 1..node.inedges.len() {
pred_i = pred_row(node.inedges[p]);
for j in 1..matrix_width {
let cur_f = f[i * matrix_width + j];
f[i * matrix_width + j] = cur_f.max(
(h[pred_i * matrix_width + j] + g).max(f[pred_i * matrix_width + j] + e),
);
let cur_h = h[i * matrix_width + j];
h[i * matrix_width + j] = cur_h.max(
h[pred_i * matrix_width + (j - 1)] + sequence_profile[profile_base + j],
);
}
}
for j in 1..matrix_width {
e_buf[i * matrix_width + j] =
(h[i * matrix_width + (j - 1)] + g).max(e_buf[i * matrix_width + (j - 1)] + e);
let value = h[i * matrix_width + j]
.max(f[i * matrix_width + j].max(e_buf[i * matrix_width + j]));
h[i * matrix_width + j] = value;
match align_type {
AlignmentType::Local => {
let value = value.max(0);
h[i * matrix_width + j] = value;
if max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
AlignmentType::Global => {
if node.outedges.is_empty() && j == matrix_width - 1 && max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
AlignmentType::Overlap => {
if node.outedges.is_empty() && max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
}
}
}
let score = max_score;
let alignment = backtrack_affine(
graph,
node_id_to_rank,
sequence_profile,
&h[..],
&e_buf[..],
&f[..],
matrix_width,
align_type,
&self.scoring,
max_i,
max_j,
max_score,
);
(alignment, score)
}
fn convex(&mut self, seq_len: usize, graph: &Graph) -> (Alignment, i32) {
let matrix_width = seq_len + 1;
let g = i32::from(self.scoring.g);
let e = i32::from(self.scoring.e);
let q = i32::from(self.scoring.q);
let c = i32::from(self.scoring.c);
let align_type = self.alignment_type;
let node_id_to_rank = &self.node_id_to_rank;
let sequence_profile = &self.sequence_profile;
let h = &mut self.h;
let f = &mut self.f;
let e_buf = &mut self.e;
let o = &mut self.o;
let q_buf = &mut self.q;
let pred_row = |edge_id: EdgeId| -> usize {
let tail = graph.edges[edge_id.0 as usize].tail;
node_id_to_rank[tail.0 as usize] as usize + 1
};
let mut max_score = match align_type {
AlignmentType::Local => 0,
AlignmentType::Global | AlignmentType::Overlap => NEG_INF,
};
let mut max_i = 0usize;
let mut max_j = 0usize;
for &node_id in &graph.rank_to_node {
let node = &graph.nodes[node_id.0 as usize];
let profile_base = node.code as usize * matrix_width;
let i = node_id_to_rank[node_id.0 as usize] as usize + 1;
let mut pred_i = if node.inedges.is_empty() {
0
} else {
pred_row(node.inedges[0])
};
for j in 1..matrix_width {
f[i * matrix_width + j] =
(h[pred_i * matrix_width + j] + g).max(f[pred_i * matrix_width + j] + e);
o[i * matrix_width + j] =
(h[pred_i * matrix_width + j] + q).max(o[pred_i * matrix_width + j] + c);
h[i * matrix_width + j] =
h[pred_i * matrix_width + (j - 1)] + sequence_profile[profile_base + j];
}
for p in 1..node.inedges.len() {
pred_i = pred_row(node.inedges[p]);
for j in 1..matrix_width {
let cur_f = f[i * matrix_width + j];
f[i * matrix_width + j] = cur_f.max(
(h[pred_i * matrix_width + j] + g).max(f[pred_i * matrix_width + j] + e),
);
let cur_o = o[i * matrix_width + j];
o[i * matrix_width + j] = cur_o.max(
(h[pred_i * matrix_width + j] + q).max(o[pred_i * matrix_width + j] + c),
);
let cur_h = h[i * matrix_width + j];
h[i * matrix_width + j] = cur_h.max(
h[pred_i * matrix_width + (j - 1)] + sequence_profile[profile_base + j],
);
}
}
for j in 1..matrix_width {
e_buf[i * matrix_width + j] =
(h[i * matrix_width + (j - 1)] + g).max(e_buf[i * matrix_width + (j - 1)] + e);
q_buf[i * matrix_width + j] =
(h[i * matrix_width + (j - 1)] + q).max(q_buf[i * matrix_width + (j - 1)] + c);
let gap_max = f[i * matrix_width + j]
.max(e_buf[i * matrix_width + j])
.max(o[i * matrix_width + j])
.max(q_buf[i * matrix_width + j]);
let value = h[i * matrix_width + j].max(gap_max);
h[i * matrix_width + j] = value;
match align_type {
AlignmentType::Local => {
let value = value.max(0);
h[i * matrix_width + j] = value;
if max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
AlignmentType::Global => {
if node.outedges.is_empty() && j == matrix_width - 1 && max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
AlignmentType::Overlap => {
if node.outedges.is_empty() && max_score < value {
max_score = value;
max_i = i;
max_j = j;
}
}
}
}
}
let score = max_score;
let alignment = backtrack_convex(
graph,
node_id_to_rank,
sequence_profile,
&h[..],
&e_buf[..],
&f[..],
&o[..],
&q_buf[..],
matrix_width,
align_type,
&self.scoring,
max_i,
max_j,
max_score,
);
(alignment, score)
}
}
fn boundary_column_value(
graph: &Graph,
node_id_to_rank: &[u32],
buffer: &[i32],
matrix_width: usize,
rank: usize,
empty_penalty: i32,
) -> i32 {
let node_id = graph.rank_to_node[rank];
let inedges = &graph.nodes[node_id.0 as usize].inedges;
let mut penalty = if inedges.is_empty() {
empty_penalty
} else {
NEG_INF
};
for &edge_id in inedges {
let tail = graph.edges[edge_id.0 as usize].tail;
let pred_row = node_id_to_rank[tail.0 as usize] as usize + 1;
penalty = penalty.max(buffer[pred_row * matrix_width]);
}
penalty
}
#[allow(dead_code)]
#[derive(Default)]
pub(crate) struct ScalarInit {
pub(crate) h: Vec<i32>,
pub(crate) e: Vec<i32>,
pub(crate) f: Vec<i32>,
pub(crate) o: Vec<i32>,
pub(crate) q: Vec<i32>,
pub(crate) sequence_profile: Vec<i32>,
pub(crate) node_id_to_rank: Vec<u32>,
pub(crate) matrix_width: usize,
}
#[allow(dead_code)]
pub(crate) fn seed_scalar_buffers(
alignment_type: AlignmentType,
scoring: Scoring,
seq: &[u8],
graph: &Graph,
) -> ScalarInit {
let mut engine = SisdEngine::new(alignment_type, scoring);
engine.initialize(seq, graph);
ScalarInit {
h: engine.h,
e: engine.e,
f: engine.f,
o: engine.o,
q: engine.q,
sequence_profile: engine.sequence_profile,
node_id_to_rank: engine.node_id_to_rank,
matrix_width: seq.len() + 1,
}
}
#[allow(dead_code)]
pub(crate) fn reseed_scalar_buffers(
scratch: &mut ScalarInit,
alignment_type: AlignmentType,
scoring: Scoring,
seq: &[u8],
graph: &Graph,
) {
let mut engine = SisdEngine::new(alignment_type, scoring);
engine.h = std::mem::take(&mut scratch.h);
engine.e = std::mem::take(&mut scratch.e);
engine.f = std::mem::take(&mut scratch.f);
engine.o = std::mem::take(&mut scratch.o);
engine.q = std::mem::take(&mut scratch.q);
engine.sequence_profile = std::mem::take(&mut scratch.sequence_profile);
engine.node_id_to_rank = std::mem::take(&mut scratch.node_id_to_rank);
engine.initialize(seq, graph);
scratch.h = engine.h;
scratch.e = engine.e;
scratch.f = engine.f;
scratch.o = engine.o;
scratch.q = engine.q;
scratch.sequence_profile = engine.sequence_profile;
scratch.node_id_to_rank = engine.node_id_to_rank;
scratch.matrix_width = seq.len() + 1;
}
impl AlignmentEngine for SisdEngine {
fn align(&mut self, seq: &[u8], graph: &Graph) -> (super::Alignment, i32) {
assert!(
seq.len() <= i32::MAX as usize,
"[spoars::SisdEngine::align] error: too large sequence!"
);
if graph.nodes.is_empty() || seq.is_empty() {
return (Vec::new(), 0);
}
let worst_case = self
.scoring
.worst_case_alignment_score(seq.len() as i64, graph.nodes.len() as i64);
assert!(
worst_case >= i64::from(NEG_INF),
"[spoars::SisdEngine::align] error: possible overflow!"
);
self.initialize(seq, graph);
match self.gap_mode {
GapMode::Linear => self.linear(seq.len(), graph),
GapMode::Affine => self.affine(seq.len(), graph),
GapMode::Convex => self.convex(seq.len(), graph),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::Graph;
fn linear_scoring() -> Scoring {
Scoring::new(5, -4, -8, -8, -8, -8).unwrap()
}
fn affine_scoring() -> Scoring {
Scoring::new(5, -4, -8, -6, -8, -6).unwrap()
}
fn convex_scoring() -> Scoring {
Scoring::new(5, -4, -8, -6, -10, -4).unwrap()
}
#[test]
fn initialize_linear_global_on_tiny_graph_does_not_panic() {
let mut g = Graph::new();
g.add_alignment_weight(&[], b"AC", 1).unwrap();
let mut engine = SisdEngine::new(AlignmentType::Global, linear_scoring());
engine.initialize(b"AG", &g);
let matrix_width = 3; assert_eq!(engine.h[0], 0);
assert_eq!(engine.h[1], -8);
assert_eq!(engine.h[2], -16);
assert_eq!(engine.h[matrix_width], -8);
}
#[test]
fn initialize_affine_local_on_tiny_graph_does_not_panic() {
let mut g = Graph::new();
g.add_alignment_weight(&[], b"AC", 1).unwrap();
let mut engine = SisdEngine::new(AlignmentType::Local, affine_scoring());
engine.initialize(b"AG", &g);
assert!(engine.h[..3].iter().all(|&v| v == 0));
assert_eq!(engine.f[0], 0);
assert_eq!(engine.e[0], 0);
}
#[test]
fn initialize_convex_overlap_on_tiny_graph_does_not_panic() {
let mut g = Graph::new();
g.add_alignment_weight(&[], b"AC", 1).unwrap();
let mut engine = SisdEngine::new(AlignmentType::Overlap, convex_scoring());
engine.initialize(b"AG", &g);
assert_eq!(engine.o[0], 0);
assert_eq!(engine.q[0], 0);
assert_eq!(engine.f[0], 0);
assert_eq!(engine.e[0], 0);
let matrix_width = 3;
assert_eq!(engine.h[matrix_width], 0);
}
#[test]
fn initialize_on_empty_graph_and_empty_sequence_does_not_panic() {
let g = Graph::new();
let mut engine = SisdEngine::new(AlignmentType::Global, linear_scoring());
engine.initialize(b"", &g);
assert_eq!(engine.h[0], 0);
}
#[test]
fn seed_scalar_buffers_matches_manually_initialized_engine_for_nw() {
let mut g = Graph::new();
g.add_alignment_weight(&[], b"AC", 1).unwrap();
let scoring = linear_scoring();
let seq: &[u8] = b"AG";
let mut engine = SisdEngine::new(AlignmentType::Global, scoring);
engine.initialize(seq, &g);
let seeded = seed_scalar_buffers(AlignmentType::Global, scoring, seq, &g);
assert_eq!(seeded.h, engine.h);
assert_eq!(seeded.e, engine.e);
assert_eq!(seeded.f, engine.f);
assert_eq!(seeded.o, engine.o);
assert_eq!(seeded.q, engine.q);
assert_eq!(seeded.sequence_profile, engine.sequence_profile);
assert_eq!(seeded.node_id_to_rank, engine.node_id_to_rank);
assert_eq!(seeded.matrix_width, seq.len() + 1);
}
#[test]
fn seed_scalar_buffers_matches_manually_initialized_engine_for_sw() {
let mut g = Graph::new();
g.add_alignment_weight(&[], b"AC", 1).unwrap();
let scoring = affine_scoring();
let seq: &[u8] = b"AG";
let mut engine = SisdEngine::new(AlignmentType::Local, scoring);
engine.initialize(seq, &g);
let seeded = seed_scalar_buffers(AlignmentType::Local, scoring, seq, &g);
assert_eq!(seeded.h, engine.h);
assert_eq!(seeded.e, engine.e);
assert_eq!(seeded.f, engine.f);
assert_eq!(seeded.o, engine.o);
assert_eq!(seeded.q, engine.q);
assert_eq!(seeded.sequence_profile, engine.sequence_profile);
assert_eq!(seeded.node_id_to_rank, engine.node_id_to_rank);
}
#[test]
fn seed_scalar_buffers_matches_manually_initialized_engine_for_ov() {
let mut g = Graph::new();
g.add_alignment_weight(&[], b"AC", 1).unwrap();
let scoring = convex_scoring();
let seq: &[u8] = b"AG";
let mut engine = SisdEngine::new(AlignmentType::Overlap, scoring);
engine.initialize(seq, &g);
let seeded = seed_scalar_buffers(AlignmentType::Overlap, scoring, seq, &g);
assert_eq!(seeded.h, engine.h);
assert_eq!(seeded.e, engine.e);
assert_eq!(seeded.f, engine.f);
assert_eq!(seeded.o, engine.o);
assert_eq!(seeded.q, engine.q);
assert_eq!(seeded.sequence_profile, engine.sequence_profile);
assert_eq!(seeded.node_id_to_rank, engine.node_id_to_rank);
}
#[test]
fn reseed_scalar_buffers_matches_fresh_seed_after_reuse_from_a_larger_alignment() {
let scoring = convex_scoring();
let alignment_type = AlignmentType::Global;
let mut big_graph = Graph::new();
big_graph
.add_alignment_weight(&[], b"ACGTACGTACGTACGT", 1)
.unwrap();
big_graph
.add_alignment_weight(&[], b"ACGTTCGTACGATCGT", 1)
.unwrap();
let mut scratch = ScalarInit::default();
reseed_scalar_buffers(
&mut scratch,
alignment_type,
scoring,
b"ACGTACGTACGTACGT",
&big_graph,
);
let mut small_graph = Graph::new();
small_graph.add_alignment_weight(&[], b"ACGT", 1).unwrap();
let seq: &[u8] = b"AGGT";
reseed_scalar_buffers(&mut scratch, alignment_type, scoring, seq, &small_graph);
let fresh = seed_scalar_buffers(alignment_type, scoring, seq, &small_graph);
let matrix_width = scratch.matrix_width;
assert_eq!(matrix_width, fresh.matrix_width);
let matrix_height = small_graph.nodes.len() + 1;
let num_codes = small_graph.num_codes as usize;
assert_eq!(
scratch.node_id_to_rank[..small_graph.nodes.len()],
fresh.node_id_to_rank[..small_graph.nodes.len()]
);
for code in 0..num_codes {
for j in 0..matrix_width {
assert_eq!(
scratch.sequence_profile[code * matrix_width + j],
fresh.sequence_profile[code * matrix_width + j],
"sequence_profile code={code} j={j}"
);
}
}
for buf_pair in [
(&scratch.h, &fresh.h),
(&scratch.e, &fresh.e),
(&scratch.f, &fresh.f),
(&scratch.o, &fresh.o),
(&scratch.q, &fresh.q),
] {
let (reused, fresh_buf) = buf_pair;
if fresh_buf.is_empty() {
continue;
}
for j in 0..matrix_width {
assert_eq!(reused[j], fresh_buf[j], "boundary row0 j={j}");
}
for i in 0..matrix_height {
assert_eq!(
reused[i * matrix_width],
fresh_buf[i * matrix_width],
"boundary col0 i={i}"
);
}
}
}
}