Example #1
0
    def __init__(self, matrix: Sequence[Sequence[float]],
                 safe: bool = False,
                 tol: float = 1e-3):
        """
        Constructor of a continuous time Markov chain.

        Parameters
        ----------
        matrix : 2-D array_like
            An infinitesimal matrix (chain generator).
        safe : bool, optional
            Flag indicating the matrix is safe to use. If `False` (default),
            no validation or attempts to fix will be performed.
        tol : float, optional
            Tolerance used when fixing broken infinitesimal generator matrix.
            If `safe = True` is set, this field is ignored. Default: 1e-3.
        """
        need_copy = False
        if not isinstance(matrix, np.ndarray):
            matrix = np.asarray(matrix)
        else:
            need_copy = True

        if not safe and not is_infinitesimal(matrix):
            matrix = fix_infinitesimal(matrix, tol=tol)[0]
            need_copy = False

        self._matrix = matrix if not need_copy else matrix.copy()
        self._order = order_of(matrix)
Example #2
0
    def __init__(self, matrix: Union[Sequence[Sequence[float]], np.ndarray],
                 safe: bool = False, tol: float = 1e-3):
        """
        Discrete time Markov chain constructor.

        Parameters
        ----------
        matrix : 2-D array_like
            Transition matrix. If `safe = False` it is checked to be stochastic
            and, if check fails, attempt to fix it with `fix_stochastic()`
            call is made. When fixing, use tolerance `tol`.
        safe : bool, optional
            Flag indicating whether there is no need to validate matrix.
            Default: `False`.
        tol : float, optional
            Tolerance used when fixing broken transition matrix.
            If `safe = True` is set, this field is ignored. Default: 1e-3.
        """
        if not isinstance(matrix, np.ndarray):
            matrix = np.asarray(matrix)
        else:
            matrix = matrix.copy()  # copy to avoid side-effects
        if not is_square(matrix):
            raise MatrixShapeError('(N, N)', matrix.shape, 'transition matrix')
        if not safe and not is_stochastic(matrix):
            matrix = fix_stochastic(matrix, tol=tol)[0]
        self._matrix = matrix
        self._order = order_of(matrix)
Example #3
0
 def __init__(self, matrix, check=True, tol=1e-8, dtype=None):
     if check:
         if not is_infinitesimal(matrix, tol, tol):
             raise ValueError("not infinitesimal matrix")
     self._matrix = np.asarray(matrix, dtype=dtype)
     self._order = order_of(self._matrix)
     self.__cache__ = {}
Example #4
0
 def test_order_of_numpy_lists(self):
     self.assertEqual(qumo.order_of(self.l1), 1)
     self.assertEqual(qumo.order_of(self.l2), 2)
     self.assertEqual(qumo.order_of(self.l11), 1)
     self.assertEqual(qumo.order_of(self.l12), 2)
     self.assertEqual(qumo.order_of(self.l21), 2)
     self.assertEqual(qumo.order_of(self.l22), 2)
     with self.assertRaises(ValueError):
         qumo.order_of(self.l23)
Example #5
0
 def test_order_of_ndarray(self):
     self.assertEqual(qumo.order_of(self.m1), 1)
     self.assertEqual(qumo.order_of(self.m2), 2)
     self.assertEqual(qumo.order_of(self.m11), 1)
     self.assertEqual(qumo.order_of(self.m12), 2)
     self.assertEqual(qumo.order_of(self.m21), 2)
     self.assertEqual(qumo.order_of(self.m22), 2)
     with self.assertRaises(ValueError):
         qumo.order_of(self.m23)
Example #6
0
 def f(x, subgenerator, pmf0):
     pmf0 = np.asarray(pmf0)
     ones = np.ones(shape=(order_of(subgenerator)))
     s = np.asarray(subgenerator)
     if 0 <= x < np.inf:
         return pmf0.dot(linalg.expm(x * s)).dot(-s).dot(ones)
     else:
         return 0.0
Example #7
0
 def order(self):
     return order_of(self.D0)
Example #8
0
    def __init__(self,
                 d0: Union[np.ndarray, Sequence[Sequence[float]]],
                 d1: Union[np.ndarray, Sequence[Sequence[float]]],
                 safe: bool = False,
                 tol: float = 1e-3,
                 factory: RandomsFactory = None):
        """
        Create MAP process with the given D0 and D1 matrices.

        If `safe = False` is set (this is default), we validate matrices
        D0 and D1. These matrices must comply three requirements:

        1) Sum `D0 + D1` must be an infinitesimal matrix
        2) All elements of D1 must be non-negative
        3) All elements of D0 except the main diagonal must be non-negative

        To avoid problems with "1e-12 is not zero", constructor accepts
        parameters `tol` (tolerance). If matrices D0 and D1 fail the check,
        it tries to fix them using `fix_markovian_arrival()` call with this
        tolerance.

        Sometimes it is useful to avoid matrices validation (e.g., when MAP
        matrices are obtained as a result of another computation). To disable
        validation, set `safe = True`.

        Parameters
        ----------
        d0 : array_like
            Matrix D0, it must be subinfinitesimal
        d1 : array_like
            Matrix D1, all its elements must be non-negative
        safe : bool, optional
            Flag indicating whether not to validate or try to fix matrices D0
            and D1 if they don't satisfy requirements. By default, `False`.
        tol : float, optional
            If `safe = False` and matrices D0 and D1 violate requirements,
            try to fix errors those are less then `tol`. If `safe = True`
            this parameter is ignored. Default: 1e-3.
        """
        super().__init__(factory)
        need_copy = [False, False]
        matrices = [d0, d1]

        for i in range(2):
            if not isinstance(matrices[i], np.ndarray):
                matrices[i] = np.asarray(matrices[i])
            else:
                need_copy[i] = True

        if not safe and not check_markovian_arrival(matrices):
            matrices, _ = fix_markovian_arrival(matrices, tol=tol)
            # Since matrices are re-built, no need to copy them:
            for i in range(2):
                need_copy[i] = False

        # Copy matrices if needed:
        for i in range(2):
            if need_copy[i]:
                matrices[i] = matrices[i].copy()

        # Extract matrices:
        self._matrices = tuple(matrices)
        self._generator = sum(self._matrices)
        self._order = order_of(self._matrices[0])
        self._inv_d0 = np.linalg.inv(self._matrices[0])

        # Since we will use Mmap for data generation, we need to generate
        # special matrices for transitions probabilities and rates.

        # 1) We need to store rates (we will use them when choosing random
        #    time we spend in the state):
        self._rates = -self._matrices[0].diagonal()

        # 2) Then, we need to store cumulative transition probabilities P.
        #    We store them in a stochastic matrix of shape N x (K*N):
        #      P[I, J] is a probability, that:
        #      - new state will be J (mod N), and
        #      - if J < N, then no packet is generated, or
        #      - if J >= N, then packet of type J // N is generated.
        # self._trans_pmf = np.hstack((
        #     self._matrices[0] + np.diag(self._rates),
        #     self._matrices[1]
        # )) / self._rates[:, None]

        # Build embedded DTMC and CTMC:
        self._ctmc = ContinuousTimeMarkovChain(self._generator, safe=True)
        self._dtmc = DiscreteTimeMarkovChain(-self._inv_d0.dot(self.d1),
                                             safe=True)
        self._states = [
            Exponential(rate, factory=factory) for rate in self._rates
        ]