Closed michalk8 closed 3 years ago
The only test that fails and is worrying me is this one: https://github.com/msmdev/msmtools/pull/18/files#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R60 I've checked whether I've made a typo when copying the data, but haven't found anything there.
to do's for Marius
Thanks for adding these new test @michalk8 !
Hi Mike, this notebook in our reproduciblity repo contains the following very small toy example matrix you could use:
:
p = np.array([
# 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
[0.0, 0.8, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #0
[0.2, 0.0, 0.6, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #1
[0.6, 0.2, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #2
[0.0, 0.05, 0.05, 0.0, 0.6, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #3
[0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.4, 0.0, 0.0, 0.1, 0.0, 0.0], #4
[0.0, 0.0, 0.0, 0.25, 0.25, 0.0, 0.1, 0.0, 0.0, 0.4, 0.0, 0.0], #5
[0.0, 0.0, 0.0, 0.0, 0.05, 0.05, 0.0, 0.7, 0.2, 0.0, 0.0, 0.0], #6
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.0, 0.0, 0.0], #7
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.2, 0.0, 0.0, 0.0, 0.0], #8
[0.0, 0.0, 0.0, 0.0, 0.05, 0.05, 0.0, 0.0, 0.0, 0.0, 0.7, 0.2], #9
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8], #10
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.2, 0.0], #11
])
I wanted to do this myself but things are a bit hectic on my end atm, so I though I will just give you the matrix for now in case you find the time to already set up some tests.
Besides some tests on this irreducible example, you could introduce an absorbing state (e.g. make state 11 absorbing by giving it 0 outgoing probability and 1 self-loop prob) and apply GPCCA. As far as I'm aware, this fails atm, something with D-orthogonality, and we need to look into this.
Hi Mike, any updates here?
@michalk8, I recall we had one failing tests where membership vectors differed between brands and krylov or something similar. I went through old PRs and realised that I discussed this with Bernhard before, see here: https://github.com/msmdev/msmtools/pull/8
Indeed, the Schur decomposition is not unique, so we would not expect to get exactly the same schur vectors back. However, we expect them to span the same invariant subspaces, which you can check by using the subspace_angles
, method, see the utility function I posted in the linked PR.
BTW, this is a nice way to check whether something is a correct schur form: np.max(subspace_angles(T.A @ X_b, X_b @ sf_b))
, see my utility function.
So, this should fix another failing test. Could you please describe in bullet points what's still failing and why it is? Then I can give you more targeted feedback.
Hi Mike, would be great to have some test cases with the toy matrix I posted above ready for our chat tomorrow. In general, we should have everything ready by Friday to discuss with Bernhard.
I've added 1 test, however 1 check there fails: https://github.com/msmdev/msmtools/pull/18/commits/6a4e9e5fca957d086bc981e8d5a0136a9bd54f3b#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R623
I've regenerated the ground truth for sparse matrices, so the only test failing apart from the above mentioned is: https://github.com/msmdev/msmtools/pull/18/commits/6a4e9e5fca957d086bc981e8d5a0136a9bd54f3b#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R98
Lastly, any idea why this happens when mu=0: https://github.com/msmdev/msmtools/pull/18/commits/6a4e9e5fca957d086bc981e8d5a0136a9bd54f3b#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R595
There are all the tests that fail for me (2 from GPCCA, the rest from other modules):
FAILED tests/analysis/test_mfpt.py::TestMfptSparse::test_mfpt_between_sets - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toarray()) or reduce k.
FAILED tests/analysis/impl/dense/gpcca_test.py::TestGPCCAMatlabRegression::test_normal_case - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestCustom::test_P2 - AssertionError: array([[7.77156117e-16, 1.27631890e-03],
FAILED tests/analysis/impl/sparse/mean_first_passage_time_test.py::TestMfpt::test_mfpt_between_sets - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toarray()) or reduce k.
FAILED tests/estimation/tests/test_effective_count_matrix.py::TestEffectiveCountMatrix::test_njobs_speedup - AssertionError: 1.3874175548553467 not less than 1.3511504530906677 : does not scale for njobs=4
FAILED tests/estimation/tests/test_transition_matrix.py::TestTransitionRevPiSym::test_sparse - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toarray()) or reduce k.
I've tested it with scipy version: 1.5.3.
I've added 1 test, however 1 check there fails: 6a4e9e5#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R623
For this one, we don't know what's going on. Need to discuss
I've regenerated the ground truth for sparse matrices, so the only test failing apart from the above mentioned is: 6a4e9e5#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R98
This one looks fine, was a problem with membership vectors being not exactly the same but they do actually span the same subspace.
Lastly, any idea why this happens when mu=0: 6a4e9e5#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R595
Need to discuss.
I looked into applying GPCCA to reducible matrices. First, I tried a case which had one transient and one single recurrent class: all good here, GPCCA correctly recovers the macrostates. Then, I moved onto an example with one transient and two recurrent classes. This is where the trouble starts.
This is the matrix:
p = np.array([
# 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
[0.0, 0.8, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #0
[0.2, 0.0, 0.6, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #1
[0.6, 0.2, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #2
[0.0, 0.05, 0.05, 0.0, 0.6, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], #3
[0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.4, 0.0, 0.0, 0.1, 0.0, 0.0], #4
[0.0, 0.0, 0.0, 0.25, 0.25, 0.0, 0.1, 0.0, 0.0, 0.4, 0.0, 0.0], #5
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 0.3, 0.0, 0.0, 0.0], #6
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.0, 0.0, 0.0], #7
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.2, 0.0, 0.0, 0.0, 0.0], #8
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7, 0.3], #9
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8], #10
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.2, 0.0], #11
])
Maybe a bit easier to see when I plot the sate graph:
Essentially, (6, 7, 8) form one recurrent class while (9, 10, 11) form the other.
When I run g.optimize
, I get
~/Projects/msmtools/msmtools/analysis/dense/gpcca.py in _do_schur(P, eta, m, z, method, tol_krylov)
256 if not np.allclose(X.T.dot(X*eta[:, None]), np.eye(X.shape[1]), atol=1e-6, rtol=1e-5):
257 print(X.T.dot(X*eta[:, None]))
--> 258 raise ValueError("Schur vectors appear to not be D-orthogonal.")
259
260 # Raise, if X doesn't fullfill the invariant subspace condition!
ValueError: Schur vectors appear to not be D-orthogonal.
It also prints the matrix X^T D X
, which is
[[ 1.00000000e+00 -5.64545831e-01 2.22044605e-16 -1.10328413e-15]
[-5.64545831e-01 1.00000000e+00 2.47749896e-16 -1.88096579e-16]
[ 2.19434836e-16 2.72066758e-16 1.00000000e+00 1.38261193e-16]
[-1.10249066e-15 -2.10303071e-16 8.14451571e-17 1.00000000e+00]]
which means that the first two Schur vectors are not D-orthogonal, the others are fine. I check the Schur matrix to find out what these first two correspond to:
array([[ 1. , -0. , -0.06543, 0.11101],
[ 0. , 1. , -0.03596, 0.07668],
[ 0. , 0. , 0.8871 , -0.23621],
[ 0. , 0. , 0. , 0.59929]])
So they correspond to the 1-eigenvalues. There are two 1-eigenvalues here, one for each recurrent class.
Okay, I'm going through the code now and I'm wondering why we're checking orthogonality wrt standart inner product and not wrt. D in 228:
if not np.allclose(Q.T.dot(Q), np.eye(Q.shape[1]), rtol=1e6*eps, atol=1e6*eps)
We should be checking this wrt. D, no? @msmdev
Okay, I'm going through the code now and I'm wondering why we're checking orthogonality wrt standart inner product and not wrt. D in 228:
if not np.allclose(Q.T.dot(Q), np.eye(Q.shape[1]), rtol=1e6*eps, atol=1e6*eps)
We should be checking this wrt. D, no? @msmdev
Changing this fixed the above error with reducible matrices.
Okay, I'm going through the code now and I'm wondering why we're checking orthogonality wrt standart inner product and not wrt. D in 228:
if not np.allclose(Q.T.dot(Q), np.eye(Q.shape[1]), rtol=1e6*eps, atol=1e6*eps)
We should be checking this wrt. D, no? @msmdev
Changing this fixed the above error with reducible matrices.
Makes perfect sense... thx!
Oh, someone's back from holidays!
@michalk8 , I would suggest to have two functions to check schur decompositions:
If we have computed the full decomposition, we can do what you are currently doing in the tests:
def _assert_schur(
P: np.ndarray, X: np.ndarray, RR: np.ndarray, N: Optional[int] = None
):
if N is not None:
np.testing.assert_array_equal(P.shape, [N, N])
np.testing.assert_array_equal(X.shape, [N, N])
np.testing.assert_array_equal(RR.shape, [N, N])
assert np.all(np.abs(X @ RR - P @ X) < eps), np.abs(X @ RR - P @ X)
assert np.all(np.abs(X[:, 0] - 1) < eps), np.abs(X[:, 0])
If we have however only computed a partial schur decomposition, the above will fail. In that case, we can use the checks we already have in do_schur
:
# Raise, if X doesn't fullfill the invariant subspace condition!
dp = np.dot(P, sp.csr_matrix(X) if issparse(P) else X)
dummy = subspace_angles(dp.toarray() if issparse(dp) else dp, np.dot(X, R))
test = np.allclose(dummy, 0.0, atol=1e-6, rtol=1e-5)
test1 = (dummy.shape[0] == m)
@msmdev , is this todo something we should be worried about?
# Search for the constant (Schur) vector, if explicitly present.
max_i = 0
for i in range(m):
vsum = np.sum(X[:,i])
dummy = ( np.ones(X[:,i].shape) * (vsum / n) )
if np.allclose(X[:,i], dummy, rtol=1e-6, atol=1e-5 ):
max_i = i #TODO: check, if more than one vec fulfills this
@msmdev , is this todo something we should be worried about?
# Search for the constant (Schur) vector, if explicitly present. max_i = 0 for i in range(m): vsum = np.sum(X[:,i]) dummy = ( np.ones(X[:,i].shape) * (vsum / n) ) if np.allclose(X[:,i], dummy, rtol=1e-6, atol=1e-5 ): max_i = i #TODO: check, if more than one vec fulfills this
Not sure. This never happened to me, but I think we can't rule this case out with 100% certainty. Would need to check the Perron Frobenius theorem for non-negative matrices to find out if we need to worry about this.
@msmdev , is this todo something we should be worried about?
# Search for the constant (Schur) vector, if explicitly present. max_i = 0 for i in range(m): vsum = np.sum(X[:,i]) dummy = ( np.ones(X[:,i].shape) * (vsum / n) ) if np.allclose(X[:,i], dummy, rtol=1e-6, atol=1e-5 ): max_i = i #TODO: check, if more than one vec fulfills this
Not sure. This never happened to me, but I think we can't rule this case out with 100% certainty. Would need to check the Perron Frobenius theorem for non-negative matrices to find out if we need to worry about this.
Would be great if you could look into this, this is one of the last open questions here.
@michalk8, I removed this check from TestCustom
:
# AssertionError: 0.0012763188973086148
assert np.all(np.abs(X @ RR - P_2 @ X) <= eps), np.abs(X @ RR - P_2 @ X).max()
This is expected to fail for partial Schur decomposition, so it does not make sense to check this for partial Schur decompositions.
@michalk8 , I added matrices P_3
- P_7
to conftest.py
. Could you add some standard tests for all of these to TestCustom
please? Could actually be as simple as what we currently have for test_P2
. Maybe just add one test on the coarse grained transition matrix that checks whether rows sum to one.
Looking forward to discussing this tomorrow! I'm optimistic we can open a PR for this soon.
Thanks for adding these Mike!
Hi @msmdev , did you have the chance to go through our tests yet? Would be great if we could merge this this week still!
Hi @msmdev , did you have the chance to go through our tests yet? Would be great if we could merge this this week still!
Sorry @Marius1311 , I couldn't find the time so far, but I will do my very best to do so as fast as possible!
thanks!
Hi @michalk8 , please have a look through @msmdev s comments and in particular, could you implement the row-matching scheme we discussed?
As of now, the only failing tests are here: https://github.com/msmdev/msmtools/pull/18#discussion_r529078100
@msmdev , Mike went through your comments and fixed basically everything, I think this is ready for another round of your review.
Todo for @michalk8 , add test for membership vectors in the dense/sparse setting.
Done in 2 places: https://github.com/msmdev/msmtools/pull/18/files#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R660 and https://github.com/msmdev/msmtools/pull/18/files#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R656 Small note: I've added the test requirement (pytest-mock) because I monkeypatch 2 methods in https://github.com/msmdev/msmtools/pull/18/files#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R338
Currently, I have an important deadline to keep, so I will try to do the review on the weekend... Please, excuse the delay!
No worries, keep us updated.
Hi @msmdev, what's the state of the art here? Did you manage to look into this over the weekend? I think our aim should be to open a PR to upstream this week - it does not need to be perfect for that yet. We just really have to push this so we get done before we get revisions in, otherwise, this will delay the process for very long and hinder maintainability of cellrank.
@Marius1311 @michalk8 I tried to review the changes, but a had troubles with many of the supplied links to the changes, so I had trouble finding the related changes. Also I had very few time to do so - please excuse, if I might have misunderstood or overlooked some things. In those cases, please try to elaborate the things in more detail to help me getting to the point very fast. Thank you!
Hi @msmdev ,
sorry for the sloppy linking, I must've link the previous commit - I will link now not the changes in the PR, but rather the branch.
Regarding the errors in https://github.com/msmdev/msmtools/pull/18#discussion_r533187270, they only occur in test_sort_real_schur
for 3 matrices (out of 5):
# M_2
E AssertionError:
E Not equal to tolerance rtol=1e-05, atol=5
E
E Mismatched elements: 4 / 4 (100%)
E Max absolute difference: 296.85321743
E Max relative difference: 296.85321743
E x: array([297.853217, 297.853217, 297.667196, 297.667196])
E y: array(1.)
# M_3
E AssertionError:
E Not equal to tolerance rtol=1e-05, atol=5
E
E Mismatched elements: 4 / 4 (100%)
E Max absolute difference: 317.45258363
E Max relative difference: 317.45258363
E x: array([318.452584, 318.452584, 318.293397, 318.293397])
E y: array(1.)
# M_5
E AssertionError:
E Not equal to tolerance rtol=1e-05, atol=5
E
E Mismatched elements: 4 / 4 (100%)
E Max absolute difference: 26991.94731741
E Max relative difference: 26991.94731741
E x: array([26992.947317, 26992.947317, 26022.44675 , 26022.44675 ])
E y: array(1.)
ping @Marius1311 https://github.com/msmdev/msmtools/pull/18#discussion_r533223339 - is this fine for us?
Hi @msmdev , sorry for the sloppy linking, I must've link the previous commit - I will link now not the changes in the PR, but rather the branch. Regarding the errors in #18 (comment), they only occur in
test_sort_real_schur
for 3 matrices (out of 5):# M_2 E AssertionError: E Not equal to tolerance rtol=1e-05, atol=5 E E Mismatched elements: 4 / 4 (100%) E Max absolute difference: 296.85321743 E Max relative difference: 296.85321743 E x: array([297.853217, 297.853217, 297.667196, 297.667196]) E y: array(1.) # M_3 E AssertionError: E Not equal to tolerance rtol=1e-05, atol=5 E E Mismatched elements: 4 / 4 (100%) E Max absolute difference: 317.45258363 E Max relative difference: 317.45258363 E x: array([318.452584, 318.452584, 318.293397, 318.293397]) E y: array(1.) # M_5 E AssertionError: E Not equal to tolerance rtol=1e-05, atol=5 E E Mismatched elements: 4 / 4 (100%) E Max absolute difference: 26991.94731741 E Max relative difference: 26991.94731741 E x: array([26992.947317, 26992.947317, 26022.44675 , 26022.44675 ]) E y: array(1.)
ping @Marius1311 #18 (comment) - is this fine for us?
Hi @michalk8 , I still have no idea what this test is testing for, so I can't say whether it's okay for us whether it fails. @msmdev , could you please explain the logic of this test? To facilitate this, here's the code:
def test_sort_real_schur(self, R_i: np.ndarray):
# test_SRSchur_num_t
Q = np.eye(4)
QQ, RR, ap = sort_real_schur(Q, R_i, z="LM", b=0)
assert np.all(np.array(ap) <= 1), ap
EQ = np.true_divide(np.linalg.norm(Q - QQ.T @ QQ, ord=1), eps)
assert_allclose(EQ, 1.0, atol=5)
EA = np.true_divide(np.linalg.norm(R_i - QQ @ RR @ QQ.T, ord=1), eps * np.linalg.norm(R_i, ord=1))
assert_allclose(EA, 1.0, atol=5)
l = eigs(R_i, k=4, which="SM", return_eigenvectors=False)
ll = eigs(RR, k=4, which="SM", return_eigenvectors=False)
EL = np.true_divide(np.abs(l - ll), eps * np.abs(l))
assert_allclose(EL, 1.0, atol=5)
According to @michalk8 , we're failing at the last assertion, which checks whether the smallest eigenvalues of these matrices are similar, if I understand this correctly.
@msmdev regarding the discrepancy between sparse/dense The rotation matrix is similar in absolute difference (10e-2), just the signs are sometimes flipped and when comparing the memberships from Matlab's ground truth, the max. absolute difference is less than 1-e4. https://github.com/michalk8/msmtools/commit/212cb240f97333039ecd0b938c0ef8579f80b42b#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R619
Hi @msmdev , sorry for the sloppy linking, I must've link the previous commit - I will link now not the changes in the PR, but rather the branch. Regarding the errors in #18 (comment), they only occur in
test_sort_real_schur
for 3 matrices (out of 5):# M_2 E AssertionError: E Not equal to tolerance rtol=1e-05, atol=5 E E Mismatched elements: 4 / 4 (100%) E Max absolute difference: 296.85321743 E Max relative difference: 296.85321743 E x: array([297.853217, 297.853217, 297.667196, 297.667196]) E y: array(1.) # M_3 E AssertionError: E Not equal to tolerance rtol=1e-05, atol=5 E E Mismatched elements: 4 / 4 (100%) E Max absolute difference: 317.45258363 E Max relative difference: 317.45258363 E x: array([318.452584, 318.452584, 318.293397, 318.293397]) E y: array(1.) # M_5 E AssertionError: E Not equal to tolerance rtol=1e-05, atol=5 E E Mismatched elements: 4 / 4 (100%) E Max absolute difference: 26991.94731741 E Max relative difference: 26991.94731741 E x: array([26992.947317, 26992.947317, 26022.44675 , 26022.44675 ]) E y: array(1.)
ping @Marius1311 #18 (comment) - is this fine for us?
Hi @michalk8 , I still have no idea what this test is testing for, so I can't say whether it's okay for us whether it fails. @msmdev , could you please explain the logic of this test? To facilitate this, here's the code:
def test_sort_real_schur(self, R_i: np.ndarray): # test_SRSchur_num_t Q = np.eye(4) QQ, RR, ap = sort_real_schur(Q, R_i, z="LM", b=0) assert np.all(np.array(ap) <= 1), ap EQ = np.true_divide(np.linalg.norm(Q - QQ.T @ QQ, ord=1), eps) assert_allclose(EQ, 1.0, atol=5) EA = np.true_divide(np.linalg.norm(R_i - QQ @ RR @ QQ.T, ord=1), eps * np.linalg.norm(R_i, ord=1)) assert_allclose(EA, 1.0, atol=5) l = eigs(R_i, k=4, which="SM", return_eigenvectors=False) ll = eigs(RR, k=4, which="SM", return_eigenvectors=False) EL = np.true_divide(np.abs(l - ll), eps * np.abs(l)) assert_allclose(EL, 1.0, atol=5)
According to @michalk8 , we're failing at the last assertion, which checks whether the smallest eigenvalues of these matrices are similar, if I understand this correctly.
@Marius1311 @michalk8 Okay, this is not so trivial: those tests are taken from Brandts original publication of the SRSchur sorting algorithm: They test normal execution with test cases 1-4 from Table I in (1) Brandts, J. H. Matlab Code for Sorting Real Schur Forms. Numer. Linear Algebr. with Appl. 2002, 9 (3), 249-261 and a fifth test case designed from the first test matrix M1 by swapping of the first block A11 with the second block A22. The formula for EA had a typo in (1), the correct formula was taken from (2) Bai, Z.; Demmel, J. W. On Swapping Diagonal Blocks in Real Schur Form. Linear Algebra Appl. 1993, 186 (C), 75-95.
Please take a look there to get an impression of what we are testing here... Anyway, I would consider this test important and it shouldn't fail so badly... Just one idea: you are using scipy.sparse.linalg.eigs to get the eigenvalues, right? This might again (I already mentioned this some time ago...) not be such a good idea .
Thanks a lot @msmdev , I completely forgot about that, switching to eigvals
solved it!
I'm happy to confirm that all the tests now pass.
@Marius1311 @msmdev would be great if both of you could confirm this by running pytest tests/analysis/impl/dense/gpcca_test.py
You might need to pip install -e.[test]
, since I've added pytest-mock
requirement (needed for this 1 test): https://github.com/michalk8/msmtools/blob/gpcca_tests/tests/analysis/impl/dense/gpcca_test.py#L339
@msmdev regarding the discrepancy between sparse/dense The rotation matrix is similar in absolute difference (10e-2), just the signs are sometimes flipped and when comparing the memberships from Matlab's ground truth, the max. absolute difference is less than 1-e4. michalk8@212cb24#diff-a77e704d8e9642584dac7cea5a986e5cf755403a1056410c6718fc75b012d381R619
Amazing, thanks for looking into this @michalk8 . This is as I expected: the rotation matrix differs, but the final outcome in terms of membership vectors is the same. There are more things we could check for here - however, I would prioritise closing this PR and opening the upstream PR. This is more important from my point of view right now as the current situation is really a bit messy, and this PR has gotten very large already.
Thanks a lot @msmdev , I completely forgot about that, switching to
eigvals
solved it! I'm happy to confirm that all the tests now pass. @Marius1311 @msmdev would be great if both of you could confirm this by runningpytest tests/analysis/impl/dense/gpcca_test.py
You might need topip install -e.[test]
, since I've addedpytest-mock
requirement (needed for this 1 test): https://github.com/michalk8/msmtools/blob/gpcca_tests/tests/analysis/impl/dense/gpcca_test.py#L339
Thanks @msmdev for pointing this out, and thanks to @michalk8 for solving this! I will attempt to run the tests locally on my machine now.
pip install -e.[test]
on mac gives zsh: no matches found: -e.[test]
. Next, tried pip install -e '.[test]'
. This works, but says WARNING: msmtools 1.2.5+273.gf6e93f8 does not provide the extra 'test'
. I pulled before doing this, so should be up to date.
Here's the output I get upon running pytest
:
==================================================================================== short test summary info =====================================================================================
FAILED tests/analysis/test_mfpt.py::TestMfptSparse::test_mfpt_between_sets - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toarray()) or reduce k.
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[0] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[10] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[200] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[500] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[1000] - AssertionError:
FAILED tests/analysis/impl/sparse/mean_first_passage_time_test.py::TestMfpt::test_mfpt_between_sets - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig...
FAILED tests/estimation/tests/test_effective_count_matrix.py::TestEffectiveCountMatrix::test_njobs_speedup - AssertionError: 1.5465750694274902 not less than 1.368762493133545 : does not scal...
FAILED tests/estimation/tests/test_transition_matrix.py::TestTransitionRevPiSym::test_sparse - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toar...
ERROR tests/analysis/impl/dense/gpcca_test.py::TestGPCCAMatlabUnit::test_objective_1st_col
The ones relevant for us, I guess, are
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[0] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[10] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[200] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[500] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[1000] - AssertionError:
ERROR tests/analysis/impl/dense/gpcca_test.py::TestGPCCAMatlabUnit::test_objective_1st_col
For the first class (I) of failing tests, here's the code:
def test_gpcca_krylov_sparse_eq_dense(self, example_matrix_mu: np.ndarray):
P, sd = get_known_input(example_matrix_mu)
# for 3 it's fine
g_s = GPCCA(csr_matrix(P), eta=sd, method="krylov").optimize(4)
g_d = GPCCA(P, eta=sd, method="krylov").optimize(4)
assert issparse(g_s.P)
assert not issparse(g_d.P)
assert_allclose(g_s.memberships.sum(1), 1.0)
assert_allclose(g_d.memberships.sum(1), 1.0)
X_k, X_b = g_s.schur_vectors, g_d.schur_vectors
RR_k, RR_b = g_s.schur_matrix, g_d.schur_matrix
# check if it's a correct Schur form
_assert_schur(P, X_b, RR_b, N=None)
_assert_schur(P, X_k, RR_k, N=None)
# check if they span the same subspace
assert np.max(subspace_angles(X_b, X_k)) < eps
ms, md = g_s.memberships, g_d.memberships
cs, cd = g_s.coarse_grained_transition_matrix, g_d.coarse_grained_transition_matrix
perm = _find_permutation(md, ms)
cs = cs[:, perm]
> assert_allclose(cs, g_d.coarse_grained_transition_matrix)
ms = ms[:, perm]
assert_allclose(ms, md)
We're failing at the indicated assertion, which checks for equality of coarse grained transition probs. Looking into the first of these failing tests in class (I) for \mu = 0
, I get the output
E AssertionError:
E Not equal to tolerance rtol=1e-05, atol=1e-08
E
E Mismatched elements: 16 / 16 (100%)
E Max absolute difference: 0.99858631
E Max relative difference: 348.37650063
E x: array([[ 7.603764e-01, 8.171464e-02, 7.559078e-02, 8.231817e-02],
E [ 8.063700e-03, -2.684025e-03, 9.948072e-01, -1.868863e-04],
E [ 8.651307e-03, 9.944040e-01, -2.670513e-03, -3.847842e-04],
E [ 8.805835e-03, -4.138702e-04, -1.865480e-04, 9.917946e-01]])
E y: array([[ 7.613608e-01, 7.934900e-02, 9.091987e-02, 6.837028e-02],
E [ 1.013324e-02, 9.939923e-01, -3.779101e-03, -3.464779e-04],
E [ 8.836629e-03, -2.862612e-03, 9.940104e-01, 1.561961e-05],
E [ 9.662125e-03, -6.172231e-04, -1.063530e-03, 9.920186e-01]])
The problem is that the vectors are not permuted correctly. Permuting 2 and 3 (start counting from 1) in the first matrix resolves the large absolute deviation.
@michalk8, the problem for class (I) failures is the permutation scheme. Could you look into it please?
@Marius1311 I forgot to do the permutation in 1 place + also permute the rows of coarse-grained TM, could you please check again?
Here's the code of the testing function
def test_objective_1st_col(self, mocker):
# check_in_matlab: _objective
P, _ = get_known_input(mu(0))
N, M = P.shape[0], 4
_, L, _ = lu(P[:, :M])
mocker.patch(
"msmtools.util.sorted_schur.sorted_schur",
return_value=(np.eye(M), L, np.array([np.nan] * M)),
)
mocker.patch("msmtools.analysis.dense.gpcca._gram_schmidt_mod", return_value=L)
with pytest.raises(
ValueError,
match=r"The first column X\[:, 0\] of the Schur "
r"vector matrix isn't constantly equal 1.",
):
_do_schur(P, eta=np.true_divide(np.ones((N,), dtype=np.float64), N), m=M)
This was a problem with me missing the pytest-mock
dependency. When I manually installed it and run the tests again using pytest
, I got
==================================================================================== short test summary info =====================================================================================
FAILED tests/analysis/test_mfpt.py::TestMfptSparse::test_mfpt_between_sets - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toarray()) or reduce k.
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[0] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[10] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[200] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[500] - AssertionError:
FAILED tests/analysis/impl/dense/gpcca_test.py::TestPETScSLEPc::test_gpcca_krylov_sparse_eq_dense[1000] - AssertionError:
FAILED tests/analysis/impl/sparse/mean_first_passage_time_test.py::TestMfpt::test_mfpt_between_sets - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig...
FAILED tests/estimation/tests/test_effective_count_matrix.py::TestEffectiveCountMatrix::test_njobs_speedup - AssertionError: 1.9440791606903076 not less than 1.553575038909912 : does not scal...
FAILED tests/estimation/tests/test_transition_matrix.py::TestTransitionRevPiSym::test_sparse - TypeError: Cannot use scipy.linalg.eig for sparse A with k >= N - 1. Use scipy.linalg.eig(A.toar...
========================================================================== 9 failed, 387 passed, 74 warnings in 45.66s ===========================================================================
i.e. the Error was gone, problem solved.
@Marius1311 I forgot to do the permutation in 1 place + also permute the rows of coarse-grained TM, could you please check again?
I @michalk8 , just pulled and re-run pytest
, the problem persits, i.e. all class (I) test still fail. For the first matrix, the output is
E AssertionError:
E Not equal to tolerance rtol=1e-05, atol=1e-08
E
E Mismatched elements: 16 / 16 (100%)
E Max absolute difference: 0.01532909
E Max relative difference: 12.9648493
E x: array([[ 7.603764e-01, 8.171464e-02, 7.559078e-02, 8.231817e-02],
E [ 8.651307e-03, 9.944040e-01, -2.670513e-03, -3.847842e-04],
E [ 8.063700e-03, -2.684025e-03, 9.948072e-01, -1.868863e-04],
E [ 8.805835e-03, -4.138702e-04, -1.865480e-04, 9.917946e-01]])
E y: array([[ 7.613608e-01, 7.934900e-02, 9.091987e-02, 6.837028e-02],
E [ 1.013324e-02, 9.939923e-01, -3.779101e-03, -3.464779e-04],
E [ 8.836629e-03, -2.862612e-03, 9.940104e-01, 1.561961e-05],
E [ 9.662125e-03, -6.172231e-04, -1.063530e-03, 9.920186e-01]])
I've ported most of the tests from the matlab repo, some don't pass because the checks are not implemented in some of the functions specifically (but are present in the functions which call them). I've also tried to preserve the test names and variables as in the Matlab repo.
TODOs:
closes #17 closes #4