From 96a9491fcc4c7df4c73f9617f2185586b0226b78 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 3 Nov 2016 14:29:21 -0400 Subject: [PATCH] Take the condition number into account when evaluating test results. This adds a ``modifier`` parameter to the assert_within_tol() method used in the SymmetricLinearGame unit tests. The condition number of the game is always passed in as the modifier, and that lets us allow a larger tolerance for nasty matrices. --- test/symmetric_linear_game_test.py | 45 ++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/test/symmetric_linear_game_test.py b/test/symmetric_linear_game_test.py index da72fd0..936a7e8 100644 --- a/test/symmetric_linear_game_test.py +++ b/test/symmetric_linear_game_test.py @@ -36,12 +36,28 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 """ Tests for the SymmetricLinearGame and Solution classes. """ - def assert_within_tol(self, first, second): + def assert_within_tol(self, first, second, modifier=1): """ Test that ``first`` and ``second`` are equal within a multiple of our default tolerances. + + Parameters + ---------- + + first : float + The first number to compare. + + second : float + The second number to compare. + + modifier : float + A scaling factor (default: 1) applied to the default + ``EPSILON`` for this comparison. If you have a poorly- + conditioned matrix, for example, you may want to set this + greater than one. + """ - self.assertTrue(abs(first - second) < EPSILON) + self.assertTrue(abs(first - second) < EPSILON*modifier) def assert_solution_exists(self, G): @@ -52,7 +68,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 expected = inner_product(G._L*soln.player1_optimal(), soln.player2_optimal()) - self.assert_within_tol(soln.game_value(), expected) + self.assert_within_tol(soln.game_value(), expected, G.condition()) @@ -113,7 +129,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 (alpha, H) = random_nn_scaling(G) value1 = G.solution().game_value() value2 = H.solution().game_value() - self.assert_within_tol(alpha*value1, value2) + self.assert_within_tol(alpha*value1, value2, H.condition()) def test_scaling_orthant(self): @@ -151,10 +167,12 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 (alpha, H) = random_translation(G) value2 = H.solution().game_value() - self.assert_within_tol(value1 + alpha, value2) + self.assert_within_tol(value1 + alpha, value2, H.condition()) # Make sure the same optimal pair works. - self.assert_within_tol(value2, inner_product(H._L*x_bar, y_bar)) + self.assert_within_tol(value2, + inner_product(H._L*x_bar, y_bar), + H.condition()) def test_translation_orthant(self): @@ -193,11 +211,14 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 y_bar = soln1.player2_optimal() soln2 = H.solution() - self.assert_within_tol(-soln1.game_value(), soln2.game_value()) + self.assert_within_tol(-soln1.game_value(), + soln2.game_value(), + H.condition()) # Make sure the switched optimal pair works. self.assert_within_tol(soln2.game_value(), - inner_product(M*y_bar, x_bar)) + inner_product(M*y_bar, x_bar), + H.condition()) def test_opposite_game_orthant(self): @@ -229,10 +250,10 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 value = soln.game_value() ip1 = inner_product(y_bar, G._L*x_bar - value*G._e1) - self.assert_within_tol(ip1, 0) + self.assert_within_tol(ip1, 0, G.condition()) ip2 = inner_product(value*G._e2 - G._L.trans()*y_bar, x_bar) - self.assert_within_tol(ip2, 0) + self.assert_within_tol(ip2, 0, G.condition()) def test_orthogonality_orthant(self): @@ -290,7 +311,9 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 # The dual game's value should always equal the primal's. dualsoln = G.dual().solution() - self.assert_within_tol(dualsoln.game_value(), soln.game_value()) + self.assert_within_tol(dualsoln.game_value(), + soln.game_value(), + G.condition()) def test_lyapunov_orthant(self): -- 2.43.2