Skip to content

Commit 05fa05c

Browse files
authored
Print some deviations (#29)
* Print some deviations * Add more prints for stat. distr. check * Add print statments in main file
1 parent 0e77c1d commit 05fa05c

2 files changed

Lines changed: 41 additions & 15 deletions

File tree

pygpcca/_gpcca.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ def _gram_schmidt_mod(X: np.ndarray, eta: np.ndarray) -> np.ndarray:
161161
)
162162
# Raise, if the (Schur)vectors aren't orthogonal!
163163
if not np.allclose(Q.conj().T.dot(Q), np.eye(Q.shape[1]), atol=1e-8, rtol=1e-5):
164-
raise ValueError("(Schur)vectors do not appear to be orthogonal.")
164+
dev = np.max(np.abs(Q.conj().T.dot(Q) - np.eye(Q.shape[1])))
165+
raise ValueError(
166+
f"(Schur)vectors do not appear to be orthogonal. Largest absolute element-wise deviation in "
167+
f"(Q^*TQ - I) is {dev}"
168+
)
165169

166170
return Q
167171

@@ -229,11 +233,14 @@ def _do_schur(
229233
if eta.shape[0] != N1:
230234
raise ValueError("eta vector length doesn't match with the shape of P.")
231235
if not np.allclose(np.sum(P, 1), 1.0, rtol=1e-6, atol=1e-6): # previously eps
236+
dev = np.max(np.abs(np.sum(P, 1) - 1.0))
232237
raise ValueError(
233-
"Not all rows of P sum up to one (within numerical precision). P must be a row-stochastic matrix."
238+
f"Not all rows of P sum up to one. P must be a row-stochastic matrix Largest deviation from row-sums to 1 "
239+
f"is {dev}."
234240
)
235241
if not np.all(eta > EPS):
236-
raise ValueError("Not all elements of eta are > 0 (within numerical precision).")
242+
smallest_eta = np.min(eta)
243+
raise ValueError(f"Not all elements of eta are > 0. The smallest element is {smallest_eta}")
237244

238245
# Weight the stochastic matrix P by the input (initial) distribution eta.
239246
if issparse(P):
@@ -279,12 +286,19 @@ def _do_schur(
279286
)
280287
# Raise, if the first column X[:,0] of the Schur vector matrix isn't constantly equal 1!
281288
if not np.allclose(X[:, 0], 1.0, atol=1e-8, rtol=1e-5):
282-
raise ValueError("The first column X[:, 0] of the Schur vector matrix isn't constantly equal 1.")
289+
dev = np.max(np.abs(X[:, 0] - 1.0))
290+
raise ValueError(
291+
f"The first column X[:, 0] of the Schur vector matrix isn't constantly equal 1. The largest "
292+
f"deviation from one is {dev}."
293+
)
283294

284295
# Raise, if the (Schur)vectors aren't D-orthogonal (don't fullfill the orthogonality condition)!
285296
if not np.allclose(X.T.dot(X * eta[:, None]), np.eye(X.shape[1]), atol=1e-6, rtol=1e-5):
286-
logging.error(X.T.dot(X * eta[:, None]))
287-
raise ValueError("Schur vectors appear to not be D-orthogonal.")
297+
dev = np.max(np.abs(X.T.dot(X * eta[:, None]) - np.eye(X.shape[1])))
298+
raise ValueError(
299+
f"Schur vectors appear to not be D-orthogonal. The largets deviation of X^T D X from the "
300+
f"identity matrix is {dev}"
301+
)
288302

289303
# Raise, if X doesn't fullfill the invariant subspace condition!
290304
dp = np.dot(P, sp.csr_matrix(X) if issparse(P) else X)
@@ -372,8 +386,9 @@ def _indexsearch(X: np.ndarray) -> np.ndarray:
372386
diffs = np.abs(np.max(X, axis=0) - np.min(X, axis=0))
373387
if not np.isclose(1.0 + diffs[0], 1.0, rtol=1e-6):
374388
raise ValueError(
375-
"First Schur vector is not constant 1. This indicates that the Schur vectors "
376-
"are incorrectly sorted. Cannot search for a simplex structure in the data."
389+
f"First Schur vector is not constant 1. This indicates that the Schur vectors "
390+
f"are incorrectly sorted. Cannot search for a simplex structure in the data. The largest deviation from 1 "
391+
f"is {diffs[0]}."
377392
)
378393
if not np.all(diffs[1:] > 1e-6):
379394
which = np.sum(diffs[1:] <= 1e-6)
@@ -516,12 +531,16 @@ def _opt_soft(X: np.ndarray, rot_matrix: np.ndarray) -> Tuple[np.ndarray, np.nda
516531
# Check for negative elements in chi and handle them.
517532
if np.any(chi < 0.0):
518533
if np.any(chi < -10 * EPS):
519-
raise ValueError(f"Some elements of chi are significantly negative: < {-10 * EPS}.")
534+
min_el = np.min(chi)
535+
raise ValueError(f"Some elements of chi are significantly negative. The minimal element in chi is {min_el}")
520536
else:
521537
chi[chi < 0.0] = 0.0
522538
chi = np.true_divide(1.0, np.sum(chi, axis=1))[:, np.newaxis] * chi
523539
if not np.allclose(np.sum(chi, axis=1), 1.0, atol=1e-8, rtol=1e-5):
524-
raise ValueError("The rows of chi don't sum up to 1.0 after rescaling.")
540+
dev = np.max(np.abs(np.sum(chi, axis=1) - 1.0))
541+
raise ValueError(
542+
f"The rows of chi don't sum up to 1.0 after rescaling. Maximum deviation from 1 is " f"{dev}"
543+
)
525544

526545
return rot_matrix, chi, fopt
527546

pygpcca/utils/_utils.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ def _sds(P: spmatrix) -> np.ndarray:
274274

275275
# check for irreducibility
276276
if np.allclose(vals, 1, rtol=1e2 * EPS, atol=1e2 * EPS):
277-
raise ValueError("This matrix is reducible.")
277+
second_largest = np.min(vals)
278+
raise ValueError(f"This matrix is reducible. The second largest eigenvalue is {second_largest}.")
278279

279280
# sort by real part and take the top one
280281
p = np.argsort(vals.real)[::-1]
@@ -289,7 +290,8 @@ def _sds(P: spmatrix) -> np.ndarray:
289290

290291
# check the sign structure
291292
if not (top_vec > -1e4 * EPS).all() and not (top_vec < 1e4 * EPS).all():
292-
raise ValueError("Top eigenvector has both positive and negative entries.")
293+
el_min, el_max = np.min(top_vec), np.max(top_vec)
294+
raise ValueError(f"Top eigenvector has both positive and negative entries. It has range = [{el_min}, {el_max}]")
293295
top_vec = np.abs(top_vec)
294296
pi = top_vec / np.sum(top_vec)
295297

@@ -415,14 +417,19 @@ def _is_stationary_distribution(T: Union[np.ndarray, spmatrix], pi: np.ndarray)
415417

416418
# check for invariance
417419
if not np.allclose(T.T.dot(pi), pi, rtol=1e4 * EPS, atol=1e4 * EPS):
418-
raise ValueError("Stationary distribution is not invariant under the transition matrix.")
420+
dev = np.max(np.abs(T.T.dot(pi) - pi))
421+
raise ValueError(
422+
f"Stationary distribution is not invariant under the transition matrix. Maximal deviation = " f"{dev}"
423+
)
419424

420425
# check for positivity
421426
if not (pi > -1e4 * EPS).all():
422-
raise ValueError("Stationary distribution has negative elements.")
427+
dev = np.min(pi)
428+
raise ValueError(f"Stationary distribution has negative elements. Minimal element = {dev}")
423429

424430
# check whether it sums to one
425431
if not np.allclose(pi.sum(), 1, rtol=1e4 * EPS, atol=1e4 * EPS):
426-
raise ValueError("Stationary distribution doe not sum to one.")
432+
dev = np.abs(pi.sum() - 1)
433+
raise ValueError(f"Stationary distribution doe not sum to one. Deviation = {dev}.")
427434

428435
return True

0 commit comments

Comments
 (0)