JAL-2629 InformationThread.findOrCreateAnnotation and related updates for a more...
[jalview.git] / src / jalview / workers / InformationThread.java
1 package jalview.workers;
2
3 import jalview.analysis.AAFrequency;
4 import jalview.api.AlignViewportI;
5 import jalview.api.AlignmentViewPanel;
6 import jalview.datamodel.AlignmentAnnotation;
7 import jalview.datamodel.AlignmentI;
8 import jalview.datamodel.Annotation;
9 import jalview.datamodel.HiddenMarkovModel;
10 import jalview.datamodel.ProfilesI;
11 import jalview.datamodel.SequenceGroup;
12 import jalview.datamodel.SequenceI;
13 import jalview.util.MessageManager;
14
15 import java.util.List;
16
17 /**
18  * This class calculates HMM Information Content annotations, based on any HMM
19  * consensus sequences and their HMM models. HMM consensus sequences may be
20  * present for the whole alignment, or subgroups of it.
21  *
22  */
23 public class InformationThread extends AlignCalcWorker
24 {
25   public static final String HMM_CALC_ID = "HMM";
26
27   private float max = 0f;
28
29   /**
30    * Constructor
31    * 
32    * @param alignViewport
33    * @param alignPanel
34    */
35   public InformationThread(AlignViewportI alignViewport,
36           AlignmentViewPanel alignPanel)
37   {
38     super(alignViewport, alignPanel);
39   }
40
41   /**
42    * Recomputes Information annotations for any HMM consensus sequences (for
43    * alignment and/or groups)
44    */
45   @Override
46   public void run()
47   {
48     if (calcMan.isPending(this))
49     {
50       return;
51     }
52     calcMan.notifyStart(this);
53     // long started = System.currentTimeMillis();
54
55     try
56     {
57       if (calcMan.isPending(this))
58       {
59         // another instance of this is waiting to run
60         calcMan.workerComplete(this);
61         return;
62       }
63       while (!calcMan.notifyWorking(this))
64       {
65         // another thread in progress, wait my turn
66         try
67         {
68           if (ap != null)
69           {
70             ap.paintAlignment(false, false);
71           }
72           Thread.sleep(200);
73         } catch (Exception ex)
74         {
75           ex.printStackTrace();
76         }
77       }
78       if (alignViewport.isClosed())
79       {
80         abortAndDestroy();
81         return;
82       }
83       AlignmentI alignment = alignViewport.getAlignment();
84
85       int aWidth = alignment == null ? -1 : alignment.getWidth();
86
87       if (aWidth < 0)
88       {
89         calcMan.workerComplete(this);
90         return;
91       }
92
93       computeProfiles(alignment);
94       updateAnnotation();
95
96       if (ap != null)
97       {
98         ap.adjustAnnotationHeight();
99         ap.paintAlignment(true, true);
100       }
101     } catch (OutOfMemoryError error)
102     {
103       calcMan.disableWorker(this);
104       ap.raiseOOMWarning("calculating information", error);
105     } finally
106     {
107       calcMan.workerComplete(this);
108     }
109   }
110
111   /**
112    * Computes HMM profiles for any HMM consensus sequences (for alignment or
113    * subgroups). Any alignment profile computed is stored on the viewport, any
114    * group profile computed is stored on the respective sequence group.
115    * 
116    * @param alignment
117    * @see AlignViewportI#setHmmProfiles(ProfilesI)
118    */
119   protected void computeProfiles(AlignmentI alignment)
120   {
121     int width = alignment.getWidth();
122
123     /*
124      * alignment HMM profile
125      */
126     SequenceI seq = alignment.getHmmConsensus();
127     if (seq != null)
128     {
129       HiddenMarkovModel hmm = seq.getHMM();
130       ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
131               0, width, true, alignViewport.isIgnoreBelowBackground(),
132               alignViewport.isInfoLetterHeight());
133       alignViewport.setHmmProfiles(hmmProfiles);
134     }
135
136     /*
137      * group HMM profiles
138      */
139     List<SequenceGroup> groups = alignment.getGroups();
140     for (SequenceGroup group : groups)
141     {
142       seq = group.getHmmConsensus();
143       if (seq != null)
144       {
145         HiddenMarkovModel hmm = seq.getHMM();
146         ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
147                 0, width, true, group.isIgnoreBelowBackground(),
148                 group.isUseInfoLetterHeight());
149         group.setHmmProfiles(hmmProfiles);
150       }
151     }
152   }
153
154   /**
155    * gets the sequences on the alignment on the viewport.
156    * 
157    * @return
158    */
159   protected SequenceI[] getSequences()
160   {
161     return alignViewport.getAlignment().getSequencesArray();
162   }
163
164   /**
165    * Get the Gap annotation for the alignment
166    * 
167    * @return
168    */
169   protected AlignmentAnnotation getGapAnnotation()
170   {
171     return alignViewport.getOccupancyAnnotation();
172   }
173
174   /**
175    * Computes Information Content annotation for any HMM consensus sequences
176    * (for alignment or groups), and updates (or adds) the annotation to the
177    * sequence and the alignment
178    */
179   @Override
180   public void updateAnnotation()
181   {
182     AlignmentI alignment = alignViewport.getAlignment();
183
184     this.max = 0f;
185
186     /*
187      * annotation for alignment HMM consensus if present
188      */
189     SequenceI hmmSeq = alignment.getHmmConsensus();
190     ProfilesI profile = alignViewport.getHmmProfiles();
191     updateInformationAnnotation(hmmSeq, profile, null);
192
193     /*
194      * annotation for group HMM consensus if present
195      */
196     for (SequenceGroup group : alignment.getGroups())
197     {
198       hmmSeq = group.getHmmConsensus();
199       ProfilesI profiles = group.getHmmProfiles();
200       updateInformationAnnotation(hmmSeq, profiles, group);
201     }
202
203     /*
204      * todo: this.max is not used, but acquires the maximum value of
205      * information in any of the annotations; set this as graphMax in all
206      * annotations to have them all scaled the same
207      */
208   }
209
210   /**
211    * Updates (and first constructs if necessary) an HMM Profile information
212    * content annotation for a sequence. The <code>group</code> argument is null
213    * for the whole alignment annotation, not null for a subgroup annotation.
214    * 
215    * @param seq
216    * @param profile
217    * @param group
218    * @return
219    */
220   protected AlignmentAnnotation updateInformationAnnotation(SequenceI seq,
221           ProfilesI profile, SequenceGroup group)
222   {
223     if (seq == null || profile == null)
224     {
225       return null;
226     }
227
228     AlignmentAnnotation ann = findOrCreateAnnotation(seq, group);
229
230     seq.addAlignmentAnnotation(ann);
231
232     max = AAFrequency.completeInformation(ann, profile,
233             profile.getStartColumn(), profile.getEndColumn() + 1);
234
235     return ann;
236   }
237
238   /**
239    * A helper method that first searches for the HMM annotation that matches the
240    * group reference (null for the whole alignment annotation). If found, its
241    * sequence reference is updated to the given sequence (the recomputed HMM
242    * consensus sequence). If not found, it is created. This supports both
243    * creating the annotation the first time hmmbuild is run, and updating it if
244    * hmmbuild is re-run.
245    * 
246    * @param seq
247    * @param group
248    * @return
249    */
250   AlignmentAnnotation findOrCreateAnnotation(SequenceI seq,
251           SequenceGroup group)
252   {
253     /*
254      * can't use Alignment.findOrCreateAnnotation here because we 
255      * want to update, rather than match on, the sequence ref
256      */
257     AlignmentAnnotation info = null;
258
259     AlignmentI alignment = alignViewport.getAlignment();
260     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
261     if (anns != null)
262     {
263       for (AlignmentAnnotation ann : anns)
264       {
265         if (HMM_CALC_ID.equals(ann.getCalcId()) && group == ann.groupRef)
266         {
267           info = ann;
268           info.setSequenceRef(seq);
269           break;
270         }
271       }
272     }
273
274     if (info == null)
275     {
276       int aWidth = alignment.getWidth();
277       String desc = MessageManager
278               .getString("label.information_description");
279       float graphMax = 6.52f; // todo where does this value derive from?
280       info = new AlignmentAnnotation(seq.getName(), desc,
281               new Annotation[aWidth], 0f, graphMax,
282               AlignmentAnnotation.BAR_GRAPH);
283       info.setCalcId(HMM_CALC_ID);
284       info.setSequenceRef(seq);
285       info.groupRef = group;
286       info.hasText = true;
287       alignment.addAnnotation(info);
288     }
289
290     return info;
291   }
292 }