add unit tests
This commit is contained in:
parent
685f43c19b
commit
d04679819d
@ -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 ."
|
||||||
|
39
src/main.js
39
src/main.js
@ -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`);
|
||||||
|
}
|
106
src/wfa.js
106
src/wfa.js
@ -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
tests/sequences
Normal file
610
tests/sequences
Normal file
File diff suppressed because one or more lines are too long
305
tests/test_affine_p0_sol
Normal file
305
tests/test_affine_p0_sol
Normal file
File diff suppressed because one or more lines are too long
305
tests/test_affine_p1_sol
Normal file
305
tests/test_affine_p1_sol
Normal file
File diff suppressed because one or more lines are too long
305
tests/test_affine_p2_sol
Normal file
305
tests/test_affine_p2_sol
Normal file
File diff suppressed because one or more lines are too long
29
tests/tests.json
Normal file
29
tests/tests.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user