/* * Copyright (c) 2010, The Broad Institute * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.broadinstitute.sting.utils; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; import net.sf.samtools.Cigar; import java.util.List; import java.util.ArrayList; import java.util.Collections; /** * Created by IntelliJ IDEA. * User: asivache * Date: Mar 23, 2009 * Time: 1:54:54 PM * To change this template use File | Settings | File Templates. */ public class SWPairwiseAlignment { private int alignment_offset; // offset of s2 w/respect to s1 private Cigar alignmentCigar; private final double w_match; private final double w_mismatch; private final double w_open; private final double w_extend; private static final int MSTATE = 0; private static final int ISTATE = 1; private static final int DSTATE = 2; // ************************************************************************ // **** IMPORTANT NOTE: **** // **** This class assumes that all bytes come from UPPERCASED chars! **** // ************************************************************************ public SWPairwiseAlignment(byte[] seq1, byte[] seq2, double match, double mismatch, double open, double extend ) { w_match = match; w_mismatch = mismatch; w_open = open; w_extend = extend; align(seq1,seq2); } public SWPairwiseAlignment(byte[] seq1, byte[] seq2) { this(seq1,seq2,1.0,-1.0/3.0,-1.0-1.0/3.0,-1.0/3.0); // match=1, mismatch = -1/3, gap=-(1+k/3) } public Cigar getCigar() { return alignmentCigar ; } public int getAlignmentStart2wrt1() { return alignment_offset; } public void align(final byte[] a, final byte[] b) { final int n = a.length; final int m = b.length; double [][] sw = new double[n+1][m+1]; int [][] btrack = new int[n+1][m+1]; calculateMatrix(a, b, sw, btrack); calculateCigar(n, m, sw, btrack); // length of the segment (continuous matches, insertions or deletions) } private void calculateMatrix(final byte[] a, final byte[] b, double [][] sw, int [][] btrack ) { final int n = a.length; final int m = b.length; // build smith-waterman matrix and keep backtrack info: for ( int i = 1 ; i < n+1 ; i++ ) { byte a_base = a[i-1]; // letter in a at the current pos for ( int j = 1 ; j < m+1 ; j++ ) { final byte b_base = b[j-1]; // letter in b at the current pos double step_diag = sw[i-1][j-1] + wd(a_base,b_base); double step_down = 0.0 ; int kd = 0; for ( int k = 1 ; k < i ; k++ ) { final double gap_penalty = wk(k); if ( step_down < sw[i-k][j]+gap_penalty ) { step_down=sw[i-k][j]+gap_penalty; kd = k; } } double step_right = 0; int ki = 0; for ( int k = 1 ; k < j ; k++ ) { final double gap_penalty = wk(k); if ( step_right < sw[i][j-k]+gap_penalty ) { step_right=sw[i][j-k]+gap_penalty; ki = k; } } if ( step_down > step_right ) { if ( step_down > step_diag ) { sw[i][j] = Math.max(0,step_down); btrack[i][j] = kd; // positive=vertical } else { sw[i][j] = Math.max(0,step_diag); btrack[i][j] = 0; // 0 = diagonal } } else { // step_down < step_right if ( step_right > step_diag ) { sw[i][j] = Math.max(0,step_right); btrack[i][j] = -ki; // negative = horizontal } else { sw[i][j] = Math.max(0,step_diag); btrack[i][j] = 0; // 0 = diagonal } } sw[i][j] = Math.max(0, Math.max(step_diag,Math.max(step_down,step_right))); } } // print(sw,a,b); } private void calculateCigar(int n, int m, double [][] sw, int [][] btrack) { // p holds the position we start backtracking from; we will be assembling a cigar in the backwards order //PrimitivePair.Int p = new PrimitivePair.Int(); int p1 = 0, p2 = 0; double maxscore = 0.0; int segment_length = 0; // length of the segment (continuous matches, insertions or deletions) // look for largest score. we use >= combined with the traversal direction // to ensure that if two scores are equal, the one closer to diagonal gets picked for ( int i = 1 ; i < n+1 ; i++ ) { if ( sw[i][m] >= maxscore ) { p1 = i; p2 = m ; maxscore = sw[i][m]; } } for ( int j = 1 ; j < m+1 ; j++ ) { if ( sw[n][j] > maxscore || sw[n][j] == maxscore && Math.abs(n-j) < Math.abs(p1 - p2)) { p1 = n; p2 = j ; maxscore = sw[n][j]; segment_length = m - j ; // end of sequence 2 is overhanging; we will just record it as 'M' segment } } // we will be placing all insertions and deletions into sequence b, so the state are named w/regard // to that sequence int state = MSTATE; List lce = new ArrayList(5); do { int btr = btrack[p1][p2]; int step_left = ( btr < 0 ? -btr : 1); int step_up = ( btr > 0 ? btr : 1 ); int new_state; if ( btr > 0 ) new_state = DSTATE; else if ( btr < 0 ) new_state = ISTATE; else new_state = MSTATE; int step_length = 1; // move to next best location in the sw matrix: switch( new_state ) { case MSTATE: p1--; p2--; break; case ISTATE: p2-=step_left; step_length = step_left; break; case DSTATE: p1-=step_up; step_length = step_up; break; } // now let's see if the state actually changed: if ( new_state == state ) segment_length+=step_length; else { // state changed, lets emit previous segment, whatever it was (Insertion Deletion, or (Mis)Match). lce.add(makeElement(state, segment_length)); segment_length = step_length; state = new_state; } } while ( sw[p1][p2] != 0 ); // post-process the last segment we are still keeping lce.add(makeElement(state, segment_length + p2)); alignment_offset = p1 - p2; Collections.reverse(lce); alignmentCigar = new Cigar(lce); } private CigarElement makeElement(int state, int segment_length) { CigarOperator o = null; switch(state) { case MSTATE: o = CigarOperator.M; break; case ISTATE: o = CigarOperator.I; break; case DSTATE: o = CigarOperator.D; break; } return new CigarElement(segment_length,o); } private double wd(byte x, byte y) { return (x == y ? w_match : w_mismatch); } private double wk(int k) { return w_open+(k-1)*w_extend; // gap } private void print(int[][] s) { for ( int i = 0 ; i < s.length ; i++) { for ( int j = 0; j < s[i].length ; j++ ) { System.out.printf(" %4d",s[i][j]); } System.out.println(); } } private void print(double[][] s) { for ( int i = 0 ; i < s.length ; i++) { for ( int j = 0; j < s[i].length ; j++ ) { System.out.printf(" %4g",s[i][j]); } System.out.println(); } } private void print(int[][] s, String a, String b) { System.out.print(" "); for ( int j = 1 ; j < s[0].length ; j++) System.out.printf(" %4c",b.charAt(j-1)) ; System.out.println(); for ( int i = 0 ; i < s.length ; i++) { if ( i > 0 ) System.out.print(a.charAt(i-1)); else System.out.print(' '); System.out.print(" "); for ( int j = 0; j < s[i].length ; j++ ) { System.out.printf(" %4d",s[i][j]); } System.out.println(); } } private void print(double[][] s, String a, String b) { System.out.print(""); for ( int j = 1 ; j < s[0].length ; j++) System.out.printf(" %4c",b.charAt(j-1)) ; System.out.println(); for ( int i = 0 ; i < s.length ; i++) { if ( i > 0 ) System.out.print(a.charAt(i-1)); else System.out.print(' '); System.out.print(" "); for ( int j = 0; j < s[i].length ; j++ ) { System.out.printf(" %2.1f",s[i][j]); } System.out.println(); } } }