JAL-2717 JAL-2668 fixes to HMMER colour scheme display names, enabled state, test...
[jalview.git] / src / jalview / schemes / HMMERAlignmentColourScheme.java
index 59c0317..8e7aef7 100644 (file)
@@ -10,55 +10,82 @@ import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
+/**
+ * A colour scheme based on a selected Hidden Markov Model. The colour is
+ * <ul>
+ * <li>white for a gap</li>
+ * <li>red for an insertion</li>
+ * <li>orange for negative information content</li>
+ * <li>white to blue for increasing information content</li>
+ * </ul>
+ * where information content is the log ratio
+ * 
+ * <pre>
+ *   log(profile match emission probability / residue background probability>
+ * </pre>
+ * 
+ * using the alignment's background frequencies for residues.
+ * 
+ * @author tzvanaalten
+ *
+ */
 public class HMMERAlignmentColourScheme extends ResidueColourScheme
 {
-  Map<Character, Double> backgroundFrequencies = new HashMap<>();
+  /*
+   * the ratio, for each symbol, of its frequency to total symbol count
+   */
+  Map<Character, Double> frequency = new HashMap<>();
 
-  Double maxLLR;
+  float logTotalCount;
 
   HiddenMarkovModel hmm;
 
-  boolean peptideSpecific;
-
-  boolean nucleotideSpecific;
-
-  public HMMERAlignmentColourScheme(HiddenMarkovModel markov)
+  /**
+   * Constructor given a Hidden Markov Model
+   * 
+   * @param sg
+   * 
+   * @param markov
+   */
+  public HMMERAlignmentColourScheme(AnnotatedCollectionI sg,
+          HiddenMarkovModel markov)
   {
     hmm = markov;
+    countFrequencies(sg);
   }
 
+  /**
+   * Default constructor (required by ColourSchemes.loadColourSchemes)
+   */
   public HMMERAlignmentColourScheme()
   {
-
   }
 
   @Override
   public Color findColour(char symbol, int position, SequenceI seq,
           String consensusResidue, float pid)
   {
-    if (hmm == null)
-    {
-      return Color.white;
-    }
     return findColour(symbol, position);
   }
 
   /**
-   * Returns the colour at a particular symbol at a column in the alignment.
+   * Returns the colour at a particular symbol at a column in the alignment:
+   * <ul>
+   * <li>white for a gap</li>
+   * <li>red for an insertion</li>
+   * <li>orange for negative information content</li>
+   * <li>white to blue for increasing information content</li>
+   * </ul>
    * 
    * @param symbol
-   * @param position
-   * @return Red for an insertion, white for a gap, orange for a negative
-   *         information content, white to blue for increasing information
-   *         content.
+   * @param column
+   * @return
    */
-  private Color findColour(char symbol, int position)
+  private Color findColour(char symbol, int column)
   {
-
-    if (Comparison.isGap(symbol))
+    if (hmm == null || Comparison.isGap(symbol))
     {
       return Color.white;
     }
@@ -66,9 +93,8 @@ public class HMMERAlignmentColourScheme extends ResidueColourScheme
     {
       symbol = Character.toUpperCase(symbol);
     }
-    Double prob;
-    prob = hmm.getMatchEmissionProbability(position, symbol);
-    Double freq = backgroundFrequencies.get(symbol);
+    double prob = hmm.getMatchEmissionProbability(column, symbol);
+    Double freq = frequency.get(symbol);
     if (freq == null)
     {
       return Color.white;
@@ -77,18 +103,16 @@ public class HMMERAlignmentColourScheme extends ResidueColourScheme
     {
       return new Color(230, 0, 0);
     }
-    Double value = Math.log(prob / freq);
+    double value = Math.log(prob / freq.doubleValue());
     Color colour = null;
     if (value > 0)
     {
-
-      colour = ColorUtils.getGraduatedColour(value.floatValue(), 0,
-              Color.WHITE, maxLLR.floatValue(), Color.blue);
+      colour = ColorUtils.getGraduatedColour((float) value, 0,
+              Color.WHITE, logTotalCount, Color.blue);
     }
     else if (value < 0)
     {
       return Color.ORANGE;
-
     }
     return colour;
   }
@@ -97,55 +121,29 @@ public class HMMERAlignmentColourScheme extends ResidueColourScheme
   public void alignmentChanged(AnnotatedCollectionI collection,
           Map<SequenceI, SequenceCollectionI> hiddenReps)
   {
-    List<SequenceI> seqs = collection.getSequences();
-    for (SequenceI seq : seqs)
-    {
-      if (seq.isHMMConsensusSequence())
-      {
-        hmm = seq.getHMM();
-        break;
-      }
-    }
-
-    count(collection);
-
+    /*
+     * ? no need to do anything if alignment is adjusted
+     * since findColour() handles everything 
+     */
   }
 
   @Override
   public ColourSchemeI getInstance(AnnotatedCollectionI sg,
           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
   {
-    HiddenMarkovModel markov = null;
-    List<SequenceI> seqs = sg.getSequences();
-    for (SequenceI seq : seqs)
-    {
-      if (seq.getHMM() != null)
-      {
-        markov = seq.getHMM();
-        break;
-      }
-    }
-
-    count(sg);
-
-    HMMERAlignmentColourScheme colour = new HMMERAlignmentColourScheme(
-            markov);
-    return colour;
-
+    return new HMMERAlignmentColourScheme(sg, hmm);
   }
 
   @Override
   public boolean isApplicableTo(AnnotatedCollectionI ac)
   {
-    return true;
-
+    return !ac.getHMMConsensusSequences().isEmpty();
   }
 
   @Override
   public String getSchemeName()
   {
-
-    return JalviewColourScheme.HMMERA.name();
+    return JalviewColourScheme.HMMERA.toString();
   }
 
   @Override
@@ -154,29 +152,22 @@ public class HMMERAlignmentColourScheme extends ResidueColourScheme
     return false;
   }
 
-  public void count(AnnotatedCollectionI sg)
+  /**
+   * Counts and stores the relatively frequency of every residue in the
+   * alignment
+   * 
+   * @param sg
+   */
+  public void countFrequencies(AnnotatedCollectionI sg)
   {
-    ResidueCount counts = new ResidueCount();
-    for (SequenceI seq : sg.getSequences())
-    {
-      for (int i = 0; i < seq.getLength(); i++)
-      {
-        if (!Comparison.isGap(seq.getCharAt(i)))
-        {
-          counts.add(seq.getCharAt(i));
-        }
-      }
-    }
-
-    int total = counts.getTotalCount();
+    ResidueCount counts = new ResidueCount(sg.getSequences());
+    int total = counts.getTotalCount(); // excludes gaps
 
     for (char symbol : counts.getSymbolCounts().symbols)
     {
-      double count = Double.valueOf(counts.getCount(symbol))
-              / Double.valueOf(total);
-      backgroundFrequencies.put(symbol, count);
+      double freq = counts.getCount(symbol) / (double) total;
+      frequency.put(symbol, freq);
     }
-    maxLLR = Math.log(total);
+    logTotalCount = (float) Math.log(total);
   }
 }
-