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