e2143732c90af84d5fe4aff230ed822c966890d5
[jalview.git] / src / jalview / datamodel / HiddenMarkovModel.java
1 package jalview.datamodel;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Scanner;
8
9 /**
10  * Data structure which stores a hidden Markov model. Currently contains file properties as well, not sure whether these should be transferred to the HMMFile class
11  * 
12  * @author TZVanaalten
13  * 
14  */
15 public class HiddenMarkovModel
16 {
17   // Stores file properties. Do not directly access this field as it contains
18   // only string value - use the getter methods. For example, to find the length
19   // of theHMM, use getModelLength()to return an int value
20   Map<String, String> fileProperties = new HashMap<>();
21   
22   //contains all of the symbols used in this model. The index of each symbol represents its lookup value 
23   List<Character> symbols = new ArrayList<>();
24
25   // contains information for each node in the model. The begin node is at index
26   // 0. Node 0 contains average emission probabilities for each symbol
27   List<HMMNode> nodes = new ArrayList<>();
28
29   // contains the HMM node for each alignment column
30   Map<Integer, Integer> nodeLookup = new HashMap<>();
31   
32   //contains the symbol index for each symbol
33   Map<Character, Integer> symbolIndexLookup = new HashMap<>();
34
35
36   final static String YES = "yes";
37
38   final static String NO = "no";
39
40   int numberOfSymbols;
41   
42   //keys for file properties hashmap
43   private final String NAME = "NAME";
44
45   private final String ACCESSION_NUMBER = "ACC";
46
47   private final String DESCRIPTION = "DESC";
48
49   private final String LENGTH = "LENG";
50
51   private final String MAX_LENGTH = "MAXL";
52
53   private final String ALPHABET = "ALPH";
54
55   private final String DATE = "DATE";
56
57   private final String COMMAND_LOG = "COM";
58
59   private final String NUMBER_OF_SEQUENCES = "NSEQ";
60
61   private final String EFF_NUMBER_OF_SEQUENCES = "EFFN";
62
63   private final String CHECK_SUM = "CKSUM";
64
65   private final String GATHERING_THRESHOLDS = "GA";
66
67   private final String TRUSTED_CUTOFFS = "TC";
68
69   private final String NOISE_CUTOFFS = "NC";
70
71   private final String STATISTICS = "STATS";
72
73   private final String COMPO = "COMPO";
74   
75   private final String GATHERING_THRESHOLD = "GA";
76
77   private final String TRUSTED_CUTOFF = "TC";
78
79   private final String NOISE_CUTOFF = "NC";
80
81   private final String VITERBI = "VITERBI";
82
83   private final String MSV = "MSV";
84
85   private final String FORWARD = "FORWARD";
86
87   private final String MAP = "MAP";
88
89   private final String REFERENCE_ANNOTATION = "RF";
90
91   private final String CONSENSUS_RESIDUE = "CONS";
92
93   private final String CONSENSUS_STRUCTURE = "CS";
94
95   private final String MASKED_VALUE = "MM";
96   
97   final static String[] TRANSITION_TYPES = new String[] { "m->m", "m->i",
98       "m->d", "i->m", "i->i", "d->m", "d->d" };
99
100   public String getTransitionType(int index)
101   {
102     return TRANSITION_TYPES[index];
103   }
104
105   public Map<Integer, Integer> getNodeLookup()
106   {
107     return nodeLookup;
108   }
109
110   public void setNodeLookup(Map<Integer, Integer> nodeLookup)
111   {
112     this.nodeLookup = nodeLookup;
113   }
114
115   public String[] getTransitionTypes()
116   {
117     return TRANSITION_TYPES;
118   }
119
120   public List<Character> getSymbols()
121   {
122     return symbols;
123   }
124
125   public Map<String, String> getFileProperties()
126   {
127     return fileProperties;
128   }
129
130   public HMMNode getNode(int nodeIndex)
131   {
132     return getNodes().get(nodeIndex);
133   }
134
135   public void setSymbols(List<Character> symbolsL)
136   {
137     this.symbols = symbolsL;
138   }
139
140   public String getName()
141   {
142     return fileProperties.get(NAME);
143   }
144   public String getAccessionNumber()
145   {
146     return fileProperties.get(ACCESSION_NUMBER);
147   }
148
149   public void setAccessionNumber(String value)
150   {
151     fileProperties.put(ACCESSION_NUMBER, value);
152   }
153
154   public String getDescription()
155   {
156     return fileProperties.get(DESCRIPTION);
157   }
158
159   public void setDescription(String value)
160   {
161     fileProperties.put(DESCRIPTION, value);
162   }
163
164   public Integer getLength()
165   {
166     if (fileProperties.get(LENGTH) == null)
167     {
168       return null;
169     }
170     return Integer.parseInt(fileProperties.get(LENGTH));
171   }
172
173   public void setLength(int value)
174   {
175     fileProperties.put(LENGTH, String.valueOf(value));
176   }
177
178   public Integer getMaxInstanceLength()
179   {
180     if (fileProperties.get(MAX_LENGTH) == null)
181     {
182       return null;
183     }
184     return Integer.parseInt(fileProperties.get(MAX_LENGTH));
185   }
186
187   public void setMaxInstanceLength(int value)
188   {
189     fileProperties.put(MAX_LENGTH, String.valueOf(value));
190   }
191
192   // gets type of symbol alphabet - "amino", "DNA", "RNA"
193   public String getAlphabetType()
194   {
195     return fileProperties.get(ALPHABET);
196   }
197
198   public void setAlphabetType(String value)
199   {
200     fileProperties.put(ALPHABET, value);
201   }
202
203   // not sure whether to implement this with Date object
204   public String getDate()
205   {
206     return fileProperties.get(DATE);
207   }
208
209   public void setDate(String value)
210   {
211     fileProperties.put(DATE, value);
212   }
213
214   // not sure whether to implement this
215   public String getCommandLineLog()
216   {
217     return fileProperties.get(COMMAND_LOG);
218   }
219
220   public void setCommandLineLog(String value)
221   {
222     fileProperties.put(COMMAND_LOG, value);
223   }
224
225   // gets the number of sequences that the HMM was trained on
226   public Integer getNumberOfSequences()
227   {
228     if (fileProperties.get(NUMBER_OF_SEQUENCES) == null)
229     {
230       return null;
231     }
232     return Integer.parseInt(fileProperties.get(NUMBER_OF_SEQUENCES));
233   }
234
235   public void setNumberOfSequences(int value)
236   {
237     fileProperties.put(NUMBER_OF_SEQUENCES, String.valueOf(value));
238   }
239
240   // gets the effective number determined during sequence weighting
241   public Double getEffectiveNumberOfSequences()
242   {
243     if (fileProperties.get(LENGTH) == null)
244     {
245       return null;
246     }
247     return Double.parseDouble(fileProperties.get(EFF_NUMBER_OF_SEQUENCES));
248   }
249
250   public void setEffectiveNumberOfSequences(double value)
251   {
252     fileProperties.put(EFF_NUMBER_OF_SEQUENCES, String.valueOf(value));
253   }
254
255   public Long getCheckSum()
256   {
257     if (fileProperties.get(LENGTH) == null)
258     {
259       return null;
260     }
261     return Long.parseLong(fileProperties.get(CHECK_SUM));
262   }
263
264   public void setCheckSum(long value)
265   {
266     fileProperties.put(CHECK_SUM, String.valueOf(value));
267   }
268
269   public List<HMMNode> getNodes()
270   {
271     return nodes;
272   }
273
274   public void setNodes(List<HMMNode> nodes)
275   {
276     this.nodes = nodes;
277   }
278   
279   /**
280    * get match emission probability for a given symbol at a column in the
281    * alignment
282    * 
283    * @param alignColumn
284    * @param symbol
285    * @return
286    * 
287    */
288   public Double getMatchEmissionProbability(int alignColumn, char symbol)
289   {
290     int symbolIndex;
291     int nodeIndex;
292     Double probability;
293     if (symbol == '-')
294     {
295       return 0d;
296     }
297     symbolIndex = symbolIndexLookup.get(symbol);
298     if (nodeLookup.containsKey(alignColumn + 1))
299     {
300       nodeIndex = nodeLookup.get(alignColumn + 1);
301       probability = getNode(nodeIndex).getMatchEmissions().get(symbolIndex);
302       probability = Math.pow(Math.E, -probability);
303       return probability;
304     }
305     else
306     {
307       return 0d;
308     }
309
310   }
311
312   /**
313    * get insert emission probability for a given symbol at a column in the
314    * alignment
315    * 
316    * @param alignColumn
317    * @param symbol
318    * @return
319    */
320   public Double getInsertEmissionProbability(int alignColumn, char symbol)
321   {
322     int symbolIndex;
323     int nodeIndex;
324     Double probability;
325     if (symbol == '-')
326     {
327       return 0d;
328     }
329     symbolIndex = symbolIndexLookup.get(symbol);
330     if (nodeLookup.containsKey(alignColumn + 1))
331     {
332       nodeIndex = nodeLookup.get(alignColumn + 1);
333       probability = getNode(nodeIndex).getInsertEmissions()
334               .get(symbolIndex);
335       probability = Math.pow(Math.E, -probability);
336       return probability;
337     }
338     else
339     {
340       return 0d;
341     }
342
343   }
344   
345   /**
346    * get state transition probability for a given transition type at a column in
347    * the alignment
348    * 
349    * @param alignColumn
350    * @param transition
351    * @return
352    */
353   public Double getStateTransitionProbability(int alignColumn,
354           String transition)
355   {
356     int transitionIndex;
357     int nodeIndex;
358     Double probability;
359     transitionIndex = getTransitionType(transition);
360     if (nodeLookup.containsKey(alignColumn + 1))
361     {
362       nodeIndex = nodeLookup.get(alignColumn + 1);
363       probability = getNode(nodeIndex).getStateTransitions()
364             .get(transitionIndex);
365       probability = Math.pow(Math.E, -probability);
366       return probability;
367     }
368     else
369     {
370       return 0d;
371     }
372
373   }
374   
375   public Integer getNodeAlignmentColumn(int nodeIndex)
376   {
377     Integer value = nodes.get(nodeIndex).getAlignmentColumn();
378    return value;
379   }
380   
381   public char getConsensusResidue(int nodeIndex)
382   {
383    char value = nodes.get(nodeIndex).getConsensusResidue();
384    return value;
385   }
386   
387   public char getReferenceAnnotation(int nodeIndex)
388   {
389    char value = nodes.get(nodeIndex).getReferenceAnnotation();
390    return value;
391   }
392   
393   public char getMaskedValue(int nodeIndex)
394   {
395    char value = nodes.get(nodeIndex).getMaskValue();
396    return value;
397   }
398   
399   public char getConsensusStructure(int nodeIndex)
400   {
401    char value = nodes.get(nodeIndex).getConsensusStructure();
402    return value;
403   }
404   
405   /**
406    * returns the average match emission for a given symbol
407    * @param symbolIndex
408    * index of symbol
409    * @return
410    * average negative log propbability of a match emission of the given symbol
411    */
412   public double getAverageMatchEmission(int symbolIndex)
413   {
414     double value = nodes.get(0).getMatchEmissions().get(symbolIndex);
415     return value;
416   }
417
418   public int getNumberOfSymbols()
419   {
420     return numberOfSymbols;
421   }
422
423   public void setNumberOfSymbols(int numberOfSymbols)
424   {
425     this.numberOfSymbols = numberOfSymbols;
426   }
427
428   
429
430   /**
431    * fills symbol array and also finds numberOfSymbols
432    * 
433    * @param parser
434    *          scanner scanning symbol line in file
435    */
436   public void fillSymbols(Scanner parser)
437   {
438     int i = 0;
439     while (parser.hasNext())
440     {
441       String strSymbol = parser.next();
442       char[] symbol = strSymbol.toCharArray();
443       symbols.add(symbol[0]);
444       symbolIndexLookup.put(symbol[0], i);
445       i++;
446     }
447     numberOfSymbols = symbols.size();
448   }
449
450   /**
451    * adds file property
452    * 
453    * @param key
454    * @param value
455    */
456   public void addFileProperty(String key, String value)
457   {
458     fileProperties.put(key, value);
459   }
460
461   public boolean referenceAnnotationIsActive()
462   {
463     String status;
464     status = fileProperties.get(REFERENCE_ANNOTATION);
465     if (status == null)
466     {
467       return false;
468     }
469     switch (status)
470     {
471     case YES:
472       return true;
473     case NO:
474       return false;
475     default:
476       return false;
477     }
478
479   }
480
481   public boolean maskValueIsActive()
482   {
483     String status;
484     status = fileProperties.get(MASKED_VALUE);
485     if (status == null)
486     {
487       return false;
488     }
489     switch (status)
490     {
491     case YES:
492       return true;
493     case NO:
494       return false;
495     default:
496       return false;
497     }
498
499   }
500
501   public boolean consensusResidueIsActive()
502   {
503     String status;
504     status = fileProperties.get(CONSENSUS_RESIDUE);
505     if (status == null)
506     {
507       return false;
508     }
509     switch (status)
510     {
511     case YES:
512       return true;
513     case NO:
514       return false;
515     default:
516       return false;
517     }
518
519   }
520
521   public boolean consensusStructureIsActive()
522   {
523     String status;
524     status = fileProperties.get(CONSENSUS_STRUCTURE);
525     if (status == null)
526     {
527       return false;
528     }
529     switch (status)
530     {
531     case YES:
532       return true;
533     case NO:
534       return false;
535     default:
536       return false;
537     }
538
539   }
540
541   public boolean mapIsActive()
542   {
543     String status;
544     status = fileProperties.get(MAP);
545     if (status == null)
546     {
547       return false;
548     }
549     switch (status)
550     {
551     case YES:
552       return true;
553     case NO:
554       return false;
555     default:
556       return false;
557     }
558
559   }
560
561   public void setAlignmentColumn(int nodeIndex, int column)
562   {
563     nodes.get(nodeIndex).setAlignmentColumn(column);
564   }
565
566   public void setReferenceAnnotation(int nodeIndex, char value)
567   {
568     nodes.get(nodeIndex).setReferenceAnnotation(value);
569   }
570
571   public void setConsensusResidue(int nodeIndex, char value)
572   {
573     nodes.get(nodeIndex).setConsensusResidue(value);
574   }
575
576   public void setConsensusStructure(int nodeIndex, char value)
577   {
578     nodes.get(nodeIndex).setConsensusStructure(value);
579   }
580
581   public void setMaskValue(int nodeIndex, char value)
582   {
583     nodes.get(nodeIndex).setMaskValue(value);
584   }
585
586   public String getGatheringThreshold()
587   {
588     String value;
589     value = fileProperties.get("GA");
590     return value;
591   }
592
593   public String getNoiseCutoff()
594   {
595     String value;
596     value = fileProperties.get("NC");
597     return value;
598   }
599
600   public String getTrustedCutoff()
601   {
602     String value;
603     value = fileProperties.get("TC");
604     return value;
605   }
606
607   public String getViterbi()
608   {
609     String value;
610     value = fileProperties.get(VITERBI);
611     return value;
612   }
613
614   public String getMSV()
615   {
616     String value;
617     value = fileProperties.get(MSV);
618     return value;
619   }
620
621   public String getForward()
622   {
623     String value;
624     value = fileProperties.get(FORWARD);
625     return value;
626   }
627
628   public void setMAPStatus(boolean status)
629   {
630     if (status == true)
631     {
632       fileProperties.put(MAP, YES);
633     }
634     else
635     {
636       fileProperties.put(MAP, NO);
637     }
638   }
639
640   public void setReferenceAnnotationStatus(boolean status)
641   {
642     if (status == true)
643     {
644       fileProperties.put(REFERENCE_ANNOTATION, YES);
645     }
646     else
647     {
648       fileProperties.put(REFERENCE_ANNOTATION, NO);
649     }
650   }
651
652   public void setMaskedValueStatus(boolean status)
653   {
654     if (status == true)
655     {
656       fileProperties.put(MASKED_VALUE, YES);
657     }
658     else
659     {
660       fileProperties.put(MASKED_VALUE, NO);
661     }
662   }
663
664   public void setConsensusResidueStatus(boolean status)
665   {
666     if (status == true)
667     {
668       fileProperties.put(CONSENSUS_RESIDUE, YES);
669     }
670     else
671     {
672       fileProperties.put(CONSENSUS_RESIDUE, NO);
673     }
674   }
675
676   public void setConsensusStructureStatus(boolean status)
677   {
678     if (status == true)
679     {
680       fileProperties.put(CONSENSUS_STRUCTURE, YES);
681     }
682     else
683     {
684       fileProperties.put(CONSENSUS_STRUCTURE, NO);
685     }
686   }
687
688   /**
689    * 
690    * @param transition
691    *          type of transition occuring
692    * @return index value representing position along stateTransition array.
693    */
694   public Integer getTransitionType(String transition)
695   {
696     Integer index;
697     switch (transition)
698     {
699     case "mm":
700       index = 0;
701       break;
702     case "mi":
703       index = 1;
704       break;
705     case "md":
706       index = 2;
707       break;
708     case "im":
709       index = 3;
710       break;
711     case "ii":
712       index = 4;
713       break;
714     case "dm":
715       index = 5;
716       break;
717     case "dd":
718       index = 6;
719       break;
720     default:
721       index = null;
722     }
723     return index;
724   }
725
726   /**
727    * find the index of the node in a hidden Markov model based on the column in
728    * the alignment
729    * 
730    * @param alignmentColumn
731    */
732
733   public Integer findNodeIndex(int alignmentColumn)
734   {
735     Integer index;
736     index = nodeLookup.get(alignmentColumn);
737     return index;
738   }
739
740   public static String findStringFromBoolean(boolean value)
741   {
742     if (value)
743     {
744       return YES;
745     }
746     else
747     {
748       return NO;
749     }
750   }
751 }
752