class HIOM(Example): def __init__(self): n = 400 g = nx.watts_strogatz_graph(n, 2, 0.02) cfg = { 'utility': False, } self.model = Model(g, ModelConfiguration(cfg)) constants = { 'dt': 0.01, 'A_min': -0.5, 'A_star': 1, 's_O': 0.01, 's_I': 0, 'd_A': 0, 'p': 1, 'r_min': 0, 't_O': np.inf, 'N': n } def initial_I(constants): return np.random.normal(0, 0.3, constants['N']) def initial_O(constants): return np.random.normal(0, 0.2, constants['N']) initial_state = { 'I': initial_I, 'O': initial_O, 'A': 1 } def update_I_A(nodes, constants): node = nodes[0] nb = np.random.choice(self.model.get_neighbors(node)) if abs(self.model.get_node_state(node, 'O') - self.model.get_node_state(nb, 'O')) > constants['t_O']: return {'I': self.model.get_node_state(node, 'I')} else: # Update information r = constants['r_min'] + (1 - constants['r_min']) / (1 + np.exp(-1 * constants['p'] * (self.model.get_node_state(node, 'O') - self.model.get_node_state(nb, 'O')))) inf = r * self.model.get_node_state(node, 'I') + (1-r) * self.model.get_node_state(nb, 'I') + np.random.normal(0, constants['s_I']) # Update attention node_A = self.model.get_node_state(node, 'A') + constants['d_A'] * (2 * constants['A_star'] - self.model.get_node_state(node, 'A')) nb_A = self.model.get_node_state(nb, 'A') + constants['d_A'] * (2 * constants['A_star'] - self.model.get_node_state(nb, 'A')) return {'I': [inf], 'A': {node: node_A, nb: nb_A}} def update_A(constants): return {'A': self.model.get_state('A') - 2 * constants['d_A'] * self.model.get_state('A')/constants['N']} def update_O(constants): noise = np.random.normal(0, constants['s_O'], constants['N']) x = self.model.get_state('O') - constants['dt'] * (self.model.get_state('O')**3 - (self.model.get_state('A') + constants['A_min']) * self.model.get_state('O') - self.model.get_state('I')) + noise return {'O': x} def shrink_I(): return {'I': self.model.get_state('I') * 0.999} def shrink_A(): return {'A': self.model.get_state('A') * 0.999} def sample_attention_weighted(graph): probs = [] A = self.model.get_state('A') factor = 1.0/sum(A) for a in A: probs.append(a * factor) return np.random.choice(graph.nodes, size=1, replace=False, p=probs) # Model definition self.model.constants = constants self.model.set_states(['I', 'A', 'O']) update_cfg = UpdateConfiguration({ 'arguments': {'constants': self.model.constants}, 'get_nodes': True }) up_I_A = Update(update_I_A, update_cfg) s_I = Update(shrink_I) s_A = Update(shrink_A) self.model.add_scheme(Scheme(sample_attention_weighted, {'args': {'graph': self.model.graph}, 'updates': [up_I_A]})) self.model.add_scheme(Scheme(lambda graph: graph.nodes, {'args': {'graph': self.model.graph}, 'lower_bound': 5000, 'updates': [s_I]})) self.model.add_scheme(Scheme(lambda graph: graph.nodes, {'args': {'graph': self.model.graph}, 'lower_bound': 10000, 'updates': [s_A]})) self.model.add_update(update_A, {'constants': self.model.constants}) self.model.add_update(update_O, {'constants': self.model.constants}) self.model.set_initial_state(initial_state, {'constants': self.model.constants}) def simulate(self, n): return self.model.simulate(n) def visualize(self, iterations): visualization_config = { 'layout': 'fr', 'plot_interval': 100, 'plot_variable': 'O', 'variable_limits': { 'A': [0, 1], 'O': [-1, 1], 'I': [-1, 1] }, 'cmin': -1, 'cmax': 1, 'color_scale': 'RdBu', 'show_plot': True, # 'plot_output': '../animations/HIOM.gif', 'plot_title': 'HIERARCHICAL ISING OPINION MODEL', } self.model.configure_visualization(visualization_config, iterations) self.model.visualize('animation')
model.set_initial_state(initial_state, {'constants': model.constants}) output = model.simulate(5000) init_graph = nx.convert_matrix.from_numpy_array(output['adjacency'][0], create_using=nx.DiGraph) fixed_positions = nx.drawing.spring_layout(init_graph) visualization_config = { # 'layout': nx.drawing.layout.spring_layout, 'fixed_positions': fixed_positions, 'plot_interval': 100, 'plot_variable': 'O', 'variable_limits': { 'A': [0, 1.5], 'O': [-1, 1], 'I': [-1, 1] }, 'cmin': -1, 'cmax': 1, 'color_scale': 'RdBu', 'show_plot': True, 'directed': False, # 'plot_output': '../animations/HIOM_dynamic.gif', 'plot_title': 'HIERARCHICAL ISING OPINION MODEL', } model.configure_visualization(visualization_config, output) model.visualize('animation')
plt.plot(x, A, label='A') plt.plot(x, C, label='C') plt.legend() plt.subplot(223) plt.plot(x, S, label='S') plt.plot(x, V, label='V') plt.legend() plt.show() visualization_config = { 'plot_interval': 2, 'initial_positions': nx.get_node_attributes(g, 'pos'), 'plot_variable': 'A', 'color_scale': 'Reds', 'variable_limits': { 'A': [0, 0.8], 'lambda': [0.5, 1.5], 'C': [-1, 1], 'V': [-1, 1], 'E': [-1, 1], 'S': [-1, 1] }, 'show_plot': True, # 'plot_output': './animations/c_vs_s.gif', 'plot_title': 'Self control vs craving simulation', } model.configure_visualization(visualization_config, its) model.visualize('animation')
class CravingSelfControl(Example): def __init__(self): # Network definition self.g = nx.random_geometric_graph(250, 0.125) cfg = { 'utility': False, } self.model = Model(self.g, ModelConfiguration(cfg)) constants = { 'q': 0.8, 'b': 0.5, 'd': 0.2, 'h': 0.2, 'k': 0.25, 'S+': 0.5, } constants['p'] = 2 * constants['d'] def initial_v(constants): return np.minimum( 1, np.maximum( 0, self.model.get_state('C') - self.model.get_state('S') - self.model.get_state('E'))) def initial_a(constants): return constants['q'] * self.model.get_state('V') + ( np.random.poisson(self.model.get_state('lambda')) / 7) initial_state = { 'C': 0, 'S': constants['S+'], 'E': 1, 'V': initial_v, 'lambda': 0.5, 'A': initial_a } def update_C(constants): c = self.model.get_state( 'C') + constants['b'] * self.model.get_state('A') * np.minimum( 1, 1 - self.model.get_state('C') ) - constants['d'] * self.model.get_state('C') return {'C': c} def update_S(constants): return { 'S': self.model.get_state('S') + constants['p'] * np.maximum(0, constants['S+'] - self.model.get_state('S')) - constants['h'] * self.model.get_state('C') - constants['k'] * self.model.get_state('A') } # Naive manner # def update_E(constants): # # return {'E': self.model.get_state('E') - 0.015} # e = np.zeros(len(self.model.nodes)) # for i, node in enumerate(self.model.nodes): # neighbor_addiction = 0 # for neighbor in self.model.get_neighbors(node): # neighbor_addiction += self.model.get_node_state(neighbor, 'A') # e[i] = neighbor_addiction / 50 # return {'E': np.maximum(-1.5, self.model.get_state('E') - e)} # Custom calculation # Less naive # def update_E(constants): # e = np.zeros(len(self.model.nodes)) # adj = self.model.get_adjacency() # for i in range(len(self.model.nodes)): # neighbors = adj[i].nonzero() # e[i] = np.sum(self.model.get_nodes_state(neighbors, 'A')) / 50 # return {'E': np.maximum(-1.5, self.model.get_state('E') - e)} # Custom calculation def update_E(constants): adj = self.model.get_adjacency() summed = np.matmul(adj, self.model.get_nodes_states()) e = summed[:, self.model.get_state_index('A')] / 50 return {'E': np.maximum(-1.5, self.model.get_state('E') - e)} def update_V(constants): return { 'V': np.minimum( 1, np.maximum( 0, self.model.get_state('C') - self.model.get_state('S') - self.model.get_state('E'))) } def update_lambda(constants): return {'lambda': self.model.get_state('lambda') + 0.01} def update_A(constants): return { 'A': constants['q'] * self.model.get_state('V') + np.minimum( (np.random.poisson(self.model.get_state('lambda')) / 7), constants['q'] * (1 - self.model.get_state('V'))) } # Model definition self.model.constants = constants self.model.set_states(['C', 'S', 'E', 'V', 'lambda', 'A']) self.model.add_update(update_C, {'constants': self.model.constants}) self.model.add_update(update_S, {'constants': self.model.constants}) self.model.add_update(update_E, {'constants': self.model.constants}) self.model.add_update(update_V, {'constants': self.model.constants}) self.model.add_update(update_lambda, {'constants': self.model.constants}) self.model.add_update(update_A, {'constants': self.model.constants}) self.model.set_initial_state(initial_state, {'constants': self.model.constants}) def simulate(self, n): return self.model.simulate(n) def plot_paper(self, iterations): A = [np.mean(it[:, 5]) for it in iterations] C = [np.mean(it[:, 0]) for it in iterations] E = [np.mean(it[:, 2]) for it in iterations] lmd = [np.mean(it[:, 4]) for it in iterations] S = [np.mean(it[:, 1]) for it in iterations] V = [np.mean(it[:, 3]) for it in iterations] x = np.arange(0, len(iterations)) plt.figure() plt.subplot(221) plt.plot(x, E, label='E') plt.plot(x, lmd, label='lambda') plt.legend() plt.subplot(222) plt.plot(x, A, label='A') plt.plot(x, C, label='C') plt.legend() plt.subplot(223) plt.plot(x, S, label='S') plt.plot(x, V, label='V') plt.legend() plt.show() def visualize(self, iterations): visualization_config = { 'plot_interval': 2, 'initial_positions': nx.get_node_attributes(self.g, 'pos'), 'plot_variable': 'A', 'color_scale': 'Reds', 'variable_limits': { 'A': [0, 0.8], 'lambda': [0.5, 1.5], 'C': [-1, 1], 'V': [-1, 1], 'E': [-1, 1], 'S': [-1, 1] }, 'show_plot': True, # 'plot_output': './animations/c_vs_s.gif', 'plot_title': 'Self control vs craving simulation', } self.model.configure_visualization(visualization_config, iterations) self.model.visualize('animation')