Parsing moved to (new) ScoreMatrixFile, drag and drop to alignment now
[jalview.git] / src / jalview / analysis / scoremodels / ScoreMatrix.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.scoremodels;
22
23 import jalview.api.analysis.ScoreModelI;
24
25 import java.util.Arrays;
26
27 public class ScoreMatrix extends PairwiseSeqScoreModel implements
28         ScoreModelI
29 {
30   public static final short UNMAPPED = (short) -1;
31
32   private static final String BAD_ASCII_ERROR = "Unexpected character %s in getPairwiseScore";
33
34   private static final int MAX_ASCII = 127;
35
36   /*
37    * the name of the model as shown in menus
38    */
39   private String name;
40
41   /*
42    * the characters that the model provides scores for
43    */
44   private char[] symbols;
45
46   /*
47    * the score matrix; both dimensions must equal the number of symbols
48    * matrix[i][j] is the substitution score for replacing symbols[i] with symbols[j]
49    */
50   private float[][] matrix;
51
52   /*
53    * quick lookup to convert from an ascii character value to the index
54    * of the corresponding symbol in the score matrix 
55    */
56   private short[] symbolIndex;
57
58   /*
59    * true for Protein Score matrix, false for dna score matrix
60    */
61   private boolean peptide;
62
63   /**
64    * Constructor
65    * 
66    * @param name
67    *          Unique, human readable name for the matrix
68    * @param alphabet
69    *          the symbols to which scores apply
70    * @param matrix
71    *          Pairwise scores indexed according to the symbol alphabet
72    */
73   public ScoreMatrix(String name, char[] alphabet, float[][] matrix)
74   {
75     this.matrix = matrix;
76     this.name = name;
77     this.symbols = alphabet;
78
79     symbolIndex = buildSymbolIndex(alphabet);
80
81     /*
82      * crude heuristic for now...
83      */
84     peptide = alphabet.length >= 20;
85   }
86
87   /**
88    * Returns an array A where A[i] is the position in the alphabet array of the
89    * character whose value is i. For example if the alphabet is { 'A', 'D', 'X'
90    * } then A['D'] = A[68] = 1.
91    * <p>
92    * Unmapped characters (not in the alphabet) get an index of -1.
93    * <p>
94    * Mappings are added automatically for lower case symbols (for non case
95    * sensitive scoring), unless they are explicitly present in the alphabet (are
96    * scored separately in the score matrix).
97    * 
98    * @param alphabet
99    * @return
100    */
101   static short[] buildSymbolIndex(char[] alphabet)
102   {
103     short[] index = new short[MAX_ASCII + 1];
104     Arrays.fill(index, UNMAPPED);
105     short pos = 0;
106     for (char c : alphabet)
107     {
108       if (c <= MAX_ASCII)
109       {
110         index[c] = pos;
111       }
112
113       /*
114        * also map lower-case character (unless separately mapped)
115        */
116       if (c >= 'A' && c <= 'Z')
117       {
118         short lowerCase = (short) (c + ('a' - 'A'));
119         if (index[lowerCase] == UNMAPPED)
120         {
121           index[lowerCase] = pos;
122         }
123       }
124       pos++;
125     }
126     return index;
127   }
128
129   @Override
130   public String getName()
131   {
132     return name;
133   }
134
135   @Override
136   public boolean isDNA()
137   {
138     return !peptide;
139   }
140
141   @Override
142   public boolean isProtein()
143   {
144     return peptide;
145   }
146
147   @Override
148   public float[][] getMatrix()
149   {
150     return matrix;
151   }
152
153   /**
154    * Returns the pairwise score for substituting c with d, or zero if c or d is
155    * an unscored or unexpected character
156    */
157   @Override
158   public float getPairwiseScore(char c, char d)
159   {
160     if (c > MAX_ASCII)
161     {
162       System.err.println(String.format(BAD_ASCII_ERROR, c));
163       return 0;
164     }
165     if (d > MAX_ASCII)
166     {
167       System.err.println(String.format(BAD_ASCII_ERROR, d));
168       return 0;
169     }
170
171     int cIndex = symbolIndex[c];
172     int dIndex = symbolIndex[d];
173     if (cIndex != UNMAPPED && dIndex != UNMAPPED)
174     {
175       return matrix[cIndex][dIndex];
176     }
177     return 0;
178   }
179
180   /**
181    * pretty print the matrix
182    */
183   @Override
184   public String toString()
185   {
186     return outputMatrix(false);
187   }
188
189   /**
190    * Print the score matrix, optionally formatted as html, with the alphabet symbols as column headings and at the start of each row
191    * @param html
192    * @return
193    */
194   public String outputMatrix(boolean html)
195   {
196     StringBuilder sb = new StringBuilder(512);
197
198     /*
199      * heading row with alphabet
200      */
201     if (html)
202     {
203       sb.append("<table border=\"1\">");
204       sb.append(html ? "<tr><th></th>" : "");
205     }
206     for (char sym : symbols)
207     {
208       if (html)
209       {
210         sb.append("<th>&nbsp;").append(sym).append("&nbsp;</th>");
211       }
212       else
213       {
214         sb.append("\t").append(sym);
215       }
216     }
217     sb.append(html ? "</tr>\n" : "\n");
218
219     /*
220      * table of scores
221      */
222     for (char c1 : symbols)
223     {
224       if (html)
225       {
226         sb.append("<tr><td>");
227       }
228       sb.append(c1).append(html ? "</td>" : "");
229       for (char c2 : symbols)
230       {
231         sb.append(html ? "<td>" : "\t")
232                 .append(matrix[symbolIndex[c1]][symbolIndex[c2]])
233                 .append(html ? "</td>" : "");
234       }
235       sb.append(html ? "</tr>\n" : "\n");
236     }
237     if (html)
238     {
239       sb.append("</table>");
240     }
241     return sb.toString();
242   }
243 }