def stationary_distribution_sensitivity(T, j): r"""Calculate the sensitivity matrix for entry j the stationary distribution vector given transition matrix T. Parameters ---------- T : numpy.ndarray shape = (n, n) Transition matrix j : int entry of stationary distribution for which the sensitivity is to be computed Returns ------- x : ndarray, shape=(n, n) Sensitivity matrix for entry index around transition matrix T. Reversibility is not assumed. Remark ------ Note, that this function uses a different normalization convention for the sensitivity compared to eigenvector_sensitivity. See there for further information. """ n = len(T) lEV = numpy.ones(n) rEV = stationary_distribution(T) eVal = 1.0 T = numpy.transpose(T) vecA = numpy.zeros(n) vecA[j] = 1.0 matA = T - eVal * numpy.identity(n) # normalize s.t. sum is one using rEV which is constant matA = numpy.concatenate((matA, [lEV])) phi = numpy.linalg.lstsq(numpy.transpose(matA), vecA) phi = numpy.delete(phi[0], -1) sensitivity = -numpy.outer(rEV, phi) + numpy.dot(phi, rEV) * numpy.outer( rEV, lEV) return sensitivity
def stationary_distribution_sensitivity(T, j): r"""Calculate the sensitivity matrix for entry j the stationary distribution vector given transition matrix T. Parameters ---------- T : numpy.ndarray shape = (n, n) Transition matrix j : int entry of stationary distribution for which the sensitivity is to be computed Returns ------- x : ndarray, shape=(n, n) Sensitivity matrix for entry index around transition matrix T. Reversibility is not assumed. Remark ------ Note, that this function uses a different normalization convention for the sensitivity compared to eigenvector_sensitivity. See there for further information. """ n = len(T) lEV = numpy.ones(n) rEV = stationary_distribution(T) eVal = 1.0 T = numpy.transpose(T) vecA = numpy.zeros(n) vecA[j] = 1.0 matA = T - eVal * numpy.identity(n) # normalize s.t. sum is one using rEV which is constant matA = numpy.concatenate((matA, [lEV])) phi = numpy.linalg.lstsq(numpy.transpose(matA), vecA) phi = numpy.delete(phi[0], -1) sensitivity = -numpy.outer(rEV, phi) + numpy.dot(phi,rEV) * numpy.outer(rEV, lEV) return sensitivity
def mfpt_between_sets(T, target, origin, mu=None): """Compute mean-first-passage time between subsets of state space. Parameters ---------- T : scipy.sparse matrix Transition matrix. target : int or list of int Set of target states. origin : int or list of int Set of starting states. mu : (M,) ndarray (optional) The stationary distribution of the transition matrix T. Returns ------- tXY : float Mean first passage time between set X and Y. Notes ----- The mean first passage time :math:`\mathbf{E}_X[T_Y]` is the expected hitting time of one state :math:`y` in :math:`Y` when starting in a state :math:`x` in :math:`X`: .. math :: \mathbb{E}_X[T_Y] = \sum_{x \in X} \frac{\mu_x \mathbb{E}_x[T_Y]}{\sum_{z \in X} \mu_z} """ if mu is None: mu = stationary_distribution(T) """Stationary distribution restriced on starting set X""" nuX = mu[origin] muX = nuX / np.sum(nuX) """Mean first-passage time to Y (for all possible starting states)""" tY = mfpt(T, target) """Mean first-passage time from X to Y""" tXY = np.dot(muX, tY[origin]) return tXY
def mfpt_between_sets(T, target, origin, mu=None): r"""Compute mean-first-passage time between subsets of state space. Parameters ---------- T : (M, M) ndarray Transition matrix. target : int or list of int Set of target states. origin : int or list of int Set of starting states. mu : (M,) ndarray (optional) The stationary distribution of the transition matrix T. Returns ------- tXY : float Mean first passage time between set X and Y. Notes ----- The mean first passage time :math:`\mathbf{E}_X[T_Y]` is the expected hitting time of one state :math:`y` in :math:`Y` when starting in a state :math:`x` in :math:`X`: .. math :: \mathbb{E}_X[T_Y] = \sum_{x \in X} \frac{\mu_x \mathbb{E}_x[T_Y]}{\sum_{z \in X} \mu_z} """ if mu is None: mu = stationary_distribution(T) """Stationary distribution restriced on starting set X""" nuX = mu[origin] muX = nuX/np.sum(nuX) """Mean first-passage time to Y (for all possible starting states)""" tY = mfpt(T,target) """Mean first-passage time from X to Y""" tXY = np.dot(muX,tY[origin]) return tXY
def backward_committor_sensitivity(T, A, B, index): """ calculate the sensitivity matrix for index of the backward committor from A to B given transition matrix T. Parameters ---------- T : numpy.ndarray shape = (n, n) Transition matrix A : array like List of integer state labels for set A B : array like List of integer state labels for set B index : entry of the committor for which the sensitivity is to be computed Returns ------- x : ndarray, shape=(n, n) Sensitivity matrix for entry index around transition matrix T. Reversibility is not assumed. """ # This is really ugly to compute. The problem is, that changes in T induce changes in # the stationary distribution and so we need to add this influence, too # I implemented something which is correct, but don't ask me about the derivation n = len(T) trT = numpy.transpose(T) one = numpy.ones(n) eq = stationary_distribution(T) mEQ = numpy.diag(eq) mIEQ = numpy.diag(1.0 / eq) mSEQ = numpy.diag(1.0 / eq / eq) backT = numpy.dot(mIEQ, numpy.dot(trT, mEQ)) qMat = forward_committor_sensitivity(backT, A, B, index) matA = trT - numpy.identity(n) matA = numpy.concatenate((matA, [one])) phiM = numpy.linalg.pinv(matA) phiM = phiM[:, 0:n] trQMat = numpy.transpose(qMat) d1 = numpy.dot(mSEQ, numpy.diagonal(numpy.dot(numpy.dot(trT, mEQ), trQMat), 0)) d2 = numpy.diagonal(numpy.dot(numpy.dot(trQMat, mIEQ), trT), 0) psi1 = numpy.dot(d1, phiM) psi2 = numpy.dot(-d2, phiM) v1 = psi1 - one * numpy.dot(psi1, eq) v3 = psi2 - one * numpy.dot(psi2, eq) part1 = numpy.outer(eq, v1) part2 = numpy.dot(numpy.dot(mEQ, trQMat), mIEQ) part3 = numpy.outer(eq, v3) sensitivity = part1 + part2 + part3 return sensitivity
def backward_committor_sensitivity(T, A, B, index): """ calculate the sensitivity matrix for index of the backward committor from A to B given transition matrix T. Parameters ---------- T : numpy.ndarray shape = (n, n) Transition matrix A : array like List of integer state labels for set A B : array like List of integer state labels for set B index : entry of the committor for which the sensitivity is to be computed Returns ------- x : ndarray, shape=(n, n) Sensitivity matrix for entry index around transition matrix T. Reversibility is not assumed. """ # This is really ugly to compute. The problem is, that changes in T induce changes in # the stationary distribution and so we need to add this influence, too # I implemented something which is correct, but don't ask me about the derivation n = len(T) trT = numpy.transpose(T) one = numpy.ones(n) eq = stationary_distribution(T) mEQ = numpy.diag(eq) mIEQ = numpy.diag(1.0 / eq) mSEQ = numpy.diag(1.0 / eq / eq) backT = numpy.dot(mIEQ, numpy.dot( trT, mEQ)) qMat = forward_committor_sensitivity(backT, A, B, index) matA = trT - numpy.identity(n) matA = numpy.concatenate((matA, [one])) phiM = numpy.linalg.pinv(matA) phiM = phiM[:,0:n] trQMat = numpy.transpose(qMat) d1 = numpy.dot( mSEQ, numpy.diagonal(numpy.dot( numpy.dot(trT, mEQ), trQMat), 0) ) d2 = numpy.diagonal(numpy.dot( numpy.dot(trQMat, mIEQ), trT), 0) psi1 = numpy.dot(d1, phiM) psi2 = numpy.dot(-d2, phiM) v1 = psi1 - one * numpy.dot(psi1, eq) v3 = psi2 - one * numpy.dot(psi2, eq) part1 = numpy.outer(eq, v1) part2 = numpy.dot( numpy.dot(mEQ, trQMat), mIEQ) part3 = numpy.outer(eq, v3) sensitivity = part1 + part2 + part3 return sensitivity