def IF(data, flow_to_index, t_shift=1): """ IF(E->S) Notes ----- The lists are cut as follows: as an example take sequences of length 5 with m=2 s_n+1 = 0, 1, 2, [3, 4] } s_n = 0, 1, [2, 3], 4 } e_n = 0, 1, [2, 3], 4 } so we cut m+1 values at most from the start andn """ # check for integer flow_to_index = [flow_to_index] if isinstance(flow_to_index, int) else flow_to_index flow_from_index = np.setdiff1d(range(len(data)), flow_to_index) # make system and env lists from data data = np.array(data) data_to = np.delete(data, flow_from_index, axis=0) data_from = np.delete(data, flow_to_index, axis=0) # setting up the array series_data = np.zeros((3, len(data[0]) - t_shift)) # slice the lists series_data[0] = data_to[:, t_shift:] series_data[1] = data_from[:, :-t_shift] series_data[2] = data_to[:, :-t_shift] series_pmf = prob.numpy_pmf(series_data) return inf.numpy_conditional_mutual_information(series_pmf, 0, 1)
def non_heteronomy(data, sys_index, m): """ H(S_n+1 | E_n, ... , E_n-m) """ # check for integer sys_index = [sys_index] if isinstance(sys_index, int) else sys_index env_index = np.setdiff1d(range(len(data)), sys_index) # make system and env lists from data data = np.array(data) sys = np.delete(data, env_index, axis=0) env = np.delete(data, sys_index, axis=0) # setting up the array series_data = np.zeros((m + 3, len(data[0]) - (m + 1)), dtype=int) # slice the lists series_data[0] = sys[:, m + 1:] for i in range(m + 1): series_data[1 + i] = env[:, m - i:-1 - i] series_pmf = prob.numpy_pmf(series_data) return inf.numpy_conditional_entropy(series_pmf, list(range(len(series_data))[1:]))
def Astar(data, sys_index, t_shift=1): """ Calculate Bertschinger's A* from history Parameters ---------- data : array_like 2-D array with rows representing variables and columns representing entries in the time series. sys_index : array_like, integer Indices of the data array corresponding to the history of the system References ---------- doi:10.1016/j.biosystems.2007.05.018) """ # correct indices sys_index = [sys_index] if isinstance(sys_index, int) else sys_index env_index = np.setdiff1d(range(len(data)), sys_index) # make system list from data data = np.array(data) sys = np.delete(data, env_index, axis=0) # create (S_n+1, S_n) data data_Astar = np.vstack((sys[:, t_shift:], sys[:, :-t_shift])) Astar_pmf = prob.numpy_pmf(data_Astar) return inf.numpy_mutual_information(Astar_pmf, range(len(sys_index)))
def seq_Astar(data, sys_index, t_shift=1): """ I(S_{n+1} ; S_{n}) """ step_index = [[t_shift, 0]] series_data, series_index = seq_maker(data, sys_index, step_index, return_index=True) series_pmf = prob.numpy_pmf(series_data) return inf.numpy_mutual_information(series_pmf, range(len(*sys_index)))
def Am(data, sys_index, m): """ Calculate Bertschinger's Am from history Parameters ---------- data : array_like 2-D array with rows representing variables and columns representing entries in the time series. sys_index : array_like, integer Indices of the data array corresponding to the history of the system env_index : array_like, integer Indices of the data array corresponding to the history of the environment m : integer Length of environment history where Am = I(S_n+1; S_n | E_n, E_n-1, ... , E_n-m) References ---------- doi:10.1016/j.biosystems.2007.05.018) Notes ----- The lists are cut as follows: as an example take sequences of length 5 with m=2 s_n+1 = 0, 1, 2, [3, 4] } s_n = 0, 1, [2, 3], 4 }3 e_n = 0, 1, [2, 3], 4 }+ e_n-1 = 0, [1, 2], 3, 4 }m e_n-2 = [0, 1], 2, 3, 4 } so we cut m+1 values at most from the start andn """ # check for integer sys_index = [sys_index] if isinstance(sys_index, int) else sys_index env_index = np.setdiff1d(range(len(data)), sys_index) # make system and env lists from data data = np.array(data) sys = np.delete(data, env_index, axis=0) env = np.delete(data, sys_index, axis=0) # setting up the array series_data = np.zeros((m + 3, len(data[0]) - (m + 1)), dtype=int) # slice the lists series_data[0] = sys[:, m + 1:] series_data[1] = sys[:, m:-1] for i in range(m + 1): series_data[2 + i] = env[:, m - i:-1 - i] series_pmf = prob.numpy_pmf(series_data) return inf.numpy_conditional_mutual_information(series_pmf, 0, 1)
def seq_non_heteronomy(data, sys_index, m, t_shift=1): """ H(S_n+1 | E_n, ... , E_n-m) """ step_index = [[t_shift], [list(range(0, -(m + 1), -1))]] series_data, series_index = seq_maker(data, sys_index, step_index, return_index=True) series_pmf = prob.numpy_pmf(series_data) # fix return inf.numpy_conditional_entropy(series_pmf, list(range(len(series_data))[1:]))
def seq_Am(data, var_index, m, t_shift=1): """ I(S_{n+1} ; S_{n} | E_{n}, E_{n-1}, ... ,E_{n-m}) """ step_index = [[t_shift, 0], list(range(0, -(m + 1), -1))] series_data, series_index = seq_maker(data, var_index, step_index, return_index=True) print(series_index) series_pmf = prob.numpy_pmf(series_data) return inf.numpy_conditional_mutual_information(series_pmf, series_index[0], series_index[1])
def seq_IF(data, flow_from_index, flow_to_index, t_shift=1): """ IF(E->S) = I(S_{n+1} : E_{n}|S_{n}) """ var_index = [flow_to_index, flow_from_index] step_index = [[t_shift, 0], [0]] series_data, series_index = seq_maker(data, var_index, step_index, return_index=True) series_pmf = prob.numpy_pmf(series_data) return inf.numpy_conditional_mutual_information(series_pmf, series_index[0], series_index[2]) ############################################################################################## # def time_gap_correlation(data, t_shift): # """ Calculates correlation matrix of time series with itself shifted by time t # Parameters # ---------- # data : array_like # 2-D array with rows representing variables and columns representing entries # in the time series. # t_shift : integer # The amount of steps to shift the correlation by. 0 gives self-correlation. # """ # if t_shift == 0: # correlation = np.zeros(len(data)**2) # for i, sn_i in enumerate(data): # for j, sn_j in enumerate(data): # # when data is constant np.corrcoeff gives a 'nan' array and can cause errors # if i != j: # correlation[i*len(data) + j] = np.corrcoef(np.vstack((sn_i, sn_j)))[0][1] # else: # correlation[i*len(data) + j] = None # else: # t_data = np.array(data)[:,t_shift:] # x_n # data_t = np.array(data)[:,:-t_shift] # x_n-t # correlation = np.zeros(len(data)**2) # for i, sn_i in enumerate(t_data): # for j, snn_j in enumerate(data_t): # # when data is constant np.corrcoeff gives a 'nan' array and can cause errors # correlation[i*len(data) + j] = np.corrcoef(np.vstack((sn_i, snn_j)))[0][1] # return correlation # ################################################################################## # # the transitions are done weirdly, needs to be re-done # class Bertschinger_Automaton(): # def __init__(self, S, E, mu, phi, psi) -> None: # self.time = 0 # self.S = S # (s_0, s_1, ...) # self.E = E # (e_0, e_1, ...) # self.phi = phi # [e][s][s'] = p(s'|s,e) # self.psi = psi # [s][e][e'] = p(e'|e,s) # self.mu = mu # [s][e] = p(s,e) # self.history = [] # self.state = None # self.set_state() # def set_state(self, state_set=None, t=None): # if isinstance(t, int): # self.time = t # # print(f"time set to {t}") # if state_set is not None: # self.state = state_set # # print(f"state manually set to {self.state}") # else: # mu_states = [] # mu_probs = [] # for i, s_i in enumerate(self.mu): # for j, e_j in enumerate(s_i): # mu_states.append((i,j)) # mu_probs.append(e_j) # choice = np.random.choice(range(len(mu_probs)), p=mu_probs) # self.state = mu_states[choice] # # print(f"Initialized to {self.state}") # self.history.append((self.time, (self.S[self.state[0]], self.E[self.state[1]]))) # def run(self, n_steps, phi=None, psi=None): # if phi is not None: # self.phi = phi # if psi is not None: # self.psi = psi # for _ in range(n_steps): # self.time += 1 # s_n = self.state[0] # e_n = self.state[1] # phi_n = self.phi[e_n][s_n] # psi_n = self.psi[s_n][e_n] # s_n1 = np.random.choice(range(len(phi_n)), p=phi_n) # e_n1 = np.random.choice(range(len(psi_n)), p=psi_n) # self.state = (s_n1, e_n1) # self.history.append((self.time, (self.S[self.state[0]], self.E[self.state[1]]))) # # print("-------------------------------------") # # print(f"t={self.time-1}: s_n={self.S[s_n]}, e_n={self.E[e_n]}") # # print(f"p_s={phi_n}, p_e={psi_n}") # # print(f"t={self.time}: s_n+1 = {self.S[s_n1]}, e_n+1 = {self.E[e_n1]}") # # print(f"ran {n_steps} steps") # def linear_history(self): # sys_history = [] # env_history = [] # for entry in self.history: # sys_history.append(entry[1][0]) # env_history.append(entry[1][1]) # return (sys_history, env_history) # def clear_history(self): # self.history = [] # def make_phi_6b(p, S, E): # """ phi[e][s][s'] = phi(e,s; s') = p(s'|e,s) """ # phi = np.zeros(shape=(len(E), len(S), len(S))) # phi[0][0] = [1-p, p] # phi[0][1] = [1, 0] # phi[1][0] = [p, 1-p] $ [0, 1] for 6a # phi[1][1] = [0, 1] # return phi # def make_psi(p, S, E): # """ psi[s][e][e'] = psi(s,e; e') = p(e'|s,e) """ # psi = np.zeros(shape=(len(S), len(E), len(E))) # psi[0][0] = [p, 1-p] # psi[0][1] = [p, 1-p] # psi[1][0] = [p, 1-p] # psi[1][1] = [p, 1-p] # return psi # def make_mu(S,E): # """ mu(s1, e1) = p(s1, e1) """ # # the distribution should sum to 1 # mu = np.zeros(shape=(len(S), len(E))) # mu[0][0] = 1 # mu[0][1] = 0 # mu[1][0] = 0 # mu[1][1] = 0 # return mu # def seq_maker_Am(data, sys_indices, env_indices, m=2): # """ # Intended for use with Bertschinger NTIC paper # (doi:10.1016/j.biosystems.2007.05.018) # Takes history data from two sources (intended: System and Environment) and # returns them in the format [..., [S_n+1, S_n, E_n, E_n-1, ... , E_n-m], ...] # e.g. for # - data = [(s0, e0), (s1, e1), ..., (s8, e8)] # - sys_indices = 0 # - env_indices = 1 # S = [s0, s1, s2, s3, s4, s5, s6, s7, s8] # E = [e0, e1, e2, e3, e4, e5, e6, e7, e8] # The offsets work as follows (eg: n = 9, m = 3) # |S_{n+1} | s0, s1, s2, s3, [s4, s5, s6, s7, s8] # |S_{n} | s0, s1, s2, [s3, s4, s5, s6, s7], s8 # ----------------------------------------------------------------- # |E_{n} | e0, e1, e2, [e3, e4, e5, e6, e7], e8 # |E_{n-1} | s0, e1, [e2, e3, e4, e5, e6], e7, e8 # |E_{n-2} | e0, [e1, e2, e3, e4, e5], e6, e7, e8 # |E_{n-3} | [e0, e1, e2, e3, e4], e5, e6, e7, e8 # """ # # check for integer # sys_indices = [sys_indices] if isinstance(sys_indices, int) else sys_indices # env_indices = [env_indices] if isinstance(env_indices, int) else env_indices # # make system and env lists from data # sys = np.delete(data.T, env_indices, axis=0) # env = np.delete(data.T, sys_indices, axis=0) # # slice the lists # system_1 = sys[:,m+1:] # system_0 = sys[:,m:-1] # series_output = np.vstack((system_1, system_0)) # for i in range(m+1): # series_output = np.concatenate((series_output, env[:,m-i:-1-i]), axis=0) # return series_output # def seq_maker_Astar(data, sys_indices): # # check for integer # sys_indices = [sys_indices] if isinstance(sys_indices, int) else sys_indices # # make system state list # sys = [] # for state_n in data: # sys_n = [] # for i in sys_indices: # sys_n.append(state_n[i]) # sys.append(tuple(sys_n)) # # make (S_n, S_n+1) # S_S1 = [] # for i, s_n in enumerate(sys): # try: # S_S1.append((s_n, sys[i+1])) # except: # pass # return S_S1 # def sequence_maker_old(system, environment, m=2): # """ # Intended for use with Bertschinger NTIC paper # (doi:10.1016/j.biosystems.2007.05.018) # S = system # E = environment # (S_n+1 : S_n | E_n, E_n-1 ... E_n-m) # Takes history data from two sources (intended: System and Environment) and # returns them in the format [[S_n+1], [S_n], [E_n], [E_n-1], ... , [E_n-m]]] # with appropriate offset, so that the data can be read vertically as well. # For data: # S = [s0, s1, s2, s3, s4, s5, s6, s7, s8] # E = [e0, e1, e2, e3, e4, e5, e6, e7, e8] # The offsets work as follows (eg: n = 9, m = 3) # |S_{n+1} | s0, s1, s2, s3, [s4, s5, s6, s7, s8] # |S_{n} | s0, s1, s2, [s3, s4, s5, s6, s7], s8 # ----------------------------------------------------------------- # |E_{n} | e0, e1, e2, [e3, e4, e5, e6, e7], e8 # |E_{n-1} | s0, e1, [e2, e3, e4, e5, e6], e7, e8 # |E_{n-2} | e0, [e1, e2, e3, e4, e5], e6, e7, e8 # |E_{n-3} | [e0, e1, e2, e3, e4], e5, e6, e7, e8 # """ # system_1 = system[m+1:] # system_0 = system[m:-1] # series_output = [system_1, system_0] # for i in range(m+1): # series_output.append(environment[m-i:-1-i]) # # transpose matrix # series_output = [list(i) for i in zip(*series_output)] # return series_output