def test_non_symmetric_matrix_raises(self): """Checks that if a non symmetric matrix is given to components_from_metric, an error is thrown""" rng = np.random.RandomState(42) M = rng.randn(10, 10) with pytest.raises(ValueError) as raised_error: components_from_metric(M) assert str(raised_error.value) == "The input metric should be symmetric."
def test_non_psd_raises(self): """Checks that a non PSD matrix (i.e. with negative eigenvalues) will raise an error when passed to components_from_metric""" rng = np.random.RandomState(42) D = np.diag([1, 5, 3, 4.2, -4, -2, 1]) P = ortho_group.rvs(7, random_state=rng) M = P.dot(D).dot(P.T) msg = ("Matrix is not positive semidefinite (PSD).") with pytest.raises(NonPSDError) as raised_error: components_from_metric(M) assert str(raised_error.value) == msg with pytest.raises(NonPSDError) as raised_error: components_from_metric(D) assert str(raised_error.value) == msg
def test_components_from_metric_edge_cases(self): """Test that components_from_metric returns the right result in various edge cases""" rng = np.random.RandomState(42) # an orthonormal matrix useful for creating matrices with given # eigenvalues: P = ortho_group.rvs(7, random_state=rng) # matrix with all its coefficients very low (to check that the algorithm # does not consider it as a diagonal matrix)(non regression test for # https://github.com/scikit-learn-contrib/metric-learn/issues/175) M = np.diag([1e-15, 2e-16, 3e-15, 4e-16, 5e-15, 6e-16, 7e-15]) M = P.dot(M).dot(P.T) L = components_from_metric(M) assert_allclose(L.T.dot(L), M) # diagonal matrix M = np.diag(np.abs(rng.randn(5))) L = components_from_metric(M) assert_allclose(L.T.dot(L), M) # low-rank matrix (with zeros) M = np.zeros((7, 7)) small_random = rng.randn(3, 3) M[:3, :3] = small_random.T.dot(small_random) L = components_from_metric(M) assert_allclose(L.T.dot(L), M) # low-rank matrix (without necessarily zeros) R = np.abs(rng.randn(7, 7)) M = R.dot(np.diag([1, 5, 3, 2, 0, 0, 0])).dot(R.T) L = components_from_metric(M) assert_allclose(L.T.dot(L), M) # matrix with a determinant still high but which is # undefinite w.r.t to numpy standards M = np.diag([1e5, 1e5, 1e5, 1e5, 1e5, 1e5, 1e-20]) M = P.dot(M).dot(P.T) assert np.abs(np.linalg.det(M)) > 10 assert np.linalg.slogdet(M)[1] > 1 # (just to show that the computed # determinant is far from null) assert np.linalg.matrix_rank(M) < M.shape[0] # (just to show that this case is indeed considered by numpy as an # indefinite case) L = components_from_metric(M) assert_allclose(L.T.dot(L), M) # matrix with lots of small nonzeros that make a big zero when multiplied M = np.diag([1e-3, 1e-3, 1e-3, 1e-3, 1e-3, 1e-3, 1e-3]) L = components_from_metric(M) assert_allclose(L.T.dot(L), M) # full rank matrix M = rng.randn(10, 10) M = M.T.dot(M) assert np.linalg.matrix_rank(M) == 10 L = components_from_metric(M) assert_allclose(L.T.dot(L), M)
def test_almost_psd_dont_raise(self): """Checks that if the metric is almost PSD (i.e. it has some negative eigenvalues very close to zero), then components_from_metric will still work""" rng = np.random.RandomState(42) D = np.diag([1, 5, 3, 4.2, -1e-20, -2e-20, -1e-20]) P = ortho_group.rvs(7, random_state=rng) M = P.dot(D).dot(P.T) L = components_from_metric(M) assert_allclose(L.T.dot(L), M)