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)
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)
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__ = {}
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)
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)
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
def order(self): return order_of(self.D0)
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 ]