JAL-2416 JAL-728 reduced dependency on Blosum62 implementation details
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 29 Mar 2017 14:21:43 +0000 (15:21 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 29 Mar 2017 14:21:43 +0000 (15:21 +0100)
src/jalview/analysis/Conservation.java

index f4346c3..2b5a8f6 100755 (executable)
@@ -52,14 +52,19 @@ public class Conservation
 
   private static final int TOUPPERCASE = 'a' - 'A';
 
+  private static final int GAP_INDEX = -1;
+
   SequenceI[] sequences;
 
   int start;
 
   int end;
 
-  Vector<int[]> seqNums; // vector of int vectors where first is sequence
-                         // checksum
+  /*
+   * a list whose i'th element is an array whose first entry is the checksum
+   * of the i'th sequence, followed by residues encoded to score matrix index
+   */
+  Vector<int[]> seqNums;
 
   int maxLength = 0; // used by quality calcs
 
@@ -72,17 +77,17 @@ public class Conservation
    */
   Map<String, Integer>[] total;
 
-  boolean canonicaliseAa = true; // if true then conservation calculation will
-
-  // map all symbols to canonical aa numbering
-  // rather than consider conservation of that
-  // symbol
+  /*
+   * if true then conservation calculation will map all symbols to canonical aa
+   * numbering rather than consider conservation of that symbol
+   */
+  boolean canonicaliseAa = true;
 
-  /** Stores calculated quality values */
   private Vector<Double> quality;
 
-  /** Stores maximum and minimum values of quality values */
-  private double[] qualityRange = new double[2];
+  private double qualityMinimum;
+
+  private double qualityMaximum;
 
   private Sequence consSequence;
 
@@ -93,8 +98,16 @@ public class Conservation
 
   private String name = "";
 
+  /*
+   * an array, for each column, of counts of symbols (by score matrix index)
+   */
   private int[][] cons2;
 
+  /*
+   * gap counts for each column
+   */
+  private int[] cons2GapCounts;
+
   private String[] consSymbs;
 
   /**
@@ -172,7 +185,6 @@ public class Conservation
    */
   private void calcSeqNum(int i, ScoreMatrix sm)
   {
-    int gapIndex = sm.getGapIndex();
     int sSize = sequences.length;
 
     if ((i > -1) && (i < sSize))
@@ -207,10 +219,18 @@ public class Conservation
         for (j = 1; j <= len; j++)
         {
           // sqnum[j] = ResidueProperties.aaIndex[sq.charAt(j - 1)];
-          sqnum[j] = sm.getMatrixIndex(sq.charAt(j - 1));
-          if (sqnum[j] == -1)
+          char residue = sq.charAt(j - 1);
+          if (Comparison.isGap(residue))
           {
-            sqnum[j] = gapIndex;
+            sqnum[j] = GAP_INDEX;
+          }
+          else
+          {
+            sqnum[j] = sm.getMatrixIndex(residue);
+            if (sqnum[j] == -1)
+            {
+              sqnum[j] = GAP_INDEX;
+            }
           }
         }
 
@@ -536,7 +556,7 @@ public class Conservation
   // From Alignment.java in jalview118
   public void findQuality()
   {
-    findQuality(0, maxLength - 1);
+    findQuality(0, maxLength - 1, ScoreModels.getInstance().getBlosum62());
   }
 
   /**
@@ -554,13 +574,12 @@ public class Conservation
       calcSeqNum(i, sm);
     }
 
-    int gapIndex = sm.getGapIndex();
-
     if ((cons2 == null) || seqNumsChanged)
     {
       // FIXME remove magic number 24 without changing calc
       // sm.getSize() returns 25 so doesn't quite do it...
       cons2 = new int[maxLength][24];
+      cons2GapCounts = new int[maxLength];
 
       int j = 0;
 
@@ -570,12 +589,21 @@ public class Conservation
 
         for (i = 1; i < sqnum.length; i++)
         {
-          cons2[i - 1][sqnum[i]]++;
+          int index = sqnum[i];
+          if (index == GAP_INDEX)
+          {
+            cons2GapCounts[i - 1]++;
+          }
+          else
+          {
+            cons2[i - 1][index]++;
+          }
         }
 
+        // TODO should this start from sqnum.length?
         for (i = sqnum.length - 1; i < maxLength; i++)
         {
-          cons2[i][gapIndex]++; // gap count
+          cons2GapCounts[i]++;
         }
         j++;
       }
@@ -583,76 +611,78 @@ public class Conservation
   }
 
   /**
-   * Calculates the quality of the set of sequences
+   * Calculates the quality of the set of sequences over the given inclusive
+   * column range, using the specified substitution score matrix
    * 
-   * @param startRes
-   *          Start residue
-   * @param endRes
-   *          End residue
+   * @param startCol
+   * @param endCol
+   * @param scoreMatrix
    */
-  public void findQuality(int startRes, int endRes)
+  protected void findQuality(int startCol, int endCol, ScoreMatrix scoreMatrix)
   {
     quality = new Vector<Double>();
 
     double max = -Double.MAX_VALUE;
-    ScoreMatrix blosum62 = ScoreModels.getInstance().getBlosum62();
-    float[][] blosumScores = blosum62.getMatrix();
+    float[][] scores = scoreMatrix.getMatrix();
 
-    // Loop over columns // JBPNote Profiling info
-    // long ts = System.currentTimeMillis();
-    // long te = System.currentTimeMillis();
-    percentIdentity(blosum62);
+    percentIdentity(scoreMatrix);
 
     int size = seqNums.size();
     int[] lengths = new int[size];
-    double tot, bigtot, sr, tmp;
-    double[] x, xx;
-    int l, j, i, ii, i2, k, seqNum;
 
-    for (l = 0; l < size; l++)
+    for (int l = 0; l < size; l++)
     {
       lengths[l] = seqNums.elementAt(l).length - 1;
     }
 
-    final int symbolCount = blosum62.getSize();
-    int gapIndex = blosum62.getGapIndex();
+    final int symbolCount = scoreMatrix.getSize();
 
-    for (j = startRes; j <= endRes; j++)
+    for (int j = startCol; j <= endCol; j++)
     {
-      bigtot = 0;
+      double bigtot = 0;
 
       // First Xr = depends on column only
-      x = new double[symbolCount];
+      double[] x = new double[symbolCount];
 
-      for (ii = 0; ii < symbolCount; ii++)
+      for (int ii = 0; ii < symbolCount; ii++)
       {
         x[ii] = 0;
 
-        for (i2 = 0; i2 < symbolCount; i2++)
+        /*
+         * todo JAL-728 currently assuming last symbol in matrix is * for gap
+         * (which we ignore as counted separately); true for BLOSUM62 but may
+         * not be once alternative matrices are supported
+         */
+        for (int i2 = 0; i2 < symbolCount - 1; i2++)
         {
-          x[ii] += (((double) cons2[j][i2] * blosumScores[ii][i2]) + 4);
+          x[ii] += (((double) cons2[j][i2] * scores[ii][i2]) + 4D);
         }
+        x[ii] += 4D + cons2GapCounts[j] * scoreMatrix.getMinimumScore();
 
         x[ii] /= size;
       }
 
       // Now calculate D for each position and sum
-      for (k = 0; k < size; k++)
+      for (int k = 0; k < size; k++)
       {
-        tot = 0;
-        xx = new double[symbolCount];
-        seqNum = (j < lengths[k]) ? seqNums.elementAt(k)[j + 1] : gapIndex;
-        // Sequence, or gap at the end
+        double tot = 0;
+        double[] xx = new double[symbolCount];
+        // sequence character index, or implied gap if sequence too short
+        int seqNum = (j < lengths[k]) ? seqNums.elementAt(k)[j + 1]
+                : GAP_INDEX;
 
-        // This is a loop over r
-        for (i = 0; i < symbolCount - 1; i++)
+        for (int i = 0; i < symbolCount - 1; i++)
         {
-          sr = 0;
-
-          sr = (double) blosumScores[i][seqNum] + 4;
+          double sr = 4D;
+          if (seqNum == GAP_INDEX)
+          {
+            sr += scoreMatrix.getMinimumScore();
+          }
+          else
+          {
+            sr += scores[i][seqNum];
+          }
 
-          // Calculate X with another loop over residues
-          // System.out.println("Xi " + i + " " + x[i] + " " + sr);
           xx[i] = x[i] - sr;
 
           tot += (xx[i] * xx[i]);
@@ -661,25 +691,18 @@ public class Conservation
         bigtot += Math.sqrt(tot);
       }
 
-      // This is the quality for one column
-      if (max < bigtot)
-      {
-        max = bigtot;
-      }
+      max = Math.max(max, bigtot);
 
-      // bigtot = bigtot * (size-cons2[j][23])/size;
       quality.addElement(new Double(bigtot));
-
-      // Need to normalize by gaps
     }
 
     double newmax = -Double.MAX_VALUE;
 
-    for (j = startRes; j <= endRes; j++)
+    for (int j = startCol; j <= endCol; j++)
     {
-      tmp = quality.elementAt(j).doubleValue();
+      double tmp = quality.elementAt(j).doubleValue();
       // tmp = ((max - tmp) * (size - cons2[j][23])) / size;
-      tmp = ((max - tmp) * (size - cons2[j][gapIndex])) / size;
+      tmp = ((max - tmp) * (size - cons2GapCounts[j])) / size;
 
       // System.out.println(tmp+ " " + j);
       quality.setElementAt(new Double(tmp), j);
@@ -690,9 +713,8 @@ public class Conservation
       }
     }
 
-    // System.out.println("Quality " + s);
-    qualityRange[0] = 0D;
-    qualityRange[1] = newmax;
+    qualityMinimum = 0D;
+    qualityMaximum = newmax;
   }
 
   /**
@@ -742,14 +764,14 @@ public class Conservation
 
     if (quality2 != null)
     {
-      quality2.graphMax = (float) qualityRange[1];
+      quality2.graphMax = (float) qualityMaximum;
       if (quality2.annotations != null
               && quality2.annotations.length < alWidth)
       {
         quality2.annotations = new Annotation[alWidth];
       }
-      qmin = (float) qualityRange[0];
-      qmax = (float) qualityRange[1];
+      qmin = (float) qualityMinimum;
+      qmax = (float) qualityMaximum;
     }
 
     for (int i = istart; i < alWidth; i++)