From 187269da0130408d24901c88acb10c217f6284f9 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 5 Mar 2019 08:15:29 +0000 Subject: [PATCH] JAL-3205 added a delta parameter to Matrix.equals() --- src/jalview/math/Matrix.java | 22 +- src/jalview/math/MatrixI.java | 10 + .../analysis/scoremodels/ScoreMatrixTest.java | 215 ++++++++++++-------- test/jalview/math/MatrixTest.java | 28 +-- 4 files changed, 154 insertions(+), 121 deletions(-) diff --git a/src/jalview/math/Matrix.java b/src/jalview/math/Matrix.java index 1e8f39d..b22bf4e 100755 --- a/src/jalview/math/Matrix.java +++ b/src/jalview/math/Matrix.java @@ -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; } diff --git a/src/jalview/math/MatrixI.java b/src/jalview/math/MatrixI.java index d72890a..2c78653 100644 --- a/src/jalview/math/MatrixI.java +++ b/src/jalview/math/MatrixI.java @@ -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); } diff --git a/test/jalview/analysis/scoremodels/ScoreMatrixTest.java b/test/jalview/analysis/scoremodels/ScoreMatrixTest.java index 90a718a..15bdce1 100644 --- a/test/jalview/analysis/scoremodels/ScoreMatrixTest.java +++ b/test/jalview/analysis/scoremodels/ScoreMatrixTest.java @@ -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)); } } diff --git a/test/jalview/math/MatrixTest.java b/test/jalview/math/MatrixTest.java index fa446e4..7dc3b9e 100644 --- a/test/jalview/math/MatrixTest.java +++ b/test/jalview/math/MatrixTest.java @@ -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)); } } -- 1.7.10.2