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) } }