JAL-3949 - refactor logging from jalview.bin.Cache to jalview.bin.Console
[jalview.git] / src / jalview / analysis / PCA.java
index c11610a..4a3cfec 100755 (executable)
-/*\r
- * Jalview - A Sequence Alignment Editor and Viewer\r
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
- *\r
- * This program is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License\r
- * as published by the Free Software Foundation; either version 2\r
- * of the License, or (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
- */\r
-package jalview.analysis;\r
-\r
-import java.io.*;\r
-\r
-import jalview.datamodel.*;\r
-import jalview.math.*;\r
-\r
-/**\r
- * Performs Principal Component Analysis on given sequences\r
- *\r
- * @author $author$\r
- * @version $Revision$\r
- */\r
-public class PCA\r
-    implements Runnable\r
-{\r
-  Matrix m;\r
-  Matrix symm;\r
-  Matrix m2;\r
-  double[] eigenvalue;\r
-  Matrix eigenvector;\r
-  StringBuffer details = new StringBuffer();\r
-\r
-  /**\r
-   * Creates a new PCA object.\r
-   *\r
-   * @param s Set of sequences to perform PCA on\r
-   */\r
-  public PCA(String[] s)\r
-  {\r
-\r
-    BinarySequence[] bs = new BinarySequence[s.length];\r
-    int ii = 0;\r
-\r
-    while ( (ii < s.length) && (s[ii] != null))\r
-    {\r
-      bs[ii] = new BinarySequence(s[ii]);\r
-      bs[ii].encode();\r
-      ii++;\r
-    }\r
-\r
-    BinarySequence[] bs2 = new BinarySequence[s.length];\r
-    ii = 0;\r
-\r
-    while ( (ii < s.length) && (s[ii] != null))\r
-    {\r
-      bs2[ii] = new BinarySequence(s[ii]);\r
-      bs2[ii].blosumEncode();\r
-      ii++;\r
-    }\r
-\r
-    //System.out.println("Created binary encoding");\r
-    //printMemory(rt);\r
-    int count = 0;\r
-\r
-    while ( (count < bs.length) && (bs[count] != null))\r
-    {\r
-      count++;\r
-    }\r
-\r
-    double[][] seqmat = new double[count][bs[0].getDBinary().length];\r
-    double[][] seqmat2 = new double[count][bs2[0].getDBinary().length];\r
-    int i = 0;\r
-\r
-    while (i < count)\r
-    {\r
-      seqmat[i] = bs[i].getDBinary();\r
-      seqmat2[i] = bs2[i].getDBinary();\r
-      i++;\r
-    }\r
-\r
-    //System.out.println("Created array");\r
-    //printMemory(rt);\r
-    //    System.out.println(" --- Original matrix ---- ");\r
-    m = new Matrix(seqmat, count, bs[0].getDBinary().length);\r
-    m2 = new Matrix(seqmat2, count, bs2[0].getDBinary().length);\r
-\r
-  }\r
-\r
-  /**\r
-   * Returns the matrix used in PCA calculation\r
-   *\r
-   * @return java.math.Matrix object\r
-   */\r
-\r
-  public Matrix getM()\r
-  {\r
-    return m;\r
-  }\r
-\r
-  /**\r
-   * Returns Eigenvalue\r
-   *\r
-   * @param i Index of diagonal within matrix\r
-   *\r
-   * @return Returns value of diagonal from matrix\r
-   */\r
-  public double getEigenvalue(int i)\r
-  {\r
-    return eigenvector.d[i];\r
-  }\r
-\r
-  /**\r
-   * DOCUMENT ME!\r
-   *\r
-   * @param l DOCUMENT ME!\r
-   * @param n DOCUMENT ME!\r
-   * @param mm DOCUMENT ME!\r
-   * @param factor DOCUMENT ME!\r
-   *\r
-   * @return DOCUMENT ME!\r
-   */\r
-  public float[][] getComponents(int l, int n, int mm, float factor)\r
-  {\r
-    float[][] out = new float[m.rows][3];\r
-\r
-    for (int i = 0; i < m.rows; i++)\r
-    {\r
-      out[i][0] = (float) component(i, l) * factor;\r
-      out[i][1] = (float) component(i, n) * factor;\r
-      out[i][2] = (float) component(i, mm) * factor;\r
-    }\r
-\r
-    return out;\r
-  }\r
-\r
-  /**\r
-   * DOCUMENT ME!\r
-   *\r
-   * @param n DOCUMENT ME!\r
-   *\r
-   * @return DOCUMENT ME!\r
-   */\r
-  public double[] component(int n)\r
-  {\r
-    // n = index of eigenvector\r
-    double[] out = new double[m.rows];\r
-\r
-    for (int i = 0; i < m.rows; i++)\r
-    {\r
-      out[i] = component(i, n);\r
-    }\r
-\r
-    return out;\r
-  }\r
-\r
-  /**\r
-   * DOCUMENT ME!\r
-   *\r
-   * @param row DOCUMENT ME!\r
-   * @param n DOCUMENT ME!\r
-   *\r
-   * @return DOCUMENT ME!\r
-   */\r
-  double component(int row, int n)\r
-  {\r
-    double out = 0.0;\r
-\r
-    for (int i = 0; i < symm.cols; i++)\r
-    {\r
-      out += (symm.value[row][i] * eigenvector.value[i][n]);\r
-    }\r
-\r
-    return out / eigenvector.d[n];\r
-  }\r
-\r
-  public String getDetails()\r
-  {\r
-    return details.toString();\r
-  }\r
-\r
-  /**\r
-   * DOCUMENT ME!\r
-   */\r
-  public void run()\r
-  {\r
-    Matrix mt = m.transpose();\r
-\r
-    details.append(" --- OrigT * Orig ---- \n");\r
-    eigenvector = mt.preMultiply(m2);\r
-\r
-    PrintStream ps = new PrintStream(System.out)\r
-    {\r
-      public void print(String x)\r
-      {\r
-        details.append(x);\r
-      }\r
-\r
-      public void println()\r
-      {\r
-        details.append("\n");\r
-      }\r
-    };\r
-\r
-    eigenvector.print(ps);\r
-\r
-    symm = eigenvector.copy();\r
-\r
-    eigenvector.tred();\r
-\r
-    details.append(" ---Tridiag transform matrix ---\n");\r
-    details.append(" --- D vector ---\n");\r
-    eigenvector.printD(ps);\r
-    ps.println();\r
-    details.append("--- E vector ---\n");\r
-    eigenvector.printE(ps);\r
-    ps.println();\r
-\r
-    // Now produce the diagonalization matrix\r
-    eigenvector.tqli();\r
-\r
-    details.append(" --- New diagonalization matrix ---\n");\r
-    details.append(" --- Eigenvalues ---\n");\r
-    eigenvector.printD(ps);\r
-    ps.println();\r
-    //  taps.println();\r
-    //  taps.println("Transformed sequences = ");\r
-    // Matrix trans =  m.preMultiply(eigenvector);\r
-    //  trans.print(System.out);\r
-  }\r
-}\r
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.analysis;
+
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.SimilarityParamsI;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.Point;
+import jalview.math.MatrixI;
+
+import java.io.PrintStream;
+
+/**
+ * Performs Principal Component Analysis on given sequences
+ */
+public class PCA implements Runnable
+{
+  /*
+   * inputs
+   */
+  final private AlignmentView seqs;
+
+  final private ScoreModelI scoreModel;
+
+  final private SimilarityParamsI similarityParams;
+
+  /*
+   * outputs
+   */
+  private MatrixI pairwiseScores;
+
+  private MatrixI tridiagonal;
+
+  private MatrixI eigenMatrix;
+
+  /**
+   * Constructor given the sequences to compute for, the similarity model to
+   * use, and a set of parameters for sequence comparison
+   * 
+   * @param sequences
+   * @param sm
+   * @param options
+   */
+  public PCA(AlignmentView sequences, ScoreModelI sm, SimilarityParamsI options)
+  {
+    this.seqs = sequences;
+    this.scoreModel = sm;
+    this.similarityParams = options;
+  }
+
+  /**
+   * Returns Eigenvalue
+   * 
+   * @param i
+   *          Index of diagonal within matrix
+   * 
+   * @return Returns value of diagonal from matrix
+   */
+  public double getEigenvalue(int i)
+  {
+    return eigenMatrix.getD()[i];
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param l
+   *          DOCUMENT ME!
+   * @param n
+   *          DOCUMENT ME!
+   * @param mm
+   *          DOCUMENT ME!
+   * @param factor
+   *          DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  public Point[] getComponents(int l, int n, int mm, float factor)
+  {
+    Point[] out = new Point[getHeight()];
+
+    for (int i = 0; i < getHeight(); i++)
+    {
+      float x = (float) component(i, l) * factor;
+      float y = (float) component(i, n) * factor;
+      float z = (float) component(i, mm) * factor;
+      out[i] = new Point(x, y, z);
+    }
+
+    return out;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param n
+   *          DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  public double[] component(int n)
+  {
+    // n = index of eigenvector
+    double[] out = new double[getHeight()];
+
+    for (int i = 0; i < out.length; i++)
+    {
+      out[i] = component(i, n);
+    }
+
+    return out;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param row
+   *          DOCUMENT ME!
+   * @param n
+   *          DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   */
+  double component(int row, int n)
+  {
+    double out = 0.0;
+
+    for (int i = 0; i < pairwiseScores.width(); i++)
+    {
+      out += (pairwiseScores.getValue(row, i) * eigenMatrix.getValue(i, n));
+    }
+
+    return out / eigenMatrix.getD()[n];
+  }
+
+  /**
+   * Answers a formatted text report of the PCA calculation results (matrices
+   * and eigenvalues) suitable for display
+   * 
+   * @return
+   */
+  public String getDetails()
+  {
+    StringBuilder sb = new StringBuilder(1024);
+    sb.append("PCA calculation using ").append(scoreModel.getName())
+            .append(" sequence similarity matrix\n========\n\n");
+    PrintStream ps = wrapOutputBuffer(sb);
+    
+    /*
+     * pairwise similarity scores
+     */
+    sb.append(" --- OrigT * Orig ---- \n");
+    pairwiseScores.print(ps, "%8.2f");
+    
+    /*
+     * tridiagonal matrix, with D and E vectors
+     */
+    sb.append(" ---Tridiag transform matrix ---\n");
+    sb.append(" --- D vector ---\n");
+    tridiagonal.printD(ps, "%15.4e");
+    ps.println();
+    sb.append("--- E vector ---\n");
+    tridiagonal.printE(ps, "%15.4e");
+    ps.println();
+    
+    /*
+     * eigenvalues matrix, with D vector
+     */
+    sb.append(" --- New diagonalization matrix ---\n");
+    eigenMatrix.print(ps, "%8.2f");
+    sb.append(" --- Eigenvalues ---\n");
+    eigenMatrix.printD(ps, "%15.4e");
+    ps.println();
+    
+    return sb.toString();
+  }
+
+  /**
+   * Performs the PCA calculation
+   */
+  @Override
+  public void run()
+  {
+    try
+    {
+      /*
+       * sequence pairwise similarity scores
+       */
+      pairwiseScores = scoreModel.findSimilarities(seqs, similarityParams);
+
+      /*
+       * tridiagonal matrix
+       */
+      tridiagonal = pairwiseScores.copy();
+      tridiagonal.tred();
+
+      /*
+       * the diagonalization matrix
+       */
+      eigenMatrix = tridiagonal.copy();
+      eigenMatrix.tqli();
+    } catch (Exception q)
+    {
+      Console.error("Error computing PCA:  " + q.getMessage());
+      q.printStackTrace();
+    }
+  }
+
+  /**
+   * Returns a PrintStream that wraps (appends its output to) the given
+   * StringBuilder
+   * 
+   * @param sb
+   * @return
+   */
+  protected PrintStream wrapOutputBuffer(StringBuilder sb)
+  {
+    PrintStream ps = new PrintStream(System.out)
+    {
+      @Override
+      public void print(String x)
+      {
+        sb.append(x);
+      }
+
+      @Override
+      public void println()
+      {
+        sb.append("\n");
+      }
+    };
+    return ps;
+  }
+
+  /**
+   * 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 pairwiseScores.height();// seqs.getSequences().length;
+  }
+
+  /**
+   * Answers the sequence pairwise similarity scores which were the first step
+   * of the PCA calculation
+   * 
+   * @return
+   */
+  public MatrixI getPairwiseScores()
+  {
+    return pairwiseScores;
+  }
+
+  public void setPairwiseScores(MatrixI m)
+  {
+    pairwiseScores = m;
+  }
+
+  public MatrixI getEigenmatrix()
+  {
+    return eigenMatrix;
+  }
+
+  public void setEigenmatrix(MatrixI m)
+  {
+    eigenMatrix = m;
+  }
+
+  public MatrixI getTridiagonal()
+  {
+    return tridiagonal;
+  }
+
+  public void setTridiagonal(MatrixI tridiagonal)
+  {
+    this.tridiagonal = tridiagonal;
+  }
+}