a67f901195dd8d8554937051937817cc11fb8800
[jalview.git] / src / jalview / analysis / PaSiMap.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.analysis;
22
23 import jalview.api.analysis.ScoreModelI;
24 import jalview.api.analysis.SimilarityParamsI;
25 import jalview.bin.Console;
26 import jalview.datamodel.AlignmentView;
27 import jalview.datamodel.Point;
28 import jalview.datamodel.SequenceI;
29 import jalview.gui.PairwiseAlignPanel;
30 import jalview.gui.PaSiMapPanel;
31 import jalview.math.Matrix;
32 import jalview.math.MatrixI;
33 import jalview.viewmodel.AlignmentViewport;
34
35
36 import java.io.PrintStream;
37 import java.util.Hashtable;
38 import java.util.Enumeration;
39
40 /**
41  * Performs Principal Component Analysis on given sequences
42  * @AUTHOR MorellThomas 
43  */
44 public class PaSiMap implements Runnable
45 {
46   /*
47    * inputs
48    */
49   final private AlignmentViewport seqs;
50
51   final private ScoreModelI scoreModel;
52
53   final private SimilarityParamsI similarityParams;
54
55   final private byte dim = 3;
56
57   /*
58    * outputs
59    */
60   private PairwiseAlignPanel alignment;
61
62   private MatrixI pairwiseScores;
63
64   private MatrixI eigenMatrix;
65
66   /**
67    * Constructor given the sequences to compute for, the similarity model to
68    * use, and a set of parameters for sequence comparison
69    * 
70    * @param sequences
71    * @param sm
72    * @param options
73    */
74   public PaSiMap(AlignmentViewport sequences, ScoreModelI sm,
75           SimilarityParamsI options)
76   {
77     this.seqs = sequences;
78     this.scoreModel = sm;
79     this.similarityParams = options;
80   }
81
82   /**
83    * Returns Eigenvalue
84    * 
85    * @param i
86    *          Index of diagonal within matrix
87    * 
88    * @return Returns value of diagonal from matrix
89    */
90   public double getEigenvalue(int i)
91   {
92     return eigenMatrix.getD()[i];
93   }
94
95   /**
96    * Returns coordinates for each datapoint
97    * 
98    * @param l
99    *          DOCUMENT ME!
100    * @param n
101    *          DOCUMENT ME!
102    * @param mm
103    *          DOCUMENT ME!
104    * @param factor ~ is 1
105    * 
106    * @return DOCUMENT ME!
107    */
108   public Point[] getComponents(int l, int n, int mm, float factor)
109   {
110     Point[] out = new Point[getHeight()];
111
112     for (int i = 0; i < out.length; i++)
113     {
114       float x = (float) component(i, l) * factor;
115       float y = (float) component(i, n) * factor;
116       float z = (float) component(i, mm) * factor;
117       out[i] = new Point(x, y, z);
118     }
119
120     return out;
121   }
122
123   /**
124    * DOCUMENT ME!
125    * 
126    * @param n
127    *          DOCUMENT ME!
128    * 
129    * @return DOCUMENT ME!
130    */
131   public double[] component(int n)
132   {
133     // n = index of eigenvector
134     double[] out = new double[getWidth()];
135
136     for (int i = 0; i < out.length; i++)
137     {
138       out[i] = component(i, n);
139     }
140
141     return out;
142   }
143
144   /**
145    * DOCUMENT ME!
146    * 
147    * @param row
148    *          DOCUMENT ME!
149    * @param n
150    *          DOCUMENT ME!
151    * 
152    * @return DOCUMENT ME!
153    */
154   double component(int row, int n)
155   {
156     return eigenMatrix.getValue(row, n);
157   }
158
159   /**
160    * Answers a formatted text report of the PaSiMap calculation results (matrices
161    * and eigenvalues) suitable for display
162    * 
163    * @return
164    */
165   public String getDetails()
166   {
167     StringBuilder sb = new StringBuilder(1024);
168     sb.append("PaSiMap calculation using ").append(scoreModel.getName())
169             .append(" sequence similarity matrix\n========\n\n");
170     PrintStream ps = wrapOutputBuffer(sb);
171
172     /*
173      * coordinates matrix, with D vector
174      */
175     sb.append(" --- Pairwise correlation coefficients ---\n");
176     pairwiseScores.print(ps, "%8.6f ");
177     ps.println();
178
179     sb.append(" --- Eigenvalues ---\n");
180     eigenMatrix.printD(ps, "%15.4e");
181     ps.println();
182
183     sb.append(" --- Coordinates ---\n");
184     eigenMatrix.print(ps, "%8.6f ");
185     ps.println();
186
187     return sb.toString();
188   }
189
190   /**
191    * Performs the PaSiMap calculation
192    *
193    * creates a new gui/PairwiseAlignPanel with the input sequences (AlignmentViewport)
194    * uses analysis/AlignSeq to creatue the pairwise alignments and calculate the AlignmentScores (float for each pair)
195    * gets all float[][] scores from the gui/PairwiseAlignPanel
196    * checks the connections for each sequence with AlignmentViewport seqs.calculateConnectivity(float[][] scores, int dim) (from analysis/Connectivity) -- throws an Exception if insufficient
197    * creates a math/MatrixI pairwiseScores of the float[][] scores
198    * copys the scores and fills the diagonal to create a symmetric matrix using math/Matrix.fillDiagonal()
199    * performs the analysis/ccAnalysis with the symmetric matrix
200    * gets the eigenmatrix and the eigenvalues using math/Matrix.tqli()
201    */
202   @Override
203   public void run()
204   {
205     try
206     {
207       alignment = new PairwiseAlignPanel(seqs, true, 100, 5);
208       float[][] scores = alignment.getAlignmentScores();        //bigger index first -- eg scores[14][13]
209
210       Hashtable<SequenceI, Integer> connectivity = seqs.calculateConnectivity(scores, dim);
211
212       pairwiseScores = new Matrix(scores);
213       pairwiseScores.fillDiagonal();
214
215       eigenMatrix = pairwiseScores.copy();
216
217       ccAnalysis cc = new ccAnalysis(pairwiseScores, dim);
218       eigenMatrix = cc.run();
219
220     } catch (Exception q)
221     {
222       Console.error("Error computing PaSiMap:  " + q.getMessage());
223       q.printStackTrace();
224     }
225   }
226
227   /**
228    * Returns a PrintStream that wraps (appends its output to) the given
229    * StringBuilder
230    * 
231    * @param sb
232    * @return
233    */
234   protected PrintStream wrapOutputBuffer(StringBuilder sb)
235   {
236     PrintStream ps = new PrintStream(System.out)
237     {
238       @Override
239       public void print(String x)
240       {
241         sb.append(x);
242       }
243
244       @Override
245       public void println()
246       {
247         sb.append("\n");
248       }
249     };
250     return ps;
251   }
252
253   /**
254    * Answers the N dimensions of the NxM PaSiMap matrix. This is the number of
255    * sequences involved in the pairwise score calculation.
256    * 
257    * @return
258    */
259   public int getHeight()
260   {
261     // TODO can any of seqs[] be null?
262     return eigenMatrix.height();// seqs.getSequences().length;
263   }
264
265   /**
266    * Answers the M dimensions of the NxM PaSiMap matrix. This is the number of
267    * sequences involved in the pairwise score calculation.
268    * 
269    * @return
270    */
271   public int getWidth()
272   {
273     // TODO can any of seqs[] be null?
274     return eigenMatrix.width();// seqs.getSequences().length;
275   }
276
277   /**
278    * Answers the sequence pairwise similarity scores which were the first step
279    * of the PaSiMap calculation
280    * 
281    * @return
282    */
283   public MatrixI getPairwiseScores()
284   {
285     return pairwiseScores;
286   }
287
288   public void setPairwiseScores(MatrixI m)
289   {
290     pairwiseScores = m;
291   }
292
293   public MatrixI getEigenmatrix()
294   {
295     return eigenMatrix;
296   }
297
298   public void setEigenmatrix(MatrixI m)
299   {
300     eigenMatrix = m;
301   }
302
303   public PairwiseAlignPanel getAlignments()
304   {
305     return alignment;
306   }
307
308   public String getAlignmentOutput()
309   {
310     return alignment.getAlignmentOutput();
311   }
312 }