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)