add unit tests
This commit is contained in:
+2
-2
@@ -4,13 +4,13 @@
|
|||||||
"description": "Wavefront alignment algorithm in JS",
|
"description": "Wavefront alignment algorithm in JS",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.43.0",
|
"eslint": "^8.43.0",
|
||||||
"eslint-config-standard": "^17.1.0",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"eslint-plugin-import": "^2.27.5",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"eslint-plugin-n": "^16.0.1",
|
"eslint-plugin-n": "^16.0.1",
|
||||||
"eslint-plugin-promise": "^6.1.1"
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
|
"progress": "^2.0.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "DEBUG=eslint:cli-engine eslint --fix ."
|
"lint": "DEBUG=eslint:cli-engine eslint --fix ."
|
||||||
|
|||||||
+28
-11
@@ -1,14 +1,31 @@
|
|||||||
import wf_align from "./wfa.js";
|
import wf_align from "./wfa.js";
|
||||||
|
import fs from "fs";
|
||||||
|
import ProgressBar from "progress";
|
||||||
|
|
||||||
const p = {
|
let data = fs.readFileSync("./tests/tests.json");
|
||||||
x: 4,
|
data = JSON.parse(data);
|
||||||
o: 6,
|
const sequences = fs.readFileSync("./tests/sequences").toString().split("\n");
|
||||||
e: 2
|
//const total = sequences.length;
|
||||||
};
|
const total = 500; // skip the later tests because of memory usage
|
||||||
|
|
||||||
// this should be score=24, Alignment=XDIX
|
for (const test_name of Object.keys(data)) {
|
||||||
console.time("time")
|
const test = data[test_name];
|
||||||
const {CIGAR, score} = wf_align("TCTTTACTCGCGCGTTGGAGAAATACAATAGT", "TCTATACTGCGCGTTTGGAGAAATAAAATAGT", p)
|
const penalties = test.penalties;
|
||||||
console.timeEnd("time")
|
const solutions = fs.readFileSync(test.solutions).toString().split("\n");
|
||||||
console.log(`score: ${score}`);
|
const bar = new ProgressBar(":bar :current/:total", { total: total / 2 });
|
||||||
console.log(`CIGAR: ${CIGAR}`);
|
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`);
|
||||||
|
}
|
||||||
+55
-51
@@ -5,6 +5,7 @@ class WavefrontComponent {
|
|||||||
this.W = []; // wavefront diag distance for each wavefront
|
this.W = []; // wavefront diag distance for each wavefront
|
||||||
this.A = []; // compact CIGAR for backtrace
|
this.A = []; // compact CIGAR for backtrace
|
||||||
}
|
}
|
||||||
|
|
||||||
// get value for wavefront=score, diag=k
|
// get value for wavefront=score, diag=k
|
||||||
get_val (score, k) {
|
get_val (score, k) {
|
||||||
if (this.W[score] !== undefined && this.W[score][k] !== undefined) {
|
if (this.W[score] !== undefined && this.W[score][k] !== undefined) {
|
||||||
@@ -14,6 +15,7 @@ class WavefrontComponent {
|
|||||||
return NaN;
|
return NaN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set value for wavefront=score, diag=k
|
// set value for wavefront=score, diag=k
|
||||||
set_val (score, k, val) {
|
set_val (score, k, val) {
|
||||||
if (this.W[score]) {
|
if (this.W[score]) {
|
||||||
@@ -24,6 +26,7 @@ class WavefrontComponent {
|
|||||||
this.W[score][k] = val;
|
this.W[score][k] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get alignment traceback
|
// get alignment traceback
|
||||||
get_traceback (score, k) {
|
get_traceback (score, k) {
|
||||||
if (this.A[score] !== undefined && this.A[score][k] !== undefined) {
|
if (this.A[score] !== undefined && this.A[score][k] !== undefined) {
|
||||||
@@ -33,6 +36,7 @@ class WavefrontComponent {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set alignment traceback
|
// set alignment traceback
|
||||||
set_traceback (score, k, traceback) {
|
set_traceback (score, k, traceback) {
|
||||||
if (this.A[score]) {
|
if (this.A[score]) {
|
||||||
@@ -43,24 +47,29 @@ class WavefrontComponent {
|
|||||||
this.A[score][k] = traceback;
|
this.A[score][k] = traceback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get hi for wavefront=score
|
// get hi for wavefront=score
|
||||||
get_hi (score) {
|
get_hi (score) {
|
||||||
const hi = this.hi[score];
|
const hi = this.hi[score];
|
||||||
return isNaN(hi) ? 0 : hi;
|
return isNaN(hi) ? 0 : hi;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set hi for wavefront=score
|
// set hi for wavefront=score
|
||||||
set_hi (score, hi) {
|
set_hi (score, hi) {
|
||||||
this.hi[score] = hi;
|
this.hi[score] = hi;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get lo for wavefront=score
|
// get lo for wavefront=score
|
||||||
get_lo (score) {
|
get_lo (score) {
|
||||||
const lo = this.lo[score];
|
const lo = this.lo[score];
|
||||||
return isNaN(lo) ? 0 : lo;
|
return isNaN(lo) ? 0 : lo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set lo for wavefront=score
|
// set lo for wavefront=score
|
||||||
set_lo (score, lo) {
|
set_lo (score, lo) {
|
||||||
this.lo[score] = lo;
|
this.lo[score] = lo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// string representation of all wavefronts
|
// string representation of all wavefronts
|
||||||
toString () {
|
toString () {
|
||||||
const traceback_str = ["OI", "EI", "OD", "ED", "SB", "IN", "DL", "EN"];
|
const traceback_str = ["OI", "EI", "OD", "ED", "SB", "IN", "DL", "EN"];
|
||||||
@@ -85,7 +94,7 @@ class WavefrontComponent {
|
|||||||
s += "|";
|
s += "|";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s += ">\t<"
|
s += ">\t<";
|
||||||
for (let k = min_lo; k <= max_hi; k++) {
|
for (let k = min_lo; k <= max_hi; k++) {
|
||||||
s += FormatNumberLength(k, 2);
|
s += FormatNumberLength(k, 2);
|
||||||
if (k < max_hi) {
|
if (k < max_hi) {
|
||||||
@@ -143,8 +152,8 @@ const traceback = {
|
|||||||
Sub: 4,
|
Sub: 4,
|
||||||
Ins: 5,
|
Ins: 5,
|
||||||
Del: 6,
|
Del: 6,
|
||||||
End: 7,
|
End: 7
|
||||||
}
|
};
|
||||||
|
|
||||||
function FormatNumberLength (num, length) {
|
function FormatNumberLength (num, length) {
|
||||||
let r = "" + num;
|
let r = "" + num;
|
||||||
@@ -170,13 +179,8 @@ function max (args) {
|
|||||||
return max === -Infinity ? NaN : max;
|
return max === -Infinity ? NaN : max;
|
||||||
}
|
}
|
||||||
|
|
||||||
function argmin (args) {
|
|
||||||
const val = min(args)
|
|
||||||
return args.indexOf(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function argmax (args) {
|
function argmax (args) {
|
||||||
const val = max(args)
|
const val = max(args);
|
||||||
return args.indexOf(val);
|
return args.indexOf(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +194,7 @@ export default function wf_align (s1, s2, penalties) {
|
|||||||
M.set_val(0, 0, 0);
|
M.set_val(0, 0, 0);
|
||||||
M.set_hi(0, 0);
|
M.set_hi(0, 0);
|
||||||
M.set_lo(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 I = new WavefrontComponent();
|
||||||
const D = new WavefrontComponent();
|
const D = new WavefrontComponent();
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -244,7 +248,7 @@ function wf_next (M, I, D, score, penalties) {
|
|||||||
I.set_traceback(score, k, [traceback.OpenIns, traceback.ExtdIns][argmax([
|
I.set_traceback(score, k, [traceback.OpenIns, traceback.ExtdIns][argmax([
|
||||||
M.get_val(score - o - e, k - 1),
|
M.get_val(score - o - e, k - 1),
|
||||||
I.get_val(score - e, k - 1)
|
I.get_val(score - e, k - 1)
|
||||||
])])
|
])]);
|
||||||
D.set_val(score, k, max([
|
D.set_val(score, k, max([
|
||||||
M.get_val(score - o - e, k + 1),
|
M.get_val(score - o - e, k + 1),
|
||||||
D.get_val(score - 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([
|
D.set_traceback(score, k, [traceback.OpenDel, traceback.ExtdDel][argmax([
|
||||||
M.get_val(score - o - e, k + 1),
|
M.get_val(score - o - e, k + 1),
|
||||||
D.get_val(score - e, k + 1)
|
D.get_val(score - e, k + 1)
|
||||||
])])
|
])]);
|
||||||
M.set_val(score, k, max([
|
M.set_val(score, k, max([
|
||||||
M.get_val(score - x, k) + 1,
|
M.get_val(score - x, k) + 1,
|
||||||
I.get_val(score, k),
|
I.get_val(score, k),
|
||||||
@@ -279,46 +283,46 @@ function wf_backtrace (M, I, D, score, penalties, A_k) {
|
|||||||
while (!done) {
|
while (!done) {
|
||||||
CIGAR_rev += traceback_CIGAR[current_traceback];
|
CIGAR_rev += traceback_CIGAR[current_traceback];
|
||||||
switch (current_traceback) {
|
switch (current_traceback) {
|
||||||
case traceback.OpenIns:
|
case traceback.OpenIns:
|
||||||
tb_s = tb_s - o - e;
|
tb_s = tb_s - o - e;
|
||||||
tb_k = tb_k - 1;
|
tb_k = tb_k - 1;
|
||||||
current_traceback = M.get_traceback(tb_s, tb_k);
|
current_traceback = M.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.ExtdIns:
|
case traceback.ExtdIns:
|
||||||
tb_s = tb_s - e;
|
tb_s = tb_s - e;
|
||||||
tb_k = tb_k - 1;
|
tb_k = tb_k - 1;
|
||||||
current_traceback = I.get_traceback(tb_s, tb_k);
|
current_traceback = I.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.OpenDel:
|
case traceback.OpenDel:
|
||||||
tb_s = tb_s - o - e;
|
tb_s = tb_s - o - e;
|
||||||
tb_k = tb_k + 1;
|
tb_k = tb_k + 1;
|
||||||
current_traceback = M.get_traceback(tb_s, tb_k);
|
current_traceback = M.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.ExtdDel:
|
case traceback.ExtdDel:
|
||||||
tb_s = tb_s - e;
|
tb_s = tb_s - e;
|
||||||
tb_k = tb_k + 1;
|
tb_k = tb_k + 1;
|
||||||
current_traceback = D.get_traceback(tb_s, tb_k);
|
current_traceback = D.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.Sub:
|
case traceback.Sub:
|
||||||
tb_s = tb_s - x;
|
tb_s = tb_s - x;
|
||||||
tb_k = tb_k;
|
// tb_k = tb_k;
|
||||||
current_traceback = M.get_traceback(tb_s, tb_k);
|
current_traceback = M.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.Ins:
|
case traceback.Ins:
|
||||||
tb_s = tb_s;
|
// tb_s = tb_s;
|
||||||
tb_k = tb_k;
|
// tb_k = tb_k;
|
||||||
current_traceback = I.get_traceback(tb_s, tb_k);
|
current_traceback = I.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.Del:
|
case traceback.Del:
|
||||||
tb_s = tb_s;
|
// tb_s = tb_s;
|
||||||
tb_k = tb_k;
|
// tb_k = tb_k;
|
||||||
current_traceback = D.get_traceback(tb_s, tb_k)
|
current_traceback = D.get_traceback(tb_s, tb_k);
|
||||||
break;
|
break;
|
||||||
case traceback.End:
|
case traceback.End:
|
||||||
done = true
|
done = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const CIGAR = Array.from(CIGAR_rev).reverse().join("");
|
const CIGAR = Array.from(CIGAR_rev).reverse().join("");
|
||||||
return {CIGAR, score};
|
return { CIGAR, score };
|
||||||
}
|
}
|
||||||
|
|||||||
+610
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
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user