def test_simulate_issue591(): """ Test MarkovChasin.simulate for P with dtype=np.float32 https://github.com/QuantEcon/QuantEcon.py/issues/591 """ num_states = 5 transition_states = 4 transition_seed = 2 random_state = np.random.RandomState(transition_seed) transitions = random_state.uniform(0., 1., transition_states) transitions /= np.sum(transitions) P = np.zeros((num_states, num_states), dtype=np.float32) P[0, :transition_states] = transitions P[1:, 0] = 1. mc = MarkovChain(P=P) simulate_seed = 22220 ts_length = 10000 seq = mc.simulate(ts_length=ts_length, init=0, num_reps=1, random_state=simulate_seed) max_state_in_seq = np.max(seq) assert_array_less(max_state_in_seq, num_states)
def test_simulate_init_array_num_reps(): P = [[0.4, 0.6], [0.2, 0.8]] mc = MarkovChain(P) ts_length = 10 init = [0, 1] num_reps = 3 X = mc.simulate(ts_length, init, num_reps) assert_array_equal(X[:, 0], init * num_reps)
def test_simulate_init_array_num_reps(): P = [[0.4, 0.6], [0.2, 0.8]] mc = MarkovChain(P) ts_length = 10 init=[0, 1] num_reps=3 X = mc.simulate(ts_length, init, num_reps) assert_array_equal(X[:, 0], init*num_reps)
def test_simulate_init_type(): P = [[0.4, 0.6], [0.2, 0.8]] mc = MarkovChain(P) seed = 0 ts_length = 3 init = 0 # int X = mc.simulate(ts_length, init=init, random_state=seed) inits_np_int = [t(init) for t in [np.int32, np.int64]] for init in inits_np_int: X_np_int = mc.simulate(ts_length, init=init, random_state=seed) assert_array_equal(X_np_int, X)
def test_simulate_ergodicity(): P = [[0.4, 0.6], [0.2, 0.8]] stationary_dist = [0.25, 0.75] init = 0 mc = MarkovChain(P) seed = 4433 ts_length = 100 num_reps = 300 tol = 0.1 x = mc.simulate(ts_length, init=init, num_reps=num_reps, random_state=seed) frequency_1 = x[:, -1].mean() ok_(np.abs(frequency_1 - stationary_dist[1]) < tol)
def test_mc_stationary_distributions_state_values(): P = [[0.4, 0.6, 0], [0.2, 0.8, 0], [0, 0, 1]] state_values = ['a', 'b', 'c'] mc = MarkovChain(P, state_values=state_values) stationary_dists_expected = [[0.25, 0.75, 0], [0, 0, 1]] stationary_dists = mc.stationary_distributions assert_allclose(stationary_dists, stationary_dists_expected)
def test_simulate_dense_vs_sparse(): n = 5 a = 1 / 3 b = (1 - a) / 2 P = np.zeros((n, n)) for i in range(n): P[i, (i - 1) % n], P[i, i], P[i, (i + 1) % n] = b, a, b mcs = [MarkovChain(P), MarkovChain(sparse.csr_matrix(P))] ts_length = 10 inits = (None, 0, [0, 1]) num_repss = (None, 2) random_state = 0 for init, num_reps in itertools.product(inits, num_repss): assert_array_equal( *(mc.simulate(ts_length, init, num_reps, random_state=random_state) for mc in mcs))
def setUp(self): state_values = [[0, 1], [2, 3], [4, 5]] # Pass python list self.state_values = np.array(state_values) self.mc_reducible_dict = { 'mc': MarkovChain([[1, 0, 0], [1, 0, 0], [0, 0, 1]], state_values=state_values), 'coms': [[0], [1], [2]], 'recs': [[0], [2]] } self.mc_periodic_dict = { 'mc': MarkovChain([[0, 1, 0], [0, 0, 1], [1, 0, 0]], state_values=state_values), 'coms': [[0, 1, 2]], 'recs': [[0, 1, 2]], 'cycs': [[0], [1], [2]] }
def test_raises_value_error_simulate_init_out_of_range(): P = [[0.4, 0.6], [0.2, 0.8]] mc = MarkovChain(P) n = mc.n ts_length = 3 assert_raises(ValueError, mc.simulate, ts_length, init=n) assert_raises(ValueError, mc.simulate, ts_length, init=-(n + 1)) assert_raises(ValueError, mc.simulate, ts_length, init=[0, n]) assert_raises(ValueError, mc.simulate, ts_length, init=[0, -(n + 1)])
def test_simulate_shape(): P = [[0.4, 0.6], [0.2, 0.8]] mc = MarkovChain(P) (ts_length, init, num_reps) = (10, None, None) assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (ts_length,)) (ts_length, init, num_reps) = (10, [0, 1], None) assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (len(init), ts_length)) (ts_length, init, num_reps) = (10, [0, 1], 3) assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (len(init)*num_reps, ts_length)) for (ts_length, init, num_reps) in [(10, None, 3), (10, None, 1)]: assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (num_reps, ts_length))
def setUp(self): """ Setup a KMRMarkovMatrix and Compute Stationary Values """ self.P = KMR_Markov_matrix_sequential(self.N, self.p, self.epsilon) self.mc = MarkovChain(self.P) self.stationary = self.mc.stationary_distributions stat_shape = self.stationary.shape if len(stat_shape) == 1: self.n_stat_dists = 1 else: self.n_stat_dists = stat_shape[0]
def test_simulate_shape(): P = [[0.4, 0.6], [0.2, 0.8]] mcs = [MarkovChain(P), MarkovChain(sparse.csr_matrix(P))] for mc in mcs: (ts_length, init, num_reps) = (10, None, None) assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (ts_length,)) (ts_length, init, num_reps) = (10, [0, 1], None) assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (len(init), ts_length)) (ts_length, init, num_reps) = (10, [0, 1], 3) assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (len(init)*num_reps, ts_length)) for (ts_length, init, num_reps) in [(10, None, 3), (10, None, 1)]: assert_array_equal(mc.simulate(ts_length, init, num_reps).shape, (num_reps, ts_length))
def test_get_index(): P = [[0.4, 0.6], [0.2, 0.8]] mc = MarkovChain(P) eq_(mc.get_index(0), 0) eq_(mc.get_index(1), 1) assert_raises(ValueError, mc.get_index, 2) assert_array_equal(mc.get_index([1, 0]), [1, 0]) assert_raises(ValueError, mc.get_index, [[1]]) mc.state_values = [1, 2] eq_(mc.get_index(1), 0) eq_(mc.get_index(2), 1) assert_raises(ValueError, mc.get_index, 0) assert_array_equal(mc.get_index([2, 1]), [1, 0]) assert_raises(ValueError, mc.get_index, [[1]]) mc.state_values = [[1, 2], [3, 4]] eq_(mc.get_index([1, 2]), 0) assert_raises(ValueError, mc.get_index, 1) assert_array_equal(mc.get_index([[3, 4], [1, 2]]), [1, 0])
def test_simulate_for_matrices_with_C_F_orders(): """ Test MarkovChasin.simulate for matrices with C- and F-orders See the issue and fix on Numba: github.com/numba/numba/issues/1103 github.com/numba/numba/issues/1104 """ P_C = np.array([[0.5, 0.5], [0, 1]], order='C') P_F = np.array([[0.5, 0.5], [0, 1]], order='F') init = 1 ts_length = 10 sample_path = np.ones(ts_length, dtype=int) computed_C_and_F = \ MarkovChain(np.array([[1.]])).simulate(ts_length, init=0) assert_array_equal(computed_C_and_F, np.zeros(ts_length, dtype=int)) computed_C = MarkovChain(P_C).simulate(ts_length, init) computed_F = MarkovChain(P_F).simulate(ts_length, init=init) assert_array_equal(computed_C, sample_path) assert_array_equal(computed_F, sample_path)
def test_raises_value_error_non_2dim(): """Test with non 2dim input""" mc = MarkovChain(np.array([0.4, 0.6]))
def test_raises_value_error_non_sym(): """Test with non symmetric input""" mc = MarkovChain(np.array([[0.4, 0.6]]))
def test_raises_value_error_non_nonnegative(): """Test with non nonnegative input""" mc = MarkovChain(np.array([[0.4, 0.6], [-0.2, 1.2]]))
def test_raises_value_error_non_sum_one(): """Test with input such that some of the rows does not sum to one""" mc = MarkovChain(np.array([[0.4, 0.6], [0.2, 0.9]]))
def test_markovchain_pmatrices(): """ Test the methods of MarkovChain, as well as mc_compute_stationary, with P matrix and known solutions """ # Matrix with two recurrent classes [0, 1] and [3, 4, 5], # which have periods 2 and 3, respectively Q = np.zeros((6, 6)) Q[0, 1], Q[1, 0] = 1, 1 Q[2, [0, 3]] = 1 / 2 Q[3, 4], Q[4, 5], Q[5, 3] = 1, 1, 1 Q_stationary_dists = \ np.array([[1/2, 1/2, 0, 0, 0, 0], [0, 0, 0, 1/3, 1/3, 1/3]]) testset = [ { 'P': np.array([[0.4, 0.6], [0.2, 0.8]]), # P matrix 'stationary_dists': np.array([[0.25, 0.75]]), # Known solution 'comm_classes': [np.arange(2)], 'rec_classes': [np.arange(2)], 'is_irreducible': True, 'period': 1, 'is_aperiodic': True, 'cyclic_classes': [np.arange(2)], }, { 'P': sparse.csr_matrix([[0.4, 0.6], [0.2, 0.8]]), 'stationary_dists': np.array([[0.25, 0.75]]), 'comm_classes': [np.arange(2)], 'rec_classes': [np.arange(2)], 'is_irreducible': True, 'period': 1, 'is_aperiodic': True, 'cyclic_classes': [np.arange(2)], }, { 'P': np.array([[0, 1], [1, 0]]), 'stationary_dists': np.array([[0.5, 0.5]]), 'comm_classes': [np.arange(2)], 'rec_classes': [np.arange(2)], 'is_irreducible': True, 'period': 2, 'is_aperiodic': False, 'cyclic_classes': [np.array([0]), np.array([1])], }, { 'P': np.eye(2), 'stationary_dists': np.array([[1, 0], [0, 1]]), 'comm_classes': [np.array([0]), np.array([1])], 'rec_classes': [np.array([0]), np.array([1])], 'is_irreducible': False, 'period': 1, 'is_aperiodic': True, }, # Reducible mc with a unique recurrent class, # where n-1 is a transient state { 'P': np.array([[1, 0], [1, 0]]), 'stationary_dists': np.array([[1, 0]]), 'comm_classes': [np.array([0]), np.array([1])], 'rec_classes': [np.array([0])], 'is_irreducible': False, 'period': 1, 'is_aperiodic': True, }, { 'P': Q, 'stationary_dists': Q_stationary_dists, 'comm_classes': [np.array([0, 1]), np.array([2]), np.array([3, 4, 5])], 'rec_classes': [np.array([0, 1]), np.array([3, 4, 5])], 'is_irreducible': False, 'period': 6, 'is_aperiodic': False, }, { 'P': sparse.csr_matrix(Q), 'stationary_dists': Q_stationary_dists, 'comm_classes': [np.array([0, 1]), np.array([2]), np.array([3, 4, 5])], 'rec_classes': [np.array([0, 1]), np.array([3, 4, 5])], 'is_irreducible': False, 'period': 6, 'is_aperiodic': False, } ] # Loop Through TestSet # for test_dict in testset: mc = MarkovChain(test_dict['P']) computed = mc.stationary_distributions assert_allclose(computed, test_dict['stationary_dists']) assert (mc.num_communication_classes == len(test_dict['comm_classes'])) assert (mc.is_irreducible == test_dict['is_irreducible']) assert (mc.num_recurrent_classes == len(test_dict['rec_classes'])) list_of_array_equal( sorted(mc.communication_classes, key=lambda x: x[0]), sorted(test_dict['comm_classes'], key=lambda x: x[0])) list_of_array_equal( sorted(mc.recurrent_classes, key=lambda x: x[0]), sorted(test_dict['rec_classes'], key=lambda x: x[0])) assert (mc.period == test_dict['period']) assert (mc.is_aperiodic == test_dict['is_aperiodic']) try: list_of_array_equal( sorted(mc.cyclic_classes, key=lambda x: x[0]), sorted(test_dict['cyclic_classes'], key=lambda x: x[0])) except NotImplementedError: assert (mc.is_irreducible is False) # Test of mc_compute_stationary computed = mc_compute_stationary(test_dict['P']) assert_allclose(computed, test_dict['stationary_dists'])
def test_light_bulb_replacement(): """ How many of the bulbs on average will have to be replaced each month? How much the budget for the repairs should be? Time of fauliure is uncertain variety of maintenance optioins failed bulbs are replaced monthly - use this for time intervals for dist """ # Define state space, the age of the bulb state_space = [0, 1, 2, 3, 4] # new, 1 month, 2 month, etc. num_states = len(state_space) num_bulbs = 1_000 # probability of bulb failing based on age of bulb in months prob = [0.5, 0.1, 0.1, 0.1, 0.2] cdf = np.cumsum(prob) cond_prob = [ p / (1 - cdf[ind - 1]) if ind > 0 else p for ind, p in enumerate(prob) ] cond_prob[-1] = 1 # roundoff error overriding transition_matrix = np.zeros(shape=(num_states, num_states)) transition_matrix[:, 0] = cond_prob for row in range(num_states - 1): transition_matrix[row, row + 1] = 1 - transition_matrix[row, 0] # Costs of inspecting & replacing light bulbs inspect_cost = 0.10 replace_cost = 2 test_cost_vector = [ inspect_cost + replace_cost * transition_matrix[x, 0] for x in range(num_states) ] inspect_vector = [inspect_cost] * num_states replace_matrix = np.array([[replace_cost] * num_states] + ([[0] * num_states] * (num_states - 1))).T # Transient probabilities # all bulbs start at age 0 (new sign) # estimate how many bulbs will be replaced during each of first 12 months? # q(n): probability dist for age of bulb at time n test_q = np.array([[1, 0, 0, 0, 0]]) # row 0 is q(0), row 1 is q(1), etc. # q_n = q_(n-1) * P for _ in range(1, 13): # for the 12 months test_q = np.vstack((test_q, np.matmul(test_q[-1], transition_matrix))) # Calculate expected transient costs - don't include month 0 exp_trans_cost = np.delete(np.matmul(test_q, test_cost_vector), 0) test_trans_cost = sum(exp_trans_cost) * num_bulbs print("test trans cost: ", test_trans_cost) num_replaced = int(sum(test_q[1:, 0]) * num_bulbs) print("Num Replaced: ", num_replaced) # Steady state probabilities # for discrete-time markov chains, as long as each state can be # reached from every other state, the transient probabilities # will approach equilibrium. markov_obj = MarkovChain(P=transition_matrix, state_values=state_space) test_steady_state = markov_obj.stationary_distributions print('steady state: ', test_steady_state) analysis = analyze_dtmc(transition_matrix, state_space, trans_kwargs={ "ts_length": 12, "init": [1, 0, 0, 0, 0] }, cost_kwargs={ "state": inspect_vector, "transition": replace_matrix, "num": num_bulbs }) test = { 'cdfs': np.array([[0.5, 1., 1., 1., 1.], [0.2, 0.2, 1., 1., 1.], [0.25, 0.25, 0.25, 1., 1.], [0.33333333, 0.33333333, 0.33333333, 0.33333333, 1.], [1., 1., 1., 1., 1.]]), 'steady_state': { 'cost': { 'kwargs': { 'num': 1000, 'state': [0.1, 0.1, 0.1, 0.1, 0.1], 'transition': np.array([[2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0]]) }, 'total': 933.3333333333333, 'vector': 0.9333333333333332 }, 'output': np.array([0.41666667, 0.20833333, 0.16666667, 0.125, 0.08333333]) }, 'transient': { 'cost': { 'kwargs': { 'num': 1000, 'state': [0.1, 0.1, 0.1, 0.1, 0.1], 'transition': np.array([[2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0], [2, 0, 0, 0, 0]]) }, 'total': 12009.303421875004, 'vector': np.array([ 1.1, 0.8, 0.75, 0.795, 1.0825, 0.99575, 0.920625, 0.8976375, 0.90770625, 0.95175438, 0.94762406, 0.93364684, 0.92705939 ]) }, 'kwargs': { 'init': [1, 0, 0, 0, 0], 'ts_length': 12 }, 'output': np.array( [[1., 0., 0., 0., 0.], [0.5, 0.5, 0., 0., 0.], [0.35, 0.25, 0.4, 0., 0.], [0.325, 0.175, 0.2, 0.3, 0.], [0.3475, 0.1625, 0.14, 0.15, 0.2], [0.49125, 0.17375, 0.13, 0.105, 0.1], [0.447875, 0.245625, 0.139, 0.0975, 0.07], [0.4103125, 0.2239375, 0.1965, 0.10425, 0.065], [0.39881875, 0.20515625, 0.17915, 0.147375, 0.0695], [0.40385313, 0.19940938, 0.164125, 0.1343625, 0.09825], [0.42587719, 0.20192656, 0.1595275, 0.12309375, 0.089575], [0.42381203, 0.21293859, 0.16154125, 0.11964563, 0.0820625], [0.41682342, 0.21190602, 0.17035088, 0.12115594, 0.07976375]]) } } # Assert two dictionaries have equal values assert all(is_analysis_equal(analysis, test)) # Assert that both have the same keys assert test.keys() == analysis.keys() assert test['steady_state'].keys() == analysis["steady_state"].keys() assert test['steady_state']['cost'].keys() == \ analysis['steady_state']['cost'].keys() assert test['steady_state']['cost']['kwargs'].keys() == \ analysis['steady_state']['cost']['kwargs'].keys() assert test['transient'].keys() == analysis["transient"].keys() assert test['transient']["kwargs"].keys() == \ analysis["transient"]["kwargs"].keys() assert test['transient']['cost'].keys() == \ analysis['transient']['cost'].keys() assert test['transient']['cost']['kwargs'].keys() == \ analysis['transient']['cost']['kwargs'].keys() # Test print_markov captured_output = io.StringIO() sys.stdout = captured_output print_markov(analysis) sys.stdout = sys.__stdout__ # reset stdout test_str = ("CDFs:\n" "[[0.5 1. 1. 1. 1. ]\n" " [0.2 0.2 1. 1. 1. ]\n" " [0.25 0.25 0.25 1. 1. ]\n" " [0.33333333 0.33333333 0.33333333 0.33333333 1. ]\n" " [1. 1. 1. 1. 1. ]]\n" "\nSteady State Probs:\n" "[0.41666667 0.20833333 0.16666667 0.125 0.08333333]\n\n" "Transient Probabilities (length 12)\n" "Initial Conditions:\n" "[1, 0, 0, 0, 0]\n" "Output:\n" "[[1. 0. 0. 0. 0. ]\n" " [0.5 0.5 0. 0. 0. ]\n" " [0.35 0.25 0.4 0. 0. ]\n" " [0.325 0.175 0.2 0.3 0. ]\n" " [0.3475 0.1625 0.14 0.15 0.2 ]\n" " [0.49125 0.17375 0.13 0.105 0.1 ]\n" " [0.447875 0.245625 0.139 0.0975 0.07 ]\n" " [0.4103125 0.2239375 0.1965 0.10425 0.065 ]\n" " [0.39881875 0.20515625 0.17915 0.147375 0.0695 ]\n" " [0.40385313 0.19940938 0.164125 0.1343625 0.09825 ]\n" " [0.42587719 0.20192656 0.1595275 0.12309375 0.089575 ]\n" " [0.42381203 0.21293859 0.16154125 0.11964563 0.0820625 ]\n" " [0.41682342 0.21190602 0.17035088 0.12115594 0.07976375]]\n" "\nCost kwargs:\n" "{'state': [0.1, 0.1, 0.1, 0.1, 0.1], 'transition': " "array([[2, 0, 0, 0, 0],\n" " [2, 0, 0, 0, 0],\n" " [2, 0, 0, 0, 0],\n" " [2, 0, 0, 0, 0],\n" " [2, 0, 0, 0, 0]]), 'num': 1000}\n" "Expected Steady State Cost:\n" "$0.93\n" "Expected Total Steady State Cost: $933.33\n" "Expected Transient Cost:\n" "[1.1 0.8 0.75 0.795 1.0825" " 0.99575\n" " 0.920625 0.8976375 0.90770625 0.95175438 0.94762406" " 0.93364684\n" " 0.92705939]\n" "Expected Total Transient Cost: $12,009.30\n") assert captured_output.getvalue() == test_str