import { Sequence, GenericSeqNode } from './SeqNode'
import { Aligner } from './Aligner'
import { Triple, GapsParams, TripleTable, AlignType, isAffineGapAlignment, AlignmentResult } from './types'
import { fillByIndex, initializePointerTable, initializeTable } from './Table'
import { isConstructorDeclaration } from 'typescript'
/**
* Generic temporal sequence aligner
*
* Implements the method reported in:
* `Syed et al. Temporal Needleman-Wunsch `__
*
* Currently, it supports linear gap penalty but extends/adapts the notation in the original paper to support all alignment paths
*
* @todo
* extend it to affine gap penalty
* @param alignType defining type of alignment.
* There are four alignment types:
* - `global` (i.e. Needleman-Wunsch or more precisely a variant of David Sankoff algorithm
* see Sankoff, D. (1972). "Matching sequences under deletion-insertion constraints".
* Proceedings of the National Academy of Sciences of the United States of America. 69 (1): 4–6
* )
* - `local` (i.e. Smith-Waterman algorithm)
* - `semi-global` alignment
* - `end-gap-free` alignment
*/
export class TemporalAligner extends Aligner {
align(seq1: Sequence, seq2: Sequence, gaps_params: GapsParams) {
this.seq1 = seq1
this.seq2 = seq2
// parse the gaps -- assume that gaps are negative numbers
const gapOpen = gaps_params.gapOpen
const gapExt = gaps_params.gapExt
let l_seq1 = seq1.length
let l_seq2 = seq2.length
if(!gapExt) { // case of linear gap applied
// backtrack matrix
let Vp: TripleTable = initializePointerTable(seq1.length, seq2.length)
// initialize V matrix -- holding the best alignment score for seq1[1:i] and seq2[1:j]
let V = initializeTable(seq1.length+1, seq2.length+1, 0.0)
// initialize the transition time tables
let [TR, TC] = this.initTransitionTable(seq1, seq2)
if(this.alignType == AlignType.Global) {
// fill the top row with the linear gap
V[0] = [0, ...fillByIndex(seq2.length, (i: number) => ((i+1) * gapOpen))]
// fill the first left column with the linear gap
for (let i=1; i ((i+1) * gapOpen))]
}
function arraysEqual(a: T[], b: T[]) {
if (a === b) return true
if (a === null || b === null) return false
if (a.length !== b.length) return false
for (let i=0; i max_s) max_s = tmp
}
}
}
const case_1 = V[i][j-1] + gapOpen // align node_j from seq_2 to a gap
const case_2 = max_s // align node_i from seq_1 to node_j from seq_2
const case_3 = V[i-1][j] + gapOpen // align node_i from seq_1 to a gap
const cases_vec: Triple = [case_1, case_2, case_3]
this.fillDynamicAndPointerTables(cases_vec, V, Vp, [i,j], /*main_dtable = */true)
// updated the TR and TC tables
let TR_l: Triple = [0, 0, 0]
let TC_l: Triple = [0, 0, 0]
for (const [flag, index] of Vp[i][j].entries()) {
if (flag) {
if(index === 0) {
TR_l[index] = TR[i][j-1][-1] // -1 = last
TC_l[index] = TC[i][j-1][0] + node_seq2.transTime!
}
else if(index == 2) {
TR_l[index] = TR[i-1][j][-1] + node_seq1.transTime!
TC_l[index] = TC[i-1][j][0]
}
}
}
TR[i][j] = TR_l
TC[i][j] = TC_l
}
}
this.alignment = {
V,
Vp,
end_i: seq1.length,
end_j: seq2.length
}
}
else { // case of affine gap applied
throw new Error("Affine gap is not supported yet")
}
}
private initTransitionTable(seq1: GenericSeqNode[],
seq2: GenericSeqNode[]): [TripleTable, TripleTable] {
// TR table
let TR: TripleTable = initializeTable(seq2.length+1, seq1.length+1, [0, 0, 0])
// initialize first column
for (let i=1; i {
if (!this.alignment) {
throw new Error("alignments to be present before retrieving them")
}
if (isAffineGapAlignment(this.alignment)) {
throw new Error("affine gap for temporal alignment not yet supported")
}
return this.retrieveAlignmentsLinear(this.alignment)
}
}