JAL-2938 revisions to implementation details
[jalview.git] / src / jalview / schemes / HmmerColourScheme.java
1 package jalview.schemes;
2
3 import jalview.datamodel.AnnotatedCollectionI;
4 import jalview.datamodel.HiddenMarkovModel;
5 import jalview.datamodel.SequenceCollectionI;
6 import jalview.datamodel.SequenceI;
7 import jalview.util.ColorUtils;
8 import jalview.util.Comparison;
9
10 import java.awt.Color;
11 import java.util.Map;
12
13 /**
14  * Base class for colour schemes based on a selected Hidden Markov Model. The
15  * colour is with reference to an HMM consensus sequence and HMM profile
16  * <ul>
17  * <li>white for a gap</li>
18  * <li>red for an insertion (position is gapped in the HMM consensus)</li>
19  * <li>orange for negative information content</li>
20  * <li>white to blue for increasing information content</li>
21  * </ul>
22  * where information content is the log ratio
23  * 
24  * <pre>
25  *   log(profile match emission probability / residue background probability)
26  * </pre>
27  * 
28  * Sub-class implementations use either global ('Uniprot') or local
29  * ('alignment') background frequencies.
30  * 
31  * @author tzvanaalten
32  * @author gmcarstairs
33  */
34 public abstract class HmmerColourScheme extends ResidueColourScheme
35 {
36   private static final Color INSERTION_COLOUR = new Color(230, 0, 0); // reddish
37
38   /*
39    * the aligned HMM consensus sequence to use as reference for colouring
40    */
41   private SequenceI hmmSeq;
42
43   private HiddenMarkovModel hmm;
44
45   private Map<Character, Float> frequencies;
46
47   /**
48    * Constructor given a Hidden Markov Model consensus sequence. This provides
49    * the HMM profile from which we can read the emission probabilities that
50    * determine the colour.
51    * 
52    * @param consensusSeq
53    */
54   public HmmerColourScheme(SequenceI consensusSeq)
55   {
56     hmmSeq = consensusSeq;
57     hmm = hmmSeq == null ? null : hmmSeq.getHMM();
58   }
59
60   /**
61    * Default constructor (required by ColourSchemes.loadColourSchemes)
62    */
63   public HmmerColourScheme()
64   {
65   }
66
67   @Override
68   public Color findColour(char symbol, int column, SequenceI seq,
69           String consensusResidue, float pid)
70   {
71     return findColour(symbol, column);
72   }
73
74   /**
75    * Returns the colour at a particular symbol at a column in the alignment:
76    * <ul>
77    * <li>white for a gap</li>
78    * <li>red for an insertion</li>
79    * <li>orange for negative information content</li>
80    * <li>white to blue for increasing information content</li>
81    * </ul>
82    * 
83    * @param symbol
84    * @param column
85    * @return
86    */
87   private Color findColour(char symbol, int column)
88   {
89     if (getHmm() == null || Comparison.isGap(symbol))
90     {
91       return Color.white;
92     }
93     if (Comparison.isGap(hmmSeq.getCharAt(column)))
94     {
95       return INSERTION_COLOUR;
96     }
97     if (Character.isLowerCase(symbol))
98     {
99       symbol = Character.toUpperCase(symbol);
100     }
101
102     final double prob = getHmm().getMatchEmissionProbability(column,
103             symbol);
104
105     Float freq = 0f;
106
107     if (!frequencies.containsKey(symbol))
108     {
109       return Color.WHITE;
110     }
111     else
112     {
113       freq = frequencies.get(symbol);
114     }
115
116     /*
117      * Orange if match emission probability is less than background probability
118      */
119     double infoRatio = prob / freq.floatValue();
120     Color colour = Color.ORANGE;
121     if (infoRatio >= 1)
122     {
123       /*
124        * log-scale graduated shade of blue if prob is greater than background  
125        */
126       float infoLog = (float) Math.log(infoRatio);
127       colour = ColorUtils.getGraduatedColour(infoLog, 0, Color.WHITE,
128               getMaxInformationScore(), Color.blue);
129     }
130
131     return colour;
132   }
133
134   /**
135    * Answers the maximum possible value of information score (log ratio), for
136    * use in scaling a graduated colour range
137    * 
138    * @return
139    */
140   abstract float getMaxInformationScore();
141
142   /**
143    * Answers a new colour scheme instance based on the HMM of the first sequence
144    * in ac that has an HMM
145    */
146   @Override
147   public ColourSchemeI getInstance(AnnotatedCollectionI ac,
148           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
149   {
150     return newInstance(ac);
151   }
152
153   /**
154    * Answers a new instance of the colour scheme for the given HMM
155    * 
156    * @param ac
157    * @return
158    */
159   protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac);
160
161   @Override
162   public boolean isSimple()
163   {
164     return false;
165   }
166
167   /**
168    * Answers true if the sequence collection has an HMM consensus sequence, else
169    * false
170    */
171   @Override
172   public boolean isApplicableTo(AnnotatedCollectionI ac)
173   {
174     return ac.getHmmConsensus() != null;
175   }
176
177   protected Map<Character, Float> getFrequencies()
178   {
179     return frequencies;
180   }
181
182   protected void setFrequencies(Map<Character, Float> frequencies)
183   {
184     this.frequencies = frequencies;
185   }
186
187   protected HiddenMarkovModel getHmm()
188   {
189     return hmm;
190   }
191
192   protected SequenceI getHmmSequence()
193   {
194     return hmmSeq;
195   }
196 }