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