package jalview.workers; import jalview.analysis.AAFrequency; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.HiddenMarkovModel; import jalview.datamodel.ProfilesI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; 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 String getCalcName() { return "Information"; } /** * Recomputes Information annotations for any HMM consensus sequences (for * alignment and/or groups) */ @Override public void run() { if (alignViewport.getAlignment().getHmmSequences().isEmpty()) { return; } if (alignViewport.isClosed()) { abortAndDestroy(); return; } AlignmentI alignment = alignViewport.getAlignment(); int aWidth = alignment == null ? -1 : alignment.getWidth(); if (aWidth < 0) { return; } /* * compute information profiles for any HMM consensus sequences * for the alignment or sub-groups */ computeProfiles(alignment); /* * construct the corresponding annotations */ updateAnnotation(); if (ap != null) { ap.adjustAnnotationHeight(); ap.paintAlignment(true, true); } } /** * 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 */ List seqs = alignment.getHmmSequences(); if (!seqs.isEmpty()) { HiddenMarkovModel hmm = seqs.get(0).getHMM(); ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width, 0, width, alignViewport.isIgnoreBelowBackground(), alignViewport.isInfoLetterHeight()); alignViewport.setHmmProfiles(hmmProfiles); } /* * group HMM profiles */ List groups = alignment.getGroups(); for (SequenceGroup group : groups) { seqs = group.getHmmSequences(); if (!seqs.isEmpty()) { HiddenMarkovModel hmm = seqs.get(0).getHMM(); ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width, 0, width, group.isIgnoreBelowBackground(), group.isUseInfoLetterHeight()); group.setHmmProfiles(hmmProfiles); } } } /** * gets the sequences on the alignment on the viewport. * * @return */ protected SequenceI[] getSequences() { return alignViewport.getAlignment().getSequencesArray(); } /** * Get the Gap annotation for the alignment * * @return */ protected AlignmentAnnotation getGapAnnotation() { return alignViewport.getAlignmentGapAnnotation(); } /** * 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 infos = new ArrayList<>(); /* * annotation for alignment HMM consensus if present */ List hmmSeqs = alignment.getHmmSequences(); if (!hmmSeqs.isEmpty()) { 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()) { hmmSeqs = group.getHmmSequences(); if (!hmmSeqs.isEmpty()) { ProfilesI profiles = group.getHmmProfiles(); float m = updateInformationAnnotation(hmmSeqs.get(0), profiles, group, infos); maxInformation = Math.max(maxInformation, m); } } /* * 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 group argument is null * for the whole alignment annotation, not null for a subgroup annotation. The * updated annotation is added to the infos list. Answers the * maximum information content value of any annotation (for use as a scaling * factor for display). * * @param seq * @param profile * @param group * @param infos * @return */ protected float updateInformationAnnotation(SequenceI seq, ProfilesI profile, SequenceGroup group, List infos) { if (seq == null || profile == null) { return 0f; } AlignmentAnnotation ann = findOrCreateAnnotation(seq, group); seq.addAlignmentAnnotation(ann); infos.add(ann); float max = AAFrequency.completeInformation(ann, profile, profile.getStartColumn(), profile.getEndColumn() + 1); 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 */ 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) { 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; } }