31fce1581c7b491787b381c1f18b9b1d59798d54
[jalview.git] / src / jalview / viewmodel / PCAModel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.viewmodel;
19
20 import java.util.Vector;
21
22 import jalview.analysis.PCA;
23 import jalview.datamodel.AlignmentView;
24 import jalview.datamodel.SequenceI;
25 import jalview.datamodel.SequencePoint;
26 import jalview.api.RotatableCanvasI;
27
28 public class PCAModel
29 {
30
31   public PCAModel(AlignmentView seqstrings2, SequenceI[] seqs2,
32           boolean nucleotide2)
33   {
34     seqstrings = seqstrings2;
35     seqs = seqs2;
36     nucleotide = nucleotide2;
37   }
38
39   private volatile PCA pca;
40
41   int top;
42
43   AlignmentView seqstrings;
44
45   SequenceI[] seqs;
46
47   /**
48    * use the identity matrix for calculating similarity between sequences.
49    */
50   private boolean nucleotide = false;
51
52   private Vector<SequencePoint> points;
53
54   private boolean jvCalcMode = true;
55
56   public boolean isJvCalcMode()
57   {
58     return jvCalcMode;
59   }
60
61   public void run()
62   {
63
64     pca = new PCA(seqstrings.getSequenceStrings(' '), nucleotide);
65     pca.setJvCalcMode(jvCalcMode);
66     pca.run();
67
68     // Now find the component coordinates
69     int ii = 0;
70
71     while ((ii < seqs.length) && (seqs[ii] != null))
72     {
73       ii++;
74     }
75
76     double[][] comps = new double[ii][ii];
77
78     for (int i = 0; i < ii; i++)
79     {
80       if (pca.getEigenvalue(i) > 1e-4)
81       {
82         comps[i] = pca.component(i);
83       }
84     }
85
86     top = pca.getM().rows - 1;
87
88     points = new Vector<SequencePoint>();
89     float[][] scores = pca.getComponents(top - 1, top - 2, top - 3, 100);
90
91     for (int i = 0; i < pca.getM().rows; i++)
92     {
93       SequencePoint sp = new SequencePoint(seqs[i], scores[i]);
94       points.addElement(sp);
95     }
96
97   }
98
99   public void updateRc(RotatableCanvasI rc)
100   {
101     rc.setPoints(points, pca.getM().rows);
102   }
103
104   public boolean isNucleotide()
105   {
106     return nucleotide;
107   }
108
109   public void setNucleotide(boolean nucleotide)
110   {
111     this.nucleotide = nucleotide;
112   }
113
114   /**
115    * 
116    * 
117    * @return index of principle dimension of PCA
118    */
119   public int getTop()
120   {
121     return top;
122   }
123
124   /**
125    * update the 2d coordinates for the list of points to the given dimensions
126    * Principal dimension is getTop(). Next greatest eigenvector is getTop()-1.
127    * Note - pca.getComponents starts counting the spectrum from rank-2 to zero,
128    * rather than rank-1, so getComponents(dimN ...) == updateRcView(dimN+1 ..)
129    * 
130    * @param dim1
131    * @param dim2
132    * @param dim3
133    */
134   public void updateRcView(int dim1, int dim2, int dim3)
135   {
136     // note: actual indices for components are dim1-1, etc (patch for JAL-1123)
137     float[][] scores = pca.getComponents(dim1 - 1, dim2 - 1, dim3 - 1, 100);
138
139     for (int i = 0; i < pca.getM().rows; i++)
140     {
141       ((SequencePoint) points.elementAt(i)).coord = scores[i];
142     }
143   }
144
145   public String getDetails()
146   {
147     return pca.getDetails();
148   }
149
150   public AlignmentView getSeqtrings()
151   {
152     return seqstrings;
153   }
154
155   public String getPointsasCsv(boolean transformed, int xdim, int ydim,
156           int zdim)
157   {
158     StringBuffer csv = new StringBuffer();
159     csv.append("\"Sequence\"");
160     if (transformed)
161     {
162       csv.append(",");
163       csv.append(xdim);
164       csv.append(",");
165       csv.append(ydim);
166       csv.append(",");
167       csv.append(zdim);
168     }
169     else
170     {
171       for (int d = 1, dmax = pca.component(1).length; d <= dmax; d++)
172       {
173         csv.append("," + d);
174       }
175     }
176     csv.append("\n");
177     for (int s = 0; s < seqs.length; s++)
178     {
179       csv.append("\"" + seqs[s].getName() + "\"");
180       double fl[];
181       if (!transformed)
182       {
183         // output pca in correct order
184         fl = pca.component(s);
185         for (int d = fl.length - 1; d >= 0; d--)
186         {
187           csv.append(",");
188           csv.append(fl[d]);
189         }
190       }
191       else
192       {
193         // output current x,y,z coords for points
194         fl = getPointPosition(s);
195         for (int d = 0; d < fl.length; d++)
196         {
197           csv.append(",");
198           csv.append(fl[d]);
199         }
200       }
201       csv.append("\n");
202     }
203     return csv.toString();
204   }
205
206   /**
207    * 
208    * @return x,y,z positions of point s (index into points) under current
209    *         transform.
210    */
211   public double[] getPointPosition(int s)
212   {
213     double pts[] = new double[3];
214     float[] p = points.elementAt(s).coord;
215     pts[0] = p[0];
216     pts[1] = p[1];
217     pts[2] = p[2];
218     return pts;
219   }
220
221   public void setJvCalcMode(boolean state)
222   {
223     jvCalcMode = state;
224   }
225
226 }