Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@thomasjpfan
Copy link

Here is a quick fix to make the eigenvectors consistent.


@pytest.mark.parametrize("add_noise", [True, False])
@pytest.mark.parametrize("seed", range(2))
def test_fastica_simple_different_solvers(add_noise, seed):
Copy link
Author

@thomasjpfan thomasjpfan Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new test to check for consistency based on what you had.

outs[solver] = sources
assert ica.components_.shape == (2, 2)
assert sources.shape == (1000, 2)
_, _, sources_fun = fastica(m.T, fun=nl, algorithm=algo, random_state=seed)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reverts the tests to what they were on upstream/main.

d, u = d[sort_indices], u[sort_indices]
# Resize and reorder to match svd
u = u[::-1, : min(X.shape) : -1]
d, u = d[sort_indices], u[:, sort_indices]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on eigh docs, the eigenvectors has shape (M, N) where N is the number of eigenvalues. Thus, I think the sorting should be done over axis=1.

@thomasjpfan thomasjpfan closed this Mar 9, 2022
@thomasjpfan
Copy link
Author

thomasjpfan commented Mar 9, 2022

After thinking it over, I do not think this is correct.

Nevermind I think this works.

@thomasjpfan thomasjpfan reopened this Mar 9, 2022
@thomasjpfan
Copy link
Author

Here is a code snippet to demonstrate:

from scipy import linalg
import numpy as np
from numpy.random import default_rng
from numpy.testing import assert_allclose

def my_svd(X):
    u, d = linalg.svd(X, full_matrices=False)[:2]
    u *= np.sign(u[0])
    return u, d

def my_eigh(X):
    d, u = linalg.eigh(X.T.dot(X))
    sort_indicies = np.argsort(d)[::-1]
    d = d[sort_indicies]
    u = u[:, sort_indicies]
    u *= np.sign(u[0])
    return u, np.sqrt(d)

rng = default_rng()
for i in range(10):
    m, n = 9, 6
    X = rng.standard_normal((m, n))
    u_svd, d_svd = my_svd(X.T)
    u_eigen, d_eigen = my_eigh(X)
    
    assert_allclose(d_svd, d_eigen)
    assert_allclose(u_svd, u_eigen)

@thomasjpfan
Copy link
Author

Although, we can use the align the eigenvectors based on the "maximum":

from scipy import linalg
import numpy as np
from numpy.random import default_rng
from numpy.testing import assert_allclose

def my_svd(X):
    u, d = linalg.svd(X, full_matrices=False)[:2]
    max_abs_cols = np.argmax(np.abs(u), axis=0)
    signs = np.sign(u[max_abs_cols, range(u.shape[1])])
    u *= signs
    
    return u, d

def my_eigh(X):
    d, u = linalg.eigh(X.T.dot(X))
    sort_indicies = np.argsort(d)[::-1]
    d = d[sort_indicies]
    u = u[:, sort_indicies]
    max_abs_cols = np.argmax(np.abs(u), axis=0)
    signs = np.sign(u[max_abs_cols, range(u.shape[1])])
    u *= signs
    
    return u, np.sqrt(d)

rng = default_rng()
for i in range(10):
    m, n = 9, 6
    X = rng.standard_normal((m, n))
    u_svd, d_svd = my_svd(X.T)
    u_eigen, d_eigen = my_eigh(X)
    
    assert_allclose(d_svd, d_eigen)
    assert_allclose(u_svd, u_eigen)

u, d = linalg.svd(XT, full_matrices=False, check_finite=False)[:2]

# Give consistent eigenvectors for both svd solvers
u *= np.sign(u[0])
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative solution is:

    max_abs_cols = np.argmax(np.abs(u), axis=0)
    signs = np.sign(u[max_abs_cols, range(u.shape[1])])
    u *= signs

If we want to be strictly the same as svd_flip.

@Micky774 Micky774 merged commit 7eec239 into Micky774:change_svd Mar 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants