JAL-2416 ScoreMatrix.getMatrix() returns a defensive copy to keep the
[jalview.git] / test / jalview / analysis / scoremodels / ScoreMatrixTest.java
index 462edf2..ae5ad5d 100644 (file)
@@ -1,18 +1,77 @@
 package jalview.analysis.scoremodels;
 
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotSame;
 import static org.testng.Assert.assertTrue;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
 
-import java.io.ByteArrayInputStream;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.ScoreMatrixFile;
+import jalview.math.MatrixI;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Arrays;
 
 import org.testng.annotations.Test;
 
 public class ScoreMatrixTest
 {
   @Test(groups = "Functional")
+  public void testConstructor()
+  {
+    // note score matrix does not have to be symmetric (though it should be!)
+    float[][] scores = new float[3][];
+    scores[0] = new float[] { 1f, 2f, 3f };
+    scores[1] = new float[] { 4f, 5f, 6f };
+    scores[2] = new float[] { 7f, 8f, 9f };
+    ScoreMatrix sm = new ScoreMatrix("Test", "ABC".toCharArray(), scores);
+    assertEquals(sm.getSize(), 3);
+    assertArrayEquals(scores, sm.getMatrix());
+    assertEquals(sm.getPairwiseScore('A', 'a'), 1f);
+    assertEquals(sm.getPairwiseScore('b', 'c'), 6f);
+    assertEquals(sm.getPairwiseScore('c', 'b'), 8f);
+    assertEquals(sm.getPairwiseScore('A', 'D'), 0f);
+    assertEquals(sm.getMatrixIndex('c'), 2);
+    assertEquals(sm.getMatrixIndex(' '), -1);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructor_matrixTooSmall()
+  {
+    float[][] scores = new float[2][];
+    scores[0] = new float[] { 1f, 2f };
+    scores[1] = new float[] { 3f, 4f };
+    new ScoreMatrix("Test", "ABC".toCharArray(), scores);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructor_matrixTooBig()
+  {
+    float[][] scores = new float[2][];
+    scores[0] = new float[] { 1f, 2f };
+    scores[1] = new float[] { 3f, 4f };
+    new ScoreMatrix("Test", "A".toCharArray(), scores);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructor_matrixNotSquare()
+  {
+    float[][] scores = new float[2][];
+    scores[0] = new float[] { 1f, 2f };
+    scores[1] = new float[] { 3f };
+    new ScoreMatrix("Test", "AB".toCharArray(), scores);
+  }
+
+  @Test(groups = "Functional")
   public void testBuildSymbolIndex()
   {
     short[] index = ScoreMatrix.buildSymbolIndex("AX-. yxYp".toCharArray());
@@ -63,110 +122,126 @@ public class ScoreMatrixTest
     }
   }
 
-  /**
-   * Test a successful parse of a (small) score matrix file
-   */
   @Test(groups = "Functional")
-  public void testParse()
+  public void testGetMatrix()
   {
-    /*
-     * some messy but valid input data, with comma, space
-     * or tab (or combinations) as score value delimiters
-     */
-    String data = "ScoreMatrix MyTest\n" + "ATU tx-\n"
-            + "1.1,1.2,1.3,1.4, 1.5, 1.6, 1.7\n"
-            + "2.1 2.2 2.3 2.4 2.5 2.6 2.7\n"
-            + "3.1\t3.2\t3.3\t3.4\t3.5\t3.6\t3.7\n"
-            + " 4.1 ,4.2,\t,4.3 ,\t4.4\t, \4.5,4.6 4.7\n"
-            + ", 5.1,5.3,5.3,5.4,5.5, 5.6, 5.7\n"
-            + "\t6.1, 6.2 6.3 6.4 6.5 6.6 6.7\n"
-            + ", \t7.1\t7.2 7.3, 7.4, 7.5\t,7.6,7.7\n";
-    ScoreMatrix sm = ScoreMatrix.parse(new ByteArrayInputStream(data
-            .getBytes()));
-    assertNotNull(sm);
-    assertEquals(sm.getName(), "MyTest");
-    assertTrue(sm.isDNA());
-    assertFalse(sm.isProtein());
-    assertEquals(sm.getPairwiseScore('A', 'A'), 1.1f);
-    assertEquals(sm.getPairwiseScore('A', 'T'), 1.2f);
-    assertEquals(sm.getPairwiseScore('a', 'T'), 1.2f); // A/a equivalent
-    assertEquals(sm.getPairwiseScore('A', 't'), 1.5f); // T/t not equivalent
-    assertEquals(sm.getPairwiseScore('a', 't'), 1.5f);
-    assertEquals(sm.getPairwiseScore('T', ' '), 2.4f);
-    assertEquals(sm.getPairwiseScore('U', 'x'), 3.6f);
-    assertEquals(sm.getPairwiseScore('u', 'x'), 3.6f);
-    assertEquals(sm.getPairwiseScore('U', 'X'), 0f); // X (upper) unmapped
-    assertEquals(sm.getPairwiseScore('A', '.'), 0f); // . unmapped
-    assertEquals(sm.getPairwiseScore('-', '-'), 7.7f);
-    assertEquals(sm.getPairwiseScore('A', (char) 128), 0f); // out of range
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    float[][] m = sm.getMatrix();
+    assertEquals(m.length, sm.getSize());
+    assertEquals(m[2][4], -3f);
+    // verify a defensive copy is returned
+    float[][] m2 = sm.getMatrix();
+    assertNotSame(m, m2);
+    assertTrue(Arrays.deepEquals(m, m2));
   }
 
   @Test(groups = "Functional")
-  public void testParse_invalidInput()
+  public void testGetMatrixIndex()
   {
-    /*
-     * valid first
-     */
-    String data = "ScoreMatrix MyTest\nXY\n1 2\n3 4\n";
-    ScoreMatrix sm = ScoreMatrix.parse(new ByteArrayInputStream(data
-            .getBytes()));
-    assertNotNull(sm);
-
-    /*
-     * Name missing
-     */
-    data = "ScoreMatrix\nXY\n1 2\n3 4\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    assertEquals(sm.getMatrixIndex('A'), 0);
+    assertEquals(sm.getMatrixIndex('R'), 1);
+    assertEquals(sm.getMatrixIndex('r'), 1);
+    assertEquals(sm.getMatrixIndex('N'), 2);
+    assertEquals(sm.getMatrixIndex('D'), 3);
+    assertEquals(sm.getMatrixIndex('X'), 22);
+    assertEquals(sm.getMatrixIndex('x'), 22);
+    assertEquals(sm.getMatrixIndex(' '), 23);
+    assertEquals(sm.getMatrixIndex('*'), 24);
+    assertEquals(sm.getMatrixIndex('.'), -1);
+    assertEquals(sm.getMatrixIndex('-'), -1);
+    assertEquals(sm.getMatrixIndex('?'), -1);
+    assertEquals(sm.getMatrixIndex((char) 128), -1);
+  }
 
-    /*
-     * ScoreMatrix header missing
-     */
-    data = "XY\n1 2\n3 4\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+  @Test(groups = "Functional")
+  public void testGetSize()
+  {
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    assertEquals(sm.getMatrix().length, sm.getSize());
+  }
 
+  @Test(groups = "Functional")
+  public void testComputePairwiseScores()
+  {
     /*
-     * Not enough rows
+     * NB score matrix assumes space for gap - Jalview converts
+     * space to gap before computing PCA or Tree
      */
-    data = "ScoreMatrix MyTest\nXY\n1 2\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+    String[] seqs = new String[] { "FKL", "R D", "QIA", "GWC" };
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
 
-    /*
-     * Not enough columns
-     */
-    data = "ScoreMatrix MyTest\nXY\n1 2\n3\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+    MatrixI pairwise = sm.computePairwiseScores(seqs);
 
     /*
-     * Too many columns
+     * should be NxN where N = number of sequences
      */
-    data = "ScoreMatrix MyTest\nXY\n1 2\n3 4 5\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+    assertEquals(pairwise.height(), 4);
+    assertEquals(pairwise.width(), 4);
 
     /*
-     * Too many rows
+     * should be symmetrical (because BLOSUM62 is)
      */
-    data = "ScoreMatrix MyTest\nXY\n1 2\n3 4\n6 7";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
-
+    for (int i = 0; i < pairwise.height(); i++)
+    {
+      for (int j = i + 1; j < pairwise.width(); j++)
+      {
+        assertEquals(pairwise.getValue(i, j), pairwise.getValue(j, i),
+                String.format("Not symmetric at [%d, %d]", i, j));
+      }
+    }
     /*
-     * unsupported delimiter |
+     * verify expected BLOSUM dot product scores
      */
-    data = "ScoreMatrix MyTest\nXY\n1|2\n3|4\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+    // F.F + K.K + L.L = 6 + 5 + 4 = 15
+    assertEquals(pairwise.getValue(0, 0), 15d);
+    // R.R + -.- + D.D = 5 + 1 + 6 = 12
+    assertEquals(pairwise.getValue(1, 1), 12d);
+    // Q.Q + I.I + A.A = 5 + 4 + 4 = 13
+    assertEquals(pairwise.getValue(2, 2), 13d);
+    // G.G + W.W + C.C = 6 + 11 + 9 = 26
+    assertEquals(pairwise.getValue(3, 3), 26d);
+    // F.R + K.- + L.D = -3 + -4 + -4 = -11
+    assertEquals(pairwise.getValue(0, 1), -11d);
+    // F.Q + K.I + L.A = -3 + -3 + -1 = -7
+    assertEquals(pairwise.getValue(0, 2), -7d);
+    // F.G + K.W + L.C = -3 + -3 + -1 = -7
+    assertEquals(pairwise.getValue(0, 3), -7d);
+    // R.Q + -.I + D.A = 1 + -4 + -2 = -5
+    assertEquals(pairwise.getValue(1, 2), -5d);
+    // R.G + -.W + D.C = -2 + -4 + -3 = -9
+    assertEquals(pairwise.getValue(1, 3), -9d);
+    // Q.G + I.W + A.C = -2 + -3 + 0 = -5
+    assertEquals(pairwise.getValue(2, 3), -5d);
+  }
 
-    /*
-     * Bad float value
-     */
-    data = "ScoreMatrix MyTest\nXY\n1 2\n3 four\n";
-    sm = ScoreMatrix.parse(new ByteArrayInputStream(data.getBytes()));
-    assertNull(sm);
+  /**
+   * Test that the result of outputMatrix can be reparsed to give an identical
+   * ScoreMatrix
+   * 
+   * @throws IOException
+   * @throws MalformedURLException
+   */
+  @Test(groups = "Functional")
+  public void testOutputMatrix_roundTrip() throws MalformedURLException,
+          IOException
+  {
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    String output = sm.outputMatrix(false);
+    FileParse fp = new FileParse(output, DataSourceType.PASTE);
+    ScoreMatrixFile parser = new ScoreMatrixFile(fp);
+    ScoreMatrix sm2 = parser.parseMatrix();
+    assertNotNull(sm2);
+    assertTrue(sm2.equals(sm));
+  }
 
+  @Test(groups = "Functional")
+  public void testEauals()
+  {
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    ScoreMatrix sm2 = new ScoreMatrix(sm.getName(), sm.getSymbols()
+            .toCharArray(), sm.getMatrix());
+    assertTrue(sm.equals(sm2));
+    assertEquals(sm.hashCode(), sm2.hashCode());
   }
 }