Merge branch 'develop' into features/JAL-2393customMatrices
[jalview.git] / src / jalview / analysis / PCA.java
index ae83400..b6766c6 100755 (executable)
@@ -23,31 +23,28 @@ package jalview.analysis;
 import jalview.analysis.scoremodels.PairwiseDistanceModel;
 import jalview.analysis.scoremodels.ScoreMatrix;
 import jalview.analysis.scoremodels.ScoreModels;
-import jalview.datamodel.BinarySequence;
-import jalview.datamodel.BinarySequence.InvalidSequenceTypeException;
-import jalview.math.Matrix;
+import jalview.math.MatrixI;
 
 import java.io.PrintStream;
 
 /**
  * Performs Principal Component Analysis on given sequences
- * 
- * @author $author$
- * @version $Revision$
  */
 public class PCA implements Runnable
 {
-  Matrix m;
-
-  Matrix symm;
+  boolean jvCalcMode = true;
 
-  Matrix m2;
+  MatrixI symm;
 
   double[] eigenvalue;
 
-  Matrix eigenvector;
+  MatrixI eigenvector;
 
-  StringBuffer details = new StringBuffer();
+  StringBuilder details = new StringBuilder(1024);
+
+  private String[] seqs;
+
+  private ScoreMatrix scoreMatrix;
 
   /**
    * Creates a new PCA object. By default, uses blosum62 matrix to generate
@@ -78,88 +75,24 @@ public class PCA implements Runnable
 
   public PCA(String[] s, boolean nucleotides, String s_m)
   {
+    this.seqs = s;
 
-    BinarySequence[] bs = new BinarySequence[s.length];
-    int ii = 0;
-
-    while ((ii < s.length) && (s[ii] != null))
-    {
-      bs[ii] = new BinarySequence(s[ii], nucleotides);
-      bs[ii].encode();
-      ii++;
-    }
-
-    BinarySequence[] bs2 = new BinarySequence[s.length];
-    ii = 0;
-    ScoreMatrix smtrx = null;
+    scoreMatrix = null;
     String sm = s_m;
     if (sm != null)
     {
-      smtrx = (ScoreMatrix) ((PairwiseDistanceModel) ScoreModels
+      scoreMatrix = (ScoreMatrix) ((PairwiseDistanceModel) ScoreModels
               .getInstance()
               .forName(sm)).getScoreModel();
     }
-    if (smtrx == null)
+    if (scoreMatrix == null)
     {
       // either we were given a non-existent score matrix or a scoremodel that
       // isn't based on a pairwise symbol score matrix
-      smtrx = ScoreModels.getInstance().getDefaultModel(!nucleotides);
+      scoreMatrix = ScoreModels.getInstance().getDefaultModel(!nucleotides);
     }
     details.append("PCA calculation using " + sm
             + " sequence similarity matrix\n========\n\n");
-    while ((ii < s.length) && (s[ii] != null))
-    {
-      bs2[ii] = new BinarySequence(s[ii], nucleotides);
-      if (smtrx != null)
-      {
-        try
-        {
-          bs2[ii].matrixEncode(smtrx);
-        } catch (InvalidSequenceTypeException x)
-        {
-          details.append("Unexpected mismatch of sequence type and score matrix. Calculation will not be valid!\n\n");
-        }
-      }
-      ii++;
-    }
-
-    // System.out.println("Created binary encoding");
-    // printMemory(rt);
-    int count = 0;
-
-    while ((count < bs.length) && (bs[count] != null))
-    {
-      count++;
-    }
-
-    double[][] seqmat = new double[count][bs[0].getDBinary().length];
-    double[][] seqmat2 = new double[count][bs2[0].getDBinary().length];
-    int i = 0;
-
-    while (i < count)
-    {
-      seqmat[i] = bs[i].getDBinary();
-      seqmat2[i] = bs2[i].getDBinary();
-      i++;
-    }
-
-    // System.out.println("Created array");
-    // printMemory(rt);
-    // System.out.println(" --- Original matrix ---- ");
-    m = new Matrix(seqmat);
-    m2 = new Matrix(seqmat2);
-
-  }
-
-  /**
-   * Returns the matrix used in PCA calculation
-   * 
-   * @return java.math.Matrix object
-   */
-
-  public Matrix getM()
-  {
-    return m;
   }
 
   /**
@@ -172,7 +105,7 @@ public class PCA implements Runnable
    */
   public double getEigenvalue(int i)
   {
-    return eigenvector.d[i];
+    return eigenvector.getD()[i];
   }
 
   /**
@@ -191,9 +124,9 @@ public class PCA implements Runnable
    */
   public float[][] getComponents(int l, int n, int mm, float factor)
   {
-    float[][] out = new float[m.rows][3];
+    float[][] out = new float[getHeight()][3];
 
-    for (int i = 0; i < m.rows; i++)
+    for (int i = 0; i < getHeight(); i++)
     {
       out[i][0] = (float) component(i, l) * factor;
       out[i][1] = (float) component(i, n) * factor;
@@ -214,9 +147,9 @@ public class PCA implements Runnable
   public double[] component(int n)
   {
     // n = index of eigenvector
-    double[] out = new double[m.rows];
+    double[] out = new double[getHeight()];
 
-    for (int i = 0; i < m.rows; i++)
+    for (int i = 0; i < out.length; i++)
     {
       out[i] = component(i, n);
     }
@@ -238,12 +171,12 @@ public class PCA implements Runnable
   {
     double out = 0.0;
 
-    for (int i = 0; i < symm.cols; i++)
+    for (int i = 0; i < symm.width(); i++)
     {
-      out += (symm.value[row][i] * eigenvector.value[i][n]);
+      out += (symm.getValue(row, i) * eigenvector.getValue(i, n));
     }
 
-    return out / eigenvector.d[n];
+    return out / eigenvector.getD()[n];
   }
 
   public String getDetails()
@@ -272,25 +205,17 @@ public class PCA implements Runnable
       }
     };
 
+    // long now = System.currentTimeMillis();
     try
     {
       details.append("PCA Calculation Mode is "
               + (jvCalcMode ? "Jalview variant" : "Original SeqSpace")
               + "\n");
-      Matrix mt = m.transpose();
 
-      details.append(" --- OrigT * Orig ---- \n");
-      if (!jvCalcMode)
-      {
-        eigenvector = mt.preMultiply(m); // standard seqspace comparison matrix
-      }
-      else
-      {
-        eigenvector = mt.preMultiply(m2); // jalview variation on seqsmace
-                                          // method
-      }
+      eigenvector = scoreMatrix.computePairwiseScores(seqs);
 
-      eigenvector.print(ps);
+      details.append(" --- OrigT * Orig ---- \n");
+      eigenvector.print(ps, "%8.2f");
 
       symm = eigenvector.copy();
 
@@ -298,10 +223,10 @@ public class PCA implements Runnable
 
       details.append(" ---Tridiag transform matrix ---\n");
       details.append(" --- D vector ---\n");
-      eigenvector.printD(ps);
+      eigenvector.printD(ps, "%15.4e");
       ps.println();
       details.append("--- E vector ---\n");
-      eigenvector.printE(ps);
+      eigenvector.printE(ps, "%15.4e");
       ps.println();
 
       // Now produce the diagonalization matrix
@@ -315,9 +240,9 @@ public class PCA implements Runnable
     }
 
     details.append(" --- New diagonalization matrix ---\n");
-    eigenvector.print(ps);
+    eigenvector.print(ps, "%8.2f");
     details.append(" --- Eigenvalues ---\n");
-    eigenvector.printD(ps);
+    eigenvector.printD(ps, "%15.4e");
     ps.println();
     /*
      * for (int seq=0;seq<symm.rows;seq++) { ps.print("\"Seq"+seq+"\""); for
@@ -325,12 +250,24 @@ public class PCA implements Runnable
      * 
      * ps.print(","+component(seq, ev)); } ps.println(); }
      */
+    // System.out.println(("PCA.run() took "
+    // + (System.currentTimeMillis() - now) + "ms"));
   }
 
-  boolean jvCalcMode = true;
-
   public void setJvCalcMode(boolean calcMode)
   {
     this.jvCalcMode = calcMode;
   }
+
+  /**
+   * Answers the N dimensions of the NxN PCA matrix. This is the number of
+   * sequences involved in the pairwise score calculation.
+   * 
+   * @return
+   */
+  public int getHeight()
+  {
+    // TODO can any of seqs[] be null?
+    return seqs.length;
+  }
 }