+
+ @Test(groups = "Functional")
+ public void testSymmetric()
+ {
+ verifySymmetric(ScoreModels.getInstance().getBlosum62());
+ verifySymmetric(ScoreModels.getInstance().getPam250());
+ verifySymmetric(ScoreModels.getInstance().getDefaultModel(false)); // dna
+ }
+
+ /**
+ * A helper method that inspects a loaded matrix and reports any asymmetry as
+ * a test failure
+ *
+ * @param sm
+ */
+ private void verifySymmetric(ScoreMatrix sm)
+ {
+ float[][] m = sm.getMatrix();
+ int rows = m.length;
+ for (int row = 0; row < rows; row++)
+ {
+ 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]));
+ }
+ }
+ }
+
+ /**
+ * A test that just asserts the expected values in the Blosum62 score matrix
+ */
+ @Test(groups = "Functional")
+ public void testBlosum62_values()
+ {
+ ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+
+ assertTrue(sm.isProtein());
+ assertFalse(sm.isDNA());
+ assertNull(sm.getDescription());
+
+ /*
+ * 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 });
+ }
+
+ /**
+ * Helper method to check pairwise scores for one residue
+ *
+ * @param sm
+ * @param res
+ * @param expected
+ * score values against 'res', in ResidueProperties.aaIndex order
+ */
+ private void verifyValues(ScoreMatrix sm, char res, float[] expected)
+ {
+ for (int j = 0; j < expected.length; j++)
+ {
+ char c2 = ResidueProperties.aa[j].charAt(0);
+ assertEquals(sm.getPairwiseScore(res, c2), expected[j],
+ String.format("%s->%s", res, c2));
+ }
+ }
+
+ @Test(groups = "Functional")
+ public void testConstructor_gapDash()
+ {
+ float[][] scores = new float[2][];
+ scores[0] = new float[] { 1f, 2f };
+ scores[1] = new float[] { 4f, 5f };
+ ScoreMatrix sm = new ScoreMatrix("Test", new char[] { 'A', '-' },
+ scores);
+ assertEquals(sm.getSize(), 2);
+ assertArrayEquals(scores, sm.getMatrix());
+ assertEquals(sm.getPairwiseScore('A', 'a'), 1f);
+ assertEquals(sm.getPairwiseScore('A', 'A'), 1f);
+ assertEquals(sm.getPairwiseScore('a', '-'), 2f);
+ assertEquals(sm.getPairwiseScore('-', 'A'), 4f);
+ assertEquals(sm.getMatrixIndex('a'), 0);
+ assertEquals(sm.getMatrixIndex('A'), 0);
+ assertEquals(sm.getMatrixIndex('-'), 1);
+ assertEquals(sm.getMatrixIndex(' '), -1);
+ assertEquals(sm.getMatrixIndex('.'), -1);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetPairwiseScore()
+ {
+ float[][] scores = new float[2][];
+ scores[0] = new float[] { 1f, 2f };
+ scores[1] = new float[] { -4f, 5f };
+ ScoreMatrix sm = new ScoreMatrix("Test", new char[] { 'A', 'B' },
+ scores);
+ assertEquals(sm.getPairwiseScore('A', 'A'), 1f);
+ assertEquals(sm.getPairwiseScore('A', 'a'), 1f);
+ assertEquals(sm.getPairwiseScore('A', 'B'), 2f);
+ assertEquals(sm.getPairwiseScore('b', 'a'), -4f);
+ assertEquals(sm.getPairwiseScore('B', 'b'), 5f);
+
+ /*
+ * unknown symbols currently score minimum score
+ * or 1 for identity with self
+ */
+ assertEquals(sm.getPairwiseScore('A', '-'), -4f);
+ assertEquals(sm.getPairwiseScore('-', 'A'), -4f);
+ assertEquals(sm.getPairwiseScore('-', '-'), 1f);
+ assertEquals(sm.getPairwiseScore('Q', 'W'), -4f);
+ assertEquals(sm.getPairwiseScore('Q', 'Q'), 1f);
+
+ /*
+ * symbols not in basic ASCII set score zero
+ */
+ char c = (char) 200;
+ assertEquals(sm.getPairwiseScore('Q', c), 0f);
+ assertEquals(sm.getPairwiseScore(c, 'Q'), 0f);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetMinimumScore()
+ {
+ ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+ assertEquals(sm.getMinimumScore(), -4f);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetMaximumScore()
+ {
+ ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+ assertEquals(sm.getMaximumScore(), 11f);
+ }
+
+ @Test(groups = "Functional")
+ public void testOutputMatrix_html()
+ {
+ float[][] scores = new float[2][];
+ scores[0] = new float[] { 1f, 2f };
+ scores[1] = new float[] { 4f, -5.3E-10f };
+ ScoreMatrix sm = new ScoreMatrix("Test", "AB".toCharArray(), scores);
+ String html = sm.outputMatrix(true);
+ String expected = "<table border=\"1\"><tr><th></th><th> A </th><th> B </th></tr>\n"
+ + "<tr><td>A</td><td>1.0</td><td>2.0</td></tr>\n"
+ + "<tr><td>B</td><td>4.0</td><td>-5.3E-10</td></tr>\n"
+ + "</table>";
+ assertEquals(html, expected);
+ }
+
+ @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());
+
+ /*
+ * verify that with a symmetric score matrix,
+ * pairwise similarity matrix is also symmetric
+ * seq1.seq1 = 5*A.A + 3*B.B = 5+9 = 14
+ * seq1.seq2 = 3*A.A + 2*A.B + B.A + 2*B.B = 3 + -4 + -2 + 6 = 3
+ * seq2.seq1 = 3*A.A + A.B + 2*B.A + 2*B.B = 3 + -2 + -4 + 6 = 3
+ * seq2.seq2 = 4*A.A + 4*B.B = 4 + 12 = 16
+ */
+ SimilarityParamsI params = new SimilarityParams(true, true, true,
+ false);
+ String seq1 = "AAABBBAA";
+ String seq2 = "AABBABBA";
+ String[] seqs1 = new String[] { seq1, seq2 };
+ MatrixI res1 = sm.findSimilarities(seqs1, params);
+ assertTrue(
+ res1.equals(new Matrix(new double[][]
+ { { 14d, 3d }, { 3d, 16d } }), delta));
+
+ /*
+ * order of sequences affects diagonal, but not off-diagonal values
+ * [0, 0] is now seq2.seq2, [1, 1] is seq1.seq1
+ * [0, 1] is now seq2.seq1 = seq1.seq2 by symmetry
+ */
+ String[] seqs2 = new String[] { seq2, seq1 };
+ MatrixI res2 = sm.findSimilarities(seqs2, params);
+ assertFalse(res1.equals(res2));
+ assertTrue(
+ res2.equals(new Matrix(new double[][]
+ { { 16d, 3d }, { 3d, 14d } }), delta));
+
+ /*
+ * now make the score matrix asymmetric
+ * seq1.seq1 = 5*A.A + 3*B.B = 5+9 = 14
+ * seq1.seq2 = 3*A.A + 2*A.B + B.A + 2*B.B = 3 + -4 + 2 + 6 = 7
+ * seq2.seq1 = 3*A.A + A.B + 2*B.A + 2*B.B = 3 + -2 + 4 + 6 = 11
+ * seq2.seq2 = 4*A.A + 4*B.B = 4 + 12 = 16
+ */
+ scores = new float[][] { { 1f, -2f }, { 2f, 3f } };
+ sm = new ScoreMatrix("Test", "AB".toCharArray(), scores);
+ assertFalse(sm.isSymmetric()); // [0, 1] != [1, 0]
+ res1 = sm.findSimilarities(seqs1, params);
+ assertTrue(
+ res1.equals(new Matrix(new double[][]
+ { { 14d, 7d }, { 11d, 16d } }), delta));
+
+ /*
+ * reverse order of sequences
+ * - reverses order of main diagonal
+ * - reflects off-diagonal values
+ */
+ res2 = sm.findSimilarities(seqs2, params);
+ assertFalse(res1.equals(res2));
+ assertTrue(
+ res2.equals(new Matrix(new double[][]
+ { { 16d, 11d }, { 7d, 14d } }), delta));
+
+ /*
+ * verify that forcing an asymmetric matrix to use
+ * symmetric calculation gives a different (wrong) result
+ */
+ PA.setValue(sm, "symmetric", true);
+ assertTrue(sm.isSymmetric()); // it's not true!
+ res2 = sm.findSimilarities(seqs1, params);
+ assertFalse(res1.equals(res2, delta));
+ }