def anal_Am(np_pmf, pmf_index, sys_index, m): """ Analytic A_m """ m_ind = len(pmf_index[0]) - 2 - m if m_ind < 0: print("require seq_len > 1+m") return None s_ind = pmf_index[sys_index][:, [-2, -1]].flatten() e_ind = np.delete(pmf_index, sys_index, axis=0)[:, m_ind:-1].flatten() se_ind = np.concatenate((s_ind, e_ind)) margin_index = np.delete(pmf_index.flatten(), se_ind) margin_pmf = prob.numpy_margin(np_pmf, margin_index) x_ind = [list(se_ind).index(x) for x in s_ind][0:len(sys_index)] y_ind = [list(se_ind).index(x) for x in s_ind][len(sys_index):2 * len(sys_index)] # print("indices") # print(pmf_index) # print(se_ind) # print(total_ind) # inf.numpy_conditional_mutual_information() return inf.numpy_conditional_mutual_information(margin_pmf, x_ind, y_ind)
def anal_step_MI(np_pmf, pmf_index, xind, yind): """ I(X;Y) """ xy_index = list(itt.chain(*xind, *yind)) margin_index = np.delete(pmf_index.flatten(), xy_index) margin_pmf = prob.numpy_margin(np_pmf, margin_index) XIND = [list(xy_index).index(x) for x in list(itt.chain(*xind))] YIND = [list(xy_index).index(x) for x in list(itt.chain(*yind))] return inf.numpy_mutual_information(margin_pmf, XIND, YIND)
def anal_Astar(np_pmf, pmf_index, sys_index): """ Analytic A* """ s_ind = pmf_index[sys_index][:, [-2, -1]].flatten() margin_index = np.delete(pmf_index.flatten(), s_ind) margin_pmf = prob.numpy_margin(np_pmf, margin_index) x_ind = [list(s_ind).index(x) for x in s_ind][0:len(sys_index)] y_ind = [list(s_ind).index(x) for x in s_ind][len(sys_index):2 * len(sys_index)] return inf.numpy_mutual_information(margin_pmf, x_ind, y_ind)
def anal_step_cond_MI(np_pmf, pmf_index, xind, yind, zind=()): """ I(X;Y|Z), z optional """ xy_index = list(itt.chain(*xind, *yind, *zind)) margin_index = np.delete(pmf_index.flatten(), xy_index) margin_pmf = prob.numpy_margin(np_pmf, margin_index) XIND = [list(xy_index).index(x) for x in list(itt.chain(*xind))] YIND = [list(xy_index).index(x) for x in list(itt.chain(*yind))] if zind == (): return inf.numpy_mutual_information(margin_pmf, XIND, YIND) return inf.numpy_conditional_mutual_information(margin_pmf, XIND, YIND)
def numpy_conditional_mutual_information(np_pmf, x_index, y_index): """ Conditional Mutual Information I(X;Y|Z) Parameters ---------- np_pmf : array_like A numpy array of the form [x1, ... , xn, p(x1, ... , xn)] with length n+1 x_index : list index for X in p(X,Y,Z) for I(X;Y|Z) y_index : list index for Y in p(X,Y,Z) for I(X;Y|Z) """ var_index = [x_index, y_index] for i, var in enumerate(var_index): var_index[i] = [var] if isinstance(var, int) else var # marginalizing pmf_z, inverse_index_z = prob.numpy_margin(np_pmf, [*var_index[0], *var_index[1]], return_inverse=True) pmf_xz, inverse_index_xz = prob.numpy_margin(np_pmf, var_index[1], return_inverse=True) pmf_yz, inverse_index_yz = prob.numpy_margin(np_pmf, var_index[0], return_inverse=True) CMI = 0 for i, p_xyz in enumerate(np_pmf[-1]): p_z = pmf_z[-1][inverse_index_z[i]] p_xz = pmf_xz[-1][inverse_index_xz[i]] p_yz = pmf_yz[-1][inverse_index_yz[i]] CMI += p_xyz * np.log2((p_z * p_xyz) / (p_xz * p_yz)) return CMI
def numpy_mutual_information(np_pmf, x_index, y_index): """ Mutual information I(X;Y) Parameters ---------- np_pmf : array_like A numpy array of the form [x1, ... , xn, p(x1, ... , xn)] with length n+1 x_index : list index for X in p(X,Y,Z) for I(X;Y|Z) y_index : list index for Y in p(X,Y,Z) for I(X;Y|Z) Note ---- .. math:: I(X;Y) = \sum_{x,y} p_{X,Y}(x,y) \log \frac{p_{X,Y}(x,y)}{p_X(x) p_Y(y)} = H(X) - H(X \mid Y) """ # y_index = np.delete(np.arange(len(np_pmf)-1), x_index) var_index = [x_index, y_index] # marginalizing pmf_x, inverse_index_x = prob.numpy_margin(np_pmf, var_index[1], return_inverse=True) pmf_y, inverse_index_y = prob.numpy_margin(np_pmf, var_index[0], return_inverse=True) MI = 0 for i, p_xy in enumerate(np_pmf[-1]): p_x = pmf_x[-1][inverse_index_x[i]] p_y = pmf_y[-1][inverse_index_y[i]] MI += p_xy * np.log2(p_xy / (p_x * p_y)) return MI
def anal_non_heteronomy(np_pmf, pmf_index, sys_index, m): """ Analytic H(S_n+1 | E_n,... E_n-m) """ m_ind = len(pmf_index[0]) - 2 - m if m_ind < 0: print("require seq_len > 1+m") return None s_ind = pmf_index[sys_index][:, -1].flatten() e_ind = np.delete(pmf_index, sys_index, axis=0)[:, m_ind:-1].flatten() se_ind = np.concatenate((s_ind, e_ind)) margin_index = np.delete(pmf_index.flatten(), se_ind) margin_pmf = prob.numpy_margin(np_pmf, margin_index) env_cond_index = [list(se_ind).index(x) for x in e_ind] return inf.numpy_conditional_entropy(margin_pmf, env_cond_index)
def anal_IF(np_pmf, pmf_index, x_index): """ Analytic IF(X->Y) = I(Y_{n+1}:X_{n}|Y_{n}) """ pmf_index = np.array(pmf_index) X_n = pmf_index[x_index][:, -2].flatten() Y_n = np.delete(pmf_index, x_index, axis=0)[:, -2].flatten() Y_n1 = np.delete(pmf_index, x_index, axis=0)[:, -1].flatten() flat_ind = np.sort(np.concatenate((X_n, Y_n, Y_n1))) margin_index = np.delete(pmf_index.flatten(), flat_ind) margin_pmf = prob.numpy_margin(np_pmf, margin_index) x_ind = [list(flat_ind).index(x) for x in X_n] y_ind = [list(flat_ind).index(y) for y in Y_n1] return inf.numpy_conditional_mutual_information(margin_pmf, x_ind, y_ind)