Merge branch 'features/JAL-2393customMatrices' into develop
[jalview.git] / test / jalview / analysis / scoremodels / FeatureDistanceModelTest.java
  */
 package jalview.analysis.scoremodels;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import jalview.api.analysis.SimilarityParamsI;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
 import jalview.gui.JvOptionPane;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
+import jalview.math.MatrixI;
 
 import java.util.Arrays;
 
@@ -34,7 +43,7 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-public class FeatureScoreModelTest
+public class FeatureDistanceModelTest
 {
 
   @BeforeClass(alwaysRun = true)
@@ -52,6 +61,17 @@ public class FeatureScoreModelTest
 
   int[] sf3 = new int[] { -1, -1, -1, -1, -1, -1, 76, 77 };
 
+  /**
+   * <pre>
+   * Load test alignment and add features to sequences: 
+   *      FER1_MESCR FER1_SPIOL FER3_RAPSA FER1_MAIZE 
+   *  sf1     X          X          X  
+   *  sf2                X                     X 
+   *  sf3                                      X
+   * </pre>
+   * 
+   * @return
+   */
   public AlignFrame getTestAlignmentFrame()
   {
     AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(
@@ -85,7 +105,7 @@ public class FeatureScoreModelTest
     alf.getFeatureRenderer().findAllFeatures(true);
     Assert.assertEquals(alf.getFeatureRenderer().getDisplayedFeatureTypes()
             .size(), 3, "Number of feature types");
-    Assert.assertTrue(alf.getCurrentView().areFeaturesDisplayed());
+    assertTrue(alf.getCurrentView().areFeaturesDisplayed());
     return alf;
   }
 
@@ -93,15 +113,17 @@ public class FeatureScoreModelTest
   public void testFeatureScoreModel() throws Exception
   {
     AlignFrame alf = getTestAlignmentFrame();
-    FeatureScoreModel fsm = new FeatureScoreModel();
-    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
+    assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
             .getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
-    float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
-            true));
-    Assert.assertTrue(dm[0][2] == 0f,
+
+    MatrixI dm = fsm.findDistances(
+            alf.getViewport().getAlignmentView(true),
+            SimilarityParams.Jalview);
+    assertEquals(dm.getValue(0, 2), 0d,
             "FER1_MESCR (0) should be identical with RAPSA (2)");
-    Assert.assertTrue(dm[0][1] > dm[0][2],
+    assertTrue(dm.getValue(0, 1) > dm.getValue(0, 2),
             "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)");
   }
 
@@ -111,15 +133,16 @@ public class FeatureScoreModelTest
     AlignFrame alf = getTestAlignmentFrame();
     // hiding first two columns shouldn't affect the tree
     alf.getViewport().hideColumns(0, 1);
-    FeatureScoreModel fsm = new FeatureScoreModel();
-    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
+    assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
             .getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
-    float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
-            true));
-    Assert.assertTrue(dm[0][2] == 0f,
+    MatrixI dm = fsm.findDistances(
+            alf.getViewport().getAlignmentView(true),
+            SimilarityParams.Jalview);
+    assertEquals(dm.getValue(0, 2), 0d,
             "FER1_MESCR (0) should be identical with RAPSA (2)");
-    Assert.assertTrue(dm[0][1] > dm[0][2],
+    assertTrue(dm.getValue(0, 1) > dm.getValue(0, 2),
             "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)");
   }
 
@@ -130,21 +153,24 @@ public class FeatureScoreModelTest
     // hide columns and check tree changes
     alf.getViewport().hideColumns(3, 4);
     alf.getViewport().hideColumns(0, 1);
-    FeatureScoreModel fsm = new FeatureScoreModel();
-    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
+    assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
             .getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
-    float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
-            true));
-    Assert.assertTrue(
-            dm[0][2] == 0f,
+    MatrixI dm = fsm.findDistances(
+            alf.getViewport().getAlignmentView(true),
+            SimilarityParams.Jalview);
+    assertEquals(
+            dm.getValue(0, 2),
+            0d,
             "After hiding last two columns FER1_MESCR (0) should still be identical with RAPSA (2)");
-    Assert.assertTrue(
-            dm[0][1] == 0f,
+    assertEquals(
+            dm.getValue(0, 1),
+            0d,
             "After hiding last two columns FER1_MESCR (0) should now also be identical with SPIOL (1)");
     for (int s = 0; s < 3; s++)
     {
-      Assert.assertTrue(dm[s][3] > 0f, "After hiding last two columns "
+      assertTrue(dm.getValue(s, 3) > 0d, "After hiding last two columns "
               + alf.getViewport().getAlignment().getSequenceAt(s).getName()
               + "(" + s + ") should still be distinct from FER1_MAIZE (3)");
     }
@@ -165,7 +191,7 @@ public class FeatureScoreModelTest
     SequenceFeature sf = null;
     sf = new SequenceFeature("disulphide bond", "", 2, 5, Float.NaN, "");
     aseq.addSequenceFeature(sf);
-    Assert.assertTrue(sf.isContactFeature());
+    assertTrue(sf.isContactFeature());
     af.refreshFeatureUI(true);
     af.getFeatureRenderer().setAllVisible(Arrays.asList("disulphide bond"));
     Assert.assertEquals(af.getFeatureRenderer().getDisplayedFeatureTypes()
@@ -190,4 +216,135 @@ public class FeatureScoreModelTest
             .size(), 0);
   }
 
+  @Test(groups = { "Functional" })
+  public void testFindDistances() throws Exception
+  {
+    String seqs = ">s1\nABCDE\n>seq2\nABCDE\n";
+    AlignFrame alf = new FileLoader().LoadFileWaitTillLoaded(seqs,
+            DataSourceType.PASTE);
+    SequenceI s1 = alf.getViewport().getAlignment().getSequenceAt(0);
+    SequenceI s2 = alf.getViewport().getAlignment().getSequenceAt(1);
+
+    /*
+     * set domain and variant features thus:
+     *     ----5
+     *  s1 ddd..
+     *  s1 .vvv.
+     *  s1 ..vvv    
+     *  s2 .ddd. 
+     *  s2 vv..v
+     *  The number of unshared feature types per column is
+     *     20120 (two features of the same type doesn't affect score)
+     *  giving an average (pairwise distance) of 5/5 or 1.0 
+     */
+    s1.addSequenceFeature(new SequenceFeature("domain", null, 1, 3, 0f,
+            null));
+    s1.addSequenceFeature(new SequenceFeature("variant", null, 2, 4, 0f,
+            null));
+    s1.addSequenceFeature(new SequenceFeature("variant", null, 3, 5, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("domain", null, 2, 4, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("variant", null, 1, 2, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("variant", null, 5, 5, 0f,
+            null));
+    alf.setShowSeqFeatures(true);
+    alf.getFeatureRenderer().findAllFeatures(true);
+
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
+    assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+            .getAlignPanel()));
+    alf.selectAllSequenceMenuItem_actionPerformed(null);
+
+    MatrixI distances = fsm.findDistances(alf.getViewport()
+            .getAlignmentView(true), SimilarityParams.Jalview);
+    assertEquals(distances.width(), 2);
+    assertEquals(distances.height(), 2);
+    assertEquals(distances.getValue(0, 0), 0d);
+    assertEquals(distances.getValue(1, 1), 0d);
+
+    assertEquals(distances.getValue(0, 1), 1d,
+            "expected identical pairs. (check normalisation for similarity score)");
+    assertEquals(distances.getValue(1, 0), 1d);
+  }
+
+  /**
+   * Verify computed distances with varying parameter options
+   */
+  @Test(groups = "Functional")
+  public void testFindDistances_withParams()
+  {
+    AlignFrame af = setupAlignmentView();
+    AlignViewport viewport = af.getViewport();
+    AlignmentView view = viewport.getAlignmentView(false);
+
+    FeatureDistanceModel sm = new FeatureDistanceModel();
+    sm.configureFromAlignmentView(af.alignPanel);
+  
+    /*
+     * feature distance model always normalises by region width
+     * gap-gap is always included (but scores zero)
+     * the only variable parameter is 'includeGaps'
+     */
+
+    /*
+     * include gaps
+     * score = 3 + 3 + 0 + 2 + 3 + 2 = 13/6
+     */
+    SimilarityParamsI params = new SimilarityParams(true, true, true, true);
+    MatrixI distances = sm.findDistances(view, params);
+    assertEquals(distances.getValue(0, 0), 0d);
+    assertEquals(distances.getValue(1, 1), 0d);
+    assertEquals(distances.getValue(0, 1), 13d / 6); // should be 13d/6
+    assertEquals(distances.getValue(1, 0), 13d / 6);
+  
+    /*
+     * exclude gaps
+     * score = 3 + 3 + 0 + 0 + 0 + 0 = 6/6
+     */
+    params = new SimilarityParams(true, true, false, true);
+    distances = sm.findDistances(view, params);
+    assertEquals(distances.getValue(0, 1), 6d / 6);// should be 6d/6
+  }
+
+  /**
+   * <pre>
+   * Set up
+   *   column      1 2 3 4 5 6
+   *        seq s1 F R - K - S
+   *        seq s2 F S - - L
+   *   s1 chain    c c   c   c
+   *   s1 domain   d d   d   d
+   *   s2 chain    c c     c
+   *   s2 metal    m m     m
+   *   s2 Pfam     P P     P
+   *      scores:  3 3 0 2 3 2
+   * </pre>
+   * 
+   * @return
+   */
+  protected AlignFrame setupAlignmentView()
+  {
+    /*
+     * for now, using space for gap to match callers of
+     * AlignmentView.getSequenceStrings()
+     * may change this to '-' (with corresponding change to matrices)
+     */
+    SequenceI s1 = new Sequence("s1", "FR K S");
+    SequenceI s2 = new Sequence("s2", "FS  L");
+
+    s1.addSequenceFeature(new SequenceFeature("chain", null, 1, 4, 0f, null));
+    s1.addSequenceFeature(new SequenceFeature("domain", null, 1, 4, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("chain", null, 1, 3, 0f, null));
+    s2.addSequenceFeature(new SequenceFeature("metal", null, 1, 3, 0f, null));
+    s2.addSequenceFeature(new SequenceFeature("Pfam", null, 1, 3, 0f, null));
+    AlignmentI al = new Alignment(new SequenceI[] { s1, s2 });
+    AlignFrame af = new AlignFrame(al, 300, 300);
+    af.setShowSeqFeatures(true);
+    af.getFeatureRenderer().findAllFeatures(true);
+    return af;
+  }
+
 }