JAL-2629 tidy tests, refactor hmmer node mapping slightly
[jalview.git] / src / jalview / datamodel / HiddenMarkovModel.java
1 package jalview.datamodel;
2
3 import jalview.schemes.ResidueProperties;
4 import jalview.util.Comparison;
5
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 /**
12  * Data structure which stores a hidden Markov model. Currently contains file
13  * properties as well, not sure whether these should be transferred to the
14  * HMMFile class
15  * 
16  * @author TZVanaalten
17  * 
18  */
19 public class HiddenMarkovModel
20 {
21   private static final double LOG2 = Math.log(2);
22
23   // Stores file properties. Do not directly access this field as it contains
24   // only string value - use the getter methods. For example, to find the length
25   // of theHMM, use getModelLength()to return an int value
26   Map<String, String> fileProperties = new HashMap<>();
27   
28   // contains all of the symbols used in this model. The index of each symbol
29   // represents its lookup value
30   List<Character> symbols = new ArrayList<>();
31
32   // contains information for each node in the model. The begin node is at index
33   // 0. Node 0 contains average emission probabilities for each symbol
34   List<HMMNode> nodes = new ArrayList<>();
35
36   // contains the HMM node for each alignment column, alignment columns start at
37   // index 0;
38   Map<Integer, HMMNode> nodeLookup = new HashMap<>();
39   
40   // contains the symbol index for each symbol
41   Map<Character, Integer> symbolIndexLookup = new HashMap<>();
42
43   final static String YES = "yes";
44
45   final static String NO = "no";
46   
47   // keys for file properties hashmap
48   private static final String NAME = "NAME";
49
50   private static final String ACCESSION_NUMBER = "ACC";
51
52   private static final String DESCRIPTION = "DESC";
53
54   private static final String LENGTH = "LENG";
55
56   private static final String MAX_LENGTH = "MAXL";
57
58   private static final String ALPHABET = "ALPH";
59
60   private static final String DATE = "DATE";
61
62   private static final String COMMAND_LOG = "COM";
63
64   private static final String NUMBER_OF_SEQUENCES = "NSEQ";
65
66   private static final String EFF_NUMBER_OF_SEQUENCES = "EFFN";
67
68   private static final String CHECK_SUM = "CKSUM";
69
70   private static final String GATHERING_THRESHOLDS = "GA";
71
72   private static final String TRUSTED_CUTOFFS = "TC";
73
74   private static final String NOISE_CUTOFFS = "NC";
75
76   private static final String STATISTICS = "STATS";
77
78   private static final String COMPO = "COMPO";
79   
80   private static final String GATHERING_THRESHOLD = "GA";
81
82   private static final String TRUSTED_CUTOFF = "TC";
83
84   private final String NOISE_CUTOFF = "NC";
85
86   private static final String VITERBI = "VITERBI";
87
88   private static final String MSV = "MSV";
89
90   private static final String FORWARD = "FORWARD";
91
92   private static final String MAP = "MAP";
93
94   private static final String REFERENCE_ANNOTATION = "RF";
95
96   private static final String CONSENSUS_RESIDUE = "CONS";
97
98   private static final String CONSENSUS_STRUCTURE = "CS";
99
100   private static final String MASKED_VALUE = "MM";
101   
102   public static final int MATCHTOMATCH = 0;
103
104   public static final int MATCHTOINSERT = 1;
105
106   public static final int MATCHTODELETE = 2;
107
108   public static final int INSERTTOMATCH = 3;
109
110   public static final int INSERTTOINSERT = 4;
111
112   public static final int DELETETOMATCH = 5;
113
114   public static final int DELETETODELETE = 6;
115
116   String fileHeader;
117
118   /**
119    * Constructor
120    */
121   public HiddenMarkovModel()
122   {
123   }
124
125   public HiddenMarkovModel(HiddenMarkovModel hmm)
126   {
127     super();
128     this.fileProperties = new HashMap<>(hmm.fileProperties);
129     this.symbols = new ArrayList<>(hmm.symbols);
130     this.nodes = new ArrayList<>(hmm.nodes);
131     this.nodeLookup = new HashMap<>(hmm.nodeLookup);
132     this.symbolIndexLookup = new HashMap<>(
133             hmm.symbolIndexLookup);
134     this.fileHeader = new String(hmm.fileHeader);
135   }
136
137   /**
138    * Returns the information content at a specified column, calculated as the
139    * sum (over possible symbols) of the log ratio
140    * 
141    * <pre>
142    *  log(emission probability / background probability) / log(2)
143    * </pre>
144    * 
145    * @param column
146    *          column position (base 0)
147    * @return
148    */
149   public float getInformationContent(int column)
150   {
151     float informationContent = 0f;
152
153     for (char symbol : getSymbols())
154     {
155       float freq = ResidueProperties.backgroundFrequencies
156               .get(getAlphabetType()).get(symbol);
157       float prob = (float) getMatchEmissionProbability(column, symbol);
158       informationContent += prob * Math.log(prob / freq);
159     }
160
161     informationContent = informationContent / (float) LOG2;
162
163     return informationContent;
164   }
165
166   /**
167    * Gets the file header of the .hmm file this model came from
168    * 
169    * @return
170    */
171   public String getFileHeader()
172   {
173     return fileHeader;
174   }
175
176   /**
177    * Sets the file header of this model.
178    * 
179    * @param header
180    */
181   public void setFileHeader(String header)
182   {
183     fileHeader = header;
184   }
185
186   /**
187    * Returns the map containing the matches between nodes and alignment column
188    * indexes.
189    * 
190    * @return
191    * 
192    */
193   public Map<Integer, HMMNode> getNodeLookup()
194   {
195     return nodeLookup;
196   }
197
198   /**
199    * Returns the list of symbols used in this hidden Markov model.
200    * 
201    * @return
202    */
203   public List<Character> getSymbols()
204   {
205     return symbols;
206   }
207   
208   /**
209    * Returns the file properties.
210    * 
211    * @return
212    */
213   public Map<String, String> getFileProperties()
214   {
215     return fileProperties;
216   }
217
218   /**
219    * Gets the node in the hidden Markov model at the specified position.
220    * 
221    * @param nodeIndex
222    *          The index of the node requested. Node 0 optionally contains the
223    *          average match emission probabilities across the entire model, and
224    *          always contains the insert emission probabilities and state
225    *          transition probabilities for the begin node. Node 1 contains the
226    *          first node in the HMM that can correspond to a column in the
227    *          alignment.
228    * @return
229    */
230   public HMMNode getNode(int nodeIndex)
231   {
232     return getNodes().get(nodeIndex);
233   }
234
235   /**
236    * Sets the list of symbols used in the hidden Markov model to the list
237    * specified.
238    * 
239    * @param symbolsL
240    *          The list of symbols to which the current list is to be changed.
241    * 
242    */
243   public void setSymbols(List<Character> symbolsL)
244   {
245     this.symbols = symbolsL;
246   }
247
248   /**
249    * Returns the name of the sequence alignment on which the HMM is based.
250    * 
251    * @return
252    */
253   public String getName()
254   {
255     return fileProperties.get(NAME);
256   }
257   
258   /**
259    * Returns the accession number.
260    * @return
261    */
262   public String getAccessionNumber()
263   {
264     return fileProperties.get(ACCESSION_NUMBER);
265   }
266
267   /**
268    * Returns a description of the sequence alignment on which the hidden Markov
269    * model is based.
270    * 
271    * @return
272    */
273   public String getDescription()
274   {
275     return fileProperties.get(DESCRIPTION);
276   }
277
278   /**
279    * Returns the length of the hidden Markov model.
280    * 
281    * @return
282    */
283   public Integer getLength()
284   {
285     if (fileProperties.get(LENGTH) == null)
286     {
287       return null;
288     }
289     return Integer.parseInt(fileProperties.get(LENGTH));
290   }
291
292   /**
293    * Returns the max instance length within the hidden Markov model.
294    * 
295    * @return
296    */
297   public Integer getMaxInstanceLength()
298   {
299     if (fileProperties.get(MAX_LENGTH) == null)
300     {
301       return null;
302     }
303     return Integer.parseInt(fileProperties.get(MAX_LENGTH));
304   }
305
306   /**
307    * Returns the type of symbol alphabet - "amino", "DNA", "RNA" are the
308    * options. Other alphabets may be added.
309    * 
310    * @return
311    */
312   public String getAlphabetType()
313   {
314     return fileProperties.get(ALPHABET);
315   }
316
317   /**
318    * Returns the date as a String.
319    * 
320    * @return
321    */
322   public String getDate()
323   {
324     return fileProperties.get(DATE);
325   }
326
327   /**
328    * Returns the command line log.
329    * 
330    * @return
331    */
332   public String getCommandLineLog()
333   {
334     return fileProperties.get(COMMAND_LOG);
335   }
336
337   /**
338    * Returns the number of sequences on which the HMM was trained.
339    * 
340    * @return
341    */
342   public Integer getNumberOfSequences()
343   {
344     if (fileProperties.get(NUMBER_OF_SEQUENCES) == null)
345     {
346       return null;
347     }
348     return Integer.parseInt(fileProperties.get(NUMBER_OF_SEQUENCES));
349   }
350
351   /**
352    * Returns the effective number of sequences on which the HMM was based.
353    * 
354    * @param value
355    */
356   public Double getEffectiveNumberOfSequences()
357   {
358     if (fileProperties.get(LENGTH) == null)
359     {
360       return null;
361     }
362     return Double.parseDouble(fileProperties.get(EFF_NUMBER_OF_SEQUENCES));
363   }
364
365   /**
366    * Returns the checksum.
367    * 
368    * @return
369    */
370   public Long getCheckSum()
371   {
372     if (fileProperties.get(LENGTH) == null)
373     {
374       return null;
375     }
376     return Long.parseLong(fileProperties.get(CHECK_SUM));
377   }
378
379   /**
380    * Returns the list of nodes in this HMM.
381    * 
382    * @return
383    */
384   public List<HMMNode> getNodes()
385   {
386     return nodes;
387   }
388
389   /**
390    * Sets the list of nodes in this HMM to the given list.
391    * 
392    * @param nodes
393    *          The list of nodes to which the current list of nodes is being
394    *          changed.
395    */
396   public void setNodes(List<HMMNode> nodes)
397   {
398     this.nodes = nodes;
399   }
400   
401   /**
402    * Gets the match emission probability for a given symbol at a column in the
403    * alignment.
404    * 
405    * @param alignColumn
406    *          The index of the alignment column, starting at index 0. Index 0
407    *          usually corresponds to index 1 in the HMM.
408    * @param symbol
409    *          The symbol for which the desired probability is being requested.
410    * @return
411    * 
412    */
413   public double getMatchEmissionProbability(int alignColumn, char symbol)
414   {
415     if (!symbolIndexLookup.containsKey(symbol))
416     {
417       return 0d;
418     }
419     int symbolIndex = symbolIndexLookup.get(symbol);
420     double probability = 0d;
421     if (nodeLookup.containsKey(alignColumn))
422     {
423       HMMNode node = nodeLookup.get(alignColumn);
424       probability = node.getMatchEmissions().get(symbolIndex);
425     }
426     return probability;
427   }
428
429   /**
430    * Gets the insert emission probability for a given symbol at a column in the
431    * alignment.
432    * 
433    * @param alignColumn
434    *          The index of the alignment column, starting at index 0. Index 0
435    *          usually corresponds to index 1 in the HMM.
436    * @param symbol
437    *          The symbol for which the desired probability is being requested.
438    * @return
439    * 
440    */
441   public double getInsertEmissionProbability(int alignColumn, char symbol)
442   {
443     if (!symbolIndexLookup.containsKey(symbol))
444     {
445       return 0d;
446     }
447     int symbolIndex = symbolIndexLookup.get(symbol);
448     double probability = 0d;
449     if (nodeLookup.containsKey(alignColumn))
450     {
451       HMMNode node = nodeLookup.get(alignColumn);
452       probability = node.getInsertEmissions().get(symbolIndex);
453     }
454     return probability;
455   }
456   
457   /**
458    * Gets the state transition probability for a given symbol at a column in the
459    * alignment.
460    * 
461    * @param alignColumn
462    *          The index of the alignment column, starting at index 0. Index 0
463    *          usually corresponds to index 1 in the HMM.
464    * @param symbol
465    *          The symbol for which the desired probability is being requested.
466    * @return
467    * 
468    */
469   public Double getStateTransitionProbability(int alignColumn,
470           int transition)
471   {
472     double probability = 0d;
473     if (nodeLookup.containsKey(alignColumn))
474     {
475       HMMNode node = nodeLookup.get(alignColumn);
476       probability = node.getStateTransitions().get(transition);
477     }
478     return probability;
479   }
480   
481   /**
482    * Returns the alignment column linked to the node at the given index.
483    * 
484    * @param nodeIndex
485    *          The index of the node, starting from index 1. Index 0 is the begin
486    *          node, which does not correspond to a column in the alignment.
487    * @return
488    */
489   public Integer getNodeAlignmentColumn(int nodeIndex)
490   {
491     Integer value = nodes.get(nodeIndex).getAlignmentColumn();
492     return value;
493   }
494   
495   /**
496    * Returns the consensus residue at the specified node.
497    * 
498    * @param nodeIndex
499    *          The index of the specified node.
500    * @return
501    */
502   public char getConsensusResidue(int nodeIndex)
503   {
504    char value = nodes.get(nodeIndex).getConsensusResidue();
505    return value;
506   }
507   
508   /**
509    * Returns the consensus at a given alignment column. If the character is
510    * lower case, its emission probability is less than 0.5.
511    * 
512    * @param columnIndex
513    *          The index of the column in the alignment for which the consensus
514    *          is desired. The list of columns starts at index 0.
515    * @return
516    */
517   public char getConsensusAtAlignColumn(int columnIndex)
518   {
519     char mostLikely = '-';
520     if (consensusResidueIsActive())
521     {
522       HMMNode node = nodeLookup.get(columnIndex);
523       if (node == null)
524       {
525         return '-';
526       }
527       mostLikely = node.getConsensusResidue();
528       return mostLikely;
529     }
530     else
531     {
532       double highestProb = 0;
533       for (char character : symbols)
534       {
535         double prob = getMatchEmissionProbability(columnIndex, character);
536         if (prob > highestProb)
537         {
538           highestProb = prob;
539           mostLikely = character;
540         }
541       }
542       if (highestProb < 0.5)
543       {
544         mostLikely = Character.toLowerCase(mostLikely);
545       }
546       return mostLikely;
547     }
548
549   }
550
551   /**
552    * Returns the reference annotation at the specified node.
553    * 
554    * @param nodeIndex
555    *          The index of the specified node.
556    * @return
557    */
558   public char getReferenceAnnotation(int nodeIndex)
559   {
560    char value = nodes.get(nodeIndex).getReferenceAnnotation();
561    return value;
562   }
563   
564   /**
565    * Returns the mask value at the specified node.
566    * 
567    * @param nodeIndex
568    *          The index of the specified node.
569    * @return
570    */
571   public char getMaskedValue(int nodeIndex)
572   {
573    char value = nodes.get(nodeIndex).getMaskValue();
574    return value;
575   }
576   
577   /**
578    * Returns the consensus structure at the specified node.
579    * 
580    * @param nodeIndex
581    *          The index of the specified node.
582    * @return
583    */
584   public char getConsensusStructure(int nodeIndex)
585   {
586    char value = nodes.get(nodeIndex).getConsensusStructure();
587    return value;
588   }
589   
590   /**
591    * Returns the average match emission probability for a given symbol
592    * 
593    * @param symbolIndex
594    *          The index of the symbol.
595    * @return
596    * 
597    */
598   public double getAverageMatchEmission(int symbolIndex)
599   {
600     double value = nodes.get(0).getMatchEmissions().get(symbolIndex);
601     return value;
602   }
603
604   /**
605    * Returns the number of symbols in the alphabet used in this HMM.
606    * 
607    * @return
608    */
609   public int getNumberOfSymbols()
610   {
611     return symbols.size();
612   }
613
614   /**
615    * Adds a file property.
616    * 
617    * @param key
618    * @param value
619    */
620   public void addFileProperty(String key, String value)
621   {
622     fileProperties.put(key, value);
623   }
624
625   /**
626    * Returns a boolean indicating whether the reference annotation is active.
627    * 
628    * @return
629    */
630   public boolean referenceAnnotationIsActive()
631   {
632     String status;
633     status = fileProperties.get(REFERENCE_ANNOTATION);
634     if (status == null)
635     {
636       return false;
637     }
638     switch (status)
639     {
640     case YES:
641       return true;
642     case NO:
643       return false;
644     default:
645       return false;
646     }
647
648   }
649
650   /**
651    * Returns a boolean indicating whether the mask value annotation is active.
652    * 
653    * @return
654    */
655   public boolean maskValueIsActive()
656   {
657     String status;
658     status = fileProperties.get(MASKED_VALUE);
659     if (status == null)
660     {
661       return false;
662     }
663     switch (status)
664     {
665     case YES:
666       return true;
667     case NO:
668       return false;
669     default:
670       return false;
671     }
672
673   }
674
675   /**
676    * Returns a boolean indicating whether the consensus residue annotation is
677    * active.
678    * 
679    * @return
680    */
681   public boolean consensusResidueIsActive()
682   {
683     String status;
684     status = fileProperties.get(CONSENSUS_RESIDUE);
685     if (status == null)
686     {
687       return false;
688     }
689     switch (status)
690     {
691     case YES:
692       return true;
693     case NO:
694       return false;
695     default:
696       return false;
697     }
698
699   }
700
701   /**
702    * Returns a boolean indicating whether the consensus structure annotation is
703    * active.
704    * 
705    * @return
706    */
707   public boolean consensusStructureIsActive()
708   {
709     String status;
710     status = fileProperties.get(CONSENSUS_STRUCTURE);
711     if (status == null)
712     {
713       return false;
714     }
715     switch (status)
716     {
717     case YES:
718       return true;
719     case NO:
720       return false;
721     default:
722       return false;
723     }
724
725   }
726
727   /**
728    * Returns a boolean indicating whether the MAP annotation is active.
729    * 
730    * @return
731    */
732   public boolean mapIsActive()
733   {
734     String status;
735     status = fileProperties.get(MAP);
736     if (status == null)
737     {
738       return false;
739     }
740     switch (status)
741     {
742     case YES:
743       return true;
744     case NO:
745       return false;
746     default:
747       return false;
748     }
749
750   }
751
752   /**
753    * Sets the alignment column of the specified node
754    * 
755    * @param nodeIndex
756    * 
757    * @param column
758    * 
759    */
760   public void setAlignmentColumn(HMMNode node, int column)
761   {
762     node.setAlignmentColumn(column);
763     nodeLookup.put(column, node);
764   }
765
766   public void updateMapping(char[] sequence)
767   {
768     int nodeNo = 1;
769     int column = 0;
770     synchronized (nodeLookup)
771     {
772       clearNodeLookup();
773       for (char residue : sequence)
774       {
775         if (!Comparison.isGap(residue))
776         {
777           HMMNode node = nodes.get(nodeNo);
778           if (node == null)
779           {
780             // error : too few nodes for sequence
781             break;
782           }
783           setAlignmentColumn(node, column);
784           nodeNo++;
785         }
786         column++;
787       }
788     }
789   }
790
791   /**
792    * Clears all data in the node lookup map
793    */
794   public void clearNodeLookup()
795   {
796     nodeLookup.clear();
797   }
798
799   /**
800    * Sets the reference annotation at a given node.
801    * 
802    * @param nodeIndex
803    * @param value
804    */
805   public void setReferenceAnnotation(int nodeIndex, char value)
806   {
807     nodes.get(nodeIndex).setReferenceAnnotation(value);
808   }
809
810   /**
811    * Sets the consensus residue at a given node.
812    * 
813    * @param nodeIndex
814    * @param value
815    */
816   public void setConsensusResidue(int nodeIndex, char value)
817   {
818     nodes.get(nodeIndex).setConsensusResidue(value);
819   }
820
821   /**
822    * Sets the consensus structure at a given node.
823    * 
824    * @param nodeIndex
825    * @param value
826    */
827   public void setConsensusStructure(int nodeIndex, char value)
828   {
829     nodes.get(nodeIndex).setConsensusStructure(value);
830   }
831
832   /**
833    * Sets the mask value at a given node.
834    * 
835    * @param nodeIndex
836    * @param value
837    */
838   public void setMaskValue(int nodeIndex, char value)
839   {
840     nodes.get(nodeIndex).setMaskValue(value);
841   }
842
843   /**
844    * Temporary implementation, should not be used.
845    * 
846    * @return
847    */
848   public String getGatheringThreshold()
849   {
850     String value;
851     value = fileProperties.get("GA");
852     return value;
853   }
854
855   /**
856    * Temporary implementation, should not be used.
857    * 
858    * @return
859    */
860   public String getNoiseCutoff()
861   {
862     String value;
863     value = fileProperties.get("NC");
864     return value;
865   }
866
867   /**
868    * Temporary implementation, should not be used.
869    * 
870    * @return
871    */
872   public String getTrustedCutoff()
873   {
874     String value;
875     value = fileProperties.get("TC");
876     return value;
877   }
878
879   /**
880    * Temporary implementation, should not be used.
881    * 
882    * @return
883    */
884   public String getViterbi()
885   {
886     String value;
887     value = fileProperties.get(VITERBI);
888     return value;
889   }
890
891   /**
892    * Temporary implementation, should not be used.
893    * 
894    * @return
895    */
896   public String getMSV()
897   {
898     String value;
899     value = fileProperties.get(MSV);
900     return value;
901   }
902
903   /**
904    * Temporary implementation, should not be used.
905    * 
906    * @return
907    */
908   public String getForward()
909   {
910     String value;
911     value = fileProperties.get(FORWARD);
912     return value;
913   }
914
915   /**
916    * Sets the activation status of the MAP annotation.
917    * 
918    * @param status
919    */
920   public void setMAPStatus(boolean status)
921   {
922     fileProperties.put(MAP, status ? YES : NO);
923   }
924
925   /**
926    * Sets the activation status of the reference annotation.
927    * 
928    * @param status
929    */
930   public void setReferenceAnnotationStatus(boolean status)
931   {
932     fileProperties.put(REFERENCE_ANNOTATION, status ? YES : NO);
933   }
934
935   /**
936    * Sets the activation status of the mask value annotation.
937    * 
938    * @param status
939    */
940   public void setMaskedValueStatus(boolean status)
941   {
942     fileProperties.put(MASKED_VALUE, status ? YES : NO);
943   }
944
945   /**
946    * Sets the activation status of the consensus residue annotation.
947    * 
948    * @param status
949    */
950   public void setConsensusResidueStatus(boolean status)
951   {
952     fileProperties.put(CONSENSUS_RESIDUE, status ? YES : NO);
953   }
954
955   /**
956    * Sets the activation status of the consensus structure annotation.
957    * 
958    * @param status
959    */
960   public void setConsensusStructureStatus(boolean status)
961   {
962     fileProperties.put(CONSENSUS_STRUCTURE, status ? YES : NO);
963   }
964
965   /**
966    * Answers the HMMNode mapped to the given alignment column (base 0), or null
967    * if none is mapped
968    * 
969    * @param alignmentColumn
970    */
971   public HMMNode getNodeForColumn(int alignmentColumn)
972   {
973     return nodeLookup.get(alignmentColumn);
974   }
975
976   /**
977    * Finds the String values of a boolean. "yes" for true and "no" for false.
978    * 
979    * @param value
980    * @return
981    */
982   public static String findStringFromBoolean(boolean value)
983   {
984     if (value)
985     {
986       return YES;
987     }
988     else
989     {
990       return NO;
991     }
992   }
993
994
995
996   /**
997    * Returns the consensus sequence based on the most probable symbol at each
998    * position. The sequence is adjusted to match the length of the existing
999    * sequence alignment. Gap characters are used as padding.
1000    * 
1001    * @param length
1002    *          The length of the longest sequence in the existing alignment.
1003    * @return
1004    */
1005   public Sequence getConsensusSequence()
1006   {
1007     int start;
1008     int end;
1009     int modelLength;
1010     start = getNodeAlignmentColumn(1);
1011     modelLength = getLength();
1012     end = getNodeAlignmentColumn(modelLength);
1013     char[] sequence = new char[end + 1];
1014     for (int index = 0; index < end + 1; index++)
1015     {
1016       Character character;
1017
1018         character = getConsensusAtAlignColumn(index);
1019
1020       if (character == null || character == '-')
1021       {
1022         sequence[index] = '-';
1023       }
1024       else
1025       {
1026         sequence[index] = Character.toUpperCase(character);
1027       }
1028       }
1029
1030
1031     Sequence seq = new Sequence(getName(), sequence, start,
1032             end);
1033     return seq;
1034   }
1035
1036
1037   /**
1038    * Initiates a HMM consensus sequence
1039    * 
1040    * @return A new HMM consensus sequence
1041    */
1042   public SequenceI initHMMSequence()
1043   {
1044     Sequence consensus = getConsensusSequence();
1045     consensus.setIsHMMConsensusSequence(true);
1046     consensus.setHMM(this);
1047     return consensus;
1048   }
1049
1050   public int getSymbolIndex(char c)
1051   {
1052     return symbolIndexLookup.get(c);
1053   }
1054
1055   public void setSymbolIndex(Character c, Integer i)
1056   {
1057     symbolIndexLookup.put(c, i);
1058   }
1059
1060
1061 }
1062