예제 #1
0
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
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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