use spoars::align::{AlignmentEngine, AlignmentType, Scoring, SimdEngine, SisdEngine};
use spoars::graph::Graph;
fn xorshift64(state: &mut u64) -> u64 {
let mut x = *state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*state = x;
x
}
const BASES: [u8; 4] = *b"ACGT";
fn random_base(state: &mut u64) -> u8 {
BASES[(xorshift64(state) % 4) as usize]
}
fn random_reference(len: usize, state: &mut u64) -> Vec<u8> {
(0..len).map(|_| random_base(state)).collect()
}
fn mutated_reads(
reference: &[u8],
n_reads: usize,
mutation_rate: f64,
state: &mut u64,
) -> Vec<Vec<u8>> {
(0..n_reads)
.map(|_| {
reference
.iter()
.map(|&base| {
let roll = (xorshift64(state) % 1_000_000) as f64 / 1_000_000.0;
if roll < mutation_rate {
random_base(state)
} else {
base
}
})
.collect()
})
.collect()
}
fn timed_align_add_loop<E: AlignmentEngine>(
mut engine: E,
reads: &[Vec<u8>],
) -> (Graph, std::time::Duration) {
let mut graph = Graph::new();
let start = std::time::Instant::now();
for read in reads {
let (alignment, _score) = engine.align(read, &graph);
graph
.add_alignment_weight(&alignment, read, 1)
.expect("add_alignment_weight must succeed for a well-formed alignment");
}
(graph, start.elapsed())
}
#[test]
#[ignore = "sanity wall-clock benchmark, not a correctness check; run with \
`cargo test --release --test simd_bench -- --ignored --nocapture`"]
fn simd_engine_is_plausibly_faster_than_sisd_on_a_larger_synthetic_corpus() {
let mut state = 0x5b0a_2510_c0de_5eed_u64;
let reference = random_reference(1_000, &mut state);
let reads = mutated_reads(&reference, 80, 0.03, &mut state);
let alignment_type = AlignmentType::Local;
let scoring = Scoring::new(5, -4, -8, -6, -10, -4).unwrap();
let simd_engine = SimdEngine::new(alignment_type, scoring);
let (mut simd_graph, simd_elapsed) = timed_align_add_loop(simd_engine, &reads);
let sisd_engine = SisdEngine::new(alignment_type, scoring);
let (mut sisd_graph, sisd_elapsed) = timed_align_add_loop(sisd_engine, &reads);
assert_eq!(
simd_graph.generate_consensus(),
sisd_graph.generate_consensus(),
"SimdEngine and SisdEngine must build equivalent graphs over the synthetic corpus"
);
let speedup = sisd_elapsed.as_secs_f64() / simd_elapsed.as_secs_f64().max(1e-9);
eprintln!(
"simd_bench: {} reads x {}bp (local/SW, default scores): SimdEngine={simd_elapsed:?} \
SisdEngine={sisd_elapsed:?} (SimdEngine {speedup:.2}x SisdEngine)",
reads.len(),
reference.len(),
);
}