add unit tests

This commit is contained in:
Arthur Lu
2024-05-28 19:40:16 +00:00
parent 685f43c19b
commit d04679819d
8 changed files with 1641 additions and 66 deletions
+3 -3
View File
@@ -4,15 +4,15 @@
"description": "Wavefront alignment algorithm in JS",
"main": "src/main.js",
"type": "module",
"dependencies": {},
"devDependencies": {
"eslint": "^8.43.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^16.0.1",
"eslint-plugin-promise": "^6.1.1"
"eslint-plugin-promise": "^6.1.1",
"progress": "^2.0.3"
},
"scripts": {
"lint": "DEBUG=eslint:cli-engine eslint --fix ."
}
}
}
+28 -11
View File
@@ -1,14 +1,31 @@
import wf_align from "./wfa.js";
import fs from "fs";
import ProgressBar from "progress";
const p = {
x: 4,
o: 6,
e: 2
};
let data = fs.readFileSync("./tests/tests.json");
data = JSON.parse(data);
const sequences = fs.readFileSync("./tests/sequences").toString().split("\n");
//const total = sequences.length;
const total = 500; // skip the later tests because of memory usage
// this should be score=24, Alignment=XDIX
console.time("time")
const {CIGAR, score} = wf_align("TCTTTACTCGCGCGTTGGAGAAATACAATAGT", "TCTATACTGCGCGTTTGGAGAAATAAAATAGT", p)
console.timeEnd("time")
console.log(`score: ${score}`);
console.log(`CIGAR: ${CIGAR}`);
for (const test_name of Object.keys(data)) {
const test = data[test_name];
const penalties = test.penalties;
const solutions = fs.readFileSync(test.solutions).toString().split("\n");
const bar = new ProgressBar(":bar :current/:total", { total: total / 2 });
console.log(`test: ${test_name}`);
let correct = 0;
let j = 0;
for (let i = 0; i < total; i += 2) {
const s1 = sequences[i].replace(">");
const s2 = sequences[i + 1].replace("<");
const { CIGAR, score } = wf_align(s1, s2, penalties);
const solution_score = Number(solutions[j].split("\t")[0]);
if (solution_score === -score) {
correct += 1;
}
j += 1;
bar.tick();
}
console.log(`correct: ${correct}\ntotal: ${total / 2}\n`);
}
+56 -52
View File
@@ -5,6 +5,7 @@ class WavefrontComponent {
this.W = []; // wavefront diag distance for each wavefront
this.A = []; // compact CIGAR for backtrace
}
// get value for wavefront=score, diag=k
get_val (score, k) {
if (this.W[score] !== undefined && this.W[score][k] !== undefined) {
@@ -14,6 +15,7 @@ class WavefrontComponent {
return NaN;
}
}
// set value for wavefront=score, diag=k
set_val (score, k, val) {
if (this.W[score]) {
@@ -24,7 +26,8 @@ class WavefrontComponent {
this.W[score][k] = val;
}
}
// get alignment traceback
// get alignment traceback
get_traceback (score, k) {
if (this.A[score] !== undefined && this.A[score][k] !== undefined) {
return this.A[score][k];
@@ -33,6 +36,7 @@ class WavefrontComponent {
return undefined;
}
}
// set alignment traceback
set_traceback (score, k, traceback) {
if (this.A[score]) {
@@ -43,24 +47,29 @@ class WavefrontComponent {
this.A[score][k] = traceback;
}
}
// get hi for wavefront=score
get_hi (score) {
const hi = this.hi[score];
return isNaN(hi) ? 0 : hi;
}
// set hi for wavefront=score
set_hi (score, hi) {
this.hi[score] = hi;
}
// get lo for wavefront=score
get_lo (score) {
const lo = this.lo[score];
return isNaN(lo) ? 0 : lo;
}
// set lo for wavefront=score
set_lo (score, lo) {
this.lo[score] = lo;
}
// string representation of all wavefronts
toString () {
const traceback_str = ["OI", "EI", "OD", "ED", "SB", "IN", "DL", "EN"];
@@ -85,7 +94,7 @@ class WavefrontComponent {
s += "|";
}
}
s += ">\t<"
s += ">\t<";
for (let k = min_lo; k <= max_hi; k++) {
s += FormatNumberLength(k, 2);
if (k < max_hi) {
@@ -143,8 +152,8 @@ const traceback = {
Sub: 4,
Ins: 5,
Del: 6,
End: 7,
}
End: 7
};
function FormatNumberLength (num, length) {
let r = "" + num;
@@ -170,13 +179,8 @@ function max (args) {
return max === -Infinity ? NaN : max;
}
function argmin (args) {
const val = min(args)
return args.indexOf(val);
}
function argmax (args) {
const val = max(args)
const val = max(args);
return args.indexOf(val);
}
@@ -190,7 +194,7 @@ export default function wf_align (s1, s2, penalties) {
M.set_val(0, 0, 0);
M.set_hi(0, 0);
M.set_lo(0, 0);
M.set_traceback(0, 0, traceback.End)
M.set_traceback(0, 0, traceback.End);
const I = new WavefrontComponent();
const D = new WavefrontComponent();
while (true) {
@@ -244,7 +248,7 @@ function wf_next (M, I, D, score, penalties) {
I.set_traceback(score, k, [traceback.OpenIns, traceback.ExtdIns][argmax([
M.get_val(score - o - e, k - 1),
I.get_val(score - e, k - 1)
])])
])]);
D.set_val(score, k, max([
M.get_val(score - o - e, k + 1),
D.get_val(score - e, k + 1)
@@ -252,7 +256,7 @@ function wf_next (M, I, D, score, penalties) {
D.set_traceback(score, k, [traceback.OpenDel, traceback.ExtdDel][argmax([
M.get_val(score - o - e, k + 1),
D.get_val(score - e, k + 1)
])])
])]);
M.set_val(score, k, max([
M.get_val(score - x, k) + 1,
I.get_val(score, k),
@@ -279,46 +283,46 @@ function wf_backtrace (M, I, D, score, penalties, A_k) {
while (!done) {
CIGAR_rev += traceback_CIGAR[current_traceback];
switch (current_traceback) {
case traceback.OpenIns:
tb_s = tb_s - o - e;
tb_k = tb_k - 1;
current_traceback = M.get_traceback(tb_s, tb_k);
break;
case traceback.ExtdIns:
tb_s = tb_s - e;
tb_k = tb_k - 1;
current_traceback = I.get_traceback(tb_s, tb_k);
break;
case traceback.OpenDel:
tb_s = tb_s - o - e;
tb_k = tb_k + 1;
current_traceback = M.get_traceback(tb_s, tb_k);
break;
case traceback.ExtdDel:
tb_s = tb_s - e;
tb_k = tb_k + 1;
current_traceback = D.get_traceback(tb_s, tb_k);
break;
case traceback.Sub:
tb_s = tb_s - x;
tb_k = tb_k;
current_traceback = M.get_traceback(tb_s, tb_k);
break;
case traceback.Ins:
tb_s = tb_s;
tb_k = tb_k;
current_traceback = I.get_traceback(tb_s, tb_k);
break;
case traceback.Del:
tb_s = tb_s;
tb_k = tb_k;
current_traceback = D.get_traceback(tb_s, tb_k)
break;
case traceback.End:
done = true
break;
case traceback.OpenIns:
tb_s = tb_s - o - e;
tb_k = tb_k - 1;
current_traceback = M.get_traceback(tb_s, tb_k);
break;
case traceback.ExtdIns:
tb_s = tb_s - e;
tb_k = tb_k - 1;
current_traceback = I.get_traceback(tb_s, tb_k);
break;
case traceback.OpenDel:
tb_s = tb_s - o - e;
tb_k = tb_k + 1;
current_traceback = M.get_traceback(tb_s, tb_k);
break;
case traceback.ExtdDel:
tb_s = tb_s - e;
tb_k = tb_k + 1;
current_traceback = D.get_traceback(tb_s, tb_k);
break;
case traceback.Sub:
tb_s = tb_s - x;
// tb_k = tb_k;
current_traceback = M.get_traceback(tb_s, tb_k);
break;
case traceback.Ins:
// tb_s = tb_s;
// tb_k = tb_k;
current_traceback = I.get_traceback(tb_s, tb_k);
break;
case traceback.Del:
// tb_s = tb_s;
// tb_k = tb_k;
current_traceback = D.get_traceback(tb_s, tb_k);
break;
case traceback.End:
done = true;
break;
}
}
const CIGAR = Array.from(CIGAR_rev).reverse().join("");
return {CIGAR, score};
return { CIGAR, score };
}
+610
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+29
View File
@@ -0,0 +1,29 @@
{
"p0": {
"penalties": {
"m": 0,
"x": 1,
"o": 2,
"e": 1
},
"solutions": "./tests/test_affine_p0_sol"
},
"p1": {
"penalties": {
"m": 0,
"x": 3,
"o": 1,
"e": 4
},
"solutions": "./tests/test_affine_p1_sol"
},
"p2": {
"penalties": {
"m": 0,
"x": 5,
"o": 3,
"e": 2
},
"solutions": "./tests/test_affine_p2_sol"
}
}