JAL-2599 modified state transition probability storage
[jalview.git] / src / jalview / io / HMMFile.java
1 package jalview.io;
2
3 import jalview.datamodel.HMMNode;
4 import jalview.datamodel.HiddenMarkovModel;
5 import jalview.datamodel.SequenceI;
6
7 import java.io.BufferedReader;
8 import java.io.FileNotFoundException;
9 import java.io.IOException;
10 import java.io.PrintWriter;
11 import java.io.UnsupportedEncodingException;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Scanner;
15
16
17 /**
18  * reads in and writes out a HMMER standard file
19  * 
20  * 
21  * @author TZVanaalten
22  *
23  */
24 public class HMMFile extends AlignFile
25         implements AlignmentFileReaderI, AlignmentFileWriterI
26 {
27   // HMM to store file data
28   private HiddenMarkovModel hmm = new HiddenMarkovModel();
29
30
31
32
33   // number of possible transitions
34   private final int NUMBER_OF_TRANSITIONS = 7;
35
36   private final String NEW_LINE = "\n";
37
38
39   // file header
40   String fileHeader;
41
42   int numberOfSymbols;
43
44   private final String SPACE = " ";
45
46   private final String COMPO = "COMPO";
47
48   private final String EMPTY = "";
49
50   private static final String TRANSITIONTYPELINE = "m->m     m->i     m->d     i->m     i->i     d->m     d->d";
51
52   public HMMFile(FileParse source) throws IOException
53   {
54     super(false, source);
55   }
56
57   public HMMFile()
58   {
59
60   }
61
62   public HiddenMarkovModel getHMM()
63   {
64     return hmm;
65   }
66
67   public void setHMM(HiddenMarkovModel model)
68   {
69     this.hmm = model;
70   }
71
72   public String getName()
73   {
74     return hmm.getName();
75   }
76
77   /**
78    * reads data from HMM file
79    * 
80    * @throws IOException
81    */
82   @Override
83   public void parse() throws IOException
84   {
85     parseFileProperties(dataIn);
86     parseModel(dataIn);
87   }
88
89
90
91   /**
92    * imports file properties from hmm file
93    * 
94    * @param input
95    *          buffered reader used to read in file
96    * @throws IOException
97    */
98   void parseFileProperties(BufferedReader input) throws IOException
99   {
100     boolean readingFile = true;
101     fileHeader = input.readLine();
102     String line = input.readLine();
103     while (readingFile)
104     {
105       if (line != null)
106       {
107         Scanner parser = new Scanner(line);
108         String next = parser.next();
109         if ("HMM".equals(next)) // indicates start of HMM data (end of file
110                               // properties)
111         {
112           readingFile = false;
113           hmm.fillSymbols(parser);
114           numberOfSymbols = hmm.getNumberOfSymbols();
115         }
116         else if ("STATS".equals(next))
117         {
118           parser.next();
119           String key;
120           String value;
121           key = parser.next();
122           value = parser.next() + SPACE + SPACE + parser.next();
123           hmm.addFileProperty(key, value);
124         }
125         else
126         {
127           String key = next;
128           String value = parser.next();
129           while (parser.hasNext())
130           {
131             value = value + SPACE + parser.next();
132           }
133           hmm.addFileProperty(key, value);
134         }
135         parser.close();
136       }
137       line = input.readLine();
138       if (line == null)
139       {
140         readingFile = false;
141       }
142     }
143
144   }
145
146   /**
147    * parses the model data from the hmm file
148    * 
149    * @param input
150    *          buffered reader used to read file
151    * @throws IOException
152    */
153   void parseModel(BufferedReader input) throws IOException
154   {
155     for (int i = 0; i < hmm.getLength() + 1; i++)
156     {
157       hmm.getNodes().add(new HMMNode());
158       String next;
159       String line;
160       line = input.readLine();
161       Scanner matchReader = new Scanner(line);
162       next = matchReader.next();
163       if (next.equals(COMPO) || i > 0)
164       {
165         // stores match emission line in list
166         List<Double> matches = new ArrayList<>();
167         matches = fillList(matchReader, numberOfSymbols);
168         hmm.getNodes().get(i).setMatchEmissions(matches);
169         if (i > 0)
170         {
171           parseAnnotations(matchReader, i);
172         }
173       }
174       matchReader.close();
175       // stores insert emission line in list
176       line = input.readLine();
177       Scanner insertReader = new Scanner(line);
178       List<Double> inserts = new ArrayList<>();
179       inserts = fillList(insertReader, numberOfSymbols);
180       hmm.getNodes().get(i).setInsertEmissions(inserts);
181       insertReader.close();
182
183       // stores state transition line in list
184       line = input.readLine();
185       Scanner transitionReader = new Scanner(line);
186       List<Double> transitions = new ArrayList<>();
187       transitions = fillList(transitionReader, NUMBER_OF_TRANSITIONS);
188       hmm.getNodes().get(i).setStateTransitions(transitions);
189       transitionReader.close();
190     }
191
192   }
193
194   /**
195    * parses annotations on match emission line
196    * 
197    * @param scanner
198    *          scanner which is processing match emission line
199    * @param index
200    *          index of node which is beign scanned
201    */
202   void parseAnnotations(Scanner scanner, int index)
203   {
204     if (hmm.mapIsActive())
205     {
206       int column;
207       column = scanner.nextInt();
208       hmm.getNodes().get(index).setAlignmentColumn(column);
209       hmm.getNodeLookup().put(column, index);
210     }
211     else
212     {
213       scanner.next();
214     }
215
216     char consensusR;
217     consensusR = charValue(scanner.next());
218     hmm.getNodes().get(index).setConsensusResidue(consensusR);
219
220       char reference;
221       reference = charValue(scanner.next());
222       hmm.getNodes().get(index).setReferenceAnnotation(reference);
223
224
225       char value;
226       value = charValue(scanner.next());
227       hmm.getNodes().get(index).setMaskValue(value);
228
229     char consensusS;
230     consensusS = charValue(scanner.next());
231     hmm.getNodes().get(index).setConsensusStructure(consensusS);
232   }
233
234
235   /**
236    * 
237    * @param input
238    *          scanner for line containing data to be transferred to list
239    * @param numberOfElements
240    *          number of elements in the list to be filled
241    * @return filled list
242    */
243   static List<Double> fillList(Scanner input,
244           int numberOfElements)
245   {
246     List<Double> list = new ArrayList<>();
247     for (int i = 0; i < numberOfElements; i++)
248     {
249
250       String next = input.next();
251       if (next.contains("*")) // state transitions to or from delete states
252                               // occasionally have values of -infinity. These
253                               // values are represented by an * in the .hmm
254                               // file, and by a null value in the
255                               // HiddenMarkovModel class
256       {
257         list.add(Double.NEGATIVE_INFINITY);
258       }
259       else
260       {
261         double prob = Double.valueOf(next);
262         prob = Math.pow(Math.E, -prob);
263         list.add(prob);
264       }
265     }
266     return list;
267   }
268
269   
270   /**
271    * writes a HiddenMarkovModel to a file
272    * 
273    * @param exportLocation
274    *          Filename, URL or Pasted String to write to
275    * @throws FileNotFoundException
276    * @throws UnsupportedEncodingException
277    *
278    **/
279   
280   public void exportFile(String exportLocation) throws IOException
281   {
282     StringBuilder file = new StringBuilder();
283     appendFileProperties(file);
284     appendModel(file);
285     file.append("//");
286
287     PrintWriter output = new PrintWriter(exportLocation);
288     output.append(file);
289     output.close();
290
291   }
292
293   String addData(int initialColumnSeparation,
294           int columnSeparation, List<String> data)
295   {
296     String line = EMPTY;
297     int index = 0;
298     for (String value : data)
299     {
300       if (index == 0)
301       {
302         line += String.format("%" + initialColumnSeparation + "s", value);
303       }
304       else
305       {
306         line += String.format("%" + columnSeparation + "s", value);
307       }
308       index++;
309     }
310     return line;
311   }
312
313   List<String> charListToStringList(List<Character> list)
314   {
315     List<String> strList = new ArrayList<>();
316     for (char value : list)
317     {
318       String strValue = Character.toString(value);
319       strList.add(strValue);
320     }
321     return strList;
322   }
323
324   List<String> doubleListToStringList(List<Double> list,
325           int noOfDecimals)
326   {
327     List<String> strList = new ArrayList<>();
328     for (double value : list)
329     {
330       String strValue;
331       if (value > 0)
332       {
333         strValue = String.format("%.5f", value);
334
335       }
336       else if (value == -0.00000d)
337       {
338         strValue = "0.00000";
339       }
340       else
341       {
342         strValue = "*";
343       }
344
345       strList.add(strValue);
346     }
347     return strList;
348   }
349
350   List<String> stringArrayToStringList(String[] array)
351   {
352     List<String> list = new ArrayList<>();
353     for (String value : array)
354     {
355       list.add(value);
356     }
357
358     return list;
359   }
360
361   void appendModel(StringBuilder file)
362   {
363     String symbolLine = "HMM";
364     List<Character> charSymbols = hmm.getSymbols();
365     List<String> strSymbols;
366     strSymbols = charListToStringList(charSymbols);
367     symbolLine += addData(11, 9, strSymbols);
368     file.append(symbolLine + NEW_LINE);
369     file.append(TRANSITIONTYPELINE + NEW_LINE);
370
371     int length = hmm.getLength();
372
373     for (int node = 0; node <= length; node++)
374     {
375       String matchLine;
376       if (node == 0)
377       {
378         matchLine = String.format("%7s", "COMPO");
379       }
380       else
381       {
382         matchLine = String.format("%7s", node);
383       }
384
385       List<String> strMatches;
386       List<Double> doubleMatches;
387       doubleMatches = hmm.getNode(node).getMatchEmissions();
388       convertListToLogSpace(doubleMatches);
389       strMatches = doubleListToStringList(doubleMatches, 5);
390       matchLine += addData(10, 9, strMatches);
391
392
393       if (node != 0)
394       {
395         matchLine += SPACE + hmm.getNodeAlignmentColumn(node);
396         matchLine += SPACE + hmm.getConsensusResidue(node);
397         matchLine += SPACE + hmm.getReferenceAnnotation(node);
398         matchLine += SPACE + hmm.getMaskedValue(node);
399         matchLine += SPACE + hmm.getConsensusStructure(node);
400
401       }
402
403       file.append(matchLine + NEW_LINE);
404       
405       String insertLine = EMPTY;
406       List<String> strInserts;
407       List<Double> doubleInserts;
408       doubleInserts = hmm.getNode(node).getInsertEmissions();
409       convertListToLogSpace(doubleInserts);
410       strInserts = doubleListToStringList(doubleInserts, 5);
411       insertLine += addData(17, 9, strInserts);
412
413       file.append(insertLine + NEW_LINE);
414
415       String transitionLine = EMPTY;
416       List<String> strTransitions;
417       List<Double> doubleTransitions;
418       doubleTransitions = hmm.getNode(node).getStateTransitions();
419       convertListToLogSpace(doubleTransitions);
420       strTransitions = doubleListToStringList(doubleTransitions, 5);
421       transitionLine += addData(17, 9, strTransitions);
422
423       file.append(transitionLine + NEW_LINE);
424     }
425   }
426
427   void appendFileProperties(StringBuilder file)
428   {
429     String line;
430
431     file.append(fileHeader + NEW_LINE);
432     
433     line = String.format("%-5s %1s", "NAME", hmm.getName());
434     file.append((line + NEW_LINE));
435
436     if (hmm.getAccessionNumber() != null)
437     {
438     line = String.format("%-5s %1s", "ACC", hmm.getAccessionNumber());
439     file.append((line + NEW_LINE));
440     }
441
442     if (hmm.getDescription() != null)
443     {
444     line = String.format("%-5s %1s", "DESC", hmm.getDescription());
445     file.append((line + NEW_LINE));
446     }
447     line = String.format("%-5s %1s", "LENG", hmm.getLength());
448     file.append((line + NEW_LINE));
449
450     if (hmm.getMaxInstanceLength() != null)
451     {
452     line = String.format("%-5s %1s", "MAXL", hmm.getMaxInstanceLength());
453     file.append((line + NEW_LINE));
454     }
455     line = String.format("%-5s %1s", "ALPH", hmm.getAlphabetType());
456     file.append((line + NEW_LINE));
457
458     boolean status;
459     String statusStr;
460
461     status = hmm.referenceAnnotationIsActive();
462     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
463     line = String.format("%-5s %1s", "RF",
464             statusStr);
465     file.append((line + NEW_LINE));
466
467     status = hmm.maskValueIsActive();
468     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
469     line = String.format("%-5s %1s", "MM",
470             statusStr);
471     file.append((line + NEW_LINE));
472     
473     status = hmm.consensusResidueIsActive();
474     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
475     line = String.format("%-5s %1s", "CONS",
476             statusStr);
477     file.append((line + NEW_LINE));
478
479     status = hmm.consensusStructureIsActive();
480     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
481     line = String.format("%-5s %1s", "CS",
482             statusStr);
483     file.append((line + NEW_LINE));
484
485     status = hmm.mapIsActive();
486     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
487     line = String.format("%-5s %1s", "MAP",
488             statusStr);
489     file.append((line + NEW_LINE));
490
491
492     if (hmm.getDate() != null)
493     {
494     line = String.format("%-5s %1s", "DATE", hmm.getDate());
495     file.append((line + NEW_LINE));
496     }
497     if (hmm.getNumberOfSequences() != null)
498     {
499     line = String.format("%-5s %1s", "NSEQ", hmm.getNumberOfSequences());
500     file.append((line + NEW_LINE));
501     }
502     if (hmm.getEffectiveNumberOfSequences() != null)
503     {
504     line = String.format("%-5s %1s", "EFFN",
505             hmm.getEffectiveNumberOfSequences());
506     file.append((line + NEW_LINE));
507     }
508     if (hmm.getCheckSum() != null)
509     {
510     line = String.format("%-5s %1s", "CKSUM", hmm.getCheckSum());
511     file.append((line + NEW_LINE));
512     }
513     if (hmm.getGatheringThreshold() != null)
514     {
515     line = String.format("%-5s %1s", "GA", hmm.getGatheringThreshold());
516     file.append((line + NEW_LINE));
517     }
518
519     if (hmm.getTrustedCutoff() != null)
520     {
521     line = String.format("%-5s %1s", "TC", hmm.getTrustedCutoff());
522     file.append((line + NEW_LINE));
523     }
524     if (hmm.getNoiseCutoff() != null)
525     {
526     line = String.format("%-5s %1s", "NC", hmm.getNoiseCutoff());
527     file.append((line + NEW_LINE));
528     }
529     if (hmm.getMSV() != null)
530     {
531       line = String.format("%-19s %18s", "STATS LOCAL MSV", hmm.getMSV());
532       file.append((line + NEW_LINE));
533
534       line = String.format("%-19s %18s", "STATS LOCAL VITERBI",
535               hmm.getViterbi());
536       file.append((line + NEW_LINE));
537     
538       line = String.format("%-19s %18s", "STATS LOCAL FORWARD",
539               hmm.getForward());
540       file.append((line + NEW_LINE));
541     }
542   }
543
544
545
546   char charValue(String string)
547   {
548     char character;
549     character = string.charAt(0);
550     return character;
551   }
552
553   @Override
554   public String print(SequenceI[] seqs, boolean jvsuffix)
555   {
556
557     return null;
558   }
559
560   void convertListToLogSpace(List<Double> list)
561   {
562
563     for (int i = 0; i < list.size(); i++)
564     {
565       double prob = list.get(i);
566       double logProb = -1 * Math.log(prob);
567
568       list.set(i, logProb);
569     }
570
571
572   }
573 }
574