def inertia_eigenvectors(basepos, already_centered = False): """ Given basepos (an array of positions), compute and return (as a 2-tuple) the lists of eigenvalues and eigenvectors of the inertia tensor (computed as if all points had the same mass). These lists are always length 3, even for len(basepos) of 0,1, or 2, overlapping or colinear points, etc, though some evals will be 0 in these cases. Optional small speedup: if caller knows basepos is centered at the origin, it can say so. """ #bruce 060119 split this out of shakedown_poly_evals_evecs_axis() in chunk.py basepos = A(basepos) # make sure it's a Numeric array if not already_centered and len(basepos): center = add.reduce(basepos)/len(basepos) basepos = basepos - center # compute inertia tensor tensor = zeros((3,3),Float) for p in basepos: rsq = dot(p, p) m= - multiply.outer(p, p) m[0,0] += rsq m[1,1] += rsq m[2,2] += rsq tensor += m evals, evecs = eigenvectors(tensor) assert len(evals) == len(evecs) == 3 return evals, evecs
def inertia_eigenvectors(basepos, already_centered=False): """ Given basepos (an array of positions), compute and return (as a 2-tuple) the lists of eigenvalues and eigenvectors of the inertia tensor (computed as if all points had the same mass). These lists are always length 3, even for len(basepos) of 0,1, or 2, overlapping or colinear points, etc, though some evals will be 0 in these cases. Optional small speedup: if caller knows basepos is centered at the origin, it can say so. """ #bruce 060119 split this out of shakedown_poly_evals_evecs_axis() in chunk.py basepos = A(basepos) # make sure it's a Numeric array if not already_centered and len(basepos): center = add.reduce(basepos) / len(basepos) basepos = basepos - center # compute inertia tensor tensor = zeros((3, 3), Float) for p in basepos: rsq = dot(p, p) m = -multiply.outer(p, p) m[0, 0] += rsq m[1, 1] += rsq m[2, 2] += rsq tensor += m evals, evecs = eigenvectors(tensor) assert len(evals) == len(evecs) == 3 return evals, evecs
def logm(P): """Returns logarithm of a matrix. This method should work if the matrix is positive definite and diagonalizable. """ roots, ev = eigenvectors(P) evI = inverse(ev.T) evT = ev log_roots = log(roots) return innerproduct(evT * log_roots, evI)
def logm(P): """Returns logarithm of a matrix. This method should work if the matrix is positive definite and diagonalizable. """ roots, ev = eigenvectors(P) evI = inverse(ev.T) evT = ev if not allclose(P, innerproduct(evT * roots, evI)): raise ArithmeticError("eigendecomposition failed") log_roots = log(roots) return innerproduct(evT * log_roots, evI)
def is_generator_unique(Q): """Conservatively tests whether a transition rate matrix uniquely yields its transition probability matrix""" if not Q.shape[0] in (3, 4): raise NotImplementedError("Only Q of 3x3 or 4x4 supported") assert _is_Q_ok(Q), "Q must be a valid transition rate matrix" e, V = eigenvectors(Q) n = len(e) # Assert that the matrix is diagonalisable if not allclose(V.dot(diag(e)).dot(inverse(V)), Q): raise ArithmeticError("matrix not diagonalisable") # Find the Perron-Frobenius eigenvalue PF_EV = argmin([norm(ones(n) / n - v / v.sum()) for v in V.T]) # Don't mess with the P-F eigenvalue - it has a special job to do ix = list(range(0, PF_EV)) + list(range(PF_EV + 1, n)) real_close = [] expe = exp(e) for i, j in combinations(ix, 2): if isclose(e.real[i], e.real[j]): real_close.append((i, j)) # Can't deal with non-primary roots yet if isclose(expe[i], expe[j]): raise NotImplementedError("non-primary root detected:\n" + repr(Q)) # If the real parts of the eigenvalues are distinct, we're ok # For each candidate complex conjugate pair, check for equivalent Qs for i, j in real_close: s = zeros(n) s[i] = 1.0 s[j] = -1.0 gen = 2.0 * pi * complex(0.0, 1.0) * V.dot(diag(s)).dot(inverse(V)) Qtest = Q + gen if _is_Q_ok(Qtest): return False Qtest = Q - gen if _is_Q_ok(Qtest): return False return True
def is_generator_unique(Q): """Conservatively tests whether a transition rate matrix uniquely yields its transition probability matrix""" assert Q.shape[0] in (3, 4), 'Q must be 3x3 or 4x4' assert _is_Q_ok(Q), 'Q must be a valid transition rate matrix' e, V = eigenvectors(Q) n = len(e) # Assert that the matrix is diagonalisable if not allclose(V.dot(diag(e)).dot(inverse(V)), Q): raise ArithmeticError('matrix not diagonalisable') # Find the Perron-Frobenius eigenvalue PF_EV = argmin([norm(ones(n)/n-v/v.sum()) for v in V.T]) # Don't mess with the P-F eigenvalue - it has a special job to do ix = range(0,PF_EV) + range(PF_EV+1,n) real_close = [] expe = exp(e) for i, j in combinations(ix, 2): if isclose(e.real[i], e.real[j]): real_close.append((i, j)) # Can't deal with non-primary roots yet if isclose(expe[i], expe[j]): raise NotImplementedError('non-primary root detected:\n'+repr(Q)) # If the real parts of the eigenvalues are distinct, we're ok # For each candidate complex conjugate pair, check for equivalent Qs for i, j in real_close: s = zeros(n) s[i] = 1. s[j] = -1. gen = 2.*pi*complex(0.,1.)*V.dot(diag(s)).dot(inverse(V)) Qtest = Q + gen if _is_Q_ok(Qtest): return False Qtest = Q - gen if _is_Q_ok(Qtest): return False return True
def slepian(M,width,sym=1): if (M*width > 27.38): raise ValueError, "Cannot reliably obtain slepian sequences for"\ " M*width > 27.38." if M < 1: return array([]) if M == 1: return ones(1,'d') odd = M % 2 if not sym and not odd: M = M+1 twoF = width/2.0 alpha = (M-1)/2.0 m = arange(0,M)-alpha n = m[:,NewAxis] k = m[NewAxis,:] AF = twoF*sinc(twoF*(n-k)) [lam,vec] = la.eigenvectors(AF) ind = argmax(abs(lam)) w = abs(vec[:,ind]) w = w / max(w) if not sym and not odd: w = w[:-1] return