import jalview.datamodel.Annotation;
import jalview.datamodel.HiddenMarkovModel;
import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
-import jalview.renderer.ResidueShaderI;
+import jalview.util.MessageManager;
+import java.util.ArrayList;
import java.util.List;
+/**
+ * This class calculates HMM Information Content annotations, based on any HMM
+ * consensus sequences and their HMM models. HMM consensus sequences may be
+ * present for the whole alignment, or subgroups of it.
+ *
+ */
public class InformationThread extends AlignCalcWorker
{
+ public static final String HMM_CALC_ID = "HMM";
+ /**
+ * Constructor
+ *
+ * @param alignViewport
+ * @param alignPanel
+ */
public InformationThread(AlignViewportI alignViewport,
- AlignmentViewPanel alignPanel)
- {
- super(alignViewport, alignPanel);
- }
-
- @Override
- public void run()
- {
- if (calcMan.isPending(this))
- {
- return;
- }
- calcMan.notifyStart(this);
- long started = System.currentTimeMillis();
-
- List<AlignmentAnnotation> information = getInformationAnnotations();
- try
- {
- if ((information == null) || calcMan.isPending(this))
- {
- calcMan.workerComplete(this);
- return;
- }
- while (!calcMan.notifyWorking(this))
- {
- // System.err.println("Thread
- // (Information"+Thread.currentThread().getName()+") Waiting around.");
- try
- {
- if (ap != null)
- {
- ap.paintAlignment(false);
- }
- Thread.sleep(200);
- } catch (Exception ex)
- {
- ex.printStackTrace();
- }
- }
- if (alignViewport.isClosed())
- {
- abortAndDestroy();
- return;
- }
- AlignmentI alignment = alignViewport.getAlignment();
-
- int aWidth = -1;
-
- if (alignment == null || (aWidth = alignment.getWidth()) < 0)
- {
- calcMan.workerComplete(this);
- return;
- }
+ AlignmentViewPanel alignPanel)
+ {
+ super(alignViewport, alignPanel);
+ }
- eraseInformation(aWidth);
- computeInformation(alignment);
- updateResultAnnotation(true);
+ @Override
+ public String getCalcName()
+ {
+ return "Information";
+ }
- if (ap != null)
- {
- ap.paintAlignment(true);
- }
- } catch (OutOfMemoryError error)
- {
- calcMan.disableWorker(this);
- ap.raiseOOMWarning("calculating information", error);
- } finally
+ /**
+ * Recomputes Information annotations for any HMM consensus sequences (for
+ * alignment and/or groups)
+ */
+ @Override
+ public void run()
+ {
+ if (alignViewport.getAlignment().getHmmSequences().isEmpty())
{
- calcMan.workerComplete(this);
- }
-
-
-
+ return;
}
-
- /**
- * Clear out any existing information annotations
- *
- * @param aWidth
- * the width (number of columns) of the annotated alignment
- */
- protected void eraseInformation(int aWidth)
+ if (alignViewport.isClosed())
{
+ abortAndDestroy();
+ return;
+ }
- List<AlignmentAnnotation> information = getInformationAnnotations();
- for (AlignmentAnnotation info : information)
+ AlignmentI alignment = alignViewport.getAlignment();
+ int aWidth = alignment == null ? -1 : alignment.getWidth();
+ if (aWidth < 0)
{
- info.annotations = new Annotation[aWidth];
- }
+ return;
}
+ /*
+ * compute information profiles for any HMM consensus sequences
+ * for the alignment or sub-groups
+ */
+ computeProfiles(alignment);
- /**
- * @param alignment
+ /*
+ * construct the corresponding annotations
*/
- protected void computeInformation(AlignmentI alignment)
- {
+ updateAnnotation();
- int width = alignment.getWidth();
- List<SequenceI> hmmSeqs = alignment.getHMMConsensusSequences();
- int index = 0;
- for (SequenceI seq : hmmSeqs)
+ if (ap != null)
{
- HiddenMarkovModel hmm = seq.getHMM();
- ProfilesI hinformation = AAFrequency.calculateInformation(hmm, width,
- 0, width, true, alignViewport.isIgnoreBelowBackground());
- alignViewport.setSequenceInformationHash(hinformation, index);
- // setColourSchemeInformation(hinformation);
- index++;
- }
+ ap.adjustAnnotationHeight();
+ ap.paintAlignment(true, true);
}
+ }
- /**
- * @return
+ /**
+ * Computes HMM profiles for any HMM consensus sequences (for alignment or
+ * subgroups). Any alignment profile computed is stored on the viewport, any
+ * group profile computed is stored on the respective sequence group.
+ *
+ * @param alignment
+ * @see AlignViewportI#setHmmProfiles(ProfilesI)
+ */
+ protected void computeProfiles(AlignmentI alignment)
+ {
+ int width = alignment.getWidth();
+
+ /*
+ * alignment HMM profile
*/
- protected SequenceI[] getSequences()
+ List<SequenceI> seqs = alignment.getHmmSequences();
+ if (!seqs.isEmpty())
{
- return alignViewport.getAlignment().getSequencesArray();
+ HiddenMarkovModel hmm = seqs.get(0).getHMM();
+ ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
+ 0, width, alignViewport.isIgnoreBelowBackground(),
+ alignViewport.isInfoLetterHeight());
+ alignViewport.setHmmProfiles(hmmProfiles);
}
- /**
- * @param hinformation
- */
- protected void setColourSchemeInformation(ProfilesI information)
+ /*
+ * group HMM profiles
+ */
+ List<SequenceGroup> groups = alignment.getGroups();
+ for (SequenceGroup group : groups)
{
- ResidueShaderI cs = alignViewport.getResidueShading();
- if (cs != null)
+ seqs = group.getHmmSequences();
+ if (!seqs.isEmpty())
{
- cs.setInformation(information);
+ HiddenMarkovModel hmm = seqs.get(0).getHMM();
+ ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
+ 0, width, group.isIgnoreBelowBackground(),
+ group.isUseInfoLetterHeight());
+ group.setHmmProfiles(hmmProfiles);
}
}
+ }
- /**
- * Get the Information annotation for the alignment
+ /**
+ * gets the sequences on the alignment on the viewport.
*
* @return
*/
- protected List<AlignmentAnnotation> getInformationAnnotations()
- {
- return alignViewport.getInformationAnnotations();
- }
-
- /**
- * Get the Gap annotation for the alignment
- *
- * @return
- */
- protected AlignmentAnnotation getGapAnnotation()
- {
- return alignViewport.getAlignmentGapAnnotation();
- }
+ protected SequenceI[] getSequences()
+ {
+ return alignViewport.getAlignment().getSequencesArray();
+ }
- /**
- * update the information annotation from the sequence profile data using
- * current visualization settings.
+ /**
+ * Get the Gap annotation for the alignment
+ *
+ * @return
*/
- @Override
- public void updateAnnotation()
- {
+ protected AlignmentAnnotation getGapAnnotation()
+ {
+ return alignViewport.getAlignmentGapAnnotation();
+ }
- updateResultAnnotation(false);
- }
+ /**
+ * Computes Information Content annotation for any HMM consensus sequences
+ * (for alignment or groups), and updates (or adds) the annotation to the
+ * sequence and the alignment
+ */
+ @Override
+ public void updateAnnotation()
+ {
+ AlignmentI alignment = alignViewport.getAlignment();
+ float maxInformation = 0f;
+ List<AlignmentAnnotation> infos = new ArrayList<>();
- public void updateResultAnnotation(boolean immediate)
+ /*
+ * annotation for alignment HMM consensus if present
+ */
+ List<SequenceI> hmmSeqs = alignment.getHmmSequences();
+ if (!hmmSeqs.isEmpty())
{
- List<AlignmentAnnotation> annots = getInformationAnnotations();
- int index = 0;
- for (AlignmentAnnotation information : annots)
+ ProfilesI profile = alignViewport.getHmmProfiles();
+ float m = updateInformationAnnotation(hmmSeqs.get(0), profile, null,
+ infos);
+ maxInformation = Math.max(maxInformation, m);
+ }
+
+ /*
+ * annotation for group HMM consensus if present
+ */
+ for (SequenceGroup group : alignment.getGroups())
{
- ProfilesI hinformation = (ProfilesI) getSequenceInformation(index);
- if (immediate || !calcMan.isWorking(this) && information != null
- && hinformation != null)
+ hmmSeqs = group.getHmmSequences();
+ if (!hmmSeqs.isEmpty())
{
- deriveInformation(information, hinformation);
- }
- index++;
+ ProfilesI profiles = group.getHmmProfiles();
+ float m = updateInformationAnnotation(hmmSeqs.get(0), profiles,
+ group, infos);
+ maxInformation = Math.max(maxInformation, m);
}
}
- /**
- * Convert the computed information data into the desired annotation for
- * display.
+ /*
+ * maxInformation holds the maximum value of information score;
+ * set this as graphMax in all annotations to scale them all the same
+ */
+ for (AlignmentAnnotation ann : infos)
+ {
+ ann.graphMax = maxInformation;
+ }
+ }
+
+ /**
+ * Updates (and first constructs if necessary) an HMM Profile information
+ * content annotation for a sequence. The <code>group</code> argument is null
+ * for the whole alignment annotation, not null for a subgroup annotation. The
+ * updated annotation is added to the <code>infos</code> list. Answers the
+ * maximum information content value of any annotation (for use as a scaling
+ * factor for display).
*
- * @param informationAnnotation
- * the annotation to be populated
- * @param hinformation
- * the computed information data
+ * @param seq
+ * @param profile
+ * @param group
+ * @param infos
+ * @return
*/
- protected void deriveInformation(
- AlignmentAnnotation informationAnnotation, ProfilesI hinformation)
+ protected float updateInformationAnnotation(SequenceI seq,
+ ProfilesI profile, SequenceGroup group,
+ List<AlignmentAnnotation> infos)
+ {
+ if (seq == null || profile == null)
{
-
- long nseq = getSequences().length;
- AAFrequency.completeInformation(informationAnnotation, hinformation,
- hinformation.getStartColumn(), hinformation.getEndColumn() + 1,
- alignViewport.isIgnoreBelowBackground(),
- alignViewport.isShowHMMSequenceLogo(), nseq);
+ return 0f;
}
+ AlignmentAnnotation ann = findOrCreateAnnotation(seq, group);
+
+ seq.addAlignmentAnnotation(ann);
+ infos.add(ann);
+ float max = AAFrequency.completeInformation(ann, profile,
+ profile.getStartColumn(), profile.getEndColumn() + 1);
- /**
- * Get the information data stored on the viewport.
+ return max;
+ }
+
+ /**
+ * A helper method that first searches for the HMM annotation that matches the
+ * group reference (null for the whole alignment annotation). If found, its
+ * sequence reference is updated to the given sequence (the recomputed HMM
+ * consensus sequence). If not found, it is created. This supports both
+ * creating the annotation the first time hmmbuild is run, and updating it if
+ * hmmbuild is re-run.
*
+ * @param seq
+ * @param group
* @return
*/
- protected Object getSequenceInformation(int index)
+ AlignmentAnnotation findOrCreateAnnotation(SequenceI seq,
+ SequenceGroup group)
+ {
+ /*
+ * can't use Alignment.findOrCreateAnnotation here because we
+ * want to update, rather than match on, the sequence ref
+ */
+ AlignmentAnnotation info = null;
+
+ AlignmentI alignment = alignViewport.getAlignment();
+ AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+ if (anns != null)
{
- // TODO convert ComplementInformationThread to use Profile
- return alignViewport.getSequenceInformationHash(index);
+ for (AlignmentAnnotation ann : anns)
+ {
+ if (HMM_CALC_ID.equals(ann.getCalcId()) && group == ann.groupRef)
+ {
+ info = ann;
+ info.setSequenceRef(seq);
+ info.label = seq.getName(); // in case group name changed!
+ break;
+ }
+ }
}
- }
-
-
-
+ if (info == null)
+ {
+ int aWidth = alignment.getWidth();
+ String desc = MessageManager
+ .getString("label.information_description");
+ float graphMax = 6.52f; // todo where does this value derive from?
+ info = new AlignmentAnnotation(seq.getName(), desc,
+ new Annotation[aWidth], 0f, graphMax,
+ AlignmentAnnotation.BAR_GRAPH);
+ info.setCalcId(HMM_CALC_ID);
+ info.setSequenceRef(seq);
+ info.groupRef = group;
+ info.hasText = true;
+ alignment.addAnnotation(info);
+ }
+ return info;
+ }
+}