JAL-3205 added a delta parameter to Matrix.equals() feature/JAL-3205symmetricCalc
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 5 Mar 2019 08:15:29 +0000 (08:15 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 5 Mar 2019 08:15:29 +0000 (08:15 +0000)
src/jalview/math/Matrix.java
src/jalview/math/MatrixI.java
test/jalview/analysis/scoremodels/ScoreMatrixTest.java
test/jalview/math/MatrixTest.java

index 1e8f39d..b22bf4e 100755 (executable)
@@ -985,12 +985,6 @@ public class Matrix implements MatrixI
     e = v;
   }
 
-  @Override
-  public int hashCode()
-  {
-    return (int) getTotal();
-  }
-
   public double getTotal()
   {
     double d = 0d;
@@ -1004,15 +998,14 @@ public class Matrix implements MatrixI
     return d;
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
-  public boolean equals(Object obj)
+  public boolean equals(MatrixI m2, double delta)
   {
-    if (!(obj instanceof MatrixI))
-    {
-      return false;
-    }
-    MatrixI m2 = (MatrixI) obj;
-    if (this.height() != m2.height() || this.width() != m2.width())
+    if (m2 == null || this.height() != m2.height()
+            || this.width() != m2.width())
     {
       return false;
     }
@@ -1020,7 +1013,8 @@ public class Matrix implements MatrixI
     {
       for (int j = 0; j < this.width(); j++)
       {
-        if (this.getValue(i, j) != m2.getValue(i, j))
+        double diff = this.getValue(i, j) - m2.getValue(i, j);
+        if (Math.abs(diff) > delta)
         {
           return false;
         }
index d72890a..2c78653 100644 (file)
@@ -159,4 +159,14 @@ public interface MatrixI
    * @param d
    */
   void multiply(double d);
+
+  /**
+   * Answers true if the two matrices have the same dimensions, and corresponding values all differ by no
+   * more than delta (which should be a positive value), else false
+   * 
+   * @param m2
+   * @param delta
+   * @return
+   */
+  boolean equals(MatrixI m2, double delta);
 }
index 90a718a..15bdce1 100644 (file)
@@ -54,7 +54,8 @@ public class ScoreMatrixTest
 
   @Test(
     groups = "Functional",
-    expectedExceptions = { IllegalArgumentException.class })
+    expectedExceptions =
+    { IllegalArgumentException.class })
   public void testConstructor_matrixTooSmall()
   {
     float[][] scores = new float[2][];
@@ -65,7 +66,8 @@ public class ScoreMatrixTest
 
   @Test(
     groups = "Functional",
-    expectedExceptions = { IllegalArgumentException.class })
+    expectedExceptions =
+    { IllegalArgumentException.class })
   public void testConstructor_matrixTooBig()
   {
     float[][] scores = new float[2][];
@@ -76,7 +78,8 @@ public class ScoreMatrixTest
 
   @Test(
     groups = "Functional",
-    expectedExceptions = { IllegalArgumentException.class })
+    expectedExceptions =
+    { IllegalArgumentException.class })
   public void testConstructor_matrixNotSquare()
   {
     float[][] scores = new float[2][];
@@ -244,8 +247,8 @@ public class ScoreMatrixTest
    * @throws MalformedURLException
    */
   @Test(groups = "Functional")
-  public void testOutputMatrix_roundTrip() throws MalformedURLException,
-          IOException
+  public void testOutputMatrix_roundTrip()
+          throws MalformedURLException, IOException
   {
     ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
     String output = sm.outputMatrix(false);
@@ -260,8 +263,8 @@ public class ScoreMatrixTest
   public void testEqualsAndHashCode()
   {
     ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
-    ScoreMatrix sm2 = new ScoreMatrix(sm.getName(), sm.getSymbols()
-            .toCharArray(), sm.getMatrix());
+    ScoreMatrix sm2 = new ScoreMatrix(sm.getName(),
+            sm.getSymbols().toCharArray(), sm.getMatrix());
     assertTrue(sm.equals(sm2));
     assertEquals(sm.hashCode(), sm2.hashCode());
 
@@ -284,19 +287,20 @@ public class ScoreMatrixTest
     String s1 = "FR-K-S";
     String s2 = "FS--L";
     ScoreMatrix blosum = ScoreModels.getInstance().getBlosum62();
-  
+
     /*
      * score gap-gap and gap-char
      * shorter sequence treated as if with trailing gaps
      * score = F^F + R^S + -^- + K^- + -^L + S^-
      * = 6 + -1 + 1 + -4 + -4 + -4 = -6
      */
-    SimilarityParamsI params = new SimilarityParams(true, true, true, false);
+    SimilarityParamsI params = new SimilarityParams(true, true, true,
+            false);
     assertEquals(blosum.computeSimilarity(s1, s2, params), -6d);
     // matchGap (arg2) is ignored:
     params = new SimilarityParams(true, false, true, false);
     assertEquals(blosum.computeSimilarity(s1, s2, params), -6d);
-  
+
     /*
      * score gap-char but not gap-gap
      * score = F^F + R^S + 0 + K^- + -^L + S^-
@@ -307,7 +311,7 @@ public class ScoreMatrixTest
     // matchGap (arg2) is ignored:
     params = new SimilarityParams(false, false, true, false);
     assertEquals(blosum.computeSimilarity(s1, s2, params), -7d);
-  
+
     /*
      * score gap-gap but not gap-char
      * score = F^F + R^S + -^- + 0 + 0 + 0
@@ -318,7 +322,7 @@ public class ScoreMatrixTest
     // matchGap (arg2) is ignored:
     params = new SimilarityParams(true, true, false, false);
     assertEquals(blosum.computeSimilarity(s1, s2, params), 6d);
-  
+
     /*
      * score neither gap-gap nor gap-char
      * score = F^F + R^S + 0 + 0 + 0 + 0
@@ -356,7 +360,7 @@ public class ScoreMatrixTest
     // matchGap (arg2) is ignored:
     params = new SimilarityParams(true, false, true, true);
     assertEquals(blosum.computeSimilarity(s1, s2, params), -2d);
-  
+
     /*
      * score gap-char but not gap-gap
      * score = F^F + R^S + 0 + K^- + -^L
@@ -367,7 +371,7 @@ public class ScoreMatrixTest
     // matchGap (arg2) is ignored:
     params = new SimilarityParams(false, false, true, true);
     assertEquals(blosum.computeSimilarity(s1, s2, params), -3d);
-  
+
     /*
      * score gap-gap but not gap-char
      * score = F^F + R^S + -^- + 0 + 0
@@ -378,7 +382,7 @@ public class ScoreMatrixTest
     // matchGap (arg2) is ignored:
     params = new SimilarityParams(true, true, false, true);
     assertEquals(blosum.computeSimilarity(s1, s2, params), 6d);
-  
+
     /*
      * score neither gap-gap nor gap-char
      * score = F^F + R^S + 0 + 0 + 0
@@ -414,9 +418,10 @@ public class ScoreMatrixTest
       assertEquals(m[row].length, rows);
       for (int col = 0; col < rows; col++)
       {
-        assertEquals(m[row][col], m[col][row], String.format("%s [%s, %s]",
-                sm.getName(), ResidueProperties.aa[row],
-                ResidueProperties.aa[col]));
+        assertEquals(m[row][col], m[col][row],
+                String.format("%s [%s, %s]", sm.getName(),
+                        ResidueProperties.aa[row],
+                        ResidueProperties.aa[col]));
       }
     }
   }
@@ -437,63 +442,98 @@ public class ScoreMatrixTest
      * verify expected scores against ARNDCQEGHILKMFPSTWYVBZX
      * scraped from https://www.ncbi.nlm.nih.gov/Class/FieldGuide/BLOSUM62.txt
      */
-    verifyValues(sm, 'A', new float[] { 4, -1, -2, -2, 0, -1, -1, 0, -2,
-        -1,
-        -1, -1, -1, -2, -1, 1, 0, -3, -2, 0, -2, -1, 0 });
-    verifyValues(sm, 'R', new float[] { -1, 5, 0, -2, -3, 1, 0, -2, 0, -3,
-        -2, 2, -1, -3, -2, -1, -1, -3, -2, -3, -1, 0, -1 });
-    verifyValues(sm, 'N', new float[] { -2, 0, 6, 1, -3, 0, 0, 0, 1, -3,
-        -3,
-        0, -2, -3, -2, 1, 0, -4, -2, -3, 3, 0, -1 });
-    verifyValues(sm, 'D', new float[] { -2, -2, 1, 6, -3, 0, 2, -1, -1, -3,
-        -4, -1, -3, -3, -1, 0, -1, -4, -3, -3, 4, 1, -1 });
-    verifyValues(sm, 'C', new float[] { 0, -3, -3, -3, 9, -3, -4, -3, -3,
-        -1,
-        -1, -3, -1, -2, -3, -1, -1, -2, -2, -1, -3, -3, -2 });
-    verifyValues(sm, 'Q', new float[] { -1, 1, 0, 0, -3, 5, 2, -2, 0, -3,
-        -2,
-        1, 0, -3, -1, 0, -1, -2, -1, -2, 0, 3, -1 });
-    verifyValues(sm, 'E', new float[] { -1, 0, 0, 2, -4, 2, 5, -2, 0, -3,
-        -3,
-        1, -2, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1 });
-    verifyValues(sm, 'G', new float[] { 0, -2, 0, -1, -3, -2, -2, 6, -2,
-        -4,
-        -4, -2, -3, -3, -2, 0, -2, -2, -3, -3, -1, -2, -1 });
-    verifyValues(sm, 'H', new float[] { -2, 0, 1, -1, -3, 0, 0, -2, 8, -3,
-        -3, -1, -2, -1, -2, -1, -2, -2, 2, -3, 0, 0, -1 });
-    verifyValues(sm, 'I', new float[] { -1, -3, -3, -3, -1, -3, -3, -4, -3,
-        4, 2, -3, 1, 0, -3, -2, -1, -3, -1, 3, -3, -3, -1 });
-    verifyValues(sm, 'L', new float[] { -1, -2, -3, -4, -1, -2, -3, -4, -3,
-        2, 4, -2, 2, 0, -3, -2, -1, -2, -1, 1, -4, -3, -1 });
-    verifyValues(sm, 'K', new float[] { -1, 2, 0, -1, -3, 1, 1, -2, -1, -3,
-        -2, 5, -1, -3, -1, 0, -1, -3, -2, -2, 0, 1, -1 });
-    verifyValues(sm, 'M', new float[] { -1, -1, -2, -3, -1, 0, -2, -3, -2,
-        1,
-        2, -1, 5, 0, -2, -1, -1, -1, -1, 1, -3, -1, -1 });
-    verifyValues(sm, 'F', new float[] { -2, -3, -3, -3, -2, -3, -3, -3, -1,
-        0, 0, -3, 0, 6, -4, -2, -2, 1, 3, -1, -3, -3, -1 });
-    verifyValues(sm, 'P', new float[] { -1, -2, -2, -1, -3, -1, -1, -2, -2,
-        -3, -3, -1, -2, -4, 7, -1, -1, -4, -3, -2, -2, -1, -2 });
-    verifyValues(sm, 'S', new float[] { 1, -1, 1, 0, -1, 0, 0, 0, -1, -2,
-        -2,
-        0, -1, -2, -1, 4, 1, -3, -2, -2, 0, 0, 0 });
-    verifyValues(sm, 'T', new float[] { 0, -1, 0, -1, -1, -1, -1, -2, -2,
-        -1,
-        -1, -1, -1, -2, -1, 1, 5, -2, -2, 0, -1, -1, 0 });
-    verifyValues(sm, 'W', new float[] { -3, -3, -4, -4, -2, -2, -3, -2, -2,
-        -3, -2, -3, -1, 1, -4, -3, -2, 11, 2, -3, -4, -3, -2 });
-    verifyValues(sm, 'Y', new float[] { -2, -2, -2, -3, -2, -1, -2, -3, 2,
-        -1, -1, -2, -1, 3, -3, -2, -2, 2, 7, -1, -3, -2, -1 });
-    verifyValues(sm, 'V', new float[] { 0, -3, -3, -3, -1, -2, -2, -3, -3,
-        3,
-        1, -2, 1, -1, -2, -2, 0, -3, -1, 4, -3, -2, -1 });
-    verifyValues(sm, 'B', new float[] { -2, -1, 3, 4, -3, 0, 1, -1, 0, -3,
-        -4, 0, -3, -3, -2, 0, -1, -4, -3, -3, 4, 1, -1 });
-    verifyValues(sm, 'Z', new float[] { -1, 0, 0, 1, -3, 3, 4, -2, 0, -3,
-        -3,
-        1, -1, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1 });
-    verifyValues(sm, 'X', new float[] { 0, -1, -1, -1, -2, -1, -1, -1, -1,
-        -1, -1, -1, -1, -1, -2, 0, 0, -2, -1, -1, -1, -1, -1 });
+    verifyValues(sm, 'A',
+            new float[]
+            { 4, -1, -2, -2, 0, -1, -1, 0, -2, -1, -1, -1, -1, -2, -1, 1, 0,
+                -3, -2, 0, -2, -1, 0 });
+    verifyValues(sm, 'R',
+            new float[]
+            { -1, 5, 0, -2, -3, 1, 0, -2, 0, -3, -2, 2, -1, -3, -2, -1, -1,
+                -3, -2, -3, -1, 0, -1 });
+    verifyValues(sm, 'N',
+            new float[]
+            { -2, 0, 6, 1, -3, 0, 0, 0, 1, -3, -3, 0, -2, -3, -2, 1, 0, -4,
+                -2, -3, 3, 0, -1 });
+    verifyValues(sm, 'D',
+            new float[]
+            { -2, -2, 1, 6, -3, 0, 2, -1, -1, -3, -4, -1, -3, -3, -1, 0, -1,
+                -4, -3, -3, 4, 1, -1 });
+    verifyValues(sm, 'C',
+            new float[]
+            { 0, -3, -3, -3, 9, -3, -4, -3, -3, -1, -1, -3, -1, -2, -3, -1,
+                -1, -2, -2, -1, -3, -3, -2 });
+    verifyValues(sm, 'Q',
+            new float[]
+            { -1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2, 1, 0, -3, -1, 0, -1, -2,
+                -1, -2, 0, 3, -1 });
+    verifyValues(sm, 'E',
+            new float[]
+            { -1, 0, 0, 2, -4, 2, 5, -2, 0, -3, -3, 1, -2, -3, -1, 0, -1,
+                -3, -2, -2, 1, 4, -1 });
+    verifyValues(sm, 'G',
+            new float[]
+            { 0, -2, 0, -1, -3, -2, -2, 6, -2, -4, -4, -2, -3, -3, -2, 0,
+                -2, -2, -3, -3, -1, -2, -1 });
+    verifyValues(sm, 'H',
+            new float[]
+            { -2, 0, 1, -1, -3, 0, 0, -2, 8, -3, -3, -1, -2, -1, -2, -1, -2,
+                -2, 2, -3, 0, 0, -1 });
+    verifyValues(sm, 'I',
+            new float[]
+            { -1, -3, -3, -3, -1, -3, -3, -4, -3, 4, 2, -3, 1, 0, -3, -2,
+                -1, -3, -1, 3, -3, -3, -1 });
+    verifyValues(sm, 'L',
+            new float[]
+            { -1, -2, -3, -4, -1, -2, -3, -4, -3, 2, 4, -2, 2, 0, -3, -2,
+                -1, -2, -1, 1, -4, -3, -1 });
+    verifyValues(sm, 'K',
+            new float[]
+            { -1, 2, 0, -1, -3, 1, 1, -2, -1, -3, -2, 5, -1, -3, -1, 0, -1,
+                -3, -2, -2, 0, 1, -1 });
+    verifyValues(sm, 'M',
+            new float[]
+            { -1, -1, -2, -3, -1, 0, -2, -3, -2, 1, 2, -1, 5, 0, -2, -1, -1,
+                -1, -1, 1, -3, -1, -1 });
+    verifyValues(sm, 'F',
+            new float[]
+            { -2, -3, -3, -3, -2, -3, -3, -3, -1, 0, 0, -3, 0, 6, -4, -2,
+                -2, 1, 3, -1, -3, -3, -1 });
+    verifyValues(sm, 'P',
+            new float[]
+            { -1, -2, -2, -1, -3, -1, -1, -2, -2, -3, -3, -1, -2, -4, 7, -1,
+                -1, -4, -3, -2, -2, -1, -2 });
+    verifyValues(sm, 'S',
+            new float[]
+            { 1, -1, 1, 0, -1, 0, 0, 0, -1, -2, -2, 0, -1, -2, -1, 4, 1, -3,
+                -2, -2, 0, 0, 0 });
+    verifyValues(sm, 'T',
+            new float[]
+            { 0, -1, 0, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, -2, -1, 1,
+                5, -2, -2, 0, -1, -1, 0 });
+    verifyValues(sm, 'W',
+            new float[]
+            { -3, -3, -4, -4, -2, -2, -3, -2, -2, -3, -2, -3, -1, 1, -4, -3,
+                -2, 11, 2, -3, -4, -3, -2 });
+    verifyValues(sm, 'Y',
+            new float[]
+            { -2, -2, -2, -3, -2, -1, -2, -3, 2, -1, -1, -2, -1, 3, -3, -2,
+                -2, 2, 7, -1, -3, -2, -1 });
+    verifyValues(sm, 'V',
+            new float[]
+            { 0, -3, -3, -3, -1, -2, -2, -3, -3, 3, 1, -2, 1, -1, -2, -2, 0,
+                -3, -1, 4, -3, -2, -1 });
+    verifyValues(sm, 'B',
+            new float[]
+            { -2, -1, 3, 4, -3, 0, 1, -1, 0, -3, -4, 0, -3, -3, -2, 0, -1,
+                -4, -3, -3, 4, 1, -1 });
+    verifyValues(sm, 'Z',
+            new float[]
+            { -1, 0, 0, 1, -3, 3, 4, -2, 0, -3, -3, 1, -1, -3, -1, 0, -1,
+                -3, -2, -2, 1, 4, -1 });
+    verifyValues(sm, 'X',
+            new float[]
+            { 0, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 0,
+                0, -2, -1, -1, -1, -1, -1 });
   }
 
   /**
@@ -599,6 +639,7 @@ public class ScoreMatrixTest
   @Test(groups = "Functional")
   public void testIsSymmetric()
   {
+    double delta = 0.0001d;
     float[][] scores = new float[][] { { 1f, -2f }, { -2f, 3f } };
     ScoreMatrix sm = new ScoreMatrix("Test", "AB".toCharArray(), scores);
     assertTrue(sm.isSymmetric());
@@ -617,9 +658,9 @@ public class ScoreMatrixTest
     String seq2 = "AABBABBA";
     String[] seqs1 = new String[] { seq1, seq2 };
     MatrixI res1 = sm.findSimilarities(seqs1, params);
-    assertEquals(res1,
-            new Matrix(new double[][]
-            { { 14d, 3d }, { 3d, 16d } }));
+    assertTrue(
+            res1.equals(new Matrix(new double[][]
+            { { 14d, 3d }, { 3d, 16d } }), delta));
 
     /*
      * order of sequences affects diagonal, but not off-diagonal values
@@ -629,9 +670,8 @@ public class ScoreMatrixTest
     String[] seqs2 = new String[] { seq2, seq1 };
     MatrixI res2 = sm.findSimilarities(seqs2, params);
     assertFalse(res1.equals(res2));
-    assertEquals(res2,
-            new Matrix(new double[][]
-            { { 16d, 3d }, { 3d, 14d } }));
+    assertTrue(res2.equals(new Matrix(new double[][]
+            { { 16d, 3d }, { 3d, 14d } }), delta));
 
     /*
      * now make the score matrix asymmetric
@@ -644,9 +684,8 @@ public class ScoreMatrixTest
     sm = new ScoreMatrix("Test", "AB".toCharArray(), scores);
     assertFalse(sm.isSymmetric()); // [0, 1] != [1, 0]
     res1 = sm.findSimilarities(seqs1, params);
-    assertEquals(res1,
-            new Matrix(new double[][]
-            { { 14d, 7d }, { 11d, 16d } }));
+    assertTrue(res1.equals(new Matrix(new double[][]
+            { { 14d, 7d }, { 11d, 16d } }), delta));
 
     /*
      * reverse order of sequences
@@ -655,9 +694,9 @@ public class ScoreMatrixTest
      */
     res2 = sm.findSimilarities(seqs2, params);
     assertFalse(res1.equals(res2));
-    assertEquals(res2,
-            new Matrix(new double[][]
-            { { 16d, 11d }, { 7d, 14d } }));
+    assertTrue(
+            res2.equals(new Matrix(new double[][]
+            { { 16d, 11d }, { 7d, 14d } }), delta));
 
     /*
      * verify that forcing an asymmetric matrix to use
@@ -666,6 +705,6 @@ public class ScoreMatrixTest
     PA.setValue(sm, "symmetric", true);
     assertTrue(sm.isSymmetric()); // it's not true!
     res2 = sm.findSimilarities(seqs1, params);
-    assertFalse(res1.equals(res2));
+    assertFalse(res1.equals(res2, delta));
   }
 }
index fa446e4..7dc3b9e 100644 (file)
@@ -2,7 +2,6 @@ package jalview.math;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotSame;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
@@ -551,37 +550,28 @@ public class MatrixTest
   }
 
   @Test(groups = "Functional")
-  public void testEquals_hashCode()
+  public void testEquals()
   {
     double[][] values = new double[][] { { 1, 2, 3 }, { 4, 5, 6 } };
     Matrix m1 = new Matrix(values);
     double[][] values2 = new double[][] { { 1, 2, 3 }, { 4, 5, 6 } };
     Matrix m2 = new Matrix(values2);
 
-    assertTrue(m1.equals(m1));
-    assertTrue(m1.equals(m2));
-    assertTrue(m2.equals(m1));
-    // equal objects should have same hashCode
-    assertEquals(m1.hashCode(), m2.hashCode());
+    double delta = 0.0001d;
+    assertTrue(m1.equals(m1, delta));
+    assertTrue(m1.equals(m2, delta));
+    assertTrue(m2.equals(m1, delta));
 
     double[][] values3 = new double[][] { { 1, 2, 3 }, { 4, 5, 7 } };
     m2 = new Matrix(values3);
-    assertFalse(m1.equals(m2));
-    assertFalse(m2.equals(m1));
-    assertNotEquals(m1.hashCode(), m2.hashCode());
-
-    // same hashCode doesn't always mean equal
-    values2 = new double[][] { { 1, 2, 3 }, { 4, 6, 5 } };
-    m2 = new Matrix(values2);
-    assertFalse(m2.equals(m1));
-    assertEquals(m1.hashCode(), m2.hashCode());
+    assertFalse(m1.equals(m2, delta));
+    assertFalse(m2.equals(m1, delta));
 
     // must be same shape
     values2 = new double[][] { { 1, 2, 3 } };
     m2 = new Matrix(values2);
-    assertFalse(m2.equals(m1));
+    assertFalse(m2.equals(m1, delta));
 
-    assertFalse(m1.equals(null));
-    assertFalse(m1.equals("foo"));
+    assertFalse(m1.equals(null, delta));
   }
 }