コード例 #1
0
    def __init__(self, class_ids, classes=None, mask=None, fill_empty_classes=False):
        if classes is not None:
            self.classes = classes
        else:
            self.classes = np.unique(class_ids)

        k = len(self.classes)
        class_ids = np.array(class_ids) if not isinstance(class_ids, np.ndarray) else class_ids
        transitions = np.zeros((k, k))
        b = 1 if mask is None else mask.ravel()
        for ii in range(len(class_ids) - 1):
            np.add.at(transitions, (class_ids[ii].ravel(), class_ids[ii + 1].ravel()), b)
        self.transitions = transitions
        self.p = transitions / np.clip(transitions.sum((-1), keepdims=True), a_min=1, a_max=None)
        if fill_empty_classes:
            self.p = fill_empty_diagonals(self.p)

        p_tmp = self.p
        p_tmp = fill_empty_diagonals(p_tmp)
        markovchain = qe.MarkovChain(p_tmp)
        self.num_cclasses = markovchain.num_communication_classes
        self.num_rclasses = markovchain.num_recurrent_classes

        self.cclasses_indices = markovchain.communication_classes_indices
        self.rclasses_indices = markovchain.recurrent_classes_indices
        transient = set(list(map(tuple, self.cclasses_indices))).difference(
            set(list(map(tuple, self.rclasses_indices)))
        )
        self.num_tclasses = len(transient)
        if len(transient):
            self.tclasses_indices = [np.asarray(i) for i in transient]
        else:
            self.tclasses_indices = None
        self.astates_indices = list(np.argwhere(np.diag(p_tmp) == 1))
        self.num_astates = len(self.astates_indices)
コード例 #2
0
    def _calc(self):
        self.lclass_ids = lag_categorical(
            self.class_ids, classes=self.classes, kernel_sz=self.kernel_sz, sigma=self.sigma, T=True
        )

        T = np.zeros((self.m, self.k, self.k))
        for ii in range(len(self.class_ids) - 1):
            np.add.at(
                T, (self.lclass_ids[ii].ravel(), self.class_ids[ii].ravel(), self.class_ids[ii + 1].ravel()), 1,
            )

        P = T.copy()
        P = P / np.clip(P.sum((-1), keepdims=True), a_min=1, a_max=None)

        if self.fill_empty_classes:
            P = fill_empty_diagonals(P)

        return T, P
コード例 #3
0
ファイル: ergodic.py プロジェクト: keyunj/VVForecast_covid19
def steady_state(P, fill_empty_classes=False):
    """
    Generalized function for calculating the steady state distribution
    for a regular or reducible Markov transition matrix P.

    Parameters
    ----------
    P        : array
               (k, k), an ergodic or non-ergodic Markov transition probability
               matrix.
    fill_empty_classes: bool, optional
                        If True, assign 1 to diagonal elements which fall in rows full
                        of 0s to ensure the transition probability matrix is a
                        stochastic one. Default is False.

    Returns
    -------
             : array
               If the Markov chain is irreducible, meaning that
               there is only one communicating class, there is one unique
               steady state distribution towards which the system is
               converging in the long run. Then steady_state is the
               same as _steady_state_ergodic (k, ).
               If the Markov chain is reducible, but only has 1 recurrent
               class, there will be one steady state distribution as well.
               If the Markov chain is reducible and there are multiple
               recurrent classes (num_rclasses), the system could be trapped
               in any one of  these recurrent classes. Then, there will be
               `num_rclasses` steady state distributions. The returned array
               will of (num_rclasses, k) dimension.

    Examples
    --------

    >>> import numpy as np
    >>> from giddy.ergodic import steady_state

    Irreducible Markov chain
    >>> p = np.array([[.5, .25, .25],[.5,0,.5],[.25,.25,.5]])
    >>> steady_state(p)
    array([0.4, 0.2, 0.4])

    Reducible Markov chain: two communicating classes
    >>> p = np.array([[.5, .5, 0],[.2,0.8,0],[0,0,1]])
    >>> steady_state(p)
    array([[0.28571429, 0.71428571, 0.        ],
           [0.        , 0.        , 1.        ]])

    Reducible Markov chain: two communicating classes
    >>> p = np.array([[.5, .5, 0],[.2,0.8,0],[0,0,0]])
    >>> steady_state(p, fill_empty_classes = True)
    array([[0.28571429, 0.71428571, 0.        ],
           [0.        , 0.        , 1.        ]])

    >>> steady_state(p, fill_empty_classes = False)
    Traceback (most recent call last):
        ...
    ValueError: Input transition probability matrix has 1 rows full of 0s. Please set fill_empty_classes=True to set diagonal elements for these rows to be 1 to make sure the matrix is stochastic.

    """

    P = np.asarray(P)
    rows0 = (P.sum(axis=1) == 0).sum()
    if rows0 > 0:
        if fill_empty_classes:
            P = fill_empty_diagonals(P)
        else:
            raise ValueError("Input transition probability matrix has "
                             "%d rows full of 0s. Please set "
                             "fill_empty_classes=True to set diagonal "
                             "elements for these rows to be 1 to make "
                             "sure the matrix is stochastic." % rows0)
    mc = qe.MarkovChain(P)
    num_classes = mc.num_communication_classes
    if num_classes == 1:
        return mc.stationary_distributions[0]
    else:
        return mc.stationary_distributions
コード例 #4
0
ファイル: ergodic.py プロジェクト: keyunj/VVForecast_covid19
def fmpt(P, fill_empty_classes=False):
    """
    Generalized function for calculating first mean passage times for an
    ergodic or non-ergodic transition probability matrix.

    Parameters
    ----------
    P        : array
               (k, k), an ergodic/non-ergodic Markov transition probability
               matrix.
    fill_empty_classes: bool, optional
                        If True, assign 1 to diagonal elements which fall in rows full
                        of 0s to ensure the transition probability matrix is a
                        stochastic one. Default is False.

    Returns
    -------
    fmpt_all : array
               (k, k), elements are the expected value for the number of
               intervals required for a chain starting in state i to first
               enter state j. If i=j then this is the recurrence time.

    Examples
    --------
    >>> import numpy as np
    >>> from giddy.ergodic import fmpt
    >>> np.set_printoptions(suppress=True) #prevent scientific format

    Irreducible Markov chain
    >>> p = np.array([[.5, .25, .25],[.5,0,.5],[.25,.25,.5]])
    >>> fm = fmpt(p)
    >>> fm
    array([[2.5       , 4.        , 3.33333333],
           [2.66666667, 5.        , 2.66666667],
           [3.33333333, 4.        , 2.5       ]])

    Thus, if it is raining today in Oz we can expect a nice day to come
    along in another 4 days, on average, and snow to hit in 3.33 days. We can
    expect another rainy day in 2.5 days. If it is nice today in Oz, we would
    experience a change in the weather (either rain or snow) in 2.67 days from
    today.

    Reducible Markov chain: two communicating classes (this is an
    artificial example)
    >>> p = np.array([[.5, .5, 0],[.2,0.8,0],[0,0,1]])
    >>> fmpt(p)
    array([[3.5, 2. , inf],
           [5. , 1.4, inf],
           [inf, inf, 1. ]])

    Thus, if it is raining today in Oz we can expect a nice day to come
    along in another 2 days, on average, and should not expect snow to hit.
    We can expect another rainy day in 3.5 days. If it is nice today in Oz,
    we should expect a rainy day in 5 days.


    >>> p = np.array([[.5, .5, 0],[.2,0.8,0],[0,0,0]])
    >>> fmpt(p, fill_empty_classes=True)
    array([[3.5, 2. , inf],
           [5. , 1.4, inf],
           [inf, inf, 1. ]])

    >>> p = np.array([[.5, .5, 0],[.2,0.8,0],[0,0,0]])
    >>> fmpt(p, fill_empty_classes=False)
    Traceback (most recent call last):
        ...
    ValueError: Input transition probability matrix has 1 rows full of 0s. Please set fill_empty_classes=True to set diagonal elements for these rows to be 1 to make sure the matrix is stochastic.
    """

    P = np.asarray(P)
    rows0 = (P.sum(axis=1) == 0).sum()
    if rows0 > 0:
        if fill_empty_classes:
            P = fill_empty_diagonals(P)
        else:
            raise ValueError("Input transition probability matrix has "
                             "%d rows full of 0s. Please set "
                             "fill_empty_classes=True to set diagonal "
                             "elements for these rows to be 1 to make "
                             "sure the matrix is stochastic." % rows0)
    mc = qe.MarkovChain(P)
    num_classes = mc.num_communication_classes
    if num_classes == 1:
        fmpt_all = _fmpt_ergodic(P)
    else:  # deal with non-ergodic Markov chains
        k = P.shape[0]
        fmpt_all = np.zeros((k, k))
        for desti in range(k):
            b = np.ones(k - 1)
            p_sub = np.delete(np.delete(P, desti, 0), desti, 1)
            p_calc = np.eye(k - 1) - p_sub
            m = np.full(k - 1, np.inf)
            row0 = (p_calc != 0).sum(axis=1)
            none0 = np.arange(k - 1)
            try:
                m[none0] = np.linalg.solve(p_calc, b)
            except np.linalg.LinAlgError as err:
                if "Singular matrix" in str(err):
                    if (row0 == 0).sum() > 0:
                        index0 = set(np.argwhere(row0 == 0).flatten())
                        x = (p_calc[:, list(index0)] != 0).sum(axis=1)
                        setx = set(np.argwhere(x).flatten())
                        while not setx.issubset(index0):
                            index0 = index0.union(setx)
                            x = (p_calc[:, list(index0)] != 0).sum(axis=1)
                            setx = set(np.argwhere(x).flatten())
                        none0 = np.asarray(list(set(none0).difference(index0)))
                        if len(none0) >= 1:
                            p_calc = p_calc[none0, :][:, none0]
                            b = b[none0]
                            m[none0] = np.linalg.solve(p_calc, b)
            recc = np.nan_to_num(
                (np.delete(P, desti, 1)[desti] * m), 0,
                posinf=np.inf).sum() + 1
            fmpt_all[:, desti] = np.insert(m, desti, recc)
            fmpt_all = np.where(fmpt_all < -1e16, np.inf, fmpt_all)
            fmpt_all = np.where(fmpt_all > 1e16, np.inf, fmpt_all)
    return fmpt_all